ERC-7160 NFT 다중 메타데이터 확장 (ERC-721 Multi-Metadata Extension)
Final · NFT·멀티토큰 · 2023-06-09 작성
하나의 NFT에 여러 개의 메타데이터 주소(URI)를 붙이고 그중 하나를 대표로 고정(pin)할 수 있게 하는 ERC-721 확장 표준입니다.
왜 필요한가
기존 ERC-721 표준의 메타데이터 확장(ERC721Metadata)은 토큰 하나에 메타데이터 주소를 하나만 둘 수 있습니다. 그러나 하나의 토큰이 여러 자산을 묶어 담거나, 메타데이터 수정 이력을 체인 위에 남기거나, 화면 비율별로 다른 이미지를 제공하거나, 여러 작가가 함께 만든 작품을 담는 경우처럼 주소가 여러 개 필요한 쓰임새가 있습니다. 표준이 없으면 각 프로젝트가 제각각의 방식으로 여러 메타데이터를 저장하게 되고 앱(dApp)과 마켓플레이스는 토큰에 어떤 주소들이 있는지 알아내거나 보여 줄 공통 방법이 없습니다. 또 소유자가 여러 메타데이터 중 무엇을 대표로 보여 줄지 고르는 고정·해제 기능도 서비스마다 따로 구현해야 해서 널리 지원되기 어렵습니다.
구현해야 하는 것
event TokenUriPinned(uint256 indexed tokenId, uint256 indexed index);: 토큰의 특정 URI가 고정될 때 발생하는 이벤트입니다. 인덱싱에 쓰입니다.event TokenUriUnpinned(uint256 indexed tokenId);: 토큰의 고정이 해제될 때 발생하는 이벤트입니다.function tokenURIs(uint256 tokenId) external view returns (uint256 index, string[] memory uris, bool pinned);: 토큰에 연결된 모든 URI 배열과 현재 가리키는 인덱스, 고정 여부를 한 번에 돌려줍니다. 토큰이 없으면 되돌려야(revert) 합니다.function pinTokenURI(uint256 tokenId, uint256 index) external;: tokenURIs가 돌려주는 배열에서 index 위치의 URI를 토큰의 대표로 고정합니다. TokenUriPinned 이벤트를 반드시 냅니다.function unpinTokenURI(uint256 tokenId) external;: 토큰의 고정을 해제합니다. 해제 후의 동작은 개발자가 정하도록 의도적으로 열어 두었습니다. TokenUriUnpinned 이벤트를 반드시 냅니다.function hasPinnedTokenURI(uint256 tokenId) external view returns (bool pinned);: 토큰에 고정된 URI가 있는지 체인 위에서 확인합니다. URI 내용이 필요 없는 온체인 로직에 유용합니다.
심층 설명
tokenURIs: 세 가지 값을 한 번에 돌려주는 조회 함수
tokenURIs(uint256 tokenId)는 (uint256 index, string[] memory uris, bool pinned)를 돌려줍니다. uris는 토큰에 연결된 모든 URI 배열이고, index는 고정된 URI의 배열 인덱스(고정이 없으면 기본 URI의 인덱스), pinned는 고정 여부입니다. 존재하지 않는 토큰이면 반드시 revert해야 합니다. 단순히 문자열 배열만 돌려주는 설계도 검토했지만 세 번 호출할 것을 한 번으로 줄이기 위해 추가 정보를 함께 돌려주도록 했다고 Rationale에 설명합니다.
pin과 unpin: 이벤트가 필수인 상태 변경
pinTokenURI(tokenId, index)는 tokenURIs 배열의 index 위치 URI를 고정하며 호출 시 TokenUriPinned 이벤트를 반드시(MUST) 내야 합니다. unpinTokenURI(tokenId)는 고정을 해제하며 TokenUriUnpinned 이벤트를 반드시 내야 합니다. 두 함수 모두 존재하지 않는 토큰이면 revert해야 하고 ERC-4906의 MetadataUpdate 이벤트를 추가로 낼 수 있습니다(MAY). 해제 후의 구체적 동작은 개발자가 정하도록 의도적으로 열어 두었습니다. 참조 구현은 두 함수 모두 토큰 소유자만 호출할 수 있게 제한합니다.
기존 tokenURI와의 하위 호환
ERC-721 메타데이터 확장의 tokenURI 함수는 토큰에 고정된 URI가 있으면 반드시 그 URI를 돌려줘야 하고 고정이 없으면 반드시 기본(default) URI를 돌려줘야 합니다. 이 규칙 덕분에 ERC-7160을 모르는 기존 앱도 tokenURI만 호출해 올바른 대표 메타데이터를 받습니다. 참조 구현은 고정이 없을 때 배열의 마지막 항목(length - 1)을 기본 URI로 사용합니다.
인터페이스 식별자와 ERC-165
이 인터페이스의 ERC-165 식별자는 0x06e1bc5b입니다. supportsInterface(0x06e1bc5b) 호출은 반드시 true를 돌려줘야 합니다. 앱은 이 확인으로 컨트랙트가 다중 메타데이터를 지원하는지 미리 알 수 있습니다.
URI 추가·삭제는 표준 밖, ERC-4906 병행 권장
토큰에 URI를 추가하거나 제거하는 기능은 이 표준과 별도로 구현해야 합니다(MUST). 추가·제거 시에는 ERC-4906에 정의된 이벤트를 내는 것이 권장(RECOMMENDED)됩니다. 이미 ERC-4906 이벤트를 지원하는 앱이 많고 메타데이터가 바뀌었다는 알림이라는 목적에 그대로 들어맞으며 중복 이벤트로 인한 앱 문제를 피하기 위해서라고 Rationale에 설명합니다. 이벤트를 받은 쪽은 tokenURIs를 호출해 갱신된 목록을 가져오면 됩니다.
이벤트 기반 인덱싱과 활용 사례
TokenUriPinned와 TokenUriUnpinned 이벤트를 구독하면 앱이 어떤 메타데이터를 보여 줄지 온체인 호출 없이 이벤트 기반으로 인덱싱할 수 있습니다. 원문이 드는 활용 사례로는 순환하는 자산 모음, 메타데이터 수정 이력의 온체인 기록, 화면 비율별 메타데이터, 동적으로 진화하는 메타데이터, 여러 작가의 공동 토큰이 있습니다. 고정된 URI는 토큰의 대표로 쓰고 나머지 목록은 갤러리나 미디어 캐러셀로 보여 줄 수 있습니다.
쓸 때 주의할 점
- 상태 변경 함수의 접근 제어: 토큰에 URI를 추가하는 기능과 이 표준의 pinTokenURI, unpinTokenURI처럼 상태를 바꾸는 함수에는 접근 제어를 신중히 지정해야 합니다. 어떤 조건에서 고정과 해제를 허용할지는 애플리케이션마다 요구가 다르므로 개발자가 정해야 합니다.