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 ERC20 {
function approve(address spender, uint256 amount) returns (bool);
function allowance(address owner, address spender) returns (uint256);
}
}
#[tracing::instrument(skip(provider, token, amount), fields(sender_address = ?sender_address, spender_address = ?spender_address, token_address = ?token.address(), token_is_native = token.is_native(), amount = ?amount))]
pub async fn approve_if_needed<P: Provider<Ethereum>>(
provider: &P,
sender_address: Address,
spender_address: Address,
token: &Currency,
amount: CurrencyAmount<Currency>,
) -> Result<bool> {
if token.is_native() {
info!("Token is native (ETH), approving WETH instead");
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 max_approval = U256::MAX;
info!("Insufficient WETH allowance, approving router");
debug!(approving_amount = ?max_approval, "Approving max amount");
let approve_calldata =
ERC20::approveCall { spender: spender_address, amount: max_approval }.abi_encode();
let tx = TransactionRequest::default()
.from(sender_address)
.to(weth_token.address())
.input(approve_calldata.into());
let pending_tx = provider
.send_transaction(tx)
.await
.context("Failed to send WETH approval transaction")?;
let tx_hash = pending_tx.watch().await?;
info!(tx_hash = ?tx_hash, "WETH approval transaction sent");
let receipt = provider
.get_transaction_receipt(tx_hash)
.await
.context("Failed to get WETH approval transaction receipt")?
.unwrap();
info!(
tx_hash = ?tx_hash,
block_number = ?receipt.block_number,
gas_used = ?receipt.gas_used,
"WETH approval confirmed"
);
return Ok(true);
}
let token_address = token.address();
debug!(token_address = ?token_address, "Processing ERC20 token approval");
let erc20 = ERC20::new(token_address, provider);
let current_allowance = erc20
.allowance(sender_address, spender_address)
.call()
.await
.context("Failed to check token allowance")?;
let required_amount = U256::from_big_int(amount.quotient());
debug!(current_allowance = ?current_allowance, required_amount = ?required_amount, "Token allowance check");
if current_allowance >= required_amount {
info!("Sufficient allowance, no approval needed");
return Ok(false);
}
let max_approval = U256::MAX;
info!("Insufficient allowance, approving router");
debug!(approving_amount = ?max_approval, "Approving max amount");
let approve_calldata =
ERC20::approveCall { spender: spender_address, amount: max_approval }.abi_encode();
let tx = TransactionRequest::default()
.from(sender_address)
.to(token_address)
.input(approve_calldata.into());
let pending_tx =
provider.send_transaction(tx).await.context("Failed to send approval transaction")?;
let tx_hash = pending_tx.watch().await?;
info!(tx_hash = ?tx_hash, "Approval transaction sent");
let _receipt = provider
.get_transaction_receipt(tx_hash)
.await
.context("Failed to get approval transaction receipt")?
.unwrap();
info!(
tx_hash = ?tx_hash,
"Approval confirmed"
);
Ok(true)
}