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 ERC-20 LP token. ERC-659 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-659 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-659 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-659 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-659 Multiple Callable Bonds Standard, will allow the users to economize their gas fee spend. Trading and burning of ERC-659 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-659 Bonds.
# Specification
pragma solidity ^0.6.2;
import '@sgmfinance/erc-659/blob/main/contracts/util/IERC659.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="SASH-BUSD bond"
.**SASH 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. ["SASH-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.
e.g.
transferBond(0x2d03B6C79B75eE7aB35298878D05fe36DC1fE8Ef,0x82a55a613429Aeb3D01fbE6841bE1AcA4fFD5b2B, [1,2,4], [42,61,25], [500000000,60000000,150000000]);
those input mean “ transfer, 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);
# 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);
# Data structure
pragma solidity ^0.6.2;
import '@sgmfinance/erc-659/blob/main/contracts/ERC659data.sol';
2
"_balances"
Is the mapping from bond class and nonce to account balances.
4D array of address => (bond class => (bond nonce => bond balances)).
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;
"_Symbol"
Is the mapping from bond class to a string of bond symbol.
2D array of bond class => bond symbol.
e.g.
**bond symbol="SASH-BUSD bond"
.**SASH 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"
mapping ( uint256 =>mapping(uint256=> string[])) private _info;
"_info"
Is the mapping from bond class and nonce to a string list of uint256.
3D array of bond class => (bond nonce => bond info).
in this list, [(bond symbol), (timestamp),(3rd string)]
e.g. ["1615584000",(2nd uint256)...]
**This function allows the return of a list of uint256. every bond contract can have their own list. But the first string in the list MUST be the UTC time code of the issuing time,
**bond symbol="SASH-BUSD bond"
.**SASH 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"
mapping ( uint256 =>mapping(uint256=> string[])) private _info;
# EVENT
"eventIssueBond"
MUST trigger when Bonds are issued. This SHOULD not include zero value Issuing.
e.g.
"issue by address(operator) 500 SASH-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 SASH-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 SASH-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 SASH-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-659 contract is not compatible with contracts that don't have an ERC-659 interface built in. This requires the existing contract to upgrade with ERC-659 interface. The receiving of ERC-659 Bond need the implementation of ERC-659 interface in the receiver contract.
However any existing ERC-20 token contract can issue their ERC-659 bond, by giving the minting role to a bonk contract with ERC-659 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-659 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-659 bonds is not limited to ERC-20 token. Standard like ERC-721 nonfungible token can also issue their bond with the help of ERC-659 interface.
# 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
Copyright and related rights waived via CC0 (opens new window).