Skip to main content

Swap

Performs a token swap using the constant-product AMM formula.

Accounts

pair
Account<Pair>
required
The pair account. PDA derived from [PAIR_SEED_PREFIX, token0, token1, params_hash].
token_in_vault
Account<TokenAccount>
required
The pair vault for the input token. Must be owned by pair PDA.
token_out_vault
Account<TokenAccount>
required
The pair vault for the output token. Must be owned by pair PDA.
user_token_in_account
Account<TokenAccount>
required
User’s input token account. Must have sufficient balance.
user_token_out_account
Account<TokenAccount>
required
User’s output token account. Will receive swapped tokens.
user
Signer
required
The user performing the swap. Must be a signer.

Arguments

amount_in
u64
required
Amount of input token to swap. Must be greater than 0.
min_amount_out
u64
required
Minimum amount of output token to receive. Used for slippage protection.

Formula

The swap uses the constant-product formula:
Δy = (Δx * y) / (x + Δx)
Where:
  • Δx = input amount after fees
  • y = output reserve
  • x = input reserve
  • Δy = output amount

Fee Structure

Swap fees are split between liquidity providers and the protocol: Swap Fee=amount_in×swap_fee_bps10000\text{Swap Fee} = \text{amount\_in} \times \frac{\text{swap\_fee\_bps}}{10000}

Fee Distribution

RecipientCalculationDestination
Futarchyswap_fee × revenue_share.swap_bps / 10000Transferred immediately to authority token account
LPsswap_fee - futarchy_feeAdded to virtual and cash reserves

Reserve Updates

The LP portion of the fee increases both virtual and cash reserves:
amount_in_with_lp_fee = amount_in - futarchy_fee
new_reserve_in = reserve_in + amount_in_with_lp_fee
cash_reserve_in = cash_reserve_in + amount_in_with_lp_fee
This means:
  • LP fees compound directly into pool liquidity
  • Futarchy fees are extracted immediately and do not affect reserves
  • The constant product invariant is preserved or increased after each swap
use anchor_lang::prelude::*;
use omnipair::program::Omnipair;

let swap_args = SwapArgs {
    amount_in: 100 * 10u64.pow(token_in_decimals),
    min_amount_out: 195 * 10u64.pow(token_out_decimals),
};

let accounts = Swap {
    pair: pair_pda,
    token_in_vault: token0_vault,
    token_out_vault: token1_vault,
    user_token_in_account: user_token0_account,
    user_token_out_account: user_token1_account,
    user: user.key(),
    // ... other accounts
};

let ctx = CpiContext::new(program_id, accounts);
omnipair::cpi::swap(ctx, swap_args)?;
{
  "signature": "5VERv8NMvzbJMEkV8xnrLkEaWRtSz9CosKDYjCJjBRnbJLgp8uirBgmG5Rk88jH2k9f4w3JHFf7Pjsk3bWqeXrC",
  "slot": 123456789
}