diff --git a/ERCS/erc-7092.md b/ERCS/erc-7092.md index 77d2216e21..b0f0603780 100644 --- a/ERCS/erc-7092.md +++ b/ERCS/erc-7092.md @@ -1,61 +1,77 @@ --- eip: 7092 title: Financial Bonds -description: This interface defines a specification for debt issued by corporations, goverments or other entities to investors in order to raise funds. +description: Represents debt issued by entities to investors. author: Samuel Gwlanold Edoumou (@Edoumou) discussions-to: https://ethereum-magicians.org/t/financial-bonds/14461 -status: Draft +status: Review type: Standards Track category: ERC created: 2023-05-28 +requires: 165 --- ## Abstract -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. +This proposal introduces fixed-income financial bonds with key characteristics defined to facilitate bond issuance in the primary market and enable buying or selling bonds in the secondary market. The standard also provides cross-chain functionalities for bonds operations and management accross multiple blockchains. ## 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](./eip-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](./eip-7092.md) 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: - -1. 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. -2. 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. -3. Increased liquidity: Fractioanal ownership will bring new investors in the bond market, this will increase liquidity in the bond market. -4. Cost savings: By replacing intermediaries with smart contracts, bond's tokenization will reduce costs associated with the bond issuance and management. -5. 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. +Fixed-income instruments are a widely utilized asset class for corporations and other entities raising funds. However, transitioning to tokenized bonds is challenging due to existing standards like [ERC-3475](./erc-3475.md), which introduces unfamiliar concepts and leads to unnecessary gas consumption. Additionally, the lack of named variables like coupon, maturity date, and principal, makes it difficult to implement ERC-3475 since developers need to remember which metadata is assigned to each parameter. ## 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](./eip-7092.md) MUST implement the following interface** +**Every contract compliant with this ERC MUST implement the following Token Interface as well as the [ERC-165](./erc-165.md) interface:** ```solidity +// SPDX-License-Identifier: CC0-1.0 pragma solidity ^0.8.0; /** -* @title ERC-7092 Financial Bonds tandard +* @title ERC-7092 Financial Bonds Standard */ -interface IERC7092 { +interface IERC7092 /** is ERC165 */ { + // events + /** + * @notice MUST be emitted when bond tokens are transferred, issued or redeemed, except during contract creation + * @param _from the account that owns bonds + * @param _to the account that receives the bond + * @param _amount amount of bond tokens to be transferred + */ + event Transfer(address indexed _from, address indexed _to, uint256 _amount); + + /** + * @notice MUST be emitted when an account is approved or when the allowance is decreased + * @param _owner bond token's owner + * @param _spender the account to be allowed to spend bonds + * @param _amount amount of bond tokens allowed by _owner to be spent by `_spender` + * Or amount of bond tokens to decrease allowance from `_spender` + */ + event Approval(address indexed _owner, address indexed _spender, uint256 _amount); + + /** + * @notice MUST be emitted when multiple bond tokens are transferred, issued or redeemed, with the exception being during contract creation + * @param _from array of bondholders accounts + * @param _to array of accounts to transfer bonds to + * @param _amount array of amounts of bond tokens to be transferred + * + ** OPTIONAL - interfaces and other contracts MUST NOT expect this function to be present. MUST be emitted in `batchTransfer` and `batchTransferFrom` functions + */ + event TransferBatch(address[] _from, address[] _to, uint256[] _amount); + + /** + * @notice MUST be emitted when multiple accounts are approved or when the allowance is decreased from multiple accounts + * @param _owner bondholder account + * @param _spender array of accounts to be allowed to spend bonds, or to decrase the allowance from + * @param _amount array of amounts of bond tokens allowed by `_owner` to be spent by multiple accounts in `_spender`. + * + ** OPTIONAL - interfaces and other contracts MUST NOT expect this function to be present. MUST be emitted in `batchApprove` and `batchDecreaseAllowance` functions + */ + event ApprovalBatch(address indexed _owner, address[] _spender, uint256[] _amount); + + // getter functions /** * @notice Returns the bond isin */ @@ -70,42 +86,22 @@ interface IERC7092 { * @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. */ function symbol() external view returns(string memory); - /** - * @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. - */ - function decimals() external view returns(uint8); - /** * @notice Returns the bond currency. This is the contract address of the token used to pay and return the bond principal */ function currency() external view returns(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. - */ - function currencyOfCoupon() external view returns(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 + * @notice Returns the bond denominiation. This is the minimum amount in which the Bonds may be issued. It must be expressend in unit of the principal currency + * ex: If the denomination is equal to 1,000 and the currency is USDC, then the bond denomination is equal to 1,000 USDC */ function denomination() external view returns(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 + * @notice Returns the issue volume (total debt amount). It is RECOMMENDED to express the issue volume in denomination unit. */ function issueVolume() external view returns(uint256); @@ -116,45 +112,19 @@ interface IERC7092 { */ function couponRate() external view returns(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. - */ - function couponType() external view returns(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. - */ - function couponFrequency() external view returns(uint256); - /** * @notice Returns the date when bonds were issued to investors. This is a Unix Timestamp like the one returned by block.timestamp */ function issueDate() external view returns(uint256); /** - * @notice Returns the bond maturity date, i.e, the date when the principal is repaid. This is a Unix Timestamp like the one returned by block.timestamp - * The` maturity date` MUST be greater than the `issue date` + * @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 */ function maturityDate() external view returns(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. - */ - function dayCountBasis() external view returns(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 + * @notice Returns the principal of an account. It is RECOMMENDED to express the principal in the bond currency unit (USDC, DAI, etc...) * @param _account account address */ function principalOf(address _account) external view returns(uint256); @@ -165,162 +135,289 @@ interface IERC7092 { * @param _owner the bondholder address * @param _spender the address that has been authorized by the bondholder */ - function approval(address _owner, address _spender) external view returns(uint256); + function allowance(address _owner, address _spender) external view returns(uint256); + // setter functions /** - * @notice Authorizes `_spender` account to manage `_amount`of their bonds + * @notice Authorizes `_spender` account to manage `_amount`of their bond tokens * @param _spender the address to be authorized by the bondholder - * @param _amount amount of bond to approve. _amount MUST be a multiple of denomination + * @param _amount amount of bond tokens to approve */ function approve(address _spender, uint256 _amount) external returns(bool); /** - * @notice Authorizes the `_spender` account to manage all their bonds + * @notice Lowers the allowance of `_spender` by `_amount` * @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. + * @param _amount amount of bond tokens to remove from allowance */ - function approveAll(address _spender) external returns(bool); + function decreaseAllowance(address _spender, uint256 _amount) external returns(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 + * @notice Moves `_amount` bonds to address `_to`. This methods also allows to attach data to the token that is being transferred + * @param _to the address to send the bonds to + * @param _amount amount of bond tokens to transfer + * @param _data additional information provided by the token holder */ - function decreaseAllowance(address _spender, uint256 _amount) external returns(bool); + function transfer(address _to, uint256 _amount, bytes calldata _data) external returns(bool); + + /** + * @notice Moves `_amount` bonds from an account that has authorized the caller through the approve function + * This methods also allows to attach data to the token that is being transferred + * @param _from the bondholder address + * @param _to the address to transfer bonds to + * @param _amount amount of bond tokens to transfer. + * @param _data additional information provided by the token holder + */ + function transferFrom(address _from, address _to, uint256 _amount, bytes calldata _data) external returns(bool); + // batch functions /** - * @notice Remove the allowance for `_spender` - * @param _spender the address to remove the authorization by from + * @notice Authorizes multiple spender accounts to manage a specified `_amount` of the bondholder tokens + * @param _spender array of accounts to be authorized by the bondholder + * @param _amount array of amounts of bond tokens to approve * * OPTIONAL - interfaces and other contracts MUST NOT expect these values to be present. The method is used to improve usability. */ - function decreaseAllowanceForAll(address _spender) external returns(bool); + function batchApprove(address[] calldata _spender, uint256[] calldata _amount) external returns(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 + * @notice Decreases the allowance of multiple spenders by corresponding amounts in `_amount` + * @param _spender array of accounts to be authorized by the bondholder + * @param _amount array of amounts of bond tokens to decrease the allowance from + * + * OPTIONAL - interfaces and other contracts MUST NOT expect this function to be present. The method is used to decrease token allowance. */ - function transfer(address _to, uint256 _amount, bytes calldata _data) external returns(bool); + function batchDecreaseAllowance(address[] calldata _spender, uint256[] calldata _amount) external returns(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 + * @notice Transfers multiple bonds with amounts specified in the array `_amount` to the corresponding accounts in the array `_to`, with the option to attach additional data + * @param _to array of accounts to send the bonds to + * @param _amount array of amounts of bond tokens to transfer + * @param _data array of 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. + * OPTIONAL - interfaces and other contracts MUST NOT expect this function to be present. */ - function transferAll(address _to, bytes calldata _data) external returns(bool); + function batchTransfer(address[] calldata _to, uint256[] calldata _amount, bytes[] calldata _data) external returns(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 + * @notice Transfers multiple bonds with amounts specified in the array `_amount` to the corresponding accounts in the array `_to` from an account that have been authorized by the `_from` account + * This method also allows to attach data to tokens that are being transferred + * @param _from array of bondholder accounts + * @param _to array of accounts to transfer bond tokens to + * @param _amount array of amounts of bond tokens to transfer. + * @param _data array of additional information provided by the token holder + * + ** OPTIONAL - interfaces and other contracts MUST NOT expect this function to be present. */ - function transferFrom(address _from, address _to, uint256 _amount, bytes calldata _data) external returns(bool); + function batchTransferFrom(address[] calldata _from, address[] calldata _to, uint256[] calldata _amount, bytes[] calldata _data) external returns(bool); +} +``` + +### Additional bond parameters Interface + +The `IERC7092ESG` interface is OPTIONAL for contracts implementing this proposal. This interface MAY be used to improve the standard usability. + +- The `currencyOfCoupon` The currency used for coupon payment may be different from the currency used to repay the principal +- The `couponType` MAY be employed to signify the interest rate that the issuer has committed to paying to investors, which may take various forms such as zero coupon, fixed rate, floating rate, and more. +- The `couponFrequency` refers to how often the bond pays interest to its bondholders, and is typically expressed in terms of time periods, such as: Annual, Semi-Annual, Quarterly, or Monthly. +- The `dayCountBasis` is used to calculate the accrued interest on a bond between two coupon payment dates or other specific periods. Some of the day count basis are: Actual/Actual, 30/360, Actual/360, Actual/365, or 30/365 +```solidity +// SPDX-License-Identifier: CC0-1.0 +pragma solidity ^0.8.0; + +interface IERC7092ESG /** is ERC165 */ { /** - * @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. + * @notice Returns the number of decimals used by the bond. For example, if it returns `10`, it means that the token amount MUST be multiplied by 10000000000 to get the standard representation. */ - function transferAllFrom(address _from, address _to, bytes calldata _data) external returns(bool); + function decimals() external view returns(uint8); /** - * @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 + * @notice Rreturns the coupon currency, which is represented by the contract address of the token used to pay coupons. It can be the same as the one used for the principal */ - event Transferred(address _from, address _to, uint256 _amount, bytes _data); + function currencyOfCoupon() external view returns(address); /** - * @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. + * @notice Returns the coupon type + * For example, 0 can denote Zero coupon, 1 can denote Fixed Rate, 2 can denote Floating Rate, and so on */ - event Approved(address _owner, address _spender, uint256 _amount); + function couponType() external view returns(uint8); /** - * @notice MUST be emitted 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 + * @notice Returns the coupon frequency, i.e. the number of times coupons are paid in a year. + */ + function couponFrequency() external view returns(uint256); + + /** + * @notice Returns the day count basis + * For example, 0 can denote actual/actual, 1 can denote actual/360, and so on */ - event AllowanceDecreased(address _owner, address _spender, uint256 _amount); + function dayCountBasis() external view returns(uint8); } ``` -## Rationale +### Cross-chain Interface + +The standard permits the implementation of the `IERC7092CrossChain` interface for cross-chain management of bond tokens. This interface is OPTIONAL and may be used by applications to allow cross-chain transactions. Any function initiating a cross-chain transaction MUST explicitly define the destination chain identifier `destinationChainID` and specify the target smart contract `destinationContract`. -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. +```solidity +// SPDX-License-Identifier: CC0-1.0 +pragma solidity ^0.8.0; -The use of terminology such as `issueVolume` and `principalOf` instead of `totalSupply` and `balanceOf` respectively, as used in other standards like -[ERC-20](./eip-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. +interface IERC7092CrossChain /** is ERC165 */ { + // events + /** + * @notice MUST be emitted when bond tokens are transferred or redeemed in a cross-chain transaction + * @param _from bondholder account + * @param _to account the transfer bond tokens to + * @param _amount amount of bond tokens to be transferred + * @param _destinationChainID The unique ID that identifies the destination Chain + */ + event CrossChainTransfer(address indexed _from, address indexed _to, uint256 _amount, bytes32 _destinationChainID); -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. + /** + * @notice MUST be emitted when several bond tokens are transferred or redeemed in a cross-chain transaction + * @param _from array of bondholders accounts + * @param _to array of accounts that receive the bond + * @param _amount array of amount of bond tokens to be transferred + * @param _destinationChainID array of unique IDs that identify the destination Chain + */ + event CrossChainTransferBatch(address[] _from, address[] _to, uint256[] _amount, bytes32[] _destinationChainID); -### Total Supply + /** + * @notice MUST be emitted when an account is approved to spend the bondholder's tokens in a different chain than the current chain + * @param _owner the bondholder account + * @param _spender the account to be allowed to spend bonds + * @param _amount amount of bond tokens allowed by `_owner` to be spent by `_spender` + * @param _destinationChainID The unique ID that identifies the destination Chain + */ + event CrossChainApproval(address indexed _owner, address indexed _spender, uint256 _amount, bytes32 _destinationChainID); -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`. + /** + * @notice MUST be emitted when multiple accounts in the array `_spender` are approved or when the allowances of multiple accounts in the array `_spender` are reduced on the destination chain which MUST be different than the current chain + * @param _owner bond token's owner + * @param _spender array of accounts to be allowed to spend bonds + * @param _amount array of amount of bond tokens allowed by _owner to be spent by _spender + * @param _destinationChainID array of unique IDs that identify the destination Chain + */ + event CrossChainApprovalBatch(address indexed _owner, address[] _spender, uint256[] _amount, bytes32[] _destinationChainID); -```javascript - totalSupply = issueVolume / denomination -``` + // functions + /** + * @notice Authorizes the `_spender` account to manage a specified `_amount`of the bondholder bond tokens on the destination Chain + * @param _spender account to be authorized by the bondholder + * @param _amount amount of bond tokens to approve + * @param _destinationChainID The unique ID that identifies the destination Chain. + * @param _destinationContract The smart contract to interact with in the destination Chain + */ + function crossChainApprove(address _spender, uint256 _amount, bytes32 _destinationChainID, address _destinationContract) external returns(bool); + + /** + * @notice Authorizes multiple spender accounts in `_spender` to manage specified amounts in `_amount` of the bondholder tokens on the destination chain + * @param _spender array of accounts to be authorized by the bondholder + * @param _amount array of amounts of bond tokens to approve + * @param _destinationChainID array of unique IDs that identifies the destination Chain. + * @param _destinationContract array of smart contracts to interact with in the destination Chain in order to Deposit or Mint tokens that are transferred. + */ + function crossChainBatchApprove(address[] calldata _spender, uint256[] calldata _amount, bytes32[] calldata _destinationChainID, address[] calldata _destinationContract) external returns(bool); + + /** + * @notice Decreases the allowance of `_spender` by a specified `_amount` on the destination Chain + * @param _spender the address to be authorized by the bondholder + * @param _amount amount of bond tokens to remove from allowance + * @param _destinationChainID The unique ID that identifies the destination Chain. + * @param _destinationContract The smart contract to interact with in the destination Chain in order to Deposit or Mint tokens that are transferred. + */ + function crossChainDecreaseAllowance(address _spender, uint256 _amount, bytes32 _destinationChainID, address _destinationContract) external; + + /** + * @notice Decreases the allowance of multiple spenders in `_spender` by corresponding amounts specified in the array `_amount` on the destination chain + * @param _spender array of accounts to be authorized by the bondholder + * @param _amount array of amounts of bond tokens to decrease the allowance from + * @param _destinationChainID array of unique IDs that identifies the destination Chain. + * @param _destinationContract array of smart contracts to interact with in the destination Chain in order to Deposit or Mint tokens that are transferred. + */ + function crossChainBatchDecreaseAllowance(address[] calldata _spender, uint256[] calldata _amount, bytes32[] calldata _destinationChainID, address[] calldata _destinationContract) external; + + /** + * @notice Moves `_amount` bond tokens to the address `_to` from the current chain to another chain (e.g., moving tokens from Ethereum to Polygon). + * This methods also allows to attach data to the token that is being transferred + * @param _to account to send bond tokens to + * @param _amount amount of bond tokens to transfer + * @param _data additional information provided by the bondholder + * @param _destinationChainID The unique ID that identifies the destination Chain. + * @param _destinationContract The smart contract to interact with in the destination Chain in order to Deposit or Mint bond tokens that are transferred. + */ + function crossChainTransfer(address _to, uint256 _amount, bytes calldata _data, bytes32 _destinationChainID, address _destinationContract) external returns(bool); -### Account Balance + /** + * @notice Transfers multiple bond tokens with amounts specified in the array `_amount` to the corresponding accounts in the array `_to` from the current chain to another chain (e.g., moving tokens from Ethereum to Polygon). + * This methods also allows to attach data to the token that is being transferred + * @param _to array of accounts to send the bonds to + * @param _amount array of amounts of bond tokens to transfer + * @param _data array of additional information provided by the bondholder + * @param _destinationChainID array of unique IDs that identify the destination Chains. + * @param _destinationContract array of smart contracts to interact with in the destination Chains in order to Deposit or Mint bond tokens that are transferred. + */ + function crossChainBatchTransfer(address[] calldata _to, uint256[] calldata _amount, bytes[] calldata _data, bytes32[] calldata _destinationChainID, address[] calldata _destinationContract) external returns(bool); -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`. + /** + * @notice Transfers `_amount` bond tokens from the `_from`account to the `_to` account from the current chain to another chain. The caller must be approved by the `_from` address. + * This methods also allows to attach data to the token that is being transferred + * @param _from the bondholder address + * @param _to the account to transfer bonds to + * @param _amount amount of bond tokens to transfer + * @param _data additional information provided by the token holder + * @param _destinationChainID The unique ID that identifies the destination Chain. + * @param _destinationContract The smart contract to interact with in the destination Chain in order to Deposit or Mint tokens that are transferred. + */ + function crossChainTransferFrom(address _from, address _to, uint256 _amount, bytes calldata _data, bytes32 _destinationChainID, address _destinationContract) external returns(bool); -```javascript - balanceOf(account) = principalOf(account) / denomination + /** + * @notice Transfers several bond tokens with amounts specified in the array `_amount` from accounts in the array `_from` to accounts in the array `_to` from the current chain to another chain. + * The caller must be approved by the `_from` accounts to spend the corresponding amounts specified in the array `_amount` + * This methods also allows to attach data to the token that is being transferred + * @param _from array of bondholder addresses + * @param _to array of accounts to transfer bonds to + * @param _amount array of amounts of bond tokens to transfer + * @param _data array of additional information provided by the token holder + * @param _destinationChainID array of unique IDs that identifies the destination Chain. + * @param _destinationContract array of smart contracts to interact with in the destination Chain in order to Deposit or Mint tokens that are transferred. + */ + function crossChainBatchTransferFrom(address[] calldata _from, address[] calldata _to, uint256[] calldata _amount, bytes[] calldata _data, bytes32[] calldata _destinationChainID, address[] calldata _destinationContract) external returns(bool); +} ``` -## Backwards Compatibility +## Rationale -Beacause some functions like `totalSupply` or `balanceOf` are not implemented, the ERC-7092 SHOULD NOT extend existing standards, -like [ERC-20](./eip-20) or [ERC-1155](./eip-1155). The ERC-7092 is a representation of a new token for bonds with all bonds characteristics -and functionalities already attached to it. +The design of this ERC aims to simplify the migration to tokenized bonds by maintaining consistency with traditional bond standards. This approach allows fixed-income instruments to be represented as on-chain tokens, manageable through wallets, and utilized by applications like decentralized exchanges, while avoiding the complexities and inefficiencies associated with other standards. This ERC facilitates the creation of new bond tokens with characteristics akin to traditional bonds, enhancing accessibility, liquidity, and cost-efficiency in bond trading and management. -**_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. +The use of traditional finance terminology, like `issueVolume` and `principalOf`, is aimed at maintaining consistency with traditional bond language, which eases the adaptation for traditional entities. +### Total Supply and Account Balance -## Reference Implementation +The `totalSupply` and `balanceOf` functions are not defined as they can be derived from `issueVolume` and `principalOf`, and `denomination`. However, these functions can be be added in any contract implementing this standard, ensuring the proper relationship between these values. + +```solidity + function totalSupply() external view returns(uint256) { + return issueVolume() / denomination(); + } -The reference implementation of the [ERC-7092](./eip-7092.md) can be found [here](../assets/eip-7092/ERC7092.sol). + function balance0f(account) external view returns(uint256) { + return principal(account) / denomination(); + } +``` + +## Backwards Compatibility + +This ERC is not backwards compatible with existing standards like [ERC-20](./erc-20.md) or [ERC-1155](./erc-1155.md) due to the absence of certain functions like `totalSupply` or `balanceOf`. A pure implementation of this standard is RECOMMENDED for issuing tokenized bonds, as any hybrid solution with other mentioned standards SHOULD fail. -Some bonds have embedded options attached to them. As an example we can cite: -1. Callable bonds that have an option that gives the issuer the right to retire bonds before they mature. -2. Puttable bonds that have an option that gives investors the right to retire bonds before they mature. -3. Convertible bonds that gives investors the right to convert their bonds to equity. +## Reference Implementation + +The complete Reference Implementation can be found [here](../assets/erc-7092/ERC7092.sol). -Bonds with embedded options can be created by inheriting from the basic ERC-7092 that integrates the proposed interface. +Bonds with embedded options like callable, puttable, or convertible bonds can be created by inheriting from the reference [`ERC7092.sol`](../assets/erc-7092/ERC7092.sol) that integrates the proposed interface. ### CALLABLE BONDS: @@ -338,7 +435,8 @@ contract ERC7092Callable is ERC7092 { * MUST be called by the issuer only */ function call(address _investor) public { - require(_principals[_investor] > 0, "ERC7092Callable: ONLY_ISSUER"); + require(msg.sender == _issuer[bondISIN].issuerAddress, "ERC7092Callable: ONLY_ISSUER"); + require(_principals[_investor] > 0, "ERC7092Callable: NO_BONDS"); require(block.timestamp < _bond[bondISIN].maturityDate, "ERC7092Callable: BOND_MATURED"); uint256 principal = _principals[_investor]; @@ -403,9 +501,15 @@ contract ERC7092Convertible is ERC7092 { } ``` +### Identity Registry + +This standard is designed specifically for tokenizing bonds. It does not inherently manage information pertaining to bondholders' identities. However, to enhance compliance with regulatory requirements and improve transparency, an identity registry can be added on top of this standard to store the identity of all authorized investors. + +By maintaining an identity registry, issuers can ensure that bond tokens issued under the `ERC7092` standard are transferred only to registered and authorized entities. This practice aligns with regulatory compliance measures and provides a structured way to manage and verify the identity of bondholders. It also helps prevent unauthorized or non-compliant transfers of bond tokens. + ## 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. +Implementing this ERC requires careful consideration of security risks related to functions approving operators to manage owner's bonds and functions allowing bond transfers. The use of these functions necessitates robust validation to ensure only the bond owner or approved accounts can call them. ## Copyright diff --git a/assets/erc-7092/BondStorage.sol b/assets/erc-7092/BondStorage.sol index d68928ac5e..fe6ca89d37 100644 --- a/assets/erc-7092/BondStorage.sol +++ b/assets/erc-7092/BondStorage.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: CC0-1.0 -pragma solidity ^0.8.18; +pragma solidity ^0.8.0; contract BondStorage { struct Bond { @@ -7,27 +7,21 @@ contract BondStorage { string name; string symbol; address currency; - address currencyOfCoupon; - uint8 decimals; uint256 denomination; uint256 issueVolume; uint256 couponRate; - uint256 couponType; - uint256 couponFrequency; uint256 issueDate; uint256 maturityDate; - uint256 dayCountBasis; } struct Issuer { + address issuerAddress; string name; string email; string country; - string headquarters; string issuerType; string creditRating; uint256 carbonCredit; - address issuerAddress; } struct IssueData { @@ -37,13 +31,12 @@ contract BondStorage { enum BondStatus {UNREGISTERED, SUBMITTED, ISSUED, REDEEMED} - mapping(string => Bond) internal _bond; + mapping(string => Bond) internal _bonds; mapping(string => Issuer) internal _issuer; mapping(address => uint256) internal _principals; - mapping(address => mapping(address => uint256)) internal _approvals; + mapping(address => mapping(address => uint256)) internal _allowed; string internal bondISIN; - string internal _countryOfIssuance; BondStatus internal _bondStatus; IssueData[] internal _listOfInvestors; @@ -57,20 +50,4 @@ contract BondStorage { event BondIssued(IssueData[] _issueData, Bond _bond); event BondRedeemed(); - - function bondStatus() external view returns(BondStatus) { - return _bondStatus; - } - - function listOfInvestors() external view returns(IssueData[] memory) { - return _listOfInvestors; - } - - function bondInfo() public view returns(Bond memory) { - return _bond[bondISIN]; - } - - function issuerInfo() public view returns(Issuer memory) { - return _issuer[bondISIN]; - } } diff --git a/assets/erc-7092/ERC7092.sol b/assets/erc-7092/ERC7092.sol index cf410ed8ed..7ab261980c 100644 --- a/assets/erc-7092/ERC7092.sol +++ b/assets/erc-7092/ERC7092.sol @@ -4,9 +4,6 @@ pragma solidity ^0.8.0; import "./IERC7092.sol"; import "./BondStorage.sol"; -/** -* @notice Minimum implementation of the ERC7092 -*/ contract ERC7092 is IERC7092, BondStorage { constructor( string memory _bondISIN, @@ -28,214 +25,206 @@ contract ERC7092 is IERC7092, BondStorage { _redeem(_listOfInvestors); } - function _issue(IssueData[] memory _issueData, Bond memory _bondInfo) internal virtual { - uint256 volume; - uint256 _issueVolume = _bondInfo.issueVolume; - - for(uint256 i; i < _issueData.length; i++) { - address investor = _issueData[i].investor; - uint256 principal = _issueData[i].principal; - uint256 _denomination = _bondInfo.denomination; - - require(investor != address(0), "ERC7092: ZERO_ADDRESS_INVESTOR"); - require(principal != 0 && (principal * _denomination) % _denomination == 0, "ERC: INVALID_PRINCIPAL_AMOUNT"); - - volume += principal; - _principals[investor] = principal; - _listOfInvestors.push(IssueData({investor:investor, principal:principal})); - } - - _bond[bondISIN] = _bondInfo; - _bond[bondISIN].issueDate = block.timestamp; - _bondStatus = BondStatus.ISSUED; - - uint256 _maturityDate = _bond[bondISIN].maturityDate; - - require(_maturityDate > block.timestamp, "ERC7092: INVALID_MATURITY_DATE"); - require(volume == _issueVolume, "ERC7092: INVALID_ISSUE_VOLUME"); - - emit BondIssued(_issueData, _bondInfo); - } - - function _redeem(IssueData[] memory _bondsData) internal virtual { - uint256 _maturityDate = _bond[bondISIN].maturityDate; - require(block.timestamp > _maturityDate, "ERC2721: WAIT_MATURITY"); - - for(uint256 i; i < _bondsData.length; i++) { - if(_principals[_bondsData[i].investor] != 0) { - _principals[_bondsData[i].investor] = 0; - } - } - - _bondStatus = BondStatus.REDEEMED; - emit BondRedeemed(); - } - function isin() external view returns(string memory) { - return _bond[bondISIN].isin; + return _bonds[bondISIN].isin; } function name() external view returns(string memory) { - return _bond[bondISIN].name; + return _bonds[bondISIN].name; } function symbol() external view returns(string memory) { - return _bond[bondISIN].symbol; - } - - function decimals() external view returns(uint8) { - return _bond[bondISIN].decimals; + return _bonds[bondISIN].symbol; } function currency() external view returns(address) { - return _bond[bondISIN].currency; + return _bonds[bondISIN].currency; } - function currencyOfCoupon() external view returns(address) { - return _bond[bondISIN].currencyOfCoupon; + function denomination() external view returns(uint256) { + return _bonds[bondISIN].denomination; } - function denomination() public view returns(uint256) { - return _bond[bondISIN].denomination; + function issueVolume() external view returns(uint256) { + return _bonds[bondISIN].issueVolume; } - function issueVolume() external view returns(uint256) { - return _bond[bondISIN].issueVolume; + function totalSupply() external view returns(uint256) { + return _bonds[bondISIN].issueVolume / _bonds[bondISIN].denomination; } function couponRate() external view returns(uint256) { - return _bond[bondISIN].couponRate; + return _bonds[bondISIN].couponRate; } - function couponType() external view returns(uint256) { - return _bond[bondISIN].couponType; + function issueDate() external view returns(uint256) { + return _bonds[bondISIN].issueDate; } - function couponFrequency() external view returns(uint256) { - return _bond[bondISIN].couponFrequency; + function maturityDate() external view returns(uint256) { + return _bonds[bondISIN].maturityDate; } - function issueDate() external view returns(uint256) { - return _bond[bondISIN].issueDate; + function principalOf(address _account) external view returns(uint256) { + return _principals[_account]; } - function maturityDate() public view returns(uint256) { - return _bond[bondISIN].maturityDate; + function balanceOf(address _account) external view returns(uint256) { + return _principals[_account] / _bonds[bondISIN].denomination; } - function dayCountBasis() external view returns(uint256) { - return _bond[bondISIN].dayCountBasis; + function allowance(address _owner, address _spender) external view returns(uint256) { + return _allowed[_owner][_spender]; } - function principalOf(address _account) external view returns(uint256) { - return _principals[_account]; - } + function approve(address _spender, uint256 _amount) external returns(bool) { + address _owner = msg.sender; - function balanceOf(address _account) public view returns(uint256) { - require(_bondStatus == BondStatus.ISSUED, "ERC7092: NOT_ISSUED_OR_REDEEMED"); + _approve(_owner, _spender, _amount); - return _principals[_account] / _bond[bondISIN].denomination; + return true; } - function totalSupply() public view returns(uint256) { - require(_bondStatus == BondStatus.ISSUED, "ERC7092: NOT_ISSUED_OR_REDEEMED"); + function decreaseAllowance(address _spender, uint256 _amount) external returns(bool) { + address _owner = msg.sender; - return _bond[bondISIN].issueVolume / _bond[bondISIN].denomination; - } + _decreaseAllowance(_owner, _spender, _amount); - function approval(address _owner, address _spender) external view returns(uint256) { - return _approvals[_owner][_spender]; + return true; } - function approve(address _spender, uint256 _amount) external returns(bool) { - address _owner = msg.sender; + function transfer(address _to, uint256 _amount, bytes calldata _data) external returns(bool) { + address _from = msg.sender; - _approve(_owner, _spender, _amount); + _transfer(_from, _to, _amount, _data); return true; } - function approveAll(address _spender) external external returns(bool) { - address _owner = msg.sender; - uint256 _amount = _principals[_owner]; + function transferFrom(address _from, address _to, uint256 _amount, bytes calldata _data) external returns(bool) { + address _spender = msg.sender; - _approve(_owner, _spender, _amount); + _spendAllowance(_from, _spender, _amount); + + _transfer(_from, _to, _amount, _data); return true; } - function decreaseAllowance(address _spender, uint256 _amount) external returns(bool) { + function batchApprove(address[] calldata _spender, uint256[] calldata _amount) external returns(bool) { address _owner = msg.sender; - _decreaseAllowance(_owner, _spender, _amount); + _batchApprove(_owner, _spender, _amount); return true; } - function decreaseAllowanceForAll(address _spender) external returns(bool) { + function batchDecreaseAllowance(address[] calldata _spender, uint256[] calldata _amount) external returns(bool) { address _owner = msg.sender; - uint256 _amount = _principals[_owner]; _decreaseAllowance(_owner, _spender, _amount); return true; } - function transfer(address _to, uint256 _amount, bytes calldata _data) external returns(bool) { - address _from = msg.sender; + function batchTransfer(address[] calldata _to, uint256[] calldata _amount, bytes[] calldata _data) external returns(bool) { + address[] memory _from; - _transfer(_from, _to, _amount, _data); + for(uint256 i = 0; i < _from.length; i++) { + _from[i] = msg.sender; + } + + _batchTransfer(_from, _to, _amount, _data); return true; } - function transferAll(address _to, bytes calldata _data) external returns(bool) { - address _from = msg.sender; - uint256 _amount = _principals[_from]; + function batchTransferFrom(address[] calldata _from, address[] calldata _to, uint256[] calldata _amount, bytes[] calldata _data) external returns(bool) { + address _spender; - _transfer(_from, _to, _amount, _data); + _batchSpendAllowance(_from, _spender, _amount); + + _batchTransfer(_from, _to, _amount, _data); return true; } - function transferFrom(address _from, address _to, uint256 _amount, bytes calldata _data) external returns(bool) { - address _spender = msg.sender; - - _spendApproval(_from, _spender, _amount); + function bondStatus() external view returns(BondStatus) { + return _bondStatus; + } - _transfer(_from, _to, _amount, _data); + function listOfInvestors() external view returns(IssueData[] memory) { + return _listOfInvestors; + } - return true; + function bondInfo() public view returns(Bond memory) { + return _bonds[bondISIN]; + } + + function issuerInfo() public view returns(Issuer memory) { + return _issuer[bondISIN]; } - function transferAllFrom(address _from, address _to, bytes calldata _data) external returns(bool) { - address _spender = msg.sender; - uint256 _amount = _principals[_from]; + function _issue(IssueData[] memory _issueData, Bond memory _bondInfo) internal virtual { + uint256 volume; + uint256 _issueVolume = _bondInfo.issueVolume; + + for(uint256 i; i < _issueData.length; i++) { + address investor = _issueData[i].investor; + uint256 principal = _issueData[i].principal; + uint256 _denomination = _bondInfo.denomination; + + require(investor != address(0), "ERC7092: ZERO_ADDRESS_INVESTOR"); + require(principal != 0 && (principal * _denomination) % _denomination == 0, "ERC: INVALID_PRINCIPAL_AMOUNT"); - _spendApproval(_from, _spender, _amount); + volume += principal; + _principals[investor] = principal; + _listOfInvestors.push(IssueData({investor:investor, principal:principal})); + } + + _bonds[bondISIN] = _bondInfo; + _bonds[bondISIN].issueDate = block.timestamp; + _bondStatus = BondStatus.ISSUED; - _transfer(_from, _to, _amount, _data); + uint256 _maturityDate = _bonds[bondISIN].maturityDate; - return true; + require(_maturityDate > block.timestamp, "ERC7092: INVALID_MATURITY_DATE"); + require(volume == _issueVolume, "ERC7092: INVALID_ISSUE_VOLUME"); + + emit BondIssued(_issueData, _bondInfo); + } + + function _redeem(IssueData[] memory _bondsData) internal virtual { + uint256 _maturityDate = _bonds[bondISIN].maturityDate; + require(block.timestamp > _maturityDate, "ERC2721: WAIT_MATURITY"); + + for(uint256 i; i < _bondsData.length; i++) { + if(_principals[_bondsData[i].investor] != 0) { + _principals[_bondsData[i].investor] = 0; + } + } + + _bondStatus = BondStatus.REDEEMED; + emit BondRedeemed(); } function _approve(address _owner, address _spender, uint256 _amount) internal virtual { require(_owner != address(0), "ERC7092: OWNER_ZERO_ADDRESS"); require(_spender != address(0), "ERC7092: SPENDER_ZERO_ADDRESS"); require(_amount > 0, "ERC7092: INVALID_AMOUNT"); + require(block.timestamp < _bonds[bondISIN].maturityDate, "ERC7092: BONDS_MATURED"); + + uint256 _balance = _principals[_owner] / _bonds[bondISIN].denomination; + uint256 _denomination = _bonds[bondISIN].denomination; - uint256 principal = _principals[_owner]; - uint256 _approval = _approvals[_owner][_spender]; - uint256 _denomination = denomination(); - uint256 _maturityDate = maturityDate(); + require(_amount <= _balance, "ERC7092: INSUFFICIENT_BALANCE"); + require((_amount * _denomination) % _denomination == 0, "ERC7092: INVALID_AMOUNT"); - require(block.timestamp < _maturityDate, "ERC7092: BONDS_MATURED"); - require(_amount <= principal, "ERC7092: INSUFFICIENT_BALANCE"); - require(_amount % _denomination == 0, "ERC7092: INVALID_AMOUNT"); + uint256 _approval = _allowed[_owner][_spender]; - _approvals[_owner][_spender] = _approval + _amount; + _allowed[_owner][_spender] = _approval + _amount; - emit Approved(_owner, _spender, _amount); + emit Approval(_owner, _spender, _amount); } function _decreaseAllowance(address _owner, address _spender, uint256 _amount) internal virtual { @@ -243,56 +232,160 @@ contract ERC7092 is IERC7092, BondStorage { require(_spender != address(0), "ERC7092: SPENDER_ZERO_ADDRESS"); require(_amount > 0, "ERC7092: INVALID_AMOUNT"); - uint256 _approval = _approvals[_owner][_spender]; - uint256 _denomination = denomination(); - uint256 _maturityDate = maturityDate(); + uint256 _allowance = _allowed[_owner][_spender]; + uint256 _denomination = _bonds[bondISIN].denomination; - require(block.timestamp < _maturityDate, "ERC7092: BONDS_MATURED"); - require(_amount <= _approval, "ERC7092: NOT_ENOUGH_APPROVAL"); - require(_amount % _denomination == 0, "ERC7092: INVALID_AMOUNT"); + require(block.timestamp < _bonds[bondISIN].maturityDate, "ERC7092: BONDS_MATURED"); + require(_amount <= _allowance, "ERC7092: NOT_ENOUGH_APPROVAL"); + require((_amount * _denomination) % _denomination == 0, "ERC7092: INVALID_AMOUNT"); - _approvals[_owner][_spender] = _approval - _amount; + _allowed[_owner][_spender] = _allowance - _amount; - emit AllowanceDecreased(_owner, _spender, _amount); + emit Approval(_owner, _spender, _amount); } - function _transfer(address _from, address _to, uint256 _amount, bytes calldata _data) internal virtual { + function _transfer( + address _from, + address _to, + uint256 _amount, + bytes calldata _data + ) internal virtual { require(_from != address(0), "ERC7092: OWNER_ZERO_ADDRESS"); require(_to != address(0), "ERC7092: SPENDER_ZERO_ADDRESS"); require(_amount > 0, "ERC7092: INVALID_AMOUNT"); uint256 principal = _principals[_from]; - uint256 _denomination = denomination(); - uint256 _maturityDate = maturityDate(); + uint256 _denomination = _bonds[bondISIN].denomination; + uint256 _balance = principal / _denomination; - require(block.timestamp < _maturityDate, "ERC7092: BONDS_MATURED"); - require(_amount <= principal, "ERC7092: INSUFFICIENT_BALANCE"); - require(_amount % _denomination == 0, "ERC7092: INVALID_AMOUNT"); + require(block.timestamp < _bonds[bondISIN].maturityDate, "ERC7092: BONDS_MATURED"); + require(_amount <= _balance, "ERC7092: INSUFFICIENT_BALANCE"); + require((_amount * _denomination) % _denomination == 0, "ERC7092: INVALID_AMOUNT"); _beforeBondTransfer(_from, _to, _amount, _data); uint256 principalTo = _principals[_to]; unchecked { - _principals[_from] = principal - _amount; - _principals[_to] = principalTo + _amount; + uint256 _principalToTransfer = _amount * _denomination; + + _principals[_from] = principal - _principalToTransfer; + _principals[_to] = principalTo + _principalToTransfer; } - emit Transferred(_from, _to, _amount, _data); + emit Transfer(_from, _to, _amount); _afterBondTransfer(_from, _to, _amount, _data); } - function _spendApproval(address _from, address _spender, uint256 _amount) internal virtual { - uint256 currentApproval = _approvals[_from][_spender]; - require(_amount <= currentApproval, "ERC7092: INSUFFICIENT_ALLOWANCE"); + function _spendAllowance(address _from, address _spender, uint256 _amount) internal virtual { + uint256 currentAllowance = _allowed[_from][_spender]; + require(_amount <= currentAllowance, "ERC7092: INSUFFICIENT_ALLOWANCE"); unchecked { - _approvals[_from][_spender] = currentApproval - _amount; + _allowed[_from][_spender] = currentAllowance - _amount; + } + } + + function _batchApprove(address _owner, address[] calldata _spender, uint256[] calldata _amount) internal virtual { + require(_owner != address(0), "ERC7092: OWNER_ZERO_ADDRESS"); + require(block.timestamp < _bonds[bondISIN].maturityDate, "ERC7092: BONDS_MATURED"); + require(_spender.length == _amount.length, "ARRAY_LENGTHS_MISMATCH"); + + uint256 _balance = _principals[_owner] / _bonds[bondISIN].denomination; + uint256 _denomination = _bonds[bondISIN].denomination; + + for(uint256 i = 0; i < _spender.length; i++) { + require(_spender[i] != address(0), "ERC7092: SPENDER_ZERO_ADDRESS"); + require(_amount[i] > 0, "ERC7092: INVALID_AMOUNT"); + require(_balance >= _amount[i], "ERC7092: INSUFFICIENT_BALANCE"); + require((_amount[i] * _denomination) % _denomination == 0, "ERC7092: INVALID_AMOUNT"); + + uint256 _approval = _allowed[_owner][_spender[i]]; + + _allowed[_owner][_spender[i]] = _approval + _amount[i]; } - } - function _beforeBondTransfer(address _from, address _to, uint256 _amount, bytes calldata _data) internal virtual {} + emit ApprovalBatch(_owner, _spender, _amount); + } + + function _decreaseAllowance(address _owner, address[] calldata _spender, uint256[] calldata _amount) internal virtual { + require(_owner != address(0), "ERC7092: OWNER_ZERO_ADDRESS"); + require(block.timestamp < _bonds[bondISIN].maturityDate, "ERC7092: BONDS_MATURED"); + require(_spender.length == _amount.length, "ARRAY_LENGTHS_MISMATCH"); + + uint256 _denomination = _bonds[bondISIN].denomination; + + for(uint256 i = 0; i < _spender.length; i++) { + require(_spender[i] != address(0), "ERC7092: SPENDER_ZERO_ADDRESS"); + require(_amount[i] > 0, "ERC7092: INVALID_AMOUNT"); + require((_amount[i] * _denomination) % _denomination == 0, "ERC7092: INVALID_AMOUNT"); - function _afterBondTransfer(address _from, address _to, uint256 _amount, bytes calldata _data) internal virtual {} + uint256 _approval = _allowed[_owner][_spender[i]]; + require(_amount[i] <= _approval, "ERC7092: NOT_ENOUGH_APPROVAL"); + + _allowed[_owner][_spender[i]] = _approval - _amount[i]; + } + + emit ApprovalBatch(_owner, _spender, _amount); + } + + function _batchTransfer(address[] memory _from, address[] calldata _to, uint256[] calldata _amount, bytes[] calldata _data) internal virtual { + require(block.timestamp < _bonds[bondISIN].maturityDate, "ERC7092: BONDS_MATURED"); + require(_from.length == _to.length, "ARRAY_LENGTHS_MISMATCH"); + + uint256 _denomination = _bonds[bondISIN].denomination; + + for(uint256 i = 0; i < _from.length; i++) { + require(_from[i] != address(0), "ERC7092: OWNER_ZERO_ADDRESS"); + require(_to[i] != address(0), "ERC7092: SPENDER_ZERO_ADDRESS"); + require(_amount[i] > 0, "ERC7092: INVALID_AMOUNT"); + + uint256 principal = _principals[_from[i]]; + uint256 _balance = principal / _denomination; + + require(_amount[i] <= _balance, "ERC7092: INSUFFICIENT_BALANCE"); + require((_amount[i] * _denomination) % _denomination == 0, "ERC7092: INVALID_AMOUNT"); + + _beforeBondTransfer(_from[i], _to[i], _amount[i], _data[i]); + + uint256 principalTo = _principals[_to[i]]; + + unchecked { + uint256 _principalToTransfer = _amount[i] * _denomination; + + _principals[_from[i]] = principal - _principalToTransfer; + _principals[_to[i]] = principalTo + _principalToTransfer; + } + + _afterBondTransfer(_from[i], _to[i], _amount[i], _data[i]); + } + + emit TransferBatch(_from, _to, _amount); + } + + function _batchSpendAllowance(address[] calldata _from, address _spender, uint256[] calldata _amount) internal virtual { + for(uint256 i = 0; i < _from.length; i++) { + uint256 currentAllowance = _allowed[_from[i]][_spender]; + require(_amount[i] <= currentAllowance, "ERC7092: INSUFFICIENT_ALLOWANCE"); + + unchecked { + _allowed[_from[i]][_spender] = currentAllowance - _amount[i]; + } + } + } + + function _beforeBondTransfer( + address _from, + address _to, + uint256 _amount, + bytes calldata _data + ) internal virtual {} + + function _afterBondTransfer( + address _from, + address _to, + uint256 _amount, + bytes calldata _data + ) internal virtual {} } diff --git a/assets/erc-7092/IERC7092.sol b/assets/erc-7092/IERC7092.sol index bae6f376f8..48f25f58ce 100644 --- a/assets/erc-7092/IERC7092.sol +++ b/assets/erc-7092/IERC7092.sol @@ -3,6 +3,8 @@ pragma solidity ^0.8.0; /** * @title ERC-7092 Financial Bonds tandard +* This interface implements only functions that are REQUIRED for the ERC7092 standard. +* OPTIONAL and INTEROPERABLE functions may be added to allow further functionalities and interoperability of bonds */ interface IERC7092 { /** @@ -17,26 +19,16 @@ interface IERC7092 { /** * @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` */ function symbol() external view returns(string memory); - /** - * @notice Returns the number of decimals the bond uses - e.g `10`, means to divide the token amount by `10000000000` - * - * OPTIONAL - */ - function decimals() external view returns(uint8); - /** * @notice Returns the bond currency. This is the contract address of the token used to pay and return the bond principal */ function currency() external view returns(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 - */ - function currencyOfCoupon() external view returns(address); - /** * @notice Returns the bond denominiation. This is the minimum amount in which the Bonds may be issued. It must be expressend in unit of the principal currency * ex: If the denomination is equal to 1,000 and the currency is USDC, then the bond denomination is equal to 1,000 USDC @@ -45,8 +37,6 @@ interface IERC7092 { /** * @notice Returns the issue volume (total debt amount). It is RECOMMENDED to express the issue volume in denomination unit. - * ex: if denomination = $1,000, and the total debt is $5,000,000 - * then, issueVolume() = $5,000, 000 / $1,000 = 5,000 bonds */ function issueVolume() external view returns(uint256); @@ -57,17 +47,6 @@ interface IERC7092 { */ function couponRate() external view returns(uint256); - /** - * @notice Returns the coupon type - * ex: 0: Zero coupon, 1: Fixed Rate, 2: Floating Rate, etc... - */ - function couponType() external view returns(uint256); - - /** - * @notice Returns the coupon frequency, i.e. the number of times coupons are paid in a year. - */ - function couponFrequency() external view returns(uint256); - /** * @notice Returns the date when bonds were issued to investors. This is a Unix Timestamp like the one returned by block.timestamp */ @@ -80,15 +59,7 @@ interface IERC7092 { function maturityDate() external view returns(uint256); /** - * @notice Returns the day count basis - * Ex: 0: actual/actual, 1: actual/360, etc... - */ - function dayCountBasis() external view returns(uint256); - - /** - * @notice Returns the principal of an account. It is RECOMMENDED to express the principal in denomination unit. - * Ex: if denomination = $1,000, and the user has invested $5,000 - * then principalOf(_account) = 5,000/1,000 = 5 + * @notice Returns the principal of an account. It is RECOMMENDED to express the principal in the bond currency unit (USDC, DAI, etc...) * @param _account account address */ function principalOf(address _account) external view returns(uint256); @@ -99,88 +70,114 @@ interface IERC7092 { * @param _owner the bondholder address * @param _spender the address that has been authorized by the bondholder */ - function approval(address _owner, address _spender) external view returns(uint256); + function allowance(address _owner, address _spender) external view returns(uint256); /** - * @notice Authorizes `_spender` account to manage `_amount`of their bonds + * @notice Authorizes `_spender` account to manage `_amount`of their bond tokens * @param _spender the address to be authorized by the bondholder - * @param _amount amount of bond to approve. _amount MUST be a multiple of denomination + * @param _amount amount of bond tokens to approve */ - function approve(address _spender, uint256 _amount) external; + function approve(address _spender, uint256 _amount) external returns(bool); /** - * @notice Authorizes the `_spender` account to manage all their bonds + * @notice Lowers the allowance of `_spender` by `_amount` * @param _spender the address to be authorized by the bondholder + * @param _amount amount of bond tokens to remove from allowance */ - function approveAll(address _spender) external; + function decreaseAllowance(address _spender, uint256 _amount) external returns(bool); /** - * @notice Lowers 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 + * @notice Moves `_amount` bonds to address `_to`. This methods also allows to attach data to the token that is being transferred + * @param _to the address to send the bonds to + * @param _amount amount of bond tokens to transfer + * @param _data additional information provided by the token holder */ - function decreaseAllowance(address _spender, uint256 _amount) external; + function transfer(address _to, uint256 _amount, bytes calldata _data) external returns(bool); /** - * @notice Removes the allowance for `_spender` - * @param _spender the address to remove the authorization by from + * @notice Moves `_amount` bonds from an account that has authorized the caller through the approve function + * This methods also allows to attach data to the token that is being transferred + * @param _from the bondholder address + * @param _to the address to transfer bonds to + * @param _amount amount of bond tokens to transfer. + * @param _data additional information provided by the token holder */ - function decreaseAllowanceForAll(address _spender) external; + function transferFrom(address _from, address _to, uint256 _amount, bytes calldata _data) external returns(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 + * @notice Authorizes multiple spender accounts to manage a specified `_amount` of the bondholder tokens + * @param _spender array of accounts to be authorized by the bondholder + * @param _amount array of amounts of bond tokens to approve + * + * OPTIONAL - interfaces and other contracts MUST NOT expect these values to be present. The method is used to improve usability. */ - function transfer(address _to, uint256 _amount, bytes calldata _data) external; + function batchApprove(address[] calldata _spender, uint256[] calldata _amount) external returns(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 + * @notice Decreases the allowance of multiple spenders by corresponding amounts in `_amount` + * @param _spender array of accounts to be authorized by the bondholder + * @param _amount array of amounts of bond tokens to decrease the allowance from + * + * OPTIONAL - interfaces and other contracts MUST NOT expect this function to be present. The method is used to decrease token allowance. */ - function transferAll(address _to, bytes calldata _data) external; + function batchDecreaseAllowance(address[] calldata _spender, uint256[] calldata _amount) external returns(bool); /** - * @notice Moves `_amount` bonds from an account that has authorized the caller 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 + * @notice Transfers multiple bonds with amounts specified in the array `_amount` to the corresponding accounts in the array `_to`, with the option to attach additional data + * @param _to array of accounts to send the bonds to + * @param _amount array of amounts of bond tokens to transfer + * @param _data array of additional information provided by the token holder + * + * OPTIONAL - interfaces and other contracts MUST NOT expect this function to be present. */ - function transferFrom(address _from, address _to, uint256 _amount, bytes calldata _data) external; + function batchTransfer(address[] calldata _to, uint256[] calldata _amount, bytes[] calldata _data) external returns(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 + * @notice Transfers multiple bonds with amounts specified in the array `_amount` to the corresponding accounts in the array `_to` from an account that have been authorized by the `_from` account + * This method also allows to attach data to tokens that are being transferred + * @param _from array of bondholder accounts + * @param _to array of accounts to transfer bond tokens to + * @param _amount array of amounts of bond tokens to transfer. + * @param _data array of additional information provided by the token holder + * + ** OPTIONAL - interfaces and other contracts MUST NOT expect this function to be present. */ - function transferAllFrom(address _from, address _to, bytes calldata _data) external; + function batchTransferFrom(address[] calldata _from, address[] calldata _to, uint256[] calldata _amount, bytes[] calldata _data) external returns(bool); /** - * @notice MUST be emitted when bonds are transferred + * @notice MUST be emitted when bond tokens are transferred, issued or redeemed, except during contract creation * @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 + * @param _amount amount of bond tokens to be transferred */ - event Transferred(address _from, address _to, uint256 _amount, bytes _data); + event Transfer(address indexed _from, address indexed _to, uint256 _amount); /** - * @notice MUST be emitted when an account is approved - * @param _owner the bonds owner + * @notice MUST be emitted when an account is approved or when the allowance is decreased + * @param _owner bond token's owner * @param _spender the account to be allowed to spend bonds - * @param _amount the amount allowed by _owner to be spent by _spender. + * @param _amount amount of bond tokens allowed by _owner to be spent by `_spender` + * Or amount of bond tokens to decrease allowance from `_spender` */ - event Approved(address _owner, address _spender, uint256 _amount); + event Approval(address indexed _owner, address indexed _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 + * @notice MUST be emitted when multiple bond tokens are transferred, issued or redeemed, with the exception being during contract creation + * @param _from array of bondholders accounts + * @param _to array of accounts to transfer bonds to + * @param _amount array of amounts of bond tokens to be transferred + * + ** OPTIONAL - interfaces and other contracts MUST NOT expect this function to be present. MUST be emitted in `batchTransfer` and `batchTransferFrom` functions + */ + event TransferBatch(address[] _from, address[] _to, uint256[] _amount); + + /** + * @notice MUST be emitted when multiple accounts are approved or when the allowance is decreased from multiple accounts + * @param _owner bondholder account + * @param _spender array of accounts to be allowed to spend bonds, or to decrase the allowance from + * @param _amount array of amounts of bond tokens allowed by `_owner` to be spent by multiple accounts in `_spender`. + * + ** OPTIONAL - interfaces and other contracts MUST NOT expect this function to be present. MUST be emitted in `batchApprove` and `batchDecreaseAllowance` functions */ - event AllowanceDecreased(address _owner, address _spender, uint256 _amount); + event ApprovalBatch(address indexed _owner, address[] _spender, uint256[] _amount); } diff --git a/assets/erc-7092/IERC7092CrossChain.sol b/assets/erc-7092/IERC7092CrossChain.sol new file mode 100644 index 0000000000..aec0f6d5e5 --- /dev/null +++ b/assets/erc-7092/IERC7092CrossChain.sol @@ -0,0 +1,139 @@ +// SPDX-License-Identifier: CC0-1.0 +pragma solidity ^0.8.0; + +interface IERC7092CrossChain { + /** + * @notice Authorizes the `_spender` account to manage a specified `_amount`of the bondholder bond tokens on the destination Chain + * @param _spender account to be authorized by the bondholder + * @param _amount amount of bond tokens to approve + * @param _destinationChainID The unique ID that identifies the destination Chain. + * @param _destinationContract The smart contract to interact with in the destination Chain + * + * OPTIONAL - interfaces and other contracts MUST NOT expect this function to be present. The method is used to approve tokens in a different chain than the current chain + */ + function crossChainApprove(address _spender, uint256 _amount, bytes32 _destinationChainID, address _destinationContract) external returns(bool); + + /** + * @notice Authorizes multiple spender accounts in `_spender` to manage specified amounts in `_amount` of the bondholder tokens on the destination chain + * @param _spender array of accounts to be authorized by the bondholder + * @param _amount array of amounts of bond tokens to approve + * @param _destinationChainID array of unique IDs that identifies the destination Chain. + * @param _destinationContract array of smart contracts to interact with in the destination Chain in order to Deposit or Mint tokens that are transferred. + * + * OPTIONAL - interfaces and other contracts MUST NOT expect this function to be present. + */ + function crossChainBatchApprove(address[] calldata _spender, uint256[] calldata _amount, bytes32[] calldata _destinationChainID, address[] calldata _destinationContract) external returns(bool); + + /** + * @notice Decreases the allowance of `_spender` by a specified `_amount` on the destination Chain + * @param _spender the address to be authorized by the bondholder + * @param _amount amount of bond tokens to remove from allowance + * @param _destinationChainID The unique ID that identifies the destination Chain. + * @param _destinationContract The smart contract to interact with in the destination Chain in order to Deposit or Mint tokens that are transferred. + * + * OPTIONAL - interfaces and other contracts MUST NOT expect this function to be present. + */ + function crossChainDecreaseAllowance(address _spender, uint256 _amount, bytes32 _destinationChainID, address _destinationContract) external; + + /** + * @notice Decreases the allowance of multiple spenders in `_spender` by corresponding amounts specified in the array `_amount` on the destination chain + * @param _spender array of accounts to be authorized by the bondholder + * @param _amount array of amounts of bond tokens to decrease the allowance from + * @param _destinationChainID array of unique IDs that identifies the destination Chain. + * @param _destinationContract array of smart contracts to interact with in the destination Chain in order to Deposit or Mint tokens that are transferred. + * + * OPTIONAL - interfaces and other contracts MUST NOT expect this function to be present. + */ + function crossChainBatchDecreaseAllowance(address[] calldata _spender, uint256[] calldata _amount, bytes32[] calldata _destinationChainID, address[] calldata _destinationContract) external; + + /** + * @notice Moves `_amount` bond tokens to the address `_to` from the current chain to another chain (e.g., moving tokens from Ethereum to Polygon). + * This methods also allows to attach data to the token that is being transferred + * @param _to account to send bond tokens to + * @param _amount amount of bond tokens to transfer + * @param _data additional information provided by the bondholder + * @param _destinationChainID The unique ID that identifies the destination Chain. + * @param _destinationContract The smart contract to interact with in the destination Chain in order to Deposit or Mint bond tokens that are transferred. + * + * OPTIONAL - interfaces and other contracts MUST NOT expect this function to be present. + */ + function crossChainTransfer(address _to, uint256 _amount, bytes calldata _data, bytes32 _destinationChainID, address _destinationContract) external returns(bool); + + /** + * @notice Transfers multiple bond tokens with amounts specified in the array `_amount` to the corresponding accounts in the array `_to` from the current chain to another chain (e.g., moving tokens from Ethereum to Polygon). + * This methods also allows to attach data to the token that is being transferred + * @param _to array of accounts to send the bonds to + * @param _amount array of amounts of bond tokens to transfer + * @param _data array of additional information provided by the bondholder + * @param _destinationChainID array of unique IDs that identify the destination Chains. + * @param _destinationContract array of smart contracts to interact with in the destination Chains in order to Deposit or Mint bond tokens that are transferred. + * + * OPTIONAL - interfaces and other contracts MUST NOT expect this function to be present. + */ + function crossChainBatchTransfer(address[] calldata _to, uint256[] calldata _amount, bytes[] calldata _data, bytes32[] calldata _destinationChainID, address[] calldata _destinationContract) external returns(bool); + + /** + * @notice Transfers `_amount` bond tokens from the `_from`account to the `_to` account from the current chain to another chain. The caller must be approved by the `_from` address. + * This methods also allows to attach data to the token that is being transferred + * @param _from the bondholder address + * @param _to the account to transfer bonds to + * @param _amount amount of bond tokens to transfer + * @param _data additional information provided by the token holder + * @param _destinationChainID The unique ID that identifies the destination Chain. + * @param _destinationContract The smart contract to interact with in the destination Chain in order to Deposit or Mint tokens that are transferred. + * + ** OPTIONAL - interfaces and other contracts MUST NOT expect this function to be present + */ + function crossChainTransferFrom(address _from, address _to, uint256 _amount, bytes calldata _data, bytes32 _destinationChainID, address _destinationContract) external returns(bool); + + /** + * @notice Transfers several bond tokens with amounts specified in the array `_amount` from accounts in the array `_from` to accounts in the array `_to` from the current chain to another chain. + * The caller must be approved by the `_from` accounts to spend the corresponding amounts specified in the array `_amount` + * This methods also allows to attach data to the token that is being transferred + * @param _from array of bondholder addresses + * @param _to array of accounts to transfer bonds to + * @param _amount array of amounts of bond tokens to transfer + * @param _data array of additional information provided by the token holder + * @param _destinationChainID array of unique IDs that identifies the destination Chain. + * @param _destinationContract array of smart contracts to interact with in the destination Chain in order to Deposit or Mint tokens that are transferred. + * + ** OPTIONAL - interfaces and other contracts MUST NOT expect this function to be present + */ + function crossChainBatchTransferFrom(address[] calldata _from, address[] calldata _to, uint256[] calldata _amount, bytes[] calldata _data, bytes32[] calldata _destinationChainID, address[] calldata _destinationContract) external returns(bool); + + /** + * @notice MUST be emitted when bond tokens are transferred or redeemed in a cross-chain transaction + * @param _from bondholder account + * @param _to account the transfer bond tokens to + * @param _amount amount of bond tokens to be transferred + * @param _destinationChainID The unique ID that identifies the destination Chain + */ + event CrossChainTransfer(address indexed _from, address indexed _to, uint256 _amount, bytes32 _destinationChainID); + + /** + * @notice MUST be emitted when several bond tokens are transferred or redeemed in a cross-chain transaction + * @param _from array of bondholders accounts + * @param _to array of accounts that receive the bond + * @param _amount array of amount of bond tokens to be transferred + * @param _destinationChainID array of unique IDs that identify the destination Chain + */ + event CrossChainTransferBatch(address[] _from, address[] _to, uint256[] _amount, bytes32[] _destinationChainID); + + /** + * @notice MUST be emitted when an account is approved to spend the bondholder's tokens in a different chain than the current chain + * @param _owner the bondholder account + * @param _spender the account to be allowed to spend bonds + * @param _amount amount of bond tokens allowed by `_owner` to be spent by `_spender` + * @param _destinationChainID The unique ID that identifies the destination Chain + */ + event CrossChainApproval(address indexed _owner, address indexed _spender, uint256 _amount, bytes32 _destinationChainID); + + /** + * @notice MUST be emitted when multiple accounts in the array `_spender` are approved or when the allowances of multiple accounts in the array `_spender` are reduced on the destination chain which MUST be different than the current chain + * @param _owner bond token's owner + * @param _spender array of accounts to be allowed to spend bonds + * @param _amount array of amount of bond tokens allowed by _owner to be spent by _spender + * @param _destinationChainID array of unique IDs that identify the destination Chain + */ + event CrossChainApprovalBatch(address indexed _owner, address[] _spender, uint256[] _amount, bytes32[] _destinationChainID); +} diff --git a/assets/erc-7092/IERC7092ESG.sol b/assets/erc-7092/IERC7092ESG.sol new file mode 100644 index 0000000000..74e8bd19f5 --- /dev/null +++ b/assets/erc-7092/IERC7092ESG.sol @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: CC0-1.0 +pragma solidity ^0.8.0; + +interface IERC7092ESG { + /** + * @notice Returns the number of decimals used by the bond. For example, if it returns `10`, it means that the token amount MUST be multiplied by 10000000000 to get the standard representation. + * + * OPTIONAL - interfaces and other contracts MUST NOT expect these values to be present. The method is used to improve usability. + */ + function decimals() external view returns(uint8); + + /** + * @notice Rreturns the coupon currency, which is represented by the contract address of the token used to pay coupons. It can be the same as 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. + */ + function currencyOfCoupon() external view returns(address); + + /** + * @notice Returns the coupon type + * For example, 0 can denote Zero coupon, 1 can denote Fixed Rate, 2 can denote Floating Rate, and so on + * + * OPTIONAL - interfaces and other contracts MUST NOT expect these values to be present. The method is used to improve usability. + */ + function couponType() external view returns(uint8); + + /** + * @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. + */ + function couponFrequency() external view returns(uint256); + + /** + * @notice Returns the day count basis + * For example, 0 can denote actual/actual, 1 can denote actual/360, and so on + * + * OPTIONAL - interfaces and other contracts MUST NOT expect these values to be present. The method is used to improve usability. + */ + function dayCountBasis() external view returns(uint8); +} diff --git a/assets/erc-7092/coupons/CouponMath.sol b/assets/erc-7092/coupons/CouponMath.sol index 458b42d5d2..10b312ad3a 100644 --- a/assets/erc-7092/coupons/CouponMath.sol +++ b/assets/erc-7092/coupons/CouponMath.sol @@ -14,10 +14,9 @@ library CouponMath { address _bondContract ) external view returns(uint256) { uint256 couponRate = IERC7092(_bondContract).couponRate(); - uint256 denomination = IERC7092(_bondContract).denomination(); uint256 principal = IERC7092(_bondContract).principalOf(_investor); - return principal * denomination * couponRate; + return principal * couponRate; } /** @@ -32,12 +31,11 @@ library CouponMath { address _bondContract ) external view returns(uint256) { uint256 couponRate = IERC7092(_bondContract).couponRate(); - uint256 denomination = IERC7092(_bondContract).denomination(); uint256 principal = IERC7092(_bondContract).principalOf(_investor); uint256 frequency = IERC7092(_bondContract).couponFrequency(); uint256 numberOfDays = _numberOfDays(_bondContract); - return principal * denomination * couponRate * _duration / (frequency * numberOfDays); + return principal * couponRate * _duration / (frequency * numberOfDays); } /**