[Ethereum] Royalties for ERC 1155

erc-1155royalties

How do you include royalties in a normal ERC1155 contract?

Something like this:


// Import 1155 token contract from Openzeppelin

import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC1155/ERC1155.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/Ownable.sol";

contract NFTContract is ERC1155, Ownable {
    
    uint256 public constant ARTWORK = 0;
    uint256 public constant PHOTO = 1;
    
    constructor() ERC1155("https://wzh58eid3jnd.usemoralis.com/{id}.json") {
        _mint(msg.sender, ARTWORK, 1, "");
        _mint(msg.sender, PHOTO, 2, "");
    }
    function mint( address to, uint256 id, uint256 amount) public onlyOwner {
        _mint(to, id, amount, "");
    }
    function burn( address from, uint256 id, uint256 amount) public {
        require(msg.sender == from);
        _burn(from, id, amount);
    }
}``` 

Best Answer

Short Answer:

  • There is currently no single way to achieve this that has been widely adopted.
  • Add the royalty functionality of the platforms you think you will need (like OpenSea). Example provided below.

Long Answer

The NFT World is divided when it comes to Royalties.

As you can see here, OpenSea seems to do this off-chain by allowing you to include the royalty information in your metadata.

Rarible has a seperate smart contract called the Royalties Registry that is used to track the royalties of each NFT.

Various internet sources suggest to add custom code into your transferFrom() functions. However I would advise against this as mentioned in EIP-2981

It is impossible to know which NFT transfers are the result of sales, and which are merely wallets moving or consolidating their NFTs. Therefore, we cannot force every transfer function, such as transferFrom()

Thankfully, this lack of conformity across the NFT space will hopefully change now that EIP-2981: NFT Royalty Standard has recently been approved. It is still early days but some marketplaces like Mintable have already implemented this and there has been public support for OpenSea to do the same.

Given that EIP-2981 is not yet supported by the bigger marketplaces my advice to you would be to:

  1. Add the royalty functionality of the platforms you think you will need (like OpenSea)
  2. Be future proof by also implementing EIP-2981.

Here is an example of how you can support both EIP-2981 and OpenSea given the information you have provided. I have copied some code from this repo.

pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import '@openzeppelin/contracts/utils/introspection/ERC165.sol';


interface IERC2981Royalties {
    
    function royaltyInfo(uint256 _tokenId, uint256 _value)
        external
        view
        returns (address _receiver, uint256 _royaltyAmount);
}

/// @dev This is a contract used to add ERC2981 support to ERC721 and 1155
abstract contract ERC2981Base is ERC165, IERC2981Royalties {
    struct RoyaltyInfo {
        address recipient;
        uint24 amount;
    }

    /// @inheritdoc ERC165
    function supportsInterface(bytes4 interfaceId)
        public
        view
        virtual
        override
        returns (bool)
    {
        return
            interfaceId == type(IERC2981Royalties).interfaceId ||
            super.supportsInterface(interfaceId);
    }
}

contract NFTContract is ERC1155, Ownable, ERC2981Base {
    
    uint256 public constant ARTWORK = 0;
    uint256 public constant PHOTO = 1;
    RoyaltyInfo private _royalties;
    
    constructor() ERC1155("https://wzh58eid3jnd.usemoralis.com/{id}.json") {
        _setRoyalties({YOUR ADDRESS HERE} , {ROYALTY PERCENTAGE IN BASIS POINTS});
        _mint(msg.sender, ARTWORK, 1, "");
        _mint(msg.sender, PHOTO, 2, "");
    }
    
    // This is just for OpenSea to find your metadata containing the royalties. 
    // This metadata is about the contract and not the individual NFTs
    function contractURI() public view returns (string memory) {
        return "https://metadata-url.com/my-metadata";
    }
    
    function mint( address to, uint256 id, uint256 amount) public onlyOwner {
        _mint(to, id, amount, "");
    }
    function burn( address from, uint256 id, uint256 amount) public {
        require(msg.sender == from);
        _burn(from, id, amount);
    }
    
    
    // Value is in basis points so 10000 = 100% , 100 = 1% etc
    function _setRoyalties(address recipient, uint256 value) internal {
        require(value <= 10000, 'ERC2981Royalties: Too high');
        _royalties = RoyaltyInfo(recipient, uint24(value));
    }


    function royaltyInfo(uint256, uint256 value)
        external
        view
        override
        returns (address receiver, uint256 royaltyAmount)
    {
        RoyaltyInfo memory royalties = _royalties;
        receiver = royalties.recipient;
        royaltyAmount = (value * royalties.amount) / 10000;
    }
    
    function supportsInterface(bytes4 interfaceId) public view virtual override(ERC1155, ERC2981Base) returns (bool) {
        return super.supportsInterface(interfaceId);
    }
    
}

For the contract metadata for OpenSea it might look something like this:

{
  "name": "NFT Contract",
  "description": "Really cool description about my art",
  "image": "https://openseacreatures.io/image.png", # Link to collection image
  "external_link": "https://openseacreatures.io", # Link to website
  "seller_fee_basis_points": 100, # Indicates a 1% seller fee.
  "fee_recipient": "0xA97F337c39cccE66adfeCB2BF99C1DdC54C2D721" # Where seller fees will be paid to.
}
Related Topic