ERC-2981 NFT 로열티 표준 (NFT Royalty Standard)
Final · NFT·멀티토큰 · 2020-09-15 작성
NFT가 팔리거나 다시 팔릴 때 창작자에게 얼마를 어느 주소로 보내야 하는지 어떤 마켓플레이스든 같은 방법으로 조회할 수 있게 하는 표준입니다.
왜 필요한가
NFT 마켓플레이스마다 로열티(royalty, 창작자에게 돌아가는 재판매 수수료)를 처리하는 방식이 달라서 서로 호환되지 않았습니다. 한 마켓플레이스에서 로열티를 설정해도 다른 곳에서 팔리면 그 정보가 공유되지 않아 지급이 끊깁니다. 원문은 이를 초기 ERC-20 토큰 시절처럼 마켓플레이스 컨트랙트가 생태계마다 제각각인 상태라고 설명합니다. 합의된 표준이 없으면 모든 마켓플레이스에서 로열티를 걷을 효과적인 수단이 없고 예술가와 창작자는 계속되는 수입을 얻지 못합니다. 원문은 이것이 NFT의 성장과 채택을 막고 창작자가 새로운 토큰을 만들 동기를 꺾는다고 봅니다. 이 표준은 어느 마켓플레이스에서 팔리든 같은 방법으로 로열티 금액과 받을 주소를 조회할 수 있게 해서 이 문제를 풉니다.
구현해야 하는 것
function supportsInterface(bytes4 interfaceID) external view returns (bool);: 컨트랙트가 주어진 인터페이스를 구현했는지 알려 줍니다. 이 표준의 식별자 0x2a55205a를 넣어 로열티 지원 여부를 확인합니다.
심층 설명
royaltyInfo 함수와 ERC-165 식별자
구현하는 컨트랙트에는 royaltyInfo(uint256 _tokenId, uint256 _salePrice) 함수가 반드시 있어야 합니다. _tokenId는 로열티를 조회할 NFT, _salePrice는 그 NFT의 판매 가격입니다. 반환값은 로열티를 받을 주소 receiver와 그 판매 가격에 대한 로열티 금액 royaltyAmount 두 개입니다. 함수는 view로 선언되어 상태를 바꾸지 않습니다. 인터페이스 IERC2981은 IERC165를 상속하며 식별자는 bytes4(keccak256("royaltyInfo(uint256,uint256)"))인 0x2a55205a입니다. 원문 예시는 ERC-721 생성자에서 _registerInterface(_INTERFACE_ID_ERC2981)로 이 식별자를 등록합니다. 마켓플레이스는 supportsInterface(_INTERFACE_ID_ERC2981)로 지원 여부를 확인합니다. ERC-721과 ERC-1155 호환 컨트랙트는 이 표준을 구현할 수 있습니다(MAY). 의무가 아닌 선택 사항입니다.
판매 가격과 같은 화폐 단위로 지급
마켓플레이스는 royaltyInfo()에 넘긴 _salePrice와 같은 교환 단위로 로열티를 지급해야 합니다(MUST). 다시 말해 _salePrice 매개변수와 royaltyAmount 반환값은 같은 화폐 단위로 표시되어야 합니다. 원문 예시로 판매 가격이 ETH면 로열티도 ETH로, 판매 가격이 USDC면 로열티도 USDC로 지급합니다. royaltyInfo() 함수 자체는 판매와 로열티에 어떤 단위가 쓰이는지 알지 못합니다. 그래서 함수는 단위를 가정하는 어떤 계산도 하면 안 되고 마켓플레이스가 넣은 숫자에 비율만 적용해 돌려주는 구조입니다. 이 표준은 판매나 로열티에 쓰는 특정 화폐나 토큰을 정하지 않습니다.
비율 계산 규칙과 반올림
구현자는 로열티 금액을 계산할 때 _salePrice의 비율(percentage)로 계산해야 합니다(MUST). _salePrice를 무시한 고정 금액을 돌려주면 안 되고(MUST NOT), _salePrice를 상수와 비교해 금액을 정해서도 안 됩니다(MUST NOT). 두 경우 모두 함수가 교환 단위를 가정하게 되기 때문입니다. 비율 값은 판매 가격과 무관해야 합니다. 비율이 10%라면 _salePrice가 10이든 10000이든 1234567890이든 10%가 적용되어야 합니다. 계산에 나머지가 생기면 가까운 정수로 올림이나 내림 중 어느 쪽을 택해도 됩니다(MAY). 원문 예시로 로열티 10%에 _salePrice가 999이면 royaltyAmount로 99와 100 둘 다 유효합니다.
비율을 바꿀 수 있는 조건
royaltyInfo()를 여러 번 호출하면 서로 다른 royaltyAmount가 돌아올 수 있습니다(MAY). 구현자는 교환 단위를 가정하지 않는 예측 가능한 변수에 따라 비율을 바꿀 수 있습니다. 예로 비율이 시간에 따라 선형으로 줄어드는 방식이 있습니다. 다만 block.timestamp처럼 예측하기 어려운 변수에 기대면 안 되고(SHOULD NOT), 더 예측 가능한 상태 변화를 써야 합니다. 원문은 NFT의 전송 횟수에 따라 비율을 정해 전송마다 비율이 낮아지는 방식과 _tokenId별로 다른 비율을 쓰는 방식을 예로 듭니다. Rationale은 block.timestamp가 문제가 되는 상황을 설명합니다. 마켓플레이스가 판매 뒤 X일 늦게 royaltyInfo()를 호출하면 지연이 없었을 때와 크게 다른 금액 Y가 나오고 수령자가 지연을 이유로 분쟁을 제기할 수 있습니다.
마켓플레이스의 지급 의무
이 표준을 지원하는 마켓플레이스는 로열티를 수령자에게 전송하는 방법을 구현해야 합니다(SHOULD). 전송과 알림의 구체적인 방식은 이후 EIP에서 정하기로 하고 이 표준에는 담지 않았습니다. 지원하는 마켓플레이스는 판매가 어디서 어떤 화폐로 이루어졌든 로열티를 지급해야 합니다(MUST). 온체인 판매뿐 아니라 장외거래(OTC)와 경매장 같은 오프체인 판매도 포함합니다. 로열티 지급은 자발적이므로, 이 표준을 따르는 주체는 블록체인 밖의 판매도 판매로 보고 지급해야 한다는 뜻입니다. royaltyInfo()가 0을 돌려주면 0 금액 거래를 보내지 않아야 합니다(SHOULD NOT). 가스만 쓰고 쓸모가 없기 때문입니다.
선택적 지급, 단일 수령자, 금액 반환을 택한 이유
어떤 전송이 판매이고 어떤 전송이 지갑 사이의 단순 이동인지 알 수 없습니다. 그래서 ERC-721의 transferFrom() 같은 전송 함수에 로열티 지급을 강제하지 않고 마켓플레이스가 자발적으로 구현할 것으로 기대합니다. 지급 방식도 정하지 않고 수령 주소는 하나만 돌려줍니다. 수수료 분배, 여러 수령자, 세금, 회계 같은 추가 로직은 수령자가 자기 수신 컨트랙트나 오프체인 처리로 구현해야 합니다. 이를 표준에 넣으면 구현 복잡도와 가스 비용이 크게 늘고 모든 경우를 다룰 수도 없기 때문입니다. 원문은 이 표준을 이후 혁신의 바탕이 되는 최소한의 가스 효율적인 구성 요소로 봅니다. 비율 대신 royaltyAmount 금액을 돌려주는 것은 얼마를 내야 하는지를 두고 마켓플레이스와 분쟁이 생기지 않게 하기 위해서입니다. 지급자는 royaltyInfo()가 정한 금액을 내야 합니다.
NFT 외 자산에도 쓰는 범용 표준
이 표준은 NFT를 염두에 두고 설계했지만 구현 컨트랙트가 ERC-721이나 ERC-1155와 호환될 것을 요구하지 않습니다. 인터페이스의 제약 안에서 자산을 고유하게 식별할 수 있다면 다른 어떤 컨트랙트도 이 인터페이스로 로열티 정보를 돌려줄 수 있습니다. 원문은 그래서 ERC-2981을 여러 자산 유형에 쓸 수 있는 범용 로열티 표준이라고 설명합니다. 기존 ERC-721, ERC-1155 표준과도 호환됩니다. 상태는 Final로 확정된 표준입니다.