-
Notifications
You must be signed in to change notification settings - Fork 479
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add ERC: Interest Rate Swaps #178
Changes from all commits
1892ea5
65dc440
ecfa5dd
ff13f59
1257279
45cfe5b
029cbc0
a123099
1036a36
4ff4f8d
c869b40
ef580da
ec831ac
1349022
fbf4307
104ce6a
4836806
4ae6ef8
754fc79
4db8f41
b49c691
dca3618
4dc7fa9
6a1ed07
58bd40d
5dd4c78
ef1524f
60734bc
fac7e39
5ea17b7
ebbc886
7ec0811
65e511c
76187bb
6fc35b6
104de9a
6161a1c
d0b6952
9d436c8
67a3af8
bcd54b1
9273382
c943fc7
a94aaea
f351354
1465458
586568b
33cf391
7776598
4191bc8
22d45cb
c9d8a53
3f65f10
5300d57
5f87cbf
66fd6c5
f547b01
20b30e2
4e299a2
8fb3cf8
291d88c
8604643
ad89478
0fe73b4
6d258c3
d1573cf
1a65f02
ab64251
957923e
940015b
067c06e
cee3c58
f27fb49
5356afd
98fb795
7dba98b
123641a
a13fe80
1b1ac56
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,183 @@ | ||
--- | ||
eip: 7586 | ||
title: Interest Rate Swaps | ||
description: Interest rate swaps derivative contracts | ||
author: Samuel Gwlanold Edoumou (@Edoumou) | ||
discussions-to: https://ethereum-magicians.org/t/interest-rate-swaps/17777 | ||
status: Draft | ||
type: Standards Track | ||
category: ERC | ||
created: 2023-12-31 | ||
requires: 20, 165 | ||
--- | ||
|
||
## Abstract | ||
|
||
This proposal introduces a standardized framework for on-chain interest rate swaps. The proposed standard aims to facilitate the seamless exchange of fixed and floating interest rate cash flows between parties, providing a foundation for decentralized finance (DeFi) applications. | ||
Edoumou marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
## Motivation | ||
|
||
Interest Rate Swapping (IRS) denotes a derivative contract wherein two parties mutually consent to exchange a series of forthcoming interest payments based on a specified notional amount. This financial instrument serves as a strategic tool for hedging against interest rate fluctuations. The mechanism entails the utilization of a benchmark index to facilitate the exchange between a variable interest rate and a fixed rate. Despite its widespread use, there is currently an absence of a standardized framework that enables the representation of IRS contracts on blockchain platforms. | ||
|
||
This proposal addresses this gap by establishing a consistent and transparent methodology for representing IRS contracts within the blockchain environment. By doing so, it would enhance the interoperability, security, and efficiency of interest rate swap transactions on distributed ledger technology. | ||
|
||
## 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. | ||
|
||
### Example Flow | ||
|
||
![alt text](../assets/eip-7586/irs.jpeg "IRS diagram") | ||
|
||
Every contract compliant with this ERC MUST implement the following interface. The contract MUST inherit from [`ERC20`](./eip-20) to tokenize the swap cash flows. | ||
|
||
```solidity | ||
pragma solidity ^0.8.0; | ||
|
||
/** | ||
* @title ERC-7586 Interest Rate Swaps | ||
*/ | ||
interface IERC7586 /** is ERC20, ERC165 */ { | ||
// events | ||
/** | ||
* @notice MUST be emitted when interest rates are swapped | ||
* @param _amount the interest difference to be transferred | ||
* @param _account the recipient account to send the interest difference to. MUST be either the `payer` or the `receiver` | ||
*/ | ||
event Swap(uint256 _amount, address _account); | ||
|
||
/** | ||
* @notice MUST be emitted when the swap contract is terminated | ||
* @param _payer the swap payer | ||
* @param _receiver the swap receiver | ||
*/ | ||
event TerminateSwap(address indexed _payer, address indexed _receiver); | ||
|
||
// functions | ||
/** | ||
* @notice Returns the IRS `payer` account address. The party who agreed to pay fixed interest | ||
*/ | ||
function fixedInterestPayer() external view returns(address); | ||
|
||
/** | ||
* @notice Returns the IRS `receiver` account address. The party who agreed to pay floating interest | ||
*/ | ||
function floatingInterestPayer() external view returns(address); | ||
|
||
/** | ||
* @notice Returns the number of decimals the swap rate and spread use - e.g. `4` means to divide the rates by `10000` | ||
* To express the interest rates in basis points unit, the decimal MUST be equal to `2`. This means rates MUST be divided by `100` | ||
* 1 basis point = 0.01% = 0.0001 | ||
* ex: if interest rate = 2.5%, then swapRate() => 250 `basis points` | ||
*/ | ||
function ratesDecimals() external view returns(uint8); | ||
|
||
/** | ||
* @notice Returns the fixed interest rate | ||
*/ | ||
function swapRate() external view returns(uint256); | ||
|
||
/** | ||
* @notice Returns the floating rate spread, i.e. the fixed part of the floating interest rate | ||
* | ||
* floatingRate = benchmark + spread | ||
*/ | ||
function spread() external view returns(uint256); | ||
|
||
/** | ||
* @notice Returns the contract address of the asset to be transferred when swapping IRS. Depending on what the two parties agreed upon, this could be a currency, etc. | ||
* Example: If the two parties agreed to swap interest rates in USDC, then this function should return the USDC contract address. | ||
* This address SHOULD be used in the `swap` function to transfer the interest difference to either the `payer` or the `receiver`. Example: IERC(assetContract).transfer | ||
*/ | ||
function assetContract() external view returns(address); | ||
|
||
/** | ||
* @notice Returns the notional amount in unit of asset to be transferred when swapping IRS. This amount serves as the basis for calculating the interest payments, and may not be exchanged | ||
* Example: If the two parties aggreed to swap interest rates in USDC, then the notional amount may be equal to 1,000,000 USDC | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think removing "USDC" here would make it more clear. |
||
*/ | ||
function notionalAmount() external view returns(uint256); | ||
|
||
/** | ||
* @notice Returns the interest payment frequency | ||
*/ | ||
function paymentFrequency() external view returns(uint256); | ||
Comment on lines
+100
to
+103
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is in seconds? Might be good to note that in the comment. |
||
|
||
/** | ||
* @notice Returns an array of specific dates on which the interest payments are exchanged. Each date MUST be a Unix timestamp like the one returned by block.timestamp | ||
* The length of the array returned by this function MUST equal the total number of swaps that should be realized | ||
* | ||
* OPTIONAL | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What does "OPTIONAL" mean here: that the contract doesn't need to implement this function, or that this function may revert? Should make that clear in the comment. |
||
*/ | ||
function paymentDates() external view returns(uint256[] memory); | ||
|
||
/** | ||
* @notice Returns the starting date of the swap contract. This is a Unix Timestamp like the one returned by block.timestamp | ||
*/ | ||
function startingDate() external view returns(uint256); | ||
|
||
/** | ||
* @notice Returns the maturity date of the swap contract. This is a Unix Timestamp like the one returned by block.timestamp | ||
*/ | ||
function maturityDate() external view returns(uint256); | ||
|
||
/** | ||
* @notice Returns the benchmark in basis point unit | ||
* Example: value of one the following rates: CF BIRC, EURIBOR, HIBOR, SHIBOR, SOFR, SONIA, TONAR, etc. | ||
*/ | ||
function benchmark() external view returns(uint256); | ||
|
||
/** | ||
* @notice Returns the oracle contract address for the benchmark rate, or the zero address when the two parties agreed to set the benchmark manually. | ||
* This contract SHOULD be used to fetch real time benchmark rate | ||
* Example: Contract address for `CF BIRC` | ||
* | ||
* OPTIONAL. The two parties MAY agree to set the benchmark manually | ||
Edoumou marked this conversation as resolved.
Show resolved
Hide resolved
|
||
*/ | ||
function oracleContractForBenchmark() external view returns(address); | ||
|
||
/** | ||
* @notice Makes swap calculation and transfers the interest difference to either the `payer` or the `receiver` | ||
*/ | ||
function swap() external returns(bool); | ||
|
||
/** | ||
* @notice Terminates the swap contract before its maturity date. MUST be called by either the `payer`or the `receiver`. | ||
*/ | ||
function terminateSwap() external; | ||
} | ||
``` | ||
### Tokenization of Swap Cash Flows | ||
|
||
The interest payments associated with the IRS MUST be tokenized by issuing digital [ERC-20](./eip-20) tokens to the respective parties according to the terms of the swap. Each token SHOULD represent a specific interest payment. Every time a swap happens (the `swap` function is called), one token MUST be burned from each party. | ||
|
||
## Rationale | ||
Edoumou marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
This standard allows parties involved in the IRS contract to define essential parameters such as notional amount, interest rates, payment frequency, and payment dates. This flexibility accommodates a diverse range of financial agreements, catering to the unique needs of different participants. | ||
|
||
To accommodate a wide array of use cases, the standard introduces optional features such as payment dates and manual benchmark setting. This allows parties to tailor the contract to specific requirements, while maintaining a core set of functions for essential functionality. | ||
|
||
To ensure real-time and accurate benchmark rates, the standard integrates with oracles. Parties have the option to use oracles for fetching benchmark rates, enhancing the reliability and accuracy of interest rate calculations. | ||
|
||
## Backwards Compatibility | ||
|
||
This standard is backward compatible with ERC-20. | ||
|
||
## Reference Implementation | ||
|
||
The complete reference implementation can be found [here](../assets/eip-7586/ERC7586.sol). | ||
|
||
This reference implementation serves as a foundation for the implementation of more advanced types of swaps. | ||
|
||
## Security Considerations | ||
|
||
Security considerations of various types must be thoroughly evaluated | ||
|
||
* Interest Rate Risk: This pertains to the potential impact of fluctuations in interest rates. | ||
* Credit Risk: There exists the possibility that one or both parties may default on their respective responsibilities. | ||
* ERC-20 Risks: All security aspects outlined in the ERC-20 standard must be taken into account. | ||
|
||
Both parties must acknowledge their awareness of these security risks before proceeding with the implementation of the standard. | ||
|
||
## Copyright | ||
|
||
Copyright and related rights waived via [CC0](../LICENSE.md). |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
// SPDX-License-Identifier: CC0-1.0 | ||
pragma solidity ^0.8.0; | ||
|
||
import "./IERC7586.sol"; | ||
import "./Tokens/ERC20.sol"; | ||
|
||
contract ERC7586 is IERC7586, ERC20 { | ||
constructor(string memory _name, string memory _symbol, IRS memory _irs) ERC20(_name, _symbol) { | ||
irsSymbol = _symbol; | ||
irs = _irs; | ||
|
||
_isActive = true; | ||
|
||
_balanceOf[_irs.payer] = _irs.paymentDates.length * 1 ether; | ||
_balanceOf[_irs.receiver] = _irs.paymentDates.length * 1 ether; | ||
_totalSupply = _irs.paymentDates.length * 2 * 1 ether; | ||
} | ||
|
||
function fixedInterestPayer() external view returns(address) { | ||
return irs.payer; | ||
} | ||
|
||
function floatingInterestPayer() external view returns(address) { | ||
return irs.receiver; | ||
} | ||
|
||
function ratesDecimals() external view returns(uint8) { | ||
return irs.ratesDecimals; | ||
} | ||
|
||
function swapRate() external view returns(uint256) { | ||
return irs.swapRate; | ||
} | ||
|
||
function spread() external view returns(uint256) { | ||
return irs.spread; | ||
} | ||
|
||
function assetContract() external view returns(address) { | ||
return irs.assetContract; | ||
} | ||
|
||
function notionalAmount() external view returns(uint256) { | ||
return irs.notionalAmount; | ||
} | ||
|
||
function paymentFrequency() external view returns(uint256) { | ||
return irs.paymentFrequency; | ||
} | ||
|
||
function paymentDates() external view returns(uint256[] memory) { | ||
return irs.paymentDates; | ||
} | ||
|
||
function startingDate() external view returns(uint256) { | ||
return irs.startingDate; | ||
} | ||
|
||
function maturityDate() external view returns(uint256) { | ||
return irs.maturityDate; | ||
} | ||
|
||
function benchmark() external view returns(uint256) { | ||
// This should be fetched from an oracle contract | ||
return irs.benchmark; | ||
} | ||
|
||
function oracleContractForBenchmark() external view returns(address) { | ||
return irs.oracleContractForBenchmark; | ||
} | ||
|
||
function isActive() public view returns(bool) { | ||
return _isActive; | ||
} | ||
|
||
function swap() external returns(bool) { | ||
require(_isActive, "Contract not Active"); | ||
require(_hasAgreed[irs.payer], "Missing Agreement"); | ||
require(_hasAgreed[irs.receiver], "Missing Agreement"); | ||
|
||
uint256 fixedRate = irs.swapRate; | ||
uint256 floatingRate = irs.benchmark + irs.spread; | ||
uint256 notional = irs.notionalAmount; | ||
|
||
uint256 fixedInterest = notional * fixedRate; | ||
uint256 floatingInterest = notional * floatingRate; | ||
|
||
uint256 interestToTransfer; | ||
address _recipient; | ||
address _payer; | ||
|
||
if(fixedInterest == floatingInterest) { | ||
revert("Nothing to swap"); | ||
} else if(fixedInterest > floatingInterest) { | ||
interestToTransfer = fixedInterest - floatingInterest; | ||
_recipient = irs.receiver; | ||
_payer = irs.payer; | ||
} else { | ||
interestToTransfer = floatingInterest - fixedInterest; | ||
_recipient = irs.payer; | ||
_payer = irs.receiver; | ||
} | ||
|
||
burn(irs.payer, 1 ether); | ||
burn(irs.receiver, 1 ether); | ||
|
||
uint256 _paymentCount = paymentCount; | ||
paymentCount = _paymentCount + 1; | ||
|
||
IERC20(irs.assetContract).transferFrom(_payer, _recipient, interestToTransfer * 1 ether / 10_000); | ||
|
||
if(paymentCount == irs.paymentDates.length) { | ||
_isActive = false; | ||
} | ||
|
||
return true; | ||
} | ||
|
||
function terminateSwap() external { | ||
require(_isActive, "Contract not Active"); | ||
|
||
_isActive = false; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Feel free to continue improving this, but your description needs to be more descriptive than just your title.