ERC-7092 금융 채권 토큰 표준 (Financial Bonds)
Final · 금고·DeFi · 2023-05-28 작성
기업이나 기관이 발행하는 채권(bond)을 토큰으로 만드는 표준입니다. 쿠폰 이자율, 만기일, 원금 같은 전통 채권 용어를 그대로 쓴 함수로 1차 발행과 2차 거래, 체인 간 이동까지 지원합니다.
왜 필요한가
기업이나 기관이 자금을 조달할 때 널리 쓰는 수단이 채권 같은 고정수익(fixed-income) 상품입니다. 그런데 이를 블록체인 토큰으로 옮기려면 기존 표준인 ERC-3475에 의존해야 했는데, 이 표준은 낯선 개념을 도입해 불필요한 가스 비용을 유발합니다. 또 쿠폰(이자), 만기일, 원금처럼 이름이 붙은 변수가 없어서 개발자가 어떤 메타데이터가 어떤 매개변수에 해당하는지 일일이 기억해야 구현할 수 있었습니다. 이 표준은 전통 금융에서 쓰는 용어를 함수 이름에 그대로 반영해 채권의 핵심 특성을 정의합니다. 그래서 전통 금융 기관이 토큰화 채권으로 넘어오기 쉽고, 채권을 지갑으로 관리하거나 탈중앙화 거래소 같은 앱에서 다룰 수 있게 됩니다.
구현해야 하는 것
event Transfer(address indexed _from, address indexed _to, uint256 _amount);: 채권 토큰이 전송, 발행, 상환될 때 발생하는 이벤트입니다. 컨트랙트 생성 시점은 예외입니다.event Approval(address indexed _owner, address indexed _spender, uint256 _amount);: 계정이 승인되거나 허용량이 줄어들 때 발생하는 이벤트입니다.event TransferBatch(address[] _from, address[] _to, uint256[] _amount);: 여러 건의 채권 토큰이 한꺼번에 전송, 발행, 상환될 때 발생하는 이벤트입니다. batchTransfer와 batchTransferFrom에서 반드시 발생시킵니다.event ApprovalBatch(address indexed _owner, address[] _spender, uint256[] _amount);: 여러 계정이 한꺼번에 승인되거나 허용량이 줄어들 때 발생하는 이벤트입니다. batchApprove와 batchDecreaseAllowance에서 반드시 발생시킵니다.function isin() external view returns(string memory);: 채권의 국제 증권 식별 번호(ISIN)를 돌려줍니다.function name() external view returns(string memory);: 채권 이름을 돌려줍니다.function symbol() external view returns(string memory);: 채권 심볼을 돌려줍니다. 발행자 약칭과 만기일을 조합한 형태가 권장됩니다.function currency() external view returns(address);: 채권 통화를 돌려줍니다. 원금을 내고 돌려받는 데 쓰는 토큰의 컨트랙트 주소입니다.function denomination() external view returns(uint256);: 채권의 액면 단위를 돌려줍니다. 채권을 발행할 수 있는 최소 금액으로, 원금 통화 단위로 표현합니다.function issueVolume() external view returns(uint256);: 발행 총량, 즉 총 부채 금액을 돌려줍니다. 액면 단위로 표현하는 것이 권장됩니다.function couponRate() external view returns(uint256);: 채권 이자율을 돌려줍니다. 베이시스 포인트 단위가 권장되며, 이자율이 5%면 500을 돌려줍니다.function issueDate() external view returns(uint256);: 채권이 투자자에게 발행된 날짜를 유닉스 타임스탬프로 돌려줍니다.function maturityDate() external view returns(uint256);: 원금이 상환되는 만기일을 유닉스 타임스탬프로 돌려줍니다. 만기일은 발행일보다 커야 합니다.function principalOf(address _account) external view returns(uint256);: 특정 계정이 보유한 원금을 돌려줍니다. 채권 통화 단위로 표현하는 것이 권장됩니다.function allowance(address _owner, address _spender) external view returns(uint256);: 소유자가 _spender 계정에 관리를 맡긴 채권 토큰 수량을 돌려줍니다.function approve(address _spender, uint256 _amount) external returns(bool);: _spender 계정이 자신의 채권 토큰을 _amount만큼 관리하도록 승인합니다.function decreaseAllowance(address _spender, uint256 _amount) external returns(bool);: _spender의 허용량을 _amount만큼 줄입니다.function transfer(address _to, uint256 _amount, bytes calldata _data) external returns(bool);: 채권을 _to 주소로 보냅니다. 보내는 토큰에 추가 데이터를 붙일 수 있습니다.function transferFrom(address _from, address _to, uint256 _amount, bytes calldata _data) external returns(bool);: approve로 권한을 받은 계정이 소유자의 채권을 대신 전송합니다. 추가 데이터를 붙일 수 있습니다.function batchApprove(address[] calldata _spender, uint256[] calldata _amount) external returns(bool);: 여러 계정을 한 번에 승인합니다. 선택 기능이라 다른 컨트랙트가 존재를 기대해서는 안 됩니다.function batchDecreaseAllowance(address[] calldata _spender, uint256[] calldata _amount) external returns(bool);: 여러 계정의 허용량을 한 번에 줄입니다. 선택 기능입니다.function batchTransfer(address[] calldata _to, uint256[] calldata _amount, bytes[] calldata _data) external returns(bool);: 여러 계정에 각각 지정한 수량의 채권을 한 번에 보냅니다. 선택 기능입니다.function batchTransferFrom(address[] calldata _from, address[] calldata _to, uint256[] calldata _amount, bytes[] calldata _data) external returns(bool);: 승인받은 계정이 여러 소유자의 채권을 여러 수신자에게 한 번에 전송합니다. 선택 기능입니다.
심층 설명
필수 토큰 인터페이스와 채권 속성
이 표준을 따르는 모든 컨트랙트는 IERC7092 토큰 인터페이스와 ERC-165 인터페이스를 반드시 구현해야 합니다. 조회 함수는 전통 채권의 핵심 특성을 그대로 이름으로 씁니다. isin은 채권 식별 번호, currency는 원금을 지급하고 돌려받는 토큰의 컨트랙트 주소, denomination은 발행 가능한 최소 금액(예: 통화가 USDC이고 denomination이 1,000이면 액면 단위는 1,000 USDC), issueVolume은 총 부채 금액, couponRate는 이자율(베이시스 포인트 권장, 5%면 500), issueDate와 maturityDate는 유닉스 타임스탬프이며 만기일은 발행일보다 커야 합니다. principalOf는 계정별 원금을 돌려줍니다. symbol은 발행자 약칭과 만기일을 조합하는 방식이 권장됩니다. 예를 들어 Green Energy가 2030년 10월 25일 만기 채권을 발행하면 GE30, GE2030, GE102530 같은 형태입니다.
전송·승인 함수와 배치 처리
approve는 _spender에게 _amount만큼의 채권 관리 권한을 주고, decreaseAllowance는 허용량을 줄입니다. transfer와 transferFrom은 _data 매개변수로 전송되는 토큰에 추가 정보를 붙일 수 있습니다. Transfer 이벤트는 전송, 발행, 상환 시 반드시 발생시키되 컨트랙트 생성 시점은 예외입니다. batchApprove, batchDecreaseAllowance, batchTransfer, batchTransferFrom은 사용성을 높이기 위한 선택 함수라 다른 인터페이스나 컨트랙트가 존재를 기대해서는 안 됩니다. 다만 batchTransfer와 batchTransferFrom에서는 TransferBatch 이벤트를, batchApprove와 batchDecreaseAllowance에서는 ApprovalBatch 이벤트를 반드시 발생시켜야 합니다.
선택 인터페이스 IERC7092ESG
채권 조건을 더 자세히 표현하는 선택 인터페이스입니다. decimals는 토큰의 소수 자릿수를 돌려줍니다. 예를 들어 10을 돌려주면 토큰 수량에 10000000000을 곱해야 표준 표현이 됩니다. currencyOfCoupon은 쿠폰을 지급하는 토큰의 컨트랙트 주소로, 원금 통화와 다를 수 있습니다. couponType은 이자 방식을 숫자로 나타내며 예를 들어 0은 제로 쿠폰, 1은 고정금리, 2는 변동금리를 뜻할 수 있습니다. couponFrequency는 1년에 쿠폰을 지급하는 횟수이며 연간, 반기, 분기, 월간 같은 주기를 표현합니다. dayCountBasis는 이자 계산에 쓰는 일수 기준으로, 예를 들어 0은 actual/actual, 1은 actual/360을 뜻할 수 있고 30/360, actual/365, 30/365 같은 기준이 있습니다.
선택 인터페이스 IERC7092CrossChain
여러 블록체인에 걸쳐 채권 토큰을 관리하기 위한 선택 인터페이스입니다. 크로스체인 거래를 시작하는 모든 함수는 목적지 체인 식별자 destinationChainID와 상호작용할 대상 컨트랙트 destinationContract를 반드시 명시해야 합니다. crossChainApprove와 crossChainDecreaseAllowance는 다른 체인에서의 승인과 허용량 감소를, crossChainTransfer와 crossChainTransferFrom은 현재 체인에서 다른 체인으로의 토큰 이동(예: 이더리움에서 폴리곤으로)을 처리하며 각각 배치 버전이 있습니다. 크로스체인 전송과 승인 시에는 CrossChainTransfer, CrossChainApproval 등의 이벤트를 반드시 발생시킵니다.
totalSupply와 balanceOf가 없는 이유
이 표준은 totalSupply와 balanceOf를 정의하지 않습니다. 두 값은 issueVolume, principalOf, denomination에서 유도할 수 있기 때문입니다. 원문은 totalSupply를 issueVolume을 denomination으로 나눈 값으로, 계정 잔액을 해당 계정의 원금을 denomination으로 나눈 값으로 구현하는 예를 보여 주며, 구현 컨트랙트에 이런 함수를 추가하는 것은 허용됩니다. 이 함수들이 없기 때문에 이 표준은 ERC-20이나 ERC-1155와 하위 호환되지 않습니다. 토큰화 채권 발행에는 이 표준만 순수하게 구현하는 방식이 권장되며, 다른 표준과 섞은 하이브리드 구현은 실패해야 한다고 명시합니다.
참조 구현과 확장: 옵션부 채권과 신원 레지스트리
참조 구현 ERC7092.sol을 상속하면 옵션이 붙은 채권을 만들 수 있습니다. 원문은 발행자만 호출해 만기 전에 채권을 회수하는 콜러블(callable) 채권, 채권을 보유한 투자자가 만기 전에 되파는 풋터블(puttable) 채권, 채권을 주식으로 바꾸는 전환(convertible) 채권의 골격 예시를 보여 줍니다. 세 예시 모두 만기 전에만 실행되도록 block.timestamp가 maturityDate보다 작은지 검사합니다. 또 이 표준 자체는 채권 보유자의 신원 정보를 관리하지 않지만, 규제 준수와 투명성을 위해 표준 위에 신원 레지스트리를 얹어 등록되고 승인된 주체에게만 채권 토큰이 전송되도록 할 수 있습니다.
쓸 때 주의할 점
- 승인·전송 함수의 접근 검증: 운영자에게 소유자의 채권을 관리하도록 승인하는 함수와 채권을 전송하는 함수는 보안 위험을 신중히 고려해야 합니다. 채권 소유자나 승인된 계정만 이 함수들을 호출할 수 있도록 견고한 검증이 필요합니다.