Fees and revenue

Where swap fees accrue, why they are not in your profit report until you sweep them, how to set a fee recipient for each chain, and how to read the accrued-versus-on-chain difference.

8 min readUpdated 4 August 2026

Your swap fee is collected on chain, by the aggregator, in the same transaction as the trade. It lands at an address you nominate for each network.

That one sentence explains almost everything else on this page — including the thing operators ask about first.

They accrue to your fee-recipient address, not to a platform wallet. The platform never holds the key to that address, so it cannot move the money for you. You transfer the funds yourself, tell the platform the transaction hash, and the revenue is then credited and appears in your profit report.

A large "Accrued" figure with nothing in Finance → Profit is the system working correctly, not a bug.

Before you turn fees on

Every chain you enable needs a fee recipient. Set it in Admin → Swap → Chains.

It does not quote at zero fee. It refuses, and the user sees the swap surface as unavailable on that network.

This is deliberate. The alternative — quoting at zero and earning nothing — looks perfectly healthy: every switch green, every swap succeeding, every dashboard populated, and nothing anywhere raising its hand. The fee is baked into calldata the user has already signed, so there is no way to go back and collect on trades that have already settled. A refusal is one setting away from correct. A silent zero is unrecoverable.

If you intend to run a network at cost — a partner chain, or one where you hold no key — tick Run at zero fee on that chain instead. That is the only way to switch the refusal off, and it is recorded in your audit trail in words.

The Fee status column on the chain list tells you which state each network is in:

Status Meaning
Earning A fee is set and a valid recipient exists. Swaps complete and you earn.
Refusing Something is missing. The column names which one thing, and swaps on this chain are refused until you fix it.
No fee Either your platform fee is 0 bps, or you ticked "Run at zero fee" here. Swaps work; you earn nothing on them.

Which address should receive the fee?

Either an ordinary wallet (an EOA) or a multi-signature wallet such as a Gnosis Safe. The trade-off is the usual one:

  • An EOA is simpler and sweeps in one signature. One compromised key loses everything that has accrued since your last sweep.
  • A Safe needs several signers to move funds, which is what you want for an address that accumulates revenue unattended. Sweeping takes longer.

Set it per chain. A Safe deployed on Ethereum does not exist at the same address on Base unless you deployed it there too, and there is no global fallback that would ever be the right guess.

Use an address that does nothing else. The revenue console compares what your books say has accrued against what the address actually holds, and that comparison is much easier to read when the balance is only ever fees.

How a fee becomes revenue

  1. A user swaps. The aggregator transfers your fee to the recipient address inside the same transaction. Nothing of yours has to succeed for this to happen — it is part of the trade.

  2. The platform records it. Once the swap is confirmed, a row is written to the fee ledger with the amount, the token, and its USD value at that moment. Status: Accrued.

  3. You sweep it. From your own wallet, transfer the accumulated tokens from the fee recipient to wherever you want them. The platform is not involved and cannot be.

  4. You record the sweep. On Admin → Swap → Fees, press Record sweep on the position and paste the transaction hash. The platform checks the transaction against the chain — it succeeded, it moved that token, from your fee recipient, to the destination you named — and marks the covered rows Sweep submitted.

  5. The platform credits your books. Once the transfer reaches the chain's confirmation depth, a background job credits your Super Admin SPOT wallet and writes a platform-profit entry of type DEX_SWAP. That is the point at which the revenue appears in Finance → Profit.

Steps 4 and 5 are separate because confirmations take minutes. A page that waited for them would time out, and a retry that waited would be indistinguishable from a second sweep.

Reading the revenue console

  1. Earned, not yet yours to spend
  2. The only figure that reaches Finance - Profit
  3. What the chain says the address holds
  4. Scans blocks for transfers with no swap behind them

Admin → Swap → Fees shows three figures that are easy to conflate and are never the same number:

  • Accrued — earned, sitting at your fee-recipient address. Not yours to spend from the platform's point of view.
  • Swept — collected and credited. This is the only figure that reaches your profit report.
  • On chain — what the token contract says your recipient address holds right now.

The difference column

The console subtracts what your books say is waiting from what the chain says is there.

A positive difference is usually fine

There is more of that token at the address than the platform recorded. Two ordinary explanations:

  • The address holds your own funds as well as fees. It is your wallet.
  • A fee was taken that the platform never recorded — see below.
A negative difference is the one to act on

The platform has booked revenue that is not at the address. Either a sweep was made without recording it, or a fee that was recorded never actually arrived. Both mean a figure in your ledger is not collectable, and the number will not correct itself.

"Unavailable" is not zero

If the chain's RPC endpoint does not answer, the console says so. It will never render an unreachable chain as a zero balance, because that reads as "your fees have vanished".

Fees the platform never recorded

The record is created by the browser after the user's wallet broadcasts. A user who closes the tab in between has still paid you — the fee is inside calldata that has already settled — but nothing wrote it down.

Find unrecorded fees on the same page scans a range of blocks for transfers to your fee recipient and lists the ones with no swap behind them.

An unrecorded transfer has no quote behind it, so it has no user, no compliance snapshot and no fee side. Creating a ledger row from a bare transfer log would put a figure in your revenue that nobody can attribute to anything — and would then let the sweep credit it as though it had been verified.

The money is collectable either way: it is already at your address, and a sweep collects it whether or not a row exists. The list is there so you know it happened.

When a sweep cannot be credited

A row marked Needs attention means the transfer confirmed on chain and the credit into your books failed. The money is still yours and still at the destination — it is the bookkeeping that stopped.

This token is not in your currency list

The platform credits a wallet in that token's ticker, and it will not invent a currency to do it. A wallet in a currency nothing else on your platform recognises cannot be traded, withdrawn or reported on — so the sweep refuses by name instead of creating one.

Fix: add the currency under Finance → Currencies and mark it active. The settlement job retries on its next pass.

The amount rounds to zero

Wallet balances carry eight decimal places. An 18-decimal token whose fee is smaller than that rounds away entirely, and crediting zero would mark the fee collected while destroying the record that it was ever owed.

Fix: let more accumulate before sweeping, or raise the precision configured for that ticker.

The wallet credit failed five times

Something below the sweep is failing — usually the wallet or currency configuration for that ticker. The transfer is confirmed; check your platform fee log for the underlying error, fix it, and the amount is still recoverable.

Setting the fee itself

Admin → Swap → Settings:

dexFeeBpstype: numberdefault: 0
Your cut of each swap, in basis points. 30 = 0.30%.
dexMaxFeeBpstype: numberdefault: 100
A ceiling on the above, so a mistyped fee cannot be saved.

It defaults to 0 so a fresh install boots and quotes on every chain: with the refusal rule above, any non-zero default would refuse every quote everywhere until a recipient was set on each network. Turn revenue on deliberately, once your recipients are in place.

Aggregators cap an integrator fee at 3% and reject anything higher, so the platform will not accept a ceiling above 300 bps.

Set the fee recipient on every chain you have enabled first, then raise dexFeeBps. Doing it the other way round takes every chain down until you catch up.

What users see

The fee is disclosed on the quote, on the confirmation dialog and on the receipt — the amount, the token and the rate, from a single source, so the three surfaces cannot disagree. You cannot switch that disclosure off.