Guide to NFT Royalty Enforcement: Integrating Royalties Into Your Smart Contract

Ahmad W KhanAhmad W Khan
5 min read

1. Why NFT Royalties Matter

Royalties enable creators to benefit from their work long after the initial sale. But enforcement is not just a technical problem; it’s also about incentives, market adoption, and smart contract standards. the “code is law” idea has limits, and social norms + tooling matter as much as code.

2. NFT Royalty Enforcement

  • On-chain Royalties: Encoded into smart contracts (ERC-2981 standard).

  • Off-chain Enforcement: Marketplaces read royalty info, but may or may not pay.

  • Direct Transfers: Peer-to-peer (gift, OTC) usually don’t enforce royalties.

Goal: Maximize royalty receipt without crippling liquidity or user experience.

3. Standards, Limits & Marketplace Reality

ERC-721

  • “One NFT, one ID” (unique tokens).

  • Royalties via ERC-2981, with one global default or token-specific settings.

ERC-1155

  • “Semi-fungible”: multiple copies per ID.

  • Great for games, membership passes, tickets, PFP editions.

  • Supports batch transfers—royalty logic can be more complex.

ERC-2981 (Royalty Standard)

  • Function:

      function royaltyInfo(uint256 tokenId, uint256 salePrice) external view returns (address receiver, uint256 royaltyAmount);
    
  • Marketplaces call this to find out who gets paid and how much.

4. Royalty Implementation

Let’s build an ERC-1155 contract with advanced royalty support (using OpenZeppelin 4.x+).

A. Install OpenZeppelin

npm install @openzeppelin/contracts

B. Solidity Contract Example

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

import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol";
import "@openzeppelin/contracts/token/common/ERC2981.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract SeasonalsNFT1155 is ERC1155, ERC2981, Ownable {
    constructor(
        string memory uri_,
        address defaultRoyaltyReceiver,
        uint96 defaultRoyaltyFee // e.g., 500 = 5%
    ) ERC1155(uri_) {
        _setDefaultRoyalty(defaultRoyaltyReceiver, defaultRoyaltyFee);
    }

    // Set token-specific royalty (optional, overrides default)
    function setTokenRoyalty(
        uint256 tokenId,
        address receiver,
        uint96 feeNumerator
    ) external onlyOwner {
        _setTokenRoyalty(tokenId, receiver, feeNumerator);
    }

    // Remove royalty for a specific token
    function resetTokenRoyalty(uint256 tokenId) external onlyOwner {
        _resetTokenRoyalty(tokenId);
    }

    // Override required by Solidity for multiple inheritance
    function supportsInterface(bytes4 interfaceId)
        public
        view
        override(ERC1155, ERC2981)
        returns (bool)
    {
        return super.supportsInterface(interfaceId);
    }
}

Key Features:

  • Default royalty for all tokens.

  • Optional per-token royalty override.

  • Easily extensible for batch minting, pausing, access control, etc.

C. Deploy & Test

  • Deploy to your preferred EVM chain (Ethereum, Polygon, Base, etc.).

  • Mint tokens to test addresses.

  • List on OpenSea, Blur, Rarible, and see royalty info show up.

D. Adding Batch Minting

function mintBatch(address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data) external onlyOwner {
    _mintBatch(to, ids, amounts, data);
}

5. Royalties for ERC-721 vs ERC-1155

FeatureERC-721ERC-1155
Token TypeUniqueSemi-fungible/Batchable
Use Case1/1 Art, PFPsEditions, Gaming, Tickets
Royalty Per TokenYesYes
Batch TransfersNoYes
Default + Per-Token RoyaltiesYesYes

Note:
Some marketplaces treat ERC-1155 royalties differently, so always test every major platform before launch.

6. Royalty Splitters

Want to split royalties between multiple wallets or collaborators? Use 0xSplits or custom contract logic.

A. Using 0xSplits (No-Code)

  1. Set up a split at 0xSplits.

  2. Get the split contract address.

  3. Use this address as your royalty receiver in _setDefaultRoyalty.

B. Custom Solidity Splitter Example

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

contract SimpleSplitter {
    address public artist;
    address public dev;
    uint256 public artistShare = 70; // 70%
    uint256 public devShare = 30;    // 30%

    receive() external payable {
        uint256 artistAmount = msg.value * artistShare / 100;
        uint256 devAmount = msg.value - artistAmount;
        payable(artist).transfer(artistAmount);
        payable(dev).transfer(devAmount);
    }
}
  • Deploy, then set this contract’s address as the royalty receiver.

Pro tip: For full security and audit-readiness, use established tools like 0xSplits or OpenZeppelin’s PaymentSplitter.

7. Royalty Enforcement Edge Cases & Security Considerations

  • Direct (wallet-to-wallet) transfers bypass royalties.
    You cannot block or charge for a simple “gift” transfer unless you add custom logic—which limits liquidity.

  • Operator filtering is only partly effective.
    Blocklists (e.g., OpenSea’s) can be bypassed by new marketplaces.

  • Gas griefing:
    Complicated royalty logic can raise gas costs. Always keep functions efficient.

  • Marketplace Upgrades:
    Platforms may change how they detect/pay royalties—monitor for changes.

  • Immutable Royalties:
    You can make royalties unchangeable after deployment for extra trust (but you’ll lose flexibility).

8. Testing on OpenSea, Blur, Rarible, Manifold

A. OpenSea

  • Supports ERC-2981 (721 & 1155), but royalties may be optional in some collections.

  • Shows royalty receiver & amount on NFT page.

  • Can set up royalties via contract or OpenSea UI (be sure to match!).

B. Blur

  • Focused on traders. Royalties often optional.

  • If you want Blur to respect royalties, follow their docs and make sure ERC-2981 is present.

C. Rarible

  • Strong royalty support, with flexible splits.

  • Supports both contract-level and marketplace-level royalty configs.

D. Manifold

  • Lets you deploy custom contracts with UI-based royalty settings.

  • Highly recommended for creators who want more control.

Testing Steps:

  1. Deploy a contract (testnet/mainnet).

  2. Mint test NFTs.

  3. List on each platform.

  4. Simulate sales. check royalty calculation and distribution.

  5. Try direct transfers. Verify behavior.

9. Monitoring, Analytics, and Automation

Tools:

  • Dune Analytics: Query royalty payments across chains/collections.

  • Reservoir: APIs and dashboards for NFT/royalty data.

  • Nansen: Advanced analytics for NFT projects and payments.

  • Custom scripts:
    Use Ethers.js or Web3.py to watch for royalty payments to your wallet.

Example using Ethers.js (Node.js):

const { ethers } = require("ethers");
const provider = new ethers.providers.JsonRpcProvider(process.env.RPC_URL);
const royaltyReceiver = "0xYourRoyaltyWalletAddress";

provider.on("block", async () => {
    const balance = await provider.getBalance(royaltyReceiver);
    console.log("Royalty receiver balance:", ethers.utils.formatEther(balance));
});

use event filters to track new sales in real-time!

10. What’s Next for Royalty Enforcement?

  • Protocol-Level Enforcement:
    L2s like Immutable X and new L1s may make on-chain enforcement the default.

  • Encrypted/Stealth Transfers:
    ZK-tech could make off-market transfers invisible, challenge for royalty collection.

  • NFT Utility Models:
    Royalties as part of a broader community, access, or revenue share model.

  • DAOs for Royalty Distribution:
    Automated, on-chain DAOs splitting royalties across hundreds of contributors.

11. FAQ

Q: How do I prevent “wash trading” for royalty farming?
A: Watch for repeated sales between the same addresses at low cost, use analytics to flag suspicious patterns. No foolproof on-chain solution yet.

Q: Can I freeze or change royalties after launch?
A: If your contract is “Ownable,” you can change royalties unless you renounce ownership or make the logic immutable. Many advanced projects do this for trust.

Q: Can I set different royalties for each NFT or edition?
A: Yes, via ERC-2981’s per-token logic. See setTokenRoyalty example above.

Q: How can I distribute royalties to hundreds of addresses?
A: Use splitter contracts (like 0xSplits, PaymentSplitter, or a DAO vault).

0
Subscribe to my newsletter

Read articles from Ahmad W Khan directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Ahmad W Khan
Ahmad W Khan