ERC4626: A new standard for tokenized vaults

ERC token standards

We’ve heard about the ERC20 and ERC721 token standards. And in case you’re not familiar with them, here is a quick refresh:

  • ERC20: an implementation for tokens that includes methods like transfer, approve, allowance, totalSupply, and balanceOf. It’s used as a base for hundreds of tokens like USDC, WBTC, or LINK.
  • ERC721: an implementation for Non Fungible Tokens (NFTs) used for assets and collections like Bored Ape Yatch Club (BAYC) or Moonbirds traded in OpenSea and other marketplaces.

They are the building blocks that allowed hundreds of apps to appear in recent years and the main reason behind the ICO era of 2018 and the NFT boom in the last two years.

These standards gave developers a starting point for their apps and allowed them to create tokens in just a few minutes. The ERC4626 is a new extension of the ERC20 and it will give a fresh perspective to the DeFi space.

ERC4626 in a nutshell

A tokenized vault holds a specific ERC20 token and gives shares to the users that deposit assets in it. When a user deposits funds, the vault will mint a specific number of shares for the user. When the user withdraws tokens from the vault, the correspondent amount of shares will be burnt.

The ERC4626 contract inherits from the ERC20 contract to manage the shares that will be minted and burnt when the users interact with the vault. It’ll do so by calling the methods mint and burn from ERC20.

This means that other methods like balanceOf and totalSupply can be used. In addition, as shares are ERC20 tokens, they can be transferred using the native transfer method (or transferFrom), unless the vault is created as non-transferrable.

ERC 4626 Methods

Here are some of the most important methods in the ERC4626 tokenized vault contract:

constructor(ERC20 asset, string name, string symbol)

The constructor that initializes an ERC4626 vault upon deployment receives three parameters:

  • asset — is the underlying token that will be stored in the vault
  • name — the name of the vault
  • symbol — the symbol of the shares minted for the users

asset()

The asset method returns the address of the ERC20 token that is deposited in the vault by the users.

deposit(uint assets, address receiver)

The deposit method is used to deposit the exact amount of assets in the vault and mint a corresponding number of shares for the receiver.

As the token will be transferred to the vault, this requires that the user had approved for the tokens to be transferred in advance via the approve or transferFrom ERC20 methods.

The methods maxDeposit returns the maximum amount of tokens that can be deposited in the vault. The previewDeposit method allows users to simulate the deposit and returns the number of shares that the user would receive.

mint(uint shares, address receiver)

Similar to the deposit method but in this case, it receives as a parameter the number of shares to mint.

The maxMint and previewMint methods are also available.

withdraw(uint assets, address receiver, address owner)

The withdraw method burns the corresponding number of shares required to send the indicated amount of the original asset to the receiver.

The maxWithdraw method returns the maximum number of tokens that the user is allowed to withdraw. The previewWithdraw allows users to simulate the withdrawal and returns the number of shares that would be burned in the withdrawal.

redeem(uint shares, address receiver, address owner)

Similar to the withdraw method although it receives as a parameter the number of shares to burn.

The maxRedeed and previewRedeem methods are also available.

Events

The ERC4626 tokenized vault has just two events:

  • Deposit: emitted from the deposit and mint methods.
  • Withdraw: emitted from the withdraw and redeem methods.

Contract interface

Here is the full contract interface for the ERC4626, which includes all the methods and events:

interface IERC4626 {
    function asset() external view returns (address assetTokenAddress);
    function totalAssets() external view returns (uint256 totalManagedAssets);
    function convertToShares(uint256 assets) external view returns (uint256 shares);
    function convertToAssets(uint256 shares) external view returns (uint256 assets);
    function maxDeposit(address receiver) external view returns (uint256 maxAssets);
    function previewDeposit(uint256 assets) external view returns (uint256 shares);
    function deposit(uint256 assets, address receiver) external returns (uint256 shares);
    function maxMint(address receiver) external view returns (uint256 maxShares);
    function previewMint(uint256 shares) external view returns (uint256 assets);
    function mint(uint256 shares, address receiver) external returns (uint256 assets);
    function maxWithdraw(address owner) external view returns (uint256 maxAssets);
    function previewWithdraw(uint256 assets) external view returns (uint256 shares);
    function withdraw(uint256 assets, address receiver, address owner) external returns (uint256 shares);
    function maxRedeem(address owner) external view returns (uint256 maxShares);
    function previewRedeem(uint256 shares) external view returns (uint256 assets);
    function redeem(uint256 shares, address receiver, address owner) external returns (uint256 assets);
    event Deposit(address indexed caller, address indexed owner, uint256 assets, uint256 shares);
    event Withdraw(address indexed caller, address indexed receiver, address indexed owner, uint256 assets, uint256 shares);
}

Example implementation

The ERC4626 specification is completed and you can find all the details here. Based on that, a pull request was created in the OpenZeppelin repository which was finally merged on June 1st, 2022. The tokenized vault is an ERC20 extension so you can import it from :

openzeppelin-contracts/contracts/token/ERC20/extensions/ERC4626.sol

You can find the full contract here.

As the specification is already completed, there are teams already working with it. For example, Rari Capital has a GitHub repo with the ERC4626 contract and an example implementation.

In that example, you can see how they’ve extended the contract to include fees, an authentication system that sets the contract deployer as the owner of the vault, more events, etc.

If it looks too complex, check out the Vault’s test file to understand how to interact with the Vault contract. You can run the tests locally using Foundry (check out our intro to Foundry here).

Conclusion

The adoption of this standard is promising, and I’ve already found one version written in Vyper by fubuloubu and another one written in Cairo.

There are also some tools under development, like this ERC4626 router by fei-protocol that can perform multicalls to different vaults.

By using a standard token, interoperability between protocols will be improved, aggregators will be easier to build, and applications will be more secure. Now it’s your time to start building with it 🤙

Have you already explored what you can achieve with Chainstack? Get started for free today.

Chainstack announces support for Harmony

We are ecstatic to announce that Harmony is now supported on Chainstack, giving Harmony developers and the community more access to on-chain utility with enterprise grade blockchain infrastructure.

Janson Lee
Mar 18
Chainstack uses cookies to provide you with a secure and
personalized experience on its website. Learn more.