Coffer Docs
For Traders

Fees

Base fees, max sell-off windows, and dynamic fees

A swap can have two pool-level charges: a base fee from the token you sell, and an optional dynamic fee within an enabled max sell-off window, deducted from the token you receive. LPs retain part of the base fee; the protocol receives the rest and the entire dynamic fee. Network fees, token transfer fees and account-creation costs are separate.

Base fee and protocol share

Without token transfer fees, selling 1 token with a 0.3% base fee leaves 0.997 token for curve pricing. If the protocol share is 20%, it takes 20% of the fee, not 20% of the trade. The remaining fee increases LP-owned reserves.

Rates vary by pool and can be updated. The 20% share is an example; use the pool's current settings when quoting.

Exact calculation

SettingEncodingRange
Base feeswap_fee_rate, scale 1_000_0000–10%; 3_000 means 0.3%
Protocol shareprotocol_fee_rate, scale 10_0000–100%; 2_000 means 20%

The base fee and its protocol share each round up to a raw input-token unit: 3.998 rounds to 4. For tiny fees, rounding can alter the effective protocol percentage and leave zero LP income. For quotes, use the exact fee pipeline and reserve accounting.

Max sell-off window

A token's max sell-off window caps how much all traders together can sell into one pool over time. The pool admin sets its duration and capacity. The counter uses input received after token transfer fees, before the pool's base fee. Older sales gradually fade from the counter. Exceeding the cap rejects the swap, regardless of accepted fees or slippage.

Window capacity

After bucket rotation and any required rescaling:

cap = floor(snapshotVirtualBalance × maxSelloffPct / 10,000)
effectiveBefore = floor(previousSales × (period − elapsed) / period) + currentSales
require effectiveBefore + receivedAmountIn <= cap

The window counts received input before the pool's base fee. The previous bucket fades linearly while the current bucket accumulates sales. Reusing a virtual-balance snapshot prevents sales from increasing their own cap. Liquidity changes, range updates, and buys rescale the snapshot and counters together. Selling cannot inflate its own capacity. At rotation, carryover is rescaled when the snapshot basis changes.

max_selloff_pct = 0 disables both cap and dynamic fee. With a positive cap, variable_fee_slope_high_pct = 0 disables only the fee. Updating the policy does not reset accumulated sales; the next swap uses the new settings. Permissions are described in Pool Controls.

Dynamic fees

Dynamic fees are an optional part of the max sell-off window. The rate is zero through the threshold. Just above it, the rate starts at the configured low rate, then rises toward the high rate at full capacity. A kink can add a middle rate and change how steeply the fee rises. These settings apply to the token being sold into the pool.

The fee is deducted from the output token and reserved for the protocol. Only output produced by the above-threshold part of a sale is charged. The rate displayed at the current fill is a point on the curve; the swap's charge uses the rates traversed by that trade. See the annotated example.

Fee curve and integer calculation

ParameterMeaning
ThresholdWindow fill through which the rate is zero
LowRate immediately above the threshold
KinkAbsolute window fill where the middle rate applies
MiddleRate at the kink
HighRate at 100% window fill

The slope_* fields store rates. Rates and threshold use scale 10_000; the kink uses whole percentages of total window capacity. For example, threshold 8_000 and kink 90 mean 80% and 90% fill. The kink sits halfway through the remaining 80–100% interval. Kink 0 gives one straight line from low to high, ignoring the middle rate. A nonzero low rate produces a jump immediately above the threshold.

Only output attributable to selling above the threshold is taxed. The implementation divides that span into four segments, prices each segment's output, and applies its average configured rate. The segment contributions are summed, then the total is rounded upward once and bounded by gross output.

vaultOutput = grossCurveOutput − dynamicFee
userOutput = vaultOutput − tokenTransferFee(vaultOutput)
minimum_amount_out is checked against userOutput

Segmentation reduces the advantage of splitting a sale, but remains an approximation: partitioning, rounding, state, and elapsed time can change the total fee. Use a version-compatible full swap quote, not gross output multiplied by the final window rate.

The LP portion of the base fee stays in trading reserves. The protocol portion and dynamic fees accumulate in per-token protocol-fee balances; LPs cannot currently claim dynamic fees. See fee storage and the planned LP claim mechanism.

On this page