-
-
Save fubuloubu/61dd062f6508852895e31319d8ab2cd4 to your computer and use it in GitHub Desktop.
| # ERC4626: Yield-Bearing Vaults | |
| # Definitions: | |
| # - asset: The underlying token managed by the Vault. | |
| # Has units defined by the corresponding ERC20 contract. | |
| # - share: The token of the Vault. Has a ratio of underlying tokens | |
| # exchanged on deposit/withdraw (defined by the vault) | |
| # - slippage: any difference between advertised share price and economic realities of | |
| # depositto or withdrawal from the Vault, which is not accounted by fees | |
| # - deposit fee: fee charged on deposit to Vault | |
| # - performance fee: fee charged on the yield generated by the Vault | |
| # - management fee: fee charged on deposited tokens while in Vault | |
| # - withdrawal fee: fee charged on exit to the Vault | |
| # NOTE: This standard does not consider any more complex fee structures than the above | |
| implements: | |
| # All ERC4626 implementations must be use ERC20 to represent shares | |
| - ERC20 | |
| # ERC4626 users should consider using the Metadata extension of ERC20, | |
| # and represent `name` and `symbol` as an extension to the underlying | |
| - ERC20Metadata | |
| # ERC4626 users should consider using ERC165 to inform outside integrators | |
| # that they indeed comply to the ERC4626 interface, to ensure that | |
| # upstream contracts do not add a Vault that does not comply to the | |
| # interface, risking loss of funds for their contracts | |
| - ERC165 | |
| ERC4626: | |
| # The address of the underlying token used for the Vault | |
| - name: asset | |
| type: function | |
| stateMutability: view | |
| outputs: | |
| - name: assetTokenAddress | |
| type: address # Must be an ERC20 | |
| # The current exchange rate of shares to tokens | |
| # NOTE: Tokens per unit share e.g. `10**Vault.decimmals()` | |
| # NOTE: Should be inclusive of any management or performance fee | |
| # NOTE: Should *not* be inclusive of any deposit or withdrawal fee | |
| # NOTE: In certain types of fee calculations, this calculation will | |
| # not reflect the "per-user" price-per-share, and instead | |
| # should reflect the "average-user" price-per-share, meaning | |
| # what the average user should expect to exchange to and from. | |
| # `assetsOf` should be used for more accurate calculations. | |
| # NOTE: This value may not be completely accurate according to | |
| # slippage or other on-chain conditions. | |
| - name: pricePerShare | |
| type: function | |
| stateMutability: view | |
| outputs: | |
| - name: assetsPerUnitShare | |
| type: uint256 | |
| # Total number of underyling assets that managed by Vault | |
| # NOTE: This should include any compounding that occurs from yield | |
| # NOTE: This is the Value that any management fees should be charged against | |
| - name: totalAssets | |
| type: function | |
| stateMutability: view | |
| outputs: | |
| - name: totalAssets | |
| type: uint256 | |
| # Total number of underlying tokens that a depositor's shares represent | |
| # NOTE: This function is more accurate than using `pricePerShare` or | |
| # `totalAssets / vault.totalSupply` for certain fee calculations | |
| # NOTE: This value may not be completely accurate according to | |
| # slippage or other on-chain conditions. | |
| - name: assetsOf | |
| type: function | |
| stateMutability: view | |
| inputs: | |
| - name: depositor | |
| type: address | |
| outputs: | |
| - name: assets | |
| type: uint256 | |
| # Allow an on-chain or off-chain user to simulate the effects of their | |
| # deposit at the current block and on-chain conditions. | |
| # NOTE: This value should simulate as closely as possible the real | |
| # outcome of a deposit. Integrators to this contract expect the | |
| # represented slippage or loss to be under 1 basis point of accuracy. | |
| - name: previewDeposit | |
| type: function | |
| stateMutability: view | |
| inputs: | |
| # Maximum number of assets they wish to deposit | |
| # NOTE: Vault will simulate attempt to deposit up to `maxAssets` | |
| - name: maxAssets | |
| type: uint256 | |
| outputs: | |
| # Number of assets that Vault has simulated as deposited | |
| # NOTE: This number may be less than `maxAssets` to communicate a | |
| # "deposit limit" or other such concept, where less than | |
| # `maxAssets` was taken from depositor, and the rest was either | |
| # refunded or simply not transferred in the first place | |
| # e.g. `depositedAssets <= maxAssets` | |
| - name: depositedAssets | |
| type: uint256 | |
| # Number of shares issued for `depositedAssets` | |
| # NOTE: any discrepency between `shares * pricePerShare` | |
| # and `depositedTokens` should be considered slippage in share | |
| # price or some other type of condition, meaning the depositor | |
| # will lose assets by depositing | |
| # NOTE: can also be used to communicate a deposit fee | |
| - name: shares | |
| type: uint256 | |
| # Perform a deposit of assets from the caller to the vault | |
| # NOTE: Conditions of deposit should match as closely as possible to | |
| # those described by `previewDeposit`. Any discrepency could | |
| # cause a revert due to tight slippage bounds by caller. | |
| # NOTE: caller should pre-approve this deposit with `asset.approve` | |
| - name: deposit | |
| type: function | |
| stateMutability: nonpayable | |
| inputs: | |
| # Party who should receive the deposited shares | |
| - name: receiver | |
| type: address | |
| # Maximum number of tokens to process in the deposit | |
| - name: assetsToDeposit | |
| type: uint256 | |
| outputs: | |
| # Number of shares created and given to `receiver` | |
| # NOTE: Should revert if `sharesCreated < minShares` | |
| - name: sharesCreated | |
| type: uint256 | |
| # Allow an on-chain or off-chain user to simulate the effects of their | |
| # mint at the current block and on-chain conditions. | |
| # NOTE: This value should simulate as closely as possible the real | |
| # outcome of a share mint. Integrators to this contract expect the | |
| # represented slippage or loss to be under 1 basis point of accuracy. | |
| - name: previewMint | |
| type: function | |
| stateMutability: view | |
| inputs: | |
| # The maximuim number of shares the depositor wishes to create | |
| # NOTE: Vault will simulate attempt to deposit and create up to `maxShares` | |
| - name: maxShares | |
| type: uint256 | |
| outputs: | |
| # Number of assets that need to deposited to mint | |
| # NOTE: any discrepency between `pricePerShare / minAssets` | |
| # and `sharesCreated` should be considered slippage in share | |
| # price or some other type of condition, meaning the depositor | |
| # will lose assets by depositing | |
| # NOTE: can also be used to communicate a deposit fee | |
| - name: minAssets | |
| type: uint256 | |
| # Exact number of shares created for `minAssets` | |
| # NOTE: This number may be less than `maxShares` to communicate a | |
| # "deposit limit" or other such concept, where less than | |
| # the expected number of shares were created. | |
| # e.g. `sharesCreated <= maxShares` | |
| - name: sharesCreated | |
| type: uint256 | |
| # Perform a mint of shares from the Vault to the caller | |
| # NOTE: Conditions of mint should match as closely as possible to | |
| # those described by `previewMint`. Any discrepency could | |
| # cause a revert due to tight slippage bounds by caller. | |
| # NOTE: caller should pre-approve this minting with `asset.approve` | |
| - name: mint | |
| type: function | |
| stateMutability: nonpayable | |
| inputs: | |
| # Party who should receive the deposited shares | |
| - name: receiver | |
| type: address | |
| # Maximum number of shares to create in the mint | |
| - name: sharesToMint | |
| type: uint256 | |
| outputs: | |
| # Number of assets deposited to the Vault | |
| - name: assetsAccepted | |
| type: uint256 | |
| # Event emitted when tokens are deposited into the vault | |
| - name: Deposit | |
| type: event | |
| inputs: | |
| # The user who triggered the deposit | |
| - name: sender | |
| indexed: true | |
| type: address | |
| # The user who is able to withdraw the created shares | |
| - name: receiver | |
| indexed: true | |
| type: address | |
| # Number of underlying tokens sent to the vault | |
| - name: value | |
| indexed: false | |
| type: uint256 | |
| # Allow an on-chain or off-chain user to simulate the effects of their | |
| # withdrawal at the current block and on-chain conditions. | |
| # NOTE: This value should simulate as closely as possible the real | |
| # outcome of a withdrawal. Integrators to this contract expect the | |
| # represented slippage or loss to be under 1 basis point of accuracy. | |
| - name: previewWithdraw | |
| type: function | |
| stateMutability: view | |
| inputs: | |
| # Maximum number of shares they wish to withdraw | |
| # NOTE: Vault will simulate attempt to withdraw up to `maxShares` | |
| - name: maxShares | |
| type: uint256 | |
| outputs: | |
| # Number of shares that Vault has redeemed | |
| # NOTE: This number may be less than `maxShares` to communicate a | |
| # "withdrawal limit" or other such concept, where less than | |
| # `maxShares` was redeemed by depositor, and the rest was left | |
| # untouched e.g. `redeemedShares <= maxShares` | |
| - name: redeemedShares | |
| type: uint256 | |
| # Number of tokens transferred out for `redeemedShares` | |
| # NOTE: any discrepency between `maxAssets / pricePerShare` | |
| # and `redeemedShares` should be considered slippage in share | |
| # price meaning the depositor has lost tokens by withdrawing | |
| # NOTE: `maxTokens` is only an *estimate* of the amount that will be | |
| # withdrawn, and should not have to fully perform a withdrawal, | |
| # but simply be as accurate as possible to the withdrawal amount | |
| # so as to be useful for depositors to gauge potential slippage. | |
| # NOTE: can also be used to communicate a withdrawal fee | |
| - name: assets | |
| type: uint256 | |
| # Perform a redemption of shares from the caller's balance | |
| # NOTE: Conditions of withdrawal should match as closely as possible to | |
| # those described by `previewWithdraw`. Any discrepency could | |
| # cause a revert due to tight slippage bounds by caller. | |
| - name: withdraw | |
| type: function | |
| stateMutability: nonpayable | |
| inputs: | |
| # Number of shares they wish to redeem | |
| - name: sharesToRedeem | |
| type: uint256 | |
| # Party who should receive redeemed tokens | |
| - name: receiver | |
| type: address | |
| outputs: | |
| # Number of tokens redeemed and sent to receiver | |
| # NOTE: Integrators to untrusted Vaults should indepedently check | |
| # token balance increase | |
| - name: tokensWithdrawn | |
| type: uint256 | |
| # Same as `withdraw`, but shares are being redeemed on behalf of | |
| # another party who has previous approved via Vault's ERC20 approval flow | |
| - name: withdrawFrom | |
| type: function | |
| stateMutability: nonpayable | |
| inputs: | |
| # Owner who shares should be redeemed from | |
| # NOTE: `owner` should pre-approve this redemption with Vault | |
| - name: owner | |
| type: address | |
| # Same as `withdraw` | |
| - name: receiver | |
| type: address | |
| # Same as `withdraw` | |
| - name: sharesToRedeem | |
| type: uint256 | |
| outputs: | |
| # Same as `withdraw` | |
| - name: tokensWithdrawn | |
| type: uint256 | |
| # Event emitted when tokens are withdrawn from the vault by a depositor. | |
| - name: Withdraw | |
| type: event | |
| inputs: | |
| # The user who triggered the withdrawal | |
| - name: owner | |
| indexed: true | |
| type: address | |
| # The user who received the withdrawn tokens | |
| - name: receiver | |
| indexed: true | |
| type: address | |
| # The number of underlying tokens sent out of the vault | |
| - name: value | |
| indexed: false | |
| type: uint256 | |
| ERC4626SlippageProtection: | |
| # Add this optional extension if you would like to give users | |
| # the ability to specify what amount of slippage loss they are | |
| # comfortable with. | |
| # NOTE: Not needed in many cases. | |
| # Same as `ERC4626.deposit` | |
| - name: deposit | |
| type: function | |
| stateMutability: nonpayable | |
| inputs: | |
| # Same as `ERC4626.deposit` | |
| - name: receiver | |
| type: address | |
| # Same as `ERC4626.deposit` | |
| - name: assetsToDeposit | |
| type: uint256 | |
| # Minimum number of shares to be accepted by depositor | |
| # Default: | |
| # `minShares = assetsToDeposit / pricePerShare` | |
| # NOTE: Only used as a guide for untrusted Vaults, integrators | |
| # should enforce that `minShares <= sharesCreated` afterwards | |
| - name: minShares | |
| optional: true | |
| type: uint256 | |
| outputs: | |
| # Same as `ERC4626.deposit` | |
| # NOTE: Should revert if `sharesCreated < minShares` | |
| - name: sharesCreated | |
| type: uint256 | |
| # Same as `ERC4626.withdraw` | |
| - name: withdraw | |
| type: function | |
| stateMutability: nonpayable | |
| inputs: | |
| # Same as `ERC4626.withdraw` | |
| - name: sharesToRedeem | |
| type: uint256 | |
| # PSame as `ERC4626.withdraw` | |
| - name: receiver | |
| type: address | |
| # Minimum number of tokens to be accepted by depositor | |
| # Default: | |
| # `minTokens = sharesToRedeem * pricePerShare` | |
| # NOTE: Only used as a guide for untrusted Vaults, integrators | |
| # should enforce that `minTokens <= tokensWithdrawn` afterwards | |
| - name: minTokens | |
| optional: true | |
| type: uint256 | |
| outputs: | |
| # Same as `ERC4626.withdraw` | |
| # NOTE: Should revert if `tokensWithdrawn < minTokens` | |
| - name: tokensWithdrawn | |
| type: uint256 |
Would it be okay to use tokensOf(depositor) to represent this more explicit case, and then use pricePerShare to represent the time- and size-weighted average of all depositors' share prices?
Yes I think it would be ok, over which period of time would this price be calculated ?
I also was thinking of adding an APY function.
Yes I think it would be ok, over which period of time would this price be calculated ?
The current average of all depositors, probably tracked by a continuously updating variable. I meant for that function to sort of serve as an "instantanous" gauge of current value, but I guess on average. Actually, maybe it makes more sense just to get rid of it, if it's contextual, it's better off just using tokensOf(depositor) instead to get an accurate reading.
I also was thinking of adding an APY function.
APY functions are really hard. And if you make it contextual to the depositor... ngmi lol. I think it should perhaps be an extension or separate EIP.
But ape doesn't know this because ape is just an ape lol
Deposit w/ Permit seems like a good optional extension, as it would only be used directly by EOA accounts. Or another EIP.