đź“– This EIP is in the review stage. It is subject to changes and feedback is appreciated.

EIP-3475: Multiple Callable Bonds Standard Source

A standard interface for contract that manage multiple bonds. This standard will allow multiple LP token contract to store more information

AuthorYu LIU, Waroon D., Cedric Ngakam, Dhruv Malik, Samuel Gwlanold, Toufic Batrice
Discussions-Tohttps://ethereum-magicians.org/t/eip-3475-multiple-callable-bonds-standard/8691
StatusReview
TypeStandards Track
CategoryERC
Created2021-04-05

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. Existing structures, such as AMM exchanges or lending platform can be updated to recognize ERC-3475 Bonds.

Specification

TransferFrom

transferFrom(address from, address to, uint256 classId, uint256 nonceId, uint256 amount) 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 bond class, the first bond class created will be 0, and so on.

The "nonce" is a nonce of the given bond class. This param is for distinctions of the issuing conditions of the bond.

The "_amount" is the amount of the bond, that will be transferred from "_from"address to "_to" address.

e.g.

transferFrom(0x2d03B6C79B75eE7aB35298878D05fe36DC1fE8Ef,0x82a55a613429Aeb3D01fbE6841bE1AcA4fFD5b2B, 1, 42, 500000000);

This example shows the transfer from _from address, to \_to address, 500000000 of bond class1 nonce 42

Issue

issue(address to, uint256 classId, uint256 nonceId, uint256 amount) 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" argument is the address to which the bond will be issued.

The "classId" is the classId of the bond, the first bond class created will be 0, and so on.

The "nonceId" is the nonceId of the given bond class. This param is for distinctions of the issuing conditions of the bond.

The "_amount" is the amount of the bond, that “to” address will receive.

e.g.

issueBond(0x2d03B6C79B75eE7aB35298878D05fe36DC1fE8Ef, 0, 5, 1000);

This example shows the issuing to wallet address 0x2d03B6C79B75eE7aB35298878D05fe36DC1fE8Ef, 1000 of bond classId 0 and nonceId 5.

Redeem

redeem(address from, uint256 classId, uint256 nonceId, uint256 amount) 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 "classId" is the class nonce of bond, the first bond class created will be 0, and so on.

The "nonceId" is the nonce of the given bond class. This param is for distinctions of the issuing conditions of the bond.

The "_amount" is the amount of the bond, that "_from" address will redeem.

e.g.

redeem(0x2d03B6C79B75eE7aB35298878D05fe36DC1fE8Ef, 1, 42, 500000000);

those input mean “redeem from wallet address(0x2d03B6C79B75eE7aB35298878D05fe36DC1fE8Ef), 500000000 of bond class1 nonce 42

Burn

burn(address from, uint256 classId, uint256 nonceId, uint256 amount) 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]);

Approve

approve(address spender, uint256 classId, uint256 nonceId, uint256 amount) Allows spender to withdraw from your account multiple times, up to the amount.

If this function is called again it overwrites the current allowance with amount.

The"spender" is the address the caller approve for his bonds

The "classId" is the classId nonce of bond, the first bond class created will be 0, and so on.

The "nonceId" is the nonceId of the given bond class. This param is for distinctions of the issuing conditions of the bond.

The "_amount" is the amount of the bond that the spender is approved for.

e.g.

approve(0x82a55a613429Aeb3D01fbE6841bE1AcA4fFD5b2B, 0, 1, 30000);

mean 0x82a55a613429Aeb3D01fbE6841bE1AcA4fFD5b2B is approved to manage 30000 bonds from class 0 and Nonce 1

SetApprovalFor

setApprovalFor(address operator, uint256 classId, bool approved) Enable or disable approval for a third party (“operator”) to manage Bond class of the caller’s bonds.

The"operator" is the Address to add to the set of authorized operators

The "classId" is the classId nonce of bond, the first bond class created will be 0, and so on.

The "_approved" True if the operator is approved, false to revoke approval

e.g.

setApprovalFor(0x82a55a613429Aeb3D01fbE6841bE1AcA4fFD5b2B, 0, true);

mean 0x82a55a613429Aeb3D01fbE6841bE1AcA4fFD5b2B is approved to transfer bonds from class 0

BatchApprove

batchApprove(address spender, uint256[] calldata classIds, uint256[] calldata nonceIds, uint256[] calldata amounts) Allows spender to withdraw from your account multiple times, up to the amount.

If this function is called again it overwrites the current allowance with amount.

The"spender" is the address the caller approve for his bonds

The "classIds" is the list of classIds of bond.

The "nonceIds" is the list of nonceIds of the given bond class.

The "_amounts" is the list of amounts of the bond that the spender is approved for.

e.g.

batchApprove(0x82a55a613429Aeb3D01fbE6841bE1AcA4fFD5b2B, [0,1,2], [30,42,3] , [30000, 50000, 2000]);

mean 0x82a55a613429Aeb3D01fbE6841bE1AcA4fFD5b2B is approved to manage :

  • 30000 bonds from class 0 and Nonce 30
  • 50000 bonds from class 1 and Nonce 42
  • 2000 bonds from class 2 and Nonce 3

TotalSupply

totalSupply(uint256 classId, uint256 nonceId) allows anyone to read the total supply of a given bond’s class and nonce, this includes burned and redeemed Supply

The "classId" is the class nonce of bond, the first bond class created will be 0, and so on.

The "nonceId" is the nonce of the bond. This param is for distinctions of the issuing conditions of the bond.

Returns the total supply of the bond in question. — e.g. "5821200000000".

function totalSupply(uint256 class, uint256 nonce) external view returns (uint256);

ActiveSupply

activeSupply(uint256 classId, uint256 nonceId) allows anyone to read the non-burned and non-redeemed Supply of a given class nonce and bond nonce.

The "classId" is the class nonce of bond, the first bond class created will be 0, and so on.

The "nonceId" 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(uint256 classId, uint256 nonceId) allows anyone to read the burned Supply of a given class and bond nonce.

The "classId" is the class nonce of bond, the first bond class created will be 0, and so on.

The "nonceId" 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(uint256 classId, uint256 nonceId) allows anyone to read the redeemed Supply of a given class and bond nonce.

The "classId" is the class nonce of bond, the first bond class created will be 0, and so on.

The "nonceId" is the nonce of the bond. This param is for distinctions of the issuing conditions of the bond.

Returns the redeemed supply of the bond in question. — e.g. "612300000000".

function redeemedSupply( uint256 class, uint256 nonce) external view returns (uint256);

BalanceOf

balanceOf(address account, uint256 classId, uint256 nonceId) allows anyone to read the remaining balance of an address. This will only return the balance of a single bond class and bond class nonce.

The "account" is the address of the token holder.

The "classId" is the class nonce of bond, the first bond class created will be 0, and so on.

The "nonceId" 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);

Symbol

symbol(uint256 classId) 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.

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);

ClassInfos

classInfos(uint256 classId) allows anyone to read the information of a bond class.

The "class" is the class nonce of bond, the first bond class created will be 0, and so on.

Returns a list of uint256 parameters of a bond class. — e.g. ["1615584000",(2nd uint256)...].** Every bond contract can have their own list.

function classInfos(uint256 classId) external view returns (uint256[] memory);

ClassInfoDescription

classInfoDescription(uint256 classInfo) allows anyone to read the information description of a class info given.

The "classInfo" is the class info.

Returns the class information description of the class info key. Every bond contract can have their own list of info description.

classInfoDescription(uint256 classInfo) external view returns (string memory);

NonceInfos

nonceInfos(uint256 classId, uint256 nonceId) allows anyone to read the information of a bond class’ 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 a list of uint256 parameters of a bond nonce. — e.g. ["1615584000",(2nd 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 nonceInfos(uint256 classId, uint256 nonceId) external view returns (uint256[] memory);

NonceInfoDescription

nonceInfoDescription(uint256 nonceInfo) allows anyone to read the information description of a nonce info given.

The "nonceInfo" is the nonce info

Returns the nonce information description of the nonce info key. Every bond contract can have their own list of info description.

nonceInfoDescription(uint256 nonceInfo) external view returns (string memory);

IsRedeemable()

isRedeemable(uint256 classId, uint256 nonceId) allows anyone to check if a bond is redeemable.** the conditions of redemption can be speechified with one or several internal functions.

The "classId" is the class nonce of bond, the first bond class created will be 0, and so on.

The "nonceId" 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);

IsApprovedFor

isApprovedFor(address owner, address operator, uint256 classId) returns if (“operator”) is approved to manage account’s Bonds class.

The"_owner" The owner of the tokens

The"_operator" Address of authorized operator

The "class" is the bond class, the first bond class created will be 0, and so on.

isApprovedFor(address owner, address operator, uint256 classId) external view returns (bool);

EVENT

"Issue" 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 Issue(address indexed _operator, address indexed _to, uint256 classId, uint256 nonceId, uint256 amount); 

"Redeem" MUST trigger when Bonds are redeemed. This SHOULD not include zero value redemption.

e.g.

"redeem by address(_operator) 500 DEBIT-USD Bond(Nonce14) to 0x2d03B6C79B75eE7aB35298878D05fe36DC1fE8Ef"

event Redeem(address indexed _operator, address indexed _from, uint256 classId, uint256 nonceId, uint256 amount);

"Burn" MUST trigger when Bonds are burned. This SHOULD not include zero value burning.

e.g.

"burn by address(_operator) 500 DEBIT-USD Bond(Nonce14) from 0x2d03B6C79B75eE7aB35298878D05fe36DC1fE8Ef"

event Burn(address indexed _operator, address indexed _from, uint256 classId, uint256 nonceId, uint256 amount);

"Transfer" 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 Issued" instead ). Transfer event with the _to 0x0 MUST not create this event(Use"event Redeemed" when redemption, and "event Burned" when burning).

e.g.

"transfer by address(_operator) 500 DEBIT-USD Bond(Nonce14) from 0x2d03B6C79B75eE7aB35298878D05fe36DC1fE8Ef, to address(_to)"

event Transfer(address indexed _operator, address indexed _from, address indexed _to, uint256 classId, uint256 nonceId, uint256 amount);

Rationale

Metadata structure: Instead of utilizing a mapping from address, the bond’s metadata like the time of redemption, redemption conditions, and interest rate can be stored in the bond class and nonce structures. Classes represent the different bond types, and nonces represent the various period of issuances. Nonces under the same class share the same metadata. Meanwhile, nonces are non-fungible. Each nonce can store a different set of metadata. Thus, upon transfer of a bond, all the metadata will be transferred to the new owner of the bond.

Batch function: ERC 3475 supports batch functions, such as batch transactions. It allows the user to transfer different bonds with different metadata to a new address at a time. This mechanism helps in the “packaging” of bonds. When a package of bonds trades on a secondary market, the new owner holds the right to reclaim the face value of each of the bonds.

AMM optimization: One of the most obvious use cases of ERC 3475 is the multilayered pool. The early version of AMM uses a separate smart contract and an ERC 20 LP token to manage a pair. By doing so, the overall liquidity inside of one pool is significantly reduced and thus generates unnecessary gas spent and slippage. Using ERC 3475, one can build a big liquidity pool with all the pairs inside. Then, with class and nonce, represent what percentage of a given token this address holds. Effectively, the ERC20 LP token is converted, in this case, to ERC 3475 (Multiple Callable Bonds) token.

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 ERC-3475 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 bank contract with ERC-3475 interface built in. The implementation of This can be found in our Use Cases.

To ensure the reading of transactions, "Issued" ,"Redeemed" ,"Burned" ,"Transfer", 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…

Reference Implementation

ERC-3475 Reference Implementation

**This demonstration show only a simple application of ERC-3475 Multiple Callable Bonds Standard. Developers can build much sophisticated logic with this interface. No function used in the example serves as a guide-line.

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 and related rights waived via CC0.

Citation

Please cite this document as:

Yu LIU, Waroon D., Cedric Ngakam, Dhruv Malik, Samuel Gwlanold, Toufic Batrice, "EIP-3475: Multiple Callable Bonds Standard," Ethereum Improvement Proposals, no. 3475, April 2021. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-3475.