ERC-7586 온체인 금리 스와프 표준 (Interest Rate Swaps)
Review · 금고·DeFi · 2023-12-31 작성
고정금리와 변동금리의 이자 지급을 맞바꾸는 파생상품 계약인 금리 스와프(Interest Rate Swap)를 블록체인 위에서 표준화된 방식으로 표현하고 실행할 수 있게 합니다.
왜 필요한가
금리 스와프는 두 당사자가 정해진 기준 금액을 바탕으로 앞으로 발생할 이자 지급을 서로 교환하기로 약속하는 계약입니다. 한쪽은 고정된 이자를 내고 다른 쪽은 시장 상황에 따라 바뀌는 변동 이자를 내는 식으로, 금리가 오르내릴 때 생기는 위험을 줄이는 수단으로 전통 금융에서 널리 쓰입니다. 그런데 이렇게 널리 쓰이는 계약인데도 이를 블록체인 위에 표현하는 표준화된 틀이 지금까지 없었습니다. 표준이 없으면 프로젝트마다 계약 조건을 제각각의 방식으로 담게 되어 서로 다른 앱과 컨트랙트가 같은 스와프 계약을 읽고 처리하기 어렵습니다. 이 제안은 금리 스와프 계약을 블록체인 환경에서 일관되고 투명하게 표현하는 방법을 정해 분산원장 위 금리 스와프 거래의 상호운용성과 보안, 효율을 높이는 것을 목표로 합니다.
구현해야 하는 것
event Swap(uint256 _amount, address _account);: 금리가 교환될 때 반드시 발생하는 이벤트입니다. 이체되는 이자 차액과 그 차액을 받는 계정(payer 또는 receiver 중 하나)을 기록합니다.event TerminateSwap(address indexed _payer, address indexed _receiver);: 스와프 계약이 종료될 때 반드시 발생하는 이벤트입니다. 지급자와 수취자 주소를 기록합니다.function fixedRatePayer() external view returns(address);: 고정 이자를 내기로 한 당사자(payer)의 계정 주소를 돌려줍니다.function floatingRatePayer() external view returns(address);: 변동 이자를 내기로 한 당사자(receiver)의 계정 주소를 돌려줍니다.function ratesDecimals() external view returns(uint8);: 스와프 금리와 스프레드에 쓰는 소수 자릿수를 돌려줍니다. 예를 들어 4면 금리를 10000으로 나눠 읽습니다. 베이시스 포인트 단위로 표현하려면 2여야 합니다.function swapRate() external view returns(uint256);: 고정 금리를 돌려줍니다. 모든 금리는 10^(ratesDecimals)를 곱한 값이어야 합니다.function spread() external view returns(uint256);: 변동 금리의 고정 부분인 스프레드를 돌려줍니다. 변동 금리는 벤치마크에 스프레드를 더한 값입니다.function dayCountBasis() external view returns(uint8);: 이자 일수 계산 기준을 돌려줍니다. 예를 들어 0은 actual/actual, 1은 actual/360을 뜻할 수 있습니다.function notionalCurrency() external view returns(address);: 명목금액이 표시되는 통화의 컨트랙트 주소를 돌려줍니다. USD 같은 법정통화로 표시되면 0 주소를 돌려줍니다.function paymentAssets() external view returns(address[] memory);: 스와프 실행 시 실제로 이체될 자산들의 컨트랙트 주소 배열을 돌려줍니다. 두 당사자가 서로 다른 통화로 받기를 원할 수 있습니다.function notionalAmount() external view returns(uint256);: 이자 계산의 기준이 되는 명목금액을 돌려줍니다. 이 금액 자체는 교환되지 않을 수 있습니다.function paymentFrequency() external view returns(uint256);: 1년 동안 지급이 이뤄져야 하는 횟수를 돌려줍니다.function fixPaymentDates() external view returns(uint256[] memory);: 고정 이자가 교환되는 날짜들의 배열을 돌려줍니다. 각 날짜는 유닉스 타임스탬프여야 하고 배열 길이는 전체 스와프 횟수와 같아야 합니다. 선택 사항입니다.function floatingPaymentDates() external view returns(uint256[] memory);: 변동 이자가 교환되는 날짜들의 배열을 돌려줍니다. 각 날짜는 유닉스 타임스탬프여야 하고 배열 길이는 전체 스와프 횟수와 같아야 합니다. 선택 사항입니다.function startingDate() external view returns(uint256);: 스와프 계약의 시작일을 유닉스 타임스탬프로 돌려줍니다.function maturityDate() external view returns(uint256);: 스와프 계약의 만기일을 유닉스 타임스탬프로 돌려줍니다.function benchmark() external view returns(uint256);: 기준이 되는 참조 금리(벤치마크)를 돌려줍니다. SOFR, EURIBOR 같은 지표 값이거나 수동으로 정한 값일 수 있습니다.function oracleContractsForBenchmark() external view returns(address);: 벤치마크 금리를 가져올 오라클 컨트랙트 주소를 돌려줍니다. 두 당사자가 벤치마크를 수동으로 정하기로 했다면 0 주소를 돌려줍니다. 선택 사항입니다.function swap() external returns(bool);: 스와프 계산을 수행하고 당사자들에게 지급금을 이체합니다.function terminateSwap() external;: 만기 전에 스와프 계약을 종료합니다. payer 또는 receiver가 호출해야 합니다.
심층 설명
당사자와 금리 구조
계약에는 두 당사자가 있습니다. fixedRatePayer는 고정 이자를 내기로 한 payer이고 floatingRatePayer는 변동 이자를 내기로 한 receiver입니다. 고정 금리는 swapRate로 조회하고, 변동 금리는 benchmark + spread로 계산합니다. benchmark는 CF BIRC, EURIBOR, HIBOR, SHIBOR, SOFR, SONIA, TONAR 같은 참조 금리 값이거나 수동으로 정한 값입니다. 모든 금리는 10^(ratesDecimals)를 곱해 정수로 표현해야 하며 베이시스 포인트 단위로 쓰려면 ratesDecimals가 2여야 합니다. 예를 들어 금리가 2.5%면 swapRate()는 250 베이시스 포인트를 돌려줍니다.
명목금액과 결제 자산
notionalAmount는 이자 계산의 기준이 되는 명목금액으로, 이 금액 자체는 교환되지 않을 수 있습니다. notionalCurrency는 명목금액이 표시되는 통화의 컨트랙트 주소(예: USDC 컨트랙트 주소)를 돌려주며 USD 같은 법정통화로 표시되면 0 주소를 돌려줍니다. 실제 지급에 쓰일 자산은 paymentAssets가 배열로 돌려주는데, 두 당사자가 서로 다른 통화로 받기를 원할 수 있기 때문입니다. 예를 들어 payer는 USDC로, receiver는 DAI로 받고 싶다면 [USDC, DAI] 또는 [DAI, USDC]를 돌려줍니다.
지급 일정과 일수 계산
paymentFrequency는 1년에 지급이 이뤄져야 하는 횟수를 돌려줍니다. startingDate와 maturityDate는 계약의 시작일과 만기일을 block.timestamp와 같은 유닉스 타임스탬프로 돌려줍니다. 선택 함수인 fixPaymentDates와 floatingPaymentDates는 고정 이자와 변동 이자가 교환되는 구체적 날짜들의 배열을 돌려주며 배열 길이는 실행되어야 할 전체 스와프 횟수와 같아야 합니다. dayCountBasis는 이자 일수 계산 기준을 숫자로 나타내는데, 예를 들어 0은 actual/actual, 1은 actual/360을 뜻할 수 있습니다.
오라클 연동
정확한 실시간 벤치마크 금리를 쓰기 위해 표준은 오라클과의 연동을 포함합니다. oracleContractsForBenchmark는 벤치마크로 쓸 참조 금리를 제공하는 오라클 컨트랙트 주소를 돌려주며 실시간 벤치마크 금리를 가져오는 데 이 컨트랙트를 쓰는 것이 권장됩니다. 다만 이 기능은 선택 사항이어서 두 당사자가 벤치마크를 수동으로 정하기로 합의할 수도 있고 그 경우 0 주소를 돌려줍니다. Rationale에 따르면 오라클 사용은 금리 계산의 신뢰성과 정확성을 높이기 위한 선택지입니다.
현금흐름의 ERC-20 토큰화
이 표준을 따르는 컨트랙트는 반드시 ERC-20을 상속해 스와프 현금흐름을 토큰화해야 합니다. 스와프에 따른 이자 지급은 계약 조건에 따라 각 당사자에게 ERC-20 토큰을 발행하는 방식으로 토큰화되어야 하며 각 토큰은 특정 이자 지급 한 건을 나타내는 것이 권장됩니다. swap 함수가 호출되어 교환이 일어날 때마다 각 당사자에게서 토큰 1개가 소각되어야 합니다. 이 표준은 ERC-20과 하위 호환됩니다.
실행과 종료
swap 함수는 스와프 계산을 수행하고 당사자들에게 지급금을 이체하며 bool 값을 돌려줍니다. 금리가 교환될 때는 반드시 Swap 이벤트가 발생해야 하고 이벤트에는 이체되는 이자 차액(_amount)과 그 차액을 받는 계정(_account)이 담깁니다. _account는 payer 또는 receiver 중 하나여야 합니다. terminateSwap은 만기 전에 계약을 종료하는 함수로, 반드시 payer 또는 receiver가 호출해야 하며 종료 시 TerminateSwap 이벤트가 발생해야 합니다.
제안 상태
이 제안은 현재 Review 상태입니다. 아직 확정되지 않아 내용이 바뀔 수 있는 단계이므로, 구현 시 최신 원문을 확인해야 합니다. 원문은 참조 구현이 더 발전된 형태의 스와프를 구현하기 위한 기초가 된다고 밝히고 있습니다.
쓸 때 주의할 점
- 금리 리스크: 금리 변동이 계약 당사자에게 미칠 수 있는 잠재적 영향을 철저히 평가해야 합니다.
- 신용 리스크: 한쪽 또는 양쪽 당사자가 각자의 의무를 이행하지 않을 가능성이 있습니다.
- ERC-20 리스크: ERC-20 표준에 명시된 모든 보안 사항을 함께 고려해야 합니다. 양 당사자는 표준 구현에 앞서 이런 보안 위험을 인지하고 있음을 확인해야 합니다.