--- eip: 7575 title: Multi-Asset ERC-4626 Vaults description: Extended ERC-4626 Interface enabling Multi-Asset Vaults author: Jeroen Offerijns (@hieronx), Alina Sinelnikova (@ilinzweilin), Vikram Arun (@vikramarun), Joey Santoro (@joeysantoro), Farhaan Ali (@0xfarhaan) discussions-to: https://ethereum-magicians.org/t/erc-7575-partial-and-extended-erc-4626-vaults/17274 status: Final type: Standards Track category: ERC created: 2023-12-11 requires: 20, 165, 2771, 4626 --- ## Abstract The following standard adapts [ERC-4626](./eip-4626.md) 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](./eip-20.md) dependency to be externalized. It also adds Vault-to-Share lookup to the share token. Lastly, it enforces [ERC-165](./eip-165.md) support for Vaults and the share token. ## Motivation One missing use case that is not supported by [ERC-4626](./eip-4626.md) 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](./eip-20.md). ## Specification ### Definitions: The existing definitions from [ERC-4626](./eip-4626.md) 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](./eip-7575.md) 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](./eip-7575.md) Vaults MUST implement [ERC-4626](./eip-4626.md) excluding the [ERC-20](./eip-20.md) methods and events. #### share The address of the underlying `share` received on deposit into the Vault. MUST return an address of an [ERC-20](./eip-20.md) 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 the `receiver` by the `shares` amount. i.e. `share.balanceOf(receiver) += shares` * exit functions MUST decrease the `share` balance of the `owner` by the `shares` amount. i.e. `share.balanceOf(owner) -= shares` MUST _NOT_ revert. ```yaml - 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](./eip-20.md). ### Pipes Pipes convert between a single `asset` and `share` which are both [ERC-20](./eip-20.md) 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](./eip-20.md) 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. ```yaml - name: vault type: function stateMutability: view inputs: - name: asset type: address outputs: - name: vault type: address ``` ### [ERC-165](./eip-165.md) support Vaults implementing [ERC-7575](./eip-7575.md) MUST implement the [ERC-165](./eip-165.md) `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](./eip-165.md) `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. ```yaml - 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](./eip-4626.md) 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](./eip-20.md) Dependency By allowing `share != address(this)`, the Vault can have an external contract managing the [ERC-20](./eip-20.md) 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](./eip-20.md), 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](./eip-20.md) 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](./eip-165.md) 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](./eip-7575.md) Vaults are not fully compatible with [ERC-4626](./eip-4626.md) because the [ERC-20](./eip-20.md) functionality has been removed. ## Reference Implementation ```solidity // 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](./eip-20.md) non-compliant Vaults must take care with supporting a redeem flow where `owner` is not `msg.sender`, since the [ERC-20](./eip-20.md) 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](./eip-2771.md). ## Copyright Copyright and related rights waived via [CC0](../LICENSE.md).