ERC-2981: Creators of the Non-Fungible Token (NFT) Royalty Standard should be aware of

ERC-2981: Creators of the Non-Fungible Token (NFT) Royalty Standard should be aware of

The ERC-2981 standard outlines the process of recording royalty information on a blockchain.

Play this article

As an NFT Creator, royalties are an additional source of income apart from the primary sale of the asset. However, the method of implementing royalties varies from marketplace to marketplace, depending on the standard - some are on-chain, and some are off-chain. When an asset is traded in different marketplaces, we need to know that we can still receive royalties. That's where ERC-2981 comes in.

What is ERC-2981:

The Ethereum Improvement Proposal states that ERC-2981 is a way to get royalty payment information for non-fungible tokens. This will make it possible for all NFT marketplaces in the ecosystem to have universal support.

Understand that ERC-2981 does not mandate royalty payments to creators. Rather, it provides the necessary information. Marketplaces must use this information to ensure creators receive their royalties.

As creators of a Non-Fungible Token (NFT), make sure you include the one function specified in ERC-2981 in your collection contract.

function royaltyInfo(uint256 _tokenId,  uint256 _salePrice) 
  external view 
  returns (address receiver, uint256 royaltyAmount);

The ERC-2981 does not include features that would allow creators in a collection to split the royalties. If we require payment splitters, it should be a custom-implemented contract that manages the splitting of payments.

Implementing the ERC-2981:

If you're building your contracts with OpenZeppelin standards, you can use their ERC-2981 implementation as follows.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Royalty.sol";
import "@openzeppelin/contracts/utils/introspection/ERC165Storage.sol";

contract RoyalPets is ERC721, Ownable, 
    using Counters for Counters.Counter;
    using Address for address;
    using Strings for uint256;
    bytes4 private constant _INTERFACE_ID_ERC2981 = 0x2a55205a;

    Counters.Counter private _tokenIdCounter;

    constructor() ERC721("MyToken", "MTK") {

    function safeMint(address to) public onlyOwner {
        uint256 tokenId = _tokenIdCounter.current();
        _safeMint(to, tokenId);

    function tokenURI(uint256 tokenId)
        returns (string memory)
        string memory baseURI = baseTokenURI();
        return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : "";

      function baseTokenURI() public view returns (string memory) {
        return "";

        function supportsInterface(bytes4 interfaceId) public view virtual override(ERC721, 
        ERC165Storage) returns (bool) {
        return super.supportsInterface(interfaceId);

        function _burn(uint256 tokenId) internal virtual override(ERC721
        ) {

        function setDefaultRoyalty(address _receiver, uint96 _feeNumerator) public onlyOwner {

        _setDefaultRoyalty(_receiver, _feeNumerator);

    function royaltyInfo(uint256 _tokenId, uint256 _salePrice) public view virtual override returns (address, uint256) {
        return super.royaltyInfo(_tokenId,_salePrice);