This proposal introduces fixed income financial bonds. Important bond characteristics such as the International Securities Identification Number (ISIN),
the issue volume, the issue date, the maturity date, the coupon rate, the coupon frequency, the principal, or the day count basis are defined to allow issuing
bonds in the primary market (origination), and different transfer functions allow to buy or sell bonds in the secondary market. The standard also provides a
functionality to allow bonds to be approved by owners in order to be spent by third party.
Motivation
Fixed income instruments is one of the asset classes that is widely used by corporations and other entities to raise funds. Bonds
are considered more secured than equity since the issuer is supposed to repay the principal at maturity in addition to coupons
that are paid to investsors.
This standard interface allows fixed income instruments to be represented as on-chain tokens, so as they can be managed through wallets,
and be used by applications like decentrailized exchanges.
The existing standard ERC-3475 that may be used to create abstract storage bonds does not follow same standards as
with traditional bonds. By introducing concepts such as classes and nonces that are not used for traditional bonds, the ERC-3475 standard makes it difficult for
traditional entities to migrate to tokenized bonds. Morever, the use of on-chain metadata with ERC-3475, like classMetadata and nonceMetadata, and also classValues
leads to unnecessary gas consumption. The lack of named variables like coupon, maturity date, principal, etc… makes it difficult to implement the ERC-3475 since developers
need to rember which metadata is assigned to each parameter.
By keeping same standards as with traditional bonds, the ERC-7092 allows to create a new token for bonds with same caracteristics as traditional bonds so as to make it simple
to migrate to tokenized bonds.
Tokenizing bonds will offer several advantages compared to traditional bond issuance and trading, among with:
Fractional ownership: The bond standard does not limit the bond denomination to some minimum value compared to traditioanal bonds where the denomination is typically equal to $100 or $1,000.
Accessibility: By allowing lower investment thresholds, tokenized bonds are supposed to attract retail investors who could not participate in traditional markets due to high minimum investment requirements.
Increased liquidity: Fractioanal ownership will bring new investors in the bond market, this will increase liquidity in the bond market.
Cost savings: By replacing intermediaries with smart contracts, bond’s tokenization will reduce costs associated with the bond issuance and management.
Easy accessibility and 24/7 trading: Tokenized bonds are supposed to be traded on digital platforms such as decentralized exchanges. Therefore, they will be more accessible compared to tradional bond market.
Specification
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.
Every contract compliant with the ERC-7092 MUST implement the following interface
pragmasolidity^0.8.0;/**
* @title ERC-7092 Financial Bonds tandard
*/interfaceIERC7092{/**
* @notice Returns the bond isin
*/functionisin()externalviewreturns(stringmemory);/**
* @notice Returns the bond name
*/functionname()externalviewreturns(stringmemory);/**
* @notice Returns the bond symbol
* It is RECOMMENDED to represent the symbol as a combination of the issuer Issuer'shorter name and the maturity date
* Ex: If a company named Green Energy issues bonds that will mature on october 25, 2030, the bond symbol could be `GE30` or `GE2030` or `GE102530`
*
* OPTIONAL - interfaces and other contracts MUST NOT expect these values to be present. The method is used to improve usability.
*/functionsymbol()externalviewreturns(stringmemory);/**
* @notice Returns the number of decimals the bond uses - e.g `10`, means to divide the token amount by `10000000000`
*
* OPTIONAL - interfaces and other contracts MUST NOT expect these values to be present. The method is used to improve usability.
*/functiondecimals()externalviewreturns(uint8);/**
* @notice Returns the bond currency. This is the contract address of the token used to pay and return the bond principal
*/functioncurrency()externalviewreturns(address);/**
* @notice Returns the copoun currency. This is the contract address of the token used to pay coupons. It can be same as the the one used for the principal
*
* OPTIONAL - interfaces and other contracts MUST NOT expect these values to be present. The method is used to improve usability.
*/functioncurrencyOfCoupon()externalviewreturns(address);/**
* @notice Returns the bond denominiation. This is the minimum amount in which the Bonds may be issued. It must be expressend in unnit of the principal currency
* ex: If the denomination is equal to 1,000 and the currency is USDC, then bond denomination is equal to 1,000 USDC
*/functiondenomination()externalviewreturns(uint256);/**
* @notice Returns the issue volume (total debt amount). It is RECOMMENDED to express the issue volume in the bond currency unit (USDC, DAI, etc...).
* NOTICE: The `issue volume` can also be expressed in `denomination` unit. In that case, the `issue volume` MUST equal the `totalSupply`
* of the bonds, i.e. the total number of bond tokens issued.
* ex: if denomination = $1,000, and the total debt is $5,000,000
* then, issueVolume() = $5,000, 000 / $1,000 = 5,000 bonds
*/functionissueVolume()externalviewreturns(uint256);/**
* @notice Returns the bond interest rate. It is RECOMMENDED to express the interest rate in basis point unit.
* 1 basis point = 0.01% = 0.0001
* ex: if interest rate = 5%, then coupon() => 500 basis points
*/functioncouponRate()externalviewreturns(uint256);/**
* @notice Returns the coupon type
* ex: 0: Zero coupon, 1: Fixed Rate, 2: Floating Rate, etc...
*
* OPTIONAL - interfaces and other contracts MUST NOT expect these values to be present. The method is used to improve usability.
*/functioncouponType()externalviewreturns(uint256);/**
* @notice Returns the coupon frequency, i.e. the number of times coupons are paid in a year.
*
* * OPTIONAL - interfaces and other contracts MUST NOT expect these values to be present. The method is used to improve usability.
*/functioncouponFrequency()externalviewreturns(uint256);/**
* @notice Returns the date when bonds were issued to investors. This is a Unix Timestamp like the one returned by block.timestamp
*/functionissueDate()externalviewreturns(uint256);/**
* @notice Returns the bond maturity date, i.e, the date when the pricipal is repaid. This is a Unix Timestamp like the one returned by block.timestamp
* The` maturity date` MUST be greater than the `issue date`
*/functionmaturityDate()externalviewreturns(uint256);/**
* @notice Returns the day count basis
* Ex: 0: actual/actual, 1: actual/360, etc...
*
* OPTIONAL - interfaces and other contracts MUST NOT expect these values to be present. The method is used to improve usability.
*/functiondayCountBasis()externalviewreturns(uint256);/**
* @notice Returns the principal of an account. It is RECOMMENDED to express the principal in the bond currency unit (USDC, DAI, etc...).
* NOTICE: The `principal` can also be expressed in `denomination` unit. In that case, the `principal` MUST equal the balance of `_account`.
* Ex: if denomination = $1,000, and the user has invested $5,000 (princiapl in currency unit), then
* principalOf(_account) = 5,000/1,000 = 5
* @param _account account address
*/functionprincipalOf(address_account)externalviewreturns(uint256);/**
* @notice Returns the amount of tokens the `_spender` account has been authorized by the `_owner``
* acount to manage their bonds
* @param _owner the bondholder address
* @param _spender the address that has been authorized by the bondholder
*/functionapproval(address_owner,address_spender)externalviewreturns(uint256);/**
* @notice Authorizes `_spender` account to manage `_amount`of their bonds
* @param _spender the address to be authorized by the bondholder
* @param _amount amount of bond to approve. _amount MUST be a multiple of denomination
*/functionapprove(address_spender,uint256_amount)externalreturns(bool);/**
* @notice Authorizes the `_spender` account to manage all their bonds
* @param _spender the address to be authorized by the bondholder
*
* OPTIONAL - interfaces and other contracts MUST NOT expect these values to be present. The method is used to improve usability.
*/functionapproveAll(address_spender)externalreturns(bool);/**
* @notice Lower the allowance of `_spender` by `_amount`
* @param _spender the address to be authorized by the bondholder
* @param _amount amount of bond to remove approval; _amount MUST be a multiple of denomination
*/functiondecreaseAllowance(address_spender,uint256_amount)externalreturns(bool);/**
* @notice Remove the allowance for `_spender`
* @param _spender the address to remove the authorization by from
*
* OPTIONAL - interfaces and other contracts MUST NOT expect these values to be present. The method is used to improve usability.
*/functiondecreaseAllowanceForAll(address_spender)externalreturns(bool);/**
* @notice Moves `_amount` bonds to address `_to`
* @param _to the address to send the bonds to
* @param _amount amount of bond to transfer. _amount MUST be a multiple of denomination
* @param _data additional information provided by the token holder
*/functiontransfer(address_to,uint256_amount,bytescalldata_data)externalreturns(bool);/**
* @notice Moves all bonds to address `_to`
* @param _to the address to send the bonds to
* @param _data additional information provided by the token holder
*
* OPTIONAL - interfaces and other contracts MUST NOT expect these values to be present. The method is used to improve usability.
*/functiontransferAll(address_to,bytescalldata_data)externalreturns(bool);/**
* @notice Moves `_amount` bonds from an account that has authorized through the approve function
* @param _from the bondholder address
* @param _to the address to transfer bonds to
* @param _amount amount of bond to transfer. _amount MUST be a multiple of denomination
* @param _data additional information provided by the token holder
*/functiontransferFrom(address_from,address_to,uint256_amount,bytescalldata_data)externalreturns(bool);/**
* @notice Moves all bonds from `_from` to `_to`. The caller must have been authorized through the approve function
* @param _from the bondholder address
* @param _to the address to transfer bonds to
* @param _data additional information provided by the token holder
*
* OPTIONAL - interfaces and other contracts MUST NOT expect these values to be present. The method is used to improve usability.
*/functiontransferAllFrom(address_from,address_to,bytescalldata_data)externalreturns(bool);/**
* @notice MUST be emitted when bonds are transferred
* @param _from the account that owns bonds
* @param _to the account that receives the bond
* @param _amount the amount of bonds to be transferred
* @param _data additional information provided by the token holder
*/eventTransferred(address_from,address_to,uint256_amount,bytes_data);/**
* @notice MUST be emitted when an account is approved
* @param _owner the bonds owner
* @param _spender the account to be allowed to spend bonds
* @param _amount the amount allowed by _owner to be spent by _spender.
*/eventApproved(address_owner,address_spender,uint256_amount);/**
* @notice MUST be emmitted when the `_owner` decreases allowance from `_sepnder` by quantity `_amount`
* @param _owner the bonds owner
* @param _spender the account that has been allowed to spend bonds
* @param _amount the amount of tokens to disapprove
*/eventAllowanceDecreased(address_owner,address_spender,uint256_amount);}
Rationale
The financial bond standard is designed to represent fixed income assets, which reprensent a loan made by an investor to a borrower.
The proposed design has been motivated by the necessity to tokenize fixed income assets, and to represent the bond token with same
characteristics as in traditional finance. Keeping the same properties as in tradional finance is necessary for issuers and investors
to move to tokenized bonds without major difficulties. The same structure used in tradional finace, i.e issuer-investment bank-investors
can be used for the bond standard, in that case the investment bank intermediary may be replaced by smart contracts. In the case of
institutional issuance, the smart contracts can be managed by the investment bank. Decentralized exchanges may also use the bond standard
to list bonds, in that case, decentralized exchanges will be in charge of managing the smart contracts. Other entities may also create
tokenized bonds by integrating this financial bond interface.
The use of terminology such as issueVolume and principalOf instead of totalSupply and balanceOf respectively, as used in other standards like
ERC-20 is motivated by the need to use same the terminology as with traditional bond. Furthermore, by adding the data input parameter
in all transfer functions to allow the transfer of additional data, the ERC-7092 SHOULD NOT be compatible with the ERC-20 standard, even if both
the principalOf and balanceOf functions were used.
Another reason that has motivated not to extend other standards like the ERC-20 is to allow token explorer platforms like etherscan to represent
the ERC-7092 token not as a simple ERC-20 token, but as a new token showing some bonds characteristics like the principal, the coupon,
the denomination, and the maturity date. Those information are very important for bondholders to know the return on the capital invested.
Total Supply
We made the choice not to define the totalSupply function beacause it can be derived from both the issue volume and the denomination.
However it is RECOMMENDED to define the totalSupply function in any contract that implements this standard. In that case,
the total supply MUST be equal to the ratio of the issue volume and the denomination.
totalSupply=issueVolume/denomination
Account Balance
Because the balance of an account can be derived from both the principal and the denomination, we made the choice not to define the balanceOf function.
However it is RECOMMENDED to define the balanceOf function in any contract that implements this standard. In that case,
the balance of an account MUST be equal to the ratio of the principal of that account and the denomination.
Beacause some functions like totalSupply or balanceOf are not implemented, the ERC-7092 SHOULD NOT extend existing standards,
like ERC-20 or ERC-1155. The ERC-7092 is a representation of a new token for bonds with all bonds characteristics
and functionalities already attached to it.
For the reasons mentionned above, we recommand a pure implementation of the standard to issue tokenized bonds, since any hybrid solution
with other standards mentionned above SHOULD fail.
Reference Implementation
The reference implementation of the ERC-7092 can be found here.
Some bonds have embedded options attached to them. As an example we can cite:
Callable bonds that have an option that gives the issuer the right to retire bonds before they mature.
Puttable bonds that have an option that gives investors the right to retire bonds before they mature.
Convertible bonds that gives investors the right to convert their bonds to equity.
Bonds with embedded options can be created by inheriting from the basic ERC-7092 that integrates the proposed interface.
CALLABLE BONDS:
pragmasolidity^0.8.0;import'ERC7092.sol';contractERC7092CallableisERC7092{// WRITE THE LOGIC TO ALLOW THE ISSUER TO CALL BONDS
// STATE VARIABLES AND FUNCTIONS NEEDED
/**
* @notice call bonds owned by `_investor`
* MUST be called by the issuer only
*/functioncall(address_investor)public{require(_principals[_investor]>0,"ERC7092Callable: ONLY_ISSUER");require(block.timestamp<_bond[bondISIN].maturityDate,"ERC7092Callable: BOND_MATURED");uint256principal=_principals[_investor];_principals[_investor]=0;// ADD LOGIC HERE
}}
PUTTABLE BONDS:
pragmasolidity^0.8.0;import'ERC7092.sol';contractERC7092PuttableisERC7092{// WRITE THE LOGIC TO ALLOW INVESTORS TO PUT BONDS
// STATE VARIABLES AND FUNCTIONS NEEDED
/**
* @notice put bonds
* MUST be called by investors who own bonds
*/functionput()public{require(_principals[msg.sender]>0,"ERC7092Puttable: ONLY_INVESTORS");require(block.timestamp<_bond[bondISIN].maturityDate,"ERC7092Puttable: BOND_MATURED");uint256principal=_principals[msg.sender];_principals[msg.sender]=0;// ADD LOGIC
}}
CONVERTIBLE BONDS:
pragmasolidity^0.8.0;import'ERC7092.sol';contractERC7092ConvertibleisERC7092{// WRITE THE LOGIC TO ALLOW INVESTOR OR ISSUER TO CONVERT BONDS TO EQUITY
// STATE VARIABLES AND FUNCTIONS NEEDED
/**
* @notice convert bonds to equity. Here we assumed that the investors must convert their bonds to equity
* Issuer can also convert invetsors bonds to equity.
*/functionconvert()public{require(_principals[msg.sender]>0,"ERC7092Convertible: ONLY_INVESTORS");require(block.timestamp<_bond[bondISIN].maturityDate,"ERC7092Convertible: BOND_MATURED");uint256principal=_principals[msg.sender];_principals[msg.sender]=0;// ADD LOGIC HERE
}}
Security Considerations
When implementing the ERC-7092, it is important to consider security risk related to functions that give approval to operators to manage owner’s bonds, and to functions that allow to transfer bonds. Functions transferAll and transferAllFrom allow to transfer all the balance of an account. Therefore, it is crucial to ensure that only the bonds owner and accounts that have been approved by the bonds owner can call these functions.