Skip to main content

Fee Structure

Every DAMM v2 pool has a three-layer fee:
Gross Input Amount
  └── Trading Fee (base_fee + dynamic_fee, capped at 99%)
        ├── Protocol Fee  (protocol_fee_percent % of trading fee)
        │     └── Referral Fee (portion of protocol fee if referral provided)
        └── LP Fee (remainder)
              └── [Compounding only] compoundingFeeBps / 10000 → pool reserves
All fee rates are stored as numerators over a fixed denominator (FEE_DENOMINATOR = 1,000,000), giving 6 decimal places of precision (e.g., fee rate of 2500 = 0.25%).

Base Fee Modes

DAMM v2 supports five base fee modes. The mode is encoded in the BaseFeeInfo bytes of the pool config.

1. FeeTimeSchedulerLinear

Fee decays linearly from a starting value over time:
fee = cliff_fee_numerator - (passed_period × reduction_factor)
passed_period = (current_point - activation_point) / period_frequency
Parameters:
FieldDescription
cliff_fee_numeratorStarting fee rate (numerator over 1,000,000)
number_of_periodHow many periods to decay over
period_frequencySlots or seconds per period
reduction_factorHow much to reduce per period
Use when: You want a high launch fee that smoothly steps down to a target fee over time (e.g., start at 5%, decay to 0.25% over 10 periods).

2. FeeTimeSchedulerExponential

Fee decays exponentially from a starting value over time:
fee = cliff_fee_numerator × (1 - reduction_factor / 10_000) ^ passed_period
passed_period = (current_point - activation_point) / period_frequency
Use when: You want a steep initial decay that flattens as the fee approaches the floor — more natural for launch mechanics.

3. FeeMarketCapSchedulerLinear

Fee decays as the price moves (market-cap based). Instead of time, passed_period is computed from price movement:
fee = cliff_fee_numerator - (passed_period × reduction_factor)
passed_period = (current_sqrt_price - init_sqrt_price) × 10_000 / init_sqrt_price / sqrt_price_step_bps
Use when: You want the fee to decay as the token price increases — rewarding price discovery with lower fees at higher market caps.

4. FeeMarketCapSchedulerExponential

Same as linear market cap scheduler but with exponential decay:
fee = cliff_fee_numerator × (1 - reduction_factor / 10_000) ^ passed_period
Use when: Similar to linear market cap scheduler but with a smoother decay curve.

5. RateLimiter

A rate limiter that enforces that only one swap can happen per transaction (CPI guard). Used to prevent sandwich attacks and MEV extraction on specific launch configurations.
RateLimiter mode validates that exactly one swap instruction exists in the transaction. If more than one is detected, the transaction fails with FailToValidateSingleSwapInstruction.

Dynamic Fee

On top of the base fee, an optional dynamic fee component adjusts fee upward during periods of high price volatility:
total_fee = base_fee + dynamic_fee
total_fee is capped at MAX_FEE_NUMERATOR (99%)
Dynamic fee uses a volatility accumulator (similar to DLMM’s dynamic fee mechanism):
ParameterDescription
bin_stepPrice sensitivity step (bps)
filter_periodTime window for volatility averaging
decay_periodHow quickly the volatility accumulator decays
reduction_factorRate of decay (0–10000)
max_volatility_accumulatorCap on accumulated volatility
variable_fee_controlScalar multiplier for the fee component
When dynamicFeeEnabled = false, the dynamic fee is always 0.

Protocol & Referral Fee

ParameterDescription
protocol_fee_percentPercentage of trading fee that goes to the protocol (0–100)
referral_fee_percentPercentage of protocol fee that goes to the referral provider (0–100)
Protocol fees are accumulated in the pool and claimed by the admin via claimProtocolFee.

Fee Constraints

ConstraintValue
Max total fee99% (MAX_FEE_NUMERATOR = 990_000)
Fee denominator1_000_000
Min fee (floor)Set by get_min_fee_numerator() per mode
compoundingFeeBps range0–10000 (only for mode 2)

Updating Fees

Pool fees can be updated post-creation via the updatePoolFees instruction (operator/admin only). The EvtUpdatePoolFees event is emitted on any fee update.
Fee updates are restricted — only the operator or admin can call updatePoolFees. Certain constraints (e.g., cannot reduce dynamic fee if the volatility accumulator has pending state) apply.