From e3723e780a63b147eb763b86f021db4d9bf29c4c Mon Sep 17 00:00:00 2001 From: Omar Abdulla Date: Sun, 13 Jul 2025 19:52:06 +0300 Subject: [PATCH 1/4] Fix function selector and argument encoding --- Cargo.lock | 153 ++++++++++++++++- Cargo.toml | 1 + crates/format/Cargo.toml | 1 + crates/format/src/input.rs | 342 +++++++++++++------------------------ 4 files changed, 272 insertions(+), 225 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9d729db..81a5f8f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1586,6 +1586,12 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" +[[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" @@ -1808,6 +1814,19 @@ dependencies = [ "syn 2.0.101", ] +[[package]] +name = "derive_more" +version = "0.99.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6edb4b64a43d977b8e99788fe3a04d483834fba1215a7e02caa415b626497f7f" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version 0.4.1", + "syn 2.0.101", +] + [[package]] name = "derive_more" version = "1.0.0" @@ -2093,6 +2112,50 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "ethabi" +version = "18.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7413c5f74cc903ea37386a8965a936cbeb334bd270862fdece542c1b2dcbc898" +dependencies = [ + "ethereum-types", + "hex", + "once_cell", + "regex", + "serde", + "serde_json", + "sha3", + "thiserror 1.0.69", + "uint 0.9.5", +] + +[[package]] +name = "ethbloom" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c22d4b5885b6aa2fe5e8b9329fb8d232bf739e434e6b87347c63bdd00c120f60" +dependencies = [ + "crunchy", + "fixed-hash", + "impl-rlp", + "impl-serde 0.4.0", + "tiny-keccak", +] + +[[package]] +name = "ethereum-types" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02d215cbf040552efcbe99a38372fe80ab9d00268e20012b79fcd0f073edd8ee" +dependencies = [ + "ethbloom", + "fixed-hash", + "impl-rlp", + "impl-serde 0.4.0", + "primitive-types 0.12.2", + "uint 0.9.5", +] + [[package]] name = "expander" version = "2.2.1" @@ -2297,6 +2360,12 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" +[[package]] +name = "futures-timer" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" + [[package]] name = "futures-util" version = "0.3.31" @@ -2759,6 +2828,16 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" +[[package]] +name = "idna" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "idna" version = "1.0.3" @@ -2809,6 +2888,24 @@ dependencies = [ "uint 0.10.0", ] +[[package]] +name = "impl-rlp" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28220f89297a075ddc7245cd538076ee98b01f2a9c23a53a4f1105d5a322808" +dependencies = [ + "rlp", +] + +[[package]] +name = "impl-serde" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc88fc67028ae3db0c853baa36269d398d5f45b6982f95549ff5def78c935cd" +dependencies = [ + "serde", +] + [[package]] name = "impl-serde" version = "0.5.0" @@ -2949,6 +3046,21 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "jsonrpc-core" +version = "18.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14f7f76aef2d054868398427f6c54943cf3d1caa9a7ec7d0c38d69df97a965eb" +dependencies = [ + "futures", + "futures-executor", + "futures-util", + "log", + "serde", + "serde_derive", + "serde_json", +] + [[package]] name = "k256" version = "0.13.4" @@ -3592,6 +3704,8 @@ checksum = "0b34d9fd68ae0b74a41b21c03c2f62847aa0ffea044eee893b4c140b37e244e2" dependencies = [ "fixed-hash", "impl-codec 0.6.0", + "impl-rlp", + "impl-serde 0.4.0", "uint 0.9.5", ] @@ -3604,7 +3718,7 @@ dependencies = [ "fixed-hash", "impl-codec 0.7.1", "impl-num-traits", - "impl-serde", + "impl-serde 0.5.0", "scale-info", "uint 0.10.0", ] @@ -3988,6 +4102,7 @@ dependencies = [ "serde", "serde_json", "tracing", + "web3", ] [[package]] @@ -4681,7 +4796,7 @@ dependencies = [ "futures", "hash-db", "hash256-std-hasher", - "impl-serde", + "impl-serde 0.5.0", "itertools 0.11.0", "k256", "libsecp256k1", @@ -4894,7 +5009,7 @@ version = "22.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee3b70ca340e41cde9d2e069d354508a6e37a6573d66f7cc38f11549002f64ec" dependencies = [ - "impl-serde", + "impl-serde 0.5.0", "parity-scale-codec", "ref-cast", "serde", @@ -5626,6 +5741,12 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" +[[package]] +name = "unicode-bidi" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" + [[package]] name = "unicode-ident" version = "1.0.18" @@ -5660,7 +5781,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" dependencies = [ "form_urlencoded", - "idna", + "idna 1.0.3", "percent-encoding", ] @@ -5900,6 +6021,30 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "web3" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5388522c899d1e1c96a4c307e3797e0f697ba7c77dd8e0e625ecba9dd0342937" +dependencies = [ + "arrayvec", + "derive_more 0.99.20", + "ethabi", + "ethereum-types", + "futures", + "futures-timer", + "hex", + "idna 0.4.0", + "jsonrpc-core", + "log", + "parking_lot", + "pin-project", + "rlp", + "serde", + "serde_json", + "tiny-keccak", +] + [[package]] name = "widestring" version = "1.2.0" diff --git a/Cargo.toml b/Cargo.toml index a28e194..67ed25f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,6 +50,7 @@ tracing-subscriber = { version = "0.3.19", default-features = false, features = "json", "env-filter", ] } +web3 = { version = "0.19.0", default-features = false } # revive compiler revive-solc-json-interface = { git = "https://github.com/paritytech/revive", rev = "3389865af7c3ff6f29a586d82157e8bc573c1a8e" } diff --git a/crates/format/Cargo.toml b/crates/format/Cargo.toml index 4352683..5742267 100644 --- a/crates/format/Cargo.toml +++ b/crates/format/Cargo.toml @@ -17,3 +17,4 @@ tracing = { workspace = true } semver = { workspace = true } serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } +web3 = { workspace = true } diff --git a/crates/format/src/input.rs b/crates/format/src/input.rs index 0c9ea5f..85a504c 100644 --- a/crates/format/src/input.rs +++ b/crates/format/src/input.rs @@ -1,15 +1,13 @@ -use std::collections::HashMap; +use std::{collections::HashMap, str::FromStr}; use alloy::{ - hex, - json_abi::{Function, JsonAbi}, - primitives::{Address, Bytes, TxKind}, + json_abi::JsonAbi, + primitives::{Address, Bytes}, rpc::types::{TransactionInput, TransactionRequest}, }; -use alloy_primitives::U256; -use alloy_sol_types::SolValue; +use alloy_primitives::TxKind; use semver::VersionReq; -use serde::{Deserialize, de::Deserializer}; +use serde::Deserialize; use serde_json::Value; #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq)] @@ -19,7 +17,6 @@ pub struct Input { pub comment: Option, #[serde(default = "default_instance")] pub instance: String, - #[serde(deserialize_with = "deserialize_method")] pub method: Method, pub calldata: Option, pub expected: Option, @@ -47,58 +44,28 @@ pub struct ExpectedOutput { #[serde(untagged)] pub enum Calldata { Single(String), - Compound(Vec), -} - -#[derive(Clone, Debug, Deserialize, Eq, PartialEq)] -#[serde(untagged)] -pub enum CalldataArg { - Literal(String), - /// For example: `Contract.address` - AddressRef(String), + Compound(Vec), } /// Specify how the contract is called. -#[derive(Debug, Default, Clone, Eq, PartialEq)] +#[derive(Debug, Default, Deserialize, Clone, Eq, PartialEq)] pub enum Method { /// Initiate a deploy transaction, calling contracts constructor. /// /// Indicated by `#deployer`. + #[serde(rename = "#deployer")] Deployer, + /// Does not calculate and insert a function selector. /// /// Indicated by `#fallback`. #[default] + #[serde(rename = "#fallback")] Fallback, - /// Call the public function with this selector. - /// - /// Calculates the selector if neither deployer or fallback matches. - Function([u8; 4]), -} -fn deserialize_method<'de, D>(deserializer: D) -> Result -where - D: Deserializer<'de>, -{ - Ok(match String::deserialize(deserializer)?.as_str() { - "#deployer" => Method::Deployer, - "#fallback" => Method::Fallback, - signature => { - let signature = if signature.ends_with(')') { - signature.to_string() - } else { - format!("{signature}()") - }; - match Function::parse(&signature) { - Ok(function) => Method::Function(function.selector().0), - Err(error) => { - return Err(serde::de::Error::custom(format!( - "parsing function signature '{signature}' error: {error}" - ))); - } - } - } - }) + /// Call the public function with the given name. + #[serde(untagged)] + FunctionName(String), } impl Input { @@ -118,7 +85,7 @@ impl Input { deployed_abis: &HashMap, deployed_contracts: &HashMap, ) -> anyhow::Result { - let Method::Function(selector) = self.method else { + let Method::FunctionName(ref function_name) = self.method else { return Ok(Bytes::default()); // fallback or deployer — no input }; @@ -128,14 +95,17 @@ impl Input { tracing::trace!("ABI found for instance: {}", &self.instance); - // Find function by selector + // We follow the same logic that's implemented in the matter-labs-tester where they resolve + // the function name into a function selector and they assume that he function doesn't have + // any existing overloads. + // https://github.com/matter-labs/era-compiler-tester/blob/1dfa7d07cba0734ca97e24704f12dd57f6990c2c/compiler_tester/src/test/case/input/mod.rs#L158-L190 let function = abi .functions() - .find(|f| f.selector().0 == selector) + .find(|function| function.name.starts_with(function_name)) .ok_or_else(|| { anyhow::anyhow!( - "Function with selector {:?} not found in ABI for the instance {:?}", - selector, + "Function with name {:?} not found in ABI for the instance {:?}", + function_name, &self.instance ) })?; @@ -160,61 +130,27 @@ impl Input { &self.instance ); - let mut encoded = selector.to_vec(); + // Allocating a vector that we will be using for the calldata. The vector size will be: + // 4 bytes for the function selector. + // function.inputs.len() * 32 bytes for the arguments (each argument is a U256). + let mut calldata = Vec::::with_capacity(4 + calldata_args.len() * 32); + calldata.extend(&function.selector().0); - for (i, param) in function.inputs.iter().enumerate() { - let arg = calldata_args.get(i).unwrap(); - let encoded_arg = match arg { - CalldataArg::Literal(value) => match param.ty.as_str() { - "uint256" | "uint" => { - let val: U256 = value.parse()?; - val.abi_encode() - } - "uint24" => { - let val: u32 = value.parse()?; - (val & 0xFFFFFF).abi_encode() - } - "bool" => { - let val: bool = value.parse()?; - val.abi_encode() - } - "address" => { - let addr: Address = value.parse()?; - addr.abi_encode() - } - "string" => value.abi_encode(), - "bytes32" => { - let val = hex::decode(value.trim_start_matches("0x"))?; - let mut fixed = [0u8; 32]; - fixed[..val.len()].copy_from_slice(&val); - fixed.abi_encode() - } - "uint256[]" | "uint[]" => { - let nums: Vec = serde_json::from_str(value)?; - nums.abi_encode() - } - "bytes" => { - let val = hex::decode(value.trim_start_matches("0x"))?; - val.abi_encode() - } - _ => anyhow::bail!("Unsupported type: {}", param.ty), - }, - CalldataArg::AddressRef(name) => { - let contract_name = name.trim_end_matches(".address"); - let addr = deployed_contracts - .get(contract_name) - .copied() - .ok_or_else(|| { - anyhow::anyhow!("Address for '{}' not found", contract_name) - })?; - addr.abi_encode() + for (arg_idx, arg) in calldata_args.iter().enumerate() { + match resolve_argument(arg, deployed_contracts) { + Ok(resolved) => { + let mut buffer = [0u8; 32]; + resolved.to_big_endian(&mut buffer); + calldata.extend(buffer); + } + Err(error) => { + tracing::error!(arg, arg_idx, ?error, "Failed to resolve argument"); + return Err(error); } }; - - encoded.extend(encoded_arg); } - Ok(Bytes::from(encoded)) + Ok(calldata.into()) } /// Parse this input into a legacy transaction. @@ -255,12 +191,76 @@ fn default_caller() -> Address { "90F8bf6A479f320ead074411a4B0e7944Ea8c9C1".parse().unwrap() } +/// This function takes in the string calldata argument provided in the JSON input and resolves it +/// into a [`U256`] which is later used to construct the calldata. +/// +/// # Note +/// +/// This piece of code is taken from the matter-labs-tester repository which is licensed under MIT +/// or Apache. The original source code can be found here: +/// https://github.com/matter-labs/era-compiler-tester/blob/0ed598a27f6eceee7008deab3ff2311075a2ec69/compiler_tester/src/test/case/input/value.rs#L43-L146 +/// +/// [`U256`]: web3::types::U256 +fn resolve_argument( + value: &str, + deployed_contracts: &HashMap, +) -> anyhow::Result { + if let Some(instance) = value.strip_suffix(".address") { + Ok(web3::types::U256::from_big_endian( + deployed_contracts + .get(instance) + .ok_or_else(|| anyhow::anyhow!("Instance `{}` not found", instance))? + .as_ref(), + )) + } else if let Some(value) = value.strip_prefix('-') { + let value = web3::types::U256::from_dec_str(value) + .map_err(|error| anyhow::anyhow!("Invalid decimal literal after `-`: {}", error))?; + if value > web3::types::U256::one() << 255u8 { + anyhow::bail!("Decimal literal after `-` is too big"); + } + let value = value + .checked_sub(web3::types::U256::one()) + .ok_or_else(|| anyhow::anyhow!("`-0` is invalid literal"))?; + Ok(web3::types::U256::max_value() + .checked_sub(value) + .expect("Always valid")) + } else if let Some(value) = value.strip_prefix("0x") { + Ok(web3::types::U256::from_str(value) + .map_err(|error| anyhow::anyhow!("Invalid hexadecimal literal: {}", error))?) + } else { + // TODO: This is a set of "variables" that we need to be able to resolve to be fully in + // compliance with the matter labs tester but we currently do not resolve them. We need to + // add logic that does their resolution in the future, perhaps through some kind of system + // context API that we pass down to the resolution function that allows it to make calls to + // the node to perform these resolutions. + let is_unsupported = [ + "$CHAIN_ID", + "$GAS_LIMIT", + "$COINBASE", + "$DIFFICULTY", + "$BLOCK_HASH", + "$BLOCK_TIMESTAMP", + ] + .iter() + .any(|var| value.starts_with(var)); + + if is_unsupported { + tracing::error!(value, "Unsupported variable used"); + anyhow::bail!("Encountered {value} which is currently unsupported by the framework"); + } else { + Ok(web3::types::U256::from_dec_str(value) + .map_err(|error| anyhow::anyhow!("Invalid decimal literal: {}", error))?) + } + } +} + #[cfg(test)] mod tests { use super::*; use alloy::json_abi::JsonAbi; - use alloy_primitives::{address, keccak256}; + use alloy_primitives::address; + use alloy_sol_types::SolValue; use std::collections::HashMap; #[test] @@ -278,16 +278,18 @@ mod tests { "#; let parsed_abi: JsonAbi = serde_json::from_str(raw_metadata).unwrap(); - let selector = keccak256("store(uint256)".as_bytes())[0..4] - .try_into() - .unwrap(); + let selector = parsed_abi + .function("store") + .unwrap() + .first() + .unwrap() + .selector() + .0; let input = Input { instance: "Contract".to_string(), - method: Method::Function(selector), - calldata: Some(Calldata::Compound(vec![CalldataArg::Literal( - "42".to_string(), - )])), + method: Method::FunctionName("store".to_owned()), + calldata: Some(Calldata::Compound(vec!["42".into()])), ..Default::default() }; @@ -305,112 +307,6 @@ mod tests { assert_eq!(decoded.0, 42); } - #[test] - fn test_encoded_input_bool() { - let raw_abi = r#"[ - { - "inputs": [{"name": "flag", "type": "bool"}], - "name": "toggle", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - } - ]"#; - - let parsed_abi: JsonAbi = serde_json::from_str(raw_abi).unwrap(); - let selector = keccak256("toggle(bool)".as_bytes())[0..4] - .try_into() - .unwrap(); - - let input = Input { - instance: "Contract".to_string(), - method: Method::Function(selector), - calldata: Some(Calldata::Compound(vec![CalldataArg::Literal( - "true".to_string(), - )])), - ..Default::default() - }; - - let mut abis = HashMap::new(); - abis.insert("Contract".to_string(), parsed_abi); - let contracts = HashMap::new(); - - let encoded = input.encoded_input(&abis, &contracts).unwrap(); - assert!(encoded.0.starts_with(&selector)); - - type T = (bool,); - let decoded: T = T::abi_decode(&encoded.0[4..]).unwrap(); - assert_eq!(decoded.0, true); - } - - #[test] - fn test_encoded_input_string() { - let raw_abi = r#"[ - { - "inputs": [{"name": "msg", "type": "string"}], - "name": "echo", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - } - ]"#; - - let parsed_abi: JsonAbi = serde_json::from_str(raw_abi).unwrap(); - let selector = keccak256("echo(string)".as_bytes())[0..4] - .try_into() - .unwrap(); - - let input = Input { - instance: "Contract".to_string(), - method: Method::Function(selector), - calldata: Some(Calldata::Compound(vec![CalldataArg::Literal( - "hello".to_string(), - )])), - ..Default::default() - }; - - let mut abis = HashMap::new(); - abis.insert("Contract".to_string(), parsed_abi); - let contracts = HashMap::new(); - - let encoded = input.encoded_input(&abis, &contracts).unwrap(); - assert!(encoded.0.starts_with(&selector)); - } - - #[test] - fn test_encoded_input_uint256_array() { - let raw_abi = r#"[ - { - "inputs": [{"name": "arr", "type": "uint256[]"}], - "name": "sum", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - } - ]"#; - - let parsed_abi: JsonAbi = serde_json::from_str(raw_abi).unwrap(); - let selector = keccak256("sum(uint256[])".as_bytes())[0..4] - .try_into() - .unwrap(); - - let input = Input { - instance: "Contract".to_string(), - method: Method::Function(selector), - calldata: Some(Calldata::Compound(vec![CalldataArg::Literal( - "[1,2,3]".to_string(), - )])), - ..Default::default() - }; - - let mut abis = HashMap::new(); - abis.insert("Contract".to_string(), parsed_abi); - let contracts = HashMap::new(); - - let encoded = input.encoded_input(&abis, &contracts).unwrap(); - assert!(encoded.0.starts_with(&selector)); - } - #[test] fn test_encoded_input_address() { let raw_abi = r#"[ @@ -424,16 +320,20 @@ mod tests { ]"#; let parsed_abi: JsonAbi = serde_json::from_str(raw_abi).unwrap(); - let selector = keccak256("send(address)".as_bytes())[0..4] - .try_into() - .unwrap(); + let selector = parsed_abi + .function("send") + .unwrap() + .first() + .unwrap() + .selector() + .0; let input = Input { instance: "Contract".to_string(), - method: Method::Function(selector), - calldata: Some(Calldata::Compound(vec![CalldataArg::Literal( + method: Method::FunctionName("send".to_owned()), + calldata: Some(Calldata::Compound(vec![ "0x1000000000000000000000000000000000000001".to_string(), - )])), + ])), ..Default::default() }; From 2373872230d5b5727dfcd4662fe016664c36b05e Mon Sep 17 00:00:00 2001 From: Omar Abdulla Date: Mon, 14 Jul 2025 00:02:48 +0300 Subject: [PATCH 2/4] Avoid extra buffer allocation --- crates/format/src/input.rs | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/crates/format/src/input.rs b/crates/format/src/input.rs index 85a504c..af9547f 100644 --- a/crates/format/src/input.rs +++ b/crates/format/src/input.rs @@ -133,15 +133,24 @@ impl Input { // Allocating a vector that we will be using for the calldata. The vector size will be: // 4 bytes for the function selector. // function.inputs.len() * 32 bytes for the arguments (each argument is a U256). - let mut calldata = Vec::::with_capacity(4 + calldata_args.len() * 32); - calldata.extend(&function.selector().0); + // + // We're using indices in the following code in order to avoid the need for us to allocate + // a new buffer for each one of the resolved arguments. + let mut calldata = vec![0u8; 4 + calldata_args.len() * 32]; + calldata[0..4].copy_from_slice(&function.selector().0); for (arg_idx, arg) in calldata_args.iter().enumerate() { match resolve_argument(arg, deployed_contracts) { Ok(resolved) => { - let mut buffer = [0u8; 32]; - resolved.to_big_endian(&mut buffer); - calldata.extend(buffer); + // Compute where the resolved argument will go in the call-data. Again, we're + // doing this in order to avoid performing an extra allocation for an interim + // buffer that is then just used to extend the calldata vector. In here, the 4 + // is the size of the selector which we already wrote to the calldata and the 32 + // is the size of each `U256` we're writing to the calldata. + let start_inclusive = 4 + arg_idx * 32; + let end_exclusive = start_inclusive + 32; + let slot = &mut calldata[start_inclusive..end_exclusive]; + resolved.to_big_endian(slot); } Err(error) => { tracing::error!(arg, arg_idx, ?error, "Failed to resolve argument"); From 43e0d0e59269b4bb400c87f7c0cc45de282d76a6 Mon Sep 17 00:00:00 2001 From: Omar Abdulla Date: Mon, 14 Jul 2025 18:27:38 +0300 Subject: [PATCH 3/4] Remove reliance on the web3 crate --- Cargo.lock | 153 +------------------------------------ Cargo.toml | 1 - crates/format/Cargo.toml | 1 - crates/format/src/input.rs | 36 +++------ 4 files changed, 16 insertions(+), 175 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 81a5f8f..9d729db 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1586,12 +1586,6 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" -[[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" @@ -1814,19 +1808,6 @@ dependencies = [ "syn 2.0.101", ] -[[package]] -name = "derive_more" -version = "0.99.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6edb4b64a43d977b8e99788fe3a04d483834fba1215a7e02caa415b626497f7f" -dependencies = [ - "convert_case", - "proc-macro2", - "quote", - "rustc_version 0.4.1", - "syn 2.0.101", -] - [[package]] name = "derive_more" version = "1.0.0" @@ -2112,50 +2093,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "ethabi" -version = "18.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7413c5f74cc903ea37386a8965a936cbeb334bd270862fdece542c1b2dcbc898" -dependencies = [ - "ethereum-types", - "hex", - "once_cell", - "regex", - "serde", - "serde_json", - "sha3", - "thiserror 1.0.69", - "uint 0.9.5", -] - -[[package]] -name = "ethbloom" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c22d4b5885b6aa2fe5e8b9329fb8d232bf739e434e6b87347c63bdd00c120f60" -dependencies = [ - "crunchy", - "fixed-hash", - "impl-rlp", - "impl-serde 0.4.0", - "tiny-keccak", -] - -[[package]] -name = "ethereum-types" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02d215cbf040552efcbe99a38372fe80ab9d00268e20012b79fcd0f073edd8ee" -dependencies = [ - "ethbloom", - "fixed-hash", - "impl-rlp", - "impl-serde 0.4.0", - "primitive-types 0.12.2", - "uint 0.9.5", -] - [[package]] name = "expander" version = "2.2.1" @@ -2360,12 +2297,6 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" -[[package]] -name = "futures-timer" -version = "3.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" - [[package]] name = "futures-util" version = "0.3.31" @@ -2828,16 +2759,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" -[[package]] -name = "idna" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - [[package]] name = "idna" version = "1.0.3" @@ -2888,24 +2809,6 @@ dependencies = [ "uint 0.10.0", ] -[[package]] -name = "impl-rlp" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f28220f89297a075ddc7245cd538076ee98b01f2a9c23a53a4f1105d5a322808" -dependencies = [ - "rlp", -] - -[[package]] -name = "impl-serde" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc88fc67028ae3db0c853baa36269d398d5f45b6982f95549ff5def78c935cd" -dependencies = [ - "serde", -] - [[package]] name = "impl-serde" version = "0.5.0" @@ -3046,21 +2949,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "jsonrpc-core" -version = "18.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14f7f76aef2d054868398427f6c54943cf3d1caa9a7ec7d0c38d69df97a965eb" -dependencies = [ - "futures", - "futures-executor", - "futures-util", - "log", - "serde", - "serde_derive", - "serde_json", -] - [[package]] name = "k256" version = "0.13.4" @@ -3704,8 +3592,6 @@ checksum = "0b34d9fd68ae0b74a41b21c03c2f62847aa0ffea044eee893b4c140b37e244e2" dependencies = [ "fixed-hash", "impl-codec 0.6.0", - "impl-rlp", - "impl-serde 0.4.0", "uint 0.9.5", ] @@ -3718,7 +3604,7 @@ dependencies = [ "fixed-hash", "impl-codec 0.7.1", "impl-num-traits", - "impl-serde 0.5.0", + "impl-serde", "scale-info", "uint 0.10.0", ] @@ -4102,7 +3988,6 @@ dependencies = [ "serde", "serde_json", "tracing", - "web3", ] [[package]] @@ -4796,7 +4681,7 @@ dependencies = [ "futures", "hash-db", "hash256-std-hasher", - "impl-serde 0.5.0", + "impl-serde", "itertools 0.11.0", "k256", "libsecp256k1", @@ -5009,7 +4894,7 @@ version = "22.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee3b70ca340e41cde9d2e069d354508a6e37a6573d66f7cc38f11549002f64ec" dependencies = [ - "impl-serde 0.5.0", + "impl-serde", "parity-scale-codec", "ref-cast", "serde", @@ -5741,12 +5626,6 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" -[[package]] -name = "unicode-bidi" -version = "0.3.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" - [[package]] name = "unicode-ident" version = "1.0.18" @@ -5781,7 +5660,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" dependencies = [ "form_urlencoded", - "idna 1.0.3", + "idna", "percent-encoding", ] @@ -6021,30 +5900,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "web3" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5388522c899d1e1c96a4c307e3797e0f697ba7c77dd8e0e625ecba9dd0342937" -dependencies = [ - "arrayvec", - "derive_more 0.99.20", - "ethabi", - "ethereum-types", - "futures", - "futures-timer", - "hex", - "idna 0.4.0", - "jsonrpc-core", - "log", - "parking_lot", - "pin-project", - "rlp", - "serde", - "serde_json", - "tiny-keccak", -] - [[package]] name = "widestring" version = "1.2.0" diff --git a/Cargo.toml b/Cargo.toml index 67ed25f..a28e194 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,7 +50,6 @@ tracing-subscriber = { version = "0.3.19", default-features = false, features = "json", "env-filter", ] } -web3 = { version = "0.19.0", default-features = false } # revive compiler revive-solc-json-interface = { git = "https://github.com/paritytech/revive", rev = "3389865af7c3ff6f29a586d82157e8bc573c1a8e" } diff --git a/crates/format/Cargo.toml b/crates/format/Cargo.toml index 5742267..4352683 100644 --- a/crates/format/Cargo.toml +++ b/crates/format/Cargo.toml @@ -17,4 +17,3 @@ tracing = { workspace = true } semver = { workspace = true } serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } -web3 = { workspace = true } diff --git a/crates/format/src/input.rs b/crates/format/src/input.rs index af9547f..127640a 100644 --- a/crates/format/src/input.rs +++ b/crates/format/src/input.rs @@ -2,7 +2,7 @@ use std::{collections::HashMap, str::FromStr}; use alloy::{ json_abi::JsonAbi, - primitives::{Address, Bytes}, + primitives::{Address, Bytes, U256}, rpc::types::{TransactionInput, TransactionRequest}, }; use alloy_primitives::TxKind; @@ -136,21 +136,13 @@ impl Input { // // We're using indices in the following code in order to avoid the need for us to allocate // a new buffer for each one of the resolved arguments. - let mut calldata = vec![0u8; 4 + calldata_args.len() * 32]; - calldata[0..4].copy_from_slice(&function.selector().0); + let mut calldata = Vec::::with_capacity(4 + calldata_args.len() * 32); + calldata.extend(function.selector().0); for (arg_idx, arg) in calldata_args.iter().enumerate() { match resolve_argument(arg, deployed_contracts) { Ok(resolved) => { - // Compute where the resolved argument will go in the call-data. Again, we're - // doing this in order to avoid performing an extra allocation for an interim - // buffer that is then just used to extend the calldata vector. In here, the 4 - // is the size of the selector which we already wrote to the calldata and the 32 - // is the size of each `U256` we're writing to the calldata. - let start_inclusive = 4 + arg_idx * 32; - let end_exclusive = start_inclusive + 32; - let slot = &mut calldata[start_inclusive..end_exclusive]; - resolved.to_big_endian(slot); + calldata.extend(resolved.to_be_bytes::<32>()); } Err(error) => { tracing::error!(arg, arg_idx, ?error, "Failed to resolve argument"); @@ -208,33 +200,29 @@ fn default_caller() -> Address { /// This piece of code is taken from the matter-labs-tester repository which is licensed under MIT /// or Apache. The original source code can be found here: /// https://github.com/matter-labs/era-compiler-tester/blob/0ed598a27f6eceee7008deab3ff2311075a2ec69/compiler_tester/src/test/case/input/value.rs#L43-L146 -/// -/// [`U256`]: web3::types::U256 fn resolve_argument( value: &str, deployed_contracts: &HashMap, -) -> anyhow::Result { +) -> anyhow::Result { if let Some(instance) = value.strip_suffix(".address") { - Ok(web3::types::U256::from_big_endian( + Ok(U256::from_be_slice( deployed_contracts .get(instance) .ok_or_else(|| anyhow::anyhow!("Instance `{}` not found", instance))? .as_ref(), )) } else if let Some(value) = value.strip_prefix('-') { - let value = web3::types::U256::from_dec_str(value) + let value = U256::from_str_radix(value, 10) .map_err(|error| anyhow::anyhow!("Invalid decimal literal after `-`: {}", error))?; - if value > web3::types::U256::one() << 255u8 { + if value > U256::ONE << 255u8 { anyhow::bail!("Decimal literal after `-` is too big"); } let value = value - .checked_sub(web3::types::U256::one()) + .checked_sub(U256::ONE) .ok_or_else(|| anyhow::anyhow!("`-0` is invalid literal"))?; - Ok(web3::types::U256::max_value() - .checked_sub(value) - .expect("Always valid")) + Ok(U256::MAX.checked_sub(value).expect("Always valid")) } else if let Some(value) = value.strip_prefix("0x") { - Ok(web3::types::U256::from_str(value) + Ok(U256::from_str(value) .map_err(|error| anyhow::anyhow!("Invalid hexadecimal literal: {}", error))?) } else { // TODO: This is a set of "variables" that we need to be able to resolve to be fully in @@ -257,7 +245,7 @@ fn resolve_argument( tracing::error!(value, "Unsupported variable used"); anyhow::bail!("Encountered {value} which is currently unsupported by the framework"); } else { - Ok(web3::types::U256::from_dec_str(value) + Ok(U256::from_str_radix(value, 10) .map_err(|error| anyhow::anyhow!("Invalid decimal literal: {}", error))?) } } From 83c20b1be3c77ddc32f0e4966d9608767b4d4fe7 Mon Sep 17 00:00:00 2001 From: Omar Abdulla Date: Mon, 14 Jul 2025 21:30:35 +0300 Subject: [PATCH 4/4] Fix tests --- crates/format/src/input.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/format/src/input.rs b/crates/format/src/input.rs index 0e4b802..270d908 100644 --- a/crates/format/src/input.rs +++ b/crates/format/src/input.rs @@ -1,4 +1,4 @@ -use std::{collections::HashMap, str::FromStr}; +use std::collections::HashMap; use alloy::{ json_abi::JsonAbi, @@ -210,7 +210,7 @@ fn resolve_argument( .ok_or_else(|| anyhow::anyhow!("`-0` is invalid literal"))?; Ok(U256::MAX.checked_sub(value).expect("Always valid")) } else if let Some(value) = value.strip_prefix("0x") { - Ok(U256::from_str(value) + Ok(U256::from_str_radix(value, 16) .map_err(|error| anyhow::anyhow!("Invalid hexadecimal literal: {}", error))?) } else { // TODO: This is a set of "variables" that we need to be able to resolve to be fully in @@ -313,7 +313,7 @@ mod tests { .selector() .0; - let input = Input { + let input: Input = Input { instance: "Contract".to_string(), method: Method::FunctionName("send".to_owned()), calldata: Some(Calldata::Compound(vec![