eip: 4626
title: Yield Bearing Vault Standard
description: A standard for yield bearing vaults.
author: Joey Santoro (@joeysantoro), t11s (@transmissions11), Jet Jadeja (@JetJadeja)
discussions-to: https://github.com/ethereum/EIPs/pull/4626
status: Draft
type: Standards Track
category: ERC
created: 2021-12-22
Yield Bearing Vault Standard
Simple Summary
A standard for yield bearing vaults.
Abstract
The following standard allows for the implementation of a standard API for yield bearing vaults within smart contracts. This standard provides basic functionality for depositing and withdrawing tokens and reading balances with an optional extension for tokenized vaults using ERC-20.
Motivation
Yield bearing vaults have a lack of standardization leading to diverse implementation details. Some various examples include lending markets (Compound, Aave, Fuse), aggregators (Yearn, Rari Vaults, Idle), and intrinsically interest bearing tokens (xSushi). This makes integration difficult at the aggregator or plugin layer for protocols which need to conform to many standards. This forces each protocol to implement their own adapters which are error prone and waste development resources.
A standard for yield bearing vaults will allow for a similar cambrian explosion to ERC-20, unlocking access to yield in a variety of applications with little specialized effort from developers.
Specification
Methods
deposit
function deposit(address _to, uint256 _value) public returns (uint256 _shares)
Deposits _value
tokens into the vault and grants ownership of them to _to
.
MAY return a pro-rata ownership _shares
value corresponding _value
, if not MUST return 0
.
MAY represent _shares
using internal accounting or an ERC-20 token.
If pro-rata shares ownership is implemented, the vault SHOULD implement balanceOf
, redeem
, totalSupply
and exchangeRate
.
withdraw
function withdraw(address _to, uint256 _value) public returns (uint256 _shares)
Withdraws _value
tokens from the vault and transfers them to _to
.
MAY return a pro-rata ownership _shares
value corresponding to _value
, if not MUST return 0
.
totalHoldings
function totalHoldings() public view returns (uint256)
Returns the total amount of underlying tokens held/managed by the vault.
balanceOfUnderlying
function balanceOfUnderlying(address _owner) public view returns (uint256)
Returns the total amount underlying tokens held in the vault for _owner
.
underlying
function underlying() public view returns (address)
Returns the address of the token the vault uses for accounting, depositing, and withdrawing.
SHOULD return a token implementing the ERC-20 standard.
totalSupply
function totalSupply() public view returns (uint256)
Returns the total number of unredeemed vault shares in circulation.
OPTIONAL - This method is only needed for vaults that implement a pro-rata share mechanism for deposits.
balanceOf
function balanceOf(address _owner) public view returns (uint256)
Returns the total amount of vault shares the _owner
currently has.
OPTIONAL - This method is only needed for vaults that implement a pro-rata share mechanism for deposits.
redeem
function redeem(address _to, uint256 _shares) public returns (uint256 _value)
Redeems a specific number of _shares
for underlying tokens and transfers them to _to
.
MAY return a pro-rata ownership _shares
value corresponding _value
, if not MUST return 0
.
OPTIONAL - This method is only needed for vaults that implement a pro-rata share mechanism for deposits.
exchangeRate
function exchangeRate() public view returns (uint256)
The amount of underlying tokens one baseUnit
of vault shares is redeemable for.
e.g. _shares * exchangeRate() / baseUnit() = _value
.
exchangeRate() * totalSupply()
MUST equal totalHoldings()
.
OPTIONAL - This method is only needed for vaults that implement a pro-rata share mechanism for deposits.
baseUnit
function baseUnit() public view returns(uint256)
The decimal scalar for vault shares and operations involving exchangeRate()
.
OPTIONAL - This method is only needed for vaults that implement a pro-rata share mechanism for deposits.
Events
Deposit
MUST be emitted when tokens are deposited into the vault.
event Deposit(address indexed _from, addres indexed _to, uint256 _value)
Where _from
is the user who triggered the deposit and approved _value
underlying tokens to the vault, and _to
is the user who is able to withdraw the deposited tokens.
Withdraw
MUST be emitted when tokens are withdrawn from the vault by a depositor.
event Withdraw(address indexed _owner, addres indexed _to, uint256 _value)
Where _from
is the user who triggered the withdrawal and held _value
underlying tokens in the vault, and _to
is the user who received the withdrawn tokens.
Rationale
The vault interface is designed to be optimized for minimal implementation and integration logic while maintaining flexibility for both parties. Details such as accounting and allocation of deposited tokens are intentionally not specified, as vaults are expected to be treated as black boxes on-chain and inspected off-chain before use.
Reference Implementation
Solmate Minimal Implementation - a tokenized vault using the ERC-20 extension with hooks for developers to add logic in deposit and withdraw.
Rari Vaults are an implementation that is nearly ready for production release. Any discrepancies between the vaults abi and this ERC will be adapted to conform to the ERC before mainnet deployment.
Security Considerations
This specification has similar security considerations to the ERC-20 interface. Fully permissionless yield aggregators, for example, could fall prey to malicious implementations which only conform to the interface but not the specification.
Copyright
Copyright and related rights waived via CC0.