Abstract
We propose a new token standard for synthetic assets that are only partially redeemable to their underlying asset, but fully backed by other collateral assets.
The standard defines an interface to mint fractional reserve assets, and a standard to reflect economical risk related data to the token holders and lenders.
Motivation
The Cambrian explosion of new L1s and L2s gave rise to bridged assets which are synthetic by nature. Indeed, ETH on Arbitrum L2, or WETH on Binance Smart Chain are not fully fungible with their mainnet counterpart. However, these assets are fully backed by their mainnet counterpart and guaranteed to be redeemable to their mainnet underlying asset, albeit with certain time delay.
Fractional reserve tokens can allow an ecosystem (chains, L2s, and other networks of economic activity) to increase its supply by allowing users to mint the asset not only by bridging it to the ecosystem, but also by borrowing it (typically against a collateral).
As an example, consider a fractional reserve token, namely, frDAI, that represents a synthetic DAI.
Such token will allow users to mint 1 frDAI upon deposit of 1 DAI, or by providing a collateral that worth more than 1 DAI.
Quick redemption of frDAI to DAI is available as long as there is still some DAI balance in the frDAI token, and otherwise, the price of frDAI may temporarily fluctuate until borrowers repay their debt.
Fractional reserve tokens may delegate minting capabilities for multiple risk curators and lending markets. Hence, a uniform standard for fractional reserve minting is needed.
Fractional reserve banking does not come without risks, such as insolvency or a bank run.
This standard does not aim to dictate economic risk management practices, but rather to have a standard on how to reflect the risk to token holders.
Specification
The proposed standard has the following requirements:
- MUST be ERC-20 compatible.
Interface
interface IERCXXX is IERC20 {
// events
event MintFractionalReserve(address indexed minter, address to, uint256 amount);
event BurnFractionalReserve(address indexed burner, address from, uint256 amount);
event SetSegregatedAccount(address account, bool segregated);
// functions
// setters
function fractionalReserveMint(address _to, uint256 _amount) external;
function fractionalReserveBurn(address _from, uint256 _amount) external;
// getters
function totalBorrowedSupply() external view returns (uint256);
function requiredReserveRatio() external view returns (uint256);
function segregatedAccount(address _account) external view returns (bool);
function totalSegregatedSupply() external view returns (uint256);
}
Reserve ratio
The reserve ratio reflects the ratio between the token that is available as cash, i.e., available for an immediate redemption (or alternatively, a token that was not minted via a fractional reserve minting), and the total supply of the token. Segregated accounts MUST be subtracted from the cash balance.
Lower reserve ratio gives rise to higher capital efficiency, however it increases the likelihood of depeg or a run on the bank, where token holders cannot immediately redeem their synthetic token.
Formally, the reserve ratio is denoted by $$\frac{totalSupply() - totalBorrowedSupply() - \sum_{a \in \text{Segregated Accounts}} \text{balanceOf}(a)}{totalSupply()}$$.
Additional fractional reserve minting MUST NOT occur when the reserve ratio, multiplied by 1e18
is lower than requiredReserveRatio()
.
Mint and burn functionality
The fractionalReserveMint
and fractionalReserveBurn
functions SHOULD be called by permissioned addresses, e.g., risk curators or lending markets. These entities SHOULD mint new tokens only to addresses that already locked collateral in a dedicated contract.
The reserve ratio is denoted by $$\frac{totalSupply() - \sum_{a \in \text{Segregated Accounts}} \text{balanceOf}(a)}{totalSupply() + totalBorrowedSupply()}$$.
fractionalReserveMint
MUST revert if the reserve ratio, multiplied by e18
exceeds requiredReserveRatio()
.
A successful call to fractionalReserveMint(_to, _amount)
MUST increase the value of totalSupply()
, totalBorrowedSupply()
, and the token balance of address _to
, by _amount
units.
A call to fractionalReserveMint
MUST emit a MintFractionalReserve
event.
A call to fractionalReserveMint
MUST revert if after the mint the reserve ratio, multiplied by 1e18
exceeds the value of requiredReserveRatio()
.
Similarly, a successful call to fractionalReserveBurn(_from, _amount)
MUST decrease the value of totalSupply()
,totalBorrowedSupply()
, and the token balance of address _from
by _amount
units.
A call to fractionalReserveBurn
MUST emit a BurnFractionalReserve
event.
Segregated accounts
Increasing the total supply could be a concern if a token is used for DAO votes and/or if dividends are distributed to token holders.
In order to mitigate such concerns, segregated accounts are introduced, with the premise that money in these accounts is not counted towards the reserve, and therefore, additional token supply cannot be minted against them.
At every point in time, it MUST hold that the sum of token balances for segregated addresses equals to totalSegregatedSupply()
.
Account balance
The fractionalReserveMint
SHOULD be used in conjunction with a lending operation, where the minted token is borrowed. The lending operation SHOULD come with an interest rate, and some of the interest rate proceedings SHOULD be distributed to token holders that are not in segregated accounts.
This standard does not dictate how distribution should occur.
Rationale
The proposed standard aims to standardise how multiple lending markets and risk providers can interact with a fractional reserve token. The actual lending operation should be done carefully by trusted entities, and it is the token owner’s responsibility to make sure the parties who have fractional reserve minting credentials are reliable.
At the core of the coordination relies the need to understand how much additional supply is available for borrow, and at what interest rate. The additional borrowable supply is deduced from the required reserve ratio, and the total, borrowable and segregated supply.
The interest rate SHOULD be monotonically increasing with the current reserve ratio.
The standard does not dictate how the accrued interest rate is distributed. One possible distribution is by making the token a rebased token. An alternative way is to introduce staking, or just airdropping of proceeds.
While a fractional reserve is most useful when it is backed by a known asset, e.g., frDAI and DAI, it can also be used in isolation. In such a case, a token will have a fixed initial supply, however additional supply can be borrowed. In such cases the supply temporarily increases, but the net holdings (totalSupply() - totalBorrowedSupply()
) remains unchanged.
Backwards Compatibility
Fractional reserve tokens should be backwards compatible with ERC-20.
Reference Implementation
// The code below is provided only for illustration, DO NOT use it in production
contract FractionalReserveToken is ERC20, Ownable {
event MintFractionalReserve(address indexed minter, address to, uint256 amount);
event BurnFractionalReserve(address indexed burner, address from, uint256 amount);
event SetSegregatedAccount(address account, bool segregated);
/// @notice token supply in these accounts is not counted towards the reserve, and
/// therefore, additional token supply cannot be minted against them.
mapping(address => bool) public segregatedAccount;
/// @notice ratio between the token that is available as cash (immediate redemption)
/// and the total supply of the token.
uint256 public requiredReserveRatio;
uint256 public totalBorrowedSupply;
constructor(
string memory _name,
string memory _symbol
) ERC20(_name, _symbol) Ownable(msg.sender) {}
function fractionalReserveMint(address to, uint256 amount) external onlyOwner {
_mint(to, amount);
totalBorrowedSupply += amount;
emit MintFractionalReserve(msg.sender, to, amount);
uint256 reserveRatio = (totalSupply() - totalBorrowedSupply - segregatedSupply) * 1e18 / totalSupply();
require(reserveRatio >= requiredReserveRatio, "reserveRatio");
}
function fractionalReserveBurn(address from, uint256 amount) external onlyOwner {
_burn(from, amount);
totalBorrowedSupply -= amount;
emit BurnFractionalReserve(msg.sender, from, amount);
}
// ------------------------------------------------------------------------------
// Code below is not part of the proposed standard
// ------------------------------------------------------------------------------
uint256 internal segregatedSupply; // supply of segregated tokens
function _update(address from, address to, uint256 value) internal override {
// keep the reserve up to date on transfers
if (!segregatedAccount[from] && segregatedAccount[to]) {
segregatedSupply += value;
}
if (segregatedAccount[from] && !segregatedAccount[to]) {
segregatedSupply -= value;
}
ERC20._update(from, to, value);
}
function mint(address account, uint256 value) external onlyOwner {
_mint(account, value);
}
function burn(address account, uint256 value) external onlyOwner {
_burn(account, value);
}
function setSegregatedAccount(address account, bool segregated) external onlyOwner {
if (segregated) {
require(!segregatedAccount[account], "segregated");
segregatedSupply += balanceOf(account);
} else {
require(segregatedAccount[account], "!segregated");
segregatedSupply -= balanceOf(account);
}
segregatedAccount[account] = segregated;
emit SetSegregatedAccount(account, segregated);
}
function setRequiredReserveRatio(uint256 value) external onlyOwner {
requiredReserveRatio = value;
}
}
Security Considerations
Fractional reserve banking comes with many economic risks. This standard does not aim to provide guidelines on how to properly mitigate them.
Copyright
Copyright and related rights waived via CC0.