When creating a token, you can configure up to 10 custom fees, automatically disbursed to specified fee collector accounts each time the token is transferred programmatically. These fees can be fixed, fractional, or royalty-based, offering revenue generation, profit-sharing, and behavior incentivization for creators. This guide is your comprehensive resource for understanding types, implementation, and best practices for custom fees on Hedera.
Fixed Fee: Paid by the sender of the fungible or non-fungible tokens. A fixed fee transfers a set amount to a fee collector account each time a token is transferred, independent of the transfer size. This fee can be collected in HBAR or another Hedera token but not in NFTs.
Fractional Fee: Take a specific portion of the transferred fungible tokens, with optional minimum and maximum limits. The token receiver (fee collector account) pays these fees by default. However, if net_of_transfers
is set to true, the sender pays the fees and the receiver collects the full token transfer amount. If this field is set to false, the receiver pays for the token custom fees and gets the remaining token balance.
Royalty Fee: Paid by the receiver account that is exchanging the fungible value for the NFT. When the NFT sender does not receive any fungible value, the fallback fee is charged to the NFT receiver.
{% hint style="info" %} Note: In addition to the custom token fee payment, the sender account must pay for the token transfer transaction fee in HBAR. The "Payment of Custom Fees & Transaction Fees in HBAR" section below covers the distinction between custom fees and transaction fees. {% endhint %}
A fixed fee entails transferring a specified token amount to predefined fee collector accounts each time a token transfer is initiated. This fee amount doesn't depend on the volume of tokens being transferred. The creator has the flexibility to collect the fee in HBAR or another fungible Hedera token. However, it's important to note that NFTs cannot be used as a token type to collect this fee. A custom fixed fee can be set for fungible and non-fungible token types.
Constructor | Description |
---|---|
new CustomFixedFee() | Initializes the CustomFixedFee object |
new CustomFixedFee()
Methods | Description | Type | Requirement |
---|---|---|---|
setFeeCollectorAccountId | Sets the fee collector account ID that collects the fee. | AccountId | Required |
setHbarAmount | Set the amount of HBAR to be collected. | HBAR | Optional |
setAmount | Sets the amount of tokens to be collected as the fee. | int64 | Optional |
setDenominatingTokenId | The ID of the token used to charge the fee. The denomination of the fee is taken as HBAR if left unset. | TokenID | Optional |
setAllCollectorsAreExempt | If true, exempts all the token's fee collector accounts from this fee. | boolean | Optional |
{% tabs %} {% tab title="Java" %}
//Create a custom token fixed fee
new CustomFixedFee()
.setAmount(1) // 1 token is transferred to the fee collecting account each time this token is transferred
.setDenominatingTokenId(tokenId) // The token to charge the fee in
.setFeeCollectorAccountId(feeCollectorAccountId); // 1 token is sent to this account everytime it is transferred
//Version: 2.0.143
{% endtab %}
{% tab title="JavaScript" %}
//Create a custom token fixed fee
new CustomFixedFee()
.setAmount(1) // 1 token is transferred to the fee collecting account each time this token is transferred
.setDenominatingTokenId(tokenId) // The token to charge the fee in
.setFeeCollectorAccountId(feeCollectorAccountId); // 1 token is sent to this account everytime it is transferred
//Version: 2.0.30
{% endtab %}
{% tab title="Go" %}
//Create a custom token fixed fee
[]hedera.Fee{
hedera.NewCustomFixedFee().
SetAmount(1). // 1 token is transferred to the fee collecting account each time this token is transferred
SetDenominatingTokenID(tokenId). // The token to charge the fee in
SetFeeCollectorAccountID(feeCollectorAccountId) // 1 token is sent to this account everytime it is transferred
},
}
//Version: 2.1.16
{% endtab %} {% endtabs %}
Fractional fees involve the transfer of a specified fraction of the tokens' total value to the designated fee collector account. You can set a custom fractional fee and impose minimum and maximum fee limits per transfer transaction. The fractional fee has to be less than or equal to 1. It cannot exceed the fractional range of a 64-bit signed integer. Applicable to fungible tokens only.
Methods | Description | Type | Requirement | Type |
---|---|---|---|---|
setFeeCollectorAccountId | Sets the fee collector account ID that collects the fee. | AccountId | Required | AccountId |
setNumerator | Sets the numerator of the fraction. | long | Required | long |
setDenominator | Sets the denominator of the fraction. Cannot be zero. | long | Required | long |
setMax | The maximum fee that can be charged, regardless of the fractional value. | long | Optional | long |
setMin | The minimum fee that can be charged, regardless of the fractional value. | long | Optional | long |
setAssessmentMethod | If true, sender pays fees and the receiver collects the full token transfer amount. If false, receiver pays fees and gets remaining token balance. | boolean | Optional | FeeAssessmentMethod |
setAllCollectorsAreExempt | If true, exempts all the token's fee collector accounts from this fee. | boolean | Optional | boolean |
{% tabs %} {% tab title="Java" %}
//Create a custom token fractional fee
new CustomFractionalFee()
.setNumerator(1) // The numerator of the fraction
.setDenominator(10) // The denominator of the fraction
.setFeeCollectorAccountId(feeCollectorAccountId); // The account collecting the 10% custom fee each time the token is transferred
//Version: 2.0.14
{% endtab %}
{% tab title="JavaScript" %}
//Create a custom token fractional fee
new CustomFractionalFee()
.setNumerator(1) // The numerator of the fraction
.setDenominator(10) // The denominator of the fraction
.setFeeCollectorAccountId(feeCollectorAccountId); // The account collecting the 10% custom fee each time the token is transferred
//Version: 2.0.30
{% endtab %}
{% tab title="Go" %}
//Create a custom token fractional fee
[]hedera.Fee{
hedera.NewCustomFractionalFee().
SetNumerator(1). // The numerator of the fraction
SetDenominator(10). // The denominator of the fraction
SetFeeCollectorAccountID(feeCollectorAccountId), // The account collecting the 10% custom fee each time the token is transferred
}
//Version: 2.1.16
{% endtab %} {% endtabs %}
The royalty fee is assessed and applied each time the ownership of an NFT is transferred and is a fraction of the value exchanged for the NFT. If no value is exchanged for the NFT, a fallback fee can be imposed on the receiving account. This fee type only applies to non-fungible tokens.
{% hint style="info" %}
🔔 NOTE: Royalty fees are strictly a convenience feature. The network can't enforce royalties if counterparties decide to split their NFT exchange into separate transactions. The NFT sender and receiver must both sign a single CryptoTransfer
to ensure the proper application of royalties. There is an open HIP discussion about broadening the class of transactions for which the network automatically collects royalties. If this topic interests or concerns you, your participation in the discussion is welcome.
{% endhint %}
Constructor | Description |
---|---|
new CustomRoyaltyFee() | Initializes the CustomRoyaltyFee object |
new CustomRoyaltyFee()
Methods | Description | Type | Requirement |
---|---|---|---|
setFeeCollectorAccountId | Sets the fee collector account ID that collects the fee. | AccountId | Required |
setNumerator | Sets the numerator of the fraction. | long | Required |
setDenominator | Sets the denominator of the fraction. | long | Required |
setFallbackFee | If present, the fixed fee to assess to the NFT receiver when no fungible value is exchanged with the sender | FixedFee | Optional |
setAllCollectorsAreExempt | If true, exempts all the token's fee collector accounts from this fee. | boolean | Optional |
{% tabs %} {% tab title="Java" %}
//Create a royalty fee
new CustomRoyaltyFee()
.setNumerator(1) // The numerator of the fraction
.setDenominator(10) // The denominator of the fraction
.setFallbackFee(new CustomFixedFee().setHbarAmount(new Hbar(1)) // The fallback fee
.setFeeCollectorAccountId(feeCollectorAccountId))) // The account that will receive the royalty fee
// v2.0.14
{% endtab %}
{% tab title="JavaScript" %}
//Create a royalty fee
new CustomRoyaltyFee()
.setNumerator(1) // The numerator of the fraction
.setDenominator(10) // The denominator of the fraction
.setFallbackFee(new CustomFixedFee().setHbarAmount(new Hbar(1)) // The fallback fee
.setFeeCollectorAccountId(feeCollectorAccountId))) // The account that will receive the royalty fee
// v2.0.30
{% endtab %}
{% tab title="Go" %}
//Create a royalty fee
[]hedera.Fee{
hedera.NewCustomRoyaltyFee().
SetFeeCollectorAccountID(feeCollectorAccountId). // The account that will receive the royalty fee
SetNumerator(1). // The numerator of the fraction
SetDenominator(10). // The denominator of the fraction
SetFallbackFee( // The fallback fee
hedera.NewCustomFixedFee().
SetFeeCollectorAccountID(feeCollectorAccountId).
SetAmount(1),
),
}
// v2.1.16
{% endtab %} {% endtabs %}
Understanding the difference between custom fees and standard transaction fees in HBAR is crucial for token issuers and developers working with Hedera. Custom fees are designed to enforce complex fee structures, such as royalties and fractional ownership. These fees ca n be fixed, fractional, or royalty-based and are usually paid in the token being transferred, although other Hedera tokens or HBAR can also be used. You can configure up to 10 custom fees automatically disbursed to designated fee collector accounts.
On the other hand, transaction fees in HBAR serve a different purpose: they compensate network nodes for processing transactions. These fees are uniform across all transaction types and are paid exclusively in HBAR. Unlike custom fees, which can be configured by the user, transaction fees are fixed by the network.
The key differences lie in their flexibility, payee, currency, and configurability. Custom fees offer greater flexibility and can be paid to any account in various tokens, and are user-defined. Transaction fees are network-defined, less flexible, and go solely to network nodes, paid only in HBAR.
Fee collector accounts can be exempt from paying custom fees. To enable this, you need to set the exemption during the creation of the custom fees (HIP-573). If not enabled, custom fees will only be exempt for an account if that account is set as a fee collector.
When it comes to setting custom fees, there are a few limits and constraints to keep in mind:
- First, fees cannot be set to a negative value.
- Each token can have up to 10 different custom fees.
- Additionally, the treasury account for a given token is automatically exempt from paying these custom transaction fees.
- The system also permits, at most, two "levels" of custom fees. That means a token being transferred might require fees in another token that also has its own fee schedule; however, this can only be nested two layers deep to prevent excessive complexity.