Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
223f6cd
chore: fix publish revm-precompile (#1258)
rakita Apr 2, 2024
8af9531
feat(interpreter): test Host object-safety, allow `dyn Host` in instr…
DaniPopes Apr 3, 2024
5d5c5f1
chore(deps): bump secp256k1 from 0.28.2 to 0.29.0 (#1260)
dependabot[bot] Apr 3, 2024
96dda38
feat(interpreter): remove SPEC generic from gas calculation functions…
DaniPopes Apr 3, 2024
4d64bbc
feat(interpreter): derive Eq for InterpreterAction (#1262)
DaniPopes Apr 5, 2024
5e730a6
chore: add and use EvmContext::take_error (#1264)
DaniPopes Apr 5, 2024
af56651
feat(`db`): Introduce `alloydb` (#1257)
Evalir Apr 5, 2024
eee0541
minor typo fix in docs (#1266)
h3lio5 Apr 6, 2024
d3db49d
chore(ci): use more stable rust toolchain plugin (#1269)
rakita Apr 7, 2024
c961887
chore: revert snailtracer without microbench (#1259)
rakita Apr 7, 2024
c1eb0e6
perf(interpreter): use `pop_top!` where possible (#1267)
DaniPopes Apr 7, 2024
11e819c
Add the modifies_memory macro (#1270)
h3lio5 Apr 7, 2024
d06f5d0
feat: pass rand feature to alloy_primitives (#1276)
Wodann Apr 8, 2024
1edfeb6
Update documentation (#1275)
Pana Apr 8, 2024
b4a87a4
feat: EOF (Ethereum Object Format) (#1143)
rakita Apr 9, 2024
cf96ce8
feat(revme): add --keep-going to statetest command (#1277)
DaniPopes Apr 9, 2024
cfc4511
docs(inspectors): change `serde` to `serde-json` for `TracerEip3155` …
Jon-Becker Apr 10, 2024
e0f72a0
feat: add flag to force hashbrown usage (#1284)
Wodann Apr 13, 2024
fa9e127
feat(revm): make `FrameOrResult` serializable (#1282)
n0b0dyCN Apr 13, 2024
7957c02
feat: add `Bytecode::original_bytecode_slice` to match `BytecodeLocke…
DaniPopes Apr 13, 2024
5d25c4d
fix: Drops check for .json when testing a single file (#1301)
mw2000 Apr 13, 2024
f4f4745
fix: correct some stack IO (#1302)
DaniPopes Apr 13, 2024
72356e3
chore(interpreter): rename wrapping_* opcodes (#1306)
DaniPopes Apr 16, 2024
cc1b9f7
chore: fix some warnings (#1305)
DaniPopes Apr 16, 2024
9cfd144
perf(interpreter): remove EOF branch in CODE{SIZE,COPY} (#1308)
DaniPopes Apr 16, 2024
aec666a
chore(deps): bump anyhow from 1.0.81 to 1.0.82 (#1293)
dependabot[bot] Apr 16, 2024
688c36c
chore(interpreter): rename some macros (#1304)
DaniPopes Apr 16, 2024
af556cd
revm-primitives add U64 and Selector from alloy
BigBenlau Apr 17, 2024
41beddf
docs: fix the Instruction Table link (#1337)
IaroslavMazur Apr 19, 2024
43199dd
chore: weekly dependabot (#1325)
mattsse Apr 19, 2024
e987d2b
chore: update GitHub Actions to Node 20 (#1338)
IaroslavMazur Apr 19, 2024
bcdc652
Implement `with_chain_id` for `CfgEnv` (#1327)
tcoratger Apr 21, 2024
76e22ba
feat: add helper methods to CallInputs (#1345)
DaniPopes Apr 21, 2024
1ca3d39
fix(revme): Print one json outcome in statetest (#1347)
rakita Apr 21, 2024
16b3f21
refactor: shrink OpCodeInfo and add more methods (#1307)
DaniPopes Apr 29, 2024
9165a4a
chore: don't clone bytes in `Bytecode::bytes` (#1344)
DaniPopes Apr 29, 2024
9ec2248
chore(deps): bump aurora-engine-modexp from 1.0.0 to 1.1.0 (#1339)
dependabot[bot] Apr 29, 2024
215a4c4
fix: return the correct error in resize_memory (#1359)
DaniPopes Apr 29, 2024
f59ce8c
feat(interpreter): add helpers for spending all gas (#1360)
DaniPopes Apr 29, 2024
ac3fa4f
perf: remove bounds check in DUP, SWAP/EXCHANGE (#1346)
DaniPopes Apr 29, 2024
1f44e4c
chore(deps): bump hashbrown from 0.14.3 to 0.14.5 (#1365)
dependabot[bot] Apr 29, 2024
3f5bb76
feat: Add uniswap V2 WETH-USDC swap example (#1353)
pawurb Apr 29, 2024
a2279f2
perf(interpreter): rewrite gas accounting for memory expansion (#1361)
DaniPopes Apr 29, 2024
67c13f3
feat: parse opcodes from strings (#1358)
DaniPopes May 1, 2024
aceb093
feat(Handler): Add ClearHandle (#1368)
rakita May 1, 2024
14f79e3
Merge branch 'bluealloy:main' into main
BigBenlau May 2, 2024
3108aa7
chore: release
actions-user May 2, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
feat: Add uniswap V2 WETH-USDC swap example (bluealloy#1353)
* Add uniswap V2 WETH-USDC swap example

* Use alloy primitives

* Use revm primitives

* Use alloydb

* Use only necessary pair slots

* typo

* Simplify error handling

* Improve numbers decoding

* Dont use panic for errors

* Remove unused feature

* Use alloydb instead of manually fetching data
  • Loading branch information
pawurb authored Apr 29, 2024
commit 3f5bb7651a26c3562e6202fdbb5405b81407ccb5
29 changes: 27 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions crates/revm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,12 @@ alloy-rpc-types = {git = "https://github.com/alloy-rs/alloy.git", optional = tru
alloy-transport = {git = "https://github.com/alloy-rs/alloy.git", optional = true, default-features = false }

[dev-dependencies]
alloy-sol-types = { version = "0.7.0", default-features = false, features = ["std"] }
ethers-contract = { version = "2.0.14", default-features = false }
anyhow = "1.0.82"
criterion = "0.5"
indicatif = "0.17"
reqwest = { version = "0.12" }

alloy-provider = { git = "https://github.com/alloy-rs/alloy.git", default-features = false, features = ["reqwest"] }
# needed for enabling TLS to use HTTPS connections when testing alloy DB
Expand Down Expand Up @@ -136,6 +138,11 @@ name = "db_by_ref"
path = "../../examples/db_by_ref.rs"
required-features = ["std", "serde-json"]

[[example]]
name = "uniswap_v2_usdc_swap"
path = "../../examples/uniswap_v2_usdc_swap.rs"
required-features = ["alloydb"]

[[bench]]
name = "bench"
path = "benches/bench.rs"
Expand Down
278 changes: 278 additions & 0 deletions examples/uniswap_v2_usdc_swap.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,278 @@
use alloy_provider::{network::Ethereum, ProviderBuilder, RootProvider};
use alloy_sol_types::{sol, SolCall, SolValue};
use alloy_transport_http::Http;
use anyhow::{anyhow, Result};
use reqwest::Client;
use revm::{
db::{AlloyDB, CacheDB},
primitives::{
address, keccak256, AccountInfo, Address, Bytes, ExecutionResult, Output, TransactTo, U256,
},
Evm,
};
use std::ops::Div;
use std::sync::Arc;

type AlloyCacheDB = CacheDB<AlloyDB<Http<Client>, Ethereum, Arc<RootProvider<Http<Client>>>>>;

#[tokio::main]
async fn main() -> Result<()> {
let client = ProviderBuilder::new()
.on_reqwest_http(
"https://mainnet.infura.io/v3/c60b0bb42f8a4c6481ecd229eddaca27"
.parse()
.unwrap(),
)
.unwrap();
let client = Arc::new(client);
let mut cache_db = CacheDB::new(AlloyDB::new(client, None));

// Random empty account
let account = address!("18B06aaF27d44B756FCF16Ca20C1f183EB49111f");

let weth = address!("c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2");
let usdc = address!("a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48");
let usdc_weth_pair = address!("B4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc");

let weth_balance_slot = U256::from(3);

// give our test account some fake WETH and ETH
let one_ether = U256::from(1_000_000_000_000_000_000u128);
let hashed_acc_balance_slot = keccak256((account, weth_balance_slot).abi_encode());
cache_db
.insert_account_storage(weth, hashed_acc_balance_slot.into(), one_ether)
.unwrap();

let acc_info = AccountInfo {
nonce: 0_u64,
balance: one_ether,
code_hash: keccak256(Bytes::new()),
code: None,
};
cache_db.insert_account_info(account, acc_info);

let acc_weth_balance_before = balance_of(weth, account, &mut cache_db)?;
println!("WETH balance before swap: {}", acc_weth_balance_before);
let acc_usdc_balance_before = balance_of(usdc, account, &mut cache_db)?;
println!("USDC balance before swap: {}", acc_usdc_balance_before);

let (reserve0, reserve1) = get_reserves(usdc_weth_pair, &mut cache_db)?;

let amount_in = one_ether.div(U256::from(10));

// calculate USDC amount out
let amount_out = get_amount_out(amount_in, reserve1, reserve0, &mut cache_db).await?;

// transfer WETH to USDC-WETH pair
transfer(account, usdc_weth_pair, amount_in, weth, &mut cache_db)?;

// execute low-level swap without using UniswapV2 router
swap(
account,
usdc_weth_pair,
account,
amount_out,
true,
&mut cache_db,
)?;

let acc_weth_balance_after = balance_of(weth, account, &mut cache_db)?;
println!("WETH balance after swap: {}", acc_weth_balance_after);
let acc_usdc_balance_after = balance_of(usdc, account, &mut cache_db)?;
println!("USDC balance after swap: {}", acc_usdc_balance_after);

Ok(())
}

fn balance_of(token: Address, address: Address, cache_db: &mut AlloyCacheDB) -> Result<U256> {
sol! {
function balanceOf(address account) public returns (uint256);
}

let encoded = balanceOfCall { account: address }.abi_encode();

let mut evm = Evm::builder()
.with_db(cache_db)
.modify_tx_env(|tx| {
// 0x1 because calling USDC proxy from zero address fails
tx.caller = address!("0000000000000000000000000000000000000001");
tx.transact_to = TransactTo::Call(token);
tx.data = encoded.into();
tx.value = U256::from(0);
})
.build();

let ref_tx = evm.transact().unwrap();
let result = ref_tx.result;

let value = match result {
ExecutionResult::Success {
output: Output::Call(value),
..
} => value,
result => return Err(anyhow!("'balanceOf' execution failed: {result:?}")),
};

let balance = <U256>::abi_decode(&value, false)?;

Ok(balance)
}

async fn get_amount_out(
amount_in: U256,
reserve_in: U256,
reserve_out: U256,
cache_db: &mut AlloyCacheDB,
) -> Result<U256> {
let uniswap_v2_router = address!("7a250d5630b4cf539739df2c5dacb4c659f2488d");
sol! {
function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) external pure returns (uint amountOut);
}

let encoded = getAmountOutCall {
amountIn: amount_in,
reserveIn: reserve_in,
reserveOut: reserve_out,
}
.abi_encode();

let mut evm = Evm::builder()
.with_db(cache_db)
.modify_tx_env(|tx| {
tx.caller = address!("0000000000000000000000000000000000000000");
tx.transact_to = TransactTo::Call(uniswap_v2_router);
tx.data = encoded.into();
tx.value = U256::from(0);
})
.build();

let ref_tx = evm.transact().unwrap();
let result = ref_tx.result;

let value = match result {
ExecutionResult::Success {
output: Output::Call(value),
..
} => value,
result => return Err(anyhow!("'getAmountOut' execution failed: {result:?}")),
};

let amount_out = <U256>::abi_decode(&value, false)?;

Ok(amount_out)
}

fn get_reserves(pair_address: Address, cache_db: &mut AlloyCacheDB) -> Result<(U256, U256)> {
sol! {
function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
}

let encoded = getReservesCall {}.abi_encode();

let mut evm = Evm::builder()
.with_db(cache_db)
.modify_tx_env(|tx| {
tx.caller = address!("0000000000000000000000000000000000000000");
tx.transact_to = TransactTo::Call(pair_address);
tx.data = encoded.into();
tx.value = U256::from(0);
})
.build();

let ref_tx = evm.transact().unwrap();
let result = ref_tx.result;

let value = match result {
ExecutionResult::Success {
output: Output::Call(value),
..
} => value,
result => return Err(anyhow!("'getReserves' execution failed: {result:?}")),
};

let (reserve0, reserve1, _) = <(U256, U256, u32)>::abi_decode(&value, false)?;

Ok((reserve0, reserve1))
}

fn swap(
from: Address,
pool_address: Address,
target: Address,
amount_out: U256,
is_token0: bool,
cache_db: &mut AlloyCacheDB,
) -> Result<()> {
sol! {
function swap(uint amount0Out, uint amount1Out, address target, bytes callback) external;
}

let amount0_out = if is_token0 { amount_out } else { U256::from(0) };
let amount1_out = if is_token0 { U256::from(0) } else { amount_out };

let encoded = swapCall {
amount0Out: amount0_out,
amount1Out: amount1_out,
target,
callback: Bytes::new(),
}
.abi_encode();

let mut evm = Evm::builder()
.with_db(cache_db)
.modify_tx_env(|tx| {
tx.caller = from;
tx.transact_to = TransactTo::Call(pool_address);
tx.data = encoded.into();
tx.value = U256::from(0);
})
.build();

let ref_tx = evm.transact_commit().unwrap();

match ref_tx {
ExecutionResult::Success { .. } => {}
result => return Err(anyhow!("'swap' execution failed: {result:?}")),
};

Ok(())
}

fn transfer(
from: Address,
to: Address,
amount: U256,
token: Address,
cache_db: &mut AlloyCacheDB,
) -> Result<()> {
sol! {
function transfer(address to, uint amount) external returns (bool);
}

let encoded = transferCall { to, amount }.abi_encode();

let mut evm = Evm::builder()
.with_db(cache_db)
.modify_tx_env(|tx| {
tx.caller = from;
tx.transact_to = TransactTo::Call(token);
tx.data = encoded.into();
tx.value = U256::from(0);
})
.build();

let ref_tx = evm.transact_commit().unwrap();
let success: bool = match ref_tx {
ExecutionResult::Success {
output: Output::Call(value),
..
} => <bool>::abi_decode(&value, false)?,
result => return Err(anyhow!("'transfer' execution failed: {result:?}")),
};

if !success {
return Err(anyhow!("'transfer' failed"));
}

Ok(())
}