use crate::data::{ExactInParams, ExactOutParams, PoolState, Quote};
pub use wp_evm_v3_core::quote::QuoteError;
fn as_v3_pool_state(state: &PoolState) -> wp_evm_v3_core::data::PoolState {
wp_evm_v3_core::data::PoolState {
token0: state.token0,
token1: state.token1,
fee: state.fee,
tick_spacing: state.tick_spacing,
sqrt_price_x96: state.sqrt_price_x96,
liquidity: state.liquidity,
tick: state.tick,
ticks: state
.ticks
.iter()
.map(|t| wp_evm_v3_core::data::TickInfo {
tick: t.tick,
liquidity_net: t.liquidity_net,
liquidity_gross: t.liquidity_gross,
})
.collect(),
}
}
fn as_v3_exact_in(params: &ExactInParams) -> wp_evm_v3_core::data::ExactInParams {
wp_evm_v3_core::data::ExactInParams {
token_in: params.token_in,
token_out: params.token_out,
amount_in: params.amount_in,
recipient: params.recipient,
}
}
fn as_v3_exact_out(params: &ExactOutParams) -> wp_evm_v3_core::data::ExactOutParams {
wp_evm_v3_core::data::ExactOutParams {
token_in: params.token_in,
token_out: params.token_out,
amount_out: params.amount_out,
recipient: params.recipient,
}
}
pub fn exact_in(state: &PoolState, params: &ExactInParams) -> Result<Quote, QuoteError> {
let v3_state = as_v3_pool_state(state);
let v3_params = as_v3_exact_in(params);
let fee = state.fee;
let v3_quote =
wp_evm_v3_core::quote::exact_in_with_fee_fn(&v3_state, &v3_params, move |_| fee)?;
Ok(Quote {
amount_in: v3_quote.amount_in,
amount_out: v3_quote.amount_out,
sqrt_price_x96_after: v3_quote.sqrt_price_x96_after,
price_impact_bps: v3_quote.price_impact_bps,
effective_fee_pips: state.fee,
})
}
pub fn exact_out(state: &PoolState, params: &ExactOutParams) -> Result<Quote, QuoteError> {
let v3_state = as_v3_pool_state(state);
let v3_params = as_v3_exact_out(params);
let fee = state.fee;
let v3_quote =
wp_evm_v3_core::quote::exact_out_with_fee_fn(&v3_state, &v3_params, move |_| fee)?;
Ok(Quote {
amount_in: v3_quote.amount_in,
amount_out: v3_quote.amount_out,
sqrt_price_x96_after: v3_quote.sqrt_price_x96_after,
price_impact_bps: v3_quote.price_impact_bps,
effective_fee_pips: state.fee,
})
}
#[cfg(test)]
mod tests {
use super::*;
use crate::data::{ExactInParams, ExactOutParams, PoolState, TickInfo};
use alloy_primitives::{address, U256};
fn fixture_algebra_usdc_weth() -> PoolState {
let sqrt_price_x96: U256 =
U256::from_str_radix("3543191142285914205922034323214", 10).unwrap();
PoolState {
token0: address!("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"),
token1: address!("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"),
fee: 500, tick_spacing: 60,
sqrt_price_x96,
liquidity: 2_000_000_000_000_000_000_000u128,
tick: 76012,
ticks: vec![
TickInfo {
tick: 74940,
liquidity_net: 1_000_000_000_000_000_000_000i128,
liquidity_gross: 1_000_000_000_000_000_000_000u128,
},
TickInfo {
tick: 75960,
liquidity_net: 1_000_000_000_000_000_000_000i128,
liquidity_gross: 1_000_000_000_000_000_000_000u128,
},
TickInfo {
tick: 76020,
liquidity_net: -2_000_000_000_000_000_000_000i128,
liquidity_gross: 2_000_000_000_000_000_000_000u128,
},
],
}
}
#[test]
fn exact_in_returns_nonzero_amount_out() {
let s = fixture_algebra_usdc_weth();
let p = ExactInParams {
token_in: s.token0,
token_out: s.token1,
amount_in: U256::from(1_000_000u64),
recipient: address!("0x0000000000000000000000000000000000000099"),
};
let q = exact_in(&s, &p).expect("quote ok");
assert!(q.amount_out > U256::ZERO);
assert_eq!(q.amount_in, p.amount_in);
assert_eq!(q.effective_fee_pips, 500);
}
#[test]
fn exact_out_round_trip_within_tolerance() {
let s = fixture_algebra_usdc_weth();
let p_in = ExactInParams {
token_in: s.token0,
token_out: s.token1,
amount_in: U256::from(1_000_000u64),
recipient: address!("0x0000000000000000000000000000000000000099"),
};
let q_in = exact_in(&s, &p_in).unwrap();
let p_out = ExactOutParams {
token_in: s.token0,
token_out: s.token1,
amount_out: q_in.amount_out,
recipient: p_in.recipient,
};
let q_out = exact_out(&s, &p_out).unwrap();
let diff = if q_out.amount_in > p_in.amount_in {
q_out.amount_in - p_in.amount_in
} else {
p_in.amount_in - q_out.amount_in
};
assert!(diff <= U256::from(1_000u64), "round-trip diff = {}", diff);
}
#[test]
fn exact_in_rejects_unknown_token() {
let s = fixture_algebra_usdc_weth();
let bogus = address!("0x000000000000000000000000000000000000dead");
let p = ExactInParams {
token_in: bogus,
token_out: s.token1,
amount_in: U256::from(1_000_000u64),
recipient: address!("0x0000000000000000000000000000000000000099"),
};
let err = exact_in(&s, &p).expect_err("should reject unknown token");
assert!(matches!(err, QuoteError::UnknownToken));
}
#[test]
fn effective_fee_pips_propagates_from_state() {
let mut s = fixture_algebra_usdc_weth();
s.fee = 1500; let p = ExactInParams {
token_in: s.token0,
token_out: s.token1,
amount_in: U256::from(1_000u64),
recipient: address!("0x0000000000000000000000000000000000000099"),
};
let q = exact_in(&s, &p).expect("quote ok");
assert_eq!(q.effective_fee_pips, 1500);
}
}