ChainScore Labs
LABS
Guides

Complying With Royalties in NFT-Fi Transactions

Chainscore © 2025
concepts

Core Concepts for Royalty Compliance

Understanding the foundational mechanisms and standards that govern how creator royalties are enforced and respected in NFT transactions across decentralized finance protocols.

01

On-Chain Royalty Enforcement

On-chain enforcement refers to royalty logic embedded directly in the NFT's smart contract. This is the most robust method.

  • Uses functions like royaltyInfo to mandate fee payments on secondary sales.
  • Protocols must read and execute this contract-level logic.
  • Provides strong guarantees but can be bypassed by non-compliant marketplaces or custom swaps.
02

Operator Filter Registries

An operator filter registry is a permissioned list that controls which marketplaces can trade an NFT collection. It's a key tool for creator-led enforcement.

  • Collections can block sales on non-royalty-paying marketplaces.
  • Implemented via standards like EIP-2981 with extensions.
  • Creates friction for users seeking to bypass fees, directing volume to compliant venues.
03

Royalty Standards (EIP-2981)

EIP-2981 is the primary Ethereum standard for communicating royalty information. It defines a universal interface for smart contracts.

  • Standardizes the royaltyInfo function to return recipient address and fee amount.
  • Enables wallets and marketplaces to display fees transparently.
  • Its adoption is critical for interoperability but does not, by itself, enforce payment.
04

Protocol-Level Enforcement

Protocol-level enforcement occurs when an NFT-Fi application (like a lending or fractionalization platform) programmatically respects royalties for all transactions on its platform.

  • The platform's smart contracts check for and forward royalties on any sale.
  • This creates a compliant ecosystem layer independent of individual marketplaces.
  • Essential for complex DeFi actions like NFT collateral liquidation.
05

Royalty Tokenization

Royalty tokenization involves separating the royalty cash flow from the NFT itself, creating a tradable financial derivative.

  • Allows creators to sell future royalty streams for upfront capital.
  • Introduces new compliance challenges for tracking and distributing payments.
  • Protocols must ensure tokenized royalty rights are honored in all subsequent transactions.
06

Fee Abstraction & Routing

Fee abstraction is the process where a protocol or marketplace handles royalty payment logistics on behalf of the user, simplifying the compliance process.

  • Automatically calculates, withholds, and routes fees to the designated creator wallet.
  • Can aggregate multiple royalty standards and custom contract logic.
  • Reduces user error and is a key feature of user-friendly, compliant platforms.

Royalty Implementation by Protocol Type

How On-Chain Royalties Work

On-chain enforcement refers to royalty logic that is hardcoded directly into the NFT's smart contract. This is the most robust method, as the royalty payment is an immutable part of the transfer logic. The most common standard is EIP-2981, which defines a royaltyInfo function that returns the recipient address and royalty amount for a given sale price.

Key Characteristics

  • Immutable Logic: Once deployed, the royalty rules cannot be altered without migrating the entire collection, providing strong creator guarantees.
  • Protocol Agnostic: Any marketplace or aggregator that queries the royaltyInfo function can respect the royalty, creating a universal standard.
  • Automatic Execution: The royalty is typically sent atomically as part of the token transfer transaction, reducing reliance on marketplace goodwill.

Example Implementation

Major collections like Art Blocks and many Yuga Labs assets implement EIP-2981. When a transfer occurs, the selling platform calls royaltyInfo(tokenId, salePrice) and must send the returned amount to the designated address to complete the trade. This creates a direct, contract-enforced revenue stream for creators.

Technical Implementation of EIP-2981

Process overview for integrating royalty payments into NFT smart contracts and marketplaces.

1

Implement the RoyaltyInfo Function in Your NFT Contract

Add the required interface and logic to your ERC-721 or ERC-1155 contract.

Detailed Instructions

First, import the IERC2981 interface and ensure your contract inherits from it. The core requirement is implementing the royaltyInfo function, which takes a tokenId and a salePrice as arguments and returns the recipient address and royalty amount. The royalty amount is calculated as a percentage of the sale price. Use a fixed denominator like 10000 for basis points to avoid floating-point math.

  • Sub-step 1: Declare state variables for the default royalty recipient and basis points (e.g., _royaltyRecipient, _royaltyBps).
  • Sub-step 2: Implement royaltyInfo(uint256 tokenId, uint256 salePrice) to return (_royaltyRecipient, salePrice * _royaltyBps / 10000).
  • Sub-step 3: Optionally, override this function for token-specific royalty settings using a mapping.
solidity
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; import "@openzeppelin/contracts/interfaces/IERC2981.sol"; contract RoyaltyNFT is ERC721, IERC2981 { address private _royaltyRecipient; uint16 private _royaltyBps; // e.g., 500 for 5% function royaltyInfo(uint256, uint256 salePrice) external view override returns (address, uint256) { return (_royaltyRecipient, (salePrice * _royaltyBps) / 10000); } }

Tip: Store the basis points as an integer (e.g., 500 for 5%) to maintain precision and gas efficiency during calculation.

2

Support the Interface in Marketplace Settlement Logic

Modify your marketplace or exchange contract to query and pay royalties on secondary sales.

Detailed Instructions

Your marketplace's settlement function must check if the NFT contract supports the IERC2981 interface using ERC-165's supportsInterface. If supported, call royaltyInfo to get the payment details. The royalty amount must be sent to the recipient, and the remaining salePrice goes to the seller. This logic should be executed before transferring the NFT to ensure payment.

  • Sub-step 1: In your executeSale function, call IERC165(nftContract).supportsInterface(type(IERC2981).interfaceId).
  • Sub-step 2: If true, call IERC2981(nftContract).royaltyInfo(tokenId, salePrice).
  • Sub-step 3: Deduct the returned royalty amount from the payment to the seller and transfer it to the royalty recipient using Address.sendValue or a safe transfer.
solidity
import "@openzeppelin/contracts/interfaces/IERC2981.sol"; import "@openzeppelin/contracts/utils/introspection/IERC165.sol"; function _transferNFTAndPayRoyalties(address nftContract, uint256 tokenId, address seller, uint256 salePrice) internal { if (IERC165(nftContract).supportsInterface(type(IERC2981).interfaceId)) { (address royaltyRecipient, uint256 royaltyAmount) = IERC2981(nftContract).royaltyInfo(tokenId, salePrice); payable(royaltyRecipient).transfer(royaltyAmount); payable(seller).transfer(salePrice - royaltyAmount); } else { payable(seller).transfer(salePrice); } IERC721(nftContract).safeTransferFrom(seller, msg.sender, tokenId); }

Tip: Always use secure transfer methods for ETH and tokens to prevent reentrancy and ensure funds are sent correctly.

3

Handle Royalty Payments in Token Transactions

Ensure royalty logic works with both ETH and ERC-20 token payments.

Detailed Instructions

Marketplaces often accept stablecoins like USDC or WETH. Your royalty payment logic must be currency-agnostic. When the sale currency is an ERC-20, you must transfer the royalty amount in that token. This requires approving the marketplace contract to spend tokens and using safeTransferFrom for the royalty portion. The royaltyInfo function returns a value in the sale's base unit (e.g., wei for ETH, 1e18 for most ERC-20s), so the calculation remains the same.

  • Sub-step 1: Determine the sale currency address (e.g., address(0) for ETH, or a token contract address).
  • Sub-step 2: Calculate the royalty amount using royaltyInfo with the salePrice in the correct base units.
  • Sub-step 3: If currency is an ERC-20, execute IERC20(currency).safeTransferFrom(buyer, royaltyRecipient, royaltyAmount).
  • Sub-step 4: Transfer the remaining balance to the seller in the same currency.
solidity
// Example for ERC-20 payment if (currency != address(0)) { IERC20 token = IERC20(currency); require(token.transferFrom(buyer, royaltyRecipient, royaltyAmount), "Royalty transfer failed"); require(token.transferFrom(buyer, seller, salePrice - royaltyAmount), "Seller payment failed"); } else { // Handle ETH payment as before }

Tip: Use OpenZeppelin's SafeERC20 library for safe token transfers, which handle non-standard ERC-20 implementations.

4

Test Royalty Enforcement with Foundry or Hardhat

Write comprehensive unit and integration tests for your implementation.

Detailed Instructions

Create tests that verify the royaltyInfo function returns correct values and that marketplace payments are split accurately. Test edge cases like zero royalties, maximum basis points (10000 for 100%), and sales with very high prices to check for overflow. Use a forked mainnet environment to test against live NFT contracts that implement EIP-2981, such as those from known collections.

  • Sub-step 1: Deploy a mock NFT contract implementing royaltyInfo with a 7.5% royalty (750 basis points).
  • Sub-step 2: Deploy your marketplace contract and list the NFT for a sale price of 1 ETH.
  • Sub-step 3: Execute a purchase and assert that 0.075 ETH is sent to the royalty recipient and 0.925 ETH to the seller.
  • Sub-step 4: Test the supportsInterface check by attempting a sale with a non-compliant NFT contract.
solidity
// Foundry test example function test_RoyaltyPayment() public { uint256 salePrice = 1 ether; uint256 expectedRoyalty = (salePrice * 750) / 10000; // 0.075 ether uint256 expectedSellerProceeds = salePrice - expectedRoyalty; marketplace.executeSale{value: salePrice}(address(nft), tokenId, seller); assertEq(royaltyRecipient.balance, expectedRoyalty); assertEq(seller.balance, expectedSellerProceeds); }

Tip: Include fuzz tests using vm.assume to test a wide range of sale prices and royalty percentages automatically.

5

Verify On-Chain Compliance and Gas Optimization

Audit gas costs and ensure your implementation adheres to the standard without unnecessary overhead.

Detailed Instructions

Profile the gas consumption of your royaltyInfo and marketplace settlement calls. The EIP-2981 standard is designed to be gas-efficient. Avoid storing royalty data in expensive storage for each token if a default is sufficient. Use immutable variables for fixed recipients and percentages where possible. Verify that your contract correctly reports interface support by returning true for bytes4(keccak256('royaltyInfo(uint256,uint256)')) ^ type(IERC2981).interfaceId.

  • Sub-step 1: Use Foundry's gasReport or Hardhat's gas reporter plugin to measure the cost of calling royaltyInfo.
  • Sub-step 2: Review storage slots; consider packing small royalty BPS values with other data to save gas.
  • Sub-step 3: Implement the supportsInterface function efficiently, often inherited from OpenZeppelin's ERC165.
  • Sub-step 4: Ensure fallback logic for non-compliant NFTs does not revert and simply bypasses royalty payments.
solidity
// Gas-efficient storage for default royalty struct RoyaltyInfo { address recipient; uint16 bps; // Max 10000, fits in 16 bits } RoyaltyInfo private _defaultRoyaltyInfo; // Packed into a single storage slot for efficiency

Tip: For contracts with many tokens, a token-level royalty override mapping can be expensive. Consider a compromise, like a merkle tree proof for rare custom royalties, to optimize for the common case.

Comparison of Royalty Standards and Enforcement Methods

Comparison of technical implementations, enforcement mechanisms, and market adoption for major NFT royalty standards.

FeatureEIP-2981 (Royalty Standard)Operator Filter RegistryCreator-Enforced Marketplaces

Standard Type

On-chain royalty info standard

Marketplace allow/deny list

Proprietary marketplace policy

Enforcement Layer

Smart contract logic (optional)

Registry contract at sale

Marketplace backend validation

Royalty Flexibility

Fixed or dynamic per token

Fixed percentage set by creator

Set per collection by platform

Gas Cost Impact

~21k gas for read

~45k gas for registry check

No on-chain cost for buyer

Adoption Examples

OpenSea, LooksRare, Manifold

OpenSea, Blur (initially)

X2Y2, Sudoswap (optional)

Bypass Vulnerability

Marketplace must implement

Can be bypassed by non-registry markets

Only enforceable on that platform

Creator Control

Info standard only, no enforcement

Can blacklist non-compliant markets

Direct platform policy negotiation

Common Challenges and Technical Solutions

Royalty enforcement on non-compliant platforms requires proactive technical strategies. The most common approach is to integrate a transfer validation hook within the NFT smart contract itself, using a pattern like OpenZeppelin's ERC721Votes or a custom beforeTokenTransfer function to block sales unless a royalty fee is paid to a designated address. Alternatively, projects can employ off-chain indexing and legal frameworks, tracking sales via subgraphs and issuing takedown notices. For example, a creator might configure their contract to levy a 7.5% fee on any transfer not originating from a whitelisted, compliant marketplace, enforcing the policy at the protocol level.