Skip to content
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

Merged
merged 13 commits into from
Sep 24, 2024
Merged

feat: v3.0.3 #98

merged 13 commits into from
Sep 24, 2024

Conversation

Schlagonia
Copy link
Collaborator

@Schlagonia Schlagonia commented Sep 4, 2024

Description

  • Allow for max uint256 to be passed on deposits, will pull the full user balance bbabc72
  • Add an optional maxLoss variable to the maxRedeem and maxWithdraw functions to match the multi strategy vault abi 5b15c52
  • Allow the strategy name to be updated post deployment 79d1fc5
  • Change the domain separator to use "Yearn vault" instead of name to match multi strategy and since name can be updated.
  • Dont cache intitial chain ID or initial domain separator since they can not be immutables a340c8d

Fixes # (issue)

Checklist

  • I have run solidity linting
  • I have run the tests on my machine
  • I have followed commitlint guidelines
  • I have rebased my changes to the latest version of the main branch
  • I have updated the SPECIFICATION.md for any relevant changes

Schlagonia and others added 8 commits September 3, 2024 17:39
* feat: max uint deposit

* fix: invariant test

* fix: rebase

* fix: dont max mint

* fix: uint 256

Co-authored-by: FP <[email protected]>

* fix: uint 256

Co-authored-by: FP <[email protected]>

* test: check balances

---------

Co-authored-by: FP <[email protected]>
* chore: update domain seperator

* feat: dont cache domain
@Schlagonia Schlagonia linked an issue Sep 13, 2024 that may be closed by this pull request
src/TokenizedStrategy.sol Outdated Show resolved Hide resolved
src/TokenizedStrategy.sol Outdated Show resolved Hide resolved

// Deposit full balance if using max uint.
if (assets == type(uint256).max) {
assets = S.asset.balanceOf(msg.sender);
Copy link
Contributor

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#deposit

Copy link
Collaborator Author

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:

  1. Redeem 100% of shares from DAI-1
  2. Deposit full DAI balance into DAI-2

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.

Comment on lines 2023 to 2025
),
keccak256(bytes(S.name)),
keccak256(bytes("Yearn Vault")),
keccak256(bytes(API_VERSION)),
Copy link
Contributor

Choose a reason for hiding this comment

The 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.
Permit2 reference: https://github.com/Uniswap/permit2/blob/cc56ad0f3439c502c246fc5cfcc3db92bb8b7219/src/EIP712.sol#L26

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)
                )
            );
    }

Copy link
Collaborator Author

Choose a reason for hiding this comment

The 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.

Copy link
Contributor

Choose a reason for hiding this comment

The 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:

testFuzz_permit(uint256) (gas: -114 (-0.133%))
test_permit_withExpiry() (gas: -171 (-0.183%))
test_permit_badV() (gas: -14535 (-0.185%))
test_permit_replay() (gas: -171 (-0.193%))
test_permit_badS() (gas: -114 (-0.203%))
test_permit_ownerSignerMismatch() (gas: -114 (-0.203%))
test_permit_earlyNonce() (gas: -114 (-0.204%))
test_permit_differentSpender() (gas: -114 (-0.204%))
test_permit_zeroAddress() (gas: -114 (-0.204%))
testFuzz_permit_multiple(bytes32) (gas: -1140 (-0.432%))

Copy link
Collaborator Author

Choose a reason for hiding this comment

The 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

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice job figuring out this!

* fix: conversion

* test: full loss

* feat: add self check to max views

* test: balance check

* fix: spelling
@Schlagonia Schlagonia merged commit e90ecb8 into master Sep 24, 2024
3 checks passed
@Schlagonia Schlagonia deleted the 3_0_3 branch September 24, 2024 20:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

URL in foundry.toml is a dead link
4 participants