| title | Multi-Asset Standard |
|---|---|
| description | A standard interface for multi-asset vaults with USD-based accounting and optional governance token wrapping |
| author | Bro (@Sun-of-Siberia) |
| discussions-to | https://github.com/Sun-of-Siberia/BOLD-APEX/issues/35 |
| status | Draft |
| type | Standards Track |
| category | ERC |
| created | 2025-11-20 |
| requires | 20, 165, 2535 |
| license | CC0-1.0 |
This EIP proposes a standard interface for Multi-Asset Vaults (MAS), enabling the creation of vault contracts that hold multiple ERC-20 tokens simultaneously with USD-based accounting. Unlike single-asset vault standards (e.g., ERC-4626), MAS supports multi-asset portfolios with proportional withdrawals, non-transferable shares by default, and optional wrapping into governance tokens. The standard is implementation-agnostic and can be deployed as personal vaults, pooled vaults, or other configurations.
Current vault standards like ERC-4626 are designed for single-asset vaults where all users deposit the same underlying token. While this model works well for many use cases, it has several limitations:
- Single Asset Limitation: ERC-4626 requires a single underlying asset, preventing multi-asset strategies
- Complex Multi-Asset Workarounds: Implementing multi-asset support requires multiple ERC-4626 instances or custom solutions
- Accounting Complexity: Multi-asset portfolios need USD-based valuation for fair share pricing
- Limited Flexibility: No standard for non-transferable shares or governance token wrapping
Multi-Asset Standard (MAS) addresses these limitations by providing:
- Multi-Asset Support: Vaults can hold and manage multiple tokens simultaneously
- USD-Based Accounting: Share valuation based on total USD value using oracle price feeds
- Proportional Withdrawals: Users receive proportional amounts of all vault assets
- Non-Transferable Shares: Default behavior prevents speculation while allowing optional governance token wrapping
- Implementation Flexibility: Can be used for personal vaults, pooled vaults, or other configurations
- Composability: Compatible with modular architectures like ERC-2535 Diamond standard
This standard is valuable for:
- Multi-asset portfolio management
- Yield strategies requiring multiple token positions
- Institutional vaults managing diversified assets
- Applications requiring fair share pricing across multiple assets
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174.
A MAS-compliant vault is a smart contract that:
- Holds one or more ERC-20 tokens (multi-asset portfolio)
- Tracks user ownership via non-transferable shares
- Generates yield through modular strategies
- Optionally wraps shares into transferable governance tokens
Recommended Architecture: MAS-compliant vaults SHOULD implement ERC-2535 (Diamond) to support modular strategy facets, though simpler single-contract implementations are possible for basic use cases.
Every MAS-compliant contract MUST implement the following interface:
// SPDX-License-Identifier: CC0-1.0
pragma solidity ^0.8.0;
interface IERC_MAS {
/// @notice Emitted when assets are deposited into the vault
/// @param user The address depositing assets
/// @param assets Array of token addresses deposited
/// @param amounts Array of amounts deposited (same order as assets)
/// @param sharesMinted Number of vault shares minted to user
/// @param totalValue Total USD value of deposited assets
event Deposited(
address indexed user,
address[] assets,
uint256[] amounts,
uint256 sharesMinted,
uint256 totalValue
);
/// @notice Emitted when assets are withdrawn from the vault
/// @param user The address withdrawing assets
/// @param assets Array of token addresses withdrawn
/// @param amounts Array of amounts withdrawn (same order as assets)
/// @param sharesBurned Number of vault shares burned from user
/// @param totalValue Total USD value of withdrawn assets
event Withdrawn(
address indexed user,
address[] assets,
uint256[] amounts,
uint256 sharesBurned,
uint256 totalValue
);
/// @notice Emitted when shares are wrapped into governance tokens
/// @param user The address wrapping shares
/// @param sharesBurned Number of vault shares burned
/// @param governanceTokensMinted Number of governance tokens minted
/// @param usdValue USD value of wrapped shares
event SharesWrapped(
address indexed user,
uint256 sharesBurned,
uint256 governanceTokensMinted,
uint256 usdValue
);
/// @notice Emitted when governance tokens are unwrapped back to shares
/// @param user The address unwrapping governance tokens
/// @param governanceTokensBurned Number of governance tokens burned
/// @param sharesMinted Number of vault shares minted
/// @param usdValue USD value of unwrapped governance tokens
event SharesUnwrapped(
address indexed user,
uint256 governanceTokensBurned,
uint256 sharesMinted,
uint256 usdValue
);
/// @notice Deposit assets into the vault and receive shares
/// @dev MUST emit Deposited event
/// @dev MUST revert if arrays length mismatch
/// @dev MUST revert if any amount is zero
/// @dev MUST calculate shares based on total USD value of assets
/// @param assets Array of ERC-20 token addresses to deposit
/// @param amounts Array of amounts to deposit (same order as assets)
/// @return shares Number of vault shares minted to caller
function deposit(address[] calldata assets, uint256[] calldata amounts)
external
returns (uint256 shares);
/// @notice Withdraw assets from the vault by burning shares
/// @dev MUST emit Withdrawn event
/// @dev MUST revert if shares exceed caller's balance
/// @dev MUST withdraw proportional amounts of all vault assets
/// @param shares Number of vault shares to burn
/// @return assets Array of token addresses withdrawn
/// @return amounts Array of amounts withdrawn (same order as assets)
function withdraw(uint256 shares)
external
returns (address[] memory assets, uint256[] memory amounts);
/// @notice Get the total number of shares in the vault
/// @return Total supply of vault shares
function totalShares() external view returns (uint256);
/// @notice Get the number of shares owned by an address
/// @param user Address to query
/// @return Number of shares owned by user
function sharesOf(address user) external view returns (uint256);
/// @notice Get the total USD value of all assets in the vault
/// @dev MUST use oracle prices for valuation
/// @dev MUST return value in 18 decimals
/// @return Total vault value in USD (18 decimals)
function totalValue() external view returns (uint256);
/// @notice Get the USD value of a user's shares
/// @param user Address to query
/// @return USD value of user's shares (18 decimals)
function valueOfShares(address user) external view returns (uint256);
/// @notice Get list of all assets held by the vault
/// @return Array of ERC-20 token addresses
function getAssets() external view returns (address[] memory);
/// @notice Get the balance of a specific asset in the vault
/// @param asset ERC-20 token address
/// @return Balance of the asset
function getAssetBalance(address asset) external view returns (uint256);
/// @notice Wrap vault shares into transferable governance tokens
/// @dev MUST emit SharesWrapped event
/// @dev MUST revert if shares exceed caller's balance
/// @dev MUST burn vault shares and mint governance tokens
/// @dev Governance token address MUST be set via setGovernanceToken()
/// @param shares Number of vault shares to wrap
/// @return governanceTokens Number of governance tokens minted
function wrapShares(uint256 shares) external returns (uint256 governanceTokens);
/// @notice Unwrap governance tokens back into vault shares
/// @dev MUST emit SharesUnwrapped event
/// @dev MUST revert if governance tokens exceed caller's balance
/// @dev MUST burn governance tokens and mint vault shares
/// @param governanceTokens Number of governance tokens to unwrap
/// @return shares Number of vault shares minted
function unwrapShares(uint256 governanceTokens) external returns (uint256 shares);
/// @notice Get the governance token address
/// @return Address of the governance token (address(0) if not set)
function governanceToken() external view returns (address);
/// @notice Check if shares are transferable
/// @dev MUST return false for standard MAS vaults (non-transferable shares)
/// @return True if shares can be transferred, false otherwise
function sharesTransferable() external view returns (bool);
}-
Share Minting: When assets are deposited, shares MUST be minted based on the total USD value of deposited assets relative to the vault's total value.
If totalShares == 0: shares = depositValueUSD // depositValueUSD is in 18 decimals; 1 share = 1 USD (18 decimals) Else: shares = (depositValueUSD * totalShares) / totalValue -
Share Burning: When shares are withdrawn, the vault MUST return a proportional amount of each asset.
For each asset: withdrawAmount = (shares * assetBalance) / totalShares -
Non-Transferable: Vault shares MUST NOT be transferable by default. This prevents speculation and ensures shares represent actual vault ownership.
-
Governance Token Wrapping: Users MAY wrap their non-transferable shares into transferable governance tokens at a 1:1 USD value ratio. The governance token address MUST be set by the vault owner.
-
Asset Management: Vaults MUST support holding multiple ERC-20 tokens simultaneously.
-
Valuation: All asset values MUST be calculated in USD using oracle price feeds (e.g., Chainlink).
-
Proportional Withdrawals: When withdrawing, users MUST receive a proportional share of ALL vault assets, not just a single asset.
-
Diamond Standard (Recommended): MAS-compliant vaults SHOULD implement ERC-2535 (Diamond) to support modular strategy facets. This enables:
- Adding new strategy facets via
diamondCut() - Removing strategy facets via
diamondCut() - Upgrading strategies without redeploying the vault
- Adding new strategy facets via
-
Alternative Implementations: Simple MAS implementations MAY use a single contract with hardcoded strategies for basic use cases.
-
Common Facet Types (for Diamond-based implementations):
- Deposit/Withdraw facets (core functionality)
- Rebalance facets (portfolio management)
- Yield facets (lending protocols, LST, restaking)
- Risk management facets (stop-loss, drawdown protection)
- Governance facets (voting, proposals)
-
Ownership: Each MAS-compliant vault MUST have a single owner (ERC-173).
-
Access Control: Only the vault owner MUST be able to:
- Add/remove facets
- Update strategy parameters
- Set governance token address
- Transfer vault ownership
-
Reentrancy Protection: All state-changing functions MUST be protected against reentrancy attacks.
-
Oracle Security: Price feeds MUST include staleness checks and circuit breakers.
-
ERC-165 Interface Detection: Contracts SHOULD implement ERC-165 to allow interface detection.
-
Batch Operations: Contracts MAY implement batch deposit/withdraw functions for gas optimization.
-
Emergency Pause: Contracts MAY implement emergency pause functionality for security incidents.
Self-Custody: Personal vaults eliminate custody risk. Users maintain full control of their assets and can transfer vault ownership at any time.
Customization: Each user can configure their own risk parameters, strategy mix, and asset allocation without affecting others.
Multi-Asset Portfolios: Unlike ERC-4626 which requires a single underlying asset, MAS supports complex multi-asset strategies (e.g., 40% WBTC + 40% WETH + 20% PAXG).
Regulatory Compliance: Personal vaults may have different regulatory treatment than pooled funds in some jurisdictions.
Prevent Speculation: Non-transferable shares ensure the vault is used for yield generation, not speculation.
Simplify Accounting: Without transfers, share accounting is simpler and more gas-efficient.
Governance Separation: Users who want transferability can wrap shares into governance tokens, separating utility from governance.
Modularity: Facets allow users to add/remove strategies without redeploying the entire vault.
Upgradeability: New yield strategies can be added as they become available (e.g., new LST protocols, restaking opportunities).
Gas Efficiency: Diamond pattern allows large contracts to be split into smaller facets, avoiding contract size limits.
Composability: Facets from different developers can be combined, creating an ecosystem of reusable strategy modules.
Multi-Asset Comparison: USD provides a common denominator for valuing different assets (BTC, ETH, Gold).
Share Calculation: USD-based shares ensure fair minting/burning regardless of which assets are deposited/withdrawn.
Governance Token Parity: 1 governance token = 1 USD worth of shares provides clear, stable voting power.
This EIP introduces a new standard and does not break compatibility with existing standards.
Relationship to ERC-4626: MAS is NOT compatible with ERC-4626 due to fundamental differences:
- ERC-4626: Single asset, pooled vault, transferable shares
- MAS-compliant vaults: Multi-asset, personal vault, non-transferable shares
Projects using ERC-4626 cannot directly migrate to MAS without architectural changes.
Relationship to ERC-2535: MAS-compliant vaults SHOULD implement ERC-2535 (Diamond) for maximum modularity and upgradeability. However, the core IERC_MAS interface can also be implemented in simpler single-contract architectures for basic use cases (see MinimalMAS reference implementation).
Relationship to ERC-20: Governance tokens wrapped from MAS vault shares SHOULD implement ERC-20 for maximum compatibility with existing DeFi protocols.
Test cases are available in the reference implementation repository:
test/erc-MAS/compliance.spec.ts- Comprehensive MAS compliance tests for the MinimalMAS reference implementation (33 tests covering all interface functions, share accounting, multi-asset operations, governance wrapping, security, and edge cases)test/erc-MAS/diamond-compliance.spec.ts- MAS compliance tests for the Diamond-based example implementation (34 tests covering interface compliance, share accounting, multi-asset support, governance token wrapping, security, and edge cases)
Two reference implementations are available at: example implementation
A minimal single-contract implementation demonstrating the core IERC_MAS interface:
contracts/examples/MinimalMAS.sol- Complete MAS vault implementation in a single contractcontracts/interfaces/IERC_MAS.sol- Standard interface definition
Use Case: Educational reference, simple vaults without modular strategies.
A full-featured Diamond-based implementation with modular strategies:
contracts/Diamond.sol- Diamond proxy implementationcontracts/facets/basic/DepositWithdrawFacet.sol- Core deposit/withdraw logiccontracts/facets/basic/RebalanceFacet.sol- Portfolio rebalancingcontracts/facets/governance/WrapUnwrapFacet.sol- Governance token wrappingcontracts/tokens/GovernanceToken.sol- Non-transferable governance tokencontracts/libraries/LibAppStorage.sol- Shared storage pattern
Use Case: Deployments requiring modularity, upgradeability, and advanced strategies.
Note: The Diamond implementation includes additional features beyond the minimal MAS interface described above (e.g., APEX inverse volatility strategy, fee discounts, treasury buyback).
Risk: Malicious or compromised price oracles could allow attackers to mint shares at incorrect valuations.
Mitigation:
- Use multiple oracle sources (e.g., Chainlink + Uniswap TWAP)
- Implement price deviation checks (reject prices that differ >X% from previous)
- Add staleness checks (reject prices older than Y minutes)
- Use circuit breakers to pause deposits during extreme volatility
Risk: External calls to ERC-20 tokens during deposit/withdraw could enable reentrancy.
Mitigation:
- Use checks-effects-interactions pattern
- Implement reentrancy guards on all state-changing functions
- Update internal state before external calls
Risk: Malicious vault owner could add a facet that steals user funds.
Mitigation:
- Users MUST trust the vault owner (it's their own vault)
- For shared vaults, implement timelock + multisig for facet changes
- Emit events for all facet additions/removals
- Consider immutable core facets for critical functionality
Risk: First depositor could manipulate share price by depositing 1 wei and donating large amounts.
Mitigation:
- Require minimum initial deposit (e.g., 1000 USD)
- Use virtual shares/assets (similar to ERC-4626 mitigation)
- Implement share price bounds checks
Risk: If governance token is compromised, attackers could mint unlimited tokens.
Mitigation:
- Governance token MUST only be mintable by the MAS vault contract
- Implement strict access controls on wrap/unwrap functions
- Consider making governance token address immutable after initial set
Risk: If one asset becomes illiquid, users cannot withdraw their proportional share.
Mitigation:
- Implement emergency withdrawal mode (withdraw available assets only)
- Allow partial withdrawals of specific assets
- Monitor asset liquidity and pause deposits if needed
Copyright and related rights waived via CC0.