use alloy::{
primitives::{address, Address, TxKind, U256},
sol,
sol_types::SolCall,
};
use anyhow::Result;
use colored::*;
use prettytable::{format, Cell, Row, Table};
use revm_trace::{
types::TokenInfo, utils::erc20_utils::get_token_infos, SimulationBatch, SimulationTx,
TransactionTrace, TxInspector,
};
use std::collections::HashMap;
#[cfg(not(feature = "foundry-fork"))]
use revm_trace::create_evm_with_tracer;
#[cfg(feature = "foundry-fork")]
use revm_trace::create_shared_evm_with_tracer;
sol! {
contract UniswapV2Router {
function swapExactETHForTokens(
uint256 amountOutMin,
address[] calldata path,
address to,
uint256 deadline
) external payable returns (uint256[] memory amounts);
}
}
fn format_amount(amount: U256, decimals: u8) -> String {
let mut value = amount.to_string();
if value.len() <= decimals as usize {
value.insert_str(0, &"0".repeat(decimals as usize - value.len() + 1));
value.insert(1, '.');
} else {
value.insert(value.len() - decimals as usize, '.');
}
value
.trim_end_matches('0')
.trim_end_matches('.')
.to_string()
}
const ETH_RPC_URL: &str = "https://eth.llamarpc.com";
#[tokio::main]
async fn main() -> Result<()> {
#[cfg(not(feature = "foundry-fork"))]
println!("Using AlloyDB backend for EVM simulation");
#[cfg(feature = "foundry-fork")]
println!("Using Foundry fork backend for EVM simulation");
let inspector = TxInspector::new();
#[cfg(not(feature = "foundry-fork"))]
let mut evm = create_evm_with_tracer(ETH_RPC_URL, inspector).await?;
#[cfg(feature = "foundry-fork")]
let mut evm = create_shared_evm_with_tracer(ETH_RPC_URL, inspector).await?;
let caller = address!("57757E3D981446D585Af0D9Ae4d7DF6D64647806");
let router = address!("7a250d5630B4cF539739dF2C5dAcb4c659F2488D");
let weth = address!("c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2");
let usdc = address!("a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48");
println!("Swap Configuration:");
println!("------------------");
println!("Caller: {}", caller);
println!("Router: {}", router);
println!("Path: WETH -> USDC\n");
let swap_amount = U256::from(100000000000000000u128); let path = vec![weth, usdc];
let deadline = U256::from(u64::MAX);
let data = UniswapV2Router::swapExactETHForTokensCall {
amountOutMin: U256::ZERO,
path,
to: caller,
deadline,
}
.abi_encode();
println!("Executing swap of {} ETH...\n", "0.1".bold());
let tx = SimulationTx {
caller,
transact_to: TxKind::Call(router),
value: swap_amount,
data: data.into(),
};
let result = evm
.trace_transactions(SimulationBatch {
transactions: vec![tx],
is_stateful: true,
overrides: None,
})
.into_iter()
.map(|v| v.unwrap())
.collect::<Vec<_>>()[0]
.clone();
println!("\nTransaction Result:");
println!("-----------------");
assert!(result.0.is_success(), "❌ Swap failed");
let mut table = Table::new();
table.set_format(*format::consts::FORMAT_BOX_CHARS);
table.add_row(Row::new(vec![
Cell::new("Token").style_spec("Fb"),
Cell::new("From").style_spec("Fb"),
Cell::new("To").style_spec("Fb"),
Cell::new("Amount").style_spec("Fb"),
]));
let mut tokens = vec![];
for transfer in &result.2.asset_transfers {
if !tokens.contains(&transfer.token) && transfer.token != Address::ZERO {
tokens.push(transfer.token);
}
}
let token_infos = get_token_infos(&mut evm, &tokens).unwrap();
let mut token_info_map = HashMap::new();
token_info_map.insert(
Address::ZERO,
TokenInfo {
name: "Ethereum".to_string(),
symbol: "ETH".to_string(),
decimals: 18,
total_supply: U256::MAX,
},
);
for (i, token_info) in token_infos.into_iter().enumerate() {
token_info_map.insert(tokens[i], token_info);
}
for transfer in &result.2.asset_transfers {
let amount = if let Some(info) = token_info_map.get(&transfer.token) {
format_amount(transfer.value, info.decimals)
} else {
format_amount(transfer.value, 18) };
table.add_row(Row::new(vec![
Cell::new(
&token_info_map
.get(&transfer.token)
.map(|i| i.symbol.clone())
.unwrap_or_else(|| "ETH".to_string()),
),
Cell::new(&format!("{:.8}...", transfer.from)),
Cell::new(&format!("{:.8}...", transfer.to.unwrap())),
Cell::new(&amount),
]));
}
println!("Swap Results:");
println!("------------");
table.printstd();
println!(
"\n{}",
"✅ All assertions passed successfully!".bold().green()
);
println!("Note: This example might fail occasionally due to DEX pool state changes");
println!("Consider using a specific block number or implementing retry logic for more stable results");
Ok(())
}