diff --git a/src/tracing/js/mod.rs b/src/tracing/js/mod.rs index ae8c4e57..f166a7d6 100644 --- a/src/tracing/js/mod.rs +++ b/src/tracing/js/mod.rs @@ -54,6 +54,8 @@ pub const RECURSION_LIMIT: usize = 10_000; #[derive(Debug)] pub struct JsInspector { ctx: Context, + /// The javascript code provided to the inspector. + code: String, /// The javascript config provided to the inspector. _js_config_value: JsValue, /// The input config object. @@ -126,9 +128,10 @@ impl JsInspector { register_builtins(&mut ctx)?; // evaluate the code - let code = format!("({code})"); - let obj = - ctx.eval(Source::from_bytes(code.as_bytes())).map_err(JsInspectorError::EvalCode)?; + let code_to_evaluate = format!("({code})"); + let obj = ctx + .eval(Source::from_bytes(code_to_evaluate.as_bytes())) + .map_err(JsInspectorError::EvalCode)?; let obj = obj.as_object().cloned().ok_or(JsInspectorError::ExpectedJsObject)?; @@ -178,6 +181,7 @@ impl JsInspector { Ok(Self { ctx, + code, _js_config_value, config, obj, @@ -192,6 +196,11 @@ impl JsInspector { }) } + /// Returns the javascript code. + pub const fn code(&self) -> &String { + &self.code + } + /// Returns the config object. pub const fn config(&self) -> &serde_json::Value { &self.config @@ -219,7 +228,7 @@ impl JsInspector { /// Note: This is supposed to be called after the inspection has finished. pub fn json_result( &mut self, - res: ResultAndState, + res: &ResultAndState, tx: &impl Transaction, block: &impl Block, db: &DB, @@ -235,7 +244,7 @@ impl JsInspector { /// Calls the result function and returns the result. pub fn result( &mut self, - res: ResultAndState, + res: &ResultAndState, tx: &TX, block: &impl Block, db: &DB, @@ -258,7 +267,7 @@ impl JsInspector { output_bytes = Some(out); } Output::Create(out, addr) => { - to = addr; + to = addr.clone(); output_bytes = Some(out); } }, @@ -293,7 +302,7 @@ impl JsInspector { value: tx.value(), block: block.number(), coinbase: block.beneficiary(), - output: output_bytes.unwrap_or_default(), + output: output_bytes.unwrap_or_default().clone(), time: block.timestamp().to_string(), intrinsic_gas: 0, transaction_ctx: self.transaction_context, @@ -718,7 +727,7 @@ mod tests { assert_eq!(res.result.is_success(), success); let (ctx, inspector) = evm.ctx_inspector(); - inspector.json_result(res, ctx.tx(), ctx.block(), ctx.db_ref()).unwrap() + inspector.json_result(&res, ctx.tx(), ctx.block(), ctx.db_ref()).unwrap() } #[test] diff --git a/src/tracing/mux.rs b/src/tracing/mux.rs index 1c0ef665..c4ea859a 100644 --- a/src/tracing/mux.rs +++ b/src/tracing/mux.rs @@ -4,10 +4,11 @@ use alloy_primitives::{map::HashMap, Address, Log, U256}; use alloy_rpc_types_eth::TransactionInfo; use alloy_rpc_types_trace::geth::{ mux::{MuxConfig, MuxFrame}, - CallConfig, FlatCallConfig, FourByteFrame, GethDebugBuiltInTracerType, NoopFrame, - PreStateConfig, + CallConfig, FlatCallConfig, FourByteFrame, GethDebugBuiltInTracerType, GethDebugTracerType, + GethTrace, NoopFrame, PreStateConfig, }; use revm::{ + context::{Block, Transaction}, context_interface::{ result::{HaltReasonTr, ResultAndState}, ContextTr, @@ -20,11 +21,17 @@ use revm::{ }; use thiserror::Error; +#[cfg(feature = "js-tracer")] +use crate::tracing::js::{JsInspector, JsInspectorError}; + /// Mux tracing inspector that runs and collects results of multiple inspectors at once. -#[derive(Clone, Debug)] +#[derive(Debug)] pub struct MuxInspector { /// An instance of FourByteInspector that can be reused four_byte: Option, + /// An instance of JsInspector that can be reused + #[cfg(feature = "js-tracer")] + js_tracer: Option, /// An instance of TracingInspector that can be reused tracing: Option, /// Configurations for different Geth trace types @@ -44,71 +51,113 @@ impl MuxInspector { /// Try creating a new instance of [MuxInspector] from the given [MuxConfig]. pub fn try_from_config(config: MuxConfig) -> Result { let mut four_byte = None; + #[cfg(feature = "js-tracer")] + let mut js_tracer: Option = None; let mut inspector_config = TracingInspectorConfig::none(); let mut configs = Vec::new(); // Process each tracer configuration for (tracer_type, tracer_config) in config.0 { match tracer_type { - GethDebugBuiltInTracerType::FourByteTracer => { - if tracer_config.is_some() { - return Err(Error::UnexpectedConfig(tracer_type)); - } - four_byte = Some(FourByteInspector::default()); - } - GethDebugBuiltInTracerType::CallTracer => { - let call_config = tracer_config - .ok_or(Error::MissingConfig(tracer_type))? - .into_call_config()?; + GethDebugTracerType::BuiltInTracer(built_in_tracer_type) => { + match built_in_tracer_type { + GethDebugBuiltInTracerType::FourByteTracer => { + if tracer_config.is_some() { + return Err(Error::UnexpectedConfig(tracer_type)); + } + four_byte = Some(FourByteInspector::default()); + } + GethDebugBuiltInTracerType::CallTracer => { + let call_config = tracer_config + .ok_or(Error::MissingConfig(built_in_tracer_type))? + .into_call_config()?; - inspector_config - .merge(TracingInspectorConfig::from_geth_call_config(&call_config)); - configs.push((tracer_type, TraceConfig::Call(call_config))); - } - GethDebugBuiltInTracerType::PreStateTracer => { - let prestate_config = tracer_config - .ok_or(Error::MissingConfig(tracer_type))? - .into_pre_state_config()?; + inspector_config + .merge(TracingInspectorConfig::from_geth_call_config(&call_config)); + configs.push((built_in_tracer_type, TraceConfig::Call(call_config))); + } + GethDebugBuiltInTracerType::PreStateTracer => { + let prestate_config = tracer_config + .ok_or(Error::MissingConfig(built_in_tracer_type))? + .into_pre_state_config()?; - inspector_config - .merge(TracingInspectorConfig::from_geth_prestate_config(&prestate_config)); - configs.push((tracer_type, TraceConfig::PreState(prestate_config))); - } - GethDebugBuiltInTracerType::NoopTracer => { - if tracer_config.is_some() { - return Err(Error::UnexpectedConfig(tracer_type)); + inspector_config.merge( + TracingInspectorConfig::from_geth_prestate_config(&prestate_config), + ); + configs.push(( + built_in_tracer_type, + TraceConfig::PreState(prestate_config), + )); + } + GethDebugBuiltInTracerType::NoopTracer => { + if tracer_config.is_some() { + return Err(Error::UnexpectedConfig(tracer_type)); + } + configs.push((built_in_tracer_type, TraceConfig::Noop)); + } + GethDebugBuiltInTracerType::FlatCallTracer => { + let flatcall_config = tracer_config + .ok_or(Error::MissingConfig(built_in_tracer_type))? + .into_flat_call_config()?; + + inspector_config.merge(TracingInspectorConfig::from_flat_call_config( + &flatcall_config, + )); + configs.push(( + built_in_tracer_type, + TraceConfig::FlatCall(flatcall_config), + )); + } + GethDebugBuiltInTracerType::MuxTracer => { + return Err(Error::UnexpectedConfig(tracer_type)); + } } - configs.push((tracer_type, TraceConfig::Noop)); } - GethDebugBuiltInTracerType::FlatCallTracer => { - let flatcall_config = tracer_config - .ok_or(Error::MissingConfig(tracer_type))? - .into_flat_call_config()?; - - inspector_config - .merge(TracingInspectorConfig::from_flat_call_config(&flatcall_config)); - configs.push((tracer_type, TraceConfig::FlatCall(flatcall_config))); + #[cfg(not(feature = "js-tracer"))] + GethDebugTracerType::JsTracer(_) => { + return Err(Error::Unsupported("JS Tracer is not enabled")) } - GethDebugBuiltInTracerType::MuxTracer => { - return Err(Error::UnexpectedConfig(tracer_type)); + #[cfg(feature = "js-tracer")] + GethDebugTracerType::JsTracer(ref code) => { + let config = match tracer_config { + Some(config) => config.into_json(), + None => serde_json::Value::Null, + }; + + js_tracer = Some(JsInspector::new(code.clone(), config)?); } } } let tracing = (!configs.is_empty()).then(|| TracingInspector::new(inspector_config)); - Ok(MuxInspector { four_byte, tracing, configs }) + Ok(MuxInspector { + four_byte, + #[cfg(feature = "js-tracer")] + js_tracer, + tracing, + configs, + }) } /// Try converting this [MuxInspector] into a [MuxFrame]. pub fn try_into_mux_frame( - &self, + &mut self, result: &ResultAndState, + tx: &impl Transaction, + block: &impl Block, db: &DB, - tx_info: TransactionInfo, ) -> Result { let mut frame = HashMap::with_capacity_and_hasher(self.configs.len(), Default::default()); + let tx_info = TransactionInfo { + block_number: Some(block.number()), + base_fee: Some(block.basefee()), + hash: None, + block_hash: None, + index: None, + }; + for (tracer_type, config) in &self.configs { let trace = match config { TraceConfig::Call(call_config) => { @@ -145,24 +194,37 @@ impl MuxInspector { TraceConfig::Noop => NoopFrame::default().into(), }; - frame.insert(*tracer_type, trace); + frame.insert(GethDebugTracerType::BuiltInTracer(*tracer_type), trace); } // Add four byte trace if inspector exists if let Some(inspector) = &self.four_byte { frame.insert( - GethDebugBuiltInTracerType::FourByteTracer, + GethDebugTracerType::BuiltInTracer(GethDebugBuiltInTracerType::FourByteTracer), FourByteFrame::from(inspector).into(), ); } + // Add js tracer if inspector exists + #[cfg(feature = "js-tracer")] + if let Some(ref mut js_inspector) = self.js_tracer { + frame.insert( + GethDebugTracerType::JsTracer(js_inspector.code().clone()), + GethTrace::JS( + js_inspector + .json_result(result, tx, block, db) + .unwrap_or_else(|err| serde_json::json!({ "error": err.to_string() })), + ), + ); + } + Ok(MuxFrame(frame)) } } impl Inspector for MuxInspector where - CTX: ContextTr, + CTX: ContextTr, { #[inline] fn initialize_interp(&mut self, interp: &mut Interpreter, context: &mut CTX) { @@ -172,6 +234,10 @@ where if let Some(ref mut inspector) = self.tracing { inspector.initialize_interp(interp, context); } + #[cfg(feature = "js-tracer")] + if let Some(ref mut inspector) = self.js_tracer { + inspector.initialize_interp(interp, context); + } } #[inline] @@ -182,6 +248,10 @@ where if let Some(ref mut inspector) = self.tracing { inspector.step(interp, context); } + #[cfg(feature = "js-tracer")] + if let Some(ref mut inspector) = self.js_tracer { + inspector.step(interp, context); + } } #[inline] @@ -192,6 +262,10 @@ where if let Some(ref mut inspector) = self.tracing { inspector.step_end(interp, context); } + #[cfg(feature = "js-tracer")] + if let Some(ref mut inspector) = self.js_tracer { + inspector.step_end(interp, context); + } } #[inline] @@ -200,6 +274,10 @@ where inspector.log(interp, context, log.clone()); } if let Some(ref mut inspector) = self.tracing { + inspector.log(interp, context, log.clone()); + } + #[cfg(feature = "js-tracer")] + if let Some(ref mut inspector) = self.js_tracer { inspector.log(interp, context, log); } } @@ -210,6 +288,10 @@ where let _ = inspector.call(context, inputs); } if let Some(ref mut inspector) = self.tracing { + let _ = inspector.call(context, inputs); + } + #[cfg(feature = "js-tracer")] + if let Some(ref mut inspector) = self.js_tracer { return inspector.call(context, inputs); } None @@ -223,6 +305,10 @@ where if let Some(ref mut inspector) = self.tracing { inspector.call_end(context, inputs, outcome); } + #[cfg(feature = "js-tracer")] + if let Some(ref mut inspector) = self.js_tracer { + inspector.call_end(context, inputs, outcome); + } } #[inline] @@ -231,6 +317,10 @@ where let _ = inspector.create(context, inputs); } if let Some(ref mut inspector) = self.tracing { + let _ = inspector.create(context, inputs); + } + #[cfg(feature = "js-tracer")] + if let Some(ref mut inspector) = self.js_tracer { return inspector.create(context, inputs); } None @@ -249,6 +339,10 @@ where if let Some(ref mut inspector) = self.tracing { inspector.create_end(context, inputs, outcome); } + #[cfg(feature = "js-tracer")] + if let Some(ref mut inspector) = self.js_tracer { + inspector.create_end(context, inputs, outcome); + } } #[inline] @@ -261,6 +355,10 @@ where let _ = inspector.eofcreate(context, inputs); } if let Some(ref mut inspector) = self.tracing { + let _ = inspector.eofcreate(context, inputs); + } + #[cfg(feature = "js-tracer")] + if let Some(ref mut inspector) = self.js_tracer { return inspector.eofcreate(context, inputs); } None @@ -279,6 +377,10 @@ where if let Some(ref mut inspector) = self.tracing { inspector.eofcreate_end(context, inputs, outcome); } + #[cfg(feature = "js-tracer")] + if let Some(ref mut inspector) = self.js_tracer { + inspector.eofcreate_end(context, inputs, outcome); + } } #[inline] @@ -289,19 +391,30 @@ where if let Some(ref mut inspector) = self.tracing { >::selfdestruct(inspector, contract, target, value); } + #[cfg(feature = "js-tracer")] + if let Some(ref mut inspector) = self.js_tracer { + >::selfdestruct(inspector, contract, target, value); + } } } /// Error type for [MuxInspector] #[derive(Debug, Error)] pub enum Error { + /// Some feature is unsupported + #[error("unsupported")] + Unsupported(&'static str), /// Config was provided for a tracer that does not expect it #[error("unexpected config for tracer '{0:?}'")] - UnexpectedConfig(GethDebugBuiltInTracerType), + UnexpectedConfig(GethDebugTracerType), /// Expected config is missing #[error("expected config is missing for tracer '{0:?}'")] MissingConfig(GethDebugBuiltInTracerType), /// Error when deserializing the config #[error("error deserializing config: {0}")] InvalidConfig(#[from] serde_json::Error), + /// Error when creating the JS inspector + #[cfg(feature = "js-tracer")] + #[error("failed to create JS inspector: {0}")] + JsInspectorErr(#[from] JsInspectorError), } diff --git a/tests/it/geth.rs b/tests/it/geth.rs index 81ce8459..6a190cc7 100644 --- a/tests/it/geth.rs +++ b/tests/it/geth.rs @@ -1,10 +1,9 @@ //! Geth tests use crate::utils::deploy_contract; use alloy_primitives::{hex, map::HashMap, Address, Bytes}; -use alloy_rpc_types_eth::TransactionInfo; use alloy_rpc_types_trace::geth::{ mux::MuxConfig, CallConfig, FlatCallConfig, GethDebugBuiltInTracerType, GethDebugTracerConfig, - GethTrace, PreStateConfig, PreStateFrame, + GethDebugTracerType, GethTrace, PreStateConfig, PreStateFrame, }; use revm::{ context::{ContextSetters, TxEnv}, @@ -141,6 +140,23 @@ fn test_geth_mux_tracer() { let mut evm = Context::mainnet().with_db(CacheDB::new(EmptyDB::default())).build_mainnet(); let code = hex!("608060405234801561001057600080fd5b506103ac806100206000396000f3fe60806040526004361061003f5760003560e01c80630332ed131461014d5780636ae1ad40146101625780638384a00214610177578063de7eb4f31461018c575b60405134815233906000805160206103578339815191529060200160405180910390a2306001600160a01b0316636ae1ad406040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561009d57600080fd5b505af19250505080156100ae575060015b50306001600160a01b0316630332ed136040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156100ea57600080fd5b505af19250505080156100fb575060015b50306001600160a01b0316638384a0026040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561013757600080fd5b505af115801561014b573d6000803e3d6000fd5b005b34801561015957600080fd5b5061014b6101a1565b34801561016e57600080fd5b5061014b610253565b34801561018357600080fd5b5061014b6102b7565b34801561019857600080fd5b5061014b6102dd565b306001600160a01b031663de7eb4f36040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156101dc57600080fd5b505af11580156101f0573d6000803e3d6000fd5b505060405162461bcd60e51b8152602060048201526024808201527f6e6573746564456d6974576974684661696c75726541667465724e6573746564604482015263115b5a5d60e21b6064820152608401915061024a9050565b60405180910390fd5b6040516000815233906000805160206103578339815191529060200160405180910390a260405162461bcd60e51b81526020600482015260156024820152746e6573746564456d6974576974684661696c75726560581b604482015260640161024a565b6040516000815233906000805160206103578339815191529060200160405180910390a2565b6040516000815233906000805160206103578339815191529060200160405180910390a2306001600160a01b0316638384a0026040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561033c57600080fd5b505af1158015610350573d6000803e3d6000fd5b5050505056fef950957d2407bed19dc99b718b46b4ce6090c05589006dfb86fd22c34865b23ea2646970667358221220090a696b9fbd22c7d1cc2a0b6d4a48c32d3ba892480713689a3145b73cfeb02164736f6c63430008130033"); + let js_tracer_code = r#" + { + "gasUsed": 0, + "previousGas": null, + "step": function(log, db) { + if (this.previousGas !== null) { + this.gasUsed += this.previousGas - log.getGas(); + } + this.previousGas = log.getGas(); + }, + "fault": function(log, db) { }, + "result": function(ctx, db) { + return { "totalGasUsed": this.gasUsed }; + } + }"# + .to_string(); + let deployer = Address::ZERO; let addr = deploy_contract(&mut evm, code.into(), deployer, SpecId::LONDON).created_address().unwrap(); @@ -151,19 +167,20 @@ fn test_geth_mux_tracer() { let prestate_config = PreStateConfig { diff_mode: Some(false), ..Default::default() }; let config = MuxConfig(HashMap::from_iter([ - (GethDebugBuiltInTracerType::FourByteTracer, None), + (GethDebugTracerType::BuiltInTracer(GethDebugBuiltInTracerType::FourByteTracer), None), ( - GethDebugBuiltInTracerType::CallTracer, + GethDebugTracerType::BuiltInTracer(GethDebugBuiltInTracerType::CallTracer), Some(GethDebugTracerConfig(serde_json::to_value(call_config).unwrap())), ), ( - GethDebugBuiltInTracerType::PreStateTracer, + GethDebugTracerType::BuiltInTracer(GethDebugBuiltInTracerType::PreStateTracer), Some(GethDebugTracerConfig(serde_json::to_value(prestate_config).unwrap())), ), ( - GethDebugBuiltInTracerType::FlatCallTracer, + GethDebugTracerType::BuiltInTracer(GethDebugBuiltInTracerType::FlatCallTracer), Some(GethDebugTracerConfig(serde_json::to_value(flatcall_config).unwrap())), ), + (GethDebugTracerType::JsTracer(js_tracer_code.clone()), None), ])); let mut insp = MuxInspector::try_from_config(config.clone()).unwrap(); @@ -183,16 +200,26 @@ fn test_geth_mux_tracer() { assert!(res.result.is_success()); let (ctx, inspector) = evm.ctx_inspector(); - let frame = - inspector.try_into_mux_frame(&res, ctx.db_ref(), TransactionInfo::default()).unwrap(); - - assert_eq!(frame.0.len(), 4); - assert!(frame.0.contains_key(&GethDebugBuiltInTracerType::FourByteTracer)); - assert!(frame.0.contains_key(&GethDebugBuiltInTracerType::CallTracer)); - assert!(frame.0.contains_key(&GethDebugBuiltInTracerType::PreStateTracer)); - assert!(frame.0.contains_key(&GethDebugBuiltInTracerType::FlatCallTracer)); - - let four_byte_frame = frame.0[&GethDebugBuiltInTracerType::FourByteTracer].clone(); + let frame = inspector.try_into_mux_frame(&res, ctx.tx(), ctx.block(), ctx.db_ref()).unwrap(); + + assert_eq!(frame.0.len(), 5); + assert!(frame.0.contains_key(&GethDebugTracerType::BuiltInTracer( + GethDebugBuiltInTracerType::FourByteTracer + ))); + assert!(frame + .0 + .contains_key(&GethDebugTracerType::BuiltInTracer(GethDebugBuiltInTracerType::CallTracer))); + assert!(frame.0.contains_key(&GethDebugTracerType::BuiltInTracer( + GethDebugBuiltInTracerType::PreStateTracer + ))); + assert!(frame.0.contains_key(&GethDebugTracerType::BuiltInTracer( + GethDebugBuiltInTracerType::FlatCallTracer + ))); + assert!(frame.0.contains_key(&GethDebugTracerType::JsTracer(js_tracer_code.clone()))); + + let four_byte_frame = frame.0 + [&GethDebugTracerType::BuiltInTracer(GethDebugBuiltInTracerType::FourByteTracer)] + .clone(); match four_byte_frame { GethTrace::FourByteTracer(four_byte_frame) => { assert_eq!(four_byte_frame.0.len(), 4); @@ -204,7 +231,9 @@ fn test_geth_mux_tracer() { _ => panic!("Expected FourByteTracer"), } - let call_frame = frame.0[&GethDebugBuiltInTracerType::CallTracer].clone(); + let call_frame = frame.0 + [&GethDebugTracerType::BuiltInTracer(GethDebugBuiltInTracerType::CallTracer)] + .clone(); match call_frame { GethTrace::CallTracer(call_frame) => { assert_eq!(call_frame.calls.len(), 3); @@ -213,7 +242,9 @@ fn test_geth_mux_tracer() { _ => panic!("Expected CallTracer"), } - let prestate_frame = frame.0[&GethDebugBuiltInTracerType::PreStateTracer].clone(); + let prestate_frame = frame.0 + [&GethDebugTracerType::BuiltInTracer(GethDebugBuiltInTracerType::PreStateTracer)] + .clone(); match prestate_frame { GethTrace::PreStateTracer(prestate_frame) => { if let PreStateFrame::Default(prestate_mode) = prestate_frame { @@ -225,7 +256,9 @@ fn test_geth_mux_tracer() { _ => panic!("Expected PreStateTracer"), } - let flatcall_frame = frame.0[&GethDebugBuiltInTracerType::FlatCallTracer].clone(); + let flatcall_frame = frame.0 + [&GethDebugTracerType::BuiltInTracer(GethDebugBuiltInTracerType::FlatCallTracer)] + .clone(); match flatcall_frame { GethTrace::FlatCallTracer(traces) => { assert_eq!(traces.len(), 6); @@ -238,6 +271,16 @@ fn test_geth_mux_tracer() { } _ => panic!("Expected FlatCallTracer"), } + + let js_tracer_frame = frame.0[&GethDebugTracerType::JsTracer(js_tracer_code)].clone(); + match js_tracer_frame { + GethTrace::JS(js_tracer_frame) => { + let expected: serde_json::Value = + serde_json::from_str("{\"totalGasUsed\": 10133}").unwrap(); + assert_eq!(js_tracer_frame, expected); + } + _ => panic!("Expected JsTracer"), + } } #[test] diff --git a/tests/it/geth_js.rs b/tests/it/geth_js.rs index a213a0aa..0df5b6b9 100644 --- a/tests/it/geth_js.rs +++ b/tests/it/geth_js.rs @@ -65,7 +65,7 @@ fn test_geth_jstracer_revert() { assert!(res.result.is_success()); let (context, insp) = evm.ctx_inspector(); - let result = insp.json_result(res, context.tx(), context.block(), context.db_ref()).unwrap(); + let result = insp.json_result(&res, context.tx(), context.block(), context.db_ref()).unwrap(); // successful operation assert!(!result["error"].as_bool().unwrap()); @@ -86,7 +86,7 @@ fn test_geth_jstracer_revert() { assert!(!res.result.is_success()); let (context, insp) = evm.ctx_inspector(); - let result = insp.json_result(res, context.tx(), context.block(), context.db_ref()).unwrap(); + let result = insp.json_result(&res, context.tx(), context.block(), context.db_ref()).unwrap(); // reverted operation assert!(result["error"].as_bool().unwrap()); @@ -173,6 +173,6 @@ fn test_geth_jstracer_proxy_contract() { assert!(res.result.is_success()); let (context, insp) = evm.ctx_inspector(); - let result = insp.json_result(res, context.tx(), context.block(), context.db_ref()).unwrap(); + let result = insp.json_result(&res, context.tx(), context.block(), context.db_ref()).unwrap(); assert_eq!(result, json!([{"event": "Transfer", "token": proxy_addr, "caller": deployer}])); }