ERC-7586 On-chain interest rate swap standard (Interest Rate Swaps)
Review · Vaults & DeFi · created 2023-12-31
Enables standardized representation and execution, on a blockchain, of an interest rate swap, a derivative contract that exchanges fixed-rate and floating-rate interest payments.
Why it is needed
An interest rate swap is a contract in which two parties agree to exchange future interest payments based on a predetermined notional amount. One party pays a fixed rate of interest while the other pays a floating rate that changes with market conditions, and it is widely used in traditional finance as a means of reducing the risk arising from interest rate fluctuations. However, despite being such a widely used contract, there has until now been no standardized framework for representing it on a blockchain. Without a standard, each project encodes contract terms in its own way, making it difficult for different apps and contracts to read and process the same swap contract. This proposal aims to define a consistent and transparent way to represent interest rate swap contracts in a blockchain environment, thereby improving the interoperability, security, and efficiency of interest rate swap transactions on distributed ledgers.
What to implement
event Swap(uint256 _amount, address _account);: An event that must be emitted whenever interest is exchanged. It records the interest differential transferred and the account that receives it (either the payer or the receiver).event TerminateSwap(address indexed _payer, address indexed _receiver);: An event that must be emitted when a swap contract is terminated. It records the payer and receiver addresses.function fixedRatePayer() external view returns(address);: Returns the account address of the party that agreed to pay fixed interest (payer).function floatingRatePayer() external view returns(address);: Returns the account address of the party that agreed to pay floating interest (receiver).function ratesDecimals() external view returns(uint8);: Returns the number of decimal places used for the swap rate and spread. For example, 4 means the rate is read by dividing by 10000. It should be 2 to express the rate in basis points.function swapRate() external view returns(uint256);: Returns the fixed interest rate. All rates must be expressed as values multiplied by 10^(ratesDecimals).function spread() external view returns(uint256);: Returns the spread, the fixed component of the floating rate. The floating rate is the benchmark plus the spread.function dayCountBasis() external view returns(uint8);: Returns the day count convention used for interest calculation. For example, 0 may mean actual/actual and 1 may mean actual/360.function notionalCurrency() external view returns(address);: Returns the contract address of the currency in which the notional amount is denominated. If denominated in a fiat currency such as USD, it returns the zero address.function paymentAssets() external view returns(address[] memory);: Returns an array of the contract addresses of the assets actually transferred when the swap is executed. The two parties may wish to receive different currencies.function notionalAmount() external view returns(uint256);: Returns the notional amount used as the basis for interest calculation. This amount itself may not be exchanged.function paymentFrequency() external view returns(uint256);: Returns the number of times payment must be made per year.function fixPaymentDates() external view returns(uint256[] memory);: Returns an array of the dates on which fixed interest is exchanged. Each date must be a Unix timestamp, and the array length must equal the total number of swaps. Optional.function floatingPaymentDates() external view returns(uint256[] memory);: Returns an array of the dates on which floating interest is exchanged. Each date must be a Unix timestamp, and the array length must equal the total number of swaps. Optional.function startingDate() external view returns(uint256);: Returns the start date of the swap contract as a Unix timestamp.function maturityDate() external view returns(uint256);: Returns the maturity date of the swap contract as a Unix timestamp.function benchmark() external view returns(uint256);: Returns the reference rate (benchmark) used as the basis. It may be an index value such as SOFR or EURIBOR, or a manually set value.function oracleContractsForBenchmark() external view returns(address);: Returns the address of the oracle contract from which the benchmark rate is fetched. If the two parties agreed to set the benchmark manually, it returns the zero address. Optional.function swap() external returns(bool);: Performs the swap calculation and transfers the payment to the parties.function terminateSwap() external;: Terminates the swap contract before maturity. Must be called by the payer or the receiver.
In depth
Parties and rate structure
The contract has two parties. fixedRatePayer is the payer who agreed to pay fixed interest, and floatingRatePayer is the receiver who agreed to pay floating interest. The fixed rate is looked up via swapRate, and the floating rate is calculated as benchmark + spread. benchmark is a reference rate value such as CF BIRC, EURIBOR, HIBOR, SHIBOR, SOFR, SONIA, or TONAR, or a manually set value. All rates must be expressed as integers multiplied by 10^(ratesDecimals), and ratesDecimals must be 2 to use basis points. For example, if the rate is 2.5%, swapRate() returns 250 basis points.
Notional amount and settlement assets
notionalAmount is the notional amount used as the basis for interest calculation, and this amount itself may not be exchanged. notionalCurrency returns the contract address of the currency in which the notional amount is denominated (for example, the USDC contract address), and returns the zero address if denominated in a fiat currency such as USD. The assets actually used for payment are returned as an array by paymentAssets, since the two parties may wish to receive different currencies. For example, if the payer wants to receive USDC and the receiver wants to receive DAI, it returns [USDC, DAI] or [DAI, USDC].
Payment schedule and day count convention
paymentFrequency returns the number of times payment must be made per year. startingDate and maturityDate return the contract's start and maturity dates as Unix timestamps such as block.timestamp. The optional functions fixPaymentDates and floatingPaymentDates return arrays of the specific dates on which fixed and floating interest are exchanged, and the array length must equal the total number of swaps to be executed. dayCountBasis is a numeric representation of the day count convention for interest calculation; for example, 0 may mean actual/actual and 1 may mean actual/360.
Oracle integration
To use accurate real-time benchmark rates, the standard includes oracle integration. oracleContractsForBenchmark returns the address of the oracle contract that supplies the reference rate used as the benchmark, and it is recommended to use this contract to fetch the real-time benchmark rate. However, this feature is optional, so the two parties may agree to set the benchmark manually, in which case it returns the zero address. According to the Rationale, oracle use is an option for improving the reliability and accuracy of interest rate calculation.
ERC-20 tokenization of cash flows
Contracts following this standard must inherit ERC-20 to tokenize swap cash flows. Interest payments under the swap should be tokenized by minting ERC-20 tokens to each party according to the contract terms, and it is recommended that each token represent one specific interest payment. Each time the swap function is called and an exchange occurs, one token must be burned from each party. This standard is backwards compatible with ERC-20.
Execution and termination
The swap function performs the swap calculation, transfers payments to the parties, and returns a bool value. When interest rates are exchanged, a Swap event must be emitted, containing the interest differential being transferred (_amount) and the account receiving that differential (_account). _account must be either the payer or the receiver. terminateSwap is a function that terminates the contract before maturity, must be called by either the payer or the receiver, and must emit a TerminateSwap event upon termination.
Proposal status
This proposal is currently in Review status. Since it has not yet been finalized and its content may change, implementers should check the latest version of the source text. The source text states that the reference implementation serves as a basis for implementing more advanced forms of swaps.
Things to watch out for
- Interest rate risk: The potential impact of interest rate fluctuations on the contracting parties must be thoroughly assessed.
- Credit risk: There is a possibility that one or both parties will fail to fulfill their respective obligations.
- ERC-20 risk: All security considerations specified in the ERC-20 standard must also be taken into account. Both parties must confirm that they are aware of these security risks before implementing the standard.