---
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).
