-
Notifications
You must be signed in to change notification settings - Fork 45
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: v3.0.3 #98
feat: v3.0.3 #98
Changes from 8 commits
06a354f
59ece7f
bbabc72
d6895bb
5b15c52
79d1fc5
a340c8d
caefbd0
67c7d56
fe69dc1
773af25
e031090
f900037
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -212,10 +212,8 @@ contract TokenizedStrategy { | |
// These are the corresponding ERC20 variables needed for the | ||
// strategies token that is issued and burned on each deposit or withdraw. | ||
uint8 decimals; // The amount of decimals that `asset` and strategy use. | ||
uint88 INITIAL_CHAIN_ID; // The initial chain id when the strategy was created. | ||
string name; // The name of the token for the strategy. | ||
uint256 totalSupply; // The total amount of shares currently issued. | ||
bytes32 INITIAL_DOMAIN_SEPARATOR; // The domain separator used for permits on the initial chain. | ||
mapping(address => uint256) nonces; // Mapping of nonces used for permit functions. | ||
mapping(address => uint256) balances; // Mapping to track current balances for each account that holds shares. | ||
mapping(address => mapping(address => uint256)) allowances; // Mapping to track the allowances for the strategies shares. | ||
|
@@ -345,7 +343,7 @@ contract TokenizedStrategy { | |
//////////////////////////////////////////////////////////////*/ | ||
|
||
/// @notice API version this TokenizedStrategy implements. | ||
string internal constant API_VERSION = "3.0.2"; | ||
string internal constant API_VERSION = "3.0.3"; | ||
|
||
/// @notice Value to set the `entered` flag to during a call. | ||
uint8 internal constant ENTERED = 2; | ||
|
@@ -451,11 +449,6 @@ contract TokenizedStrategy { | |
S.name = _name; | ||
// Set decimals based off the `asset`. | ||
S.decimals = ERC20(_asset).decimals(); | ||
// Set initial chain id for permit replay protection. | ||
require(block.chainid < type(uint88).max, "invalid chain id"); | ||
S.INITIAL_CHAIN_ID = uint88(block.chainid); | ||
// Set the initial domain separator for permit functions. | ||
S.INITIAL_DOMAIN_SEPARATOR = _computeDomainSeparator(S); | ||
|
||
// Default to a 10 day profit unlock period. | ||
S.profitMaxUnlockTime = 10 days; | ||
|
@@ -497,6 +490,12 @@ contract TokenizedStrategy { | |
) external nonReentrant returns (uint256 shares) { | ||
// Get the storage slot for all following calls. | ||
StrategyData storage S = _strategyStorage(); | ||
|
||
// Deposit full balance if using max uint. | ||
if (assets == type(uint256).max) { | ||
assets = S.asset.balanceOf(msg.sender); | ||
} | ||
|
||
// Checking max deposit will also check if shutdown. | ||
require( | ||
assets <= _maxDeposit(S, receiver), | ||
|
@@ -524,6 +523,7 @@ contract TokenizedStrategy { | |
) external nonReentrant returns (uint256 assets) { | ||
// Get the storage slot for all following calls. | ||
StrategyData storage S = _strategyStorage(); | ||
|
||
// Checking max mint will also check if shutdown. | ||
require(shares <= _maxMint(S, receiver), "ERC4626: mint more than max"); | ||
// Check for rounding error. | ||
|
@@ -780,6 +780,17 @@ contract TokenizedStrategy { | |
return _maxWithdraw(_strategyStorage(), owner); | ||
} | ||
|
||
/** | ||
* @notice Accepts a `maxLoss` variable in order to match the multi | ||
* strategy vaults ABI. | ||
Schlagonia marked this conversation as resolved.
Show resolved
Hide resolved
|
||
*/ | ||
function maxWithdraw( | ||
address owner, | ||
uint256 /*maxLoss*/ | ||
) external view returns (uint256) { | ||
return _maxWithdraw(_strategyStorage(), owner); | ||
} | ||
|
||
/** | ||
* @notice Total number of strategy shares that can be | ||
* redeemed from the strategy by `owner`, where `owner` | ||
|
@@ -792,6 +803,17 @@ contract TokenizedStrategy { | |
return _maxRedeem(_strategyStorage(), owner); | ||
} | ||
|
||
/** | ||
* @notice Accepts a `maxLoss` variable in order to match the multi | ||
* strategy vaults ABI. | ||
Schlagonia marked this conversation as resolved.
Show resolved
Hide resolved
|
||
*/ | ||
function maxRedeem( | ||
address owner, | ||
uint256 /*maxLoss*/ | ||
) external view returns (uint256) { | ||
return _maxRedeem(_strategyStorage(), owner); | ||
} | ||
|
||
/*////////////////////////////////////////////////////////////// | ||
INTERNAL 4626 VIEW METHODS | ||
//////////////////////////////////////////////////////////////*/ | ||
|
@@ -1594,6 +1616,14 @@ contract TokenizedStrategy { | |
emit UpdateProfitMaxUnlockTime(_profitMaxUnlockTime); | ||
} | ||
|
||
/** | ||
* @notice Updates the name for the strategy. | ||
* @param _name The new name for the strategy. | ||
*/ | ||
function setName(string calldata _name) external onlyManagement { | ||
_strategyStorage().name = _name; | ||
} | ||
|
||
/*////////////////////////////////////////////////////////////// | ||
ERC20 METHODS | ||
//////////////////////////////////////////////////////////////*/ | ||
|
@@ -1982,39 +2012,16 @@ contract TokenizedStrategy { | |
* @notice Returns the domain separator used in the encoding of the signature | ||
* for {permit}, as defined by {EIP712}. | ||
* | ||
* @dev This checks that the current chain id is the same as when the contract | ||
* was deployed to prevent replay attacks. If false it will calculate a new | ||
* domain separator based on the new chain id. | ||
* | ||
* @return . The domain separator that will be used for any {permit} calls. | ||
*/ | ||
function DOMAIN_SEPARATOR() public view returns (bytes32) { | ||
StrategyData storage S = _strategyStorage(); | ||
return | ||
block.chainid == S.INITIAL_CHAIN_ID | ||
? S.INITIAL_DOMAIN_SEPARATOR | ||
: _computeDomainSeparator(S); | ||
} | ||
|
||
/** | ||
* @dev Calculates and returns the domain separator to be used in any | ||
* permit functions for the strategies {permit} calls. | ||
* | ||
* This will be used at the initialization of each new strategies storage. | ||
* It would then be used in the future in the case of any forks in which | ||
* the current chain id is not the same as the original. | ||
* | ||
*/ | ||
function _computeDomainSeparator( | ||
StrategyData storage S | ||
) internal view returns (bytes32) { | ||
return | ||
keccak256( | ||
abi.encode( | ||
keccak256( | ||
"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" | ||
), | ||
keccak256(bytes(S.name)), | ||
keccak256(bytes("Yearn Vault")), | ||
keccak256(bytes(API_VERSION)), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not needed but nice to do You can store these values as constants for cheaper txs. Simpler version: bytes32 internal constant _TYPE_HASH = keccak256(
"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
);
bytes32 internal constant _NAME_HASH = keccak256("Yearn Vault");
bytes32 internal constant _VERSION_HASH = keccak256(bytes(API_VERSION));
function DOMAIN_SEPARATOR() public view returns (bytes32) {
return
keccak256(
abi.encode(
_TYPE_HASH,
_NAME_HASH,
_VERSION_HASH,
block.chainid,
address(this)
)
);
} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I had assumed that the compiler is smart enough to store the individual hashes as constants in the bytecode even without declaring them as constants so the more readable version is preferred. Though I havent verified it is and will double check to make sure We are removing the cached domain_seperator since it cannot be stored as immutables and have to be kept in storage which is more expensive to pull from rather than recalculating it every call. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe newer versions of the compiler are smarter but this one still leaves room for gas savings:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So it appears that all of those savings are actually on coming from the _name_hash. The other two do seem to be storing the hashes as constants but the name hash is not because it is casting the string to bytes() first before hashing it which i guess is not stored in its final form So if we dont cast the string to bytes() which we no longer need to do cause its not pulling from storage, then it appears that the cost for permit is the same as declaring them constants outside the function There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice job figuring out this! |
||
block.chainid, | ||
address(this) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the use case for using
uint256.max
? I like this flow when repaying the debt but for deposit, I haven't seen it yet.Will it break the ERC4626 standard: “MUST revert if all of
assets
cannot be deposited”? https://eips.ethereum.org/EIPS/eip-4626#depositThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The most common case we have run into is when doing multiple txns in a row, especially in a SAFE script, when the value of the underlying the wallet will have is not know.
Ex:
Since the txn executes at a different block than created the amount cannot be known exactly since DAI-1 PPS is always changing and so dust will get left behind.
It does not match 4626 exactly and would be a special case user would need to know about similar to our
masLoss
additional parameter.