use std::time::Duration;
use alloy::{
network::Ethereum,
primitives::{Address, TxHash},
providers::{DynProvider, Provider},
rpc::types::TransactionRequest,
};
use anyhow::Result;
use async_trait::async_trait;
use tracing::{debug, error, info, instrument};
use uniswap_sdk_core::entities::BaseCurrency;
use waterpump_evm_core::pool_key::SlipstreamPoolKey;
use super::{
build_quote_call_parameters, build_swap_call_parameters, decode_quote_amount,
decode_swap_event_to_result, handle_slipstream_error,
};
use crate::{
pool_swappers::common::{
build_transaction_with_gas_prices, find_event, send_and_wait_for_transaction,
},
traits::pool_swapper::PoolSwapper,
types::{
swap_params::{GasPriceOptions, QuoteOptions, SwapOptions, SwapParams},
swap_results::V3SwapResult,
},
};
#[derive(Clone, Debug)]
pub struct SlipstreamOneHopSwapper {
pub pool_address: Address,
pub pool_key: SlipstreamPoolKey,
pub router_address: Address,
pub quoter_address: Address,
pub sender_address: Address,
pub provider: DynProvider<Ethereum>,
}
impl SlipstreamOneHopSwapper {
#[instrument(skip(pool_key), fields(pool_address = ?pool_address, token_a = ?pool_key.token_a.address(), token_b = ?pool_key.token_b.address(), tick_spacing = ?pool_key.tick_spacing, router_address = ?router_address, quoter_address = ?quoter_address, sender_address = ?sender_address))]
pub fn new(
pool_address: Address,
pool_key: SlipstreamPoolKey,
router_address: Address,
quoter_address: Address,
sender_address: Address,
provider: DynProvider<Ethereum>,
) -> Self {
debug!(pool_address = ?pool_address, "Using provided pool address");
Self { pool_address, pool_key, router_address, quoter_address, sender_address, provider }
}
pub fn sender_address(&self) -> Address { self.sender_address }
pub fn router_address(&self) -> Address { self.router_address }
pub fn quoter_address(&self) -> Address { self.quoter_address }
pub fn pool_address(&self) -> Address { self.pool_address }
pub fn pool_key(&self) -> &SlipstreamPoolKey { &self.pool_key }
}
use crate::impl_token_helper;
impl_token_helper!(SlipstreamOneHopSwapper);
use crate::{impl_pool_base, impl_slipstream_pool_state, impl_slipstream_pool_viewer};
impl_pool_base!(SlipstreamOneHopSwapper);
impl_slipstream_pool_viewer!(SlipstreamOneHopSwapper);
impl_slipstream_pool_state!(SlipstreamOneHopSwapper);
#[async_trait]
impl PoolSwapper for SlipstreamOneHopSwapper {
#[instrument(skip(self, swap_params, swap_options, gas_price_options), fields(pool_address = ?self.pool_address, router_address = ?self.router_address(), is_to_b = ?swap_params.is_to_b, trade_type = ?swap_params.trade_type, amount = ?swap_params.amount))]
async fn swap(
&self,
swap_params: SwapParams,
swap_options: SwapOptions,
gas_price_options: Option<GasPriceOptions>,
) -> Result<V3SwapResult> {
let is_to_b = swap_params.is_to_b;
let swap_call_parameters =
build_swap_call_parameters(&self.pool_key, swap_params, swap_options)?;
debug!(
calldata_len = swap_call_parameters.calldata.len(),
value = ?swap_call_parameters.value,
"Swap call parameters built"
);
let tx = build_transaction_with_gas_prices(
&self.provider,
self.sender_address(),
self.router_address(),
swap_call_parameters,
gas_price_options,
)
.await?;
let receipt = send_and_wait_for_transaction(
&self.provider,
tx,
Some(Duration::from_secs(120)),
Some(|e| handle_slipstream_error(format!("{}", e))),
)
.await?;
let tx_hash = receipt.transaction_hash;
info!(
tx_hash = ?tx_hash,
block_number = ?receipt.block_number,
gas_used = ?receipt.gas_used,
status = ?receipt.status(),
"Transaction confirmed"
);
if !receipt.status() {
error!(
tx_hash = ?tx_hash,
block_number = ?receipt.block_number,
"Transaction failed"
);
return Err(anyhow::anyhow!("Transaction failed"));
}
let currency0 = self.pool_key.token_a.clone();
let currency1 = self.pool_key.token_b.clone();
let is_to_b_captured = is_to_b;
find_event(
&receipt,
move |log: &alloy::rpc::types::Log, tx_hash: TxHash| -> anyhow::Result<V3SwapResult> {
decode_swap_event_to_result(
log,
tx_hash,
is_to_b_captured,
currency0.clone(),
currency1.clone(),
)
},
)
}
#[instrument(skip(self, swap_params, swap_options), fields(pool_address = ?self.pool_address, router_address = ?self.router_address(), is_to_b = ?swap_params.is_to_b, trade_type = ?swap_params.trade_type, amount = ?swap_params.amount))]
async fn simulate_swap(
&self,
swap_params: SwapParams,
swap_options: SwapOptions,
) -> Result<()> {
let swap_call_parameters =
build_swap_call_parameters(&self.pool_key, swap_params, swap_options)?;
debug!(
calldata_len = swap_call_parameters.calldata.len(),
value = ?swap_call_parameters.value,
"Simulation call parameters built"
);
let tx = TransactionRequest::default()
.from(self.sender_address())
.to(self.router_address())
.input(swap_call_parameters.calldata.into())
.value(swap_call_parameters.value);
let res = Provider::call(&self.provider, tx).await?;
info!(result_len = res.len(), "Simulation completed successfully");
debug!(simulation_result = ?res, "Simulation result details");
Ok(())
}
#[instrument(skip(self, swap_params, options), fields(pool_address = ?self.pool_address, quoter_address = ?self.quoter_address, is_to_b = ?swap_params.is_to_b, trade_type = ?swap_params.trade_type, amount = ?swap_params.amount, use_quoter_v2 = ?options.use_quoter_v2))]
async fn quote(
&self,
swap_params: SwapParams,
options: QuoteOptions,
) -> Result<alloy::primitives::U256> {
let use_quoter_v2 = options.use_quoter_v2;
let swap_call_parameters =
build_quote_call_parameters(&self.pool_key, swap_params.clone(), options)?;
debug!(
calldata_len = swap_call_parameters.calldata.len(),
value = ?swap_call_parameters.value,
"Quote call parameters built"
);
let tx = TransactionRequest::default()
.from(self.sender_address())
.to(self.quoter_address)
.input(swap_call_parameters.calldata.into())
.value(swap_call_parameters.value);
let res = Provider::call(&self.provider, tx).await?;
let amount = decode_quote_amount(res, &swap_params.trade_type, use_quoter_v2)?;
info!(
quoted_amount = ?amount,
trade_type = ?swap_params.trade_type,
"Quote completed successfully"
);
Ok(amount)
}
}