EIP3475 - Multiple Callable Bonds
# Simple Summary
A standard interface for contract, that manage multiple callable bonds. A single contract includes any given number of bond classes, bond nonce, bond balance of an address. This standard provides independent functions to read, transfer any collection of bonds, as well as allow bonds to be redeemed from the bond issuer if certain conditions are met. This token standard can replace current ERC20 LP token. ERC-3475 has more complex data structure, which will allow the LP token to store more information, and allow the developer to build more sophisticated logic for the redemption and reward system of the DEFI project in question.
# Abstract
This API standard allows for the creation of any number of bonds type in a single contract. Existing LP token standards like ERC-20 require deployment of separate factory and token contracts per token type. The need of issuing bonds with multiple redemption data can’t be achieved with existing token standards. ERC-3475 Multiple Callable Bonds Standard allows for each bond class ID to represent a new configurable token type, and for each bond nonce to represent an issuing date or any other forms of data in uint256. Every single nonce of a bond class may have its own metadata, supply and other redemption conditions.
# Motivation
Current LP token is a simple ERC-20 token, which has not much complicity in data structure. To allow more complex reward and redemption logic to be built, we need a new LP token standard that can manage multiple bonds, stores much more data and gas efficient. ERC-3475 standard interface allows any tokens on solidity compatible block chains to create its own bond. These bonds with the same interface standard can be exchanged in secondary market. And it allows any 3rd party wallet applications or exchanges to read the balance and the redemption conditions of these tokens. ERC-3475 bonds can also be packed into separate packages. Those packages can in their turn be divided and exchanged in a secondary market.
New functions built in ERC-3475 Multiple Callable Bonds Standard, will allow the users to economize their gas fee spend. Trading and burning of ERC-3475 Bonds will also multiply tokens market cap, helping it to recover from recession period(1) (opens new window). Existing structures, such as AMM exchanges or lending platform can be updated to recognize ERC-3475 Bonds.
# Data structure
pragma solidity ^0.6.2;
import '@DeBond-Protocol/EIP-3475/blob/main/contracts/util/ERC3475data.sol';
2
"_balances"
Is the array of bond class to a nonce, and to the balance. mapping(address =>mapping(uint256=> uint256)) \_balances;
is the balance of an address are associated with the following 3d array: B (a,c,n)
, where a is the vector of address, c
is the vector of class and n
is the vector of nonce.
e.g.
0x2d03B6C79B75eE7aB35298878D05fe36DC1fE8Ef =>(1 =>(5 => 500000000)); this example gives the balance of: address 0x2d03B6C79B75eE7aB35298878D05fe36DC1fE8Ef possess 500000000 of bond class 1, bond nonce 5.
mapping (address => mapping( uint256 =>mapping(uint256=> uint256))) private _balances;
"_totalSupply"
Is the mapping from bond class and nonce to the total active supply.
**total supply = total supply +burned supply +redeemed supply.
3D array of bond class => (bond nonce => bond total supply).
e.g.
1 =>(5 => 25000000000); this example gives the total supply of: bond class 1, bond nonce 5 has a total supply of 25000000000 .
mapping (uint256 => mapping(uint256 => uint256)) private _activeSupply;
"_activeSupply"`Is the mapping from bond class and nonce to the total active supply.
**Active supply = total supply - burned supply - redeemed supply.
3D array of bond class => (bond nonce => bond active supply).
e.g.
1 =>(5 => 25000000000); this example gives the total active supply of: bond class 1, bond nonce 5 has a total active supply of 25000000000 .
mapping (uint256 => mapping(uint256 => uint256)) private _activeSupply;
"_burnedSupply"
Is the mapping from bond class and nonce to the total burned supply.
**Burned supply = total supply - active supply - redeemed supply.
3D array of bond class => (bond nonce => bond burned supply).
e.g.
1 =>(5 => 0); this example gives the total burned supply of: bond class 1, bond nonce 5 has a total burned supply of 0 .
mapping (uint256 => mapping(uint256 => uint256)) private _burnedSupply;
"_redeemedSupply"
Is the mapping from bond class and nonce to the total active supply.
*** redeemed supply = total supply - active supply - burned supply.*
3D array of bond class => (bond nonce => bond redeemed supply).
e.g.
1 =>(5 => 5000000000); this example gives the total redeemed supply of: bond class 1, bond nonce 5 has a total redeemed supply of 5000000000 .
mapping (uint256 => mapping(uint256 => uint256)) private _redeemedSupply;
"_nonceInfo"
is the mapping from a bond nonce to a list of uint256 variables, list of bond nonce to bond info. In this list, the 0th variable MUST be the starting time of the nonce in question. The 1st variable MUST be the maturity time of the nonce in question. Other variables can be described bybondClass[class].nonceInfoDiscription[];
and defined by code.
e.g. ["1615584000","1616584000",(uint256)...]
**this example gives the starting time of the bond nonce of the bond class, which is 1615584000; the maturity time, 1616584000...
mapping ( uint256 =>mapping(uint256=> string[])) private _nonceInfo;
# Specification
pragma solidity ^0.6.2;
import '@DeBond-Protocol/EIP-3475/blob/main/contracts/util/IERC3475.sol';
2
# totalSupply()
"totalSupply()"
allows anyone to read the total supply of a given class nonce and bond nonce, this include burned and redeemed Supply
The "class"
is the class nonce of bond, the first bond class created will be 0, and so on.
The "nonce"
is the nonce of the bond. This param is for distinctions of the issuing conditions of the bond.
Returns the active supply of the bond in question. — e.g. "5821200000000"
.
function totalSupply( uint256 class, uint256 nonce) external view returns (uint256);
activeSupply()
"activeSupply()"
allows anyone to read the non-burned and non-redeemed Supply of a given class nonce and bond nonce.
The "class"
is the class nonce of bond, the first bond class created will be 0, and so on.
The "nonce"
is the nonce of the bond. This param is for distinctions of the issuing conditions of the bond.
Returns the active supply of the bond in question. — e.g. "5821200000000"
.
function activeSupply( uint256 class, uint256 nonce) external view returns (uint256);
# burnedSupply()
"burnedSupply()"
allows anyone to read the redeemed Supply of a given class and bond nonce.
The "class"
is the class nonce of bond, the first bond class created will be 0, and so on.
The "nonce"
is the nonce of the bond. This param is for distinctions of the issuing conditions of the bond.
Returns the active supply of the bond in question. — e.g. "612300000000"
.
function burnedSupply( uint256 class, uint256 nonce) external view returns (uint256);
# redeemedSupply**()**
"redeemedSupply()"
allows anyone to read the redeemed Supply of a given class and bond nonce.
The "class"
is the class nonce of bond, the first bond class created will be 0, and so on.
The "nonce"
is the nonce of the bond. This param is for distinctions of the issuing conditions of the bond.
Returns the active supply of the bond in question. — e.g. "612300000000"
.
function redeemedSupply( uint256 class, uint256 nonce) external view returns (uint256);
# balanceOf()
"balanceOf()"
allows anyone to read the remaining balance of an address. This will only return the balance of a single bond class and bond date nonce.
The "account"
is the address of the token holder.
The "class"
is the class nonce of bond, the first bond class created will be 0, and so on.
The "nonce"
is the nonce of the bond. This param is for distinctions of the issuing conditions of the bond.
Returns the balance of the giving bond class and bond nonce. — e.g. "571300000000"
.
function balanceOf(address account, uint256 class, uint256 nonce) external view returns (uint256);
# getBondSymbol()
"getBondSymbol()"
allows anyone to read the symbol of a bond class.
The "class"
is the class nonce of bond, the first bond class created will be 0, and so on.
The "nonce"
is the nonce of the bond. This param is for distinctions of the issuing conditions of the bond.
Returns the symbol string of the bond class. — e.g. bond symbol="DEBIT-BUSD bond"
.**DEBIT as the first half of the bond symbol represents the settlement token of the bond. BUSD as the second half of the bond symbol represents the token used for the perches of this bond. If the bond have more than one settlement token or buying token, the symbol should be "Token1,Token2-Token3,Token4 bond"
function getBondSymbol(uint256 class) external view returns (uint256);
# getBondInfo()
"getBondInfo()"
allows anyone to read the information of a bond nonce.
The "class"
is the class nonce of bond, the first bond class created will be 0, and so on.
The "nonce"
is the nonce of the bond. This param is for distinctions of the issuing conditions of the bond.
Returns the bond symbol and a list of uint256 parameters of a bond nonce. — e.g. ["DEBIT-BUSD","1615584000",(3rd uint256)...]
.*** Every bond contract can have their own list. But the first uint256 in the list MUST be the UTC time code of the issuing time.*
function getBondInfo(uint256 class, uint256 nonce) external view returns (string memory BondSymbol, uint256 timestamp, uint256 info2, uint256 info3, uint256 info4, uint256 info5,uint256 info6);
# bondIsRedeemable()
"bondIsRedeemble()"
allows anyone to check if a bond is redeemable.*** the conditions of redemption can be speechified with one or several internal functions.*
The "class"
is the class nonce of bond, the first bond class created will be 0, and so on.
The "nonce"
is the nonce of the bond. This param is for distinctions of the issuing conditions of the bond.
Returns "true"
if the cited bond is redeemable. and "false"
if is not.
function bondIsRedeemable(uint256 class, uint256 nonce) external view returns (bool);
# issueBond()
"issueBond()"
allows issuing any number of bond types to an address.
The calling of this function needs to be restricted to bond issuer contract.
The "_to"
is the address to which the bond will be issued.
The "class"
is the class nonce of bond, the first bond class created will be 0, and so on.
The "_amount"
is the amount of the bond, that "_to"
address will receive.
e.g.
issueBond(0x2d03B6C79B75eE7aB35298878D05fe36DC1fE8Ef,0,1000);
those input mean“ issuing to wallet address, 1000of bond class 0." Returns a bool.
"true"
if the bond are issued. and "false"
if are not.
function issueBond(address _to, uint256 class, uint256 _amount) external returns(bool);
# redeemBond()
"redeemBond()"
allows redemption of any number of bond types from an address.
The calling of this function needs to be restricted to bond issuer contract.
The "_from"
is the address from which the bond will be redeemed.
The "class"
is the class nonce of bond, the first bond class created will be 0, and so on.
The "nonce"
is the list of nonce of the given bond class. This param is for distinctions of the issuing conditions of the bond.
The "_amount"
is the list of amount of the bond, that "_from"
address will redeem.
e.g.
redeemBond(0x2d03B6C79B75eE7aB35298878D05fe36DC1fE8Ef, [1,2,4], [42,61,25][500000000,60000000,150000000]);
those input mean “redeem from wallet address(0x2d03B6C79B75eE7aB35298878D05fe36DC1fE8Ef), 500000000 of bond class1 nonce 42, 60000000 of bond class2 nonce 61, 150000000 of bond class3 nonce 25.
Returns a bool
"true"
if the bond are redeemed and "false"
if are not.
function redeemBond(address _from, uint256 class, uint256[] calldata nonce, uint256[] calldata _amount) external returns(bool);
# transferBond()
"transferBond()"
allows the transfer of any number of bond types from an address to another.
The"_from"
argument is the address of the holder whose balance about to decrees.
The "_to"
argument is the address of the recipient whose balance is about to increased.
The "class"
is the list of class nonce of bond, the first bond class created will be 0, and so on.
The "nonce"
is the list of nonce of the given bond class. This param is for distinctions of the issuing conditions of the bond.
The "_amount"
is the list of amount of the bond, that will be transferred from "_from"
address to "_to"
address.
$$ \left{ \begin{aligned} B \left(a_1, c, n\right):=B \left(a_1, c, n\right) -_amount,\ B \left(a_2, c, n\right):=B \left(a_2, c, n\right) +_amount,\ if _amount\leq B \left(a_1, c, n\right) \end{aligned} \right. $$
$$ \left{ \begin{aligned} B \left(a_1, c, n\right):=B \left(a_1, c, n\right),\ B \left(a_2, c, n\right):=B \left(a_2, c, n\right),\ if _amount>B \left(a_1, c, n\right) \end{aligned} \right. $$
e.g.
transferBond(0x2d03B6C79B75eE7aB35298878D05fe36DC1fE8Ef,0x82a55a613429Aeb3D01fbE6841bE1AcA4fFD5b2B, [1,2,4], [42,61,25], [500000000,60000000,150000000]);
This example shows the transfer from _from
address, to \_to
address, 500000000
of bond class1
nonce 42
, 60000000
of bond class 2
nonce 61
, 150000000
of bond class 3
nonce 25
.
Returns a bool.
"true"
if passed.
**If one transaction in the group is failed the function will revert all passed transactions. *
function transferBond(address _from, address _to, uint256[] calldata class, uint256[] calldata nonce, uint256[] calldata _amount) external returns(bool);
# burnBond()
"burnBond()"
allows the transfer of any number of bond types from an address to another.
The"_from"
argument is the address of the holder whose balance about to decrees.
The "class"
is the list of class nonce of bond, the first bond class created will be 0, and so on.
The "nonce"
is the list of nonce of the given bond class. This param is for distinctions of the issuing conditions of the bond.
The "_amount"
is the list of amount of the bond, that will be transferred from "_from"
address to "_to"
address.
e.g.
burnBond(0x82a55a613429Aeb3D01fbE6841bE1AcA4fFD5b2B, [1,2,4],[42,61,25], [500000000,60000000,150000000]);
those input mean “ burn, from "_from"
address, to "_to"
address, 500000000 of bond class1 nonce 42, 60000000 of bond class2 nonce 61, 150000000 of bond class3 nonce 25.
Returns a bool.
"true"
if passed.
**If one transaction in the group is failed the function will revert all passed transactions. *
function transferBond(address _from, address _to, uint256[] calldata class, uint256[] calldata nonce, uint256[] calldata _amount) external returns(bool);
# EVENT
"eventIssueBond"
MUST trigger when Bonds are issued. This SHOULD not include zero value Issuing.
e.g.
"issue by address(operator) 500 DEBIT-USD Bond(Nonce14) to 0x2d03B6C79B75eE7aB35298878D05fe36DC1fE8Ef"
event eventIssueBond(address _operator, address _to, uint256 class, uint256 nonce, uint256 _amount);
"eventRedeemBond"
MUST trigger when Bonds are redeemed. This SHOULD not include zero value redemption. When burn a bond MUST not create this event(Use"eventBurnBond"
instead).
e.g.
"redeem by address(_operator) 500 DEBIT-USD Bond(Nonce14) to 0x2d03B6C79B75eE7aB35298878D05fe36DC1fE8Ef"
event eventRedeemBond(address _operator, address _from, uint256 class, uint256 nonce, uint256 _amount);
"eventBurnBond"
MUST trigger when Bonds are burned. This SHOULD not include zero value burning. When redeem a bond MUST not create this event(Use"event redeemBond"
instead).
e.g.
"burn by address(_operator) 500 DEBIT-USD Bond(Nonce14) from 0x2d03B6C79B75eE7aB35298878D05fe36DC1fE8Ef"
event eventBurnBond(address _operator, address _from, uint256 class, uint256 nonce, uint256 _amount);
"eventTransferBond"
MUST trigger when Bonds are transferred. This SHOULD not include zero value transfers. Transfer event with the _from
0x0
MUST not create this event(Use"event issueBond"
instead ). Transfer event with the _to
0x0
MUST not create this event(Use"event redeemBond"
when redemption, and "event burnBond"
when burning).
e.g.
"transfer by address(_operator) 500 DEBIT-USD Bond(Nonce14) from 0x2d03B6C79B75eE7aB35298878D05fe36DC1fE8Ef, to address(_to)"
event eventTransferBond(address _operator, address _from, address _to, uint256 class, uint256 nonce, uint256 _amount);
# Backwards Compatibility
ERC-3475 contract is not Compatible with contracts that don't have an ERC-3475 interface built in. This requires the existing contract to upgrade with ERC-3475 interface. The receiving of ERC-3475 Bond need the implementation of ERC3475 interface in the receiver contract.
However any existing ERC-20 token contract can issue their ERC-3475 bond, by giving the minting role to a bonk contract with ERC-3475 interface built in. The implementation of This can be found in our Use Cases.
To ensure the reading of transactions, "eventIssueBond"
,"eventRedeemBond"
,"eventBurnBond"
,"eventTransferBond"
, Events cited above MUST be emitted when such transaction is passed.
Note that the ERC 3475 interface is also compatible with ERC-20 and ERC-721 interface . But the creation of a separated bank contract is recommended for reading and future upgrade needs and .
The issuing of ERC-3475 bonds is not limited to ERC-20 token. Standard like ERC-721 nonfungible token can also issue their bond with the help of ERC-3475 interface.
Any ERC3475 bond or any hybrid of ERC-20 and ERC-721 contract can be used as the collateral for another ERC-3475 bond. This allows the market to create bond represent no longer a single type of ERC-20 token. Bond can now represent a collection of collaterals in ERC-20 token, ERC-721 nonfungible token and ERC-3475 Multiple callable bonds...
# Use Cases
https://github.com/sgmfinance/erc-3475
**This demonstration show only a simple application of ERC3475 Multiple Callable Bonds Standard. Developers can build much sophisticated logic with this interface. No function used in the example serves as a guild-line.
# Reference
https://eips.ethereum.org/EIPS/eip-20
https://eips.ethereum.org/EIPS/eip-721
# Security Considerations
There are no known security considerations for this EIP. More security considerations will be added after the authoring/feedback process of this EIP.
# Copyright
# License
Copyright and related rights waived via CC0 (opens new window).