A Guide to Implementing NFT Royalties on ERC-721 & ERC-1155

Posted By : Tushar

Aug 30, 2024

NFT royalties are payments made to the original creators each time their digital assets are resold on the secondary market. These payments are automated through smart contracts embedded in blockchain networks using NFT development services , ensuring that creators continue to earn from their work long after the initial sale.

 

How NFT Royalties Work

 

Creators can set their desired royalty percentage during the minting process of the NFT. When a secondary sale occurs, the smart contract automatically allocates the specified percentage of the transaction as a royalty payment to the creator.

 

Example

 

One notable example is Beeple's "Crossroads" NFT, which was resold for approximately $6.6 million on the secondary market. Beeple received 10% of the resale value as a royalty payment, demonstrating how NFT royalties provide creators with an ongoing revenue stream.

 

You may also like | How to Get the Transaction History of an NFT

 

The Need for NFT Royalties

 

Despite the visibility gained through social media, artists and creators often struggle to receive fair compensation for their work. Traditional art and content sales typically result in one-time payments, with creators losing control over secondary sales. NFT royalties offer a revolutionary solution by ensuring that creators continue to benefit financially from the resale of their work, providing a sustainable income model and restoring control over how their creations are monetized.

 

Also, explore | How to Create a Compressed NFT on Solana

 

Ways To Implement Royalties

 

To implement the functionality of royalties in smart contracts, we mainly work with two types of smart contracts:

 

  • ERC-721 (Non-Fungible Token) Contracts
  • ERC-1155 (Multi-Token Standard) Contracts.

 

Description of Smart Contracts

 

ERC-721(NFT) Contract

 

In an ERC-721, every NFT is unique, meaning each NFT must reference its specific content. The ERC-721 standard provides a set of functions that developers can integrate into their smart contracts to create, transfer, and manage NFTs. These functions allow for the creation of unique tokens, each with distinct metadata, making them individually identifiable.

 

Internally, ERC-721 smart contracts maintain a ledger of token ownership, manage transfers between users, and track the total token supply along with the balance of tokens for each address. A well-known example of an application using the ERC-721 standard is CryptoKitties, a blockchain-based game that allows users to buy, sell, and breed virtual assets. Other examples include Ethermon, MyCrypto, and Cryptodoggies.

 

Explore more | A Detailed Guide to NFT Minting on Solana using Metaplex API

 

Here's a sample contract, demonstrating how to integrate royalties functionality to an erc-721 smart contract. 

 

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IERC721 {
   function balanceOf(address owner) external view returns (uint256 balance);
   function ownerOf(uint256 tokenId) external view returns (address owner);
   function safeTransferFrom(address from, address to, uint256 tokenId) external;
   function transferFrom(address from, address to, uint256 tokenId) external;
   function approve(address to, uint256 tokenId) external;
   function getApproved(uint256 tokenId) external view returns (address operator);
   function setApprovalForAll(address operator, bool _approved) external;
   function isApprovedForAll(address owner, address operator) external view returns (bool);
}
interface IERC721Receiver {
   function onERC721Received(address operator, address from, uint256 tokenId, bytes calldata data) external returns (bytes4);
}
contract ERC721WithRoyalties is IERC721 {
   string private _name;
   string private _symbol;
   mapping(uint256 => address) private _owners;
   mapping(address => uint256) private _balances;
   mapping(uint256 => address) private _tokenApprovals;
   mapping(address => mapping(address => bool)) private _operatorApprovals;
   // Royalties mapping: tokenId => (royaltyRecipient, royaltyPercentage)
   mapping(uint256 => address) private _royaltyRecipients;
   mapping(uint256 => uint256) private _royaltyPercentages;
   // Mapping to store token URIs
   mapping(uint256 => string) private _tokenURIs;
   constructor(string memory name_, string memory symbol_) {
       _name = name_;
       _symbol = symbol_;
   }
   // ERC721 Metadata
   function name() public view returns (string memory) {
       return _name;
   }
   function symbol() public view returns (string memory) {
       return _symbol;
   }
   function tokenURI(uint256 tokenId) public view returns (string memory) {
       require(_exists(tokenId), "ERC721: URI query for nonexistent token");
       return _tokenURIs[tokenId];
   }
   // Function to set token URI
   function _setTokenURI(uint256 tokenId, string memory uri) internal {
       require(_exists(tokenId), "ERC721: URI set of nonexistent token");
       _tokenURIs[tokenId] = uri;
   }
   // ERC721 Functions
   function balanceOf(address owner) public view override returns (uint256) {
       require(owner != address(0), "ERC721: balance query for the zero address");
       return _balances[owner];
   }
   function ownerOf(uint256 tokenId) public view override returns (address) {
       address owner = _owners[tokenId];
       require(owner != address(0), "ERC721: owner query for nonexistent token");
       return owner;
   }
   function approve(address to, uint256 tokenId) public override {
       address owner = ownerOf(tokenId);
       require(to != owner, "ERC721: approval to current owner");
       require(msg.sender == owner || isApprovedForAll(owner, msg.sender), "ERC721: approve caller is not owner nor approved for all");
       _tokenApprovals[tokenId] = to;
       emit Approval(owner, to, tokenId);
   }
   function getApproved(uint256 tokenId) public view override returns (address) {
       require(_exists(tokenId), "ERC721: approved query for nonexistent token");
       return _tokenApprovals[tokenId];
   }
   function setApprovalForAll(address operator, bool approved) public override {
       _operatorApprovals[msg.sender][operator] = approved;
       emit ApprovalForAll(msg.sender, operator, approved);
   }
   function isApprovedForAll(address owner, address operator) public view override returns (bool) {
       return _operatorApprovals[owner][operator];
   }
   function transferFrom(address from, address to, uint256 tokenId) public override {
       require(_isApprovedOrOwner(msg.sender, tokenId), "ERC721: transfer caller is not owner nor approved");
       _transfer(from, to, tokenId);
   }
   function safeTransferFrom(address from, address to, uint256 tokenId) public override {
       safeTransferFrom(from, to, tokenId, "");
   }
   function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory _data) public  {
       require(_isApprovedOrOwner(msg.sender, tokenId), "ERC721: transfer caller is not owner nor approved");
       _transfer(from, to, tokenId);
       require(_checkOnERC721Received(from, to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer");
   }
   // Internal Functions
   function _exists(uint256 tokenId) internal view returns (bool) {
       return _owners[tokenId] != address(0);
   }
   function _isApprovedOrOwner(address spender, uint256 tokenId) internal view returns (bool) {
       require(_exists(tokenId), "ERC721: operator query for nonexistent token");
       address owner = ownerOf(tokenId);
       return (spender == owner || getApproved(tokenId) == spender || isApprovedForAll(owner, spender));
   }
   function _safeMint(address to, uint256 tokenId, string memory uri) internal {
       _mint(to, tokenId);
       _setTokenURI(tokenId, uri); // Set the token URI on mint
       require(_checkOnERC721Received(address(0), to, tokenId, ""), "ERC721: transfer to non ERC721Receiver implementer");
   }
   function _mint(address to, uint256 tokenId) internal {
       require(to != address(0), "ERC721: mint to the zero address");
       require(!_exists(tokenId), "ERC721: token already minted");
       _balances[to] += 1;
       _owners[tokenId] = to;
       emit Transfer(address(0), to, tokenId);
   }
   function _transfer(address from, address to, uint256 tokenId) internal {
       require(ownerOf(tokenId) == from, "ERC721: transfer of token that is not own");
       require(to != address(0), "ERC721: transfer to the zero address");
       _approve(address(0), tokenId);
       _balances[from] -= 1;
       _balances[to] += 1;
       _owners[tokenId] = to;
       emit Transfer(from, to, tokenId);
   }
   function _approve(address to, uint256 tokenId) internal {
       _tokenApprovals[tokenId] = to;
       emit Approval(ownerOf(tokenId), to, tokenId);
   }
   function _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory _data) private returns (bool) {
       if (to.code.length > 0) {
           try IERC721Receiver(to).onERC721Received(msg.sender, from, tokenId, _data) returns (bytes4 retval) {
               return retval == IERC721Receiver(to).onERC721Received.selector;
           } catch {
               return false;
           }
       } else {
           return true;
       }
   }
   // Royalty Functions
   function setRoyaltyInfo(uint256 tokenId, address recipient, uint256 percentage) external {
       require(msg.sender == ownerOf(tokenId), "ERC721: caller is not owner of the token");
       require(percentage <= 10000, "ERC721: royalty percentage too high"); // Max 100%
       _royaltyRecipients[tokenId] = recipient;
       _royaltyPercentages[tokenId] = percentage;
   }
   function getRoyaltyInfo(uint256 tokenId) external view returns (address recipient, uint256 percentage) {
       require(_exists(tokenId), "ERC721: querying royalty info for nonexistent token");
       return (_royaltyRecipients[tokenId], _royaltyPercentages[tokenId]);
   }
   function _calculateRoyalty(uint256 salePrice, uint256 tokenId) internal view returns (uint256) {
       uint256 percentage = _royaltyPercentages[tokenId];
       return salePrice * percentage / 10000; // Royalty percentage is out of 10000
   }
   // To be used in conjunction with marketplace integrations
   function payRoyalty(uint256 tokenId, uint256 salePrice) external payable {
       uint256 royaltyAmount = _calculateRoyalty(salePrice, tokenId);
       require(msg.value == royaltyAmount, "ERC721: incorrect royalty payment");
       address recipient = _royaltyRecipients[tokenId];
       require(recipient != address(0), "ERC721: no recipient set for royalties");
       payable(recipient).transfer(msg.value);
   }
   // Events
   event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
   event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
   event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
}

 

 

ERC-1155 (NFT) Contract

 

ERC-1155 is a versatile token standard on the Ethereum blockchain that enables the creation and transfer of both fungible and non-fungible tokens within a single transaction. Combining the features of earlier standards like ERC-20 and ERC-721, it enhances efficiency and significantly reduces costs. This standard supports an infinite variety of tokens, including semi-fungible ones, and offers secure transfer mechanisms without the need to approve each token contract individually.

 

ERC-1155 allows multiple assets to be managed within a single smart contract, reducing transaction costs and streamlining operations. It enables the transfer of multiple items to one or more recipients in a single transaction. For example, in blockchain games, ERC-1155 simplifies management by integrating various components"”such as shields, swords, and in-game currency"”into a single smart contract, eliminating the need for multiple contracts for each asset.

 

Also, check | A Step by Step Tutorial of Building a Cross Chain NFT Bridge

 

Importance of ERC-1155 Token

 

Prior to ERC-1155, creating a use case that involved both ERC-20 (fungible) and ERC-721 (non-fungible) tokens required separate contracts for each type. Additionally, ERC-1155 allows for the management of multiple NFT collections within a single smart contract, eliminating the need to create a separate contract for each collection. This consolidation reduces the number of transactions, which is crucial for saving blockchain space and enhancing the efficiency of smart contract deployment.


Example ERC-1155 Token

 

Consider an online event ticketing system that uses ERC-1155 tokens to manage access to various events. In this system, different token IDs within the smart contract represent various ticket categories, such as general admission, VIP passes, and early bird specials. When users purchase tickets, they receive ERC-1155 tokens corresponding to their chosen ticket type. These tokens are stored in their digital wallets and can be presented at the event for admission. To validate entry, event organizers simply scan the token's QR code.

 

Also, discover | How to Create a Rentable NFTs


Difference Between ERC721 and ERC1155 Smart Contract  

 

ERC-721 was the first major NFT standard, created in 2017 by the Ethereum development studio ConsenSys, and remains the most popular protocol for NFTs. ERC-1155, a newer standard introduced in 2018 by Enjin, a gaming company specializing in blockchain technology, brought additional flexibility.

 

ERC-721 tokens are unique and strictly non-fungible, meaning each token is distinct and cannot be interchanged with another. In contrast, ERC-1155 tokens can be fungible, non-fungible, or even semi-fungible. This versatility allows ERC-1155 tokens to represent both collectibles and traditional assets like currencies or commodities.

 

While both ERC-721 and ERC-1155 are valid standards for NFTs, the key difference is that ERC-721 tokens are always non-fungible, whereas ERC-1155 tokens can be either fungible or non-fungible. ERC-721 is typically preferred for collectibles, while ERC-1155 is favored for use cases involving traditional assets and gaming items due to its efficiency and versatility.

 

If you are looking to hire NFT developers, explore our large talent pool, comprising skilled full-stack developers.  

Leave a

Comment

Name is required

Invalid Name

Comment is required

Recaptcha is required.

blog-detail

October 16, 2024 at 09:17 am

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