Liquidity
Pool Fees
Every swap on Topaz pays a fee. This page is the complete map: how the fee is set, who collects it, and how the three fee modules (default, custom, dynamic) layer together.
Where fees go
Topaz routes 100% of trading fees to veTOPAZ voters who voted for the pool that earned them — not to a protocol treasury. The voter share for a given epoch is proportional to how much you voted for that gauge, and the fees you can claim today are the fees collected during the prior epoch (synchronized with bribes).
LPs don't collect swap fees on staked v2 LP tokens — those fees are forwarded to the FeesVotingReward contract for voters. LPs earn TOPAZ emissions instead. For Slipstream, the same swap-fees-go-to-voters rule applies to staked positions; an unstaked Slipstream position continues to collect its share of swap fees in-NFT.
v2 pool fees
v2 pools have a single fee value set per-pool, taken from the input token on each swap. Defaults:
| Parameter | Value | Description |
|---|---|---|
| Stable pool default | 0.05% (5 bps) | Set by PoolFactory.stableFee. |
| Volatile pool default | 0.30% (30 bps) | Set by PoolFactory.volatileFee. |
| Maximum allowed | 3.00% (300 bps) | PoolFactory.MAX_FEE — the hard cap for any custom override. |
The fee manager (a privileged role on PoolFactory) can change the global default for stable/volatile pools or set a custom fee per pool. There is also a ZERO_FEE_INDICATOR sentinel that lets a specific pool be set to 0% fee — distinguishing it from unset/default behavior.
Slipstream default fees
Slipstream pools use tick spacing as their pool-identity discriminator. Each tick spacing has a recommended default fee at the factory:
| Parameter | Value | Description |
|---|---|---|
| Tick spacing 1 | 0.01% (100 pips) | Highly correlated pairs (USDT/USDC). |
| Tick spacing 50 | 0.05% (500 pips) | Tight-correlated pairs (ETH/stETH, BTC/WBTC) and low-vol blue chips. |
| Tick spacing 200 | 0.30% (3,000 pips) | Volatile blue chips (TOPAZ/BNB, ETH/USDT). |
| Tick spacing 2000 | 1.00% (10,000 pips) | Long-tail pairs. |
These are the defaults at pool creation; every fee on every pool can be customized by the protocol fee manager via the CustomSwapFeeModule (0%–3% range), and dynamic fees can layer on top.
Note the precision: Slipstream fees are denominated in pips (1 / 1,000,000), so 500 pips = 5 bps = 0.05%. v2 pool fees are denominated in basis points out of 10,000 — a different scale. The frontend always displays percent.
Custom fee module (Slipstream)
The protocol fee manager can override the default fee on any Slipstream pool via the CustomSwapFeeModule:
- ✓Per-pool overrides — any pool can have any fee between 0% and 3%.
- ✓Special sentinel: setting fee = 420 (pips) means a true zero-fee pool, distinguishing it from 'unset' (which falls back to the tick-spacing default).
- ✓Hard cap: 3% (30,000 pips). The fee manager cannot exceed this.
Dynamic fee module (Slipstream)
Some Slipstream pools opt into a volatility-based dynamic fee. The module reads the pool's TWAP and adds a surcharge when the current tick has moved far from the time-weighted average:
totalFee = min(baseFee + dynamicFee, feeCap)
dynamicFee = |currentTick − twapTick| × scalingFactor / 1e6
twapTick = TWAP over secondsAgo (default 600s)- ✓Stable conditions (tick ≈ TWAP): you pay only the base fee.
- ✓Volatile conditions: fee rises with the divergence, compensating LPs for elevated IL risk.
- ✓Capped: every pool that opts in has a feeCap (default 1%); the module never charges more than that.
- ✓Graceful fallback: if the pool lacks oracle data (low cardinality or fresh pool), the module returns 0 dynamic fee, so users still pay only the base fee.
Dynamic fees are disabled by default (defaultScalingFactor = 0). The fee manager opts a pool in by setting a feeCap and scalingFactor. For more on how TWAP works and BNB Chain cardinality requirements, see Oracles & TWAP.
Unstaked position fee (Slipstream)
To encourage LPs to stake their Slipstream NFTs in gauges (which makes their liquidity vote-weighted), the protocol applies an additional fee to unstaked positions when an alive gauge exists for the pool. The fee is taken at the swap level — unstaked LPs effectively pay a higher share of their fee to the gauge (which then flows to voters).
| Parameter | Value | Description |
|---|---|---|
| Default unstaked fee | 10% (100,000 / 1,000,000) | Of the swap fee paid to unstaked LPs. |
| Maximum unstaked fee | 50% | The fee manager can set a higher value up to 50% via the CustomUnstakedFeeModule. |
| Applies when | Gauge exists & is alive | No gauge or killed gauge → no unstaked fee. |
Fee resolution order
When a Slipstream swap fires, the pool computes its fee in this order:
1. Read baseFee from dynamicFeeConfig[pool]
- 420 → zero fee
- 0 → fall back to factory.tickSpacingToFee(tickSpacing)
- other → use it
2. Read dynamic fee config:
- Pool has scalingFactor > 0 → use per-pool config
- Otherwise → use defaultScalingFactor + defaultFeeCap
3. totalFee = min(baseFee + dynamicFee, feeCap)
4. If tx.origin is in the discounts map, apply discount
5. Charge totalFee on the swap inputAggregator and partner discounts
The dynamic fee module supports per-address discounts, set by the fee manager. Useful for whitelisting aggregators or partner contracts that route significant volume through Topaz. Discount is applied at swap time based on tx.origin, so it benefits the end user even when an aggregator is the msg.sender.
- ✓Discount precision: 1,000,000 (so 200,000 = 20% off).
- ✓Maximum discount: 50% (MAX_DISCOUNT = 500,000).
- ✓Discounts apply to the totalFee after caps. They cannot make a swap unprofitable for LPs by going negative.
Continue reading
Oracles & TWAP →
How the dynamic-fee module reads price history from the pool.
Staking in Gauges →
Skip the unstaked-position fee by staking your position NFT.
Gauge Voting →
How pool fees flow to voters via the FeesVotingReward contract.
Contracts →
DynamicSwapFeeModule, CustomSwapFeeModule, and CustomUnstakedFeeModule addresses.
