ERC-2981 NFT royalty standard

Final · NFTs & multi-token · created 2020-09-15

A standard that lets any marketplace look up, in the same way, how much should be sent to the creator, and to which address, when an NFT is sold or resold.

Why it is needed

Each NFT marketplace handled royalties (the resale fee that goes to the creator) in a different way, so they were not compatible with each other. Even if a royalty is set on one marketplace, that information is not shared when the NFT is sold elsewhere, so payments stop. The original text describes this as a state like the early ERC-20 token days, where marketplace contracts differed across each ecosystem. Without an agreed standard, there is no effective means of collecting royalties on every marketplace, and artists and creators do not receive ongoing income. The original text holds that this hinders the growth and adoption of NFTs and discourages creators from making new tokens. This standard solves the problem by letting the royalty amount and the receiving address be looked up in the same way, no matter which marketplace the NFT is sold on.

What to implement

In depth

The royaltyInfo function and the ERC-165 identifier

A contract that implements this standard must have the function royaltyInfo(uint256 _tokenId, uint256 _salePrice). _tokenId is the NFT whose royalty is being looked up, and _salePrice is the sale price of that NFT. It returns two values: receiver, the address that receives the royalty, and royaltyAmount, the royalty amount for that sale price. The function is declared view and does not change state. The interface IERC2981 inherits IERC165, and its identifier is 0x2a55205a, which is bytes4(keccak256("royaltyInfo(uint256,uint256)")). The original example registers this identifier in the ERC-721 constructor with _registerInterface(_INTERFACE_ID_ERC2981). A marketplace checks for support with supportsInterface(_INTERFACE_ID_ERC2981). ERC-721 and ERC-1155 compatible contracts MAY implement this standard. It is optional, not an obligation.

Pay in the same currency unit as the sale price

The marketplace MUST pay the royalty in the same unit of exchange as the _salePrice passed to royaltyInfo(). In other words, the _salePrice parameter and the royaltyAmount return value must be expressed in the same currency unit. In the original example, if the sale price is in ETH, the royalty is paid in ETH, and if the sale price is in USDC, the royalty is paid in USDC. The royaltyInfo() function itself does not know which unit is used for the sale and the royalty. So the function must not perform any calculation that assumes a unit, and is structured to apply only a percentage to the number the marketplace supplies and return the result. This standard does not specify a particular currency or token used for sales or royalties.

Percentage calculation rule and rounding

When calculating the royalty amount, the implementer MUST calculate it as a percentage of _salePrice. The function MUST NOT return a fixed amount that ignores _salePrice, and MUST NOT decide the amount by comparing _salePrice with a constant. In both cases the function would end up assuming an exchange unit. The percentage value must be independent of the sale price. If the percentage is 10%, then 10% must apply whether _salePrice is 10, 10000, or 1234567890. If the calculation leaves a remainder, rounding either up or down to the nearest integer MAY be chosen. In the original example, with a 10% royalty and a _salePrice of 999, both 99 and 100 are valid as royaltyAmount.

Conditions under which the percentage may change

Multiple calls to royaltyInfo() MAY return different royaltyAmount values. An implementer may change the percentage according to a predictable variable that does not assume an exchange unit. One example is a percentage that decreases linearly over time. However, it SHOULD NOT rely on a hard-to-predict variable such as block.timestamp, and should use more predictable state changes. The original text gives as examples setting the percentage by the NFT's number of transfers so that it falls with each transfer, and using a different percentage for each _tokenId. The Rationale explains the situation in which block.timestamp becomes a problem. If a marketplace calls royaltyInfo() X days late after a sale, an amount Y that differs greatly from the no-delay case comes back, and the recipient could raise a dispute citing the delay.

The marketplace's payment obligation

A marketplace that supports this standard SHOULD implement a way to transfer the royalty to the recipient. The specific method of transfer and notification is left to a later EIP and is not included in this standard. A supporting marketplace MUST pay the royalty regardless of where or in what currency the sale took place. This covers not only on-chain sales but also off-chain sales such as over-the-counter (OTC) deals and auction houses. Because royalty payment is voluntary, this means that parties following this standard must treat sales outside the blockchain as sales and pay accordingly. If royaltyInfo() returns 0, the marketplace SHOULD NOT send a zero-amount transaction, because it only spends gas and serves no purpose.

Why optional payment, a single recipient, and returning an amount were chosen

It is not possible to know which transfer is a sale and which is a simple move between wallets. So royalty payment is not forced into transfer functions such as ERC-721's transferFrom(), and marketplaces are expected to implement it voluntarily. The payment method is also left unspecified, and only one receiving address is returned. Additional logic such as fee splitting, multiple recipients, taxes, and accounting must be implemented by the recipient in their own receiving contract or through off-chain processing. Putting this into the standard would greatly increase implementation complexity and gas cost, and it could not cover every case anyway. The original text sees this standard as a minimal, gas-efficient building block on which later innovation can be based. Returning the royaltyAmount amount instead of a percentage is meant to prevent disputes with marketplaces over how much is owed. The payer must pay the amount determined by royaltyInfo().

A general-purpose standard that also applies to non-NFT assets

This standard was designed with NFTs in mind, but it does not require the implementing contract to be compatible with ERC-721 or ERC-1155. Within the constraints of the interface, any other contract can return royalty information through this interface as long as it can uniquely identify an asset. The original text therefore describes ERC-2981 as a general-purpose royalty standard usable across many asset types. It is also compatible with the existing ERC-721 and ERC-1155 standards. Its status is Final, meaning it is a finalized standard.

Original ERC · ← ERCs