From 48ed8db4db5d93ad0cf38936c5b9c94496e30f79 Mon Sep 17 00:00:00 2001 From: activecoder10 Date: Thu, 3 Jul 2025 15:12:23 +0300 Subject: [PATCH] Parsed ABI field in order to get method parameter --- crates/core/Cargo.toml | 1 + crates/core/src/driver/mod.rs | 83 ++++++++++++++++++++++++++++------ crates/format/src/input.rs | 84 +++++++++++++++++++++++++++++++++-- 3 files changed, 150 insertions(+), 18 deletions(-) diff --git a/crates/core/Cargo.toml b/crates/core/Cargo.toml index 7d2f946..9cdac1c 100644 --- a/crates/core/Cargo.toml +++ b/crates/core/Cargo.toml @@ -27,4 +27,5 @@ log = { workspace = true } env_logger = { workspace = true } rayon = { workspace = true } revive-solc-json-interface = { workspace = true } +serde_json = { workspace = true } temp-dir = { workspace = true } diff --git a/crates/core/src/driver/mod.rs b/crates/core/src/driver/mod.rs index 3e5ce55..d4583a5 100644 --- a/crates/core/src/driver/mod.rs +++ b/crates/core/src/driver/mod.rs @@ -1,12 +1,13 @@ //! The test driver handles the compilation and execution of the test cases. +use alloy::json_abi::JsonAbi; use alloy::primitives::Bytes; use alloy::rpc::types::TransactionInput; use alloy::{ primitives::{Address, TxKind, map::HashMap}, rpc::types::{ - TransactionReceipt, TransactionRequest, - trace::geth::{AccountState, DiffMode, GethTrace}, + TransactionRequest, + trace::geth::{AccountState, DiffMode}, }, }; use revive_dt_compiler::{Compiler, CompilerInput, SolidityCompiler}; @@ -15,6 +16,7 @@ use revive_dt_format::{input::Input, metadata::Metadata, mode::SolcMode}; use revive_dt_node_interaction::EthereumNode; use revive_dt_report::reporter::{CompilationTask, Report, Span}; use revive_solc_json_interface::SolcStandardJsonOutput; +use serde_json::Value; use crate::Platform; @@ -28,6 +30,7 @@ pub struct State<'a, T: Platform> { span: Span, contracts: Contracts, deployed_contracts: HashMap, + deployed_abis: HashMap, } impl<'a, T> State<'a, T> @@ -40,6 +43,7 @@ where span, contracts: Default::default(), deployed_contracts: Default::default(), + deployed_abis: Default::default(), } } @@ -126,15 +130,21 @@ where std::any::type_name::() ); - let tx = - match input.legacy_transaction(self.config.network_id, nonce, &self.deployed_contracts) - { - Ok(tx) => tx, - Err(err) => { - log::error!("Failed to construct legacy transaction: {err:?}"); - return Err(err); - } - }; + let tx = match input.legacy_transaction( + self.config.network_id, + nonce, + &self.deployed_contracts, + &self.deployed_abis, + ) { + Ok(tx) => { + log::debug!("Legacy transaction data: {:#?}", tx); + tx + } + Err(err) => { + log::error!("Failed to construct legacy transaction: {err:?}"); + return Err(err); + } + }; log::trace!("Executing transaction for input: {input:?}"); @@ -191,9 +201,6 @@ where &contract_name, &input.instance ); - if contract_name != &input.instance { - continue; - } let bytecode = contract .evm @@ -270,6 +277,54 @@ where address, std::any::type_name::() ); + + if let Some(Value::String(metadata_json_str)) = &contract.metadata { + log::trace!( + "metadata found for contract {}, {}", + contract_name, + metadata_json_str + ); + + match serde_json::from_str::(metadata_json_str) { + Ok(metadata_json) => { + if let Some(abi_value) = + metadata_json.get("output").and_then(|o| o.get("abi")) + { + match serde_json::from_value::(abi_value.clone()) { + Ok(parsed_abi) => { + log::trace!( + "ABI found in metadata for contract {}", + &contract_name + ); + self.deployed_abis + .insert(contract_name.clone(), parsed_abi); + } + Err(err) => { + log::debug!( + "Failed to parse ABI from metadata for {}: {}", + contract_name, + err + ); + } + } + } else { + log::debug!( + "No ABI found in metadata for contract {}", + contract_name + ); + } + } + Err(err) => { + log::debug!( + "Failed to parse metadata JSON string for contract {}: {}", + contract_name, + err + ); + } + } + } else { + log::debug!("No metadata found for contract {}", contract_name); + } } } } diff --git a/crates/format/src/input.rs b/crates/format/src/input.rs index a9d3562..b88e136 100644 --- a/crates/format/src/input.rs +++ b/crates/format/src/input.rs @@ -1,9 +1,10 @@ use std::collections::HashMap; use alloy::{ - json_abi::Function, - primitives::{Address, TxKind}, - rpc::types::TransactionRequest, + hex, + json_abi::{Function, JsonAbi}, + primitives::{Address, Bytes, TxKind}, + rpc::types::{TransactionInput, TransactionRequest}, }; use semver::VersionReq; use serde::{Deserialize, de::Deserializer}; @@ -44,7 +45,14 @@ pub struct ExpectedOutput { #[serde(untagged)] pub enum Calldata { Single(String), - Compound(Vec), + Compound(Vec), +} + +#[derive(Clone, Debug, Deserialize, Eq, PartialEq)] +#[serde(untagged)] +pub enum CalldataArg { + Literal(String), + AddressRef(String), // will be "Contract.address" } /// Specify how the contract is called. @@ -102,12 +110,77 @@ impl Input { .ok_or_else(|| anyhow::anyhow!("instance {instance} not deployed")) } + pub fn encoded_input( + &self, + deployed_abis: &HashMap, + deployed_contracts: &HashMap, + ) -> anyhow::Result { + let Method::Function(selector) = self.method else { + return Ok(Bytes::default()); // fallback or deployer — no input + }; + + // ABI + let abi = deployed_abis + .get(&self.instance) + .ok_or_else(|| anyhow::anyhow!("ABI for instance '{}' not found", &self.instance))?; + + // Find function by selector + let function = abi + .functions() + .find(|f| f.selector().0 == selector) + .ok_or_else(|| { + anyhow::anyhow!( + "Function with selector {:?} not found in ABI for the instance {:?}", + selector, + &self.instance + ) + })?; + + // Parse calldata + let calldata_args = match &self.calldata { + Some(Calldata::Compound(args)) => args, + _ => anyhow::bail!("Expected compound calldata for function call"), + }; + + // Convert each argument + let mut tokens = Vec::new(); + for (i, param) in function.inputs.iter().enumerate() { + let arg = calldata_args + .get(i) + .ok_or_else(|| anyhow::anyhow!("Missing calldata argument {}", i))?; + let token = match arg { + CalldataArg::Literal(value) => match param.ty.to_string().as_str() { + "uint256" | "uint" => Token::Uint(value.parse()?), + "address" => Token::Address(value.parse()?), + _ => anyhow::bail!("Unsupported literal type {}", param.ty), + }, + CalldataArg::AddressRef(name) => { + let addr = if name.ends_with(".address") { + let contract_name = name.trim_end_matches(".address"); + deployed_contracts.get(contract_name).copied() + } else { + None + }; + Token::Address( + addr.ok_or_else(|| anyhow::anyhow!("Address for '{}' not found", name))?, + ) + } + }; + tokens.push(token); + } + + // Encode + let encoded = function.encode_input(&tokens)?; + Ok(Bytes::from(encoded)) + } + /// Parse this input into a legacy transaction. pub fn legacy_transaction( &self, chain_id: u64, nonce: u64, deployed_contracts: &HashMap, + deployed_abis: &HashMap, ) -> anyhow::Result { let to = match self.method { Method::Deployer => Some(TxKind::Create), @@ -116,6 +189,8 @@ impl Input { )), }; + let input_data = self.encoded_input(deployed_abis, deployed_contracts)?; + Ok(TransactionRequest { from: Some(self.caller), to, @@ -123,6 +198,7 @@ impl Input { chain_id: Some(chain_id), gas_price: Some(5_000_000), gas: Some(5_000_000), + input: TransactionInput::new(input_data), ..Default::default() }) }