eip | title | description | author | discussions-to | status | type | category | created | requires |
---|---|---|---|---|---|---|---|---|---|
7575 |
Multi-Asset ERC-4626 Vaults |
Extended ERC-4626 Interface enabling Multi-Asset Vaults |
Jeroen Offerijns (@hieronx), Alina Sinelnikova (@ilinzweilin), Vikram Arun (@vikramarun), Joey Santoro (@joeysantoro), Farhaan Ali (@0xfarhaan) |
Final |
Standards Track |
ERC |
2023-12-11 |
20, 165, 2771, 4626 |
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.
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.
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)
All ERC-7575 Vaults MUST implement ERC-4626 excluding the ERC-20 methods and events.
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 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 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
.
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.
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
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.
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.
ERC-7575 Vaults are not fully compatible with ERC-4626 because the ERC-20 functionality has been removed.
// 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;
}
}
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 and related rights waived via CC0.