Skip to main content

Pair

The Pair account stores the state of a trading pair, including reserves, debt, and pricing information.

Account Structure

#[account]
pub struct Pair {
    // Token addresses
    pub token0: Pubkey,
    pub token1: Pubkey,
    pub lp_mint: Pubkey,

    pub token0_decimals: u8,
    pub token1_decimals: u8,

    // Pair parameters
    pub rate_model: Pubkey,
    pub swap_fee_bps: u16,
    pub half_life: u64,
    pub fixed_cf_bps: Option<u16>,
    
    // Virtual Reserves (for AMM pricing)
    pub reserve0: u64,
    pub reserve1: u64,

    // Cash Reserves (actual liquidity in vaults)
    pub cash_reserve0: u64,
    pub cash_reserve1: u64,
    
    // Price tracking (Double-EMA)
    pub last_price0_ema: LastPriceEMA,
    pub last_price1_ema: LastPriceEMA,
    pub last_update: u64,
    
    // Interest rates
    pub last_rate0: u64,
    pub last_rate1: u64,
    
    // Debt tracking
    pub total_debt0: u64,
    pub total_debt1: u64,
    pub total_debt0_shares: u128,  // u128 for precision
    pub total_debt1_shares: u128,
    
    // LP liquidity tracking
    pub total_supply: u64,
    
    // Collateral tracking
    pub total_collateral0: u64,
    pub total_collateral1: u64,

    pub params_hash: [u8; 32],
    pub version: u8,
    pub bump: u8,
    pub vault_bumps: VaultBumps,
    
    pub reduce_only: bool,  // Per-pair reduce-only mode
}

// Double-EMA price tracking
pub struct LastPriceEMA {
    pub symmetric: u64,    // Standard two-way EMA
    pub directional: u64,  // One-way EMA (snaps down, rises gradually)
}

Fields

token0
Pubkey
Mint address of token0. Must be less than token1 (canonical ordering).
token1
Pubkey
Mint address of token1. Must be greater than token0 (canonical ordering).
lp_mint
Pubkey
Mint address of the LP token for this pair.
token0_decimals
u8
Decimal places for token0.
token1_decimals
u8
Decimal places for token1.
rate_model
Pubkey
Address of the rate model account for this pair.
swap_fee_bps
u16
Swap fee in basis points (0-10000). Applied to all swaps.
half_life
u64
EMA half-life in seconds. Controls how quickly EMA prices adjust to spot prices.
fixed_cf_bps
Option<u16>
Optional fixed collateral factor in basis points. If Some, overrides dynamic CF calculation.
reserve0
u64
Virtual reserve of token0. Used for AMM pricing. Related to cash and debt by: reserve = cash_reserve + total_debt
reserve1
u64
Virtual reserve of token1. Used for AMM pricing. Related to cash and debt by: reserve = cash_reserve + total_debt
cash_reserve0
u64
Actual available liquidity of token0 in vaults. Protocol fees are tracked as vault_balance - cash_reserve.
cash_reserve1
u64
Actual available liquidity of token1 in vaults. Protocol fees are tracked as vault_balance - cash_reserve.
last_price0_ema
LastPriceEMA
Double-EMA price tracking for token0. Contains both symmetric (two-way) and directional (one-way) EMA values, scaled by NAD (1e9).
last_price1_ema
LastPriceEMA
Double-EMA price tracking for token1. Contains both symmetric (two-way) and directional (one-way) EMA values, scaled by NAD (1e9).
last_update
u64
Slot number of last state update.
last_rate0
u64
Last interest rate for token0, scaled by NAD (1e9).
last_rate1
u64
Last interest rate for token1, scaled by NAD (1e9).
total_debt0
u64
Total debt of token0 across all positions (including accrued interest).
total_debt1
u64
Total debt of token1 across all positions (including accrued interest).
total_debt0_shares
u128
Total debt shares for token0. Used for proportional debt tracking. Stored as u128 for precision.
total_debt1_shares
u128
Total debt shares for token1. Used for proportional debt tracking. Stored as u128 for precision.
total_supply
u64
Total supply of LP tokens. Includes MIN_LIQUIDITY that is permanently locked.
reduce_only
bool
Per-pair reduce-only mode. When enabled, prevents new borrows and liquidity additions.
total_collateral0
u64
Total collateral of token0 across all positions.
total_collateral1
u64
Total collateral of token1 across all positions.
params_hash
[u8; 32]
SHA256 hash of pair parameters. Used for PDA derivation.
version
u8
Protocol version. Currently 1.
bump
u8
PDA bump seed.

PDA Derivation

The pair account is a PDA derived from:
[PAIR_SEED_PREFIX, token0, token1, params_hash]
Where PAIR_SEED_PREFIX = b"gamm_pair".

Reserve Relationship

The three reserve types are related by the solvency invariant:
reserve (virtual) = cash_reserve + total_debt

Fee Surplus

Protocol fees are tracked implicitly as the surplus between actual vault balances and accounted cash reserves: Fee Surplus=RvaultRcash\text{Fee Surplus} = R_{\text{vault}} - R_{\text{cash}} This surplus accumulates from two sources:
Fee SourceMechanism
Swap FeesFutarchy portion transferred immediately; LP portion added to reserves
Interest FeesCash-covered portion reduces cash_reserve, creating vault surplus
The surplus is claimable by the futarchy governance system via claim_protocol_fees.

Interest Fee Handling

When interest accrues and cash reserves are limited, the protocol uses a cash coverage mechanism:
cash_covered_fee = min(protocol_fee, cash_reserve)
uncovered_fee = protocol_fee - cash_covered_fee
  • Cash covered fee: Reduces cash_reserve, creating immediate surplus
  • Uncovered fee: Added to reserve (virtual); LPs get temporary claim until borrowers repay
This ensures the state transition invariant ΔR_virtual = ΔR_cash + ΔR_debt always holds.

Key Methods

spot_price0_nad() -> u64

Returns the current spot price of token0 in token1 terms, scaled by NAD.

spot_price1_nad() -> u64

Returns the current spot price of token1 in token0 terms, scaled by NAD.

ema_price0_nad() -> u64

Returns the symmetric EMA price of token0 in token1 terms, scaled by NAD.

ema_price1_nad() -> u64

Returns the symmetric EMA price of token1 in token0 terms, scaled by NAD.

directional_ema_price0_nad() -> u64

Returns the directional (one-way) EMA price of token0, scaled by NAD.

directional_ema_price1_nad() -> u64

Returns the directional (one-way) EMA price of token1, scaled by NAD.

k() -> u128

Returns the constant product reserve0 * reserve1.

get_max_debt_and_cf_bps_for_collateral() -> (u64, u16, u16)

Calculates maximum debt, borrow CF, and liquidation CF for a given collateral amount using pessimistic pricing and price impact simulation.

update() -> Result<()>

Updates both symmetric and directional EMA prices, interest rates, and applies accrued interest to reserves and debt.