ERC-3525 반대체성 토큰 (Semi-Fungible Token)

Final · NFT·멀티토큰 · 2020-12-01 작성

ERC-721과 호환되는 토큰에 슬롯(slot)과 수량(value)을 더해, 슬롯이 같은 토큰끼리는 수량을 쪼개서 주고받을 수 있게 하는 반대체성 토큰 표준입니다.

왜 필요한가

이더리움 토큰은 그동안 두 갈래로 나뉘어 있었습니다. ERC-20은 모든 단위가 서로 똑같은 대체 가능 토큰이고, ERC-721은 하나하나가 구별되는 대체 불가 토큰(NFT)입니다. 그런데 ERC-20은 데이터 구조나 속성 조합이 다른 자산마다 별도의 컨트랙트를 새로 만들어야 해서 실제로는 엄청나게 많은 ERC-20 컨트랙트가 생겨나는 문제가 있습니다. 반대로 ERC-721에는 수량 개념이 없어서 계산, 유동성, 관리 면에서 크게 불리합니다. 예를 들어 채권, 보험증권, 베스팅 플랜 같은 금융상품을 ERC-721로 만들면 토큰 안의 가치를 다룰 표준 인터페이스가 없어, 토큰이 나타내는 지분의 일부만 이전하는 일이 불가능합니다. 그래서 ERC-20의 수량 기능과 ERC-721의 개별 속성을 함께 가진 반대체성 토큰이 필요합니다. ERC-721과 뒤로 호환되게 만들면 이미 쓰이는 기반시설을 그대로 활용할 수 있어 채택도 빨라집니다.

구현해야 하는 것

심층 설명

ID, SLOT, VALUE 3요소 모델

각 토큰은 ERC-721과 동등한 ID를 가져 세계에서 유일한 개체로 식별되고 ERC-721 호환 방식으로 전송·승인할 수 있습니다. 여기에 더해 각 토큰은 수량 성격의 value 속성을 갖는데, 그 의미는 ERC-20 토큰의 balance와 매우 비슷합니다. 또 각 토큰에는 slot 속성이 있어, 같은 slot을 가진 두 토큰의 value는 서로 대체 가능한 것으로 취급됩니다. 조회 함수도 이 구조를 따릅니다. balanceOf(uint256 _tokenId)는 토큰의 value를, slotOf(uint256 _tokenId)는 토큰의 slot을 돌려주고 valueDecimals()는 value의 소수 자릿수를 돌려줍니다. 지갑 호환을 고려해 ERC-20의 decimals()와 겹치지 않도록 이름을 valueDecimals()로 정했습니다. 토큰의 slot이 바뀌면 SlotChanged 이벤트를 발생시켜야 합니다. 모든 호환 컨트랙트는 ERC-3525와 함께 ERC-721, ERC-165 인터페이스를 반드시 구현해야 하며 기본 인터페이스의 ERC-165 식별자는 0xd5358140입니다.

두 가지 value 전송 모델

ERC-721 호환 토큰 전송 외에 두 가지 전송이 추가됩니다. 첫째, transferFrom(_fromTokenId, _toTokenId, _value)는 같은 slot의 토큰 사이에서 value를 옮깁니다. 호출자는 _fromTokenId의 소유자이거나, 전체 권한 오퍼레이터이거나, 그 토큰 전체 또는 최소 _value만큼 승인받은 오퍼레이터여야 합니다. 두 토큰 중 하나라도 0이거나 존재하지 않으면, 두 토큰의 slot이 다르면, _value가 잔액이나 승인 한도를 넘으면 반드시 되돌려야 하고 TransferValue 이벤트를 발생시켜야 합니다. 둘째, transferFrom(_fromTokenId, _to, _value)는 토큰에서 주소로 value를 보냅니다. 실제로는 _to가 소유한 같은 slot의 토큰을 찾거나 새로 만들어 그 토큰이 value를 받게 하고 받은 토큰의 ID를 반환합니다. _to가 0 주소면 되돌려야 하며 Transfer와 TransferValue 이벤트를 모두 발생시켜야 합니다. 두 경우 모두 받는 쪽이 컨트랙트면 onERC3525Received 존재 여부를 확인하고 존재하면 호출해 0x009ce20b가 아니면 되돌려야 합니다.

네 단계 승인 모델

승인은 네 가지 수준으로 나뉩니다. setApprovalForAll은 ERC-721 호환으로, 소유자의 모든 토큰과 그 value까지 관리할 수 있는 전체 승인입니다. setApprovalForSlot(선택)은 지정한 slot의 토큰 전부를 관리하는 슬롯 승인입니다. ERC-721 호환 approve는 특정 토큰 ID 하나와 그 value를 관리하는 토큰 ID 승인이고 approve(_tokenId, _operator, _value)는 특정 토큰에서 지정한 최대 value까지만 관리하는 value 승인입니다. 어떤 승인 함수든 호출자는 소유자이거나 더 높은 수준의 권한을 승인받은 자여야 합니다. 수준 간 관계도 정해져 있습니다. 토큰 ID를 승인받은 오퍼레이터는 그 토큰에서 value를 부분 전송할 수 있지만 반대로 토큰의 value 전액을 승인받았다고 해서 토큰 개체 자체를 이전할 수 있는 것은 아닙니다. setApprovalForAll을 받으면 어느 토큰에서든 value를 부분 전송할 수 있고 제3자에게 value 부분 전송을 승인해 줄 수도 있습니다.

세이프 전송 대신 확인·알림·응답 방식

ERC-721 계열의 세이프 전송(Safe Transfer)은 안전·비안전 전송 방식 중 무엇을 쓸지 보내는 쪽이 골라야 하는데, 보내는 사람이 어느 쪽을 써야 할지 모르는 경우가 많고 safeTransferFrom을 부르면 받는 컨트랙트가 콜백을 구현하지 않았다는 이유만으로 전송이 실패할 수 있습니다. 이 표준은 대신 확인, 알림, 응답(Check, Notify and Response) 방식을 씁니다. 별도의 safeTransferFrom 없이 전송 함수는 한 종류만 호출하면 되고 모든 ERC-3525 컨트랙트는 받는 컨트랙트에 onERC3525Received가 있는지 확인해 있으면 반드시 호출해야 합니다. 받는 컨트랙트는 전송을 받아들이면 0x009ce20b를 돌려주고 거부하면 다른 값을 돌려주거나 되돌립니다. 자기 자신에게 value를 보내는 경우에도 onERC3525Received가 호출되므로, 컨트랙트는 자기 전송과 외부 수신에 서로 다른 수락 규칙을 둘 수 있습니다.

메타데이터와 온체인 JSON 권장

메타데이터 확장은 ERC-721 메타데이터 확장과 호환되며 컨트랙트 전체의 contractURI()와 슬롯별 slotURI(uint256)를 추가합니다. 두 함수 모두 data:application/json; 헤더로 시작하는 JSON 형식 URI를 돌려주는 것이 권장됩니다. 중앙화 시스템에 저장된 데이터보다 스마트 컨트랙트에 저장된 데이터가 더 신뢰할 수 있으므로, contractURI, slotURI, tokenURI 모두 외부 URL 대신 JSON을 직접 반환하라고 권고합니다. 컨트랙트 메타데이터 스키마에는 name, description, image, external_link, valueDecimals가 있고, 슬롯 메타데이터의 properties 항목에는 is_intrinsic 필드가 있습니다. slot 값을 여러 속성의 keccak256 해시로 만드는 것이 모범 사례인데, 계산에 쓰인 속성이면 is_intrinsic을 TRUE로 표시하고 order에 계산에 등장한 순서를 적습니다. 이 표준은 수집품이나 게임 아이템보다 금융상품에 적합하게 설계되었기에, 투명성과 안전을 위해 메타데이터를 컨트랙트 코드에서 직접 생성할 것을 강하게 권장합니다.

선택 확장: 슬롯 열거와 슬롯 승인

두 가지 선택 확장이 있습니다. 슬롯 열거 확장(IERC3525SlotEnumerable, ERC-165 식별자 0x3b741b9e)은 컨트랙트가 가진 슬롯 전체 목록을 공개해 탐색할 수 있게 합니다. slotCount()로 슬롯 총수를, slotByIndex(_index)로 순번별 슬롯을, tokenSupplyInSlot(_slot)으로 해당 슬롯 토큰의 총 개수를, tokenInSlotByIndex(_slot, _index)로 그 슬롯 안의 순번별 토큰 ID를 조회합니다. 슬롯 승인 확장(IERC3525SlotApprovable, 식별자 0xb688be58)은 소유자의 같은 슬롯 토큰 전부를 오퍼레이터가 관리하게 해 줍니다. setApprovalForSlot의 호출자는 소유자 본인이거나 setApprovalForAll로 권한을 받은 오퍼레이터여야 하고 승인이 바뀌면 ApprovalForSlot 이벤트를 발생시켜야 합니다. isApprovedForSlot으로 승인 여부를 조회합니다.

쓸 때 주의할 점

ERC 원문 · ← ERCs