Swaps
Fees
A swap has up to four costs. The quote shows each of them, and amountOut is already net of the first three.
| Cost | Who takes it | Size | Shown as |
|---|---|---|---|
| Router fee | The router (FeeCollector) | feeBps of the output, default 5 bps (0.05%), hard-capped at 100 bps (1%) on-chain | feeBps; fee in the Swapped event |
| LP fees | Each pool's liquidity providers | The pool's tier: e.g. 0.01%–2% on V3, 0.25–0.3% on V2, any (even dynamic) on V4 | fee on each hop of splits[].route |
| Token taxes | The token contract | Measured by the probe (buy/sell/transfer tax) | taxBps |
| Gas | The network | gas.estimate units × gas price | gas.costInTokenOut, simulation.gas |
Router fee
- Taken once, on the total output of the swap (not per hop or per leg), in
tokenOut. - Forwarded in the same transaction to
feeRecipient. The Executor never holds funds. feeBpsis read from the Executor at quote time, so the quote always matches the contract.- Changing it:
Executor.setFee(feeBps, feeRecipient)(owner only).feeBps > 100reverts withFeeTooHigh().feeRecipientcan't be zero or the Executor itself. - No router fee on the launchpad path (
venue: "launchpad"): that path doesn't go through the Executor.
Worked example
1 HYPE → USDC, pool output 24.80 USDC, feeBps = 5:
| USDC | |
|---|---|
| Output of the pools | 24.796576 |
| Router fee (5 bps) | −0.012398 |
amountOut (to the recipient) | 24.784178 |
minOut at 50 bps slippage | 24.660257 |
Where the fees go: FeeCollector
The Executor's feeRecipient is the FeeCollector contract. Fees pile up there in whatever tokens people bought. The keeper process converts them:
- Every 10 minutes it reads the FeeCollector's balances of the 100 most-traded tokens and values them in USD.
- Each balance worth more than
FEE_CONVERT_MIN_USD(default $5) is routed with the normal planner into the target token (native HYPE by default; kHYPE or KNTQ once they exist on Elysium).FeeCollector.convertswaps it through the Executor and sends the result to the treasury. - Manipulation guard. The keeper samples every token's price every 30 s into an exponential moving average (~2.5 min, 5-sample warm-up). It skips a conversion if the live quote is more than 2% below what the averaged prices predict, or if the trade itself would move the price by more than 2%. The
minOutit sends ismax(99% of the quote, 98% of the EMA-expected output). A sandwich or a briefly pushed pool can't force a bad conversion; the fees just wait for the next round.
The owner can configure(keeper, treasury, target) and sweep(token, amount) to the treasury without converting.
LP fees
Pool fees are part of the pool's math, so the exact quote already includes them. Each hop reports its tier in hundredths of a bp (3000 = 0.3%). For V2, the router uses the fee read from the factory's bytecode (25 or 30 bps), and the Executor passes it as v2FeeBps. Declaring a lower fee than the real one fails safe: the pair reverts on its K check.
Token taxes
Some tokens take a percentage on transfers. The probe measures them, and the quote:
- deducts
sellTax(in) + buyTax(out)fromamountOut(the raw router output stays inrouterAmountOut), - computes
minOutfrom the taxed amount, so a correctly taxed swap doesn't fail on slippage, - lists the taxes in
warnings.
Gas
Gas is not taken from the swap; the sender pays it in HYPE as usual. But the router counts it when choosing a route: each candidate's output is reduced by its gas cost converted into tokenOut, using these per-hop estimates:
| Item | Gas |
|---|---|
| Base (Executor overhead) | 60,000 |
| Per split leg | 20,000 |
| V3 hop | 90,000 |
| V2 hop | 70,000 |
| V4 hop | 130,000 |
| Solidly hop | 80,000 |
| Rewrap (WHYPE↔WHYPE) | 45,000 |
gas.estimate is that model's total for the chosen route; simulation.gas is the node's real estimate of the exact transaction.
Orders
Limit orders and DCA slices are executed through the Executor, so the router fee applies to each fill. Keepers are not paid by the order: the operator's keeper pays its own gas.
Next: Token safety