From d21a194a5648f7c75167534d3dc95445e821f9f8 Mon Sep 17 00:00:00 2001 From: royvardhan Date: Sun, 8 Dec 2024 13:13:18 +0530 Subject: [PATCH 01/18] feat: add erc20_gas and impl PreExecutionHandler --- Cargo.lock | 384 +++++++++++++++++++++++++++------ Cargo.toml | 1 + examples/erc20_gas/Cargo.toml | 41 ++++ examples/erc20_gas/src/main.rs | 136 ++++++++++++ 4 files changed, 501 insertions(+), 61 deletions(-) create mode 100644 examples/erc20_gas/Cargo.toml create mode 100644 examples/erc20_gas/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index fa90de124f..f76628474a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -66,16 +66,32 @@ dependencies = [ "strum", ] +[[package]] +name = "alloy-consensus" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "705687d5bfd019fee57cf9e206b27b30a9a9617535d5590a02b171e813208f8e" +dependencies = [ + "alloy-eips 0.4.2", + "alloy-primitives 0.8.12", + "alloy-rlp", + "alloy-serde 0.4.2", + "auto_impl", + "c-kzg", + "derive_more 1.0.0", + "serde", +] + [[package]] name = "alloy-consensus" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae09ffd7c29062431dd86061deefe4e3c6f07fa0d674930095f8dcedb0baf02c" dependencies = [ - "alloy-eips", - "alloy-primitives", + "alloy-eips 0.6.4", + "alloy-primitives 0.8.12", "alloy-rlp", - "alloy-serde", + "alloy-serde 0.6.4", "auto_impl", "c-kzg", "derive_more 1.0.0", @@ -88,7 +104,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0069cf0642457f87a01a014f6dc29d5d893cd4fd8fddf0c3cdfad1bb3ebafc41" dependencies = [ - "alloy-primitives", + "alloy-primitives 0.8.12", "alloy-rlp", "serde", ] @@ -99,7 +115,7 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea59dc42102bc9a1905dc57901edc6dd48b9f38115df86c7d252acba70d71d04" dependencies = [ - "alloy-primitives", + "alloy-primitives 0.8.12", "alloy-rlp", "k256", "serde", @@ -111,12 +127,30 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f6cee6a35793f3db8a5ffe60e86c695f321d081a567211245f503e8c498fce8" dependencies = [ - "alloy-primitives", + "alloy-primitives 0.8.12", "alloy-rlp", "derive_more 1.0.0", "serde", ] +[[package]] +name = "alloy-eips" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ffb906284a1e1f63c4607da2068c8197458a352d0b3e9796e67353d72a9be85" +dependencies = [ + "alloy-eip2930", + "alloy-eip7702 0.1.1", + "alloy-primitives 0.8.12", + "alloy-rlp", + "alloy-serde 0.4.2", + "c-kzg", + "derive_more 1.0.0", + "once_cell", + "serde", + "sha2", +] + [[package]] name = "alloy-eips" version = "0.6.4" @@ -125,9 +159,9 @@ checksum = "5b6aa3961694b30ba53d41006131a2fca3bdab22e4c344e46db2c639e7c2dfdd" dependencies = [ "alloy-eip2930", "alloy-eip7702 0.4.1", - "alloy-primitives", + "alloy-primitives 0.8.12", "alloy-rlp", - "alloy-serde", + "alloy-serde 0.6.4", "c-kzg", "derive_more 1.0.0", "once_cell", @@ -141,19 +175,33 @@ version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b84c506bf264110fa7e90d9924f742f40ef53c6572ea56a0b0bd714a567ed389" dependencies = [ - "alloy-primitives", + "alloy-primitives 0.8.12", "alloy-sol-type-parser", "serde", "serde_json", ] +[[package]] +name = "alloy-json-rpc" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fa8a1a3c4cbd221f2b8e3693aeb328fca79a757fe556ed08e47bbbc2a70db7" +dependencies = [ + "alloy-primitives 0.8.12", + "alloy-sol-types", + "serde", + "serde_json", + "thiserror", + "tracing", +] + [[package]] name = "alloy-json-rpc" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3694b7e480728c0b3e228384f223937f14c10caef5a4c766021190fc8f283d35" dependencies = [ - "alloy-primitives", + "alloy-primitives 0.8.12", "alloy-sol-types", "serde", "serde_json", @@ -161,20 +209,41 @@ dependencies = [ "tracing", ] +[[package]] +name = "alloy-network" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85fa23a6a9d612b52e402c995f2d582c25165ec03ac6edf64c861a76bc5b87cd" +dependencies = [ + "alloy-consensus 0.4.2", + "alloy-eips 0.4.2", + "alloy-json-rpc 0.4.2", + "alloy-network-primitives 0.4.2", + "alloy-primitives 0.8.12", + "alloy-rpc-types-eth 0.4.2", + "alloy-serde 0.4.2", + "alloy-signer 0.4.2", + "alloy-sol-types", + "async-trait", + "auto_impl", + "futures-utils-wasm", + "thiserror", +] + [[package]] name = "alloy-network" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea94b8ceb5c75d7df0a93ba0acc53b55a22b47b532b600a800a87ef04eb5b0b4" dependencies = [ - "alloy-consensus", - "alloy-eips", - "alloy-json-rpc", - "alloy-network-primitives", - "alloy-primitives", - "alloy-rpc-types-eth", - "alloy-serde", - "alloy-signer", + "alloy-consensus 0.6.4", + "alloy-eips 0.6.4", + "alloy-json-rpc 0.6.4", + "alloy-network-primitives 0.6.4", + "alloy-primitives 0.8.12", + "alloy-rpc-types-eth 0.6.4", + "alloy-serde 0.6.4", + "alloy-signer 0.6.4", "alloy-sol-types", "async-trait", "auto_impl", @@ -184,17 +253,52 @@ dependencies = [ "thiserror", ] +[[package]] +name = "alloy-network-primitives" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "801492711d4392b2ccf5fc0bc69e299fa1aab15167d74dcaa9aab96a54f684bd" +dependencies = [ + "alloy-consensus 0.4.2", + "alloy-eips 0.4.2", + "alloy-primitives 0.8.12", + "alloy-serde 0.4.2", + "serde", +] + [[package]] name = "alloy-network-primitives" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df9f3e281005943944d15ee8491534a1c7b3cbf7a7de26f8c433b842b93eb5f9" dependencies = [ - "alloy-consensus", - "alloy-eips", - "alloy-primitives", - "alloy-serde", + "alloy-consensus 0.6.4", + "alloy-eips 0.6.4", + "alloy-primitives 0.8.12", + "alloy-serde 0.6.4", + "serde", +] + +[[package]] +name = "alloy-primitives" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "600d34d8de81e23b6d909c094e23b3d357e01ca36b78a8c5424c501eedbe86f0" +dependencies = [ + "alloy-rlp", + "bytes", + "cfg-if", + "const-hex", + "derive_more 0.99.18", + "hex-literal", + "itoa", + "k256", + "keccak-asm", + "proptest", + "rand", + "ruint", "serde", + "tiny-keccak", ] [[package]] @@ -229,6 +333,40 @@ dependencies = [ "tiny-keccak", ] +[[package]] +name = "alloy-provider" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcfaa4ffec0af04e3555686b8aacbcdf7d13638133a0672749209069750f78a6" +dependencies = [ + "alloy-chains", + "alloy-consensus 0.4.2", + "alloy-eips 0.4.2", + "alloy-json-rpc 0.4.2", + "alloy-network 0.4.2", + "alloy-network-primitives 0.4.2", + "alloy-primitives 0.8.12", + "alloy-rpc-client 0.4.2", + "alloy-rpc-types-eth 0.4.2", + "alloy-transport 0.4.2", + "alloy-transport-http 0.4.2", + "async-stream", + "async-trait", + "auto_impl", + "dashmap", + "futures", + "futures-utils-wasm", + "lru", + "pin-project", + "reqwest", + "serde", + "serde_json", + "thiserror", + "tokio", + "tracing", + "url", +] + [[package]] name = "alloy-provider" version = "0.6.4" @@ -236,16 +374,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40c1f9eede27bf4c13c099e8e64d54efd7ce80ef6ea47478aa75d5d74e2dba3b" dependencies = [ "alloy-chains", - "alloy-consensus", - "alloy-eips", - "alloy-json-rpc", - "alloy-network", - "alloy-network-primitives", - "alloy-primitives", - "alloy-rpc-client", - "alloy-rpc-types-eth", - "alloy-transport", - "alloy-transport-http", + "alloy-consensus 0.6.4", + "alloy-eips 0.6.4", + "alloy-json-rpc 0.6.4", + "alloy-network 0.6.4", + "alloy-network-primitives 0.6.4", + "alloy-primitives 0.8.12", + "alloy-rpc-client 0.6.4", + "alloy-rpc-types-eth 0.6.4", + "alloy-transport 0.6.4", + "alloy-transport-http 0.6.4", "async-stream", "async-trait", "auto_impl", @@ -288,16 +426,38 @@ dependencies = [ "syn 2.0.87", ] +[[package]] +name = "alloy-rpc-client" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "370143ed581aace6e663342d21d209c6b2e34ee6142f7d6675adb518deeaf0dc" +dependencies = [ + "alloy-json-rpc 0.4.2", + "alloy-primitives 0.8.12", + "alloy-transport 0.4.2", + "alloy-transport-http 0.4.2", + "futures", + "pin-project", + "reqwest", + "serde", + "serde_json", + "tokio", + "tokio-stream", + "tower 0.5.1", + "tracing", + "url", +] + [[package]] name = "alloy-rpc-client" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "374dbe0dc3abdc2c964f36b3d3edf9cdb3db29d16bda34aa123f03d810bec1dd" dependencies = [ - "alloy-json-rpc", - "alloy-primitives", - "alloy-transport", - "alloy-transport-http", + "alloy-json-rpc 0.6.4", + "alloy-primitives 0.8.12", + "alloy-transport 0.6.4", + "alloy-transport-http 0.6.4", "futures", "pin-project", "reqwest", @@ -311,18 +471,37 @@ dependencies = [ "wasmtimer", ] +[[package]] +name = "alloy-rpc-types-eth" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413f4aa3ccf2c3e4234a047c5fa4727916d7daf25a89f9b765df0ba09784fd87" +dependencies = [ + "alloy-consensus 0.4.2", + "alloy-eips 0.4.2", + "alloy-network-primitives 0.4.2", + "alloy-primitives 0.8.12", + "alloy-rlp", + "alloy-serde 0.4.2", + "alloy-sol-types", + "derive_more 1.0.0", + "itertools 0.13.0", + "serde", + "serde_json", +] + [[package]] name = "alloy-rpc-types-eth" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8a477281940d82d29315846c7216db45b15e90bcd52309da9f54bcf7ad94a11" dependencies = [ - "alloy-consensus", - "alloy-eips", - "alloy-network-primitives", - "alloy-primitives", + "alloy-consensus 0.6.4", + "alloy-eips 0.6.4", + "alloy-network-primitives 0.6.4", + "alloy-primitives 0.8.12", "alloy-rlp", - "alloy-serde", + "alloy-serde 0.6.4", "alloy-sol-types", "derive_more 1.0.0", "itertools 0.13.0", @@ -330,24 +509,49 @@ dependencies = [ "serde_json", ] +[[package]] +name = "alloy-serde" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dff0ab1cdd43ca001e324dc27ee0e8606bd2161d6623c63e0e0b8c4dfc13600" +dependencies = [ + "alloy-primitives 0.8.12", + "serde", + "serde_json", +] + [[package]] name = "alloy-serde" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dfa4a7ccf15b2492bb68088692481fd6b2604ccbee1d0d6c44c21427ae4df83" dependencies = [ - "alloy-primitives", + "alloy-primitives 0.8.12", "serde", "serde_json", ] +[[package]] +name = "alloy-signer" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fd4e0ad79c81a27ca659be5d176ca12399141659fef2bcbfdc848da478f4504" +dependencies = [ + "alloy-primitives 0.8.12", + "async-trait", + "auto_impl", + "elliptic-curve", + "k256", + "thiserror", +] + [[package]] name = "alloy-signer" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e10aec39d60dc27edcac447302c7803d2371946fb737245320a05b78eb2fafd" dependencies = [ - "alloy-primitives", + "alloy-primitives 0.8.12", "async-trait", "auto_impl", "elliptic-curve", @@ -419,19 +623,38 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6520d427d4a8eb7aa803d852d7a52ceb0c519e784c292f64bb339e636918cf27" dependencies = [ "alloy-json-abi", - "alloy-primitives", + "alloy-primitives 0.8.12", "alloy-sol-macro", "const-hex", "serde", ] +[[package]] +name = "alloy-transport" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ac3e97dad3d31770db0fc89bd6a63b789fbae78963086733f960cf32c483904" +dependencies = [ + "alloy-json-rpc 0.4.2", + "base64", + "futures-util", + "futures-utils-wasm", + "serde", + "serde_json", + "thiserror", + "tokio", + "tower 0.5.1", + "tracing", + "url", +] + [[package]] name = "alloy-transport" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f99acddb34000d104961897dbb0240298e8b775a7efffb9fda2a1a3efedd65b3" dependencies = [ - "alloy-json-rpc", + "alloy-json-rpc 0.6.4", "base64", "futures-util", "futures-utils-wasm", @@ -445,14 +668,29 @@ dependencies = [ "wasmtimer", ] +[[package]] +name = "alloy-transport-http" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b367dcccada5b28987c2296717ee04b9a5637aacd78eacb1726ef211678b5212" +dependencies = [ + "alloy-json-rpc 0.4.2", + "alloy-transport 0.4.2", + "reqwest", + "serde_json", + "tower 0.5.1", + "tracing", + "url", +] + [[package]] name = "alloy-transport-http" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5dc013132e34eeadaa0add7e74164c1503988bfba8bae885b32e0918ba85a8a6" dependencies = [ - "alloy-json-rpc", - "alloy-transport", + "alloy-json-rpc 0.6.4", + "alloy-transport 0.6.4", "reqwest", "serde_json", "tower 0.5.1", @@ -997,6 +1235,12 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + [[package]] name = "core-foundation" version = "0.9.4" @@ -1174,8 +1418,10 @@ version = "0.99.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" dependencies = [ + "convert_case", "proc-macro2", "quote", + "rustc_version 0.4.1", "syn 2.0.87", ] @@ -1403,9 +1649,9 @@ dependencies = [ name = "example-block-traces" version = "0.0.0" dependencies = [ - "alloy-consensus", - "alloy-eips", - "alloy-provider", + "alloy-consensus 0.6.4", + "alloy-eips 0.6.4", + "alloy-provider 0.6.4", "anyhow", "indicatif", "revm", @@ -1431,12 +1677,28 @@ dependencies = [ "revm", ] +[[package]] +name = "example-erc20-gas" +version = "0.0.0" +dependencies = [ + "alloy-eips 0.4.2", + "alloy-primitives 0.6.4", + "alloy-provider 0.4.2", + "alloy-sol-types", + "alloy-transport-http 0.4.2", + "anyhow", + "reqwest", + "revm", + "revm-database", + "tokio", +] + [[package]] name = "example-uniswap-get-reserves" version = "0.0.0" dependencies = [ - "alloy-eips", - "alloy-provider", + "alloy-eips 0.6.4", + "alloy-provider 0.6.4", "alloy-sol-types", "anyhow", "revm", @@ -1448,10 +1710,10 @@ dependencies = [ name = "example-uniswap-v2-usdc-swap" version = "0.0.0" dependencies = [ - "alloy-eips", - "alloy-provider", + "alloy-eips 0.6.4", + "alloy-provider 0.6.4", "alloy-sol-types", - "alloy-transport-http", + "alloy-transport-http 0.6.4", "anyhow", "reqwest", "revm", @@ -2932,7 +3194,7 @@ dependencies = [ name = "revm" version = "14.0.1" dependencies = [ - "alloy-provider", + "alloy-provider 0.6.4", "alloy-sol-types", "anyhow", "criterion", @@ -3000,10 +3262,10 @@ dependencies = [ name = "revm-database" version = "1.0.0" dependencies = [ - "alloy-eips", - "alloy-provider", + "alloy-eips 0.6.4", + "alloy-provider 0.6.4", "alloy-sol-types", - "alloy-transport", + "alloy-transport 0.6.4", "anyhow", "auto_impl", "criterion", @@ -3136,7 +3398,7 @@ dependencies = [ name = "revm-primitives" version = "9.0.1" dependencies = [ - "alloy-primitives", + "alloy-primitives 0.8.12", ] [[package]] @@ -3145,7 +3407,7 @@ version = "1.0.0" dependencies = [ "alloy-eip2930", "alloy-eip7702 0.1.1", - "alloy-primitives", + "alloy-primitives 0.8.12", "enumn", "revm-primitives", "serde", diff --git a/Cargo.toml b/Cargo.toml index fba5545a7f..bb58c86a50 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,6 +32,7 @@ members = [ "examples/database_components", "examples/uniswap_get_reserves", "examples/uniswap_v2_usdc_swap", + "examples/erc20_gas", #"examples/custom_opcodes", ] resolver = "2" diff --git a/examples/erc20_gas/Cargo.toml b/examples/erc20_gas/Cargo.toml new file mode 100644 index 0000000000..1d469bcf51 --- /dev/null +++ b/examples/erc20_gas/Cargo.toml @@ -0,0 +1,41 @@ +[package] +name = "example-erc20-gas" +version = "0.0.0" +publish = false +authors.workspace = true +edition.workspace = true +keywords.workspace = true +license.workspace = true +repository.workspace = true +readme.workspace = true + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] + +[lints.rust] +unreachable_pub = "warn" +unused_must_use = "deny" +rust_2018_idioms = "deny" + +[lints.rustdoc] +all = "warn" + +[dependencies] +revm.workspace = true +database = { workspace = true, features = ["std", "alloydb"] } + +# alloy dependencies +alloy-sol-types = { version = "0.8.2", default-features = false, features = ["std"] } +alloy-primitives = "0.6" +alloy-eips = "0.4.2" +alloy-transport-http = "0.4.2" +alloy-provider = "0.4.2" +reqwest = { version = "0.12" } +anyhow = "1.0.89" + +# async runtime +tokio = { version = "1.40", features = ["rt-multi-thread", "macros"] } + + + diff --git a/examples/erc20_gas/src/main.rs b/examples/erc20_gas/src/main.rs new file mode 100644 index 0000000000..38d93eb4f3 --- /dev/null +++ b/examples/erc20_gas/src/main.rs @@ -0,0 +1,136 @@ +use std::convert::Infallible; + +use revm::{primitives::{address, keccak256, Address, U256}, state::EvmStorageSlot}; +use alloy_provider::{network::Ethereum, RootProvider}; +use alloy_transport_http::Http; +use database::{AlloyDB, CacheDB}; +use reqwest::Client; +use revm::{ + context_interface::{ + result::InvalidTransaction, transaction::Eip4844Tx, Block, JournalStateGetter, JournalStateGetterDBError, Transaction, TransactionGetter, TransactionType + }, + database_interface::WrapDatabaseAsync, + handler::{EthPreExecution, EthPreExecutionContext, EthPreExecutionError}, + handler_interface::PreExecutionHandler, + Context, Database, +}; +use alloy_sol_types::{sol, SolValue}; + + +sol! { + interface IERC20 { + function transfer(address to, uint256 amount) external returns (bool); + function balanceOf(address owner) external view returns (uint256); + function allowance(address owner, address spender) external view returns (uint256); + function approve(address spender, uint256 amount) external returns (bool); + function transferFrom(address from, address to, uint256 amount) external returns (bool); + } +} + + + +type AlloyCacheDB = + CacheDB, Ethereum, RootProvider>>>>; + +// Constants +const TOKEN: Address = address!("1234567890123456789012345678901234567890"); +const TREASURY: Address = address!("0000000000000000000000000000000000000001"); +const ERC20_TRANSFER_SIGNATURE: [u8; 4] = [0xa9, 0x05, 0x9c, 0xbb]; // keccak256("transfer(address,uint256)")[:4] + + + +#[derive(Debug)] +pub enum Erc20PreExecutionError { + Whatever +} + +impl From for Erc20PreExecutionError { + fn from(_: Infallible) -> Self { + Self::Whatever + } +} + +impl From for Erc20PreExecutionError { + fn from(_: InvalidTransaction) -> Self { + Self::Whatever + } +} + + + + +struct Erc20PreExecution { + inner: EthPreExecution +} + +impl Erc20PreExecution { + fn new() -> Self { + Self { + inner: EthPreExecution::new() + } + } +} + +impl PreExecutionHandler for Erc20PreExecution { + type Context = Context; + type Error = Erc20PreExecutionError; + + fn load_accounts(&self, context: &mut Self::Context) -> Result<(), Self::Error> { + self.inner.load_accounts(context) + } + + fn apply_eip7702_auth_list(&self, context: &mut Self::Context) -> Result { + self.inner.apply_eip7702_auth_list(context) + } + + fn deduct_caller(&self, context: &mut Self::Context) -> Result<(), Self::Error> { + let basefee = context.block.basefee(); + let blob_price = U256::from(context.block.blob_gasprice().unwrap_or_default()); + let effective_gas_price = context.tx().effective_gas_price(*basefee); + + // Subtract gas costs from the caller's account. + // We need to saturate the gas cost to prevent underflow in case that `disable_balance_check` is enabled. + + let mut gas_cost = U256::from(context.tx().common_fields().gas_limit()) + .saturating_mul(effective_gas_price); + + + // EIP-4844 + if context.tx().tx_type() == TransactionType::Eip4844 { + let blob_gas = U256::from(context.tx().eip4844().total_blob_gas()); + gas_cost = gas_cost.saturating_add(blob_price.saturating_mul(blob_gas)); + } + + // Get the balance slot for the caller + let caller = context.tx().common_fields().caller(); + let balance_slot: U256 = keccak256((caller, U256::from(3)).abi_encode()).into(); + + let token_account = context.journal().load_account(TOKEN)?.data; + + let storage_value = token_account.storage.get(&balance_slot).expect("Balance slot not found").present_value(); + + if storage_value < gas_cost { + return Err(Erc20PreExecutionError::Whatever); + } + + // Subtract the gas cost from the caller's balance + let new_balance = storage_value.saturating_sub(gas_cost); + + token_account.storage.insert(balance_slot, EvmStorageSlot::new_changed(storage_value, new_balance)); + + // We could add the gas cost to the treasury's balance + let treasury_account = context.journal().load_account(TREASURY)?.data; + let treasury_balance_slot: U256 = keccak256((TREASURY, U256::from(3)).abi_encode()).into(); + let treasury_balance = treasury_account.storage.get(&treasury_balance_slot).expect("Treasury balance slot not found").present_value(); + let new_treasury_balance = treasury_balance.saturating_add(gas_cost); + treasury_account.storage.insert(treasury_balance_slot, EvmStorageSlot::new_changed(treasury_balance, new_treasury_balance)); + + Ok(()) + } +} + + + +fn main() { + println!("Hello, world!"); +} From 3fcafd47937cf5cc135df5bbfd62f7d68e150836 Mon Sep 17 00:00:00 2001 From: royvardhan Date: Sun, 8 Dec 2024 22:23:30 +0530 Subject: [PATCH 02/18] feat: shorten --- examples/erc20_gas/src/main.rs | 48 +++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/examples/erc20_gas/src/main.rs b/examples/erc20_gas/src/main.rs index 38d93eb4f3..b8ec9c8b02 100644 --- a/examples/erc20_gas/src/main.rs +++ b/examples/erc20_gas/src/main.rs @@ -39,8 +39,9 @@ const ERC20_TRANSFER_SIGNATURE: [u8; 4] = [0xa9, 0x05, 0x9c, 0xbb]; // keccak256 -#[derive(Debug)] +#[derive(Debug, Default)] pub enum Erc20PreExecutionError { + #[default] Whatever } @@ -58,7 +59,7 @@ impl From for Erc20PreExecutionError { - +#[derive(Default)] struct Erc20PreExecution { inner: EthPreExecution } @@ -66,7 +67,7 @@ struct Erc20PreExecution { impl Erc20PreExecution { fn new() -> Self { Self { - inner: EthPreExecution::new() + inner: EthPreExecution::default() } } } @@ -103,32 +104,37 @@ impl PreExecutionHandler for Erc20PreExecution { // Get the balance slot for the caller let caller = context.tx().common_fields().caller(); - let balance_slot: U256 = keccak256((caller, U256::from(3)).abi_encode()).into(); - - let token_account = context.journal().load_account(TOKEN)?.data; - let storage_value = token_account.storage.get(&balance_slot).expect("Balance slot not found").present_value(); + token_operation(context, caller, caller, gas_cost)?; - if storage_value < gas_cost { - return Err(Erc20PreExecutionError::Whatever); - } + Ok(()) + } +} - // Subtract the gas cost from the caller's balance - let new_balance = storage_value.saturating_sub(gas_cost); - token_account.storage.insert(balance_slot, EvmStorageSlot::new_changed(storage_value, new_balance)); - // We could add the gas cost to the treasury's balance - let treasury_account = context.journal().load_account(TREASURY)?.data; - let treasury_balance_slot: U256 = keccak256((TREASURY, U256::from(3)).abi_encode()).into(); - let treasury_balance = treasury_account.storage.get(&treasury_balance_slot).expect("Treasury balance slot not found").present_value(); - let new_treasury_balance = treasury_balance.saturating_add(gas_cost); - treasury_account.storage.insert(treasury_balance_slot, EvmStorageSlot::new_changed(treasury_balance, new_treasury_balance)); +fn token_operation(context: &mut Context, sender: Address, recipient: Address, amount: U256) -> Result<(), Erc20PreExecutionError> { + let token_account = context.journal().load_account(TOKEN)?.data; + + let sender_balance_slot: U256 = keccak256((sender, U256::from(3)).abi_encode()).into(); + let sender_balance = token_account.storage.get(&sender_balance_slot).expect("Balance slot not found").present_value(); - Ok(()) + + if sender_balance < amount { + return Err(Erc20PreExecutionError::Whatever); } -} + // Subtract the amount from the sender's balance + let sender_new_balance = sender_balance.saturating_sub(amount); + token_account.storage.insert(sender_balance_slot, EvmStorageSlot::new_changed(sender_balance, sender_new_balance)); + // Add the amount to the recipient's balance + let recipient_balance_slot: U256 = keccak256((recipient, U256::from(3)).abi_encode()).into(); + let recipient_balance = token_account.storage.get(&recipient_balance_slot).expect("To balance slot not found").present_value(); + let recipient_new_balance = recipient_balance.saturating_add(amount); + token_account.storage.insert(recipient_balance_slot, EvmStorageSlot::new_changed(recipient_balance, recipient_new_balance)); + + Ok(()) +} fn main() { From 9de7f79542de6fa54d3058c029cd7ce2535cb78c Mon Sep 17 00:00:00 2001 From: royvardhan Date: Sun, 8 Dec 2024 22:57:38 +0530 Subject: [PATCH 03/18] feat: impl post execution trait --- Cargo.lock | 1 + examples/erc20_gas/Cargo.toml | 2 + examples/erc20_gas/src/main.rs | 81 +++++++++++++++++++++++++++++++++- 3 files changed, 82 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f76628474a..f43afc3a32 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1690,6 +1690,7 @@ dependencies = [ "reqwest", "revm", "revm-database", + "revm-specification", "tokio", ] diff --git a/examples/erc20_gas/Cargo.toml b/examples/erc20_gas/Cargo.toml index 1d469bcf51..35b0781490 100644 --- a/examples/erc20_gas/Cargo.toml +++ b/examples/erc20_gas/Cargo.toml @@ -9,6 +9,7 @@ license.workspace = true repository.workspace = true readme.workspace = true + [package.metadata.docs.rs] all-features = true rustdoc-args = ["--cfg", "docsrs"] @@ -24,6 +25,7 @@ all = "warn" [dependencies] revm.workspace = true database = { workspace = true, features = ["std", "alloydb"] } +specification.workspace = true # alloy dependencies alloy-sol-types = { version = "0.8.2", default-features = false, features = ["std"] } diff --git a/examples/erc20_gas/src/main.rs b/examples/erc20_gas/src/main.rs index b8ec9c8b02..6fe54d7533 100644 --- a/examples/erc20_gas/src/main.rs +++ b/examples/erc20_gas/src/main.rs @@ -1,6 +1,6 @@ use std::convert::Infallible; -use revm::{primitives::{address, keccak256, Address, U256}, state::EvmStorageSlot}; +use revm::{context::Cfg, context_interface::result::{ExecutionResult, HaltReason, HaltReasonTrait, Output, ResultAndState, SuccessReason}, handler::{EthPostExecution, FrameResult}, handler_interface::PostExecutionHandler, primitives::{address, keccak256, Address, U256}, state::EvmStorageSlot}; use alloy_provider::{network::Ethereum, RootProvider}; use alloy_transport_http::Http; use database::{AlloyDB, CacheDB}; @@ -15,6 +15,7 @@ use revm::{ Context, Database, }; use alloy_sol_types::{sol, SolValue}; +use specification::hardfork::SpecId; sol! { @@ -105,7 +106,7 @@ impl PreExecutionHandler for Erc20PreExecution { // Get the balance slot for the caller let caller = context.tx().common_fields().caller(); - token_operation(context, caller, caller, gas_cost)?; + token_operation(context, caller, TREASURY, gas_cost)?; Ok(()) } @@ -137,6 +138,82 @@ fn token_operation(context: &mut Context, sender: Address, recipient: Address, a } + +#[derive(Default)] +struct Erc20PostExecution { + inner: EthPostExecution +} + +#[derive(Default, Eq, PartialEq, Debug, Clone)] +pub enum Erc20HaltReason { + #[default] + Whatever +} + +impl From for Erc20HaltReason { + fn from(_: HaltReason) -> Self { + Self::Whatever + } +} + + + +impl PostExecutionHandler for Erc20PostExecution { + type Context = Context; + type Error = Erc20PreExecutionError; + type ExecResult = FrameResult; + type Output = ResultAndState; + + + // These trait functions do not return anything + fn refund(&self, _: &mut Self::Context, _: &mut Self::ExecResult, _: i64) { + // Do nothing + } + + fn reimburse_caller(&self, context: &mut Self::Context, exec_result: &mut Self::ExecResult) -> Result<(), Self::Error> { + let basefee = context.block.basefee(); + let caller = context.tx().common_fields().caller(); + let effective_gas_price = context.tx().effective_gas_price(*basefee); + let gas = exec_result.gas(); + + let reimbursement = effective_gas_price * U256::from(gas.remaining() + gas.refunded() as u64); + token_operation(context, TREASURY, caller, reimbursement)?; + + + Ok(()) + + } + + fn reward_beneficiary(&self, context: &mut Self::Context, exec_result: &mut Self::ExecResult) -> Result<(), Self::Error> { + let tx = context.tx(); + let beneficiary = context.block.beneficiary(); + let basefee = context.block.basefee(); + let effective_gas_price = tx.effective_gas_price(*basefee); + let gas = exec_result.gas(); + + let coinbase_gas_price = if context.cfg.spec().is_enabled_in(SpecId::LONDON) { + effective_gas_price.saturating_sub(*basefee) + } else { + effective_gas_price + }; + + let reward = coinbase_gas_price * U256::from(gas.spent() - gas.refunded() as u64); + token_operation(context, TREASURY, *beneficiary, reward)?; + + Ok(()) + } + + fn output(&self, _: &mut Self::Context, _: Self::ExecResult) -> Result { + Err(Erc20PreExecutionError::Whatever) + } + + fn clear(&self, _: &mut Self::Context) { + // Do nothing + } +} + + + fn main() { println!("Hello, world!"); } From b50bf6a2c22ac87e3292623ec3be02b3feb09d58 Mon Sep 17 00:00:00 2001 From: royvardhan Date: Sun, 8 Dec 2024 23:31:43 +0530 Subject: [PATCH 04/18] feat: impl Erc20PostExecution new --- examples/erc20_gas/src/main.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/examples/erc20_gas/src/main.rs b/examples/erc20_gas/src/main.rs index 6fe54d7533..152db4ddf9 100644 --- a/examples/erc20_gas/src/main.rs +++ b/examples/erc20_gas/src/main.rs @@ -156,6 +156,14 @@ impl From for Erc20HaltReason { } } +impl Erc20PostExecution { + fn new() -> Self { + Self { + inner: EthPostExecution::default() + } + } +} + impl PostExecutionHandler for Erc20PostExecution { @@ -165,9 +173,8 @@ impl PostExecutionHandler for Erc20PostExecution { type Output = ResultAndState; - // These trait functions do not return anything - fn refund(&self, _: &mut Self::Context, _: &mut Self::ExecResult, _: i64) { - // Do nothing + fn refund(&self, context: &mut Self::Context, exec_result: &mut Self::ExecResult, eip7702_refund: i64) { + self.inner.refund(context, exec_result, eip7702_refund) } fn reimburse_caller(&self, context: &mut Self::Context, exec_result: &mut Self::ExecResult) -> Result<(), Self::Error> { From 34f2dc1ce0d9e4c5981e6a152c135169245080ec Mon Sep 17 00:00:00 2001 From: royvardhan Date: Sun, 8 Dec 2024 23:43:49 +0530 Subject: [PATCH 05/18] fix: Erc20PostExecutionError --- examples/erc20_gas/src/main.rs | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/examples/erc20_gas/src/main.rs b/examples/erc20_gas/src/main.rs index 152db4ddf9..ac95ff8f30 100644 --- a/examples/erc20_gas/src/main.rs +++ b/examples/erc20_gas/src/main.rs @@ -141,7 +141,16 @@ fn token_operation(context: &mut Context, sender: Address, recipient: Address, a #[derive(Default)] struct Erc20PostExecution { - inner: EthPostExecution + inner: EthPostExecution +} + + +impl Erc20PostExecution { + fn new() -> Self { + Self { + inner: EthPostExecution::default() + } + } } #[derive(Default, Eq, PartialEq, Debug, Clone)] @@ -156,19 +165,17 @@ impl From for Erc20HaltReason { } } -impl Erc20PostExecution { - fn new() -> Self { - Self { - inner: EthPostExecution::default() - } - } +#[derive(Default)] +pub enum Erc20PostExecutionError{ + #[default] + Whatever } impl PostExecutionHandler for Erc20PostExecution { type Context = Context; - type Error = Erc20PreExecutionError; + type Error = Erc20PostExecutionError; type ExecResult = FrameResult; type Output = ResultAndState; @@ -184,7 +191,7 @@ impl PostExecutionHandler for Erc20PostExecution { let gas = exec_result.gas(); let reimbursement = effective_gas_price * U256::from(gas.remaining() + gas.refunded() as u64); - token_operation(context, TREASURY, caller, reimbursement)?; + token_operation(context, TREASURY, caller, reimbursement).unwrap(); Ok(()) @@ -205,13 +212,13 @@ impl PostExecutionHandler for Erc20PostExecution { }; let reward = coinbase_gas_price * U256::from(gas.spent() - gas.refunded() as u64); - token_operation(context, TREASURY, *beneficiary, reward)?; + token_operation(context, TREASURY, *beneficiary, reward).unwrap(); Ok(()) } fn output(&self, _: &mut Self::Context, _: Self::ExecResult) -> Result { - Err(Erc20PreExecutionError::Whatever) + Err(Erc20PostExecutionError::Whatever) } fn clear(&self, _: &mut Self::Context) { From 1a47503a2b04693501c1418a2b4dcbb5816776e0 Mon Sep 17 00:00:00 2001 From: royvardhan Date: Mon, 9 Dec 2024 10:53:15 +0530 Subject: [PATCH 06/18] feat: add validation --- examples/erc20_gas/src/main.rs | 149 +++++++++++++++++++++------------ 1 file changed, 94 insertions(+), 55 deletions(-) diff --git a/examples/erc20_gas/src/main.rs b/examples/erc20_gas/src/main.rs index ac95ff8f30..a9dd78e11a 100644 --- a/examples/erc20_gas/src/main.rs +++ b/examples/erc20_gas/src/main.rs @@ -1,18 +1,20 @@ -use std::convert::Infallible; +use std::cmp::Ordering; -use revm::{context::Cfg, context_interface::result::{ExecutionResult, HaltReason, HaltReasonTrait, Output, ResultAndState, SuccessReason}, handler::{EthPostExecution, FrameResult}, handler_interface::PostExecutionHandler, primitives::{address, keccak256, Address, U256}, state::EvmStorageSlot}; +use revm::{context::Cfg, context_interface::result::{ HaltReason, ResultAndState}, handler::{ EthValidation, FrameResult}, handler_interface::{PostExecutionHandler, ValidationHandler}, primitives::{address, keccak256, Address, U256}, state::EvmStorageSlot}; use alloy_provider::{network::Ethereum, RootProvider}; use alloy_transport_http::Http; use database::{AlloyDB, CacheDB}; use reqwest::Client; use revm::{ context_interface::{ - result::InvalidTransaction, transaction::Eip4844Tx, Block, JournalStateGetter, JournalStateGetterDBError, Transaction, TransactionGetter, TransactionType + result::{EVMError, InvalidTransaction}, + transaction::Eip4844Tx, + Block, JournalStateGetter, JournalStateGetterDBError, Transaction, TransactionGetter, TransactionType }, database_interface::WrapDatabaseAsync, - handler::{EthPreExecution, EthPreExecutionContext, EthPreExecutionError}, + handler::{EthPreExecution, EthPostExecution}, handler_interface::PreExecutionHandler, - Context, Database, + Context, }; use alloy_sol_types::{sol, SolValue}; use specification::hardfork::SpecId; @@ -40,42 +42,26 @@ const ERC20_TRANSFER_SIGNATURE: [u8; 4] = [0xa9, 0x05, 0x9c, 0xbb]; // keccak256 -#[derive(Debug, Default)] -pub enum Erc20PreExecutionError { - #[default] - Whatever -} - -impl From for Erc20PreExecutionError { - fn from(_: Infallible) -> Self { - Self::Whatever - } -} +type Erc20Error = EVMError, InvalidTransaction>; -impl From for Erc20PreExecutionError { - fn from(_: InvalidTransaction) -> Self { - Self::Whatever - } -} -#[derive(Default)] struct Erc20PreExecution { - inner: EthPreExecution + inner: EthPreExecution } impl Erc20PreExecution { fn new() -> Self { Self { - inner: EthPreExecution::default() + inner: EthPreExecution::new() } } } impl PreExecutionHandler for Erc20PreExecution { type Context = Context; - type Error = Erc20PreExecutionError; + type Error = Erc20Error; fn load_accounts(&self, context: &mut Self::Context) -> Result<(), Self::Error> { self.inner.load_accounts(context) @@ -114,7 +100,7 @@ impl PreExecutionHandler for Erc20PreExecution { -fn token_operation(context: &mut Context, sender: Address, recipient: Address, amount: U256) -> Result<(), Erc20PreExecutionError> { +fn token_operation(context: &mut Context, sender: Address, recipient: Address, amount: U256) -> Result<(), Erc20Error> { let token_account = context.journal().load_account(TOKEN)?.data; let sender_balance_slot: U256 = keccak256((sender, U256::from(3)).abi_encode()).into(); @@ -122,7 +108,7 @@ fn token_operation(context: &mut Context, sender: Address, recipient: Address, a if sender_balance < amount { - return Err(Erc20PreExecutionError::Whatever); + return Err(EVMError::Transaction(InvalidTransaction::MaxFeePerBlobGasNotSupported)); } // Subtract the amount from the sender's balance let sender_new_balance = sender_balance.saturating_sub(amount); @@ -139,45 +125,26 @@ fn token_operation(context: &mut Context, sender: Address, recipient: Address, a -#[derive(Default)] struct Erc20PostExecution { - inner: EthPostExecution + inner: EthPostExecution } impl Erc20PostExecution { fn new() -> Self { Self { - inner: EthPostExecution::default() + inner: EthPostExecution::new() } } } -#[derive(Default, Eq, PartialEq, Debug, Clone)] -pub enum Erc20HaltReason { - #[default] - Whatever -} - -impl From for Erc20HaltReason { - fn from(_: HaltReason) -> Self { - Self::Whatever - } -} - -#[derive(Default)] -pub enum Erc20PostExecutionError{ - #[default] - Whatever -} - impl PostExecutionHandler for Erc20PostExecution { type Context = Context; - type Error = Erc20PostExecutionError; + type Error = Erc20Error; type ExecResult = FrameResult; - type Output = ResultAndState; + type Output = ResultAndState; fn refund(&self, context: &mut Self::Context, exec_result: &mut Self::ExecResult, eip7702_refund: i64) { @@ -217,16 +184,88 @@ impl PostExecutionHandler for Erc20PostExecution { Ok(()) } - fn output(&self, _: &mut Self::Context, _: Self::ExecResult) -> Result { - Err(Erc20PostExecutionError::Whatever) - } + fn output(&self, context: &mut Self::Context, result: Self::ExecResult) -> Result { + self.inner.output(context, result) + } + + fn clear(&self, context: &mut Self::Context) { + self.inner.clear(context) + } +} + + +struct Erc20Validation { + inner: EthValidation +} - fn clear(&self, _: &mut Self::Context) { - // Do nothing + +impl Erc20Validation { + fn new() -> Self { + Self { + inner: EthValidation::new() + } } } +impl ValidationHandler for Erc20Validation { + type Context = Context; + type Error = Erc20Error; + + + fn validate_env(&self, context: &Self::Context) -> Result<(), Self::Error> { + self.inner.validate_env(context) + } + + fn validate_tx_against_state(&self, context: &mut Self::Context) -> Result<(), Self::Error> { + let caller = context.tx().common_fields().caller(); + let caller_nonce = context.journal().load_account(caller)?.data.info.nonce; + let token_account = context.journal().load_account(TOKEN)?.data.clone(); + + if !context.cfg.is_nonce_check_disabled() { + let tx_nonce = context.tx().common_fields().nonce(); + let state_nonce = caller_nonce; + match tx_nonce.cmp(&state_nonce) { + Ordering::Less => return Err(EVMError::Transaction(InvalidTransaction::NonceTooLow { tx: tx_nonce, state: state_nonce }.into())), + Ordering::Greater => return Err(EVMError::Transaction(InvalidTransaction::NonceTooHigh { tx: tx_nonce, state: state_nonce }.into())), + _ => (), + } + } + + // gas_limit * max_fee + value + let mut balance_check = U256::from(context.tx().common_fields().gas_limit()) + .checked_mul(U256::from(context.tx().max_fee())) + .and_then(|gas_cost| gas_cost.checked_add(context.tx().common_fields().value())) + .ok_or(InvalidTransaction::OverflowPaymentInTransaction)?; + + if context.tx().tx_type() == TransactionType::Eip4844 { + let tx = context.tx().eip4844(); + let data_fee = tx.calc_max_data_fee(); + balance_check = balance_check + .checked_add(data_fee) + .ok_or(InvalidTransaction::OverflowPaymentInTransaction)?; + } + + // Get the account balance from token slot + let account_balance_slot: U256 = keccak256((caller, U256::from(3)).abi_encode()).into(); + let account_balance = token_account.storage.get(&account_balance_slot).expect("Balance slot not found").present_value(); + + if account_balance < balance_check && !context.cfg.is_balance_check_disabled() { + return Err(InvalidTransaction::LackOfFundForMaxFee { + fee: Box::new(balance_check), + balance: Box::new(account_balance), + } + .into()); + }; + + Ok(()) + } + + fn validate_initial_tx_gas(&self, context: &Self::Context) -> Result { + self.inner.validate_initial_tx_gas(context) + } +} + fn main() { println!("Hello, world!"); From bc06bc0dd442a8a3978fa95b4ef492b2f5ca0791 Mon Sep 17 00:00:00 2001 From: royvardhan Date: Mon, 9 Dec 2024 11:08:50 +0530 Subject: [PATCH 07/18] feat: add main and fix imports --- Cargo.lock | 375 ++++------------------ examples/erc20_gas/Cargo.toml | 20 +- examples/erc20_gas/src/main.rs | 71 +++- examples/uniswap_v2_usdc_swap/src/main.rs | 2 +- 4 files changed, 134 insertions(+), 334 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f43afc3a32..4b700a3bf5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -66,32 +66,16 @@ dependencies = [ "strum", ] -[[package]] -name = "alloy-consensus" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "705687d5bfd019fee57cf9e206b27b30a9a9617535d5590a02b171e813208f8e" -dependencies = [ - "alloy-eips 0.4.2", - "alloy-primitives 0.8.12", - "alloy-rlp", - "alloy-serde 0.4.2", - "auto_impl", - "c-kzg", - "derive_more 1.0.0", - "serde", -] - [[package]] name = "alloy-consensus" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae09ffd7c29062431dd86061deefe4e3c6f07fa0d674930095f8dcedb0baf02c" dependencies = [ - "alloy-eips 0.6.4", - "alloy-primitives 0.8.12", + "alloy-eips", + "alloy-primitives", "alloy-rlp", - "alloy-serde 0.6.4", + "alloy-serde", "auto_impl", "c-kzg", "derive_more 1.0.0", @@ -104,7 +88,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0069cf0642457f87a01a014f6dc29d5d893cd4fd8fddf0c3cdfad1bb3ebafc41" dependencies = [ - "alloy-primitives 0.8.12", + "alloy-primitives", "alloy-rlp", "serde", ] @@ -115,7 +99,7 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea59dc42102bc9a1905dc57901edc6dd48b9f38115df86c7d252acba70d71d04" dependencies = [ - "alloy-primitives 0.8.12", + "alloy-primitives", "alloy-rlp", "k256", "serde", @@ -127,30 +111,12 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f6cee6a35793f3db8a5ffe60e86c695f321d081a567211245f503e8c498fce8" dependencies = [ - "alloy-primitives 0.8.12", + "alloy-primitives", "alloy-rlp", "derive_more 1.0.0", "serde", ] -[[package]] -name = "alloy-eips" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ffb906284a1e1f63c4607da2068c8197458a352d0b3e9796e67353d72a9be85" -dependencies = [ - "alloy-eip2930", - "alloy-eip7702 0.1.1", - "alloy-primitives 0.8.12", - "alloy-rlp", - "alloy-serde 0.4.2", - "c-kzg", - "derive_more 1.0.0", - "once_cell", - "serde", - "sha2", -] - [[package]] name = "alloy-eips" version = "0.6.4" @@ -159,9 +125,9 @@ checksum = "5b6aa3961694b30ba53d41006131a2fca3bdab22e4c344e46db2c639e7c2dfdd" dependencies = [ "alloy-eip2930", "alloy-eip7702 0.4.1", - "alloy-primitives 0.8.12", + "alloy-primitives", "alloy-rlp", - "alloy-serde 0.6.4", + "alloy-serde", "c-kzg", "derive_more 1.0.0", "once_cell", @@ -175,33 +141,19 @@ version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b84c506bf264110fa7e90d9924f742f40ef53c6572ea56a0b0bd714a567ed389" dependencies = [ - "alloy-primitives 0.8.12", + "alloy-primitives", "alloy-sol-type-parser", "serde", "serde_json", ] -[[package]] -name = "alloy-json-rpc" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8fa8a1a3c4cbd221f2b8e3693aeb328fca79a757fe556ed08e47bbbc2a70db7" -dependencies = [ - "alloy-primitives 0.8.12", - "alloy-sol-types", - "serde", - "serde_json", - "thiserror", - "tracing", -] - [[package]] name = "alloy-json-rpc" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3694b7e480728c0b3e228384f223937f14c10caef5a4c766021190fc8f283d35" dependencies = [ - "alloy-primitives 0.8.12", + "alloy-primitives", "alloy-sol-types", "serde", "serde_json", @@ -209,41 +161,20 @@ dependencies = [ "tracing", ] -[[package]] -name = "alloy-network" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85fa23a6a9d612b52e402c995f2d582c25165ec03ac6edf64c861a76bc5b87cd" -dependencies = [ - "alloy-consensus 0.4.2", - "alloy-eips 0.4.2", - "alloy-json-rpc 0.4.2", - "alloy-network-primitives 0.4.2", - "alloy-primitives 0.8.12", - "alloy-rpc-types-eth 0.4.2", - "alloy-serde 0.4.2", - "alloy-signer 0.4.2", - "alloy-sol-types", - "async-trait", - "auto_impl", - "futures-utils-wasm", - "thiserror", -] - [[package]] name = "alloy-network" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea94b8ceb5c75d7df0a93ba0acc53b55a22b47b532b600a800a87ef04eb5b0b4" dependencies = [ - "alloy-consensus 0.6.4", - "alloy-eips 0.6.4", - "alloy-json-rpc 0.6.4", - "alloy-network-primitives 0.6.4", - "alloy-primitives 0.8.12", - "alloy-rpc-types-eth 0.6.4", - "alloy-serde 0.6.4", - "alloy-signer 0.6.4", + "alloy-consensus", + "alloy-eips", + "alloy-json-rpc", + "alloy-network-primitives", + "alloy-primitives", + "alloy-rpc-types-eth", + "alloy-serde", + "alloy-signer", "alloy-sol-types", "async-trait", "auto_impl", @@ -253,54 +184,19 @@ dependencies = [ "thiserror", ] -[[package]] -name = "alloy-network-primitives" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "801492711d4392b2ccf5fc0bc69e299fa1aab15167d74dcaa9aab96a54f684bd" -dependencies = [ - "alloy-consensus 0.4.2", - "alloy-eips 0.4.2", - "alloy-primitives 0.8.12", - "alloy-serde 0.4.2", - "serde", -] - [[package]] name = "alloy-network-primitives" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df9f3e281005943944d15ee8491534a1c7b3cbf7a7de26f8c433b842b93eb5f9" dependencies = [ - "alloy-consensus 0.6.4", - "alloy-eips 0.6.4", - "alloy-primitives 0.8.12", - "alloy-serde 0.6.4", + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-serde", "serde", ] -[[package]] -name = "alloy-primitives" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "600d34d8de81e23b6d909c094e23b3d357e01ca36b78a8c5424c501eedbe86f0" -dependencies = [ - "alloy-rlp", - "bytes", - "cfg-if", - "const-hex", - "derive_more 0.99.18", - "hex-literal", - "itoa", - "k256", - "keccak-asm", - "proptest", - "rand", - "ruint", - "serde", - "tiny-keccak", -] - [[package]] name = "alloy-primitives" version = "0.8.12" @@ -333,40 +229,6 @@ dependencies = [ "tiny-keccak", ] -[[package]] -name = "alloy-provider" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcfaa4ffec0af04e3555686b8aacbcdf7d13638133a0672749209069750f78a6" -dependencies = [ - "alloy-chains", - "alloy-consensus 0.4.2", - "alloy-eips 0.4.2", - "alloy-json-rpc 0.4.2", - "alloy-network 0.4.2", - "alloy-network-primitives 0.4.2", - "alloy-primitives 0.8.12", - "alloy-rpc-client 0.4.2", - "alloy-rpc-types-eth 0.4.2", - "alloy-transport 0.4.2", - "alloy-transport-http 0.4.2", - "async-stream", - "async-trait", - "auto_impl", - "dashmap", - "futures", - "futures-utils-wasm", - "lru", - "pin-project", - "reqwest", - "serde", - "serde_json", - "thiserror", - "tokio", - "tracing", - "url", -] - [[package]] name = "alloy-provider" version = "0.6.4" @@ -374,16 +236,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40c1f9eede27bf4c13c099e8e64d54efd7ce80ef6ea47478aa75d5d74e2dba3b" dependencies = [ "alloy-chains", - "alloy-consensus 0.6.4", - "alloy-eips 0.6.4", - "alloy-json-rpc 0.6.4", - "alloy-network 0.6.4", - "alloy-network-primitives 0.6.4", - "alloy-primitives 0.8.12", - "alloy-rpc-client 0.6.4", - "alloy-rpc-types-eth 0.6.4", - "alloy-transport 0.6.4", - "alloy-transport-http 0.6.4", + "alloy-consensus", + "alloy-eips", + "alloy-json-rpc", + "alloy-network", + "alloy-network-primitives", + "alloy-primitives", + "alloy-rpc-client", + "alloy-rpc-types-eth", + "alloy-transport", + "alloy-transport-http", "async-stream", "async-trait", "auto_impl", @@ -426,38 +288,16 @@ dependencies = [ "syn 2.0.87", ] -[[package]] -name = "alloy-rpc-client" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "370143ed581aace6e663342d21d209c6b2e34ee6142f7d6675adb518deeaf0dc" -dependencies = [ - "alloy-json-rpc 0.4.2", - "alloy-primitives 0.8.12", - "alloy-transport 0.4.2", - "alloy-transport-http 0.4.2", - "futures", - "pin-project", - "reqwest", - "serde", - "serde_json", - "tokio", - "tokio-stream", - "tower 0.5.1", - "tracing", - "url", -] - [[package]] name = "alloy-rpc-client" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "374dbe0dc3abdc2c964f36b3d3edf9cdb3db29d16bda34aa123f03d810bec1dd" dependencies = [ - "alloy-json-rpc 0.6.4", - "alloy-primitives 0.8.12", - "alloy-transport 0.6.4", - "alloy-transport-http 0.6.4", + "alloy-json-rpc", + "alloy-primitives", + "alloy-transport", + "alloy-transport-http", "futures", "pin-project", "reqwest", @@ -471,37 +311,18 @@ dependencies = [ "wasmtimer", ] -[[package]] -name = "alloy-rpc-types-eth" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413f4aa3ccf2c3e4234a047c5fa4727916d7daf25a89f9b765df0ba09784fd87" -dependencies = [ - "alloy-consensus 0.4.2", - "alloy-eips 0.4.2", - "alloy-network-primitives 0.4.2", - "alloy-primitives 0.8.12", - "alloy-rlp", - "alloy-serde 0.4.2", - "alloy-sol-types", - "derive_more 1.0.0", - "itertools 0.13.0", - "serde", - "serde_json", -] - [[package]] name = "alloy-rpc-types-eth" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8a477281940d82d29315846c7216db45b15e90bcd52309da9f54bcf7ad94a11" dependencies = [ - "alloy-consensus 0.6.4", - "alloy-eips 0.6.4", - "alloy-network-primitives 0.6.4", - "alloy-primitives 0.8.12", + "alloy-consensus", + "alloy-eips", + "alloy-network-primitives", + "alloy-primitives", "alloy-rlp", - "alloy-serde 0.6.4", + "alloy-serde", "alloy-sol-types", "derive_more 1.0.0", "itertools 0.13.0", @@ -509,49 +330,24 @@ dependencies = [ "serde_json", ] -[[package]] -name = "alloy-serde" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dff0ab1cdd43ca001e324dc27ee0e8606bd2161d6623c63e0e0b8c4dfc13600" -dependencies = [ - "alloy-primitives 0.8.12", - "serde", - "serde_json", -] - [[package]] name = "alloy-serde" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dfa4a7ccf15b2492bb68088692481fd6b2604ccbee1d0d6c44c21427ae4df83" dependencies = [ - "alloy-primitives 0.8.12", + "alloy-primitives", "serde", "serde_json", ] -[[package]] -name = "alloy-signer" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fd4e0ad79c81a27ca659be5d176ca12399141659fef2bcbfdc848da478f4504" -dependencies = [ - "alloy-primitives 0.8.12", - "async-trait", - "auto_impl", - "elliptic-curve", - "k256", - "thiserror", -] - [[package]] name = "alloy-signer" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e10aec39d60dc27edcac447302c7803d2371946fb737245320a05b78eb2fafd" dependencies = [ - "alloy-primitives 0.8.12", + "alloy-primitives", "async-trait", "auto_impl", "elliptic-curve", @@ -623,38 +419,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6520d427d4a8eb7aa803d852d7a52ceb0c519e784c292f64bb339e636918cf27" dependencies = [ "alloy-json-abi", - "alloy-primitives 0.8.12", + "alloy-primitives", "alloy-sol-macro", "const-hex", "serde", ] -[[package]] -name = "alloy-transport" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ac3e97dad3d31770db0fc89bd6a63b789fbae78963086733f960cf32c483904" -dependencies = [ - "alloy-json-rpc 0.4.2", - "base64", - "futures-util", - "futures-utils-wasm", - "serde", - "serde_json", - "thiserror", - "tokio", - "tower 0.5.1", - "tracing", - "url", -] - [[package]] name = "alloy-transport" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f99acddb34000d104961897dbb0240298e8b775a7efffb9fda2a1a3efedd65b3" dependencies = [ - "alloy-json-rpc 0.6.4", + "alloy-json-rpc", "base64", "futures-util", "futures-utils-wasm", @@ -668,29 +445,14 @@ dependencies = [ "wasmtimer", ] -[[package]] -name = "alloy-transport-http" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b367dcccada5b28987c2296717ee04b9a5637aacd78eacb1726ef211678b5212" -dependencies = [ - "alloy-json-rpc 0.4.2", - "alloy-transport 0.4.2", - "reqwest", - "serde_json", - "tower 0.5.1", - "tracing", - "url", -] - [[package]] name = "alloy-transport-http" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5dc013132e34eeadaa0add7e74164c1503988bfba8bae885b32e0918ba85a8a6" dependencies = [ - "alloy-json-rpc 0.6.4", - "alloy-transport 0.6.4", + "alloy-json-rpc", + "alloy-transport", "reqwest", "serde_json", "tower 0.5.1", @@ -1235,12 +997,6 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" -[[package]] -name = "convert_case" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" - [[package]] name = "core-foundation" version = "0.9.4" @@ -1418,10 +1174,8 @@ version = "0.99.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" dependencies = [ - "convert_case", "proc-macro2", "quote", - "rustc_version 0.4.1", "syn 2.0.87", ] @@ -1649,9 +1403,9 @@ dependencies = [ name = "example-block-traces" version = "0.0.0" dependencies = [ - "alloy-consensus 0.6.4", - "alloy-eips 0.6.4", - "alloy-provider 0.6.4", + "alloy-consensus", + "alloy-eips", + "alloy-provider", "anyhow", "indicatif", "revm", @@ -1681,11 +1435,10 @@ dependencies = [ name = "example-erc20-gas" version = "0.0.0" dependencies = [ - "alloy-eips 0.4.2", - "alloy-primitives 0.6.4", - "alloy-provider 0.4.2", + "alloy-eips", + "alloy-provider", "alloy-sol-types", - "alloy-transport-http 0.4.2", + "alloy-transport-http", "anyhow", "reqwest", "revm", @@ -1698,8 +1451,8 @@ dependencies = [ name = "example-uniswap-get-reserves" version = "0.0.0" dependencies = [ - "alloy-eips 0.6.4", - "alloy-provider 0.6.4", + "alloy-eips", + "alloy-provider", "alloy-sol-types", "anyhow", "revm", @@ -1711,10 +1464,10 @@ dependencies = [ name = "example-uniswap-v2-usdc-swap" version = "0.0.0" dependencies = [ - "alloy-eips 0.6.4", - "alloy-provider 0.6.4", + "alloy-eips", + "alloy-provider", "alloy-sol-types", - "alloy-transport-http 0.6.4", + "alloy-transport-http", "anyhow", "reqwest", "revm", @@ -3195,7 +2948,7 @@ dependencies = [ name = "revm" version = "14.0.1" dependencies = [ - "alloy-provider 0.6.4", + "alloy-provider", "alloy-sol-types", "anyhow", "criterion", @@ -3263,10 +3016,10 @@ dependencies = [ name = "revm-database" version = "1.0.0" dependencies = [ - "alloy-eips 0.6.4", - "alloy-provider 0.6.4", + "alloy-eips", + "alloy-provider", "alloy-sol-types", - "alloy-transport 0.6.4", + "alloy-transport", "anyhow", "auto_impl", "criterion", @@ -3399,7 +3152,7 @@ dependencies = [ name = "revm-primitives" version = "9.0.1" dependencies = [ - "alloy-primitives 0.8.12", + "alloy-primitives", ] [[package]] @@ -3408,7 +3161,7 @@ version = "1.0.0" dependencies = [ "alloy-eip2930", "alloy-eip7702 0.1.1", - "alloy-primitives 0.8.12", + "alloy-primitives", "enumn", "revm-primitives", "serde", diff --git a/examples/erc20_gas/Cargo.toml b/examples/erc20_gas/Cargo.toml index 35b0781490..24d7cd1e9c 100644 --- a/examples/erc20_gas/Cargo.toml +++ b/examples/erc20_gas/Cargo.toml @@ -27,17 +27,19 @@ revm.workspace = true database = { workspace = true, features = ["std", "alloydb"] } specification.workspace = true -# alloy dependencies -alloy-sol-types = { version = "0.8.2", default-features = false, features = ["std"] } -alloy-primitives = "0.6" -alloy-eips = "0.4.2" -alloy-transport-http = "0.4.2" -alloy-provider = "0.4.2" -reqwest = { version = "0.12" } -anyhow = "1.0.89" -# async runtime +# tokio tokio = { version = "1.40", features = ["rt-multi-thread", "macros"] } +# alloy +alloy-sol-types = { version = "0.8.2", default-features = false, features = [ + "std", +] } +alloy-eips = "0.6" +alloy-transport-http = "0.6" +alloy-provider = "0.6" +reqwest = { version = "0.12" } +anyhow = "1.0.89" + diff --git a/examples/erc20_gas/src/main.rs b/examples/erc20_gas/src/main.rs index a9dd78e11a..97ec480fa9 100644 --- a/examples/erc20_gas/src/main.rs +++ b/examples/erc20_gas/src/main.rs @@ -1,24 +1,26 @@ -use std::cmp::Ordering; -use revm::{context::Cfg, context_interface::result::{ HaltReason, ResultAndState}, handler::{ EthValidation, FrameResult}, handler_interface::{PostExecutionHandler, ValidationHandler}, primitives::{address, keccak256, Address, U256}, state::EvmStorageSlot}; -use alloy_provider::{network::Ethereum, RootProvider}; +use std::cmp::Ordering; +use alloy_provider::{network::Ethereum, ProviderBuilder, RootProvider}; +use alloy_sol_types::{sol, SolValue}; use alloy_transport_http::Http; -use database::{AlloyDB, CacheDB}; -use reqwest::Client; +use reqwest::{Client, Url}; use revm::{ + context::Cfg, context_interface::{ - result::{EVMError, InvalidTransaction}, + result::{EVMError, HaltReason, InvalidTransaction, ResultAndState}, transaction::Eip4844Tx, - Block, JournalStateGetter, JournalStateGetterDBError, Transaction, TransactionGetter, TransactionType + Block, JournalStateGetter, JournalStateGetterDBError, Transaction, TransactionGetter, TransactionType, }, database_interface::WrapDatabaseAsync, - handler::{EthPreExecution, EthPostExecution}, - handler_interface::PreExecutionHandler, + handler::{EthPostExecution, EthPreExecution, EthValidation, FrameResult}, + handler_interface::{PostExecutionHandler, PreExecutionHandler, ValidationHandler}, + primitives::{address, keccak256, Address, Bytes, U256}, + state::{AccountInfo, EvmStorageSlot}, Context, }; -use alloy_sol_types::{sol, SolValue}; +use database::{AlloyDB, BlockId, CacheDB}; use specification::hardfork::SpecId; - +use anyhow::Result; sol! { interface IERC20 { @@ -267,6 +269,49 @@ impl ValidationHandler for Erc20Validation { } -fn main() { - println!("Hello, world!"); + +#[tokio::main] +async fn main() -> Result<()> { + // Set up the HTTP transport which is consumed by the RPC client. + let rpc_url: Url = "https://mainnet.infura.io/v3/c60b0bb42f8a4c6481ecd229eddaca27".parse()?; + + let client = ProviderBuilder::new().on_http(rpc_url); + + let alloy = WrapDatabaseAsync::new(AlloyDB::new(client, BlockId::latest())).unwrap(); + let mut cache_db = CacheDB::new(alloy); + + // Random empty account: From + let account = address!("18B06aaF27d44B756FCF16Ca20C1f183EB49111f"); + // Random empty account: To + let account_to = address!("0x21a4B6F62E51e59274b6Be1705c7c68781B87C77"); + + let usdc = address!("a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"); + + // USDC has 6 decimals + let hundred_tokens = U256::from(100_000_000_000_000_000u128); + + let balance_slot = keccak256((account, U256::from(3)).abi_encode()).into(); + + cache_db.insert_account_storage(usdc, balance_slot, hundred_tokens); + cache_db.insert_account_info(account, AccountInfo { + nonce: 0, + balance: hundred_tokens, + code_hash: keccak256(Bytes::new()), + code: None, + }); + +// let balance_before = balance_of(usdc, account, &mut cache_db).unwrap(); +// // Transfer 100 tokens from account to account_to +// transfer(account, account_to, hundred_tokens, usdc, &mut cache_db)?; + +// let balance_after = balance_of(usdc, account, &mut cache_db)?; + +// println!("Balance before: {balance_before}"); +// println!("Balance after: {balance_after}"); + + + Ok(()) } + + + diff --git a/examples/uniswap_v2_usdc_swap/src/main.rs b/examples/uniswap_v2_usdc_swap/src/main.rs index 882fbe9b52..d98614dde7 100644 --- a/examples/uniswap_v2_usdc_swap/src/main.rs +++ b/examples/uniswap_v2_usdc_swap/src/main.rs @@ -90,7 +90,7 @@ async fn main() -> Result<()> { Ok(()) } -fn balance_of(token: Address, address: Address, alloy_db: &mut AlloyCacheDB) -> Result { +pub fn balance_of(token: Address, address: Address, alloy_db: &mut AlloyCacheDB) -> Result { sol! { function balanceOf(address account) public returns (uint256); } From 7f83016628be6f2acb6d9bf379d96f3f206116b8 Mon Sep 17 00:00:00 2001 From: royvardhan Date: Mon, 9 Dec 2024 11:13:58 +0530 Subject: [PATCH 08/18] feat: complete impl --- examples/erc20_gas/src/main.rs | 114 +++++++++++++++++++++++++++------ 1 file changed, 93 insertions(+), 21 deletions(-) diff --git a/examples/erc20_gas/src/main.rs b/examples/erc20_gas/src/main.rs index 97ec480fa9..4e8d4e1dc6 100644 --- a/examples/erc20_gas/src/main.rs +++ b/examples/erc20_gas/src/main.rs @@ -7,30 +7,24 @@ use reqwest::{Client, Url}; use revm::{ context::Cfg, context_interface::{ - result::{EVMError, HaltReason, InvalidTransaction, ResultAndState}, + result::{EVMError, ExecutionResult, HaltReason, InvalidTransaction, Output, ResultAndState}, transaction::Eip4844Tx, Block, JournalStateGetter, JournalStateGetterDBError, Transaction, TransactionGetter, TransactionType, }, database_interface::WrapDatabaseAsync, - handler::{EthPostExecution, EthPreExecution, EthValidation, FrameResult}, + handler::{EthHandler, EthPostExecution, EthPreExecution, EthValidation, FrameResult}, handler_interface::{PostExecutionHandler, PreExecutionHandler, ValidationHandler}, - primitives::{address, keccak256, Address, Bytes, U256}, + primitives::{address, keccak256, Address, Bytes, TxKind, U256}, state::{AccountInfo, EvmStorageSlot}, - Context, + Context, MainEvm, }; use database::{AlloyDB, BlockId, CacheDB}; use specification::hardfork::SpecId; -use anyhow::Result; - -sol! { - interface IERC20 { - function transfer(address to, uint256 amount) external returns (bool); - function balanceOf(address owner) external view returns (uint256); - function allowance(address owner, address spender) external view returns (uint256); - function approve(address spender, uint256 amount) external returns (bool); - function transferFrom(address from, address to, uint256 amount) external returns (bool); - } -} +use anyhow::{anyhow, Result}; +use revm::EvmExec; +use alloy_sol_types::SolCall; +use revm::EvmCommit; + @@ -268,6 +262,83 @@ impl ValidationHandler for Erc20Validation { } } +fn balance_of(token: Address, address: Address, alloy_db: &mut AlloyCacheDB) -> Result { + sol! { + function balanceOf(address account) public returns (uint256); + } + + let encoded = balanceOfCall { account: address }.abi_encode(); + + let mut evm = MainEvm::new( + Context::builder() + .with_db(alloy_db) + .modify_tx_chained(|tx| { + // 0x1 because calling USDC proxy from zero address fails + tx.caller = address!("0000000000000000000000000000000000000001"); + tx.transact_to = TxKind::Call(token); + tx.data = encoded.into(); + tx.value = U256::from(0); + }), + EthHandler::default(), + ); + + let ref_tx = evm.exec().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 = ::abi_decode(&value, false)?; + + Ok(balance) +} + +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 = MainEvm::new( + Context::builder() + .with_db(cache_db) + .modify_tx_chained(|tx| { + tx.caller = from; + tx.transact_to = TxKind::Call(token); + tx.data = encoded.into(); + tx.value = U256::from(0); + }), + EthHandler::default(), + ); + + let ref_tx = evm.exec_commit().unwrap(); + let success: bool = match ref_tx { + ExecutionResult::Success { + output: Output::Call(value), + .. + } => ::abi_decode(&value, false)?, + result => return Err(anyhow!("'transfer' execution failed: {result:?}")), + }; + + if !success { + return Err(anyhow!("'transfer' failed")); + } + + Ok(()) +} + #[tokio::main] @@ -300,14 +371,15 @@ async fn main() -> Result<()> { code: None, }); -// let balance_before = balance_of(usdc, account, &mut cache_db).unwrap(); -// // Transfer 100 tokens from account to account_to -// transfer(account, account_to, hundred_tokens, usdc, &mut cache_db)?; + let balance_before = balance_of(usdc, account, &mut cache_db).unwrap(); + + // Transfer 100 tokens from account to account_to + transfer(account, account_to, hundred_tokens, usdc, &mut cache_db)?; -// let balance_after = balance_of(usdc, account, &mut cache_db)?; + let balance_after = balance_of(usdc, account, &mut cache_db)?; -// println!("Balance before: {balance_before}"); -// println!("Balance after: {balance_after}"); + println!("Balance before: {balance_before}"); + println!("Balance after: {balance_after}"); Ok(()) From ec41dfc6f4784a6fe939a5e942c63f6165597541 Mon Sep 17 00:00:00 2001 From: royvardhan Date: Mon, 9 Dec 2024 11:52:24 +0530 Subject: [PATCH 09/18] feat: organize handlers --- examples/erc20_gas/src/error.rs | 6 + examples/erc20_gas/src/handlers/mod.rs | 7 + .../erc20_gas/src/handlers/post_execution.rs | 94 +++++ .../erc20_gas/src/handlers/pre_execution.rs | 52 +++ examples/erc20_gas/src/handlers/validation.rs | 105 ++++++ examples/erc20_gas/src/main.rs | 341 ++++-------------- 6 files changed, 339 insertions(+), 266 deletions(-) create mode 100644 examples/erc20_gas/src/error.rs create mode 100644 examples/erc20_gas/src/handlers/mod.rs create mode 100644 examples/erc20_gas/src/handlers/post_execution.rs create mode 100644 examples/erc20_gas/src/handlers/pre_execution.rs create mode 100644 examples/erc20_gas/src/handlers/validation.rs diff --git a/examples/erc20_gas/src/error.rs b/examples/erc20_gas/src/error.rs new file mode 100644 index 0000000000..597950974f --- /dev/null +++ b/examples/erc20_gas/src/error.rs @@ -0,0 +1,6 @@ +use revm::{ + context_interface::{result::{EVMError, InvalidTransaction}, JournalStateGetterDBError}, + Context, +}; + +pub type Erc20Error = EVMError, InvalidTransaction>; \ No newline at end of file diff --git a/examples/erc20_gas/src/handlers/mod.rs b/examples/erc20_gas/src/handlers/mod.rs new file mode 100644 index 0000000000..58438d6a79 --- /dev/null +++ b/examples/erc20_gas/src/handlers/mod.rs @@ -0,0 +1,7 @@ +pub mod pre_execution; +pub mod post_execution; +pub mod validation; + +pub use pre_execution::Erc20PreExecution; +pub use post_execution::Erc20PostExecution; +pub use validation::Erc20Validation; \ No newline at end of file diff --git a/examples/erc20_gas/src/handlers/post_execution.rs b/examples/erc20_gas/src/handlers/post_execution.rs new file mode 100644 index 0000000000..5cbcc498d9 --- /dev/null +++ b/examples/erc20_gas/src/handlers/post_execution.rs @@ -0,0 +1,94 @@ +use crate::error::Erc20Error; +use crate::{token_operation, TREASURY}; +use revm::{ + context::Cfg, + context_interface::{ + result::{HaltReason, ResultAndState}, + Block, Transaction, TransactionGetter, + }, + handler::{EthPostExecution, FrameResult}, + handler_interface::PostExecutionHandler, + primitives::U256, + specification::hardfork::SpecId, + Context, +}; + +pub struct Erc20PostExecution { + inner: EthPostExecution, +} + +impl Erc20PostExecution { + pub fn new() -> Self { + Self { + inner: EthPostExecution::new(), + } + } +} + +impl PostExecutionHandler for Erc20PostExecution { + type Context = Context; + type Error = Erc20Error; + type ExecResult = FrameResult; + type Output = ResultAndState; + + fn refund( + &self, + context: &mut Self::Context, + exec_result: &mut Self::ExecResult, + eip7702_refund: i64, + ) { + self.inner.refund(context, exec_result, eip7702_refund) + } + + fn reimburse_caller( + &self, + context: &mut Self::Context, + exec_result: &mut Self::ExecResult, + ) -> Result<(), Self::Error> { + let basefee = context.block.basefee(); + let caller = context.tx().common_fields().caller(); + let effective_gas_price = context.tx().effective_gas_price(*basefee); + let gas = exec_result.gas(); + + let reimbursement = + effective_gas_price * U256::from(gas.remaining() + gas.refunded() as u64); + token_operation(context, TREASURY, caller, reimbursement).unwrap(); + + Ok(()) + } + + fn reward_beneficiary( + &self, + context: &mut Self::Context, + exec_result: &mut Self::ExecResult, + ) -> Result<(), Self::Error> { + let tx = context.tx(); + let beneficiary = context.block.beneficiary(); + let basefee = context.block.basefee(); + let effective_gas_price = tx.effective_gas_price(*basefee); + let gas = exec_result.gas(); + + let coinbase_gas_price = if context.cfg.spec().is_enabled_in(SpecId::LONDON) { + effective_gas_price.saturating_sub(*basefee) + } else { + effective_gas_price + }; + + let reward = coinbase_gas_price * U256::from(gas.spent() - gas.refunded() as u64); + token_operation(context, TREASURY, *beneficiary, reward).unwrap(); + + Ok(()) + } + + fn output( + &self, + context: &mut Self::Context, + result: Self::ExecResult, + ) -> Result { + self.inner.output(context, result) + } + + fn clear(&self, context: &mut Self::Context) { + self.inner.clear(context) + } +} diff --git a/examples/erc20_gas/src/handlers/pre_execution.rs b/examples/erc20_gas/src/handlers/pre_execution.rs new file mode 100644 index 0000000000..7146b6502b --- /dev/null +++ b/examples/erc20_gas/src/handlers/pre_execution.rs @@ -0,0 +1,52 @@ +use crate::error::Erc20Error; +use crate::{token_operation, TREASURY}; +use revm::context_interface::transaction::Eip4844Tx; +use revm::context_interface::{Block, Transaction, TransactionGetter}; +use revm::{ + context_interface::TransactionType, handler::EthPreExecution, + handler_interface::PreExecutionHandler, primitives::U256, Context, +}; + +pub struct Erc20PreExecution { + inner: EthPreExecution, +} + +impl Erc20PreExecution { + pub fn new() -> Self { + Self { + inner: EthPreExecution::new(), + } + } +} + +impl PreExecutionHandler for Erc20PreExecution { + type Context = Context; + type Error = Erc20Error; + + fn load_accounts(&self, context: &mut Self::Context) -> Result<(), Self::Error> { + self.inner.load_accounts(context) + } + + fn apply_eip7702_auth_list(&self, context: &mut Self::Context) -> Result { + self.inner.apply_eip7702_auth_list(context) + } + + fn deduct_caller(&self, context: &mut Self::Context) -> Result<(), Self::Error> { + let basefee = context.block.basefee(); + let blob_price = U256::from(context.block.blob_gasprice().unwrap_or_default()); + let effective_gas_price = context.tx().effective_gas_price(*basefee); + + let mut gas_cost = U256::from(context.tx().common_fields().gas_limit()) + .saturating_mul(effective_gas_price); + + if context.tx().tx_type() == TransactionType::Eip4844 { + let blob_gas = U256::from(context.tx().eip4844().total_blob_gas()); + gas_cost = gas_cost.saturating_add(blob_price.saturating_mul(blob_gas)); + } + + let caller = context.tx().common_fields().caller(); + token_operation(context, caller, TREASURY, gas_cost)?; + + Ok(()) + } +} diff --git a/examples/erc20_gas/src/handlers/validation.rs b/examples/erc20_gas/src/handlers/validation.rs new file mode 100644 index 0000000000..a0076a4dd2 --- /dev/null +++ b/examples/erc20_gas/src/handlers/validation.rs @@ -0,0 +1,105 @@ +use crate::error::Erc20Error; +use crate::keccak256; +use crate::TOKEN; +use alloy_sol_types::SolValue; +use revm::context_interface::{Transaction, TransactionGetter}; +use revm::{ + context::Cfg, + context_interface::{ + result::{EVMError, InvalidTransaction}, + transaction::Eip4844Tx, + JournalStateGetter, TransactionType, + }, + handler::EthValidation, + handler_interface::ValidationHandler, + primitives::U256, + Context, +}; +use std::cmp::Ordering; + +pub struct Erc20Validation { + inner: EthValidation, +} + +impl Erc20Validation { + pub fn new() -> Self { + Self { + inner: EthValidation::new(), + } + } +} + +impl ValidationHandler for Erc20Validation { + type Context = Context; + type Error = Erc20Error; + + fn validate_env(&self, context: &Self::Context) -> Result<(), Self::Error> { + self.inner.validate_env(context) + } + + fn validate_tx_against_state(&self, context: &mut Self::Context) -> Result<(), Self::Error> { + let caller = context.tx().common_fields().caller(); + let caller_nonce = context.journal().load_account(caller)?.data.info.nonce; + let token_account = context.journal().load_account(TOKEN)?.data.clone(); + + if !context.cfg.is_nonce_check_disabled() { + let tx_nonce = context.tx().common_fields().nonce(); + let state_nonce = caller_nonce; + match tx_nonce.cmp(&state_nonce) { + Ordering::Less => { + return Err(EVMError::Transaction( + InvalidTransaction::NonceTooLow { + tx: tx_nonce, + state: state_nonce, + } + .into(), + )) + } + Ordering::Greater => { + return Err(EVMError::Transaction( + InvalidTransaction::NonceTooHigh { + tx: tx_nonce, + state: state_nonce, + } + .into(), + )) + } + _ => (), + } + } + + let mut balance_check = U256::from(context.tx().common_fields().gas_limit()) + .checked_mul(U256::from(context.tx().max_fee())) + .and_then(|gas_cost| gas_cost.checked_add(context.tx().common_fields().value())) + .ok_or(InvalidTransaction::OverflowPaymentInTransaction)?; + + if context.tx().tx_type() == TransactionType::Eip4844 { + let tx = context.tx().eip4844(); + let data_fee = tx.calc_max_data_fee(); + balance_check = balance_check + .checked_add(data_fee) + .ok_or(InvalidTransaction::OverflowPaymentInTransaction)?; + } + + let account_balance_slot: U256 = keccak256((caller, U256::from(3)).abi_encode()).into(); + let account_balance = token_account + .storage + .get(&account_balance_slot) + .expect("Balance slot not found") + .present_value(); + + if account_balance < balance_check && !context.cfg.is_balance_check_disabled() { + return Err(InvalidTransaction::LackOfFundForMaxFee { + fee: Box::new(balance_check), + balance: Box::new(account_balance), + } + .into()); + }; + + Ok(()) + } + + fn validate_initial_tx_gas(&self, context: &Self::Context) -> Result { + self.inner.validate_initial_tx_gas(context) + } +} diff --git a/examples/erc20_gas/src/main.rs b/examples/erc20_gas/src/main.rs index 4e8d4e1dc6..7c5aa5345f 100644 --- a/examples/erc20_gas/src/main.rs +++ b/examples/erc20_gas/src/main.rs @@ -1,267 +1,125 @@ - -use std::cmp::Ordering; use alloy_provider::{network::Ethereum, ProviderBuilder, RootProvider}; -use alloy_sol_types::{sol, SolValue}; +use alloy_sol_types::{sol, SolCall, SolValue}; use alloy_transport_http::Http; +use anyhow::{anyhow, Result}; +use database::{AlloyDB, BlockId, CacheDB}; use reqwest::{Client, Url}; use revm::{ - context::Cfg, context_interface::{ - result::{EVMError, ExecutionResult, HaltReason, InvalidTransaction, Output, ResultAndState}, - transaction::Eip4844Tx, - Block, JournalStateGetter, JournalStateGetterDBError, Transaction, TransactionGetter, TransactionType, + result::{EVMError, ExecutionResult, InvalidTransaction, Output}, + JournalStateGetter, }, database_interface::WrapDatabaseAsync, - handler::{EthHandler, EthPostExecution, EthPreExecution, EthValidation, FrameResult}, - handler_interface::{PostExecutionHandler, PreExecutionHandler, ValidationHandler}, + handler::EthHandler, primitives::{address, keccak256, Address, Bytes, TxKind, U256}, state::{AccountInfo, EvmStorageSlot}, - Context, MainEvm, + Context, EvmCommit, EvmExec, MainEvm, }; -use database::{AlloyDB, BlockId, CacheDB}; -use specification::hardfork::SpecId; -use anyhow::{anyhow, Result}; -use revm::EvmExec; -use alloy_sol_types::SolCall; -use revm::EvmCommit; - +mod error; +mod handlers; +use error::Erc20Error; +use handlers::{Erc20PostExecution, Erc20PreExecution, Erc20Validation}; type AlloyCacheDB = CacheDB, Ethereum, RootProvider>>>>; // Constants -const TOKEN: Address = address!("1234567890123456789012345678901234567890"); -const TREASURY: Address = address!("0000000000000000000000000000000000000001"); -const ERC20_TRANSFER_SIGNATURE: [u8; 4] = [0xa9, 0x05, 0x9c, 0xbb]; // keccak256("transfer(address,uint256)")[:4] - - - -type Erc20Error = EVMError, InvalidTransaction>; - +pub const TOKEN: Address = address!("1234567890123456789012345678901234567890"); +pub const TREASURY: Address = address!("0000000000000000000000000000000000000001"); +#[tokio::main] +async fn main() -> Result<()> { + // Set up the HTTP transport which is consumed by the RPC client. + let rpc_url: Url = "https://mainnet.infura.io/v3/c60b0bb42f8a4c6481ecd229eddaca27".parse()?; + let client = ProviderBuilder::new().on_http(rpc_url); -struct Erc20PreExecution { - inner: EthPreExecution -} + let alloy = WrapDatabaseAsync::new(AlloyDB::new(client, BlockId::latest())).unwrap(); + let mut cache_db = CacheDB::new(alloy); -impl Erc20PreExecution { - fn new() -> Self { - Self { - inner: EthPreExecution::new() - } - } -} + // Random empty account: From + let account = address!("18B06aaF27d44B756FCF16Ca20C1f183EB49111f"); + // Random empty account: To + let account_to = address!("0x21a4B6F62E51e59274b6Be1705c7c68781B87C77"); -impl PreExecutionHandler for Erc20PreExecution { - type Context = Context; - type Error = Erc20Error; + let usdc = address!("a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"); - fn load_accounts(&self, context: &mut Self::Context) -> Result<(), Self::Error> { - self.inner.load_accounts(context) - } + // USDC has 6 decimals + let hundred_tokens = U256::from(100_000_000_000_000_000u128); - fn apply_eip7702_auth_list(&self, context: &mut Self::Context) -> Result { - self.inner.apply_eip7702_auth_list(context) - } + let balance_slot = keccak256((account, U256::from(3)).abi_encode()).into(); - fn deduct_caller(&self, context: &mut Self::Context) -> Result<(), Self::Error> { - let basefee = context.block.basefee(); - let blob_price = U256::from(context.block.blob_gasprice().unwrap_or_default()); - let effective_gas_price = context.tx().effective_gas_price(*basefee); - - // Subtract gas costs from the caller's account. - // We need to saturate the gas cost to prevent underflow in case that `disable_balance_check` is enabled. + cache_db.insert_account_storage(usdc, balance_slot, hundred_tokens); + cache_db.insert_account_info( + account, + AccountInfo { + nonce: 0, + balance: hundred_tokens, + code_hash: keccak256(Bytes::new()), + code: None, + }, + ); - let mut gas_cost = U256::from(context.tx().common_fields().gas_limit()) - .saturating_mul(effective_gas_price); + let balance_before = balance_of(usdc, account, &mut cache_db).unwrap(); - - // EIP-4844 - if context.tx().tx_type() == TransactionType::Eip4844 { - let blob_gas = U256::from(context.tx().eip4844().total_blob_gas()); - gas_cost = gas_cost.saturating_add(blob_price.saturating_mul(blob_gas)); - } + // Transfer 100 tokens from account to account_to + // Magic happens here with custom handlers + transfer(account, account_to, hundred_tokens, usdc, &mut cache_db)?; - // Get the balance slot for the caller - let caller = context.tx().common_fields().caller(); + let balance_after = balance_of(usdc, account, &mut cache_db)?; - token_operation(context, caller, TREASURY, gas_cost)?; + println!("Balance before: {balance_before}"); + println!("Balance after: {balance_after}"); - Ok(()) - } + Ok(()) } - - -fn token_operation(context: &mut Context, sender: Address, recipient: Address, amount: U256) -> Result<(), Erc20Error> { +/// Helpers +pub fn token_operation( + context: &mut Context, + sender: Address, + recipient: Address, + amount: U256, +) -> Result<(), Erc20Error> { let token_account = context.journal().load_account(TOKEN)?.data; - + let sender_balance_slot: U256 = keccak256((sender, U256::from(3)).abi_encode()).into(); - let sender_balance = token_account.storage.get(&sender_balance_slot).expect("Balance slot not found").present_value(); + let sender_balance = token_account + .storage + .get(&sender_balance_slot) + .expect("Balance slot not found") + .present_value(); - if sender_balance < amount { - return Err(EVMError::Transaction(InvalidTransaction::MaxFeePerBlobGasNotSupported)); + return Err(EVMError::Transaction( + InvalidTransaction::MaxFeePerBlobGasNotSupported, + )); } // Subtract the amount from the sender's balance let sender_new_balance = sender_balance.saturating_sub(amount); - token_account.storage.insert(sender_balance_slot, EvmStorageSlot::new_changed(sender_balance, sender_new_balance)); + token_account.storage.insert( + sender_balance_slot, + EvmStorageSlot::new_changed(sender_balance, sender_new_balance), + ); // Add the amount to the recipient's balance let recipient_balance_slot: U256 = keccak256((recipient, U256::from(3)).abi_encode()).into(); - let recipient_balance = token_account.storage.get(&recipient_balance_slot).expect("To balance slot not found").present_value(); + let recipient_balance = token_account + .storage + .get(&recipient_balance_slot) + .expect("To balance slot not found") + .present_value(); let recipient_new_balance = recipient_balance.saturating_add(amount); - token_account.storage.insert(recipient_balance_slot, EvmStorageSlot::new_changed(recipient_balance, recipient_new_balance)); + token_account.storage.insert( + recipient_balance_slot, + EvmStorageSlot::new_changed(recipient_balance, recipient_new_balance), + ); Ok(()) } - - -struct Erc20PostExecution { - inner: EthPostExecution -} - - -impl Erc20PostExecution { - fn new() -> Self { - Self { - inner: EthPostExecution::new() - } - } -} - - - -impl PostExecutionHandler for Erc20PostExecution { - type Context = Context; - type Error = Erc20Error; - type ExecResult = FrameResult; - type Output = ResultAndState; - - - fn refund(&self, context: &mut Self::Context, exec_result: &mut Self::ExecResult, eip7702_refund: i64) { - self.inner.refund(context, exec_result, eip7702_refund) - } - - fn reimburse_caller(&self, context: &mut Self::Context, exec_result: &mut Self::ExecResult) -> Result<(), Self::Error> { - let basefee = context.block.basefee(); - let caller = context.tx().common_fields().caller(); - let effective_gas_price = context.tx().effective_gas_price(*basefee); - let gas = exec_result.gas(); - - let reimbursement = effective_gas_price * U256::from(gas.remaining() + gas.refunded() as u64); - token_operation(context, TREASURY, caller, reimbursement).unwrap(); - - - Ok(()) - - } - - fn reward_beneficiary(&self, context: &mut Self::Context, exec_result: &mut Self::ExecResult) -> Result<(), Self::Error> { - let tx = context.tx(); - let beneficiary = context.block.beneficiary(); - let basefee = context.block.basefee(); - let effective_gas_price = tx.effective_gas_price(*basefee); - let gas = exec_result.gas(); - - let coinbase_gas_price = if context.cfg.spec().is_enabled_in(SpecId::LONDON) { - effective_gas_price.saturating_sub(*basefee) - } else { - effective_gas_price - }; - - let reward = coinbase_gas_price * U256::from(gas.spent() - gas.refunded() as u64); - token_operation(context, TREASURY, *beneficiary, reward).unwrap(); - - Ok(()) - } - - fn output(&self, context: &mut Self::Context, result: Self::ExecResult) -> Result { - self.inner.output(context, result) - } - - fn clear(&self, context: &mut Self::Context) { - self.inner.clear(context) - } -} - - -struct Erc20Validation { - inner: EthValidation -} - - -impl Erc20Validation { - fn new() -> Self { - Self { - inner: EthValidation::new() - } - } -} - - -impl ValidationHandler for Erc20Validation { - type Context = Context; - type Error = Erc20Error; - - - fn validate_env(&self, context: &Self::Context) -> Result<(), Self::Error> { - self.inner.validate_env(context) - } - - fn validate_tx_against_state(&self, context: &mut Self::Context) -> Result<(), Self::Error> { - let caller = context.tx().common_fields().caller(); - let caller_nonce = context.journal().load_account(caller)?.data.info.nonce; - let token_account = context.journal().load_account(TOKEN)?.data.clone(); - - if !context.cfg.is_nonce_check_disabled() { - let tx_nonce = context.tx().common_fields().nonce(); - let state_nonce = caller_nonce; - match tx_nonce.cmp(&state_nonce) { - Ordering::Less => return Err(EVMError::Transaction(InvalidTransaction::NonceTooLow { tx: tx_nonce, state: state_nonce }.into())), - Ordering::Greater => return Err(EVMError::Transaction(InvalidTransaction::NonceTooHigh { tx: tx_nonce, state: state_nonce }.into())), - _ => (), - } - } - - // gas_limit * max_fee + value - let mut balance_check = U256::from(context.tx().common_fields().gas_limit()) - .checked_mul(U256::from(context.tx().max_fee())) - .and_then(|gas_cost| gas_cost.checked_add(context.tx().common_fields().value())) - .ok_or(InvalidTransaction::OverflowPaymentInTransaction)?; - - if context.tx().tx_type() == TransactionType::Eip4844 { - let tx = context.tx().eip4844(); - let data_fee = tx.calc_max_data_fee(); - balance_check = balance_check - .checked_add(data_fee) - .ok_or(InvalidTransaction::OverflowPaymentInTransaction)?; - } - - // Get the account balance from token slot - let account_balance_slot: U256 = keccak256((caller, U256::from(3)).abi_encode()).into(); - let account_balance = token_account.storage.get(&account_balance_slot).expect("Balance slot not found").present_value(); - - if account_balance < balance_check && !context.cfg.is_balance_check_disabled() { - return Err(InvalidTransaction::LackOfFundForMaxFee { - fee: Box::new(balance_check), - balance: Box::new(account_balance), - } - .into()); - }; - - Ok(()) - } - - fn validate_initial_tx_gas(&self, context: &Self::Context) -> Result { - self.inner.validate_initial_tx_gas(context) - } -} - fn balance_of(token: Address, address: Address, alloy_db: &mut AlloyCacheDB) -> Result { sol! { function balanceOf(address account) public returns (uint256); @@ -338,52 +196,3 @@ fn transfer( Ok(()) } - - - -#[tokio::main] -async fn main() -> Result<()> { - // Set up the HTTP transport which is consumed by the RPC client. - let rpc_url: Url = "https://mainnet.infura.io/v3/c60b0bb42f8a4c6481ecd229eddaca27".parse()?; - - let client = ProviderBuilder::new().on_http(rpc_url); - - let alloy = WrapDatabaseAsync::new(AlloyDB::new(client, BlockId::latest())).unwrap(); - let mut cache_db = CacheDB::new(alloy); - - // Random empty account: From - let account = address!("18B06aaF27d44B756FCF16Ca20C1f183EB49111f"); - // Random empty account: To - let account_to = address!("0x21a4B6F62E51e59274b6Be1705c7c68781B87C77"); - - let usdc = address!("a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"); - - // USDC has 6 decimals - let hundred_tokens = U256::from(100_000_000_000_000_000u128); - - let balance_slot = keccak256((account, U256::from(3)).abi_encode()).into(); - - cache_db.insert_account_storage(usdc, balance_slot, hundred_tokens); - cache_db.insert_account_info(account, AccountInfo { - nonce: 0, - balance: hundred_tokens, - code_hash: keccak256(Bytes::new()), - code: None, - }); - - let balance_before = balance_of(usdc, account, &mut cache_db).unwrap(); - - // Transfer 100 tokens from account to account_to - transfer(account, account_to, hundred_tokens, usdc, &mut cache_db)?; - - let balance_after = balance_of(usdc, account, &mut cache_db)?; - - println!("Balance before: {balance_before}"); - println!("Balance after: {balance_after}"); - - - Ok(()) -} - - - From e5c9f348f4d77d714fce32492a65f2c82a8f240d Mon Sep 17 00:00:00 2001 From: royvardhan Date: Mon, 9 Dec 2024 11:54:42 +0530 Subject: [PATCH 10/18] chore: remove pub --- examples/uniswap_v2_usdc_swap/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/uniswap_v2_usdc_swap/src/main.rs b/examples/uniswap_v2_usdc_swap/src/main.rs index d98614dde7..882fbe9b52 100644 --- a/examples/uniswap_v2_usdc_swap/src/main.rs +++ b/examples/uniswap_v2_usdc_swap/src/main.rs @@ -90,7 +90,7 @@ async fn main() -> Result<()> { Ok(()) } -pub fn balance_of(token: Address, address: Address, alloy_db: &mut AlloyCacheDB) -> Result { +fn balance_of(token: Address, address: Address, alloy_db: &mut AlloyCacheDB) -> Result { sol! { function balanceOf(address account) public returns (uint256); } From 181ea9ac39a79a986e8e60d34a3821d1077bd87f Mon Sep 17 00:00:00 2001 From: royvardhan Date: Fri, 13 Dec 2024 22:44:46 +0530 Subject: [PATCH 11/18] feat: custom evm with generic handlers --- examples/erc20_gas/src/error.rs | 6 -- examples/erc20_gas/src/handlers/mod.rs | 30 +++++++++- .../erc20_gas/src/handlers/post_execution.rs | 41 +++++++++----- .../erc20_gas/src/handlers/pre_execution.rs | 30 ++++++---- examples/erc20_gas/src/handlers/validation.rs | 55 +++++++++---------- examples/erc20_gas/src/main.rs | 47 +++++++++------- 6 files changed, 123 insertions(+), 86 deletions(-) delete mode 100644 examples/erc20_gas/src/error.rs diff --git a/examples/erc20_gas/src/error.rs b/examples/erc20_gas/src/error.rs deleted file mode 100644 index 597950974f..0000000000 --- a/examples/erc20_gas/src/error.rs +++ /dev/null @@ -1,6 +0,0 @@ -use revm::{ - context_interface::{result::{EVMError, InvalidTransaction}, JournalStateGetterDBError}, - Context, -}; - -pub type Erc20Error = EVMError, InvalidTransaction>; \ No newline at end of file diff --git a/examples/erc20_gas/src/handlers/mod.rs b/examples/erc20_gas/src/handlers/mod.rs index 58438d6a79..02af2b70ae 100644 --- a/examples/erc20_gas/src/handlers/mod.rs +++ b/examples/erc20_gas/src/handlers/mod.rs @@ -1,7 +1,31 @@ -pub mod pre_execution; pub mod post_execution; +pub mod pre_execution; pub mod validation; -pub use pre_execution::Erc20PreExecution; +use revm::{ + context::{block::BlockEnv, tx::TxEnv, CfgEnv, Context}, + context_interface::result::{EVMError, InvalidTransaction}, + database_interface::Database, + handler::{EthExecution, EthHandler}, + Evm, +}; + pub use post_execution::Erc20PostExecution; -pub use validation::Erc20Validation; \ No newline at end of file +pub use pre_execution::Erc20PreExecution; +pub use validation::Erc20Validation; + +pub type Erc20Error = EVMError<::Error, InvalidTransaction>; + +pub type Erc20Context = Context; + +pub type Erc20Handler< + CTX, + ERROR, + VAL = Erc20Validation, + PREEXEC = Erc20PreExecution, + EXEC = EthExecution, + POSTEXEC = Erc20PostExecution, +> = EthHandler; + +pub type Erc20Evm = + Evm, Erc20Context, Erc20Handler, Erc20Error>>; diff --git a/examples/erc20_gas/src/handlers/post_execution.rs b/examples/erc20_gas/src/handlers/post_execution.rs index 5cbcc498d9..cb526ceaa5 100644 --- a/examples/erc20_gas/src/handlers/post_execution.rs +++ b/examples/erc20_gas/src/handlers/post_execution.rs @@ -1,5 +1,8 @@ -use crate::error::Erc20Error; use crate::{token_operation, TREASURY}; +use revm::context_interface::result::{HaltReasonTrait, InvalidHeader, InvalidTransaction}; +use revm::context_interface::JournalStateGetterDBError; +use revm::handler::{EthPostExecutionContext, EthPostExecutionError}; +use revm::precompile::PrecompileErrors; use revm::{ context::Cfg, context_interface::{ @@ -10,14 +13,13 @@ use revm::{ handler_interface::PostExecutionHandler, primitives::U256, specification::hardfork::SpecId, - Context, }; -pub struct Erc20PostExecution { - inner: EthPostExecution, +pub struct Erc20PostExecution { + inner: EthPostExecution, } -impl Erc20PostExecution { +impl Erc20PostExecution { pub fn new() -> Self { Self { inner: EthPostExecution::new(), @@ -25,11 +27,20 @@ impl Erc20PostExecution { } } -impl PostExecutionHandler for Erc20PostExecution { - type Context = Context; - type Error = Erc20Error; +impl PostExecutionHandler for Erc20PostExecution +where + CTX: EthPostExecutionContext, + ERROR: EthPostExecutionError + + From + + From + + From> + + From, + HALTREASON: HaltReasonTrait, +{ + type Context = CTX; + type Error = ERROR; type ExecResult = FrameResult; - type Output = ResultAndState; + type Output = ResultAndState; fn refund( &self, @@ -45,14 +56,14 @@ impl PostExecutionHandler for Erc20PostExecution { context: &mut Self::Context, exec_result: &mut Self::ExecResult, ) -> Result<(), Self::Error> { - let basefee = context.block.basefee(); + let basefee = context.block().basefee(); let caller = context.tx().common_fields().caller(); let effective_gas_price = context.tx().effective_gas_price(*basefee); let gas = exec_result.gas(); let reimbursement = effective_gas_price * U256::from(gas.remaining() + gas.refunded() as u64); - token_operation(context, TREASURY, caller, reimbursement).unwrap(); + token_operation::(context, TREASURY, caller, reimbursement)?; Ok(()) } @@ -63,19 +74,19 @@ impl PostExecutionHandler for Erc20PostExecution { exec_result: &mut Self::ExecResult, ) -> Result<(), Self::Error> { let tx = context.tx(); - let beneficiary = context.block.beneficiary(); - let basefee = context.block.basefee(); + let beneficiary = context.block().beneficiary(); + let basefee = context.block().basefee(); let effective_gas_price = tx.effective_gas_price(*basefee); let gas = exec_result.gas(); - let coinbase_gas_price = if context.cfg.spec().is_enabled_in(SpecId::LONDON) { + let coinbase_gas_price = if context.cfg().spec().into().is_enabled_in(SpecId::LONDON) { effective_gas_price.saturating_sub(*basefee) } else { effective_gas_price }; let reward = coinbase_gas_price * U256::from(gas.spent() - gas.refunded() as u64); - token_operation(context, TREASURY, *beneficiary, reward).unwrap(); + token_operation::(context, TREASURY, *beneficiary, reward)?; Ok(()) } diff --git a/examples/erc20_gas/src/handlers/pre_execution.rs b/examples/erc20_gas/src/handlers/pre_execution.rs index 7146b6502b..aee8ec79df 100644 --- a/examples/erc20_gas/src/handlers/pre_execution.rs +++ b/examples/erc20_gas/src/handlers/pre_execution.rs @@ -1,17 +1,19 @@ -use crate::error::Erc20Error; use crate::{token_operation, TREASURY}; +use revm::context_interface::result::InvalidHeader; use revm::context_interface::transaction::Eip4844Tx; use revm::context_interface::{Block, Transaction, TransactionGetter}; +use revm::handler::{EthPreExecutionContext, EthPreExecutionError}; +use revm::precompile::PrecompileErrors; use revm::{ context_interface::TransactionType, handler::EthPreExecution, - handler_interface::PreExecutionHandler, primitives::U256, Context, + handler_interface::PreExecutionHandler, primitives::U256, }; -pub struct Erc20PreExecution { - inner: EthPreExecution, +pub struct Erc20PreExecution { + inner: EthPreExecution, } -impl Erc20PreExecution { +impl Erc20PreExecution { pub fn new() -> Self { Self { inner: EthPreExecution::new(), @@ -19,9 +21,13 @@ impl Erc20PreExecution { } } -impl PreExecutionHandler for Erc20PreExecution { - type Context = Context; - type Error = Erc20Error; +impl PreExecutionHandler for Erc20PreExecution +where + CTX: EthPreExecutionContext, + ERROR: EthPreExecutionError + From + From, +{ + type Context = CTX; + type Error = ERROR; fn load_accounts(&self, context: &mut Self::Context) -> Result<(), Self::Error> { self.inner.load_accounts(context) @@ -32,20 +38,20 @@ impl PreExecutionHandler for Erc20PreExecution { } fn deduct_caller(&self, context: &mut Self::Context) -> Result<(), Self::Error> { - let basefee = context.block.basefee(); - let blob_price = U256::from(context.block.blob_gasprice().unwrap_or_default()); + let basefee = context.block().basefee(); + let blob_price = U256::from(context.block().blob_gasprice().unwrap_or_default()); let effective_gas_price = context.tx().effective_gas_price(*basefee); let mut gas_cost = U256::from(context.tx().common_fields().gas_limit()) .saturating_mul(effective_gas_price); - if context.tx().tx_type() == TransactionType::Eip4844 { + if context.tx().tx_type().into() == TransactionType::Eip4844 { let blob_gas = U256::from(context.tx().eip4844().total_blob_gas()); gas_cost = gas_cost.saturating_add(blob_price.saturating_mul(blob_gas)); } let caller = context.tx().common_fields().caller(); - token_operation(context, caller, TREASURY, gas_cost)?; + token_operation::(context, caller, TREASURY, gas_cost)?; Ok(()) } diff --git a/examples/erc20_gas/src/handlers/validation.rs b/examples/erc20_gas/src/handlers/validation.rs index a0076a4dd2..63396debdc 100644 --- a/examples/erc20_gas/src/handlers/validation.rs +++ b/examples/erc20_gas/src/handlers/validation.rs @@ -1,27 +1,24 @@ -use crate::error::Erc20Error; use crate::keccak256; use crate::TOKEN; use alloy_sol_types::SolValue; +use revm::context_interface::JournaledState; use revm::context_interface::{Transaction, TransactionGetter}; +use revm::handler::EthValidationContext; +use revm::handler::EthValidationError; use revm::{ context::Cfg, - context_interface::{ - result::{EVMError, InvalidTransaction}, - transaction::Eip4844Tx, - JournalStateGetter, TransactionType, - }, + context_interface::{result::InvalidTransaction, transaction::Eip4844Tx, TransactionType}, handler::EthValidation, handler_interface::ValidationHandler, primitives::U256, - Context, }; use std::cmp::Ordering; -pub struct Erc20Validation { - inner: EthValidation, +pub struct Erc20Validation { + inner: EthValidation, } -impl Erc20Validation { +impl Erc20Validation { pub fn new() -> Self { Self { inner: EthValidation::new(), @@ -29,9 +26,13 @@ impl Erc20Validation { } } -impl ValidationHandler for Erc20Validation { - type Context = Context; - type Error = Erc20Error; +impl ValidationHandler for Erc20Validation +where + CTX: EthValidationContext, + ERROR: EthValidationError, +{ + type Context = CTX; + type Error = ERROR; fn validate_env(&self, context: &Self::Context) -> Result<(), Self::Error> { self.inner.validate_env(context) @@ -42,27 +43,21 @@ impl ValidationHandler for Erc20Validation { let caller_nonce = context.journal().load_account(caller)?.data.info.nonce; let token_account = context.journal().load_account(TOKEN)?.data.clone(); - if !context.cfg.is_nonce_check_disabled() { + if !context.cfg().is_nonce_check_disabled() { let tx_nonce = context.tx().common_fields().nonce(); let state_nonce = caller_nonce; match tx_nonce.cmp(&state_nonce) { Ordering::Less => { - return Err(EVMError::Transaction( - InvalidTransaction::NonceTooLow { - tx: tx_nonce, - state: state_nonce, - } - .into(), - )) + return Err(ERROR::from(InvalidTransaction::NonceTooLow { + tx: tx_nonce, + state: state_nonce, + })) } Ordering::Greater => { - return Err(EVMError::Transaction( - InvalidTransaction::NonceTooHigh { - tx: tx_nonce, - state: state_nonce, - } - .into(), - )) + return Err(ERROR::from(InvalidTransaction::NonceTooHigh { + tx: tx_nonce, + state: state_nonce, + })) } _ => (), } @@ -73,7 +68,7 @@ impl ValidationHandler for Erc20Validation { .and_then(|gas_cost| gas_cost.checked_add(context.tx().common_fields().value())) .ok_or(InvalidTransaction::OverflowPaymentInTransaction)?; - if context.tx().tx_type() == TransactionType::Eip4844 { + if context.tx().tx_type().into() == TransactionType::Eip4844 { let tx = context.tx().eip4844(); let data_fee = tx.calc_max_data_fee(); balance_check = balance_check @@ -88,7 +83,7 @@ impl ValidationHandler for Erc20Validation { .expect("Balance slot not found") .present_value(); - if account_balance < balance_check && !context.cfg.is_balance_check_disabled() { + if account_balance < balance_check && !context.cfg().is_balance_check_disabled() { return Err(InvalidTransaction::LackOfFundForMaxFee { fee: Box::new(balance_check), balance: Box::new(account_balance), diff --git a/examples/erc20_gas/src/main.rs b/examples/erc20_gas/src/main.rs index 7c5aa5345f..c91a7bb4d0 100644 --- a/examples/erc20_gas/src/main.rs +++ b/examples/erc20_gas/src/main.rs @@ -6,21 +6,19 @@ use database::{AlloyDB, BlockId, CacheDB}; use reqwest::{Client, Url}; use revm::{ context_interface::{ - result::{EVMError, ExecutionResult, InvalidTransaction, Output}, - JournalStateGetter, + result::{ExecutionResult, InvalidHeader, InvalidTransaction, Output}, + JournalStateGetter, JournalStateGetterDBError, JournaledState, }, database_interface::WrapDatabaseAsync, - handler::EthHandler, + handler::EthExecution, + precompile::PrecompileErrors, primitives::{address, keccak256, Address, Bytes, TxKind, U256}, state::{AccountInfo, EvmStorageSlot}, - Context, EvmCommit, EvmExec, MainEvm, + Context, EvmCommit, MainEvm, }; -mod error; mod handlers; - -use error::Erc20Error; -use handlers::{Erc20PostExecution, Erc20PreExecution, Erc20Validation}; +use handlers::{Erc20Evm, Erc20Handler, Erc20PostExecution, Erc20PreExecution, Erc20Validation}; type AlloyCacheDB = CacheDB, Ethereum, RootProvider>>>>; @@ -77,12 +75,19 @@ async fn main() -> Result<()> { } /// Helpers -pub fn token_operation( - context: &mut Context, +pub fn token_operation( + context: &mut CTX, sender: Address, recipient: Address, amount: U256, -) -> Result<(), Erc20Error> { +) -> Result<(), ERROR> +where + CTX: JournalStateGetter, + ERROR: From + + From + + From> + + From, +{ let token_account = context.journal().load_account(TOKEN)?.data; let sender_balance_slot: U256 = keccak256((sender, U256::from(3)).abi_encode()).into(); @@ -93,7 +98,7 @@ pub fn token_operation( .present_value(); if sender_balance < amount { - return Err(EVMError::Transaction( + return Err(ERROR::from( InvalidTransaction::MaxFeePerBlobGasNotSupported, )); } @@ -137,13 +142,11 @@ fn balance_of(token: Address, address: Address, alloy_db: &mut AlloyCacheDB) -> tx.data = encoded.into(); tx.value = U256::from(0); }), - EthHandler::default(), + Erc20Handler::default(), ); - let ref_tx = evm.exec().unwrap(); - let result = ref_tx.result; - - let value = match result { + let ref_tx = evm.exec_commit().unwrap(); + let value = match ref_tx { ExecutionResult::Success { output: Output::Call(value), .. @@ -169,7 +172,7 @@ fn transfer( let encoded = transferCall { to, amount }.abi_encode(); - let mut evm = MainEvm::new( + let mut evm = Erc20Evm::new( Context::builder() .with_db(cache_db) .modify_tx_chained(|tx| { @@ -178,9 +181,13 @@ fn transfer( tx.data = encoded.into(); tx.value = U256::from(0); }), - EthHandler::default(), + Erc20Handler::new( + Erc20Validation::new(), + Erc20PreExecution::new(), + EthExecution::new(), + Erc20PostExecution::new(), + ), ); - let ref_tx = evm.exec_commit().unwrap(); let success: bool = match ref_tx { ExecutionResult::Success { From 1fb3d4842b23edf47b7c85613175a6adf9976afa Mon Sep 17 00:00:00 2001 From: royvardhan Date: Fri, 13 Dec 2024 22:48:25 +0530 Subject: [PATCH 12/18] chore: naming --- examples/erc20_gas/src/handlers/mod.rs | 13 ++++++++----- examples/erc20_gas/src/main.rs | 8 ++++---- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/examples/erc20_gas/src/handlers/mod.rs b/examples/erc20_gas/src/handlers/mod.rs index 02af2b70ae..4ddbdb301d 100644 --- a/examples/erc20_gas/src/handlers/mod.rs +++ b/examples/erc20_gas/src/handlers/mod.rs @@ -14,11 +14,11 @@ pub use post_execution::Erc20PostExecution; pub use pre_execution::Erc20PreExecution; pub use validation::Erc20Validation; -pub type Erc20Error = EVMError<::Error, InvalidTransaction>; +pub type Erc20GasError = EVMError<::Error, InvalidTransaction>; -pub type Erc20Context = Context; +pub type Erc20GasContext = Context; -pub type Erc20Handler< +pub type CustomHandler< CTX, ERROR, VAL = Erc20Validation, @@ -27,5 +27,8 @@ pub type Erc20Handler< POSTEXEC = Erc20PostExecution, > = EthHandler; -pub type Erc20Evm = - Evm, Erc20Context, Erc20Handler, Erc20Error>>; +pub type CustomEvm = Evm< + Erc20GasError, + Erc20GasContext, + CustomHandler, Erc20GasError>, +>; diff --git a/examples/erc20_gas/src/main.rs b/examples/erc20_gas/src/main.rs index c91a7bb4d0..a24ec18e31 100644 --- a/examples/erc20_gas/src/main.rs +++ b/examples/erc20_gas/src/main.rs @@ -18,7 +18,7 @@ use revm::{ }; mod handlers; -use handlers::{Erc20Evm, Erc20Handler, Erc20PostExecution, Erc20PreExecution, Erc20Validation}; +use handlers::{CustomEvm, CustomHandler, Erc20PostExecution, Erc20PreExecution, Erc20Validation}; type AlloyCacheDB = CacheDB, Ethereum, RootProvider>>>>; @@ -142,7 +142,7 @@ fn balance_of(token: Address, address: Address, alloy_db: &mut AlloyCacheDB) -> tx.data = encoded.into(); tx.value = U256::from(0); }), - Erc20Handler::default(), + CustomHandler::default(), ); let ref_tx = evm.exec_commit().unwrap(); @@ -172,7 +172,7 @@ fn transfer( let encoded = transferCall { to, amount }.abi_encode(); - let mut evm = Erc20Evm::new( + let mut evm = CustomEvm::new( Context::builder() .with_db(cache_db) .modify_tx_chained(|tx| { @@ -181,7 +181,7 @@ fn transfer( tx.data = encoded.into(); tx.value = U256::from(0); }), - Erc20Handler::new( + CustomHandler::new( Erc20Validation::new(), Erc20PreExecution::new(), EthExecution::new(), From 706f29e27eb1aad9aa18debe210892f4cc88da88 Mon Sep 17 00:00:00 2001 From: royvardhan Date: Fri, 20 Dec 2024 14:05:54 +0530 Subject: [PATCH 13/18] fix address --- examples/erc20_gas/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/erc20_gas/src/main.rs b/examples/erc20_gas/src/main.rs index a24ec18e31..a4f064af8c 100644 --- a/examples/erc20_gas/src/main.rs +++ b/examples/erc20_gas/src/main.rs @@ -40,7 +40,7 @@ async fn main() -> Result<()> { // Random empty account: From let account = address!("18B06aaF27d44B756FCF16Ca20C1f183EB49111f"); // Random empty account: To - let account_to = address!("0x21a4B6F62E51e59274b6Be1705c7c68781B87C77"); + let account_to = address!("21a4B6F62E51e59274b6Be1705c7c68781B87C77"); let usdc = address!("a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"); From fe1fa85213892b28bf60cf592ee622fbdfac1240 Mon Sep 17 00:00:00 2001 From: royvardhan Date: Fri, 20 Dec 2024 14:08:41 +0530 Subject: [PATCH 14/18] fix workspace test --- examples/erc20_gas/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/erc20_gas/src/main.rs b/examples/erc20_gas/src/main.rs index a4f064af8c..05aa8e97f0 100644 --- a/examples/erc20_gas/src/main.rs +++ b/examples/erc20_gas/src/main.rs @@ -49,7 +49,7 @@ async fn main() -> Result<()> { let balance_slot = keccak256((account, U256::from(3)).abi_encode()).into(); - cache_db.insert_account_storage(usdc, balance_slot, hundred_tokens); + let _ = cache_db.insert_account_storage(usdc, balance_slot, hundred_tokens); cache_db.insert_account_info( account, AccountInfo { From c22a1a9cb6c590fc38413bdc3ba3c659e4c87d36 Mon Sep 17 00:00:00 2001 From: royvardhan Date: Fri, 20 Dec 2024 14:09:35 +0530 Subject: [PATCH 15/18] fix workspace test --- examples/erc20_gas/src/main.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/erc20_gas/src/main.rs b/examples/erc20_gas/src/main.rs index 05aa8e97f0..d411e30fd5 100644 --- a/examples/erc20_gas/src/main.rs +++ b/examples/erc20_gas/src/main.rs @@ -49,7 +49,9 @@ async fn main() -> Result<()> { let balance_slot = keccak256((account, U256::from(3)).abi_encode()).into(); - let _ = cache_db.insert_account_storage(usdc, balance_slot, hundred_tokens); + cache_db + .insert_account_storage(usdc, balance_slot, hundred_tokens) + .unwrap(); cache_db.insert_account_info( account, AccountInfo { From 60b68a9d06796e747d32aa1413acab0b6178289f Mon Sep 17 00:00:00 2001 From: royvardhan Date: Fri, 20 Dec 2024 16:38:06 +0530 Subject: [PATCH 16/18] clippy --- examples/erc20_gas/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/erc20_gas/src/main.rs b/examples/erc20_gas/src/main.rs index d411e30fd5..4beb74648b 100644 --- a/examples/erc20_gas/src/main.rs +++ b/examples/erc20_gas/src/main.rs @@ -17,7 +17,7 @@ use revm::{ Context, EvmCommit, MainEvm, }; -mod handlers; +pub mod handlers; use handlers::{CustomEvm, CustomHandler, Erc20PostExecution, Erc20PreExecution, Erc20Validation}; type AlloyCacheDB = From 8ec7d3dc2e07ca78c271f6a40b06cbefefaeaf62 Mon Sep 17 00:00:00 2001 From: rakita Date: Tue, 31 Dec 2024 14:56:49 +0100 Subject: [PATCH 17/18] resolve merge --- .../erc20_gas/src/handlers/post_execution.rs | 29 ++++++++++++------- .../erc20_gas/src/handlers/pre_execution.rs | 20 ++++++++----- examples/erc20_gas/src/handlers/validation.rs | 8 ++++- examples/erc20_gas/src/main.rs | 6 ++-- 4 files changed, 41 insertions(+), 22 deletions(-) diff --git a/examples/erc20_gas/src/handlers/post_execution.rs b/examples/erc20_gas/src/handlers/post_execution.rs index cb526ceaa5..8eabaeba26 100644 --- a/examples/erc20_gas/src/handlers/post_execution.rs +++ b/examples/erc20_gas/src/handlers/post_execution.rs @@ -1,6 +1,6 @@ use crate::{token_operation, TREASURY}; use revm::context_interface::result::{HaltReasonTrait, InvalidHeader, InvalidTransaction}; -use revm::context_interface::JournalStateGetterDBError; +use revm::context_interface::JournalDBError; use revm::handler::{EthPostExecutionContext, EthPostExecutionError}; use revm::precompile::PrecompileErrors; use revm::{ @@ -27,13 +27,19 @@ impl Erc20PostExecution { } } +impl Default for Erc20PostExecution { + fn default() -> Self { + Self::new() + } +} + impl PostExecutionHandler for Erc20PostExecution where CTX: EthPostExecutionContext, ERROR: EthPostExecutionError + From + From - + From> + + From> + From, HALTREASON: HaltReasonTrait, { @@ -56,14 +62,14 @@ where context: &mut Self::Context, exec_result: &mut Self::ExecResult, ) -> Result<(), Self::Error> { - let basefee = context.block().basefee(); + let basefee = context.block().basefee() as u128; let caller = context.tx().common_fields().caller(); - let effective_gas_price = context.tx().effective_gas_price(*basefee); + let effective_gas_price = context.tx().effective_gas_price(basefee); let gas = exec_result.gas(); let reimbursement = - effective_gas_price * U256::from(gas.remaining() + gas.refunded() as u64); - token_operation::(context, TREASURY, caller, reimbursement)?; + effective_gas_price.saturating_mul((gas.remaining() + gas.refunded() as u64) as u128); + token_operation::(context, TREASURY, caller, U256::from(reimbursement))?; Ok(()) } @@ -75,18 +81,19 @@ where ) -> Result<(), Self::Error> { let tx = context.tx(); let beneficiary = context.block().beneficiary(); - let basefee = context.block().basefee(); - let effective_gas_price = tx.effective_gas_price(*basefee); + let basefee = context.block().basefee() as u128; + let effective_gas_price = tx.effective_gas_price(basefee); let gas = exec_result.gas(); let coinbase_gas_price = if context.cfg().spec().into().is_enabled_in(SpecId::LONDON) { - effective_gas_price.saturating_sub(*basefee) + effective_gas_price.saturating_sub(basefee) } else { effective_gas_price }; - let reward = coinbase_gas_price * U256::from(gas.spent() - gas.refunded() as u64); - token_operation::(context, TREASURY, *beneficiary, reward)?; + let reward = + coinbase_gas_price.saturating_mul((gas.spent() - gas.refunded() as u64) as u128); + token_operation::(context, TREASURY, beneficiary, U256::from(reward))?; Ok(()) } diff --git a/examples/erc20_gas/src/handlers/pre_execution.rs b/examples/erc20_gas/src/handlers/pre_execution.rs index aee8ec79df..a7618a7e29 100644 --- a/examples/erc20_gas/src/handlers/pre_execution.rs +++ b/examples/erc20_gas/src/handlers/pre_execution.rs @@ -21,6 +21,12 @@ impl Erc20PreExecution { } } +impl Default for Erc20PreExecution { + fn default() -> Self { + Self::new() + } +} + impl PreExecutionHandler for Erc20PreExecution where CTX: EthPreExecutionContext, @@ -38,20 +44,20 @@ where } fn deduct_caller(&self, context: &mut Self::Context) -> Result<(), Self::Error> { - let basefee = context.block().basefee(); - let blob_price = U256::from(context.block().blob_gasprice().unwrap_or_default()); - let effective_gas_price = context.tx().effective_gas_price(*basefee); + let basefee = context.block().basefee() as u128; + let blob_price = context.block().blob_gasprice().unwrap_or_default(); + let effective_gas_price = context.tx().effective_gas_price(basefee); - let mut gas_cost = U256::from(context.tx().common_fields().gas_limit()) - .saturating_mul(effective_gas_price); + let mut gas_cost = + (context.tx().common_fields().gas_limit() as u128).saturating_mul(effective_gas_price); if context.tx().tx_type().into() == TransactionType::Eip4844 { - let blob_gas = U256::from(context.tx().eip4844().total_blob_gas()); + let blob_gas = context.tx().eip4844().total_blob_gas() as u128; gas_cost = gas_cost.saturating_add(blob_price.saturating_mul(blob_gas)); } let caller = context.tx().common_fields().caller(); - token_operation::(context, caller, TREASURY, gas_cost)?; + token_operation::(context, caller, TREASURY, U256::from(gas_cost))?; Ok(()) } diff --git a/examples/erc20_gas/src/handlers/validation.rs b/examples/erc20_gas/src/handlers/validation.rs index 63396debdc..0c1541b4b9 100644 --- a/examples/erc20_gas/src/handlers/validation.rs +++ b/examples/erc20_gas/src/handlers/validation.rs @@ -1,7 +1,7 @@ use crate::keccak256; use crate::TOKEN; use alloy_sol_types::SolValue; -use revm::context_interface::JournaledState; +use revm::context_interface::Journal; use revm::context_interface::{Transaction, TransactionGetter}; use revm::handler::EthValidationContext; use revm::handler::EthValidationError; @@ -26,6 +26,12 @@ impl Erc20Validation { } } +impl Default for Erc20Validation { + fn default() -> Self { + Self::new() + } +} + impl ValidationHandler for Erc20Validation where CTX: EthValidationContext, diff --git a/examples/erc20_gas/src/main.rs b/examples/erc20_gas/src/main.rs index 4beb74648b..80587079bb 100644 --- a/examples/erc20_gas/src/main.rs +++ b/examples/erc20_gas/src/main.rs @@ -7,7 +7,7 @@ use reqwest::{Client, Url}; use revm::{ context_interface::{ result::{ExecutionResult, InvalidHeader, InvalidTransaction, Output}, - JournalStateGetter, JournalStateGetterDBError, JournaledState, + Journal, JournalDBError, JournalGetter, }, database_interface::WrapDatabaseAsync, handler::EthExecution, @@ -84,10 +84,10 @@ pub fn token_operation( amount: U256, ) -> Result<(), ERROR> where - CTX: JournalStateGetter, + CTX: JournalGetter, ERROR: From + From - + From> + + From> + From, { let token_account = context.journal().load_account(TOKEN)?.data; From e75be115145ce774a1d4822bc50a0d1713a489fe Mon Sep 17 00:00:00 2001 From: rakita Date: Tue, 31 Dec 2024 15:07:05 +0100 Subject: [PATCH 18/18] cleanup and doc --- Cargo.lock | 2 -- examples/erc20_gas/Cargo.toml | 6 ------ examples/erc20_gas/src/handlers/mod.rs | 8 ++++---- examples/erc20_gas/src/handlers/post_execution.rs | 11 ++++------- examples/erc20_gas/src/handlers/pre_execution.rs | 15 ++++++++------- examples/erc20_gas/src/handlers/validation.rs | 14 ++++++-------- examples/erc20_gas/src/main.rs | 6 ++++++ 7 files changed, 28 insertions(+), 34 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 19870ea6c3..79dff5bc1d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1468,7 +1468,6 @@ dependencies = [ name = "example-erc20-gas" version = "0.0.0" dependencies = [ - "alloy-eips", "alloy-provider", "alloy-sol-types", "alloy-transport-http", @@ -1476,7 +1475,6 @@ dependencies = [ "reqwest", "revm", "revm-database", - "revm-specification", "tokio", ] diff --git a/examples/erc20_gas/Cargo.toml b/examples/erc20_gas/Cargo.toml index 24d7cd1e9c..2d9648712b 100644 --- a/examples/erc20_gas/Cargo.toml +++ b/examples/erc20_gas/Cargo.toml @@ -25,8 +25,6 @@ all = "warn" [dependencies] revm.workspace = true database = { workspace = true, features = ["std", "alloydb"] } -specification.workspace = true - # tokio tokio = { version = "1.40", features = ["rt-multi-thread", "macros"] } @@ -35,11 +33,7 @@ tokio = { version = "1.40", features = ["rt-multi-thread", "macros"] } alloy-sol-types = { version = "0.8.2", default-features = false, features = [ "std", ] } -alloy-eips = "0.6" alloy-transport-http = "0.6" alloy-provider = "0.6" reqwest = { version = "0.12" } anyhow = "1.0.89" - - - diff --git a/examples/erc20_gas/src/handlers/mod.rs b/examples/erc20_gas/src/handlers/mod.rs index 4ddbdb301d..cc25fb1e21 100644 --- a/examples/erc20_gas/src/handlers/mod.rs +++ b/examples/erc20_gas/src/handlers/mod.rs @@ -2,6 +2,10 @@ pub mod post_execution; pub mod pre_execution; pub mod validation; +pub use post_execution::Erc20PostExecution; +pub use pre_execution::Erc20PreExecution; +pub use validation::Erc20Validation; + use revm::{ context::{block::BlockEnv, tx::TxEnv, CfgEnv, Context}, context_interface::result::{EVMError, InvalidTransaction}, @@ -10,10 +14,6 @@ use revm::{ Evm, }; -pub use post_execution::Erc20PostExecution; -pub use pre_execution::Erc20PreExecution; -pub use validation::Erc20Validation; - pub type Erc20GasError = EVMError<::Error, InvalidTransaction>; pub type Erc20GasContext = Context; diff --git a/examples/erc20_gas/src/handlers/post_execution.rs b/examples/erc20_gas/src/handlers/post_execution.rs index 8eabaeba26..d145dfe1d3 100644 --- a/examples/erc20_gas/src/handlers/post_execution.rs +++ b/examples/erc20_gas/src/handlers/post_execution.rs @@ -1,16 +1,13 @@ use crate::{token_operation, TREASURY}; -use revm::context_interface::result::{HaltReasonTrait, InvalidHeader, InvalidTransaction}; -use revm::context_interface::JournalDBError; -use revm::handler::{EthPostExecutionContext, EthPostExecutionError}; -use revm::precompile::PrecompileErrors; use revm::{ context::Cfg, context_interface::{ - result::{HaltReason, ResultAndState}, - Block, Transaction, TransactionGetter, + result::{HaltReason, HaltReasonTrait, InvalidHeader, InvalidTransaction, ResultAndState}, + Block, JournalDBError, Transaction, TransactionGetter, }, - handler::{EthPostExecution, FrameResult}, + handler::{EthPostExecution, EthPostExecutionContext, EthPostExecutionError, FrameResult}, handler_interface::PostExecutionHandler, + precompile::PrecompileErrors, primitives::U256, specification::hardfork::SpecId, }; diff --git a/examples/erc20_gas/src/handlers/pre_execution.rs b/examples/erc20_gas/src/handlers/pre_execution.rs index a7618a7e29..b9cfc3c87c 100644 --- a/examples/erc20_gas/src/handlers/pre_execution.rs +++ b/examples/erc20_gas/src/handlers/pre_execution.rs @@ -1,12 +1,13 @@ use crate::{token_operation, TREASURY}; -use revm::context_interface::result::InvalidHeader; -use revm::context_interface::transaction::Eip4844Tx; -use revm::context_interface::{Block, Transaction, TransactionGetter}; -use revm::handler::{EthPreExecutionContext, EthPreExecutionError}; -use revm::precompile::PrecompileErrors; use revm::{ - context_interface::TransactionType, handler::EthPreExecution, - handler_interface::PreExecutionHandler, primitives::U256, + context_interface::{ + result::InvalidHeader, transaction::Eip4844Tx, Block, Transaction, TransactionGetter, + TransactionType, + }, + handler::{EthPreExecution, EthPreExecutionContext, EthPreExecutionError}, + handler_interface::PreExecutionHandler, + precompile::PrecompileErrors, + primitives::U256, }; pub struct Erc20PreExecution { diff --git a/examples/erc20_gas/src/handlers/validation.rs b/examples/erc20_gas/src/handlers/validation.rs index 0c1541b4b9..3d7e64d34d 100644 --- a/examples/erc20_gas/src/handlers/validation.rs +++ b/examples/erc20_gas/src/handlers/validation.rs @@ -1,16 +1,14 @@ -use crate::keccak256; use crate::TOKEN; use alloy_sol_types::SolValue; -use revm::context_interface::Journal; -use revm::context_interface::{Transaction, TransactionGetter}; -use revm::handler::EthValidationContext; -use revm::handler::EthValidationError; use revm::{ context::Cfg, - context_interface::{result::InvalidTransaction, transaction::Eip4844Tx, TransactionType}, - handler::EthValidation, + context_interface::{ + result::InvalidTransaction, transaction::Eip4844Tx, Journal, Transaction, + TransactionGetter, TransactionType, + }, + handler::{EthValidation, EthValidationContext, EthValidationError}, handler_interface::ValidationHandler, - primitives::U256, + primitives::{keccak256, U256}, }; use std::cmp::Ordering; diff --git a/examples/erc20_gas/src/main.rs b/examples/erc20_gas/src/main.rs index 80587079bb..126f301f30 100644 --- a/examples/erc20_gas/src/main.rs +++ b/examples/erc20_gas/src/main.rs @@ -1,3 +1,9 @@ +//! Example of a custom handler for ERC20 gas calculation. +//! +//! Gas is going to be deducted from ERC20 token. + +#![cfg_attr(not(test), warn(unused_crate_dependencies))] + use alloy_provider::{network::Ethereum, ProviderBuilder, RootProvider}; use alloy_sol_types::{sol, SolCall, SolValue}; use alloy_transport_http::Http;