@nomicfoundation/edr 0.12.0-next.5 → 0.12.0-next.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/config.rs CHANGED
@@ -2,16 +2,13 @@ use core::fmt::{Debug, Display};
2
2
  use std::{
3
3
  num::NonZeroU64,
4
4
  path::PathBuf,
5
- sync::Arc,
6
5
  time::{Duration, SystemTime},
7
6
  };
8
7
 
9
8
  use edr_coverage::reporter::SyncOnCollectedCoverageCallback;
10
- use edr_eth::{
11
- signature::{secret_key_from_str, SecretKey},
12
- Bytes, HashMap, HashSet,
13
- };
14
- use edr_solidity::contract_decoder::ContractDecoder;
9
+ use edr_eip1559::{BaseFeeActivation, ConstantBaseFeeParams};
10
+ use edr_eth::{Bytes, HashMap, HashSet};
11
+ use edr_signer::{secret_key_from_str, SecretKey};
15
12
  use napi::{
16
13
  bindgen_prelude::{BigInt, Promise, Reference, Uint8Array},
17
14
  threadsafe_function::{
@@ -27,6 +24,49 @@ use crate::{
27
24
  precompile::Precompile, subscription::SubscriptionConfig,
28
25
  };
29
26
 
27
+ /// Configuration for EIP-1559 parameters
28
+ #[napi(object)]
29
+ pub struct BaseFeeParamActivation {
30
+ pub activation: Either<BaseFeeActivationByBlockNumber, BaseFeeActivationByHardfork>,
31
+ pub max_change_denominator: BigInt,
32
+ pub elasticity_multiplier: BigInt,
33
+ }
34
+
35
+ #[napi(object)]
36
+ pub struct BaseFeeActivationByBlockNumber {
37
+ /// The block number at which the `base_fee_params` is activated
38
+ pub block_number: BigInt,
39
+ }
40
+ #[napi(object)]
41
+ pub struct BaseFeeActivationByHardfork {
42
+ /// The hardfork at which the `base_fee_params` is activated
43
+ pub hardfork: String,
44
+ }
45
+
46
+ impl TryFrom<BaseFeeParamActivation> for (BaseFeeActivation<String>, ConstantBaseFeeParams) {
47
+ type Error = napi::Error;
48
+
49
+ fn try_from(value: BaseFeeParamActivation) -> Result<Self, Self::Error> {
50
+ let base_fee_params = ConstantBaseFeeParams {
51
+ max_change_denominator: value.max_change_denominator.try_cast()?,
52
+ elasticity_multiplier: value.elasticity_multiplier.try_cast()?,
53
+ };
54
+
55
+ match value.activation {
56
+ Either::A(BaseFeeActivationByBlockNumber { block_number }) => {
57
+ let activation_block_number: u64 = block_number.try_cast()?;
58
+ Ok((
59
+ BaseFeeActivation::BlockNumber(activation_block_number),
60
+ base_fee_params,
61
+ ))
62
+ }
63
+ Either::B(BaseFeeActivationByHardfork { hardfork }) => {
64
+ Ok((BaseFeeActivation::Hardfork(hardfork), base_fee_params))
65
+ }
66
+ }
67
+ }
68
+ }
69
+
30
70
  /// Specification of a chain with possible overrides.
31
71
  #[napi(object)]
32
72
  pub struct ChainOverride {
@@ -142,6 +182,15 @@ pub struct ProviderConfig {
142
182
  pub bail_on_call_failure: bool,
143
183
  /// Whether to return an `Err` when a `eth_sendTransaction` fails
144
184
  pub bail_on_transaction_failure: bool,
185
+ /// EIP-1559 base fee parameters activations to be used to calculate the
186
+ /// block base fee.
187
+ ///
188
+ /// Provide an ordered list of `base_fee_params` to be
189
+ /// used starting from the specified activation point (hardfork or block
190
+ /// number).
191
+ /// If not provided, the default values from the chain spec
192
+ /// will be used.
193
+ pub base_fee_config: Option<Vec<BaseFeeParamActivation>>,
145
194
  /// The gas limit of each block
146
195
  pub block_gas_limit: BigInt,
147
196
  /// The chain ID of the blockchain
@@ -423,7 +472,7 @@ impl ProviderConfig {
423
472
  // This is the only place in production code where it's allowed to use
424
473
  // `DangerousSecretKeyStr`.
425
474
  #[allow(deprecated)]
426
- use edr_eth::signature::DangerousSecretKeyStr;
475
+ use edr_signer::DangerousSecretKeyStr;
427
476
 
428
477
  static_assertions::assert_not_impl_all!(JsString: Debug, Display, serde::Serialize);
429
478
  static_assertions::assert_not_impl_all!(JsStringUtf8: Debug, Display, serde::Serialize);
@@ -443,6 +492,11 @@ impl ProviderConfig {
443
492
  })
444
493
  .collect::<napi::Result<Vec<_>>>()?;
445
494
 
495
+ let base_fee_params: Option<Vec<(BaseFeeActivation<String>, ConstantBaseFeeParams)>> = self
496
+ .base_fee_config
497
+ .map(|vec| vec.into_iter().map(TryInto::try_into).collect())
498
+ .transpose()?;
499
+
446
500
  let block_gas_limit =
447
501
  NonZeroU64::new(self.block_gas_limit.try_cast()?).ok_or_else(|| {
448
502
  napi::Error::new(
@@ -468,6 +522,7 @@ impl ProviderConfig {
468
522
  allow_unlimited_contract_size: self.allow_unlimited_contract_size,
469
523
  bail_on_call_failure: self.bail_on_call_failure,
470
524
  bail_on_transaction_failure: self.bail_on_transaction_failure,
525
+ base_fee_params,
471
526
  block_gas_limit,
472
527
  chain_id: self.chain_id.try_cast()?,
473
528
  coinbase: self.coinbase.try_cast()?,
@@ -549,7 +604,6 @@ impl From<BuildInfoAndOutput> for edr_napi_core::solidity::config::BuildInfoAndO
549
604
 
550
605
  /// Result of [`resolve_configs`].
551
606
  pub struct ConfigResolution {
552
- pub contract_decoder: Arc<ContractDecoder>,
553
607
  pub logger_config: edr_napi_core::logger::Config,
554
608
  pub provider_config: edr_napi_core::provider::Config,
555
609
  pub subscription_callback: edr_napi_core::subscription::Callback,
@@ -562,28 +616,15 @@ pub fn resolve_configs(
562
616
  provider_config: ProviderConfig,
563
617
  logger_config: LoggerConfig,
564
618
  subscription_config: SubscriptionConfig,
565
- tracing_config: TracingConfigWithBuffers,
566
619
  ) -> napi::Result<ConfigResolution> {
567
620
  let provider_config = provider_config.resolve(env, runtime)?;
568
621
  let logger_config = logger_config.resolve(env)?;
569
622
 
570
- // TODO: https://github.com/NomicFoundation/edr/issues/760
571
- let build_info_config = edr_solidity::artifacts::BuildInfoConfig::parse_from_buffers(
572
- (&edr_napi_core::solidity::config::TracingConfigWithBuffers::from(tracing_config)).into(),
573
- )
574
- .map_err(|error| napi::Error::from_reason(error.to_string()))?;
575
-
576
- let contract_decoder = ContractDecoder::new(&build_info_config).map_or_else(
577
- |error| Err(napi::Error::from_reason(error.to_string())),
578
- |contract_decoder| Ok(Arc::new(contract_decoder)),
579
- )?;
580
-
581
623
  let subscription_config = edr_napi_core::subscription::Config::from(subscription_config);
582
624
  let subscription_callback =
583
625
  edr_napi_core::subscription::Callback::new(env, subscription_config.subscription_callback)?;
584
626
 
585
627
  Ok(ConfigResolution {
586
- contract_decoder,
587
628
  logger_config,
588
629
  provider_config,
589
630
  subscription_callback,
package/src/context.rs CHANGED
@@ -19,6 +19,7 @@ use tracing_subscriber::{prelude::*, EnvFilter, Registry};
19
19
 
20
20
  use crate::{
21
21
  config::{resolve_configs, ConfigResolution, ProviderConfig, TracingConfigWithBuffers},
22
+ contract_decoder::ContractDecoder,
22
23
  logger::LoggerConfig,
23
24
  provider::{Provider, ProviderFactory},
24
25
  solidity_tests::{
@@ -57,7 +58,7 @@ impl EdrContext {
57
58
  provider_config: ProviderConfig,
58
59
  logger_config: LoggerConfig,
59
60
  subscription_config: SubscriptionConfig,
60
- tracing_config: TracingConfigWithBuffers,
61
+ contract_decoder: &ContractDecoder,
61
62
  ) -> napi::Result<JsObject> {
62
63
  let (deferred, promise) = env.create_deferred()?;
63
64
 
@@ -76,7 +77,6 @@ impl EdrContext {
76
77
  let runtime = runtime::Handle::current();
77
78
 
78
79
  let ConfigResolution {
79
- contract_decoder,
80
80
  logger_config,
81
81
  provider_config,
82
82
  subscription_callback,
@@ -86,7 +86,6 @@ impl EdrContext {
86
86
  provider_config,
87
87
  logger_config,
88
88
  subscription_config,
89
- tracing_config
90
89
  ));
91
90
 
92
91
  #[cfg(feature = "scenarios")]
@@ -105,6 +104,7 @@ impl EdrContext {
105
104
  try_or_reject_promise!(context.get_provider_factory(&chain_type))
106
105
  };
107
106
 
107
+ let contract_decoder = Arc::clone(contract_decoder.as_inner());
108
108
  runtime.clone().spawn_blocking(move || {
109
109
  let result = factory
110
110
  .create_provider(
@@ -0,0 +1,57 @@
1
+ use std::sync::Arc;
2
+
3
+ use napi_derive::napi;
4
+
5
+ use crate::config::TracingConfigWithBuffers;
6
+
7
+ #[napi]
8
+ pub struct ContractDecoder {
9
+ inner: Arc<edr_solidity::contract_decoder::ContractDecoder>,
10
+ }
11
+
12
+ #[napi]
13
+ impl ContractDecoder {
14
+ #[doc = "Creates an empty instance."]
15
+ #[napi(constructor, catch_unwind)]
16
+ // Following TS convention for the constructor without arguments to be `new()`.
17
+ #[allow(clippy::new_without_default)]
18
+ pub fn new() -> Self {
19
+ Self {
20
+ inner: Arc::new(edr_solidity::contract_decoder::ContractDecoder::default()),
21
+ }
22
+ }
23
+
24
+ #[doc = "Creates a new instance with the provided configuration."]
25
+ #[napi(factory, catch_unwind)]
26
+ pub fn with_contracts(config: TracingConfigWithBuffers) -> napi::Result<Self> {
27
+ let build_info_config = edr_solidity::artifacts::BuildInfoConfig::parse_from_buffers(
28
+ (&edr_napi_core::solidity::config::TracingConfigWithBuffers::from(config)).into(),
29
+ )
30
+ .map_err(|error| napi::Error::from_reason(error.to_string()))?;
31
+
32
+ let contract_decoder =
33
+ edr_solidity::contract_decoder::ContractDecoder::new(&build_info_config).map_or_else(
34
+ |error| Err(napi::Error::from_reason(error.to_string())),
35
+ |contract_decoder| Ok(Arc::new(contract_decoder)),
36
+ )?;
37
+
38
+ Ok(Self {
39
+ inner: contract_decoder,
40
+ })
41
+ }
42
+ }
43
+
44
+ impl ContractDecoder {
45
+ /// Returns a reference to the inner contract decoder.
46
+ pub fn as_inner(&self) -> &Arc<edr_solidity::contract_decoder::ContractDecoder> {
47
+ &self.inner
48
+ }
49
+ }
50
+
51
+ impl From<Arc<edr_solidity::contract_decoder::ContractDecoder>> for ContractDecoder {
52
+ fn from(contract_decoder: Arc<edr_solidity::contract_decoder::ContractDecoder>) -> Self {
53
+ Self {
54
+ inner: contract_decoder,
55
+ }
56
+ }
57
+ }
@@ -3,6 +3,8 @@ use std::collections::HashMap;
3
3
  use napi::bindgen_prelude::{BigInt, Uint8Array};
4
4
  use napi_derive::napi;
5
5
 
6
+ // False positive: imported by HH2
7
+ #[allow(dead_code)]
6
8
  #[napi(object)]
7
9
  pub struct DebugTraceResult {
8
10
  pub pass: bool,
package/src/lib.rs CHANGED
@@ -17,6 +17,8 @@ pub mod chains;
17
17
  pub mod config;
18
18
  /// Types related to an EDR N-API context.
19
19
  pub mod context;
20
+ /// Types for decoding smart contract data.
21
+ pub mod contract_decoder;
20
22
  mod debug_trace;
21
23
  /// Types and functions related to code coverage instrumentation.
22
24
  pub mod instrument;
package/src/log.rs CHANGED
@@ -9,8 +9,8 @@ pub struct ExecutionLog {
9
9
  pub data: Uint8Array,
10
10
  }
11
11
 
12
- impl From<&edr_eth::log::ExecutionLog> for ExecutionLog {
13
- fn from(value: &edr_eth::log::ExecutionLog) -> Self {
12
+ impl From<&edr_receipt::log::ExecutionLog> for ExecutionLog {
13
+ fn from(value: &edr_receipt::log::ExecutionLog) -> Self {
14
14
  let topics = value
15
15
  .topics()
16
16
  .iter()
package/src/mock/time.rs CHANGED
@@ -1,7 +1,8 @@
1
1
  use std::sync::Arc;
2
2
 
3
- use edr_eth::{spec::ChainSpec, B256};
3
+ use edr_eth::B256;
4
4
  use edr_evm::spec::RuntimeSpec;
5
+ use edr_evm_spec::ChainSpec;
5
6
  use edr_generic::GenericChainSpec;
6
7
  use edr_napi_core::logger::Logger;
7
8
  use edr_rpc_eth::RpcSpec;
@@ -10,7 +11,8 @@ use napi_derive::napi;
10
11
 
11
12
  use crate::{
12
13
  cast::TryCast as _,
13
- config::{resolve_configs, ConfigResolution, ProviderConfig, TracingConfigWithBuffers},
14
+ config::{resolve_configs, ConfigResolution, ProviderConfig},
15
+ contract_decoder::ContractDecoder,
14
16
  logger::LoggerConfig,
15
17
  provider::Provider,
16
18
  subscription::SubscriptionConfig,
@@ -49,7 +51,7 @@ pub fn create_provider_with_mock_timer(
49
51
  provider_config: ProviderConfig,
50
52
  logger_config: LoggerConfig,
51
53
  subscription_config: SubscriptionConfig,
52
- tracing_config: TracingConfigWithBuffers,
54
+ contract_decoder: &ContractDecoder,
53
55
  time: &MockTime,
54
56
  ) -> napi::Result<JsObject> {
55
57
  let (deferred, promise) = env.create_deferred()?;
@@ -69,7 +71,6 @@ pub fn create_provider_with_mock_timer(
69
71
  let runtime = runtime::Handle::current();
70
72
 
71
73
  let ConfigResolution {
72
- contract_decoder,
73
74
  logger_config,
74
75
  provider_config,
75
76
  subscription_callback,
@@ -79,9 +80,9 @@ pub fn create_provider_with_mock_timer(
79
80
  provider_config,
80
81
  logger_config,
81
82
  subscription_config,
82
- tracing_config,
83
83
  ));
84
84
 
85
+ let contract_decoder = Arc::clone(contract_decoder.as_inner());
85
86
  let timer = Arc::clone(&time.inner);
86
87
 
87
88
  runtime.clone().spawn_blocking(move || {
@@ -95,7 +96,7 @@ pub fn create_provider_with_mock_timer(
95
96
  )?;
96
97
 
97
98
  let provider_config =
98
- edr_provider::ProviderConfig::<edr_eth::l1::SpecId>::try_from(provider_config)?;
99
+ edr_provider::ProviderConfig::<edr_chain_l1::Hardfork>::try_from(provider_config)?;
99
100
 
100
101
  let provider =
101
102
  edr_provider::Provider::<GenericChainSpec, Arc<edr_provider::time::MockTime>>::new(
@@ -111,7 +112,7 @@ pub fn create_provider_with_mock_timer(
111
112
  subscription_callback.call(event);
112
113
  }),
113
114
  provider_config,
114
- contract_decoder.clone(),
115
+ Arc::clone(&contract_decoder),
115
116
  timer,
116
117
  )
117
118
  .map_err(|error| napi::Error::from_reason(error.to_string()))?;
package/src/mock.rs CHANGED
@@ -2,6 +2,7 @@ pub mod time;
2
2
 
3
3
  use std::sync::Arc;
4
4
 
5
+ use edr_evm_spec::EvmHaltReason;
5
6
  use edr_napi_core::provider::SyncProvider;
6
7
  use edr_rpc_client::jsonrpc;
7
8
  use edr_solidity::contract_decoder::ContractDecoder;
@@ -22,20 +23,11 @@ impl MockProvider {
22
23
  }
23
24
 
24
25
  impl SyncProvider for MockProvider {
25
- fn add_compilation_result(
26
- &self,
27
- _solc_version: String,
28
- _compiler_input: edr_solidity::artifacts::CompilerInput,
29
- _compiler_output: edr_solidity::artifacts::CompilerOutput,
30
- ) -> napi::Result<bool> {
31
- Ok(false) // Mock provider does not handle compilation results
32
- }
33
-
34
26
  fn handle_request(
35
27
  &self,
36
28
  _request: String,
37
29
  _contract_decoder: Arc<ContractDecoder>,
38
- ) -> napi::Result<edr_napi_core::spec::Response<edr_eth::l1::HaltReason>> {
30
+ ) -> napi::Result<edr_napi_core::spec::Response<EvmHaltReason>> {
39
31
  let response = jsonrpc::ResponseData::Success {
40
32
  result: self.mocked_response.clone(),
41
33
  };
@@ -1,4 +1,4 @@
1
- use edr_eth::l1;
1
+ use edr_evm_spec::EvmHaltReason;
2
2
  use edr_napi_core::spec::SolidityTraceData;
3
3
  use edr_solidity::contract_decoder::NestedTraceDecoder as _;
4
4
  use napi::Either;
@@ -11,11 +11,11 @@ use crate::{
11
11
 
12
12
  #[napi]
13
13
  pub struct Response {
14
- inner: edr_napi_core::spec::Response<l1::HaltReason>,
14
+ inner: edr_napi_core::spec::Response<EvmHaltReason>,
15
15
  }
16
16
 
17
- impl From<edr_napi_core::spec::Response<l1::HaltReason>> for Response {
18
- fn from(value: edr_napi_core::spec::Response<l1::HaltReason>) -> Self {
17
+ impl From<edr_napi_core::spec::Response<EvmHaltReason>> for Response {
18
+ fn from(value: edr_napi_core::spec::Response<EvmHaltReason>) -> Self {
19
19
  Self { inner: value }
20
20
  }
21
21
  }
package/src/provider.rs CHANGED
@@ -5,18 +5,18 @@ mod response;
5
5
  use std::sync::Arc;
6
6
 
7
7
  use edr_napi_core::provider::SyncProvider;
8
- use edr_solidity::contract_decoder::ContractDecoder;
8
+ use edr_solidity::compiler::create_models_and_decode_bytecodes;
9
9
  use napi::{tokio::runtime, Env, JsFunction, JsObject, Status};
10
10
  use napi_derive::napi;
11
11
 
12
12
  pub use self::factory::ProviderFactory;
13
13
  use self::response::Response;
14
- use crate::call_override::CallOverrideCallback;
14
+ use crate::{call_override::CallOverrideCallback, contract_decoder::ContractDecoder};
15
15
 
16
16
  /// A JSON-RPC provider for Ethereum.
17
17
  #[napi]
18
18
  pub struct Provider {
19
- contract_decoder: Arc<ContractDecoder>,
19
+ contract_decoder: Arc<edr_solidity::contract_decoder::ContractDecoder>,
20
20
  provider: Arc<dyn SyncProvider>,
21
21
  runtime: runtime::Handle,
22
22
  #[cfg(feature = "scenarios")]
@@ -28,7 +28,7 @@ impl Provider {
28
28
  pub fn new(
29
29
  provider: Arc<dyn SyncProvider>,
30
30
  runtime: runtime::Handle,
31
- contract_decoder: Arc<ContractDecoder>,
31
+ contract_decoder: Arc<edr_solidity::contract_decoder::ContractDecoder>,
32
32
  #[cfg(feature = "scenarios")] scenario_file: Option<
33
33
  napi::tokio::sync::Mutex<napi::tokio::fs::File>,
34
34
  >,
@@ -54,8 +54,8 @@ impl Provider {
54
54
  solc_version: String,
55
55
  compiler_input: serde_json::Value,
56
56
  compiler_output: serde_json::Value,
57
- ) -> napi::Result<bool> {
58
- let provider = self.provider.clone();
57
+ ) -> napi::Result<()> {
58
+ let contract_decoder = self.contract_decoder.clone();
59
59
 
60
60
  self.runtime
61
61
  .spawn_blocking(move || {
@@ -65,12 +65,33 @@ impl Provider {
65
65
  let compiler_output = serde_json::from_value(compiler_output)
66
66
  .map_err(|error| napi::Error::from_reason(error.to_string()))?;
67
67
 
68
- provider.add_compilation_result(solc_version, compiler_input, compiler_output)
68
+ let contracts = match create_models_and_decode_bytecodes(
69
+ solc_version,
70
+ &compiler_input,
71
+ &compiler_output,
72
+ ) {
73
+ Ok(contracts) => contracts,
74
+ Err(error) => {
75
+ return Err(napi::Error::from_reason(format!("Contract decoder failed to be updated. Please report this to help us improve Hardhat.\n{error}")));
76
+ }
77
+ };
78
+
79
+ for contract in contracts {
80
+ contract_decoder.add_contract_metadata(contract);
81
+ }
82
+
83
+ Ok(())
69
84
  })
70
85
  .await
71
86
  .map_err(|error| napi::Error::new(Status::GenericFailure, error.to_string()))?
72
87
  }
73
88
 
89
+ #[doc = "Retrieves the instance's contract decoder."]
90
+ #[napi(catch_unwind)]
91
+ pub fn contract_decoder(&self) -> ContractDecoder {
92
+ ContractDecoder::from(Arc::clone(&self.contract_decoder))
93
+ }
94
+
74
95
  #[doc = "Handles a JSON-RPC request and returns a JSON-RPC response."]
75
96
  #[napi(catch_unwind)]
76
97
  pub async fn handle_request(&self, request: String) -> napi::Result<Response> {
package/src/result.rs CHANGED
@@ -1,4 +1,5 @@
1
1
  use edr_evm::trace::AfterMessage;
2
+ use edr_evm_spec::EvmHaltReason;
2
3
  use napi::{
3
4
  bindgen_prelude::{BigInt, Either3, Uint8Array},
4
5
  Either,
@@ -99,61 +100,36 @@ pub enum ExceptionalHalt {
99
100
  CreateInitCodeSizeLimit,
100
101
  }
101
102
 
102
- impl From<edr_eth::l1::HaltReason> for ExceptionalHalt {
103
- fn from(halt: edr_eth::l1::HaltReason) -> Self {
103
+ impl From<EvmHaltReason> for ExceptionalHalt {
104
+ fn from(halt: EvmHaltReason) -> Self {
104
105
  match halt {
105
- edr_eth::l1::HaltReason::OutOfGas(..) => ExceptionalHalt::OutOfGas,
106
- edr_eth::l1::HaltReason::OpcodeNotFound => ExceptionalHalt::OpcodeNotFound,
107
- edr_eth::l1::HaltReason::InvalidFEOpcode => ExceptionalHalt::InvalidFEOpcode,
108
- edr_eth::l1::HaltReason::InvalidJump => ExceptionalHalt::InvalidJump,
109
- edr_eth::l1::HaltReason::NotActivated => ExceptionalHalt::NotActivated,
110
- edr_eth::l1::HaltReason::StackUnderflow => ExceptionalHalt::StackUnderflow,
111
- edr_eth::l1::HaltReason::StackOverflow => ExceptionalHalt::StackOverflow,
112
- edr_eth::l1::HaltReason::OutOfOffset => ExceptionalHalt::OutOfOffset,
113
- edr_eth::l1::HaltReason::CreateCollision => ExceptionalHalt::CreateCollision,
114
- edr_eth::l1::HaltReason::PrecompileError => ExceptionalHalt::PrecompileError,
115
- edr_eth::l1::HaltReason::NonceOverflow => ExceptionalHalt::NonceOverflow,
116
- edr_eth::l1::HaltReason::CreateContractSizeLimit => {
117
- ExceptionalHalt::CreateContractSizeLimit
118
- }
119
- edr_eth::l1::HaltReason::CreateContractStartingWithEF => {
106
+ EvmHaltReason::OutOfGas(..) => ExceptionalHalt::OutOfGas,
107
+ EvmHaltReason::OpcodeNotFound => ExceptionalHalt::OpcodeNotFound,
108
+ EvmHaltReason::InvalidFEOpcode => ExceptionalHalt::InvalidFEOpcode,
109
+ EvmHaltReason::InvalidJump => ExceptionalHalt::InvalidJump,
110
+ EvmHaltReason::NotActivated => ExceptionalHalt::NotActivated,
111
+ EvmHaltReason::StackUnderflow => ExceptionalHalt::StackUnderflow,
112
+ EvmHaltReason::StackOverflow => ExceptionalHalt::StackOverflow,
113
+ EvmHaltReason::OutOfOffset => ExceptionalHalt::OutOfOffset,
114
+ EvmHaltReason::CreateCollision => ExceptionalHalt::CreateCollision,
115
+ EvmHaltReason::PrecompileError => ExceptionalHalt::PrecompileError,
116
+ EvmHaltReason::NonceOverflow => ExceptionalHalt::NonceOverflow,
117
+ EvmHaltReason::CreateContractSizeLimit => ExceptionalHalt::CreateContractSizeLimit,
118
+ EvmHaltReason::CreateContractStartingWithEF => {
120
119
  ExceptionalHalt::CreateContractStartingWithEF
121
120
  }
122
- edr_eth::l1::HaltReason::CreateInitCodeSizeLimit => {
123
- ExceptionalHalt::CreateInitCodeSizeLimit
124
- }
125
- edr_eth::l1::HaltReason::OverflowPayment
126
- | edr_eth::l1::HaltReason::StateChangeDuringStaticCall
127
- | edr_eth::l1::HaltReason::CallNotAllowedInsideStatic
128
- | edr_eth::l1::HaltReason::OutOfFunds
129
- | edr_eth::l1::HaltReason::CallTooDeep => {
121
+ EvmHaltReason::CreateInitCodeSizeLimit => ExceptionalHalt::CreateInitCodeSizeLimit,
122
+ EvmHaltReason::OverflowPayment
123
+ | EvmHaltReason::StateChangeDuringStaticCall
124
+ | EvmHaltReason::CallNotAllowedInsideStatic
125
+ | EvmHaltReason::OutOfFunds
126
+ | EvmHaltReason::CallTooDeep => {
130
127
  unreachable!("Internal halts that can be only found inside Inspector: {halt:?}")
131
128
  }
132
129
  }
133
130
  }
134
131
  }
135
132
 
136
- impl From<ExceptionalHalt> for edr_eth::l1::HaltReason {
137
- fn from(value: ExceptionalHalt) -> Self {
138
- match value {
139
- ExceptionalHalt::OutOfGas => Self::OutOfGas(edr_eth::l1::OutOfGasError::Basic),
140
- ExceptionalHalt::OpcodeNotFound => Self::OpcodeNotFound,
141
- ExceptionalHalt::InvalidFEOpcode => Self::InvalidFEOpcode,
142
- ExceptionalHalt::InvalidJump => Self::InvalidJump,
143
- ExceptionalHalt::NotActivated => Self::NotActivated,
144
- ExceptionalHalt::StackUnderflow => Self::StackUnderflow,
145
- ExceptionalHalt::StackOverflow => Self::StackOverflow,
146
- ExceptionalHalt::OutOfOffset => Self::OutOfOffset,
147
- ExceptionalHalt::CreateCollision => Self::CreateCollision,
148
- ExceptionalHalt::PrecompileError => Self::PrecompileError,
149
- ExceptionalHalt::NonceOverflow => Self::NonceOverflow,
150
- ExceptionalHalt::CreateContractSizeLimit => Self::CreateContractSizeLimit,
151
- ExceptionalHalt::CreateContractStartingWithEF => Self::CreateContractStartingWithEF,
152
- ExceptionalHalt::CreateInitCodeSizeLimit => Self::CreateInitCodeSizeLimit,
153
- }
154
- }
155
- }
156
-
157
133
  /// The result when the EVM terminates due to an exceptional halt.
158
134
  #[napi(object)]
159
135
  pub struct HaltResult {
@@ -173,8 +149,8 @@ pub struct ExecutionResult {
173
149
  pub contract_address: Option<Uint8Array>,
174
150
  }
175
151
 
176
- impl From<&AfterMessage<edr_eth::l1::HaltReason>> for ExecutionResult {
177
- fn from(value: &AfterMessage<edr_eth::l1::HaltReason>) -> Self {
152
+ impl From<&AfterMessage<EvmHaltReason>> for ExecutionResult {
153
+ fn from(value: &AfterMessage<EvmHaltReason>) -> Self {
178
154
  let AfterMessage {
179
155
  execution_result,
180
156
  contract_address,
@@ -109,7 +109,7 @@ pub struct SolidityTestRunnerConfigArgs {
109
109
  /// Defaults to false.
110
110
  pub disable_block_gas_limit: Option<bool>,
111
111
  /// The memory limit of the EVM in bytes.
112
- /// Defaults to 33_554_432 (2^25 = 32MiB).
112
+ /// Defaults to `33_554_432` (2^25 = 32MiB).
113
113
  #[serde(serialize_with = "serialize_optional_bigint_as_struct")]
114
114
  pub memory_limit: Option<BigInt>,
115
115
  /// The predeploys applied in local mode. Defaults to no predeploys.
@@ -143,6 +143,8 @@ pub struct SolidityTestRunnerConfigArgs {
143
143
  /// If an invariant config setting is not set, but a corresponding fuzz
144
144
  /// config value is set, then the fuzz config value will be used.
145
145
  pub invariant: Option<InvariantConfigArgs>,
146
+ /// Whether to collect stack traces.
147
+ pub collect_stack_traces: Option<CollectStackTraces>,
146
148
  /// Controls which test results should include execution traces. Defaults to
147
149
  /// None.
148
150
  pub include_traces: Option<IncludeTraces>,
@@ -194,6 +196,7 @@ impl SolidityTestRunnerConfigArgs {
194
196
  prompt_timeout,
195
197
  fuzz,
196
198
  invariant,
199
+ collect_stack_traces,
197
200
  include_traces,
198
201
  observability,
199
202
  test_pattern,
@@ -296,6 +299,10 @@ impl SolidityTestRunnerConfigArgs {
296
299
  cheatcode,
297
300
  fuzz,
298
301
  invariant,
302
+ collect_stack_traces: collect_stack_traces.map_or(
303
+ edr_solidity_tests::CollectStackTraces::OnFailure,
304
+ edr_solidity_tests::CollectStackTraces::from,
305
+ ),
299
306
  on_collected_coverage_fn,
300
307
  test_pattern,
301
308
  };
@@ -721,6 +728,29 @@ pub struct AddressLabel {
721
728
  pub label: String,
722
729
  }
723
730
 
731
+ /// A type that controls when stack traces are collected.
732
+ #[napi]
733
+ #[derive(Debug, serde::Serialize)]
734
+ pub enum CollectStackTraces {
735
+ /// Always collects stack traces, adding performance overhead.
736
+ Always,
737
+ /// Only collects stack traces upon failure, re-executing the test. This
738
+ /// minimizes performance overhead.
739
+ ///
740
+ /// Not all tests can be re-executed since certain cheatcodes contain
741
+ /// non-deterministic side-effects.
742
+ OnFailure,
743
+ }
744
+
745
+ impl From<CollectStackTraces> for edr_solidity_tests::CollectStackTraces {
746
+ fn from(value: CollectStackTraces) -> Self {
747
+ match value {
748
+ CollectStackTraces::Always => edr_solidity_tests::CollectStackTraces::Always,
749
+ CollectStackTraces::OnFailure => edr_solidity_tests::CollectStackTraces::OnFailure,
750
+ }
751
+ }
752
+ }
753
+
724
754
  /// Configuration for [`SolidityTestRunnerConfigArgs::include_traces`] that
725
755
  /// controls execution trace decoding and inclusion in test results.
726
756
  #[napi]
@@ -8,7 +8,7 @@ use edr_napi_core::solidity::{
8
8
  use edr_solidity::artifacts::ArtifactId;
9
9
  use edr_solidity_tests::{
10
10
  contracts::ContractsByArtifact, decode::RevertDecoder, evm_context::L1EvmBuilder,
11
- multi_runner::TestContract, revm::context::TxEnv, MultiContractRunner,
11
+ multi_runner::TestContract, MultiContractRunner,
12
12
  };
13
13
  use napi::tokio;
14
14
  use napi_derive::napi;
@@ -33,14 +33,14 @@ impl SyncTestRunnerFactory for L1TestRunnerFactory {
33
33
  let runner = tokio::task::block_in_place(|| {
34
34
  runtime
35
35
  .block_on(MultiContractRunner::<
36
- edr_eth::l1::BlockEnv,
36
+ edr_chain_l1::BlockEnv,
37
37
  (),
38
38
  L1EvmBuilder,
39
- edr_eth::l1::HaltReason,
40
- edr_eth::l1::SpecId,
39
+ edr_chain_l1::HaltReason,
40
+ edr_chain_l1::Hardfork,
41
41
  _,
42
- edr_eth::l1::InvalidTransaction,
43
- TxEnv,
42
+ edr_chain_l1::InvalidTransaction,
43
+ edr_chain_l1::TxEnv,
44
44
  >::new(
45
45
  config.try_into()?,
46
46
  contracts,