use alloy_primitives::{Address, Bytes, U256, address};
use super::{AdapterCache, CallOutcome};
#[non_exhaustive]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct SwapQuote {
pub amount_out: U256,
}
impl SwapQuote {
pub fn new(amount_out: U256) -> Self {
Self { amount_out }
}
}
#[non_exhaustive]
#[derive(Debug)]
pub enum SimError {
Unsupported(super::ProtocolId),
MissingMetadata(&'static str),
Reverted,
MalformedOutput(&'static str),
Execution(Box<dyn std::error::Error + Send + Sync + 'static>),
Custom(String),
}
impl core::fmt::Display for SimError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::Unsupported(protocol) => {
write!(f, "swap simulation unsupported for {protocol:?}")
}
Self::MissingMetadata(what) => write!(f, "missing metadata for swap sim: {what}"),
Self::Reverted => write!(f, "quote call reverted or halted"),
Self::MalformedOutput(what) => write!(f, "malformed quote output: {what}"),
Self::Execution(err) => write!(f, "quote execution failed: {err}"),
Self::Custom(err) => write!(f, "swap sim error: {err}"),
}
}
}
impl std::error::Error for SimError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Execution(err) => Some(&**err as &(dyn std::error::Error + 'static)),
_ => None,
}
}
}
#[non_exhaustive]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct SimConfig {
pub v3_quoter: Address,
pub v2_router: Address,
pub from: Address,
}
pub const MAINNET_V3_QUOTER_V2: Address = address!("61fFE014bA17989E743c5F6cB21bF9697530B21e");
pub const MAINNET_V2_ROUTER_02: Address = address!("7a250d5630B4cF539739dF2C5dAcb4c659F2488D");
impl Default for SimConfig {
fn default() -> Self {
Self {
v3_quoter: MAINNET_V3_QUOTER_V2,
v2_router: MAINNET_V2_ROUTER_02,
from: Address::ZERO,
}
}
}
impl SimConfig {
pub fn with_v3_quoter(mut self, quoter: Address) -> Self {
self.v3_quoter = quoter;
self
}
pub fn with_v2_router(mut self, router: Address) -> Self {
self.v2_router = router;
self
}
pub fn with_from(mut self, from: Address) -> Self {
self.from = from;
self
}
}
pub fn quote_via_call(
cache: &mut dyn AdapterCache,
target: Address,
calldata: Bytes,
) -> Result<Bytes, SimError> {
quote_via_call_from(cache, Address::ZERO, target, calldata)
}
pub fn quote_via_call_from(
cache: &mut dyn AdapterCache,
from: Address,
target: Address,
calldata: Bytes,
) -> Result<Bytes, SimError> {
classify_quote_outcome(
cache
.call_raw(from, target, calldata, false)
.map_err(|e| SimError::Execution(Box::new(e)))?,
)
}
#[cfg(feature = "uniswap-v3")]
pub(crate) fn quote_via_call_with_code_overrides_from(
cache: &mut dyn AdapterCache,
from: Address,
target: Address,
calldata: Bytes,
code_overrides: &[(Address, Bytes)],
) -> Result<Bytes, SimError> {
classify_quote_outcome(
cache
.call_raw_with_code_overrides(from, target, calldata, code_overrides, false)
.map_err(|e| SimError::Execution(Box::new(e)))?,
)
}
fn classify_quote_outcome(outcome: CallOutcome) -> Result<Bytes, SimError> {
match outcome {
CallOutcome::Success { output, .. } => Ok(output),
CallOutcome::Revert { .. } | CallOutcome::Halt { .. } => Err(SimError::Reverted),
}
}
pub(crate) mod abi {
use alloy_sol_types::sol;
sol! {
struct QuoteExactInputSingleParams {
address tokenIn;
address tokenOut;
uint256 amountIn;
uint24 fee;
uint160 sqrtPriceLimitX96;
}
function quoteExactInputSingle(QuoteExactInputSingleParams params)
returns (
uint256 amountOut,
uint160 sqrtPriceX96After,
uint32 initializedTicksCrossed,
uint256 gasEstimate
);
function getAmountsOut(uint256 amountIn, address[] path)
returns (uint256[] amounts);
function getAmountOut(uint256 amountIn, address tokenIn) returns (uint256 amountOut);
function get_dy(int128 i, int128 j, uint256 dx) returns (uint256 dy);
function queryBatchSwap(
uint8 kind,
BatchSwapStep[] swaps,
address[] assets,
FundManagement funds
) returns (int256[] assetDeltas);
struct BatchSwapStep {
bytes32 poolId;
uint256 assetInIndex;
uint256 assetOutIndex;
uint256 amount;
bytes userData;
}
struct FundManagement {
address sender;
bool fromInternalBalance;
address recipient;
bool toInternalBalance;
}
}
sol! {
interface CurveCryptoSwap {
function get_dy(uint256 i, uint256 j, uint256 dx) returns (uint256 dy);
}
}
}
#[allow(unused_imports)]
pub(crate) use abi::*;