ERC-5516 소울바운드 다중 소유자 토큰 (Soulbound Multi-owner Tokens)

Final · NFT·멀티토큰 · 2022-08-19 작성

양도할 수 없는 증명용 토큰(소울바운드 토큰)을 한 번의 거래로 여러 계정에 동시에 발행하고 같은 컨트랙트 안에 여러 토큰 종류를 담을 수 있게 하는 표준입니다.

왜 필요한가

졸업장이나 참석 증명처럼 남에게 넘기면 안 되는 토큰을 소울바운드 토큰(SBT)이라고 부릅니다. 기존의 계정 귀속 토큰 표준에서는 토큰이 계정 하나에만 묶이기 때문에, 사용자가 계정 열쇠(키)를 잃어버리거나 키를 교체하면 토큰도 함께 잃게 됩니다. 또 같은 증명서를 여러 사람에게 주려면 받는 사람마다 발행 거래를 따로 보내야 해서 거래 비용이 늘어나고 토큰 종류마다 별도 컨트랙트를 배포하면 같은 코드가 블록체인에 중복으로 올라갑니다. 이 표준은 하나의 토큰을 여러 계정에 동시에 묶고 한 번의 거래로 여러 수령자에게 발행하며 여러 토큰 종류를 한 컨트랙트에 담을 수 있게 해서 이런 문제를 풉니다. 받은 사람이 원하지 않는 토큰은 스스로 포기할 수도 있습니다.

구현해야 하는 것

심층 설명

인터페이스와 ERC-165

이 표준을 구현하는 컨트랙트는 ERC-5516 인터페이스의 모든 함수를 구현해야 하고(MUST), ERC-165의 supportsInterface에 interfaceID로 0x85a5f87c가 들어오면 true를 반환해야 합니다. 인터페이스는 issue, renounce, has, hasRenounced, issuerOf, uri, deriveTokenId 일곱 함수와 Issued, Renounced 두 이벤트로 구성됩니다. ERC-1155의 멀티 토큰 구조에서 영감을 받았지만 부분 호환을 버리고 증명 발행이라는 목적에 맞는 최소 인터페이스로 설계했습니다.

토큰 ID 유도 규칙

tokenId는 (issuer, metadataURI) 쌍에서 결정적으로 유도되어야 하고 유도 함수는 이 쌍에 대해 충돌 저항성이 있어야 합니다. 권장 구현은 uint256(keccak256(abi.encodePacked(issuer, metadataURI)))입니다. issue(recipients, metadataURI) 호출은 deriveTokenId(msg.sender, metadataURI)와 같은 값을 반환해야 하며 deriveTokenId는 인자와 불변 배포 파라미터만의 함수여야 하고 가변 컨트랙트 상태에 의존하면 안 됩니다. tokenId에 호출자 주소가 섞이기 때문에, 대기 중인 발행 거래를 멤풀에서 본 공격자가 같은 tokenId를 선점할 수 없습니다. 공격자가 보내는 복사 거래는 자기 주소 기준의 다른 식별자가 나오기 때문입니다.

재발행과 원자성

같은 msg.sender가 같은 metadataURI로 issue를 다시 호출하면 같은 tokenId로 귀결되고 새 토큰을 만드는 대신 그 tokenId의 보유자 집합을 넓혀야 합니다. 기존 tokenId를 원래 발행자가 아닌 주소가 노리는 issue 호출은 거부해야 합니다. 요구 조건으로 recipients는 비어 있으면 안 되고 0 주소가 없어야 하며 이미 그 tokenId를 보유했거나 과거에 포기한 주소가 하나라도 있으면 전체 호출이 되돌려집니다. 부분 성공을 허용하면 Issued 이벤트의 recipients를 실제로 자격이 부여된 주소 집합으로 믿을 수 없게 되기 때문입니다. 따라서 큰 집단에 재발행하는 발행자는 이미 받았거나 포기한 주소를 체인 밖에서 걸러낸 뒤 호출해야 합니다.

포기(renounce)의 최종성

renounce(tokenId)가 성공하면 호출자의 보유가 지워지고 그 주소에 대해 영구적입니다. 이후 같은 tokenId로 그 주소를 recipients에 포함한 issue 호출은 모두 거부되어야 합니다. 발행자가 협조하거나 강요받더라도 다시 붙일 수 없게 해서 어떤 증명을 자기 계정에 남길지에 대한 보유자의 결정권을 지키는 의도된 설계입니다. 마음이 바뀐 포기자에게 증명을 복구하려면 발행자가 다른 metadataURI로 새 tokenId를 발행해야 합니다.

메타데이터와 URI 불변성

uri(tokenId)가 반환하는 값은 첫 발행 때 고정되며 재발행이나 다른 어떤 작업으로도 바꿀 수 없습니다. tokenId가 URI에서 유도되므로 체인 위에서 URI를 바꾸면 식별자와 메타데이터의 결합이 깨지기 때문입니다. URI 문자열에 {id}가 있으면 클라이언트는 이를 토큰 ID의 16진수 표현으로 치환해야 하는데, 0x 접두사 없는 소문자 [0-9a-f]로 쓰고 필요하면 앞을 0으로 채워 64자로 만듭니다. 구현은 고정 베이스 URI에 발행 시 받은 metadataURI를 이어 붙여 uri 값을 구성할 수 있으며 이 경우 tokenId를 다시 계산하는 검증자는 uri(tokenId) 반환값이 아니라 issue에 원래 넣은 metadataURI를 써야 합니다. 베이스 URI는 배포 후 불변이어야 하고 이를 수정하는 어떤 기능도 노출하면 안 됩니다.

이벤트 규칙과 인덱싱

issue 호출마다(재발행 포함) Issued 이벤트를 정확히 한 번 내야 하고 이벤트의 recipients에는 그 호출에서 추가된 주소만 담아야 합니다. 누적 보유자 집합이 아닙니다. renounce도 호출마다 Renounced 이벤트를 정확히 한 번 냅니다. 이 규칙 덕분에 컨트랙트가 내는 이벤트 로그만으로 모든 현재 토큰 잔액의 정확한 기록을 만들 수 있고 데이터베이스나 탐색기가 이벤트를 수신해 컨트랙트 안의 모든 토큰을 색인하고 분류해 검색할 수 있습니다. 인덱서는 두 이벤트를 모두 처리해야 하며 가장 최근 Issued 이벤트를 그 tokenId의 전체 보유자 집합으로 가정하면 안 됩니다.

발행자 회수 기능의 의도적 제외

기본 인터페이스에는 발행자 측 회수(revocation)가 없습니다. 일단 발행되면 (주소, tokenId) 결합을 지우는 유일한 작업은 보유자의 자발적 renounce입니다. 회수 정책은 만료, 대체, 부정행위 등 용도마다 크게 달라서 한 가지 모델을 기본 인터페이스에 표준화하면 다른 모델이 필요한 구현을 제약하기 때문입니다. 회수가 필요한 발행자는 인터페이스를 자체 확장하거나, 대체하는 새 tokenId를 발행하고 검증자가 그 발행자의 최신 증명만 인정하게 만드는 방식으로 모델링할 수 있습니다. 회수를 뺀 덕분에 발행자 키가 나중에 유출되더라도 보유자 외에는 누구도 결합을 지울 수 없다는 보유자 측 보장이 강해집니다.

다중 소유와 키 분실 대응

양도 불가 토큰은 계정 키가 유출되거나 교체되면 보유자가 토큰과의 연결을 잃을 수 있습니다. 이 표준은 하나의 토큰을 여러 계정에 묶을 수 있게 해서 이 문제의 해법을 제시합니다. 멀티시그 기능을 구현한 컨트랙트 계정으로 발행하는 방법도 있는데, 이는 ERC-4973의 예외 처리에서 권장된 방식과 같습니다. 멀티 토큰 구조 덕분에 여러 토큰 종류가 한 컨트랙트에 저장되어 중복 바이트코드 배포를 막고 발행된 모든 토큰을 같은 컨트랙트 주소에서 접근할 수 있습니다. 이 표준은 Final 상태로 확정되었습니다.

쓸 때 주의할 점

ERC 원문 · ← ERCs