Multi-Asset ERC-4626 Vaults
Abstract
The following standard adapts ERC-4626 to support multiple assets or entry points for the same share token. This also enables Vaults which don’t have a true share token but rather convert between two arbitrary external tokens.
It adds a new share
method to the Vault, to allow the ERC-20 dependency to be externalized.
It also adds Vault-to-Share lookup to the share token.
Lastly, it enforces ERC-165 support for Vaults and the share token.
Motivation
One missing use case that is not supported by ERC-4626 is Vaults which have multiple assets or entry points such as liquidity provider (LP) Tokens. These are generally unwieldy or non-compliant due to the requirement of ERC-4626 to itself be an ERC-20.
Specification
Definitions:
The existing definitions from ERC-4626 apply. In addition, this spec defines:
- Multi-Asset Vaults: A Vault which has multiple assets/entry points. The Multi-Asset Vault refers to the group of ERC-7575 contracts with the entry points for a specific asset, linked to one common
share
token. - Pipe: A converter from one token to another (unidirectional or bidirectional)
Methods
All ERC-7575 Vaults MUST implement ERC-4626 excluding the ERC-20 methods and events.
share
The address of the underlying share
received on deposit into the Vault. MUST return an address of an ERC-20 share representation of the Vault.
share
MAY return the address of the Vault itself.
If the share
returns an external token i.e. share != address(this)
:
- entry functions MUST increase the
share
balance of thereceiver
by theshares
amount. i.e.share.balanceOf(receiver) += shares
- exit functions MUST decrease the
share
balance of theowner
by theshares
amount. i.e.share.balanceOf(owner) -= shares
MUST NOT revert.
- name: share
type: function
stateMutability: view
inputs: []
outputs:
- name: shareTokenAddress
type: address
Multi-Asset Vaults
Multi-Asset Vaults share a single share
token with multiple entry points denominated in different asset
tokens.
Multi-Asset Vaults MUST implement the share
method on each entry point. The entry points SHOULD NOT be ERC-20.
Pipes
Pipes convert between a single asset
and share
which are both ERC-20 tokens outside the Vault.
A Pipe MAY be either unidirectional or bidirectional.
A unidirectional Pipe SHOULD implement only the entry function(s) deposit
and/or mint
, not redeem
and/or withdraw
.
The entry points SHOULD lock or burn the asset
from the msg.sender
and mint or transfer the share
to the receiver
. For bidirectional pipes, the exit points SHOULD lock or burn the share
from the owner
and mint or transfer the asset
to the receiver
.
Share-to-Vault lookup
The ERC-20 implementation of share
SHOULD implement a vault
method, that returns the address of the Vault for a specific asset
.
SHOULD emit the VaultUpdate
event when a Vault linked to the share changes.
- name: vault
type: function
stateMutability: view
inputs:
- name: asset
type: address
outputs:
- name: vault
type: address
ERC-165 support
Vaults implementing ERC-7575 MUST implement the ERC-165 supportsInterface
function. The Vault contract MUST return the constant value true
if 0x2f0a18c5
is passed through the interfaceID
argument.
The share contract SHOULD implement the ERC-165 supportsInterface
function. The share token MUST return the constant value true
if 0xf815c03d
is passed through the interfaceID
argument.
Events
VaultUpdate
The Vault linked to the share has been updated.
- name: VaultUpdate
type: event
inputs:
- name: asset
indexed: true
type: address
- name: vault
indexed: false
type: address
Rationale
This standard is intentionally flexible to support both existing ERC-4626 Vaults easily by the introduction of a single new method, but also flexible to support new use cases by allowing separate share tokens.
Ability to externalize ERC-20 Dependency
By allowing share != address(this)
, the Vault can have an external contract managing the ERC-20 functionality of the Share. In the case of Multi-Asset, this avoids the confusion that might arise if each Vault itself were required to be an ERC-20, which could confuse integrators and front-ends.
This approach also enables the creation of new types of Vaults, such as Pipes, which facilitate the conversion between two external ERC-20 tokens. These Pipes could be unidirectional (i.e. only for assets to shares via deposit/mint, or shares to assets via redeem/withdraw) or bidirectional for both entry and exit flows.
Including Share-to-Vault lookup optionally
The vault
method is included to look up a Vault for a share
by its asset
, combined with the VaultUpdate
event and ERC-165 support. This enables integrations to easily query Multi-Asset Vaults.
This is optional, to maintain backward compatibility with use cases where the share
is an existing deployed contract.
Backwards Compatibility
ERC-7575 Vaults are not fully compatible with ERC-4626 because the ERC-20 functionality has been removed.
Reference Implementation
// This code snippet is incomplete pseudocode used for example only and is no way intended to be used in production or guaranteed to be secure
contract Share is ERC20 {
mapping (address asset => address) vault;
function updateVault(address asset, address vault_) public {
vault[asset] = vault_;
emit UpdateVault(asset, vault_);
}
function supportsInterface(bytes4 interfaceId) external pure override returns (bool) {
return interfaceId == 0xf815c03d || interfaceId == 0x01ffc9a7;
}
}
contract TokenAVault is ERC7575 {
address public share = address(Share);
address public asset = address(TokenA);
// ERC4626 implementation
function supportsInterface(bytes4 interfaceId) external pure override returns (bool) {
return interfaceId == 0x2f0a18c5 || interfaceId == 0x01ffc9a7;
}
}
contract TokenBVault is ERC7575 {
address public share = address(Share);
address public asset = address(TokenB);
// ERC4626 implementation
function supportsInterface(bytes4 interfaceId) external pure override returns (bool) {
return interfaceId == 0x2f0a18c5 || interfaceId == 0x01ffc9a7;
}
}
Security Considerations
ERC-20 non-compliant Vaults must take care with supporting a redeem flow where owner
is not msg.sender
, since the ERC-20 approval flow does not by itself work if the Vault and share are separate contracts. It can work by setting up the Vault as a Trusted Forwarder of the share token, using ERC-2771.
Copyright
Copyright and related rights waived via CC0.