This document describes a tokenized reserve mechanism. This includes functionality that allow stakeholders to participate in goverance stategies wthin the reserve.
Tokenized vaults store ERC-20 tokens that are represented by shares within vault smart contracts. Implementations can follow the ERC-4626 standard to provide basic functionality for depositing, withdrawing, and reading balances for a vault. As tokenization becomes increasingly popular, there is a need for standard for how these vaults holding tokenized assets are audited. Applications could use tokenized vaults to store assets while allowing other interseted parties to purpose, restrict and track the events of the vault.
This specification introduces a standard for an on-chain reserve that uses utilizes active stakeholders. Core functionality, which is an extension of ERC-4626, will allow stakehoolder representation in the form of depositing and withdrawing. The record of asset activity, represented by the underlying ERC-20 token, wiil be easily accessible by party.
In a tokenized reserve, stakeholders mint shares from the vault when depositing the underlying token. The goal is to create a reserve similar to a real-world reserve fund used as a contingency for an entity. In the real world an entity agrees on some condition that would justify the use of funds, like when running low on regular funds. In a decentralized environment, an entity could incorporate stakeholders and replicate this mechanism. Assets associated with the reserve, including its origin and destination will vary in decentralized environments, so transparent auditing is needed.
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.
- owner: The creator of the reserve
- user: Stakeholders participating in proposals
- reserve: The assets held on the contract excluding the ERC4626 underlying token
- proposal: Created by reserve owners or stakeholder to request withdrawals
- name: ERC-20 token name
- ticker: ERC-20 ticker
- asset: ERC-4626 underlying ERC-20 address
- rAuth: Authorized user, for cases utilizing more than one owner/ limiting owner withdrawals
- rOwner: Owner of the Reserve
The reserve will account for activity by all participants.
Some criteria created by the owner is REQUIRED to onboard new stakeholders with the underlying asset of the ERC-4626 vault.
Each new proposal is recorded and its information SHOULD be retrievable by a call function.
The REQUIRED information includes the amount and address for a proposal.
A proposal MUST be open before the transfer of any tokens out of the reserve.
A proposalOpen MAY be requested by stakeholders or the owner.
Once opened a stakeholder MUST use proposalDeposit to represent support for the proposal.
import "./ERC4626.sol";
interface TokenizedReserve is ERC4626{
/**
* @dev Event emitted after a new policy is created
*/
event policies(
address indexed token,
uint256 indexed policyNum,
uint256 indexed amount,
address recipient
);
/**
* @dev Event emitted after a new deposit is made by the owner
*/
event depositR(
address indexed token,
uint256 indexed amount,
uint256 indexed time,
uint256 count
);
/**
* @dev Get time a deposit/withdrawal was made by the owner
* @param count Number for deposit count
* @return block.timestamp format
*/
function ownerTime(uint256 count) external view returns (uint256);
/**
* @dev Get amount deposited to reserve by owner
* @param count Number for deposit count
* @param proposal The proposal number to deposit to
* @return uint256 Amount of an asset that was deposited
*/
function ownerDeposit(uint256 count, uint256 proposal) external view returns(uint256);
/**
* @dev Amount withdrawn for a opened proposal by the owner
* @param propoal The proposal number
* @return Amount of ERC20
*/
function ownerWithdrawals(uint256 proposal) external view returns(uint256);
/**
* @dev Token type deposited to reserve by the owner
* - MUST be an address of ERC20 token
* @param count Number of deposit count
* @return address Address of ERC20 token
*/
function tokenDeposit(uint256 count) external view returns(address);
/**
* @dev Token type withdrawn for an opened proposal by the owner
* - MUST be ERC20 address
* @param proposal The proposal number for the token used
* @return Token ERC20 address
*/
function tokenWithdrawal(uint256 proposal) external view returns(address);
/**
* @dev User amount deposited to a proposal for shares
* - MUST be an ERC20 token
* @param user Address of user
* @param proposal The proposal number the user deposited to
* @return uint256 Amount of ERC20 deposited
*/
function userDeposit(address user, uint256 proposal) external view returns(uint256);
/**
* @dev Amount withdrawn from a proposal by the user
* @param user The address of user
* @param proposal The proposal number for user withdrawal
* @param uint256 Amount of ERC20
*/
function userWithdrawals(address user, uint256 proposal) public view returns(uint256);
/**
* @dev Make a deposit to a proposal creating new shares using deposit() function from ERC4626
* - MUST be opened proposal
* - MUST NOT be opened proposal that was closed
* - SHOULD be only method to deposit to ERC4626 vault
* NOTE: using the deposit() will cause assets to not be accounted for in a proposal (see Security Considerations section)
* @param assets Amount being deposited
* @param receiver Address of depositor
* @param proposal The number associated proposal
* @return Amount of shares minted
*/
function proposalDeposit(uint256 assets, address receiver, uint256 proposal) external returns(uint256);
/**
* @dev Burn shares, receive 1 to 1 value of shares using withdraw() function from ERC4626
* - MUST have userDeposit greater than or equal to userWithdrawal
* - SHOULD be only method for withdrawing underlying token from ERC4626 vault
* @param assets Amount being deposited
* @param receiver Address of receiver
* @param owner Address of token owner
* @param proposal Number associated proposal
* @return Amount of the asset
*/
function proposalWithdraw(uint256 assets, address receiver, address owner, uint256 proposal)external returns(uint256);
/**
* @dev Issue new policy
* - MUST create new policy number
* - MUST account for amount withdrawn
* - MUST be only method to withdraw ERC20 tokens owned by the reserve (excluding share tokens deposited in a proposal )
* - MUST be owner
* - SHOULD emit proposals event
* @param token Address of ERC-20 token
* @param amount Token amount being withdrawn
* @param receiver Address of token recipient
* @return The proposal number
*/
function openProposal(address token, uint256 amount, address receiver) external returns (uint256);
/**
* @dev Make a deposit and/or close an opened proposal
* - MUST be owner
* - MUST account for amount received
* - SHOULD emit proposals event
* @param token Address of ERC-20 token
* @param proposal Number of the desired proposals
* @param amount Token amount being deposited to the reserve
* @param close Choose to close the proposal
* @return true for closed proposal
*/
function closeProposal(address token, uint256 proposal, uint256 amount, bool close) external returns (bool);
/**
* @dev Accounting for tokens deposited in the reserve
* - MUST be reserve owner
* - SHOULD emit depositR event
* NOTE: No shares are issued, funds can not be redeemed and no proposal is opened. Withdrawnal made with openProposal function.
* @param token Address of ERC-20 token being deposited
* @param sender Address of token sender
* @param amount Token amount being deposited
*/
function depositReserve(address token, address sender, uint256 amount) external;
}
This proposed standard is designed to be a core implementation of a tokenized reserve interface. Other non-specified conditions should be addressed on a case-by-case basis. Each reserve uses ERC-20 standard for shares, and ERC-4626 for the creation of shares. The underlying token MAY be considered to be termed as the reserve token and share token is minted by the ERC-4626 vault. The ERC-4626 standard is used to create shares that represent stakeholder paticitapting in a proposal, thus within the reserve. There MUST be a representation of interested parties in the reserve. The implementer can decide how to treat representation based on users entering and leaving the vault. For example, a stakeholder could not ba able to use the same reserve token for multiple proposals, which could allow shares to be distributed fairly.
Tokenized reserves are made compatible with ERC-20 and ERC-4626.
Tokenized reserves share the same security considerations as ERC-20 and ERC-4626. Including:
asset stored on a contract can be withdrawn by the owner with no restrictions or authorizing party, like requiring an rAuth. Depending on the authorizing implementation, the asset could still be withdrawn by the owner.A RECOMMENDED implementation:
- The openProposal MUST explictly restrict the transfer of the underlying asset.
- If the underlying asset is owned by the reserve and not the vault(within a proposal),
the reserve MUST account for the difference to avoid user asset loss.
Copyright and related rights waived via CC0.