ERC-7092 Financial bond token standard (Financial Bonds)
Final · Vaults & DeFi · created 2023-05-28
A standard for turning bonds issued by companies or institutions into tokens. It uses functions that keep traditional bond terms such as coupon rate, maturity date, and principal, and supports primary issuance, secondary trading, and movement between chains.
Why it is needed
Fixed-income products such as bonds are a widely used way for companies and institutions to raise funds. But moving them onto a blockchain as tokens meant relying on the existing standard ERC-3475, which introduces unfamiliar concepts and causes unnecessary gas costs. It also has no named variables for coupon (interest), maturity date, or principal, so developers had to remember which metadata corresponds to which parameter in order to implement it. This standard reflects terms used in traditional finance directly in its function names to define the core characteristics of a bond. This makes it easier for traditional financial institutions to move to tokenized bonds, and lets bonds be managed in wallets or handled in applications such as decentralized exchanges.
What to implement
event Transfer(address indexed _from, address indexed _to, uint256 _amount);: The event emitted when bond tokens are transferred, issued, or redeemed. Contract creation is an exception.event Approval(address indexed _owner, address indexed _spender, uint256 _amount);: The event emitted when an account is approved or when an allowance is decreased.event TransferBatch(address[] _from, address[] _to, uint256[] _amount);: The event emitted when multiple bond tokens are transferred, issued, or redeemed at once. It MUST be emitted by batchTransfer and batchTransferFrom.event ApprovalBatch(address indexed _owner, address[] _spender, uint256[] _amount);: The event emitted when multiple accounts are approved or have their allowances decreased at once. It MUST be emitted by batchApprove and batchDecreaseAllowance.function isin() external view returns(string memory);: Returns the International Securities Identification Number (ISIN) of the bond.function name() external view returns(string memory);: Returns the name of the bond.function symbol() external view returns(string memory);: Returns the symbol of the bond. A combination of an issuer abbreviation and the maturity date is RECOMMENDED.function currency() external view returns(address);: Returns the bond currency. This is the contract address of the token used to pay and receive back the principal.function denomination() external view returns(uint256);: Returns the denomination of the bond. This is the minimum amount in which the bond can be issued, expressed in units of the principal currency.function issueVolume() external view returns(uint256);: Returns the issue volume, that is, the total debt amount. Expressing it in units of the denomination is RECOMMENDED.function couponRate() external view returns(uint256);: Returns the interest rate of the bond. Basis points are RECOMMENDED, so a 5% rate returns 500.function issueDate() external view returns(uint256);: Returns the date the bond was issued to investors, as a Unix timestamp.function maturityDate() external view returns(uint256);: Returns the maturity date, when the principal is repaid, as a Unix timestamp. The maturity date MUST be greater than the issue date.function principalOf(address _account) external view returns(uint256);: Returns the principal held by a specific account. Expressing it in units of the bond currency is RECOMMENDED.function allowance(address _owner, address _spender) external view returns(uint256);: Returns the amount of bond tokens the owner has authorized the _spender account to manage.function approve(address _spender, uint256 _amount) external returns(bool);: Approves the _spender account to manage _amount of the caller's bond tokens.function decreaseAllowance(address _spender, uint256 _amount) external returns(bool);: Decreases the allowance of _spender by _amount.function transfer(address _to, uint256 _amount, bytes calldata _data) external returns(bool);: Sends bonds to the _to address. Additional data can be attached to the tokens being sent.function transferFrom(address _from, address _to, uint256 _amount, bytes calldata _data) external returns(bool);: An account that has been granted authority through approve transfers the owner's bonds on the owner's behalf. Additional data can be attached.function batchApprove(address[] calldata _spender, uint256[] calldata _amount) external returns(bool);: Approves multiple accounts at once. This is an optional feature, so other contracts MUST NOT expect it to exist.function batchDecreaseAllowance(address[] calldata _spender, uint256[] calldata _amount) external returns(bool);: Decreases the allowances of multiple accounts at once. This is an optional feature.function batchTransfer(address[] calldata _to, uint256[] calldata _amount, bytes[] calldata _data) external returns(bool);: Sends a specified amount of bonds to each of multiple accounts at once. This is an optional feature.function batchTransferFrom(address[] calldata _from, address[] calldata _to, uint256[] calldata _amount, bytes[] calldata _data) external returns(bool);: An approved account transfers the bonds of multiple owners to multiple recipients at once. This is an optional feature.
In depth
Required token interface and bond properties
Every contract that follows this standard MUST implement the IERC7092 token interface and the ERC-165 interface. The query functions use the core characteristics of a traditional bond directly as their names. isin is the bond identification number. currency is the contract address of the token used to pay and receive back the principal. denomination is the minimum amount that can be issued (for example, if the currency is USDC and the denomination is 1,000, the denomination unit is 1,000 USDC). issueVolume is the total debt amount. couponRate is the interest rate (basis points recommended, so 5% is 500). issueDate and maturityDate are Unix timestamps, and the maturity date must be greater than the issue date. principalOf returns the principal per account. For symbol, combining an issuer abbreviation and the maturity date is recommended. For example, if Green Energy issues a bond maturing on October 25, 2030, the symbol could be GE30, GE2030, or GE102530.
Transfer and approval functions and batch processing
approve gives _spender the authority to manage _amount of bonds, and decreaseAllowance reduces the allowance. transfer and transferFrom can attach extra information to the transferred tokens through the _data parameter. The Transfer event MUST be emitted on transfer, issuance, and redemption, except at contract creation. batchApprove, batchDecreaseAllowance, batchTransfer, and batchTransferFrom are optional functions that improve usability, so other interfaces or contracts MUST NOT expect them to exist. However, batchTransfer and batchTransferFrom MUST emit the TransferBatch event, and batchApprove and batchDecreaseAllowance MUST emit the ApprovalBatch event.
Optional interface IERC7092ESG
An optional interface that expresses bond terms in more detail. decimals returns the number of decimal places of the token. For example, if it returns 10, the token amount must be multiplied by 10000000000 to get the standard representation. currencyOfCoupon is the contract address of the token that pays the coupon, which can differ from the principal currency. couponType expresses the interest method as a number. For example, 0 could mean zero coupon, 1 fixed rate, and 2 floating rate. couponFrequency is the number of coupon payments per year and expresses periods such as annual, semiannual, quarterly, and monthly. dayCountBasis is the day count basis used to calculate interest. For example, 0 could mean actual/actual and 1 could mean actual/360, and there are also bases such as 30/360, actual/365, and 30/365.
Optional interface IERC7092CrossChain
An optional interface for managing bond tokens across multiple blockchains. Every function that initiates a cross-chain transaction MUST specify the destination chain identifier destinationChainID and the destination contract to interact with, destinationContract. crossChainApprove and crossChainDecreaseAllowance handle approval and allowance reduction on another chain, and crossChainTransfer and crossChainTransferFrom handle moving tokens from the current chain to another chain (for example, from Ethereum to Polygon), each with a batch version. Cross-chain transfers and approvals MUST emit events such as CrossChainTransfer and CrossChainApproval.
Why there is no totalSupply or balanceOf
This standard does not define totalSupply or balanceOf, because both values can be derived from issueVolume, principalOf, and denomination. The source text shows an example that implements totalSupply as issueVolume divided by denomination and an account balance as that account's principal divided by denomination, and it allows implementing contracts to add such functions. Because these functions are absent, this standard is not backward compatible with ERC-20 or ERC-1155. For tokenized bond issuance, a pure implementation of this standard alone is recommended, and it states that a hybrid implementation mixed with other standards must fail.
Reference implementation and extensions: bonds with options and an identity registry
Inheriting from the reference implementation ERC7092.sol makes it possible to create bonds with options attached. The source text shows skeleton examples of a callable bond, which only the issuer can call to redeem the bond before maturity, a puttable bond, which the investor holding the bond can sell back before maturity, and a convertible bond, which converts the bond into shares. All three examples check that block.timestamp is less than maturityDate so that they can only be executed before maturity. The standard itself does not manage the identity of bondholders, but for regulatory compliance and transparency, an identity registry can be layered on top of the standard so that bond tokens can be transferred only to registered and approved parties.
Things to watch out for
- Access checks on approval and transfer functions: The function that approves an operator to manage the owner's bonds and the function that transfers bonds must carefully account for security risks. Robust checks are needed so that only the bond owner or an approved account can call these functions.