Skip to main content

Documentation Index

Fetch the complete documentation index at: https://tech.ramses.xyz/llms.txt

Use this file to discover all available pages before exploring further.

Fee Architecture

Ramses V3 uses tick spacing to determine pool characteristics and allows governance-controlled fee adjustment per pool, diverging from Uniswap V3’s fixed fee tiers.
While Uniswap V3 uses predefined static fee tiers that are immutable after pool creation, Ramses V3 decouples tick spacing from fees. Each tick spacing has a default initial fee, but governance can adjust the swap fee of any pool at any time via factory.setFee(pool, newFee).

Fee Model

How Fees Are Set

Fees in Ramses V3 are not determined by an on-chain algorithm during swaps. Instead:
  1. Each tick spacing has a default initial fee set in the factory’s tickSpacingInitialFee mapping
  2. When a pool is created, it inherits the initial fee for its tick spacing
  3. Governance can adjust any pool’s fee at any time via RamsesV3Factory.setFee(pool, fee)
  4. The pool stores its current fee and applies it to all swaps
// Factory sets the fee for a specific pool (governance only)
function setFee(address _pool, uint24 _fee) external;

// Pool stores and exposes its fee
function fee() external view returns (uint24);

Default Tick Spacing / Fee Mapping

Tick SpacingDefault FeeMin Price MovementTypical Use CaseExample Pairs
10.01% (100)0.01%Ultra-stableStablecoin pairs (USDC/USDT)
50.025% (250)0.05%Highly correlatedPegged assets
100.05% (500)0.10%Correlated assetsETH/wstETH, BTC/WBTC
500.30% (3000)0.50%Standard volatilityMost token pairs
1001.00% (10000)1.00%Higher volatilityLess liquid pairs
2002.00% (20000)2.00%High volatilityExotic/new tokens
Fees are denominated in parts per million (1/1,000,000). For example, 100 = 0.01%, 3000 = 0.30%.

Swap Fee Distribution

LP Fee Collection

Swap fees are distributed pro-rata to all in-range liquidity at the time of the swap:
  • In-Range Positions: Positions where tickLower ≤ currentTick < tickUpper earn fees
  • Out-of-Range Positions: Positions outside the active price earn 0 fees
  • Fee Accrual: Fees accumulate continuously as long as a position remains in-range
  • Manual Collection: LPs must call collect() to claim accumulated fees (not auto-compounded)
// Collect accumulated fees for a position (via pool directly)
function collect(
    address recipient,
    uint256 index,
    int24 tickLower,
    int24 tickUpper,
    uint128 amount0Requested,
    uint128 amount1Requested
) external returns (uint128 amount0, uint128 amount1);

Protocol Fee Split (feeProtocol)

The feeProtocol setting determines what percentage of swap fees are routed to the protocol (FeeCollector) vs staying with LPs. It is also denominated in parts per million (1/1,000,000). The fee split differs dramatically based on whether a pool has a gauge:

Gauged Pools (100% to voters)

When a gauge is attached to a pool via the Voter contract, gaugeFeeSplitEnable sets the pool’s feeProtocol to 1,000,000 (100%). This means all swap fees are routed to the FeeCollector, which distributes them to the pool’s FeeDistributor for xRAM voters to claim. LPs on gauged pools earn emissions from the gauge instead of swap fees.

Non-Gauged Pools (95% to LPs, 5% to treasury)

Pools without a gauge use the factory’s default feeProtocol of 50,000 (5%). This means:
  • 95% of swap fees go to LPs (collected via pool.collect())
  • 5% goes to the FeeCollector, which sends it to the treasury (since there’s no gauge/FeeDistributor)

Fee Flow

Swap → Pool Collects Fee (per pool.fee())

Split Based on feeProtocol Setting

Gauged Pool (feeProtocol = 1,000,000):
   └─→ 100% → FeeCollector → FeeDistributor → xRAM Voters claim
       (minus treasuryFees share → Treasury)

Non-Gauged Pool (feeProtocol = 50,000):
   ├─→ 95% → LPs (collected via pool.collect())
   └─→ 5%  → FeeCollector → Treasury (no FeeDistributor exists)

Protocol Fee Distribution

Contract: FeeCollector.sol When collectProtocolFees(pool) is called, the FeeCollector:
  1. Calls pool.collectProtocol() to pull the protocol’s share of fees
  2. If the pool has no gauge (or gauge is not alive): sends everything directly to the treasury
  3. If the pool has a live gauge: splits fees between the FeeDistributor (for voters) and the treasury based on the treasuryFees ratio
ScenarioFeeDistributor (Voters)Treasury
Gauged pool100% - treasuryFeestreasuryFees share
Non-gauged poolN/A (no FeeDistributor)100% of protocol portion

FeeCollector Interface

interface IFeeCollector {
    // Collect protocol fees from a pool and distribute to FeeDistributor + Treasury
    function collectProtocolFees(address pool) external;

    // Treasury configuration (governance)
    function setTreasury(address newTreasury) external;
    function setTreasuryFees(uint256 _treasuryFees) external;

    // Read state
    function treasury() external returns (address);
    function treasuryFees() external returns (uint256);
}

Distribution Cycle

  1. Accumulation: Fees accumulate in pools continuously
  2. Collection: Anyone can call collectProtocolFees(pool) to trigger collection and distribution (permissionless)
  3. Voter Claims: On gauged pools, xRAM holders claim from FeeDistributor proportional to their voting weight
  4. LP Claims: On non-gauged pools, LPs collect their 95% share anytime via collect()

Integrator Considerations

Querying Current Fees

Always query the current fee before estimating swap outputs:
// Get current fee for a pool
uint24 currentFee = pool.fee();

// Fee is denominated in parts per million (1/1,000,000)
// Example: 100 = 0.01%, 3000 = 0.30%

Slippage Protection

Since fees can be changed by governance, set appropriate slippage tolerance:
// Bad: Hardcoded fee assumption
uint256 expectedOut = amountIn * (1000000 - 3000) / 1000000; // Assumes 0.30%

// Good: Query current fee
uint24 currentFee = pool.fee();
uint256 expectedOut = amountIn * (1000000 - currentFee) / 1000000;
uint256 minOut = expectedOut * 99 / 100; // 1% slippage tolerance

Events

// Emitted by factory when a pool's fee is changed
event FeeAdjustment(address pool, uint24 newFee);

// Emitted by factory when default protocol fee changes
event SetFeeProtocol(uint24 feeProtocolOld, uint24 feeProtocolNew);

// Emitted by factory when pool-specific protocol fee changes
event SetPoolFeeProtocol(address pool, uint24 feeProtocolOld, uint24 feeProtocolNew);

Additional Resources