ERC-7631 이중 성격 토큰 쌍 (Dual Nature Token Pair)
Final · NFT·멀티토큰 · 2024-02-21 작성
ERC-20 토큰과 ERC-721 토큰(NFT)을 한 쌍으로 연결해, 한쪽에서 일어난 전송이 다른 쪽에 반영되는 토큰을 만들 수 있게 하고 두 컨트랙트의 연결 관계 조회와 NFT 자동 발행 건너뛰기 설정을 정의합니다.
왜 필요한가
하나의 토큰을 ERC-20(대체 가능 토큰)과 ERC-721(대체 불가능 토큰, NFT)의 두 모습으로 함께 쓰는 설계가 있습니다. 예를 들어 ERC-20을 취득하면 보유량에 비례해 NFT가 자동으로 발행되는 식으로, NFT를 잘게 나눠 갖는 분할 소유를 표준 토큰만으로 구현할 수 있습니다. 그런데 두 컨트랙트가 서로 연결돼 있다는 사실을 밖에서 확인할 표준 방법이 없으면, 탈중앙 거래소나 NFT 마켓플레이스가 두 토큰의 관계를 화면에 보여 줄 수 없습니다. 또 ERC-20이 움직일 때마다 NFT가 자동으로 따라 발행되거나 전송되면, 베스팅 컨트랙트나 유동성 풀처럼 큰 금액을 다루는 계정은 수량에 비례하는 O(n) 가스비를 내야 하고 블록 가스 한도를 넘겨 전송 자체가 실패할 수도 있습니다. 이 표준은 연결 관계를 조회하는 공통 인터페이스와 NFT 발행을 건너뛰는 설정 방법을 정의해 이런 문제를 풉니다.
구현해야 하는 것
function mirrorERC721() external view returns (address);: 짝이 되는 미러 ERC-721 컨트랙트의 주소를 돌려줍니다. 아직 연결 전이면 되돌리거나 0 주소를 돌려줄 수 있습니다.event SkipNFTSet(address indexed owner, bool status);: owner의 NFT 건너뛰기 상태가 status로 바뀌었음을 인덱서에 알리는 이벤트입니다.function getSkipNFT(address owner) external view returns (bool);: ERC-20에서 ERC-721로 동기화할 때 owner에 대한 NFT 발행과 전송을 건너뛰어야 하면 true를 돌려줍니다.function setSkipNFT(bool status) external;: 호출한 계정 자신의 NFT 건너뛰기 상태를 설정하고 SkipNFTSet 이벤트를 냅니다.function baseERC20() external view returns (address);: 짝이 되는 베이스 ERC-20 컨트랙트의 주소를 돌려줍니다. 아직 연결 전이면 되돌리거나 0 주소를 돌려줄 수 있습니다.
심층 설명
베이스와 미러로 이루어진 쌍
이중 성격 토큰 쌍은 ERC-20 컨트랙트 하나와 ERC-721 컨트랙트 하나로 이루어집니다. 관례상 ERC-20 쪽을 베이스(base) 컨트랙트, ERC-721 쪽을 미러(mirror) 컨트랙트라고 부릅니다. 보통의 구현에서는 ERC-20 잔액에서 ERC-721 보유 수량을 쉽게 끌어낼 수 있어 ERC-20을 베이스로 삼지만 필요하면 대부분의 로직을 ERC-721 쪽에 두는 것도 금지하지 않습니다. 두 토큰은 각각 ERC-20과 ERC-721 표준을 완전히 지키면서 한쪽에서 일어난 전송이 다른 쪽에 반영될 수 있습니다. 이를 이용하면 ERC-20 취득이 보유량에 비례한 ERC-721 자동 발행으로 이어지는 네이티브 분할 소유 같은 응용이 가능합니다.
상호 참조 규칙과 불변성
베이스는 mirrorERC721()을, 미러는 baseERC20()을 반드시 구현합니다. 두 메서드는 아직 연결되지 않았음을 나타내기 위해 되돌리거나(revert) 0 주소를 돌려줄 수 있습니다. 0이 아닌 주소를 돌려주는 경우 그 주소는 반대쪽 인터페이스를 구현해야 하고 그쪽의 메서드가 다시 이 컨트랙트 주소를 돌려줘야 합니다. 한 번 0이 아닌 주소를 돌려준 뒤에는 되돌려서도 안 되고 값이 바뀌어서도 안 됩니다. 0이 아닌 주소를 돌려준다는 사실 자체가 필요한 인터페이스를 구현했다는 신호이므로 ERC-165 같은 별도 감지 장치는 필요 없습니다.
getSkipNFT와 setSkipNFT의 동작
getSkipNFT(owner)가 true를 돌려주면 ERC-20에서 ERC-721로 동기화할 때 owner에 대한 ERC-721 발행과 전송을 건너뛰어야 합니다. 이 메서드는 초기화 전이거나 기능을 지원하지 않는 경우 되돌릴 수 있는데, 그때 상호작용하는 코드는 setSkipNFT 기능이 없는 것으로 보고 관련 UI를 숨기며 해당 owner의 상태는 정의되지 않은 것으로 봐야 합니다. 다만 어떤 owner에 대해 한 번 true나 false를 돌려줬다면 그 owner에 대해서는 다시 되돌리면 안 됩니다. setSkipNFT는 호출자 자신의 상태를 설정하고 SkipNFTSet 이벤트를 내며 누구나 호출할 수 있게 두는 것이 권장됩니다. 이벤트는 구현 단순화를 위해 상태가 바뀌지 않아도 낼 수 있습니다. 또 Solidity나 Vyper로 컴파일된 컨트랙트는 정의되지 않은 메서드 호출에 원래 되돌리기 때문에, 이 두 메서드를 명시적으로 정의하지 않은 베이스 구현도 IERC7631BaseNFTSkippable을 따르는 것으로 봅니다.
NFT 건너뛰기가 필요한 상황
건너뛰기 설정은 ERC-20 전송 때마다 ERC-721이 자동 발행되는 것을 피하게 해 줍니다. 원문이 드는 예는 여러 사용자에게 나눠 줄 ERC-20을 베스팅 컨트랙트에 대량으로 넣을 때, ERC-721 판매용 캔디 머신 컨트랙트에 ERC-20을 대량으로 넣을 때, 유동성 풀에 ERC-20을 대량으로 넣고 뺄 때, 관리자 계정 사이에서 대량 이동할 때입니다. 이 메서드를 표준에 넣으면 앱이 사용자에게 건너뛰기 옵션을 쉽게 보여 줄 수 있고 여러 ERC-721 발행에 드는 O(n) 가스비 없이 어떤 금액의 ERC-20도 옮길 수 있습니다. 대량 자동 전송은 블록 가스 한도를 넘길 수도 있어 가스비가 싼 EVM 체인에서도 권장됩니다. owner가 스마트 컨트랙트면 getSkipNFT가 기본으로 true를 돌려주게 하는 패턴이 유용하다고 소개합니다. 반환값을 불리언으로 한 것은 단순함을 위한 선택이며 더 복잡한 동작이 필요하면 개발자가 메서드를 추가할 수 있습니다.
동기화 로직과 연결 절차는 구현 자유
이 표준은 두 토큰 사이의 동기화 로직 자체는 정의하지 않습니다. 자동 리베이스 토큰 같은 새로운 사용 사례와 다양한 구현 패턴에 유연성을 남기기 위해서입니다. 연결 절차도 생성자에서 연결하거나, 초기화 함수 또는 관리자 전용 공개 메서드로 연결하는 등 어떤 방식이든 쓸 수 있습니다. 제약은 하나뿐입니다. 한 번 성립한 쌍은 바꿀 수 없어야 하는데, 이는 인덱싱 로직을 단순하게 유지하기 위한 것입니다. 이 제안은 Final 상태로 확정된 표준입니다.
쓸 때 주의할 점
- 동기화 함수 접근 제어: 동기화 로직을 위한 외부 메서드는 반드시 짝이 되는 상대 컨트랙트만 호출할 수 있도록 보호해야 합니다.
- 희귀 NFT 노리기: 희귀도가 다른 ERC-721을 제공하는 컬렉션이라면, 메타데이터 수집과 ERC-20 전송으로 희귀 NFT를 골라 가져가기 어렵게 메타데이터를 공개해야 합니다. 같은 계정이 일정 시간 보유한 뒤에 메타데이터를 공개하는 방식이 권장됩니다.
- 가스 고갈로 인한 서비스 거부: ERC-20 전송이 여러 ERC-721의 발행, 전송, 소각을 자동으로 일으키면 보통의 O(1)이 아닌 O(n) 가스비가 들고 ERC-721 토큰 ID를 고르는 로직도 추가 가스를 쓸 수 있습니다. 동기화 로직은 이런 가스 비용을 고려해 가스 고갈로 전송이 막히는 문제를 예방해야 합니다.