ERC-7432 NFT 역할 부여 표준 (Non-Fungible Token Roles)

Final · NFT·멀티토큰 · 2023-07-14 작성

NFT의 소유권은 그대로 두고 정해진 시각에 자동 만료되는 역할만 다른 계정에 부여하는 공통 방법을 정의합니다. 디지털 부동산 임대나 보험 증권처럼 NFT의 쓸모를 나누는 데 쓸 수 있습니다.

왜 필요한가

NFT는 소유자가 한 명뿐이어서 그 쓸모를 다른 사람과 나누기 어렵습니다. 예를 들어 디지털 부동산 NFT의 관리나 임차 권한을 다른 사람에게 맡기려면 토큰 자체를 넘겨야 하는데, 그러면 소유권까지 함께 넘어갑니다. 또 온체인에서 누가 어떤 권한을 언제까지 갖는지 기록하는 공통 규칙이 없으면, 에어드랍 수령 자격처럼 특별한 행동에 대한 접근 제어를 탈중앙 앱(dApp)마다 제각각 구현해야 합니다. ERC-7432는 NFT 하나에 여러 역할(role)을 붙이고 각 역할이 정해진 시각에 자동으로 만료되게 하는 공통 인터페이스를 정의합니다. 역할은 bytes32 값으로 구분하고 임의 크기의 추가 데이터를 붙일 수 있어 소유권을 지키면서 쓸모만 빌려 주는 다양한 용도에 맞출 수 있습니다.

구현해야 하는 것

심층 설명

ERC-721의 확장이 아닌 독립 표준

이 표준은 ERC-721을 상속하지 않습니다. NFT와 같은 컨트랙트에 구현할 수도 있고 별도의 외부 컨트랙트로 구현할 수도 있게 하기 위해서입니다. 그래서 모든 함수와 이벤트가 tokenAddress와 tokenId를 함께 받습니다. 이 구조 덕분에 이미 배포되어 바꿀 수 없는(immutable) NFT에도 독립형 ERC-7432 컨트랙트를 역할의 공식 출처로 삼아 역할 기능을 붙일 수 있습니다. 준수 컨트랙트는 IERC7432 인터페이스를 반드시 구현해야 하고 ERC-165 구현이 권장되며 이 인터페이스의 ERC-165 식별자는 0xd00ca5cf입니다.

역할 식별자와 Role 구조체

역할은 bytes32 값으로 구분합니다. 역할 이름과 입력 타입을 keccak256으로 해시한 값, 즉 keccak256("RoleName(input_type)") 형태를 쓰는 것이 권장됩니다. grantRole에 넘기는 Role 구조체는 roleId, tokenAddress, tokenId, recipient(역할을 받는 계정), expirationDate(uint64 만료 시각), revocable(조기 취소 가능 여부), data(임의 크기의 추가 데이터)로 이루어집니다. data는 bytes 타입이어서 앱이 역할에 필요한 정보를 자유롭게 인코딩해 담을 수 있고 roleData 함수로 그대로 조회되며 RoleGranted 이벤트에도 함께 기록되어 온체인과 오프체인 양쪽에서 쓸 수 있습니다.

grantRole과 토큰 잠금

grantRole은 expirationDate가 과거이거나 msg.sender가 NFT 소유자를 대신해 역할을 부여하도록 승인된 계정이 아니면 반드시 실패(revert)해야 합니다. 역할을 부여하면 RoleGranted 이벤트를 내고 토큰이 동결되거나 에스크로 계정으로 옮겨지는 경우에는 TokenLocked 이벤트도 함께 내야 합니다. 원문은 구현 방식을 못 박지 않으려고 중립적인 용어인 잠금(lock)을 씁니다. 역할이 만료될 때까지 전송을 막는 동결 방식일 수도 있고 에스크로 컨트랙트에 맡기는 방식일 수도 있으며 어느 쪽을 쓸지는 구현자가 용도에 맞게 정합니다.

자동 만료와 uint64 날짜

만료는 grantRole이 정하는 expirationDate와 이를 조회하는 roleExpirationDate 함수로 구현됩니다. 날짜는 uint256 대신 uint64로 표현하는데, 대부분의 프로그래밍 언어가 uint256을 기본 지원하지 않기 때문입니다. uint64로 표현할 수 있는 최대 유닉스 타임스탬프는 약 584,942,417,355년에 해당해 사실상 영구로 볼 수 있습니다. 그래서 역할이 절대 만료되지 않아야 하는 용도에는 type(uint64).max를 쓰는 것이 권장됩니다.

취소 가능 여부와 unlockToken

revocable이 true면 원래 소유자와 수신자 모두 만료 전에 revokeRole로 역할을 취소할 수 있고 false면 수신자만 취소할 수 있습니다. 수신자가 원치 않는 역할을 스스로 지울 수 있도록 revocable 값과 관계없이 수신자의 취소는 항상 허용하는 것이 권장됩니다. revokeRole은 msg.sender가 원래 소유자나 수신자를 대신할 권한이 없으면 실패해야 합니다. unlockToken은 잠긴 NFT를 원래 소유자에게 돌려주거나 동결을 푸는 함수로, msg.sender가 승인되지 않았거나 만료되지 않은 취소 불가 역할이 하나라도 남아 있으면 실패해야 합니다. 이 규칙이 취소 불가 역할을 받은 수신자를 보호합니다.

역할 승인(Role Approval)

ERC-721의 승인과 비슷하게 다른 계정이 소유자를 대신해 역할을 관리할 수 있습니다. setRoleApprovalForAll로 특정 토큰 주소에 대해 운영자(operator)에게 역할 부여와 취소 권한을 주거나 거둘 수 있고 승인 상태가 바뀌면 RoleApprovalForAll 이벤트가 기록됩니다. isRoleApprovedForAll은 운영자가 원래 NFT 소유자를 대신해 역할을 부여하고 취소하도록 승인된 경우에만 true를 돌려주어야 합니다. 이 기능은 NFT를 소유하지 않은 제3자가 ERC-7432와 상호작용할 수 있게 하려고 도입되었으며 준수 컨트랙트는 두 함수를 반드시 구현해야 합니다.

메타데이터 확장

선택 사항인 메타데이터 확장은 기존 NFT의 JSON 메타데이터에 roles 배열을 더합니다. 이 기능을 지원하는 앱은 ERC-721의 메타데이터 확장도 구현해야 합니다. 각 역할 항목에는 역할을 구분하는 id(bytes32), 사람이 읽을 수 있는 name, 설명 description, 그리고 inputs가 들어갑니다. inputs는 grantRole에 인코딩해 넘겨야 할 매개변수를 설명하는 중요한 필드로, Solidity ABI 명세에 정의된 type과 components 속성을 쓰는 것이 권장됩니다. roles 배열의 속성 구성은 제안일 뿐이어서 개발자가 역할 이미지 같은 다른 정보를 필요에 따라 더할 수 있습니다.

쓸 때 주의할 점

ERC 원문 · ← ERCs