Skip to content

Commit

Permalink
feat: version 3.0.2 (#85)
Browse files Browse the repository at this point in the history
* feat: version 302

* fix: remove increase and decrease allowance (#75)

* feat: just track total assets (#73)

* feat: just track total assets

* fix: comments

* feat: pass the storage pointer (#74)

* feat: pass the storage pointer

* chore: rebase

* chore: formatting and comments

* chore: rebase

* fix: formatting functions

* fix: test coverage

* fix: remove lcov file

* fix: ignore coverage file

* fix: max redeem rounding (#83)

* fix: use convert to shares

* fix: tests

* test: add check

* test: fix invariants

* fix: formatting (#78)

* fix: naming

* fix: pack struct (#81)

* fix: pack struct further

* fix: format

* chore: dont use modifier require

* chore: organize

* chore: remove is functions

* chore: add chain id to symbol

* chore: organize

* fix: revert symbol change

* chore: fix symbol bakc

* fix: minor changes (#82)

* chore: remove min fee

* fix: tests

* build: cheaper reentrancy

* chore: fix spech

* feat: remove internal preview

* feat: make factory immutable (#87)

* feat: lower report costs (#76)

* feat: only update unlock date

* feat: dont burn unlocked (#77)

* feat: burn shares once

* feat: dont convert twice

* fix: loss shares to burn

* chore: comments

* fix: report updates

* fix: dont burn unless there is shares

* chore: rebase to storage pointer

* chore: organize reporting

* chore: rebase

* chore: final formatting and script (#88)

* fix: solc

* chore: comments

* chore: deployment script

* chore: yarn
  • Loading branch information
Schlagonia authored Feb 4, 2024
1 parent 9fdb2da commit b2ad48b
Show file tree
Hide file tree
Showing 28 changed files with 1,035 additions and 1,017 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@ docs/
.env

node_modules/
.gas-snapshot
.gas-snapshot
lcov.info
4 changes: 2 additions & 2 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
[submodule "lib/openzeppelin-contracts"]
path = lib/openzeppelin-contracts
url = https://github.com/OpenZeppelin/openzeppelin-contracts
release = v4.8.2
branch = v4.8.2
release = v4.9.5
branch = v4.9.5
[submodule "lib/erc4626-tests"]
path = lib/erc4626-tests
url = https://github.com/a16z/erc4626-tests
Expand Down
4 changes: 2 additions & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"solidity.compileUsingRemoteVersion": "v0.8.18",
"solidity.compileUsingRemoteVersion": "v0.8.18+commit.87f61d96",
"solidity.remappings": [
"@openzeppelin/=./lib/openzeppelin-contracts/",
"@openzeppelin/=lib/openzeppelin-contracts/",
"forge-std/=lib/forge-std/src/",
"ds-test/=lib/forge-std/lib/ds-test/src/",
"erc4626-tests/=lib/erc4626-tests/"
Expand Down
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,20 @@ To print out the storage layout of any contract (e.g 'test/MockStrategy.sol')
make inspect contract=MockStrategy
```

## Deployment

Deployments of the TokenizedStrategy are done using create2 to be at a deterministic address on any EVM chain.

Check the [docs](https://docs.yearn.fi/developers/v3/overview) for the most updated deployment address.

Deployments on new chains can be done permissionlessly by anyone using the included script. First follow the steps to deploy the vault factory from the [Vaults V3](https://github.com/yearn/yearn-vaults-v3/tree/3.0.2) repo.

You can then deploy the TokenizedStrategy using the provided scipt.

```
forge script script/Deploy.s.sol:Deploy --rpc-url YOUR_RPC_URL
```

### To make contributions please follow the [Contribution Guidelines](https://github.com/yearn/tokenized-strategy/blob/master/CONTRIBUTING.md)

# Resources
Expand Down
4 changes: 2 additions & 2 deletions SPECIFICATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ Fee assessment and distribution is handled during each `report` call after profi

It will report the amount of fees that need to be charged and the strategy will issue shares for that amount of fees.

There are two potential fees. Performance fees and protocol fees. Performance fees are configurable by management of the strategy and payed based on the reported profit during each report with a min of 5% and a max of 50%.
There are two potential fees. Performance fees and protocol fees. Performance fees are configurable by management of the strategy and payed based on the reported profit during each report with a max of 50%.

Protocol fees are configured by Yearn governance through the Factory and are taken as a percent of the performanceFees charged. I.E. profit = 100, performance fees = 20% protocol fees = 10%. Then total fees charged = 100 * .2 = 20 of which 10% is sent to the protocol fee recipient (2) and 90% (18) is sent the strategy specific `performanceFeeRecipient`.

Expand Down Expand Up @@ -155,7 +155,7 @@ A strategies management can set another address to serve as an `emergencyAdmin`.
#### Setting Performance Fee
Setting the percent in terms of basis points for the amount of profit to be charged as a fee.

This has a minimum of 5% and a maximum of 50%.
There is a maximum of 50%.

#### Setting performance fee recipient
Setting the non-zero address that will receive any shares issued as a result of the performance fee.
Expand Down
2 changes: 1 addition & 1 deletion lib/openzeppelin-contracts
10 changes: 5 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@
"prettier": "^2.5.1",
"prettier-plugin-solidity": "^1.0.0-beta.19",
"pretty-quick": "^3.1.3",
"solc": "^0.8.12",
"solc": "0.8.18",
"solhint": "^3.3.7",
"solhint-plugin-prettier": "^0.0.5"
},
"scripts": {
"format": "prettier --write 'src/**/*.(sol|json)'",
"format:check": "prettier --check 'src/**/*.*(sol|json)'",
"lint": "solhint 'src/**/*.sol'",
"lint:fix": "solhint --fix 'src/**/*.sol'"
"format": "prettier --write 'src/**/*.(sol|json)' 'script/*.sol'",
"format:check": "prettier --check 'src/**/*.*(sol|json)' 'script/*.sol'",
"lint": "solhint 'src/**/*.sol' 'script/*.sol'",
"lint:fix": "solhint --fix 'src/**/*.sol' 'script/*.sol'"
}
}
36 changes: 18 additions & 18 deletions script/Deploy.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,27 @@ import "forge-std/Script.sol";

// Deploy a contract to a deterministic address with create2 factory.
contract Deploy is Script {
// Create X address.
Deployer public deployer =
Deployer(0xba5Ed099633D3B313e4D5F7bdc1305d3c28ba5Ed);

Deployer public deployer = Deployer(0x8D85e7c9A4e369E53Acc8d5426aE1568198b0112);
// Vault factory address for v3.0.1
address public factory = 0xE9E8C89c8Fc7E8b8F23425688eb68987231178e5;

function run() external {
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
vm.startBroadcast(deployerPrivateKey);

// Append constructor args to the bytecode
bytes memory bytecode = vm.getCode("TokenizedStrategy.sol:TokenizedStrategy");
bytes memory bytecode = abi.encodePacked(
vm.getCode("TokenizedStrategy.sol:TokenizedStrategy"),
abi.encode(factory)
);

// Pick an unique salt
uint256 salt = uint256(keccak256("v3.0.1"));
bytes32 salt = keccak256("v3.0.2");

address contractAddress = deployer.deploy(bytecode, salt);
address contractAddress = deployer.deployCreate2(salt, bytecode);

console.log("Address is ", contractAddress);

Expand All @@ -27,17 +34,10 @@ contract Deploy is Script {
}

contract Deployer {
event Deployed(address addr, uint256 salt);

function deploy(bytes memory code, uint256 salt) external returns (address) {
address addr;
assembly {
addr := create2(0, add(code, 0x20), mload(code), salt)
if iszero(extcodesize(addr)) {
revert(0, 0)
}
}
emit Deployed(addr, salt);
return addr;
}
}
event ContractCreation(address indexed newContract, bytes32 indexed salt);

function deployCreate2(
bytes32 salt,
bytes memory initCode
) public payable returns (address newContract) {}
}
60 changes: 36 additions & 24 deletions src/BaseStrategy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ abstract contract BaseStrategy {
* @dev Use to assure that the call is coming from the strategies management.
*/
modifier onlyManagement() {
TokenizedStrategy.isManagement(msg.sender);
TokenizedStrategy.requireManagement(msg.sender);
_;
}

Expand All @@ -61,7 +61,7 @@ abstract contract BaseStrategy {
* management or the keeper.
*/
modifier onlyKeepers() {
TokenizedStrategy.isKeeperOrManagement(msg.sender);
TokenizedStrategy.requireKeeperOrManagement(msg.sender);
_;
}

Expand All @@ -70,10 +70,13 @@ abstract contract BaseStrategy {
* management or the emergency admin.
*/
modifier onlyEmergencyAuthorized() {
TokenizedStrategy.isEmergencyAuthorized(msg.sender);
TokenizedStrategy.requireEmergencyAuthorized(msg.sender);
_;
}

/**
* @dev Require that the msg.sender is this address.
*/
function _onlySelf() internal view {
require(msg.sender == address(this), "!self");
}
Expand All @@ -83,7 +86,7 @@ abstract contract BaseStrategy {
//////////////////////////////////////////////////////////////*/

/**
* This is the address of the TokenizedStrategy implementation
* @dev This is the address of the TokenizedStrategy implementation
* contract that will be used by all strategies to handle the
* accounting, logic, storage etc.
*
Expand All @@ -103,7 +106,13 @@ abstract contract BaseStrategy {
//////////////////////////////////////////////////////////////*/

/**
* This variable is set to address(this) during initialization of each strategy.
* @dev Underlying asset the Strategy is earning yield on.
* Stored here for cheap retrievals within the strategy.
*/
ERC20 internal immutable asset;

/**
* @dev This variable is set to address(this) during initialization of each strategy.
*
* This can be used to retrieve storage data within the strategy
* contract as if it were a linked library.
Expand All @@ -116,10 +125,6 @@ abstract contract BaseStrategy {
*/
ITokenizedStrategy internal immutable TokenizedStrategy;

// Underlying asset the Strategy is earning yield on.
// Stored here for cheap retrievals within the strategy.
ERC20 internal immutable asset;

/**
* @notice Used to initialize the strategy on deployment.
*
Expand All @@ -140,7 +145,7 @@ abstract contract BaseStrategy {
// Initialize the strategy's storage variables.
_delegateCall(
abi.encodeCall(
ITokenizedStrategy.init,
ITokenizedStrategy.initialize,
(_asset, _name, msg.sender, msg.sender, msg.sender)
)
);
Expand All @@ -162,22 +167,22 @@ abstract contract BaseStrategy {
//////////////////////////////////////////////////////////////*/

/**
* @dev Should deploy up to '_amount' of 'asset' in the yield source.
* @dev Can deploy up to '_amount' of 'asset' in the yield source.
*
* This function is called at the end of a {deposit} or {mint}
* call. Meaning that unless a whitelist is implemented it will
* be entirely permissionless and thus can be sandwiched or otherwise
* manipulated.
*
* @param _amount The amount of 'asset' that the strategy should attempt
* @param _amount The amount of 'asset' that the strategy can attempt
* to deposit in the yield source.
*/
function _deployFunds(uint256 _amount) internal virtual;

/**
* @dev Will attempt to free the '_amount' of 'asset'.
* @dev Should attempt to free the '_amount' of 'asset'.
*
* The amount of 'asset' that is already loose has already
* NOTE: The amount of 'asset' that is already loose has already
* been accounted for.
*
* This function is called during {withdraw} and {redeem} calls.
Expand Down Expand Up @@ -245,9 +250,7 @@ abstract contract BaseStrategy {
* sandwiched can use the tend when a certain threshold
* of idle to totalAssets has been reached.
*
* The TokenizedStrategy contract will do all needed debt and idle updates
* after this has finished and will have no effect on PPS of the strategy
* till report() is called.
* This will have no effect on PPS of the strategy till report() is called.
*
* @param _totalIdle The current amount of idle funds that are available to deploy.
*/
Expand Down Expand Up @@ -357,7 +360,7 @@ abstract contract BaseStrategy {
//////////////////////////////////////////////////////////////*/

/**
* @notice Should deploy up to '_amount' of 'asset' in yield source.
* @notice Can deploy up to '_amount' of 'asset' in yield source.
* @dev Callback for the TokenizedStrategy to call during a {deposit}
* or {mint} to tell the strategy it can deploy funds.
*
Expand All @@ -367,15 +370,15 @@ abstract contract BaseStrategy {
* Unless a whitelist is implemented this will be entirely permissionless
* and thus can be sandwiched or otherwise manipulated.
*
* @param _amount The amount of 'asset' that the strategy should
* @param _amount The amount of 'asset' that the strategy can
* attempt to deposit in the yield source.
*/
function deployFunds(uint256 _amount) external virtual onlySelf {
_deployFunds(_amount);
}

/**
* @notice Will attempt to free the '_amount' of 'asset'.
* @notice Should attempt to free the '_amount' of 'asset'.
* @dev Callback for the TokenizedStrategy to call during a withdraw
* or redeem to free the needed funds to service the withdraw.
*
Expand Down Expand Up @@ -412,7 +415,7 @@ abstract contract BaseStrategy {
* so msg.sender == address(this).
*
* We name the function `tendThis` so that `tend` calls are forwarded to
* the TokenizedStrategy so it can do the necessary accounting.
* the TokenizedStrategy.
* @param _totalIdle The amount of current idle funds that can be
* deployed during the tend
Expand All @@ -429,8 +432,7 @@ abstract contract BaseStrategy {
* the TokenizedStrategy so msg.sender == address(this).
*
* We name the function `shutdownWithdraw` so that `emergencyWithdraw`
* calls are forwarded to the TokenizedStrategy so it can do the necessary
* accounting after the withdraw.
* calls are forwarded to the TokenizedStrategy.
*
* @param _amount The amount of asset to attempt to free.
*/
Expand Down Expand Up @@ -470,7 +472,17 @@ abstract contract BaseStrategy {
return result;
}

// execute a function on the TokenizedStrategy and return any value.
/**
* @dev Execute a function on the TokenizedStrategy and return any value.
*
* This fallback function will be executed when any of the standard functions
* defined in the TokenizedStrategy are called since they wont be defined in
* this contract.
*
* It will delegatecall the TokenizedStrategy implementation with the exact
* calldata and return any relevant values.
*
*/
fallback() external {

Check warning on line 486 in src/BaseStrategy.sol

View workflow job for this annotation

GitHub Actions / solidity

When fallback is not payable you will not be able to receive ether

Check warning on line 486 in src/BaseStrategy.sol

View workflow job for this annotation

GitHub Actions / solidity

Fallback function must be simple
// load our target address
address _tokenizedStrategyAddress = tokenizedStrategyAddress;
Expand Down
Loading

0 comments on commit b2ad48b

Please sign in to comment.