riptide-amm-math 2.0.0

The Riptide program math library
Documentation
use super::SingleSideLiquidity;

use super::error::{CoreError, INVALID_ORACLE_DATA};

use super::oracle::{build_liquidity, build_price, consume_liquidity, OraclePayload};

use borsh::BorshDeserialize;

use riptide_amm_macros::alias;

#[cfg(feature = "wasm")]
use riptide_amm_macros::wasm_expose;

#[derive(Debug, Clone, Copy, Eq, PartialEq)]
#[cfg_attr(feature = "wasm", wasm_expose)]
pub enum QuoteType {
    TokenAExactIn,
    TokenAExactOut,
    TokenBExactIn,
    TokenBExactOut,
}

impl QuoteType {
    pub(crate) fn new(amount_is_token_a: bool, amount_is_input: bool) -> Self {
        match (amount_is_token_a, amount_is_input) {
            (true, true) => QuoteType::TokenAExactIn,
            (true, false) => QuoteType::TokenAExactOut,
            (false, true) => QuoteType::TokenBExactIn,
            (false, false) => QuoteType::TokenBExactOut,
        }
    }

    pub fn exact_in(&self) -> bool {
        matches!(self, QuoteType::TokenAExactIn | QuoteType::TokenBExactIn)
    }

    pub fn exact_out(&self) -> bool {
        matches!(self, QuoteType::TokenAExactOut | QuoteType::TokenBExactOut)
    }

    #[alias(output_is_token_b, a_to_b)]
    pub fn input_is_token_a(&self) -> bool {
        matches!(self, QuoteType::TokenAExactIn | QuoteType::TokenBExactOut)
    }

    #[alias(output_is_token_a, b_to_a)]
    pub fn input_is_token_b(&self) -> bool {
        matches!(self, QuoteType::TokenBExactIn | QuoteType::TokenAExactOut)
    }
}

#[derive(Debug, Clone, Copy, Eq, PartialEq)]
#[cfg_attr(feature = "wasm", wasm_expose)]
pub struct Quote {
    pub amount_in: u64,
    pub amount_out: u64,
    pub quote_type: QuoteType,
}

#[derive(Default, Debug, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "wasm", wasm_expose)]
pub struct Prices {
    pub oracle_price_q64_64: u128,
    pub best_bid_price_q64_64: u128,
    pub best_ask_price_q64_64: u128,
    pub ask_spread_per_m: i32,
    pub bid_spread_per_m: i32,
}

#[derive(Default, Debug, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "wasm", wasm_expose)]
pub struct Price {
    pub oracle_price_q64_64: u128,
    pub best_price_q64_64: u128,
    pub spread_per_m: i32,
}

#[cfg_attr(feature = "wasm", wasm_expose)]
pub fn quote_exact_in(
    amount: u64,
    amount_is_token_a: bool,
    oracle_data: &[u8],
    reserves_a: u64,
    reserves_b: u64,
    skew_cliff_min_per_m: i32,
    skew_cliff_max_per_m: i32,
) -> Result<Quote, CoreError> {
    let mut oracle_data = oracle_data;
    let payload = OraclePayload::deserialize(&mut oracle_data).map_err(|_| INVALID_ORACLE_DATA)?;
    let quote_type = QuoteType::new(amount_is_token_a, true);
    let liquidity = build_liquidity(
        &payload,
        quote_type,
        reserves_a,
        reserves_b,
        skew_cliff_min_per_m,
        skew_cliff_max_per_m,
    )?;
    consume_liquidity(amount, quote_type, &liquidity)
}

#[cfg_attr(feature = "wasm", wasm_expose)]
pub fn quote_exact_out(
    amount: u64,
    amount_is_token_a: bool,
    oracle_data: &[u8],
    reserves_a: u64,
    reserves_b: u64,
    skew_cliff_min_per_m: i32,
    skew_cliff_max_per_m: i32,
) -> Result<Quote, CoreError> {
    let mut oracle_data = oracle_data;
    let payload = OraclePayload::deserialize(&mut oracle_data).map_err(|_| INVALID_ORACLE_DATA)?;
    let quote_type = QuoteType::new(amount_is_token_a, false);
    let liquidity = build_liquidity(
        &payload,
        quote_type,
        reserves_a,
        reserves_b,
        skew_cliff_min_per_m,
        skew_cliff_max_per_m,
    )?;
    consume_liquidity(amount, quote_type, &liquidity)
}

#[cfg_attr(feature = "wasm", wasm_expose)]
pub fn bid_price(
    oracle_data: &[u8],
    liquidity: SingleSideLiquidity,
    reserves_a: u64,
    reserves_b: u64,
) -> Result<Price, CoreError> {
    let mut oracle_data = oracle_data;
    let payload = OraclePayload::deserialize(&mut oracle_data).map_err(|_| INVALID_ORACLE_DATA)?;
    build_price(
        &liquidity,
        &payload.data,
        QuoteType::TokenAExactIn,
        reserves_a,
        reserves_b,
    )
}

#[cfg_attr(feature = "wasm", wasm_expose)]
pub fn ask_price(
    oracle_data: &[u8],
    liquidity: SingleSideLiquidity,
    reserves_a: u64,
    reserves_b: u64,
) -> Result<Price, CoreError> {
    let mut oracle_data = oracle_data;
    let payload = OraclePayload::deserialize(&mut oracle_data).map_err(|_| INVALID_ORACLE_DATA)?;
    build_price(
        &liquidity,
        &payload.data,
        QuoteType::TokenBExactIn,
        reserves_a,
        reserves_b,
    )
}

#[cfg_attr(feature = "wasm", wasm_expose)]
pub fn bid_liquidity(
    oracle_data: &[u8],
    reserves_a: u64,
    reserves_b: u64,
    skew_cliff_min_per_m: i32,
    skew_cliff_max_per_m: i32,
) -> Result<SingleSideLiquidity, CoreError> {
    let mut oracle_data = oracle_data;
    let payload = OraclePayload::deserialize(&mut oracle_data).map_err(|_| INVALID_ORACLE_DATA)?;
    build_liquidity(
        &payload,
        QuoteType::TokenAExactIn,
        reserves_a,
        reserves_b,
        skew_cliff_min_per_m,
        skew_cliff_max_per_m,
    )
}

#[cfg_attr(feature = "wasm", wasm_expose)]
pub fn ask_liquidity(
    oracle_data: &[u8],
    reserves_a: u64,
    reserves_b: u64,
    skew_cliff_min_per_m: i32,
    skew_cliff_max_per_m: i32,
) -> Result<SingleSideLiquidity, CoreError> {
    let mut oracle_data = oracle_data;
    let payload = OraclePayload::deserialize(&mut oracle_data).map_err(|_| INVALID_ORACLE_DATA)?;
    build_liquidity(
        &payload,
        QuoteType::TokenBExactIn,
        reserves_a,
        reserves_b,
        skew_cliff_min_per_m,
        skew_cliff_max_per_m,
    )
}