NFT Royalty Fees: Everything You Need To Know (Edge-cases… | Hedera Hedera Network Services Token Service Mint and configure tokens and accounts. Consensus Service Verifiable timestamps and ordering of events. Smart Contracts Run Solidity smart contracts. HBAR The Hedera network's native cryptocurrency. Insights How It Works Learn about Hedera from end to end. Explorers View live and historical data on Hedera. Dashboards Analyze network activity and metrics. Network Nodes Understand networks and node types. Devs Start Building Get Started Learn core concepts and build the future. Documentation Review the API and build using your favorite language. Developer Resources Integrations Plugins and microservices for Hedera. Fee Estimator Understand and estimate transaction costs. Open Source Hedera is committed to open, transparent code. Learning Center Learn about web3 and blockchain technologies. Grants Grants & accelerators for your project. Bounties Find bugs. Submit a report. Earn rewards. Ecosystem ECOSYSTEM Hedera Ecosystem Applications, developer tools, network explorers, and more. NFT Ecosystem Metrics Analyze on-chain and market NFT ecosystem metrics. CATEGORIES Web3 Applications Connect into the innovative startups decentralizing the web on Hedera. Enterprise Applications Learn about the Fortune 500 companies decentralizing the web on Hedera. Wallets & Custodians Create a Hedera account to manage HBAR, fungible tokens, and NFTs. Network Explorers Hedera mainnet and testnet graphical network explorers. Developer Tooling Third-party APIs, integrations, and plugins to build apps on Hedera. Grants & Accelerators Boost your project with support from the Hedera ecosystem. Partner Program Explore our partners to bring your vision into reality. Hedera Council Over 30 highly diversified organizations govern Hedera. Use Cases Hedera Solutions Asset Tokenization Studio Open source toolkit for tokenizing assets securely. Stablecoin Studio All-in-one toolkit for stablecoin solutions. Hedera Guardian Auditable carbon markets and traceability. Functional Use Cases Data Integrity & AI Reliable, secure, and ethically governed insights. Sustainability Enabling fair carbon markets with trust. Real-World Asset Tokenization Seamless tokenization of real-world assets and digital at scale. Consumer Engagement & Loyalty Mint, distribute, and redeem loyalty rewards. Decentralized Identity Maintain the lifecycle of credentials. Decentralized Logs Scalable, real-time timestamped events. DeFi Dapps built for the next-generation of finance. NFTs Low, fixed fees. Immutable royalties. Payments Scalable, real-time, and affordable crypto-payments. HBAR Overview Learn about Hedera's token, HBAR. Treasury Management Hedera’s report of the HBAR supply. Governance Decentralized Governance Hedera Council See the world's leading organizations that own Hedera. About Meet Hedera's Board of Directors and team. Journey Watch Hedera's journey to build an empowered digital future for all. Transparent Governance Public Policy Hedera's mission is to inform policy and regulation that impact the industry. Meeting Minutes Immutably recorded on Hedera. Roadmap Follow Hedera's roadmap in its journey to build the future. Resources Company What's New Partners Papers Careers Media Blog Technical Press Podcast Community Events Meetups Store Brand Navigation QUICKSTART NFT Royalty Fees: Everything You Need To Know (Edge-cases Included) technical Nov 14, 2022 by Michiel Mulders Developer Relations When you create a new non-fungible token using the Hedera Token Service (HTS), you have the possibility to set one or multiple royalty fees. You can do this by using the CustomRoyaltyFee method, which allows you to set all parameters for your custom NFT royalty fee. In its simplest form, each time an NFT is transferred, the Hedera network will charge a fraction of the value exchanged in this transaction. However, this does not apply when transferring from and to the token's treasury account. If you need to return the NFT to the treasury account, you should not have to pay for it. In the same way, it doesn't make sense for the treasury account to pay its royalty fee when sending out NFTs to collectors. This blog post will show multiple code examples illustrating the correct way to set a custom royalty fee and numerous edge cases to help you better understand how the Hedera network works. If you want to play around with the code examples yourself, make sure you have a funded account you can use as the operator account to fund other accounts generated in the examples. First, let's look at a regular custom royalty fee code snippet. How to set a custom royalty fee? Here's an example of a custom royalty fee where we charge a 50% fee on the exchanged value each time an NFT from this collection is transferred. Besides that, if the user doesn't add an Hbar transfer (exchanged value) to an NFT transfer transaction, we want the user to pay a fallback fee of 1 Hbar. // DEFINE CUSTOM FEE SCHEDULE (50% royalty fee - 5/10ths) let nftCustomFee = new CustomRoyaltyFee() .setNumerator(5) .setDenominator(10) .setFeeCollectorAccountId(treasuryId) //the fallback fee is set to 1 hbar. .setFallbackFee(new CustomFixedFee().setHbarAmount(new Hbar(1))); // CREATE NFT WITH CUSTOM FEE let nftCreate = await new TokenCreateTransaction() .setTokenName("Fall Collection") .setTokenSymbol("LEAF") .setTokenType(TokenType.NonFungibleUnique) .setDecimals(0) .setInitialSupply(0) .setTreasuryAccountId(treasuryId) .setSupplyType(TokenSupplyType.Finite) .setMaxSupply(5) .setCustomFees([nftCustomFee]) .setAdminKey(adminKey) .setSupplyKey(supplyKey) .freezeWith(client) .sign(treasuryKey); let nftCreateTxSign = await nftCreate.sign(adminKey); let nftCreateSubmit = await nftCreateTxSign.execute(client); let nftCreateRx = await nftCreateSubmit.getReceipt(client); let tokenId = nftCreateRx.tokenId; console.log(`Created NFT with Token ID: ${tokenId} \n`); Copy You can find the full code at case-normal.js. This example completes multiple steps: Transfer NFT from Treasury->Alice (no royalty fee) Transfer NFT from Alice->Bob and Bob pays 10 Hbar to Alice (royalty fee is paid) Transfer NFT from Bob->Treasury (no royalty fee) If you execute the code at case-normal.js, you'll get the following output which nicely prints the differences in balances after each step. Starting balances: - Treasury balance: 5 NFTs of ID:0.0.48830139 and 5 ℏ - Alice balance: undefined NFTs of ID:0.0.48830139 and 30 ℏ - Bob balance: 0 NFTs of ID:0.0.48830139 and 30 ℏ --- NFT transfer Treasury->Alice status: SUCCESS - Treasury balance: 4 NFTs of ID:0.0.48830139 and 5 ℏ - Alice balance: 1 NFTs of ID:0.0.48830139 and 30 ℏ - Bob balance: 0 NFTs of ID:0.0.48830139 and 30 ℏ --- NFT transfer Alice->Bob status: SUCCESS - Treasury balance: 4 NFTs of ID:0.0.48830139 and 10 ℏ (+5 Hbar = 50% royalty fee) - Alice balance: 0 NFTs of ID:0.0.48830139 and 35 ℏ (+5 Hbar value exchange) - Bob balance: 1 NFTs of ID:0.0.48830139 and 20 ℏ (- 10 Hbar) --- NFT transfer Bob->Treasury status: SUCCESS - Treasury balance: 5 NFTs of ID:0.0.48830139 and 10 ℏ - Alice balance: 0 NFTs of ID:0.0.48830139 and 35 ℏ - Bob balance: 0 NFTs of ID:0.0.48830139 and 20 ℏ It's also possible to exempt fee collectors from paying royalty fees when they transfer NFTs. With the implementation of HIP-573 on mainnet in release v0.31 (November 10th, 2022), you can exempt collection accounts from paying custom fees when exchanging token units, fungible tokens, or non-fungible tokens. This tutorial explains the complete setup, but here's a quick snippet showing how to do it. const fee1 = new CustomFractionalFee() .setFeeCollectorAccountId(accountId1) .setNumerator(1) .setDenominator(100) .setAllCollectorsAreExempt(true); // new property to exempt fee collectors const fee2 = new CustomFractionalFee() .setFeeCollectorAccountId(accountId2) .setNumerator(2) .setDenominator(100) .setAllCollectorsAreExempt(true); // you have to set it for each fee you define Copy Now, let's take a look at some NFT custom royalty fee edge cases. Edge case 1 Question: What happens if no fungible value is exchanged in an NFT transfer and the royalty fee schedule defines a fallback fee? Output: The fallback fee of 1 Hbar is paid. - Treasury balance: 5 NFTs of ID:0.0.48289984 and 5 ℏ - Alice balance: undefined NFTs of ID:0.0.48289984 and 30 ℏ - Bob balance: 0 NFTs of ID:0.0.48289984 and 30 ℏ NFT transfer Treasury->Alice status: SUCCESS - Treasury balance: 4 NFTs of ID:0.0.48289984 and 5 ℏ - Alice balance: 1 NFTs of ID:0.0.48289984 and 30 ℏ - Bob balance: 0 NFTs of ID:0.0.48289984 and 30 ℏ NFT transfer Alice->Bob status: SUCCESS - Treasury balance: 4 NFTs of ID:0.0.48289984 and 6 ℏ - Alice balance: 0 NFTs of ID:0.0.48289984 and 30 ℏ - Bob balance: 1 NFTs of ID:0.0.48289984 and 29 ℏ You can find the full code example at case-1.js. Edge case 2 Question: What happens if no fungible value is exchanged in an NFT transfer and the royalty fee schedule doesn't define a fallback fee Output: Nothing will be charged when transferring the NFT. In other words, if you don't set a fallback fee, the royalty fee can be evaded when transferring an NFT between different accounts. - Treasury balance: 5 NFTs of ID:0.0.48289984 and 5 ℏ - Alice balance: undefined NFTs of ID:0.0.48289984 and 30 ℏ - Bob balance: 0 NFTs of ID:0.0.48289984 and 30 ℏ NFT transfer Treasury->Alice status: SUCCESS - Treasury balance: 4 NFTs of ID:0.0.48289984 and 5 ℏ - Alice balance: 1 NFTs of ID:0.0.48289984 and 30 ℏ - Bob balance: 0 NFTs of ID:0.0.48289984 and 30 ℏ NFT transfer Alice->Bob status: SUCCESS - Treasury balance: 4 NFTs of ID:0.0.48289984 and 5 ℏ - Alice balance: 0 NFTs of ID:0.0.48289984 and 30 ℏ - Bob balance: 1 NFTs of ID:0.0.48289984 and 30 ℏ You can find the full code example at case-2.js. Edge case 3 Question: What happens if no fungible value is exchanged in an NFT transfer and the receiver does not have the fixed fee fallback token but is associated with the fallback token? Output: Fails with insufficient balance error for Bob (receiver) because he has 0 tokens: INSUFFICIENT_SENDER_ACCOUNT_BALANCE_FOR_CUSTOM_FEE. The same error is thrown when he has one or two tokens of this random token and the fallback fee is set to a higher amount than two. - Treasury balance: 5 NFTs of ID:0.0.48294326 and 5 ℏ - Alice balance: undefined NFTs of ID:0.0.48294326 and 30 ℏ - Bob balance: 0 NFTs of ID:0.0.48294326 and 30 ℏ NFT transfer Treasury->Alice status: SUCCESS - Treasury balance: 4 NFTs of ID:0.0.48294326 and 5 ℏ - Alice balance: 1 NFTs of ID:0.0.48294326 and 30 ℏ - Bob balance: 0 NFTs of ID:0.0.48294326 and 30 ℏ ReceiptStatusError: receipt for transaction [email protected] contained error status INSUFFICIENT_SENDER_ACCOUNT_BALANCE_FOR_CUSTOM_FEE Here's a code snippet that shows the royalty fee definition. Bob's account will be associated with this random token. const randomTokenId = TokenId.fromString("0.0.48114789") let nftCustomFee = new CustomRoyaltyFee() .setNumerator(5) .setDenominator(10) .setFeeCollectorAccountId(treasuryId) //the fallback to a random token .setFallbackFee( new CustomFixedFee() .setAmount(10) .setDenominatingTokenId(randomTokenId) .setFeeCollectorAccountId(treasuryId) ); Copy You can find the full code example at case-3.js. Edge case 4 Question: What happens if no fungible value is exchanged in an NFT transfer and the buyer is not associated with the fixed fee fallback token? Output: The transfer between Alice->Bob fails because Bob has to pay the fallback fee (no value exchanged) but he is not associated with the random token. Here, you get the expected TOKEN_NOT_ASSOCIATED_TO_ACCOUNT error. - Treasury balance: 5 NFTs of ID:0.0.48290038 and 5 ℏ - Alice balance: undefined NFTs of ID:0.0.48290038 and 30 ℏ - Bob balance: 0 NFTs of ID:0.0.48290038 and 30 ℏ NFT transfer Treasury->Alice status: SUCCESS - Treasury balance: 4 NFTs of ID:0.0.48290038 and 5 ℏ - Alice balance: 1 NFTs of ID:0.0.48290038 and 30 ℏ - Bob balance: 0 NFTs of ID:0.0.48290038 and 30 ℏ ReceiptStatusError: receipt for transaction [email protected] contained error status TOKEN_NOT_ASSOCIATED_TO_ACCOUNT ... You can find the full code example at case-4.js. Edge case 5 Question: What happens when you create an NFT but the fee collector account is not associated with the fallback fee (CustomFixedFee)? Output: The TokenCreateTransaction with the error: TOKEN_NOT_ASSOCIATED_TO_FEE_COLLECTOR. You can find the full code example at case-5.js. Edge case 6 Question: What happens when you add a token as a custom royalty fee to an NFT and then delete this token on the ledger? Output: As the docs for deleting a token mention, it will throw an error TOKEN_WAS_DELETED when transferring this NFT when no value is exchanged. It means that you can only exchange this NFT when transferring Hbar value. NFT transfer Treasury->Alice status: SUCCESS - Treasury balance: 5 ℏ NFTs of ID:0.0.48290163 4 and rand token: 980 - Alice balance: 20 ℏ NFTs of ID:0.0.48290163 1 and rand token: 10 - Bob balance: 20 ℏ NFTs of ID:0.0.48290163 0 and rand token: 10 - Token deleted ReceiptStatusError: receipt for transaction [email protected] contained error status TOKEN_WAS_DELETED ... You can find the full code example at case-6.js. Edge case 7 Question: Can you update the royalty fee for NFTs to any number above 100%? Output: This example first sets the royalty fee to 50% (5/10ths) and then updates the fee to 200% (200/100ths). However, the TokenFeeScheduleUpdateTransaction fails with the error ROYALTY_FRACTION_CANNOT_EXCEED_ONE, which means that 100% is the maximum fee you can charge. - Creating accounts... - Created NFT with Token ID 0.0.2748981 and fee schedule 5/10ths ReceiptStatusError: receipt for transaction [email protected] contained error status ROYALTY_FRACTION_CANNOT_EXCEED_ONE ... You can find the full code example at case-7.js. Edge case 8 Question: Can you steal funds from someone's account that has auto-association slots available by sending them an NFT with a fallback fee?  Output: No, stealing funds is impossible because the account receiving the NFT has to sign the fallback fee being withdrawn from their account's balance. Without the receiving user's signature, you'll get an INVALID_SIGNATURE error. ReceiptStatusError: receipt for transaction [email protected] contained error status INVALID_SIGNATURE ... You can find the full code example at case-8.js. Check out the Code Check out all code examples on GitHub. Continue Learning If you want to learn more, check the Hedera Token Service or Custom Fees docs. Share This Back to blog What is gRPC, gRPC-Web, and Proxies? Ed Marquez Pragmatic Blockchain Design Patterns – Integrating Blockchain into Business Processes Michiel Mulders Zero Cost EthereumTransaction on Success: Hedera's New Fee Model for Relay Operators Oliver Thorn Hedera Adopts Chainlink Standard for Cross-Chain Interoperability To Accelerate Ecosystem Adoption Hedera Team Hedera Developer Highlights March 2025 Michiel Mulders Hedera Release Cycle Overview Ed Marquez View All Posts Sign up for the newsletter CONNECT WITH US Transparency Open Source Audits & Standards Sustainability Commitment Carbon Offsets Governance Hedera Council Public Policy Treasury Management Meeting Minutes LLC Agreement Node Requirements Community Events Meetups HBAR Telegram Developer Discord Twitter Community Support FAQ Network Status Developer Discord StackOverflow Brand Brand Guidelines Built on Hedera Logo Hedera Store About Team Partners Journey Roadmap Careers Contact General Inquiry Public Relations © 2018-2025 Hedera Hashgraph, LLC. All trademarks and company names are the property of their respective owners. All rights in the Deutsche Telekom mark are protected by Deutsche Telekom AG. All rights reserved. Hedera uses the third party marks with permission. Terms of Use  |  Privacy Policy