waterpump-evm-pool-sdk 0.1.0

EVM pool SDK — viewers, infusers, harvesters, swappers for Uniswap V3/V4, PancakeSwap, Slipstream, Shadow, Algebra
Documentation
use alloy::{
    network::Ethereum,
    primitives::{Address, U256},
    providers::Provider,
    rpc::types::TransactionRequest,
    sol,
};
use alloy_sol_types::SolCall;
use anyhow::{Context, Result};
use tracing::{debug, info};
use uniswap_sdk_core::{
    entities::{BaseCurrency, BaseCurrencyCore, FractionBase},
    prelude::{Currency, CurrencyAmount, WETH9},
    utils::FromBig,
};
use waterpump_evm_core::weth_ext::Weth9Ext;

sol! {
    #[sol(rpc)]
    interface IWETH {
        function deposit() payable;
        function withdraw(uint256 amount) returns (bool);
        function balanceOf(address account) returns (uint256);
    }
}

/// Wrap ETH to WETH
/// Returns true if wrapping was needed and executed, false if not needed
#[tracing::instrument(skip(provider, amount), fields(sender_address = ?sender_address, amount = ?amount, currency_is_native = amount.currency.is_native()))]
pub async fn wrap_eth_if_needed<P: Provider<Ethereum>>(
    provider: &P,
    sender_address: Address,
    amount: CurrencyAmount<Currency>,
) -> Result<bool> {
    // Only wrap if the currency is native ETH
    if !amount.currency.is_native() {
        debug!("Currency is not native, no wrapping needed");
        return Ok(false);
    }

    info!("Token is native (ETH), wrapping to WETH...");

    // Get chain ID from provider
    let chain_id_raw =
        provider.get_chain_id().await.context("Failed to get chain ID from provider")?;
    let chain_id: u64 = chain_id_raw;

    let weth_token = WETH9::on_chain_with_custom(chain_id)?;
    let currency: Currency = weth_token.into();
    let weth_address = currency.address();
    debug!(weth_address = ?weth_address, "WETH address");

    let weth = IWETH::new(weth_address, provider);

    // Check current WETH balance
    let current_balance =
        weth.balanceOf(sender_address).call().await.context("Failed to check WETH balance")?;

    let required_amount = U256::from_big_int(amount.quotient());

    debug!(current_balance = ?current_balance, required_amount = ?required_amount, "WETH balance check");

    // Check if we need to wrap
    if current_balance >= required_amount {
        info!("Sufficient WETH balance, no wrapping needed");
        return Ok(false);
    }

    // Calculate how much ETH to wrap
    let eth_to_wrap = required_amount - current_balance;
    info!("Insufficient WETH balance, wrapping ETH");
    debug!(wrapping_amount = ?eth_to_wrap, "Wrapping amount");

    // Check ETH balance
    let eth_balance = provider.get_balance(sender_address).await?;
    if eth_balance < eth_to_wrap {
        return Err(anyhow::anyhow!(
            "Insufficient ETH balance. Have: {}, Need: {}",
            eth_balance,
            eth_to_wrap
        ));
    }

    // Call WETH deposit function
    let deposit_calldata = IWETH::depositCall {}.abi_encode();

    let tx = TransactionRequest::default()
        .from(sender_address)
        .to(weth_address)
        .input(deposit_calldata.into())
        .value(eth_to_wrap);

    let pending_tx =
        provider.send_transaction(tx).await.context("Failed to send WETH deposit transaction")?;

    let tx_hash = pending_tx.watch().await?;
    info!(tx_hash = ?tx_hash, "WETH deposit transaction sent");

    let receipt = provider
        .get_transaction_receipt(tx_hash)
        .await
        .context("Failed to get WETH deposit transaction receipt")?
        .unwrap();

    info!(
        tx_hash = ?tx_hash,
        block_number = ?receipt.block_number,
        gas_used = ?receipt.gas_used,
        "WETH deposit confirmed"
    );

    Ok(true)
}