Skip to main content

riptide_amm_math/
quote.rs

1use super::SingleSideLiquidity;
2
3use super::error::{CoreError, INVALID_ORACLE_DATA};
4
5use super::oracle::{build_liquidity, build_price, consume_liquidity, OraclePayload};
6
7use borsh::BorshDeserialize;
8
9use riptide_amm_macros::alias;
10
11#[cfg(feature = "wasm")]
12use riptide_amm_macros::wasm_expose;
13
14#[derive(Debug, Clone, Copy, Eq, PartialEq)]
15#[cfg_attr(feature = "wasm", wasm_expose)]
16pub enum QuoteType {
17    TokenAExactIn,
18    TokenAExactOut,
19    TokenBExactIn,
20    TokenBExactOut,
21}
22
23impl QuoteType {
24    pub(crate) fn new(amount_is_token_a: bool, amount_is_input: bool) -> Self {
25        match (amount_is_token_a, amount_is_input) {
26            (true, true) => QuoteType::TokenAExactIn,
27            (true, false) => QuoteType::TokenAExactOut,
28            (false, true) => QuoteType::TokenBExactIn,
29            (false, false) => QuoteType::TokenBExactOut,
30        }
31    }
32
33    pub fn exact_in(&self) -> bool {
34        matches!(self, QuoteType::TokenAExactIn | QuoteType::TokenBExactIn)
35    }
36
37    pub fn exact_out(&self) -> bool {
38        matches!(self, QuoteType::TokenAExactOut | QuoteType::TokenBExactOut)
39    }
40
41    #[alias(output_is_token_b, a_to_b)]
42    pub fn input_is_token_a(&self) -> bool {
43        matches!(self, QuoteType::TokenAExactIn | QuoteType::TokenBExactOut)
44    }
45
46    #[alias(output_is_token_a, b_to_a)]
47    pub fn input_is_token_b(&self) -> bool {
48        matches!(self, QuoteType::TokenBExactIn | QuoteType::TokenAExactOut)
49    }
50}
51
52#[derive(Debug, Clone, Copy, Eq, PartialEq)]
53#[cfg_attr(feature = "wasm", wasm_expose)]
54pub struct Quote {
55    pub amount_in: u64,
56    pub amount_out: u64,
57    pub quote_type: QuoteType,
58}
59
60#[derive(Default, Debug, Clone, Eq, PartialEq)]
61#[cfg_attr(feature = "wasm", wasm_expose)]
62pub struct Prices {
63    pub oracle_price_q64_64: u128,
64    pub best_bid_price_q64_64: u128,
65    pub best_ask_price_q64_64: u128,
66    pub ask_spread_per_m: i32,
67    pub bid_spread_per_m: i32,
68}
69
70#[derive(Default, Debug, Clone, Eq, PartialEq)]
71#[cfg_attr(feature = "wasm", wasm_expose)]
72pub struct Price {
73    pub oracle_price_q64_64: u128,
74    pub best_price_q64_64: u128,
75    pub spread_per_m: i32,
76}
77
78#[cfg_attr(feature = "wasm", wasm_expose)]
79pub fn quote_exact_in(
80    amount: u64,
81    amount_is_token_a: bool,
82    oracle_data: &[u8],
83    reserves_a: u64,
84    reserves_b: u64,
85    skew_cliff_min_per_m: i32,
86    skew_cliff_max_per_m: i32,
87) -> Result<Quote, CoreError> {
88    let mut oracle_data = oracle_data;
89    let payload = OraclePayload::deserialize(&mut oracle_data).map_err(|_| INVALID_ORACLE_DATA)?;
90    let quote_type = QuoteType::new(amount_is_token_a, true);
91    let liquidity = build_liquidity(
92        &payload,
93        quote_type,
94        reserves_a,
95        reserves_b,
96        skew_cliff_min_per_m,
97        skew_cliff_max_per_m,
98    )?;
99    consume_liquidity(amount, quote_type, &liquidity)
100}
101
102#[cfg_attr(feature = "wasm", wasm_expose)]
103pub fn quote_exact_out(
104    amount: u64,
105    amount_is_token_a: bool,
106    oracle_data: &[u8],
107    reserves_a: u64,
108    reserves_b: u64,
109    skew_cliff_min_per_m: i32,
110    skew_cliff_max_per_m: i32,
111) -> Result<Quote, CoreError> {
112    let mut oracle_data = oracle_data;
113    let payload = OraclePayload::deserialize(&mut oracle_data).map_err(|_| INVALID_ORACLE_DATA)?;
114    let quote_type = QuoteType::new(amount_is_token_a, false);
115    let liquidity = build_liquidity(
116        &payload,
117        quote_type,
118        reserves_a,
119        reserves_b,
120        skew_cliff_min_per_m,
121        skew_cliff_max_per_m,
122    )?;
123    consume_liquidity(amount, quote_type, &liquidity)
124}
125
126#[cfg_attr(feature = "wasm", wasm_expose)]
127pub fn bid_price(
128    oracle_data: &[u8],
129    liquidity: SingleSideLiquidity,
130    reserves_a: u64,
131    reserves_b: u64,
132) -> Result<Price, CoreError> {
133    let mut oracle_data = oracle_data;
134    let payload = OraclePayload::deserialize(&mut oracle_data).map_err(|_| INVALID_ORACLE_DATA)?;
135    build_price(
136        &liquidity,
137        &payload.data,
138        QuoteType::TokenAExactIn,
139        reserves_a,
140        reserves_b,
141    )
142}
143
144#[cfg_attr(feature = "wasm", wasm_expose)]
145pub fn ask_price(
146    oracle_data: &[u8],
147    liquidity: SingleSideLiquidity,
148    reserves_a: u64,
149    reserves_b: u64,
150) -> Result<Price, CoreError> {
151    let mut oracle_data = oracle_data;
152    let payload = OraclePayload::deserialize(&mut oracle_data).map_err(|_| INVALID_ORACLE_DATA)?;
153    build_price(
154        &liquidity,
155        &payload.data,
156        QuoteType::TokenBExactIn,
157        reserves_a,
158        reserves_b,
159    )
160}
161
162#[cfg_attr(feature = "wasm", wasm_expose)]
163pub fn bid_liquidity(
164    oracle_data: &[u8],
165    reserves_a: u64,
166    reserves_b: u64,
167    skew_cliff_min_per_m: i32,
168    skew_cliff_max_per_m: i32,
169) -> Result<SingleSideLiquidity, CoreError> {
170    let mut oracle_data = oracle_data;
171    let payload = OraclePayload::deserialize(&mut oracle_data).map_err(|_| INVALID_ORACLE_DATA)?;
172    build_liquidity(
173        &payload,
174        QuoteType::TokenAExactIn,
175        reserves_a,
176        reserves_b,
177        skew_cliff_min_per_m,
178        skew_cliff_max_per_m,
179    )
180}
181
182#[cfg_attr(feature = "wasm", wasm_expose)]
183pub fn ask_liquidity(
184    oracle_data: &[u8],
185    reserves_a: u64,
186    reserves_b: u64,
187    skew_cliff_min_per_m: i32,
188    skew_cliff_max_per_m: i32,
189) -> Result<SingleSideLiquidity, CoreError> {
190    let mut oracle_data = oracle_data;
191    let payload = OraclePayload::deserialize(&mut oracle_data).map_err(|_| INVALID_ORACLE_DATA)?;
192    build_liquidity(
193        &payload,
194        QuoteType::TokenBExactIn,
195        reserves_a,
196        reserves_b,
197        skew_cliff_min_per_m,
198        skew_cliff_max_per_m,
199    )
200}