use alloy::{
primitives::{aliases::U128, Address, Bytes, TxHash, U256},
rpc::types::Log,
sol,
};
use alloy_sol_types::SolCall;
use anyhow::Result;
use tracing::{debug, error, info, instrument};
use uniswap_sdk_core::prelude::{Currency, CurrencyAmount, ToBig, TradeType};
use uniswap_v3_sdk::prelude::{IQuoter, IQuoterV2};
use crate::{
pool_swappers::common::SwapEventData,
types::swap_results::{V3SwapResult, V3SwapWithIntermediateResult},
};
sol! {
event Swap(
address indexed sender,
address indexed recipient,
int256 amount0,
int256 amount1,
uint160 sqrtPriceX96,
uint128 liquidity,
int24 tick
);
}
#[instrument(skip(res), fields(response_len = res.len(), trade_type = ?trade_type, use_quoter_v2 = use_quoter_v2))]
pub fn decode_quote_amount(
res: Bytes,
trade_type: &TradeType,
use_quoter_v2: bool,
) -> Result<U256> {
let amount = match trade_type {
TradeType::ExactInput => {
if use_quoter_v2 {
IQuoterV2::quoteExactInputSingleCall::abi_decode_returns_validate(res.as_ref())
.unwrap()
.amountOut
} else {
IQuoter::quoteExactInputSingleCall::abi_decode_returns_validate(res.as_ref())
.unwrap()
}
}
TradeType::ExactOutput => {
if use_quoter_v2 {
IQuoterV2::quoteExactOutputSingleCall::abi_decode_returns_validate(res.as_ref())
.unwrap()
.amountIn
} else {
IQuoter::quoteExactOutputSingleCall::abi_decode_returns_validate(res.as_ref())
.unwrap()
}
}
};
Ok(amount)
}
#[instrument(skip(log), fields(log_address = ?log.address(), pool_address = ?pool_address))]
pub fn decode_swap_event(log: &Log, pool_address: Option<Address>) -> Result<Log<Swap>> {
if let Some(expected_address) = pool_address {
if log.address() != expected_address {
error!(
log_address = ?log.address(),
expected_address = ?expected_address,
"Log address does not match expected pool address"
);
return Err(anyhow::anyhow!(
"Log address {:?} does not match expected pool address {:?}",
log.address(),
expected_address
));
}
}
let result = log.log_decode::<Swap>().map_err(|e| {
debug!(error = ?e, "Failed to decode Swap event");
anyhow::anyhow!("Failed to decode Swap event: {:?}", e)
});
if result.is_ok() {
debug!("Successfully decoded swap event");
}
result
}
#[instrument(skip(logs), fields(log_count = logs.len(), pool_address = ?pool_address))]
pub fn decode_swap_events_from_logs(
logs: &[Log],
pool_address: Option<Address>,
) -> Result<Vec<Log<Swap>>> {
let mut swap_events = Vec::new();
for log in logs {
if let Ok(swap_event) = decode_swap_event(log, pool_address) {
swap_events.push(swap_event);
}
}
info!(
total_logs = logs.len(),
decoded_swap_events = swap_events.len(),
"Finished decoding swap events"
);
Ok(swap_events)
}
#[instrument(skip(log), fields(
log_address = ?log.address(),
tx_hash = ?tx_hash,
is_to_b = is_to_b
))]
pub fn decode_swap_event_to_result(
log: &Log,
tx_hash: TxHash,
is_to_b: bool,
currency0: Currency,
currency1: Currency,
) -> Result<V3SwapResult> {
let decoded = log.log_decode::<Swap>()?;
let swap_data = SwapEventData {
sender: decoded.inner.sender,
recipient: decoded.inner.recipient,
amount0: decoded.inner.amount0,
amount1: decoded.inner.amount1,
sqrt_price_x96: decoded.inner.sqrtPriceX96,
liquidity: decoded.inner.liquidity,
tick: decoded.inner.tick,
};
let (amount_in, amount_out, currency_in, currency_out) = if is_to_b {
(swap_data.amount0.unsigned_abs(), swap_data.amount1.unsigned_abs(), currency0, currency1)
} else {
(swap_data.amount1.unsigned_abs(), swap_data.amount0.unsigned_abs(), currency1, currency0)
};
Ok(V3SwapResult {
tx_hash,
sender: swap_data.sender,
recipient: swap_data.recipient,
amount_in: CurrencyAmount::from_raw_amount(currency_in, amount_in.to_big_int())?,
amount_out: CurrencyAmount::from_raw_amount(currency_out, amount_out.to_big_int())?,
sqrt_price_x96: swap_data.sqrt_price_x96,
liquidity: U128::from(swap_data.liquidity),
tick: swap_data.tick,
})
}
#[instrument(skip(log), fields(
log_address = ?log.address(),
tx_hash = ?tx_hash,
is_to_b = is_to_b,
is_to_intermediate = is_to_intermediate
))]
pub fn decode_swap_event_to_result_with_intermediate(
log: &Log,
tx_hash: TxHash,
is_to_b: bool,
is_to_intermediate: bool,
currency0: Currency,
currency1: Currency,
) -> Result<V3SwapWithIntermediateResult> {
let decoded = log.log_decode::<Swap>()?;
let swap_data = SwapEventData {
sender: decoded.inner.sender,
recipient: decoded.inner.recipient,
amount0: decoded.inner.amount0,
amount1: decoded.inner.amount1,
sqrt_price_x96: decoded.inner.sqrtPriceX96,
liquidity: decoded.inner.liquidity,
tick: decoded.inner.tick,
};
let (amount_in, amount_out, currency_in, currency_out) = if is_to_b {
(swap_data.amount0.unsigned_abs(), swap_data.amount1.unsigned_abs(), currency0, currency1)
} else {
(swap_data.amount1.unsigned_abs(), swap_data.amount0.unsigned_abs(), currency1, currency0)
};
Ok(V3SwapWithIntermediateResult {
tx_hash,
sender: swap_data.sender,
recipient: swap_data.recipient,
amount_in: CurrencyAmount::from_raw_amount(currency_in, amount_in.to_big_int())?,
amount_out: CurrencyAmount::from_raw_amount(currency_out, amount_out.to_big_int())?,
sqrt_price_x96: swap_data.sqrt_price_x96,
liquidity: U128::from(swap_data.liquidity),
tick: swap_data.tick,
is_to_intermediate,
})
}