Fee Module

Manages the discrete price points and liquidity distribution across the price curve.

Source: sources/libraries/fee_helper.move | sources/libraries/pair_parameter_helper.move

Modules: ferra_damm::fee_helper | ferra_damm::pair_parameter_helper

The Fee Module is the core fee calculation engine of the Ferra DAMM protocol. It combines two independent fee mechanisms — Fee Scheduler (time-based base fee) and Dynamic Fee (volatility-based variable fee) — to determine the total fee for each swap.


Fee Architecture Overview

┌──────────────────────────────────────────────────────────────────┐
│                        Total Fee                                 │
│                                                                  │
│    total_fee = base_fee + variable_fee                           │
│    capped at MAX_FEE (50% = 500,000,000)                         │
│                                                                  │
│  ┌────────────────────────┐  ┌─────────────────────────────────┐ │
│  │      Base Fee           │  │        Variable Fee            │ │
│  │   (Fee Scheduler)       │  │      (Dynamic Fee)             │ │
│  │                         │  │                                │ │
│  │  If scheduler enabled:  │  │  If dynamic fee enabled:       │ │
│  │    Linear or Exponential│  │    Based on volatility         │ │
│  │    decay from cliff_fee │  │    accumulator × tick_spacing  │ │
│  │                         │  │                                │ │
│  │  If scheduler disabled: │  │  If dynamic fee disabled:      │ │
│  │    Static fee_rate      │  │    0                           │ │
│  └─────────────────────────┘  └────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────┘

PairParameters

The PairParameters struct is stored in each Pool and holds all fee state.

Static Fee Fields

Field
Type
Description

fee_rate

u64

Base fee rate when scheduler is disabled. Precision: 10^9 (e.g., 2,500,000 = 0.25%)

current_sqrt_price

u128

Current pool sqrt price (Q64.64 format)

current_tick_index

I32

Current pool tick index

tick_spacing

u32

Pool tick spacing

activation_timestamp

u64

When pool/fee scheduler activates (ms)

Fee Scheduler Fields

Field
Type
Description

fee_scheduler_mode

u8

0 = Linear, 1 = Exponential

enabled_fee_scheduler

bool

Whether fee scheduler is active

cliff_fee_numerator

u64

Initial maximum fee. Must be > 0, max 500,000,000 (50%)

number_of_period

u16

Total number of reduction periods

period_frequency

u64

Duration of each period (ms)

fee_scheduler_reduction_factor

u64

Linear: absolute reduction per period. Exponential: decay factor in basis points

Dynamic Fee Config Fields

Field
Type
Description

enabled_dynamic_fee

bool

Whether dynamic fee is active

filter_period

u16

Seconds before updating volatility reference. Must be <= decay_period

decay_period

u16

Seconds for full volatility reset. Max: 4095. Must be > 0

reduction_factor

u16

Decay rate in basis points (1-10000). Used to reduce volatility_reference

variable_fee_control

u32

Scaling factor for variable fee. Max: 2,000,000

max_volatility_accumulator

u32

Cap on volatility accumulator. Max: 1,048,575 (0xFFFFF)

Dynamic Fee State Fields (Updated on Every Swap)

Field
Type
Description

volatility_accumulator

u32

Current accumulated volatility level

volatility_reference

u32

Reference volatility from previous period

id_reference

I32

Reference tick index for delta calculation

time_of_last_update

u64

Timestamp of last volatility update


Fee Scheduler (Base Fee)

The Fee Scheduler provides time-based fee reduction from an initial cliff fee down to the base fee rate.

How It Works

  1. Pool is created with activation_timestamp and cliff_fee_numerator

  2. Before activation: fee = cliff_fee_numerator (maximum fee)

  3. After activation: fee decreases over number_of_period periods

  4. After all periods complete: fee = fee_rate (base fee)

get_total_fee_rate

Returns the total fee rate combining base fee and variable fee.

Returns: Total fee rate, capped at MAX_FEE (500,000,000 = 50%)


get_base_fee

Returns the current base fee based on the fee scheduler state.

Logic:

  • If scheduler disabled or activation_timestamp == 0: returns static fee_rate

  • If before activation or at max period: returns fee_rate or cliff fee accordingly

  • Otherwise: calculates fee based on mode (linear or exponential)


Linear Mode (fee_scheduler_mode = 0)

Fee decreases by a fixed amount each period.

Formula:

Example:

  • cliff_fee_numerator = 100,000,000 (10%)

  • fee_scheduler_reduction_factor = 9,000,000 (0.9% per period)

  • number_of_period = 10

  • Result: Fee decreases from 10% → 9.1% → 8.2% → ... → 1% over 10 periods

Validation: Total reduction (reduction_factor × number_of_period) must not exceed cliff_fee_numerator


Exponential Mode (fee_scheduler_mode = 1)

Fee decays exponentially using a compound decay factor.

Formula:

Uses Q64.64 fixed-point arithmetic for precision.

Example:

  • cliff_fee_numerator = 100,000,000 (10%)

  • reduction_factor = 2000 (20% decay per period)

  • number_of_period = 10

  • Result: 10% → 8% → 6.4% → 5.12% → ... asymptotically approaching 0

Validation: reduction_factor must be > 0 and < 10000 (basis_point_max)


get_current_number_period

Calculates which period we are currently in.

Logic:

  • Before activation: returns number_of_period (maximum penalty = cliff fee)

  • After activation: period = (elapsed_time + period_frequency - 1) / period_frequency

  • Capped at number_of_period


Fee Scheduler Query Functions

Function
Returns
Description

is_fee_scheduler_enabled(params)

bool

Whether fee scheduler is active

get_cliff_fee_numerator(params)

u64

Initial cliff fee

get_scheduler_number_of_period(params)

u16

Total periods

get_scheduler_period_frequency(params)

u64

Period duration (ms)

get_scheduler_reduction_factor(params)

u64

Reduction per period

get_activation_timestamp(params)

u64

Activation time (ms)

get_fee_scheduler_mode(params)

u8

0 = Linear, 1 = Exponential


Dynamic Fee (Variable Fee)

The Dynamic Fee mechanism automatically adjusts fees based on real-time market volatility. During high volatility (large price movements), fees increase to protect LPs. During calm markets, fees decay back to zero.

How It Works

  1. Each swap updates the volatility accumulator based on price movement

  2. Variable fee is calculated from the volatility accumulator

  3. After filter_period, the reference volatility decays by reduction_factor

  4. After decay_period, volatility reference resets to zero

get_variable_fee

Calculates the current variable fee from volatility state.

Formula:

Example:

  • volatility_accumulator = 100

  • tick_spacing = 60

  • variable_fee_control = 1000

  • prod = 100 × 60 = 6000

  • variable_fee = (6000 × 6000 × 1000 + 99) / 100 = 360,000,000 (36%)


Volatility Update Flow

On each swap, the following functions are called in sequence:

update_volatility_parameters

Entry point called on every swap to update volatility state.

Flow:

  1. update_references(params, timestamp) — handles time-based decay

  2. update_volatility_accumulator(params, active_id) — adds new volatility


update_references

Handles time-based volatility decay.

Logic:


update_volatility_accumulator

Adds new volatility based on price movement since reference.

Formula:

The × 10 factor is the volatility per bin constant.


get_delta_id

Calculates the absolute tick distance from the reference, normalized by tick spacing.

Formula: |active_id - id_reference| / tick_spacing


Dynamic Fee Query Functions

Function
Returns
Description

is_dynamic_fee_enabled(params)

bool

Whether dynamic fee is active

get_dynamic_filter_period(params)

u16

Filter period (seconds)

get_dynamic_decay_period(params)

u16

Full decay period (seconds)

get_dynamic_reduction_factor(params)

u16

Decay rate in basis points

get_dynamic_variable_fee_control(params)

u32

Scaling factor

get_dynamic_max_volatility_accumulator(params)

u32

Max volatility cap

get_volatility_accumulator(params)

u32

Current volatility level

get_volatility_reference(params)

u32

Reference volatility

get_id_reference(params)

I32

Reference tick index

get_time_of_last_update(params)

u64

Last update timestamp

get_active_id(params)

I32

Current tick index


Fee Helper (Validation & Calculation)

The fee_helper module provides fee parameter validation and fee amount calculation utilities.

validate_dynamic_fee_parameters

Validates dynamic fee configuration.

Validation Rules:

  • filter_period <= decay_period

  • 0 < decay_period <= 4095

  • 0 < reduction_factor <= 10000

  • variable_fee_control <= 2,000,000

  • 0 < max_volatility_accumulator <= 1,048,575


validate_base_fee

Validates fee scheduler configuration to ensure the minimum fee after all periods is above the threshold.

Validation Rules:

  • fee_rate <= MAX_FEE_RATE (10%)

  • cliff_fee_numerator > 0 and <= MAX_FEE (50%)

  • number_of_period > 0

  • period_frequency > 0

  • Linear mode: reduction_factor × number_of_period <= cliff_fee_numerator

  • Exponential mode: 0 < reduction_factor < 10000

  • Minimum fee after all periods must be >= MIN_FEE_NUMERATOR (100,000 = 0.01%)


Fee Amount Calculation Functions

get_fee_amount_inclusive

Extracts fee from an amount that already includes fees.

Formula: fee = amount_with_fees × total_fee_rate / PRECISION (rounds up)

Example: 1% fee on 10,000 → fee = 100


get_fee_amount_exclusive

Calculates fee to add on top of an amount.

Formula: fee = amount × total_fee_rate / (PRECISION - total_fee_rate) (rounds up)

Example: 1% fee on 9,900 → fee = 100 (total becomes ~10,000)


get_composition_fee

Calculates composition fee using a quadratic formula for balanced liquidity incentives.

Formula: fee = amount × total_fee_rate × (total_fee_rate + PRECISION) / PRECISION²


get_protocol_fee_amount

Calculates the protocol's share of a fee amount.

Formula: protocol_fee = fee_amount × protocol_share / BASIS_POINT_MAX (rounds down)

Example: 25% protocol share on 100 fee → protocol_fee = 25


General Query Functions

Function
Returns
Description

get_tick_spacing(params)

u32

Pool tick spacing

get_fee_rate(params)

u64

Static base fee rate

get_current_sqrt_price(params)

u128

Current sqrt price

get_current_tick_index(params)

I32

Current tick index


Constants Reference

Constant
Value
Description

PRECISION

1,000,000,000 (10^9)

Fee precision denominator

SQUARED_PRECISION

10^18

Precision squared for composition fee

MAX_FEE_RATE

100,000,000 (10%)

Maximum base fee rate

MAX_FEE

500,000,000 (50%)

Maximum total fee (base + dynamic)

MIN_FEE_NUMERATOR

100,000 (0.01%)

Minimum fee after scheduler

BASIS_POINT_MAX

10,000

Basis point denominator

MAX_VARIABLE_FEE_CONTROL

2,000,000

Max scaling factor

MAX_DECAY_PERIOD

4,095 (0xFFF)

Max decay period in seconds

MAX_VOLATILITY_ACCUMULATOR

1,048,575 (0xFFFFF)

Max volatility level

FEE_SCHEDULER_MODE_LINEAR

0

Linear fee scheduler mode

FEE_SCHEDULER_MODE_EXPONENTIAL

1

Exponential fee scheduler mode

COLLECT_FEE_MODE_ON_BOTH

0

Collect fee on input token

COLLECT_FEE_MODE_ON_QUOTE

1

Collect fee on quote token


Error Codes

fee_helper

Code
Constant
Description

502

E_FEE_TOO_HIGH

Fee rate exceeds maximum

505

E_INVALID_DECAY_PERIOD

Decay period is 0 or exceeds max

506

E_INVALID_REDUCTION_FACTOR

Reduction factor is 0 or exceeds basis point max

507

E_INVALID_VARIABLE_FEE_CONTROL

Variable fee control exceeds max

508

E_INVALID_MAX_VOLATILITY_ACCUMULATOR

Max volatility is 0 or exceeds max

509

E_INVALID_PARAMETER

Filter period > decay period

510

E_INVALID_FEE_SCHEDULER

Invalid scheduler config (zero values)

511

E_LINEAR_REDUCTION_TOO_HIGH

Linear total reduction exceeds cliff fee

512

E_MIN_FEE_TOO_LOW

Minimum fee after scheduler below threshold

pair_parameter_helper

Code
Constant
Description

900

E_INVALID_PARAMETER

Invalid volatility parameter (exceeds 20 bits)


Usage Examples

Reading Current Fee State

Fee Scheduler Timeline

Dynamic Fee Response to Volatility

Last updated