📖 This EIP is in the review stage. It is subject to changes and feedback is appreciated.

EIP-4626: Tokenized Vault Standard Source

A standard for tokenized vaults with a single underlying ERC-20 token.

AuthorJoey Santoro, t11s, Jet Jadeja, Alberto Cuesta Cañada
Discussions-Tohttps://ethereum-magicians.org/t/eip-4626-yield-bearing-vault-standard/7900
StatusReview
TypeStandards Track
CategoryERC
Created2021-12-22

Abstract

The following standard allows for the implementation of a standard API for tokenized vaults with a single underlying ERC-20 within smart contracts. This standard is an extension on the ERC-20 token that provides basic functionality for depositing and withdrawing tokens and reading balances.

Motivation

Tokenized vaults have a lack of standardization leading to diverse implementation details. Some various examples include lending markets (Compound, Aave, Fuse), aggregators (Yearn, Rari Vaults, Idle), and intrinsically interest bearing tokens (xSushi). This makes integration difficult at the aggregator or plugin layer for protocols which need to conform to many standards. This forces each protocol to implement their own adapters which are error prone and waste development resources.

A standard for tokenized vaults will allow for a similar cambrian explosion to ERC-20, unlocking access to yield and other strategies in a variety of applications with little specialized effort from developers.

Specification

All tokenized vaults MUST implement ERC-20. If a vault is to be non-transferrable, it MAY revert on calls to transfer or transferFrom. The ERC-20 operations balanceOf, transfer, totalSupply, etc. operate on the vault “shares” which represent ownership in the underlying.

Methods

deposit

function deposit(address to, uint256 value) public returns (uint256 shares)

Mints shares amount of vault tokens to to by depositing exactly value underlying tokens.

MUST support the ERC-20 transferFrom flow where the vault has at least value approval over msg.sender’s balance of underlying.

MAY support an additional flow in which the underlying tokens are owned by the vault contract before the deposit execution, and are accounted for during deposit.

MUST emit the Deposit event.

mint

function mint(address to, uint256 shares) public returns (uint256 value)

Mints exactly shares amount of vault tokens to to by depositing value underlying tokens.

value MUST equal the return value of a calculateUnderlying call, with shares as a parameter, executed immediately before withdraw in the same transaction.

MUST support the ERC-20 transferFrom flow where the vault has at least value approval over msg.sender’s balance of underlying.

MAY support an additional flow in which the underlying tokens are owned by the vault contract before the mint execution, and are accounted for during mint.

MUST emit the Deposit event.

withdraw

function withdraw(address from, address to, uint256 value) public returns (uint256 shares)

Burns shares vault tokens from from, withdrawing exactly value underlying tokens to to.

shares MUST equal the return value of a calculateShares call, with value as a parameter, executed immediately before mint in the same transaction.

MUST support the ERC-20 flow in which the from address holds the shares vault tokens being burned. If from != msg.sender, then msg.sender MUST have ERC-20 approval over at least shares vault tokens from from.

MAY support an additional flow in which the vault tokens being burned are owned by the vault contract before the withdraw execution, instead of being owned by from.

MUST emit the Withdraw event.

redeem

function redeem(address from, address to, uint256 shares) public returns (uint256 value)

Burns exactly shares vault tokens from from, withdrawing value underlying tokens to to.

MUST support the ERC-20 flow in which the from address holds the shares vault tokens being burned. If from != msg.sender, then msg.sender MUST have ERC-20 approval over at least shares vault tokens from from.

MAY support an additional flow in which the vault tokens being burned are owned by the vault contract before the redeem execution, instead of being owned by from.

MUST emit the Withdraw event.

totalUnderlying

function totalUnderlying() public view returns (uint256)

Returns the total amount of underlying tokens held/managed by the vault.

balanceOfUnderlying

function balanceOfUnderlying(address owner) public view returns (uint256)

Returns the total amount underlying tokens held in the vault for owner.

underlying

function underlying() public view returns (address)

Returns the address of the token the vault uses for accounting, depositing, and withdrawing.

MUST return the address of a token implementing the ERC-20 standard.

calculateShares

function calculateShares(uint256 underlyingAmount) public view returns (uint256 shareAmount)

Returns the amount of vault tokens that need to be burned to obtain a given amount of underlying tokens in a withdraw call.

calculateUnderlying

function calculateUnderlying(uint256 shareAmount) public view returns (uint256 underlyingAmount);

Returns the amount of underlying tokens that need to be given to the vault to obtain a given amount of vault tokens in a mint call.

Events

Deposit

MUST be emitted when tokens are deposited into the vault.

event Deposit(address indexed from, address indexed to, uint256 value)

Where from is the user who triggered the deposit for value underlying tokens to the vault, and to is the user who is able to withdraw the deposited tokens.

Withdraw

MUST be emitted when tokens are withdrawn from the vault by a depositor.

event Withdraw(address indexed from, address indexed to, uint256 value)

Where from is the owner who and held value underlying tokens in the vault, and to is the user who received the withdrawn tokens.

Rationale

The vault interface is designed to be optimized for integrators with a feature complete yet minimal interface. Details such as accounting and allocation of deposited tokens are intentionally not specified, as vaults are expected to be treated as black boxes on-chain and inspected off-chain before use.

ERC-20 is forced because implementation details like token approval and balance calculation directly carry over to the shares accounting. This standardization makes the vaults immediately compatible with all ERC-20 use cases in addition to ERC-4626.

The vaults are opinionated on a default deposit/withdraw flow because it gives integrators a default pattern to implement. Special use cases can be overloaded on these functions by including additional logic.

The mint function was included for symmetry and feature completeness. Most current use cases of shares based vaults do not ascribe special meaning to the shares such that a user would optimize for a specific number of shares (mint) rather than specific amount of underlying (deposit). However, it is easy to imagine future vault strategies which would have unique and independently useful share representations.

The calculateShares method cannot be guaranteed to be simultaneously exact with the return values for both deposit and withdraw. It matches the return value of withdraw to allow querying the vault for the exact amount of underlying that should be approved for transfer in a withdraw call for the current transaction. The inclusion of a calculateSharesOnDeposit function was considered unnecessary.

The calculateUnderlying method cannot be guaranteed to be simultaneously exact with the return values for both mint and redeem. It matches the return value of mint to allow querying the vault for the exact amount of shares that should be taken from the caller in a mint call for the current transaction. The inclusion of a calculateUnderlyingOnRedeem function was considered unnecessary.

Backwards Compatibility

ERC-4626 is fully backward compatible with the ERC-20 standard and has no known compatibility issues with other standards. For production implementations of vaults which do not use ERC-4626, wrapper adapters can be developed and used.

Reference Implementation

Solmate Minimal Implementation - a tokenized vault using the ERC-20 extension with hooks for developers to add logic in deposit and withdraw.

Rari Vaults are an implementation that is nearly ready for production release. Any discrepancies between the vaults abi and this ERC will be adapted to conform to the ERC before mainnet deployment.

Security Considerations

This specification has similar security considerations to the ERC-20 interface. Fully permissionless yield aggregators, for example, could fall prey to malicious implementations which only conform to the interface but not the specification.

Copyright and related rights waived via CC0.

Citation

Please cite this document as:

Joey Santoro, t11s, Jet Jadeja, Alberto Cuesta Cañada, "EIP-4626: Tokenized Vault Standard," Ethereum Improvement Proposals, no. 4626, December 2021. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-4626.