# Transaction Fee

Source: https://docs.settlemint.com/docs/architects/components/token-features/transaction-fee
Per-transaction token fee that deducts a basis-point fee from mint, burn, transfer, and redemption events before downstream checks and audit reads consume the final amount.



DALP can collect issuer fees on mint, burn, transfer, and redemption by splitting a fee-bearing token movement into a net movement plus a fee movement. The feature calculates the fee in basis points and indexes collection history for reconciliation. Transaction Fee changes token amounts only. [Asset policy](/docs/architects/concepts/asset-policy) covers the compliance rules that decide whether an operation may proceed.

***

## How the feature behaves [#how-the-feature-behaves]

Transaction Fee runs in the token amount-rewriting pipeline. It checks the configured rate for the operation and calculates `amount * feeBps / 10_000`. Downstream checks and analytics features consume the resulting token movements.

<Mermaid
  chart="flowchart TD
  Request[Mint, burn, transfer, or redeem request] --> Rate[Read configured rate]
  Rate --> Calc[Calculate fee = amount * feeBps / 10_000]
  Calc --> Net[Apply net token movement]
  Calc --> Fee[Route fee to fee recipient]
  Net --> Checks[Downstream checks and feature hooks use final amount]
  Fee --> Event[Emit TransactionFeeCollected]
  Event --> Index[Indexer stores collection row]
  Index --> Read[Collection API returns audit history]"
/>

| Operation | Fee rate used | Net movement                                              | Fee movement                                         | Collection signal |
| --------- | ------------- | --------------------------------------------------------- | ---------------------------------------------------- | ----------------- |
| Mint      | Mint fee      | Mint `amount - fee` to the holder                         | Mint `fee` to the fee recipient                      | `mint`            |
| Burn      | Burn fee      | Burn `amount - fee` from the holder                       | Transfer `fee` from the holder to the fee recipient  | `burn`            |
| Transfer  | Transfer fee  | Transfer `amount - fee` to the recipient                  | Transfer `fee` from the sender to the fee recipient  | `transfer`        |
| Redeem    | Burn fee      | Redemption payout is calculated by the redemption feature | Transaction Fee emits a burn-style collection signal | `redeem`          |

The feature applies three invariants across those operations:

* Fee rates are basis-point values between `0` and `10_000`.
* A zero-amount movement does not create a fee collection.
* A movement from or to the configured fee recipient does not create another fee. That prevents the feature from charging the fee movement it created itself.

***

## Interface capabilities [#interface-capabilities]

| Capability              | Who can call      | Inputs                                         | Effect                                   | Emits                     | Notes                      |
| ----------------------- | ----------------- | ---------------------------------------------- | ---------------------------------------- | ------------------------- | -------------------------- |
| Deduct fee on operation | Automatic hook    | Mint, burn, transfer, or redeem movement       | Rewrites the movement and routes the fee | `TransactionFeeCollected` | `supportsRewriting = true` |
| Set fee rates           | `GOVERNANCE_ROLE` | Mint, burn, and transfer rates in basis points | Updates future fee calculations          | `FeeRatesUpdated`         | Blocked after freeze       |
| Set fee recipient       | `GOVERNANCE_ROLE` | Recipient address                              | Redirects future fee collection          | `FeeRecipientUpdated`     | Cannot be the zero address |
| Freeze fee rates        | `GOVERNANCE_ROLE` | None                                           | Permanently locks all fee rates          | `FeeRatesFrozen`          | Irreversible               |

Fee rates must be between `0` and `10_000` basis points. `10_000` means 100 percent of the operation amount.

***

## Configuration lifecycle [#configuration-lifecycle]

Attach the feature with initial mint, burn, and transfer rates plus a non-zero fee recipient. After launch, governance can update rates while rates are not frozen, change the fee recipient, or permanently freeze the rates.

| Lifecycle action | Allowed when                                 | Result                                              |
| ---------------- | -------------------------------------------- | --------------------------------------------------- |
| Attach feature   | The token is configured with Transaction Fee | Initial rates and the fee recipient are stored      |
| Update rates     | Rates are not frozen                         | Future fee calculations use the new rates           |
| Change recipient | The caller has `GOVERNANCE_ROLE`             | Future collections route to the new recipient       |
| Freeze rates     | Rates are not already frozen                 | Future rate changes and another freeze attempt fail |

DALP API mutations for setting rates, setting the recipient, and freezing rates use the transaction queue. A successful request returns the queued-operation response and a status URL for the corresponding transaction-fee operation. The mutation requires the transaction-fee feature to be attached to the token and the caller wallet to pass wallet verification.

***

## Business impact [#business-impact]

| Reader concern                  | What changes                                                                                                                                                                   |
| ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Holder balance                  | Holders receive the post-fee token amount on mint, burn, and transfer operations. Senders initiate transfers for the gross amount. Recipients receive the net amount.          |
| Issuer or fee-recipient balance | Fees are collected in the same token and routed to the configured `feeRecipient`.                                                                                              |
| Redemption economics            | Redemption uses the burn fee calculation for the in-token collection signal. Transaction Fee does not reduce the denomination-asset payout calculated by a redemption feature. |

***

## Risks and abuse cases [#risks-and-abuse-cases]

* **Compliance module ordering risk:** Because Transaction Fee rewrites amounts, downstream checks can receive the post-fee amount. If a compliance module enforces minimum transfer amounts, align the threshold with the maximum configured fee rate.
* **Rate freeze timing:** Leaving rates unfrozen allows governance to change rates later. Freeze rates when the asset programme requires fixed token economics.
* **Fee recipient control:** An unexpected fee recipient redirects value. Use governed role assignment, multisig controls where appropriate, and collection monitoring.
* **Full-fee configuration:** A 100 percent rate leaves the business recipient with zero tokens for that operation. DALP allows the rate, so asset operators must choose rates that fit the product terms.

***

## Controls and guardrails [#controls-and-guardrails]

| Role              | Actions                                      | Guardrail                                                   |
| ----------------- | -------------------------------------------- | ----------------------------------------------------------- |
| `GOVERNANCE_ROLE` | `setFeeRates()`: set fee rates per operation | Freeze rates after launch when rates must not change        |
| `GOVERNANCE_ROLE` | `setFeeRecipient()`: set fee destination     | Restrict governance and monitor recipient changes           |
| `GOVERNANCE_ROLE` | `freezeFeeRates()`: lock rates permanently   | Treat freeze as irreversible asset-configuration governance |

***

## Failure modes and edge cases [#failure-modes-and-edge-cases]

* **Frozen rates:** Setting rates after freeze fails with the token fee rates frozen error. The chain remains the source of truth if the indexer row is not yet available.
* **Invalid rate:** Rates above `10_000` basis points fail.
* **Invalid recipient:** A zero-address fee recipient fails during setup or recipient update.
* **Zero-amount movement:** The feature does not charge a fee when the operation amount is zero.
* **Fee-recipient movement:** The feature does not charge a fee when the sender or receiver is the configured fee recipient.
* **Mint exactness:** Minting to a holder results in the holder receiving `amount - mintFee`. If a product term requires the holder to receive an exact number of tokens, account for the fee in the gross mint amount or use a different fee model.

***

## Auditability and operational signals [#auditability-and-operational-signals]

`TransactionFeeCollected(from, feeAmount, operationType)` is emitted per fee-bearing operation. The operation type is one of `mint`, `burn`, `transfer`, or `redeem`. DALP also emits `FeeRatesUpdated`, `FeeRecipientUpdated`, and `FeeRatesFrozen` for configuration changes.

DALP exposes indexed collection rows for the active transaction-fee feature at `GET /api/v2/tokens/{tokenAddress}/transaction-fee/collections`. The read returns a paginated `data`, `meta`, and `links` response. Each row includes:

| Field                              | Meaning                                                         |
| ---------------------------------- | --------------------------------------------------------------- |
| `counterpartyAddress`              | The event `from` address. Mint collections use the zero address |
| `operationType`                    | `mint`, `burn`, `transfer`, or `redeem`                         |
| `feeAmount`                        | Decimal display amount                                          |
| `feeAmountExact`                   | Lossless integer amount                                         |
| `blockNumber` and `blockTimestamp` | Chain position for the collection event                         |
| `txHash` and `logIndex`            | Event location for reconciliation                               |

Use the collection read when a dashboard, reconciliation job, or audit export needs collection history without scanning token events itself. The endpoint returns an empty collection when the token has no attached transaction-fee feature.

***

## Dependencies [#dependencies]

* No external ERC-20 is required. The fee is paid in the token itself.
* `supportsRewriting = true` means the feature participates in the amount-rewriting pipeline.
* The feature should run before analytics features that need the final post-fee amount.

***

## Compatibility and ordering notes [#compatibility-and-ordering-notes]

* **Before analytics:** Historical Balances and Voting Power should consume post-fee amounts.
* **Compliance modules:** Compliance checks can receive the post-rewrite amount. If any module enforces minimum amounts, account for the maximum configured fee rate.
* **AUM Fee:** Compatible. Both collect in-token fees.
* **External Transaction Fee:** Compatible as a separate mechanism for external-fee collection.

***

## Change impact [#change-impact]

* **Enable after launch:** Only transactions from activation onward use the feature.
* **Disable:** No retroactive effect. In-flight queued transactions complete using the feature state observed during execution.
* **Rate change before freeze:** Applies to future executions.
* **Recipient change:** Applies to future collections.

***

## See also [#see-also]

* [Token Features Catalog](/docs/architects/components/token-features): return to the full feature catalog
* [Transaction Fee Accounting](/docs/architects/components/token-features/transaction-fee-accounting): compare off-chain fee accounting with in-token deduction
* [External Transaction Fee](/docs/architects/components/token-features/external-transaction-fee): collect fees through a separate fee token
* [Compliance Modules](/docs/compliance-security/security/compliance): understand transfer-level enforcement
