ERC-1046 tokenURI 상호운용성 확장 (tokenURI Interoperability)
Final · 이름·메타데이터 · 2018-04-13 작성
ERC-20 토큰에도 ERC-721처럼 이미지와 설명이 담긴 주소를 돌려주는 tokenURI() 함수를 두고 ERC-20·721·1155 메타데이터에 토큰 종류를 알리는 interop 필드를 넣어 지갑이 세 표준을 같은 방식으로 읽게 합니다.
왜 필요한가
ERC-721 대체 불가 토큰(NFT)에는 썸네일 이미지, 제목, 설명 같은 부가 정보가 담긴 주소를 돌려주는 tokenURI 함수가 있습니다. 그런데 ERC-20 토큰에는 같은 역할을 하는 표준 함수가 없어서 토큰 로고나 소개글을 어디서 가져와야 하는지 정해진 방법이 없었습니다. 또 지갑이나 웹사이트가 어떤 컨트랙트를 만났을 때 그것이 ERC-20인지 ERC-721인지 ERC-1155인지, 그 토큰이 어떤 기능을 가졌는지 메타데이터만 보고는 알기 어려웠습니다. 종류를 알아내려면 컨트랙트에 직접 질의하는 ERC-165 방식을 쓸 수 있지만 원문은 이런 정적 호출이 지갑과 웹사이트에 시간이 많이 들고 유연하지 않다고 설명합니다. 이 표준은 ERC-20에 tokenURI를 추가하고 세 표준의 메타데이터 JSON에 토큰 종류를 알려 주는 interop 필드를 넣어 이 문제를 풉니다.
구현해야 하는 것
function tokenURI() external view returns (string);: ERC-20 토큰의 메타데이터 JSON이 있는 URI를 돌려줍니다. 돌려준 데이터는 JSON이어야 하고 이 표준의 ERC-20 Token Metadata Schema를 따라야 합니다.function tokenURI(uint256 tokenId) external view returns (string);: ERC-1155 토큰 ID의 메타데이터 JSON이 있는 URI를 돌려줍니다. 컨트랙트에 uri(tokenId) 함수가 있으면 같은 URI여야 합니다.
심층 설명
상호운용 메타데이터(InteroperabilityMetadata)
세 표준의 메타데이터 JSON에 공통으로 들어가는 interop 객체의 형태입니다. erc1046, erc721, erc1155 세 필드가 있고 모든 필드는 선택 사항이어야 합니다. 각 필드는 해당 표준의 메타데이터일 때 반드시 true로 적고 그렇지 않으면 반드시 생략해야 합니다. erc1046이 true면 지갑은 그 주소를 ERC-20 토큰으로, erc721이 true면 ERC-721 토큰으로, erc1155가 true면 ERC-1155 토큰으로 다루라는 뜻입니다. 이 인터페이스는 다른 제안이 확장할 수 있습니다. 모든 필드가 불리언일 필요는 없어서 JSON으로 직렬화할 수 있는 어떤 선택 객체든 확장 필드로 쓸 수 있습니다.
ERC-20 인터페이스 확장: tokenURI()
이 표준을 따르는 ERC-20 컨트랙트는 ERC20TokenMetadata 인터페이스를 반드시 구현해야 합니다. 인터페이스에는 인자 없이 호출하는 tokenURI() 함수 하나만 있고 external view로 string을 돌려줍니다. 돌려준 URI가 가리키는 데이터는 반드시 JSON 형식이어야 하며 이 표준의 ERC-20 Token Metadata Schema를 지원해야 합니다. ERC-721의 tokenURI(uint256)와 달리 토큰 ID 인자가 없습니다. 원문은 ERC-721과 같은 이름을 쓴 이유로 코드 재사용이 쉬워진다는 점을 듭니다.
ERC-20 토큰 메타데이터 스키마
tokenURI()가 가리키는 JSON은 ERC20TokenMetadata 인터페이스를 따라야 합니다. interop은 필수이고 나머지는 선택입니다. name과 symbol은 컨트랙트에 name(), symbol() 함수가 있고 빈 문자열이 아닌 값을 돌려주면 반드시 같은 값이어야 합니다. decimals도 decimals() 함수가 있으면 반드시 같은 값이어야 하고 필드와 함수가 둘 다 없으면 기본값은 18입니다. description은 마크업과 줄바꿈이 없는 한 문단짜리 짧은 설명입니다. image는 mime 타입이 image/*인 자원을 가리키는 URI 하나이고 images는 같은 성격의 URI 배열입니다. 비트맵이면 너비 320에서 1080픽셀, 가로세로 비율 1.91:1에서 4:5 사이가 권장됩니다. icons는 아이콘용 URI 배열로, 비트맵이면 너비 320에서 1080픽셀이 권장되고 높이는 너비와 반드시 같아야 하며 비율 1:1에 투명 배경을 반드시 써야 합니다.
ERC-721 확장
ERC-721을 구현하고 토큰 메타데이터 URI를 쓰는 컨트랙트는 기존 ERC721TokenMetadata를 확장한 ERC721TokenMetadataInterop 형태를 쓰는 것이 권장(SHOULD)됩니다. 추가되는 것은 interop 필드 하나이며 서로 다른 토큰 URI 사이의 혼동을 피하기 위한 것입니다. 원문의 ERC-721 확장은 메타데이터 스키마만 다루고 새 Solidity 함수는 추가하지 않습니다. 기존 tokenURI(tokenId)가 가리키는 JSON에 interop을 더하면 됩니다.
ERC-1155 확장: tokenURI(tokenId)와 스키마
메타데이터 확장을 쓰는 ERC-1155 컨트랙트는 ERC1155TokenMetadataInterop 인터페이스를 구현하는 것이 권장(SHOULD)됩니다. 이 인터페이스의 tokenURI(uint256 tokenId)는 토큰 ID를 받아 string을 돌려주며 가리키는 데이터는 반드시 JSON이어야 하고 이 표준의 ERC-1155 스키마 확장을 지원해야 합니다. 컨트랙트에 uri(tokenId) 함수가 있으면 tokenURI(tokenId)는 반드시 그와 같은 URI를 돌려줘야 합니다. 메타데이터 쪽에서는 ERC1155TokenMetadata를 확장해 interop 필드를 더한 ERC1155TokenMetadataInterop 형태가 권장(RECOMMENDED)됩니다. 단, 위 Solidity 인터페이스를 구현한 컨트랙트는 이 확장 형태를 반드시 써야 합니다.
가스 절약 권고: name·symbol·decimals 생략
가스를 아끼기 위해 이 표준을 따르는 컨트랙트는 name(), symbol(), decimals() 함수를 구현하지 않고 그 값을 메타데이터 URI에만 넣는 것이 권장됩니다. ERC-20 토큰의 decimals가 18이면 메타데이터에 decimals 필드를 넣지 않는 것이 권장(NOT RECOMMENDED to include)됩니다. 스키마에서 decimals 필드와 decimals() 함수가 둘 다 없을 때 기본값이 18이므로, 둘을 모두 생략해도 지갑은 18로 해석합니다. 다만 함수를 구현한다면 메타데이터 값과 반드시 일치해야 한다는 규칙은 그대로 적용됩니다.
설계 이유와 호환성
원문은 ERC-721과 같은 tokenURI라는 이름을 쓰면 개발자가 ERC-20에 메타데이터를 붙이기 쉽고 코드를 재사용하기 쉬우며 생태계에 미치는 영향이 거의 없다고 설명합니다. 토큰 종류 판별은 ERC-165로도 할 수 있지만 정적 호출(static call)은 지갑과 웹사이트에 시간이 많이 들고 유연하지 않습니다. 대신 토큰 URI 안에 상호운용 정보를 넣으면 유연성과 성능이 함께 좋아진다고 봅니다. 이 표준은 ERC-20 기능을 선택적으로 확장하는 것이므로 완전히 하위 호환되고 ERC-721과 ERC-1155에 대한 권고도 하위 호환됩니다. 원문 상태는 Final입니다.
쓸 때 주의할 점
- 서버 측 요청 위조(SSRF): 지갑은 임의의 URL에 요청을 보내는 데 주의해야 합니다. 원문은 허용할 스킴(scheme)과 포트를 화이트리스트로 정해 URI를 걸러 내는 방법을 권장합니다. 취약한 지갑은 예를 들어 로컬에서 돌아가는 redis 데이터베이스의 데이터를 바꾸도록 속을 수 있습니다.