Creating a Smart Contract with NFT Royalties

Posted By : Suchit

Apr 24, 2023

An NFT (Non-Fungible Token) is a unique digital asset that represents ownership or proof of authenticity of a digital item. NFTs are created on blockchain networks and are often used to represent digital art, collectibles, music, videos, and other digital assets.

 

ERC721 is a widely adopted Ethereum token standard for creating non-fungible tokens (NFTs) on the blockchain. Unlike fungible tokens such as cryptocurrencies that are interchangeable with one another, NFTs are unique and represent ownership of a specific asset or piece of digital content.

 

ERC721 defines a set of rules and interfaces that ensure interoperability between different NFTs on the Ethereum blockchain. It specifies a set of required functions that a smart contract must implement to be considered ERC721-compliant. These functions include minting new tokens, transferring ownership of tokens, and providing metadata about each token.

You can use the Openzeppelin library to create ERC721 contracts. OpenZeppelin is an open-source platform for developing smart contracts and decentralized applications (dApps) on the Ethereum blockchain. It offers a set of audited and secure smart contract libraries that developers can use to create new contracts or add functionality to existing ones.

 

OpenZeppelin libraries include the most commonly used Solidity smart contracts, such as ERC20 and ERC721 token standards, as well as additional libraries for access control, payments, and security. The code is community-driven, audited, and frequently updated to address any potential security vulnerabilities. Openzeppelin also provides optional extension interfaces that allow for additional functionality, such as metadata (ERC721Metadata) and token URI storage (ERC721URIStorage). The ERC2981 interface provides a standardized way to implement royalties for NFTs.

 

ERC2981 is an Ethereum standard that extends the ERC721 non-fungible token (NFT) standard by allowing the creator or owner of an NFT to earn royalties every time it is sold on the secondary market. This means that the creator or owner can continue to benefit from the success of their NFT even after it has been sold to another user.

 

The ERC-2981 standard only specifies a way to signal royalty information for NFTs, and it does not enforce or facilitate royalty payments. It is up to the involved parties to agree on the terms of the royalty payment and ensure that it is paid accordingly.

 

You may also like to explore | A Quick Guide to Ethereum ERC Token Standards

 

Following is an implementation of the ERC2981 interface, which is a standard for retrieving royalty payment information for non-fungible tokens (NFTs). The contract defines a set of functions for managing and retrieving royalty information for tokens.

 

Creating an NFT Smart Contract for Royalty Solution

 

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
 
import "@openzeppelin/contracts/token/interfaces/IERC2981.sol";
import "@openzeppelin/contracts/token/utils/introspection/ERC165.sol";
 
abstract contract ERC2981 is IERC2981, ERC165 {
    struct RoyaltyInfo {
        address receiver;
        uint96 royaltyFraction;
    }
 
    RoyaltyInfo private _defaultRoyaltyInfo;
    mapping(uint256 => RoyaltyInfo) private _tokenRoyaltyInfo;
 
    
    function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC165) returns (bool) {
        return interfaceId == type(IERC2981).interfaceId || super.supportsInterface(interfaceId);
    }
 
    function royaltyInfo(uint256 tokenId, uint256 salePrice) public view virtual override returns (address, uint256) {
        RoyaltyInfo memory royalty = _tokenRoyaltyInfo[tokenId];
 
        if (royalty.receiver == address(0)) {
            royalty = _defaultRoyaltyInfo;
        }
 
        uint256 royaltyAmount = (salePrice * royalty.royaltyFraction) / _feeDenominator();
 
        return (royalty.receiver, royaltyAmount);
    }
 
    function _feeDenominator() internal pure virtual returns (uint96) {
        return 10000;
    }
 
    function _setDefaultRoyalty(address receiver, uint96 feeNumerator) internal virtual {
        require(feeNumerator <= _feeDenominator(), "ERC2981: royalty fee will exceed salePrice");
        require(receiver != address(0), "ERC2981: invalid receiver");
 
        _defaultRoyaltyInfo = RoyaltyInfo(receiver, feeNumerator);
    }
 
    function _deleteDefaultRoyalty() internal virtual {
        delete _defaultRoyaltyInfo;
    }
 
    function _setTokenRoyalty(uint256 tokenId, address receiver, uint96 feeNumerator) internal virtual {
        require(feeNumerator <= _feeDenominator(), "ERC2981: royalty fee will exceed salePrice");
        require(receiver != address(0), "ERC2981: Invalid parameters");
 
        _tokenRoyaltyInfo[tokenId] = RoyaltyInfo(receiver, feeNumerator);
    }
 
    function _resetTokenRoyalty(uint256 tokenId) internal virtual {
        delete _tokenRoyaltyInfo[tokenId];
    }
}

 

The contract has a struct named RoyaltyInfo, which contains information about the recipient of the royalty and the fraction of the sale price that the royalty represents. The contract also has two variables: _defaultRoyaltyInfo, which represents the default royalty information for all tokens, and _tokenRoyaltyInfo, which is a mapping of token IDs to their specific royalty information.

 

The contract implements the IERC2981 interface, which requires the implementation of a royaltyInfo function that returns the royalty information for a given token ID and sale price. The implementation first checks if there is specific royalty information for the given token ID and if not, it uses the default royalty information. It then calculates the royalty amount based on the sale price and the royalty fraction and returns the royalty recipient and amount.

 

The contract also has four internal functions: _feeDenominator, _setDefaultRoyalty, _deleteDefaultRoyalty, and _setTokenRoyalty. _feeDenominator returns the denominator with which to interpret the royalty fraction as a fraction of the sale price. By default, it returns 10000, meaning the royalty fee is specified in basis points. _setDefaultRoyalty sets the default royalty information for all tokens. _deleteDefaultRoyalty removes the default royalty information. _setTokenRoyalty sets the royalty information for a specific token ID, overriding the global default.

 

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
 
import "@openzeppelin/contracts/token/common/ERC2981.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
 
contract NFT is ERC721URIStorage, ERC2981, Ownable {
  using Counters for Counters.Counter;
  Counters.Counter private _tokenIds;
 
  constructor(string memory _name, string memory _symbol) ERC721(_name, _symbol) {
    _setDefaultRoyalty(msg.sender, 100);
  }
 
  function _burn(uint256 tokenId) internal virtual override {
    super._burn(tokenId);
    _resetTokenRoyalty(tokenId);
  }
 
  function burn(uint256 tokenId)
    public onlyOwner {
      _burn(tokenId);
  }
 
  function mint(address recipient, string memory tokenURI)
    public onlyOwner
    returns (uint256) {
      _tokenIds.increment();
 
      uint256 newItemId = _tokenIds.current();
      _safeMint(recipient, newItemId);
      _setTokenURI(newItemId, tokenURI);
 
      return newItemId;
  }
 
  function mintWithRoyalty(address recipient, string memory tokenURI, address royaltyReceiver, uint96 roayltyFee)
    public onlyOwner
    returns (uint256) {
      uint256 tokenId = mint(recipient, tokenURI);
      _setTokenRoyalty(tokenId, royaltyReceiver, roayltyFee);
 
      return tokenId;
  }
 
  function supportsInterface(bytes4 interfaceId) public view virtual override(ERC721, ERC2981)
    returns (bool) {
      return super.supportsInterface(interfaceId);
  }
}

 

The above contract is a Solidity smart contract that implements a non-fungible token (NFT) using the ERC721 standard with the additional functionality of ERC2981, which is a standard for royalty payments on NFTs.

 

The constructor sets the name and symbol of the NFT using the ERC721 constructor and then calls the _setDefaultRoyalty function, which sets the default royalty receiver and fee for the NFT contract.

 

The contract defines four functions:

 

_burn: This is an internal function that overrides the _burn function in ERC721URIStorage to add the functionality of resetting the royalty for the token being burned.

 

burnNFT: This function allows the contract owner to burn (delete) an NFT from the contract.

 

mintNFT: This function allows the contract owner to mint (create) a new NFT and assign it to a specified recipient address. It uses the _safeMint function provided by ERC721URIStorage to create a new token ID and assign it to the recipient and then set the URI for the token.

 

mintWithRoyalty: This function allows the contract owner to mint a new NFT with royalty functionality. It calls the mintNFT function to create the token and then sets the royalty receiver and fee for the token using the _setTokenRoyalty function provided by ERC2981.

 

You might be interested in |  Types of NFTs (Non-Fungible Tokens) You Didn't Know

 

Finally, the contract overrides the supportsInterface function from ERC721URIStorage and ERC2981 to return true if the specified interface is supported by the contract, and false otherwise.

 

If you already have a project in mind and want to know how to get started with it, you may connect with our skilled NFT developers

Leave a

Comment

Name is required

Invalid Name

Comment is required

Recaptcha is required.

blog-detail

November 21, 2024 at 12:28 pm

Your comment is awaiting moderation.

By using this site, you allow our use of cookies. For more information on the cookies we use and how to delete or block them, please read our cookie notice.

Chat with Us
Telegram Button
Youtube Button

Contact Us

Oodles | Blockchain Development Company

Name is required

Please enter a valid Name

Please enter a valid Phone Number

Please remove URL from text