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

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/index.d.ts CHANGED
@@ -198,6 +198,16 @@ export interface CodeCoverageConfig {
198
198
  */
199
199
  onCollectedCoverageCallback: (coverageHits: Uint8Array[]) => Promise<void>
200
200
  }
201
+ export interface GasReportConfig {
202
+ /**
203
+ * Gas reports are collected after a block is mined or `eth_call` is
204
+ * executed.
205
+ *
206
+ * Exceptions thrown in the callback will be propagated to the original
207
+ * caller.
208
+ */
209
+ onCollectedGasReportCallback: (gasReport: GasReport) => Promise<void>
210
+ }
201
211
  /** Configuration for forking a blockchain */
202
212
  export interface ForkConfig {
203
213
  /**
@@ -258,6 +268,8 @@ export interface MiningConfig {
258
268
  export interface ObservabilityConfig {
259
269
  /** If present, configures runtime observability to collect code coverage. */
260
270
  codeCoverage?: CodeCoverageConfig
271
+ /** If present, configures runtime observability to collect gas reports. */
272
+ gasReport?: GasReportConfig
261
273
  }
262
274
  /** Configuration for a provider */
263
275
  export interface ProviderConfig {
@@ -372,6 +384,27 @@ export interface DebugTraceLogItem {
372
384
  /** Map of all stored values with keys and values encoded as hex strings. */
373
385
  storage?: Record<string, string>
374
386
  }
387
+ export interface GasReport {
388
+ contracts: Record<string, ContractGasReport>
389
+ }
390
+ export interface ContractGasReport {
391
+ deployments: Array<DeploymentGasReport>
392
+ functions: Record<string, Array<FunctionGasReport>>
393
+ }
394
+ export enum GasReportExecutionStatus {
395
+ Success = 0,
396
+ Revert = 1,
397
+ Halt = 2
398
+ }
399
+ export interface DeploymentGasReport {
400
+ gas: bigint
401
+ size: bigint
402
+ status: GasReportExecutionStatus
403
+ }
404
+ export interface FunctionGasReport {
405
+ gas: bigint
406
+ status: GasReportExecutionStatus
407
+ }
375
408
  export interface InstrumentationResult {
376
409
  /** The generated source code with coverage instrumentation. */
377
410
  readonly source: string
@@ -703,6 +736,13 @@ export interface SolidityTestRunnerConfigArgs {
703
736
  * match the pattern will be executed and reported as a test result.
704
737
  */
705
738
  testPattern?: string
739
+ /**
740
+ * Controls whether to generate a gas report after running the tests.
741
+ * Enabling this also enables collection of all traces and EVM isolation
742
+ * mode.
743
+ * Defaults to false.
744
+ */
745
+ generateGasReport?: boolean
706
746
  }
707
747
  /** Fuzz testing configuration */
708
748
  export interface FuzzConfigArgs {
@@ -1103,6 +1143,11 @@ export interface DecodedTraceParameters {
1103
1143
  */
1104
1144
  arguments: Array<string>
1105
1145
  }
1146
+ /** The result of a Solidity test run. */
1147
+ export interface SolidityTestResult {
1148
+ /** Gas report, if it was generated. */
1149
+ readonly gasReport?: GasReport
1150
+ }
1106
1151
  /** Configuration for subscriptions. */
1107
1152
  export interface SubscriptionConfig {
1108
1153
  /** Callback to be called when a new event is received. */
@@ -1363,22 +1408,37 @@ export interface Withdrawal {
1363
1408
  amount: bigint
1364
1409
  }
1365
1410
  export declare class EdrContext {
1366
- /**Creates a new [`EdrContext`] instance. Should only be called once! */
1411
+ /** Creates a new [`EdrContext`] instance. Should only be called once! */
1367
1412
  constructor()
1368
- /**Constructs a new provider with the provided configuration. */
1413
+ /** Constructs a new provider with the provided configuration. */
1369
1414
  createProvider(chainType: string, providerConfig: ProviderConfig, loggerConfig: LoggerConfig, subscriptionConfig: SubscriptionConfig, contractDecoder: ContractDecoder): Promise<Provider>
1370
- /**Registers a new provider factory for the provided chain type. */
1415
+ /** Registers a new provider factory for the provided chain type. */
1371
1416
  registerProviderFactory(chainType: string, factory: ProviderFactory): Promise<void>
1372
1417
  registerSolidityTestRunnerFactory(chainType: string, factory: SolidityTestRunnerFactory): Promise<void>
1373
1418
  /**
1374
- *Executes Solidity tests.
1419
+ * Executes Solidity tests
1375
1420
  *
1376
- *The function will return as soon as test execution is started.
1377
- *The progress callback will be called with the results of each test
1378
- *suite. It is up to the caller to track how many times the callback
1379
- *is called to know when all tests are done.
1380
- */
1381
- runSolidityTests(chainType: string, artifacts: Array<Artifact>, testSuites: Array<ArtifactId>, configArgs: SolidityTestRunnerConfigArgs, tracingConfig: TracingConfigWithBuffers, onTestSuiteCompletedCallback: (result: SuiteResult) => void): Promise<void>
1421
+ * The function will return a promise that resolves to a
1422
+ * [`SolidityTestResult`].
1423
+ *
1424
+ * Arguments:
1425
+ * - `chainType`: the same chain type that was passed to
1426
+ * `registerProviderFactory`.
1427
+ * - `artifacts`: the project's compilation output artifacts. It's
1428
+ * important to include include all artifacts here, otherwise cheatcodes
1429
+ * that access artifacts and other functionality (e.g. auto-linking, gas
1430
+ * reports) can break.
1431
+ * - `testSuites`: the test suite ids that specify which test suites to
1432
+ * execute. The test suite artifacts must be present in `artifacts`.
1433
+ * - `configArgs`: solidity test runner configuration. See the struct docs
1434
+ * for details.
1435
+ * - `tracingConfig`: the build infos used for stack trace generation.
1436
+ * These are lazily parsed and it's important that they're passed as
1437
+ * Uint8 arrays for performance.
1438
+ * - `onTestSuiteCompletedCallback`: The progress callback will be called
1439
+ * with the results of each test suite as soon as it finished executing.
1440
+ */
1441
+ runSolidityTests(chainType: string, artifacts: Array<Artifact>, testSuites: Array<ArtifactId>, configArgs: SolidityTestRunnerConfigArgs, tracingConfig: TracingConfigWithBuffers, onTestSuiteCompletedCallback: (result: SuiteResult) => void): Promise<SolidityTestResult>
1382
1442
  }
1383
1443
  export declare class ContractDecoder {
1384
1444
  /**Creates an empty instance. */
package/index.js CHANGED
@@ -310,7 +310,7 @@ if (!nativeBinding) {
310
310
  throw new Error(`Failed to load native binding`)
311
311
  }
312
312
 
313
- const { GENERIC_CHAIN_TYPE, genericChainProviderFactory, L1_CHAIN_TYPE, l1GenesisState, l1ProviderFactory, SpecId, l1HardforkFromString, l1HardforkToString, l1HardforkLatest, FRONTIER, FRONTIER_THAWING, HOMESTEAD, DAO_FORK, TANGERINE, SPURIOUS_DRAGON, BYZANTIUM, CONSTANTINOPLE, PETERSBURG, ISTANBUL, MUIR_GLACIER, BERLIN, LONDON, ARROW_GLACIER, GRAY_GLACIER, MERGE, SHANGHAI, CANCUN, PRAGUE, OpHardfork, opHardforkFromString, opHardforkToString, opLatestHardfork, OP_CHAIN_TYPE, opGenesisState, opProviderFactory, BEDROCK, REGOLITH, CANYON, ECOTONE, FJORD, GRANITE, HOLOCENE, ISTHMUS, MineOrdering, EdrContext, ContractDecoder, addStatementCoverageInstrumentation, Precompile, precompileP256Verify, ProviderFactory, Response, Provider, SuccessReason, ExceptionalHalt, CachedChains, CachedEndpoints, FsAccessPermission, CollectStackTraces, IncludeTraces, SolidityTestRunnerFactory, l1SolidityTestRunnerFactory, opSolidityTestRunnerFactory, SuiteResult, TestResult, TestStatus, CallKind, LogKind, linkHexStringBytecode, printStackTrace, Exit, ExitCode, BytecodeWrapper, ContractFunctionType, ReturnData, StackTraceEntryType, stackTraceEntryTypeToString, FALLBACK_FUNCTION_NAME, RECEIVE_FUNCTION_NAME, CONSTRUCTOR_FUNCTION_NAME, UNRECOGNIZED_FUNCTION_NAME, UNKNOWN_FUNCTION_NAME, PRECOMPILE_FUNCTION_NAME, UNRECOGNIZED_CONTRACT_NAME, RawTrace, getLatestSupportedSolcVersion } = nativeBinding
313
+ const { GENERIC_CHAIN_TYPE, genericChainProviderFactory, L1_CHAIN_TYPE, l1GenesisState, l1ProviderFactory, SpecId, l1HardforkFromString, l1HardforkToString, l1HardforkLatest, FRONTIER, FRONTIER_THAWING, HOMESTEAD, DAO_FORK, TANGERINE, SPURIOUS_DRAGON, BYZANTIUM, CONSTANTINOPLE, PETERSBURG, ISTANBUL, MUIR_GLACIER, BERLIN, LONDON, ARROW_GLACIER, GRAY_GLACIER, MERGE, SHANGHAI, CANCUN, PRAGUE, OpHardfork, opHardforkFromString, opHardforkToString, opLatestHardfork, OP_CHAIN_TYPE, opGenesisState, opProviderFactory, BEDROCK, REGOLITH, CANYON, ECOTONE, FJORD, GRANITE, HOLOCENE, ISTHMUS, MineOrdering, EdrContext, ContractDecoder, GasReportExecutionStatus, addStatementCoverageInstrumentation, Precompile, precompileP256Verify, ProviderFactory, Response, Provider, SuccessReason, ExceptionalHalt, CachedChains, CachedEndpoints, FsAccessPermission, CollectStackTraces, IncludeTraces, SolidityTestRunnerFactory, l1SolidityTestRunnerFactory, opSolidityTestRunnerFactory, SuiteResult, TestResult, TestStatus, CallKind, LogKind, linkHexStringBytecode, printStackTrace, Exit, ExitCode, BytecodeWrapper, ContractFunctionType, ReturnData, StackTraceEntryType, stackTraceEntryTypeToString, FALLBACK_FUNCTION_NAME, RECEIVE_FUNCTION_NAME, CONSTRUCTOR_FUNCTION_NAME, UNRECOGNIZED_FUNCTION_NAME, UNKNOWN_FUNCTION_NAME, PRECOMPILE_FUNCTION_NAME, UNRECOGNIZED_CONTRACT_NAME, RawTrace, getLatestSupportedSolcVersion } = nativeBinding
314
314
 
315
315
  module.exports.GENERIC_CHAIN_TYPE = GENERIC_CHAIN_TYPE
316
316
  module.exports.genericChainProviderFactory = genericChainProviderFactory
@@ -358,6 +358,7 @@ module.exports.ISTHMUS = ISTHMUS
358
358
  module.exports.MineOrdering = MineOrdering
359
359
  module.exports.EdrContext = EdrContext
360
360
  module.exports.ContractDecoder = ContractDecoder
361
+ module.exports.GasReportExecutionStatus = GasReportExecutionStatus
361
362
  module.exports.addStatementCoverageInstrumentation = addStatementCoverageInstrumentation
362
363
  module.exports.Precompile = Precompile
363
364
  module.exports.precompileP256Verify = precompileP256Verify
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nomicfoundation/edr",
3
- "version": "0.12.0-next.7",
3
+ "version": "0.12.0-next.9",
4
4
  "devDependencies": {
5
5
  "@napi-rs/cli": "^2.18.4",
6
6
  "@nomicfoundation/ethereumjs-util": "^9.0.4",
@@ -58,14 +58,14 @@
58
58
  },
59
59
  "repository": "NomicFoundation/edr.git",
60
60
  "types": "index.d.ts",
61
- "optionalDependencies": {
62
- "@nomicfoundation/edr-darwin-arm64": "0.12.0-next.7",
63
- "@nomicfoundation/edr-darwin-x64": "0.12.0-next.7",
64
- "@nomicfoundation/edr-linux-arm64-gnu": "0.12.0-next.7",
65
- "@nomicfoundation/edr-linux-arm64-musl": "0.12.0-next.7",
66
- "@nomicfoundation/edr-linux-x64-gnu": "0.12.0-next.7",
67
- "@nomicfoundation/edr-linux-x64-musl": "0.12.0-next.7",
68
- "@nomicfoundation/edr-win32-x64-msvc": "0.12.0-next.7"
61
+ "dependencies": {
62
+ "@nomicfoundation/edr-darwin-arm64": "0.12.0-next.9",
63
+ "@nomicfoundation/edr-darwin-x64": "0.12.0-next.9",
64
+ "@nomicfoundation/edr-linux-arm64-gnu": "0.12.0-next.9",
65
+ "@nomicfoundation/edr-linux-arm64-musl": "0.12.0-next.9",
66
+ "@nomicfoundation/edr-linux-x64-gnu": "0.12.0-next.9",
67
+ "@nomicfoundation/edr-linux-x64-musl": "0.12.0-next.9",
68
+ "@nomicfoundation/edr-win32-x64-msvc": "0.12.0-next.9"
69
69
  },
70
70
  "scripts": {
71
71
  "artifacts": "napi artifacts",
package/src/account.rs CHANGED
@@ -1,6 +1,7 @@
1
1
  use derive_more::Debug;
2
- use edr_eth::{hex, HashMap, U256};
2
+ use edr_primitives::{hex, Address, HashMap, U256};
3
3
  use edr_solidity_tests::{backend::Predeploy, revm::state::AccountInfo};
4
+ use edr_state_api::EvmStorageSlot;
4
5
  use napi::bindgen_prelude::{BigInt, Uint8Array};
5
6
  use napi_derive::napi;
6
7
 
@@ -36,7 +37,7 @@ pub struct AccountOverride {
36
37
  pub storage: Option<Vec<StorageSlot>>,
37
38
  }
38
39
 
39
- impl TryFrom<AccountOverride> for (edr_eth::Address, edr_provider::AccountOverride) {
40
+ impl TryFrom<AccountOverride> for (Address, edr_provider::AccountOverride) {
40
41
  type Error = napi::Error;
41
42
 
42
43
  fn try_from(value: AccountOverride) -> Result<Self, Self::Error> {
@@ -53,9 +54,9 @@ impl TryFrom<AccountOverride> for (edr_eth::Address, edr_provider::AccountOverri
53
54
  .into_iter()
54
55
  .map(|StorageSlot { index, value }| {
55
56
  let value = value.try_cast()?;
56
- let slot = edr_evm::state::EvmStorageSlot::new(value, 0);
57
+ let slot = EvmStorageSlot::new(value, 0);
57
58
 
58
- let index: edr_eth::U256 = index.try_cast()?;
59
+ let index: U256 = index.try_cast()?;
59
60
  Ok((index, slot))
60
61
  })
61
62
  .collect::<napi::Result<_>>()
@@ -69,7 +70,7 @@ impl TryFrom<AccountOverride> for (edr_eth::Address, edr_provider::AccountOverri
69
70
  storage,
70
71
  };
71
72
 
72
- let address: edr_eth::Address = address.try_cast()?;
73
+ let address: Address = address.try_cast()?;
73
74
 
74
75
  Ok((address, account_override))
75
76
  }
package/src/block.rs CHANGED
@@ -16,7 +16,7 @@ pub struct BlobGas {
16
16
  pub excess_gas: BigInt,
17
17
  }
18
18
 
19
- impl TryFrom<BlobGas> for edr_eth::block::BlobGas {
19
+ impl TryFrom<BlobGas> for edr_block_header::BlobGas {
20
20
  type Error = napi::Error;
21
21
 
22
22
  fn try_from(value: BlobGas) -> Result<Self, Self::Error> {
@@ -1,6 +1,6 @@
1
1
  use std::sync::mpsc::channel;
2
2
 
3
- use edr_eth::{Address, Bytes};
3
+ use edr_primitives::{Address, Bytes};
4
4
  use napi::{
5
5
  bindgen_prelude::{Promise, Uint8Array},
6
6
  threadsafe_function::{
package/src/cast.rs CHANGED
@@ -1,4 +1,4 @@
1
- use edr_eth::{Address, Bytecode, Bytes, B256, B64, U256};
1
+ use edr_primitives::{Address, Bytecode, Bytes, B256, B64, U256};
2
2
  use napi::{
3
3
  bindgen_prelude::{BigInt, Uint8Array},
4
4
  Status,
package/src/chains/op.rs CHANGED
@@ -1,12 +1,18 @@
1
1
  use std::{str::FromStr, sync::Arc};
2
2
 
3
- use edr_eth::hex;
4
3
  use edr_napi_core::{
5
4
  logger::Logger,
6
5
  provider::{SyncProvider, SyncProviderFactory},
7
6
  subscription::subscriber_callback_for_chain_spec,
8
7
  };
9
- use edr_op::{predeploys::GAS_PRICE_ORACLE_ADDRESS, OpChainSpec};
8
+ use edr_op::{
9
+ predeploys::{
10
+ gas_price_oracle_code_ecotone, gas_price_oracle_code_fjord, gas_price_oracle_code_isthmus,
11
+ GAS_PRICE_ORACLE_ADDRESS,
12
+ },
13
+ OpChainSpec,
14
+ };
15
+ use edr_primitives::hex;
10
16
  use edr_provider::time::CurrentTime;
11
17
  use edr_solidity::contract_decoder::ContractDecoder;
12
18
  use napi::{
@@ -347,16 +353,11 @@ fn gas_price_oracle_override(hardfork: edr_op::Hardfork) -> AccountOverride {
347
353
  }
348
354
 
349
355
  fn gas_price_oracle_ecotone() -> AccountOverride {
350
- let gas_price_oracle_code = hex::decode(include_str!(
351
- "../../data/op/predeploys/gas_price_oracle/ecotone.txt"
352
- ))
353
- .expect("The bytecode for the GasPriceOracle predeploy should be a valid hex string");
354
-
355
356
  AccountOverride {
356
357
  address: Uint8Array::with_data_copied(GAS_PRICE_ORACLE_ADDRESS),
357
358
  balance: None,
358
359
  nonce: None,
359
- code: Some(gas_price_oracle_code.into()),
360
+ code: Some(gas_price_oracle_code_ecotone().into()),
360
361
  storage: Some(vec![StorageSlot {
361
362
  index: BigInt::from(0u64),
362
363
  // bool isEcotone = true
@@ -368,16 +369,11 @@ fn gas_price_oracle_ecotone() -> AccountOverride {
368
369
  }
369
370
 
370
371
  fn gas_price_oracle_fjord() -> AccountOverride {
371
- let gas_price_oracle_code = hex::decode(include_str!(
372
- "../../data/op/predeploys/gas_price_oracle/fjord.txt"
373
- ))
374
- .expect("The bytecode for the GasPriceOracle predeploy should be a valid hex string");
375
-
376
372
  AccountOverride {
377
373
  address: Uint8Array::with_data_copied(GAS_PRICE_ORACLE_ADDRESS),
378
374
  balance: None,
379
375
  nonce: None,
380
- code: Some(gas_price_oracle_code.into()),
376
+ code: Some(gas_price_oracle_code_fjord().into()),
381
377
  storage: Some(vec![StorageSlot {
382
378
  index: BigInt::from(0u64),
383
379
  // bool isEcotone = true
@@ -390,16 +386,11 @@ fn gas_price_oracle_fjord() -> AccountOverride {
390
386
  }
391
387
 
392
388
  fn gas_price_oracle_isthmus() -> AccountOverride {
393
- let gas_price_oracle_code = hex::decode(include_str!(
394
- "../../data/op/predeploys/gas_price_oracle/isthmus.txt"
395
- ))
396
- .expect("The bytecode for the GasPriceOracle predeploy should be a valid hex string");
397
-
398
389
  AccountOverride {
399
390
  address: Uint8Array::with_data_copied(GAS_PRICE_ORACLE_ADDRESS),
400
391
  balance: None,
401
392
  nonce: None,
402
- code: Some(gas_price_oracle_code.into()),
393
+ code: Some(gas_price_oracle_code_isthmus().into()),
403
394
  storage: Some(vec![StorageSlot {
404
395
  index: BigInt::from(0u64),
405
396
  // bool isEcotone = true
package/src/config.rs CHANGED
@@ -7,7 +7,8 @@ use std::{
7
7
 
8
8
  use edr_coverage::reporter::SyncOnCollectedCoverageCallback;
9
9
  use edr_eip1559::{BaseFeeActivation, ConstantBaseFeeParams};
10
- use edr_eth::{Bytes, HashMap, HashSet};
10
+ use edr_gas_report::SyncOnCollectedGasReportCallback;
11
+ use edr_primitives::{Bytes, HashMap, HashSet};
11
12
  use edr_signer::{secret_key_from_str, SecretKey};
12
13
  use napi::{
13
14
  bindgen_prelude::{BigInt, Promise, Reference, Uint8Array},
@@ -20,8 +21,8 @@ use napi::{
20
21
  use napi_derive::napi;
21
22
 
22
23
  use crate::{
23
- account::AccountOverride, block::BlobGas, cast::TryCast, logger::LoggerConfig,
24
- precompile::Precompile, subscription::SubscriptionConfig,
24
+ account::AccountOverride, block::BlobGas, cast::TryCast, gas_report::GasReport,
25
+ logger::LoggerConfig, precompile::Precompile, subscription::SubscriptionConfig,
25
26
  };
26
27
 
27
28
  /// Configuration for EIP-1559 parameters
@@ -92,6 +93,17 @@ pub struct CodeCoverageConfig {
92
93
  pub on_collected_coverage_callback: JsFunction,
93
94
  }
94
95
 
96
+ #[napi(object)]
97
+ pub struct GasReportConfig {
98
+ /// Gas reports are collected after a block is mined or `eth_call` is
99
+ /// executed.
100
+ ///
101
+ /// Exceptions thrown in the callback will be propagated to the original
102
+ /// caller.
103
+ #[napi(ts_type = "(gasReport: GasReport) => Promise<void>")]
104
+ pub on_collected_gas_report_callback: JsFunction,
105
+ }
106
+
95
107
  /// Configuration for forking a blockchain
96
108
  #[napi(object)]
97
109
  pub struct ForkConfig {
@@ -169,6 +181,8 @@ pub struct MiningConfig {
169
181
  pub struct ObservabilityConfig {
170
182
  /// If present, configures runtime observability to collect code coverage.
171
183
  pub code_coverage: Option<CodeCoverageConfig>,
184
+ /// If present, configures runtime observability to collect gas reports.
185
+ pub gas_report: Option<GasReportConfig>,
172
186
  }
173
187
 
174
188
  /// Configuration for a provider
@@ -385,6 +399,8 @@ impl ObservabilityConfig {
385
399
  .code_coverage
386
400
  .map(
387
401
  |code_coverage| -> napi::Result<Box<dyn SyncOnCollectedCoverageCallback>> {
402
+ let runtime = runtime.clone();
403
+
388
404
  let mut on_collected_coverage_callback: ThreadsafeFunction<
389
405
  _,
390
406
  ErrorStrategy::Fatal,
@@ -450,9 +466,61 @@ impl ObservabilityConfig {
450
466
  },
451
467
  )
452
468
  .transpose()?;
469
+ let on_collected_gas_report_fn = self.gas_report.map(
470
+ |gas_report| -> napi::Result<Box<dyn SyncOnCollectedGasReportCallback>> {
471
+ let mut on_collected_gas_report_callback: ThreadsafeFunction<
472
+ _,
473
+ ErrorStrategy::Fatal,
474
+ > = gas_report
475
+ .on_collected_gas_report_callback
476
+ .create_threadsafe_function(
477
+ 0,
478
+ |ctx: ThreadSafeCallContext<GasReport>| {
479
+ let report = ctx.value;
480
+ Ok(vec![report])
481
+ }
482
+ ,
483
+ )?;
484
+ // Maintain a weak reference to the function to avoid blocking the event loop
485
+ // from exiting.
486
+ on_collected_gas_report_callback.unref(env)?;
487
+
488
+ let on_collected_gas_report_fn: Box<dyn SyncOnCollectedGasReportCallback> =
489
+ Box::new(move |report| {
490
+ let runtime = runtime.clone();
491
+
492
+ let (sender, receiver) = std::sync::mpsc::channel();
493
+
494
+ // Convert the report to the N-API representation
495
+ let status = on_collected_gas_report_callback
496
+ .call_with_return_value(GasReport::from(report), ThreadsafeFunctionCallMode::Blocking, move |result: Promise<()>| {
497
+ // We spawn a background task to handle the async callback
498
+ runtime.spawn(async move {
499
+ let result = result.await;
500
+ sender.send(result).map_err(|_error| {
501
+ napi::Error::new(
502
+ napi::Status::GenericFailure,
503
+ "Failed to send result from on_collected_gas_report_callback",
504
+ )
505
+ })
506
+ });
507
+ Ok(())
508
+ });
509
+
510
+ assert_eq!(status, napi::Status::Ok);
511
+
512
+ let () = receiver.recv().expect("Receive can only fail if the channel is closed")?;
513
+
514
+ Ok(())
515
+ });
516
+
517
+ Ok(on_collected_gas_report_fn)
518
+ },
519
+ ).transpose()?;
453
520
 
454
521
  Ok(edr_provider::observability::Config {
455
522
  on_collected_coverage_fn,
523
+ on_collected_gas_report_fn,
456
524
  ..edr_provider::observability::Config::default()
457
525
  })
458
526
  }
@@ -509,7 +577,7 @@ impl ProviderConfig {
509
577
  .genesis_state
510
578
  .into_iter()
511
579
  .map(TryInto::try_into)
512
- .collect::<napi::Result<HashMap<edr_eth::Address, edr_provider::AccountOverride>>>()?;
580
+ .collect::<napi::Result<HashMap<edr_primitives::Address, edr_provider::AccountOverride>>>()?;
513
581
 
514
582
  let precompile_overrides = self
515
583
  .precompile_overrides
package/src/context.rs CHANGED
@@ -1,7 +1,7 @@
1
1
  use std::sync::Arc;
2
2
 
3
- use edr_eth::HashMap;
4
3
  use edr_napi_core::{provider::SyncProviderFactory, solidity};
4
+ use edr_primitives::HashMap;
5
5
  use edr_solidity_tests::{
6
6
  decode::RevertDecoder,
7
7
  multi_runner::{SuiteResultAndArtifactId, TestContract, TestContracts},
@@ -26,7 +26,7 @@ use crate::{
26
26
  artifact::{Artifact, ArtifactId},
27
27
  config::SolidityTestRunnerConfigArgs,
28
28
  factory::SolidityTestRunnerFactory,
29
- test_results::SuiteResult,
29
+ test_results::{SolidityTestResult, SuiteResult},
30
30
  LinkingOutput,
31
31
  },
32
32
  subscription::SubscriptionConfig,
@@ -39,7 +39,7 @@ pub struct EdrContext {
39
39
 
40
40
  #[napi]
41
41
  impl EdrContext {
42
- #[doc = "Creates a new [`EdrContext`] instance. Should only be called once!"]
42
+ /// Creates a new [`EdrContext`] instance. Should only be called once!
43
43
  #[napi(catch_unwind, constructor)]
44
44
  pub fn new() -> napi::Result<Self> {
45
45
  let context = Context::new()?;
@@ -49,7 +49,7 @@ impl EdrContext {
49
49
  })
50
50
  }
51
51
 
52
- #[doc = "Constructs a new provider with the provided configuration."]
52
+ /// Constructs a new provider with the provided configuration.
53
53
  #[napi(catch_unwind, ts_return_type = "Promise<Provider>")]
54
54
  pub fn create_provider(
55
55
  &self,
@@ -130,7 +130,7 @@ impl EdrContext {
130
130
  Ok(promise)
131
131
  }
132
132
 
133
- #[doc = "Registers a new provider factory for the provided chain type."]
133
+ /// Registers a new provider factory for the provided chain type.
134
134
  #[napi(catch_unwind)]
135
135
  pub async fn register_provider_factory(
136
136
  &self,
@@ -153,14 +153,29 @@ impl EdrContext {
153
153
  Ok(())
154
154
  }
155
155
 
156
- #[doc = "Executes Solidity tests."]
157
- #[doc = ""]
158
- #[doc = "The function will return as soon as test execution is started."]
159
- #[doc = "The progress callback will be called with the results of each test"]
160
- #[doc = "suite. It is up to the caller to track how many times the callback"]
161
- #[doc = "is called to know when all tests are done."]
156
+ /// Executes Solidity tests
157
+ ///
158
+ /// The function will return a promise that resolves to a
159
+ /// [`SolidityTestResult`].
160
+ ///
161
+ /// Arguments:
162
+ /// - `chainType`: the same chain type that was passed to
163
+ /// `registerProviderFactory`.
164
+ /// - `artifacts`: the project's compilation output artifacts. It's
165
+ /// important to include include all artifacts here, otherwise cheatcodes
166
+ /// that access artifacts and other functionality (e.g. auto-linking, gas
167
+ /// reports) can break.
168
+ /// - `testSuites`: the test suite ids that specify which test suites to
169
+ /// execute. The test suite artifacts must be present in `artifacts`.
170
+ /// - `configArgs`: solidity test runner configuration. See the struct docs
171
+ /// for details.
172
+ /// - `tracingConfig`: the build infos used for stack trace generation.
173
+ /// These are lazily parsed and it's important that they're passed as
174
+ /// Uint8 arrays for performance.
175
+ /// - `onTestSuiteCompletedCallback`: The progress callback will be called
176
+ /// with the results of each test suite as soon as it finished executing.
162
177
  #[allow(clippy::too_many_arguments)]
163
- #[napi(catch_unwind, ts_return_type = "Promise<void>")]
178
+ #[napi(catch_unwind, ts_return_type = "Promise<SolidityTestResult>")]
164
179
  pub fn run_solidity_tests(
165
180
  &self,
166
181
  env: Env,
@@ -291,7 +306,7 @@ impl EdrContext {
291
306
  .expect("Failed to join test runner factory thread"));
292
307
 
293
308
  let runtime_for_runner = runtime.clone();
294
- let () = try_or_reject_deferred!(runtime
309
+ let test_result = try_or_reject_deferred!(runtime
295
310
  .clone()
296
311
  .spawn_blocking(move || {
297
312
  test_runner.run_tests(
@@ -323,7 +338,7 @@ impl EdrContext {
323
338
  .await
324
339
  .expect("Failed to join test runner thread"));
325
340
 
326
- deferred.resolve(move |_env| Ok(()));
341
+ deferred.resolve(move |_env| Ok(SolidityTestResult::from(test_result)));
327
342
  });
328
343
 
329
344
  Ok(promise)
@@ -0,0 +1,92 @@
1
+ use std::collections::HashMap;
2
+
3
+ use napi::bindgen_prelude::BigInt;
4
+ use napi_derive::napi;
5
+
6
+ #[napi(object)]
7
+ pub struct GasReport {
8
+ pub contracts: HashMap<String, ContractGasReport>,
9
+ }
10
+
11
+ #[napi(object)]
12
+ pub struct ContractGasReport {
13
+ pub deployments: Vec<DeploymentGasReport>,
14
+ pub functions: HashMap<String, Vec<FunctionGasReport>>,
15
+ }
16
+
17
+ #[napi]
18
+ pub enum GasReportExecutionStatus {
19
+ Success,
20
+ Revert,
21
+ Halt,
22
+ }
23
+
24
+ #[napi(object)]
25
+ pub struct DeploymentGasReport {
26
+ pub gas: BigInt,
27
+ pub size: BigInt,
28
+ pub status: GasReportExecutionStatus,
29
+ }
30
+
31
+ #[napi(object)]
32
+ pub struct FunctionGasReport {
33
+ pub gas: BigInt,
34
+ pub status: GasReportExecutionStatus,
35
+ }
36
+
37
+ impl From<edr_gas_report::GasReport> for GasReport {
38
+ fn from(value: edr_gas_report::GasReport) -> Self {
39
+ Self {
40
+ contracts: value
41
+ .into_inner()
42
+ .into_iter()
43
+ .map(|(k, v)| (k, v.into()))
44
+ .collect(),
45
+ }
46
+ }
47
+ }
48
+
49
+ impl From<edr_gas_report::ContractGasReport> for ContractGasReport {
50
+ fn from(value: edr_gas_report::ContractGasReport) -> Self {
51
+ Self {
52
+ deployments: value.deployments.into_iter().map(Into::into).collect(),
53
+ functions: value
54
+ .functions
55
+ .into_iter()
56
+ .map(|(k, v)| {
57
+ let function_reports = v.into_iter().map(FunctionGasReport::from).collect();
58
+ (k, function_reports)
59
+ })
60
+ .collect(),
61
+ }
62
+ }
63
+ }
64
+
65
+ impl From<edr_gas_report::GasReportExecutionStatus> for GasReportExecutionStatus {
66
+ fn from(value: edr_gas_report::GasReportExecutionStatus) -> Self {
67
+ match value {
68
+ edr_gas_report::GasReportExecutionStatus::Success => Self::Success,
69
+ edr_gas_report::GasReportExecutionStatus::Revert => Self::Revert,
70
+ edr_gas_report::GasReportExecutionStatus::Halt => Self::Halt,
71
+ }
72
+ }
73
+ }
74
+
75
+ impl From<edr_gas_report::DeploymentGasReport> for DeploymentGasReport {
76
+ fn from(value: edr_gas_report::DeploymentGasReport) -> Self {
77
+ Self {
78
+ gas: BigInt::from(value.gas),
79
+ size: BigInt::from(value.size),
80
+ status: value.status.into(),
81
+ }
82
+ }
83
+ }
84
+
85
+ impl From<edr_gas_report::FunctionGasReport> for FunctionGasReport {
86
+ fn from(value: edr_gas_report::FunctionGasReport) -> Self {
87
+ Self {
88
+ gas: BigInt::from(value.gas),
89
+ status: value.status.into(),
90
+ }
91
+ }
92
+ }
package/src/lib.rs CHANGED
@@ -20,6 +20,7 @@ pub mod context;
20
20
  /// Types for decoding smart contract data.
21
21
  pub mod contract_decoder;
22
22
  mod debug_trace;
23
+ pub mod gas_report;
23
24
  /// Types and functions related to code coverage instrumentation.
24
25
  pub mod instrument;
25
26
  /// Types for EVM execution logs.
package/src/logger.rs CHANGED
@@ -1,7 +1,7 @@
1
1
  use std::sync::{mpsc::channel, Arc};
2
2
 
3
- use edr_eth::Bytes;
4
3
  use edr_napi_core::logger::LoggerError;
4
+ use edr_primitives::Bytes;
5
5
  use napi::{
6
6
  threadsafe_function::{
7
7
  ErrorStrategy, ThreadSafeCallContext, ThreadsafeFunction, ThreadsafeFunctionCallMode,
package/src/mock/time.rs CHANGED
@@ -1,11 +1,11 @@
1
1
  use std::sync::Arc;
2
2
 
3
- use edr_eth::B256;
4
3
  use edr_evm::spec::RuntimeSpec;
5
4
  use edr_evm_spec::ChainSpec;
6
5
  use edr_generic::GenericChainSpec;
7
6
  use edr_napi_core::logger::Logger;
8
- use edr_rpc_eth::RpcSpec;
7
+ use edr_primitives::B256;
8
+ use edr_rpc_spec::RpcSpec;
9
9
  use napi::{bindgen_prelude::BigInt, tokio::runtime, Env, JsObject};
10
10
  use napi_derive::napi;
11
11
 
package/src/precompile.rs CHANGED
@@ -1,5 +1,5 @@
1
- use edr_eth::Address;
2
1
  use edr_evm::precompile::{self, PrecompileFn, PrecompileWithAddress};
2
+ use edr_primitives::Address;
3
3
  use napi::bindgen_prelude::Uint8Array;
4
4
  use napi_derive::napi;
5
5
 
package/src/result.rs CHANGED
@@ -19,17 +19,17 @@ pub enum SuccessReason {
19
19
  SelfDestruct,
20
20
  }
21
21
 
22
- impl From<edr_eth::result::SuccessReason> for SuccessReason {
23
- fn from(eval: edr_eth::result::SuccessReason) -> Self {
22
+ impl From<edr_evm::result::SuccessReason> for SuccessReason {
23
+ fn from(eval: edr_evm::result::SuccessReason) -> Self {
24
24
  match eval {
25
- edr_eth::result::SuccessReason::Stop => Self::Stop,
26
- edr_eth::result::SuccessReason::Return => Self::Return,
27
- edr_eth::result::SuccessReason::SelfDestruct => Self::SelfDestruct,
25
+ edr_evm::result::SuccessReason::Stop => Self::Stop,
26
+ edr_evm::result::SuccessReason::Return => Self::Return,
27
+ edr_evm::result::SuccessReason::SelfDestruct => Self::SelfDestruct,
28
28
  }
29
29
  }
30
30
  }
31
31
 
32
- impl From<SuccessReason> for edr_eth::result::SuccessReason {
32
+ impl From<SuccessReason> for edr_evm::result::SuccessReason {
33
33
  fn from(value: SuccessReason) -> Self {
34
34
  match value {
35
35
  SuccessReason::Stop => Self::Stop,
@@ -157,7 +157,7 @@ impl From<&AfterMessage<EvmHaltReason>> for ExecutionResult {
157
157
  } = value;
158
158
 
159
159
  let result = match execution_result {
160
- edr_eth::result::ExecutionResult::Success {
160
+ edr_evm::result::ExecutionResult::Success {
161
161
  reason,
162
162
  gas_used,
163
163
  gas_refunded,
@@ -172,12 +172,12 @@ impl From<&AfterMessage<EvmHaltReason>> for ExecutionResult {
172
172
  gas_refunded: BigInt::from(*gas_refunded),
173
173
  logs,
174
174
  output: match output {
175
- edr_eth::result::Output::Call(return_value) => {
175
+ edr_evm::result::Output::Call(return_value) => {
176
176
  let return_value = Uint8Array::with_data_copied(return_value);
177
177
 
178
178
  Either::A(CallOutput { return_value })
179
179
  }
180
- edr_eth::result::Output::Create(return_value, address) => {
180
+ edr_evm::result::Output::Create(return_value, address) => {
181
181
  let return_value = Uint8Array::with_data_copied(return_value);
182
182
 
183
183
  Either::B(CreateOutput {
@@ -188,7 +188,7 @@ impl From<&AfterMessage<EvmHaltReason>> for ExecutionResult {
188
188
  },
189
189
  })
190
190
  }
191
- edr_eth::result::ExecutionResult::Revert { gas_used, output } => {
191
+ edr_evm::result::ExecutionResult::Revert { gas_used, output } => {
192
192
  let output = Uint8Array::with_data_copied(output);
193
193
 
194
194
  Either3::B(RevertResult {
@@ -196,7 +196,7 @@ impl From<&AfterMessage<EvmHaltReason>> for ExecutionResult {
196
196
  output,
197
197
  })
198
198
  }
199
- edr_eth::result::ExecutionResult::Halt { reason, gas_used } => Either3::C(HaltResult {
199
+ edr_evm::result::ExecutionResult::Halt { reason, gas_used } => Either3::C(HaltResult {
200
200
  reason: ExceptionalHalt::from(*reason),
201
201
  gas_used: BigInt::from(*gas_used),
202
202
  }),
package/src/serde.rs CHANGED
@@ -1,4 +1,4 @@
1
- use edr_eth::hex;
1
+ use edr_primitives::hex;
2
2
  use napi::bindgen_prelude::{BigInt, Uint8Array};
3
3
  use serde::Serializer;
4
4
 
@@ -1,7 +1,7 @@
1
1
  use std::{collections::HashMap, path::PathBuf};
2
2
 
3
3
  use derive_more::Debug;
4
- use edr_eth::hex;
4
+ use edr_primitives::hex;
5
5
  use edr_solidity_tests::{
6
6
  executors::invariant::InvariantConfig,
7
7
  fuzz::FuzzConfig,
@@ -155,6 +155,11 @@ pub struct SolidityTestRunnerConfigArgs {
155
155
  /// A regex pattern to filter tests. If provided, only test methods that
156
156
  /// match the pattern will be executed and reported as a test result.
157
157
  pub test_pattern: Option<String>,
158
+ /// Controls whether to generate a gas report after running the tests.
159
+ /// Enabling this also enables collection of all traces and EVM isolation
160
+ /// mode.
161
+ /// Defaults to false.
162
+ pub generate_gas_report: Option<bool>,
158
163
  }
159
164
 
160
165
  impl SolidityTestRunnerConfigArgs {
@@ -200,6 +205,7 @@ impl SolidityTestRunnerConfigArgs {
200
205
  include_traces,
201
206
  observability,
202
207
  test_pattern,
208
+ generate_gas_report,
203
209
  } = self;
204
210
 
205
211
  let test_pattern = TestFilterConfig {
@@ -305,6 +311,7 @@ impl SolidityTestRunnerConfigArgs {
305
311
  ),
306
312
  on_collected_coverage_fn,
307
313
  test_pattern,
314
+ generate_gas_report,
308
315
  };
309
316
 
310
317
  Ok(config)
@@ -1,10 +1,10 @@
1
1
  use std::{collections::BTreeMap, sync::Arc};
2
2
 
3
- use edr_eth::Bytes;
4
3
  use edr_napi_core::solidity::{
5
4
  config::{TestRunnerConfig, TracingConfigWithBuffers},
6
5
  SyncTestRunner, SyncTestRunnerFactory,
7
6
  };
7
+ use edr_primitives::Bytes;
8
8
  use edr_solidity::artifacts::ArtifactId;
9
9
  use edr_solidity_tests::{
10
10
  contracts::ContractsByArtifact, decode::RevertDecoder, evm_context::L1EvmBuilder,
@@ -1,11 +1,11 @@
1
1
  use std::{collections::BTreeMap, sync::Arc};
2
2
 
3
- use edr_eth::Bytes;
4
3
  use edr_napi_core::solidity::{
5
4
  config::{TestRunnerConfig, TracingConfigWithBuffers},
6
5
  SyncTestRunner, SyncTestRunnerFactory,
7
6
  };
8
7
  use edr_op::solidity_tests::OpEvmBuilder;
8
+ use edr_primitives::Bytes;
9
9
  use edr_solidity::artifacts::ArtifactId;
10
10
  use edr_solidity_tests::{
11
11
  contracts::ContractsByArtifact, decode::RevertDecoder, multi_runner::TestContract,
@@ -18,6 +18,7 @@ use napi_derive::napi;
18
18
 
19
19
  use crate::{
20
20
  cast::TryCast,
21
+ gas_report::GasReport,
21
22
  solidity_tests::{artifact::ArtifactId, config::IncludeTraces},
22
23
  trace::{solidity_stack_trace::SolidityStackTraceEntry, u256_to_bigint},
23
24
  };
@@ -717,3 +718,19 @@ impl From<traces::CallKind> for CallKind {
717
718
  }
718
719
  }
719
720
  }
721
+
722
+ /// The result of a Solidity test run.
723
+ #[napi(object)]
724
+ pub struct SolidityTestResult {
725
+ /// Gas report, if it was generated.
726
+ #[napi(readonly)]
727
+ pub gas_report: Option<GasReport>,
728
+ }
729
+
730
+ impl From<edr_solidity_tests::multi_runner::SolidityTestResult> for SolidityTestResult {
731
+ fn from(value: edr_solidity_tests::multi_runner::SolidityTestResult) -> Self {
732
+ Self {
733
+ gas_report: value.gas_report.map(GasReport::from),
734
+ }
735
+ }
736
+ }
@@ -9,7 +9,7 @@ pub mod test_results;
9
9
 
10
10
  use std::path::Path;
11
11
 
12
- use edr_eth::Bytes;
12
+ use edr_primitives::Bytes;
13
13
  use edr_solidity::linker::{LinkOutput, Linker};
14
14
  use edr_solidity_tests::{constants::LIBRARY_DEPLOYER, contracts::ContractsByArtifact};
15
15
  use foundry_compilers::artifacts::Libraries;
@@ -3,7 +3,7 @@
3
3
 
4
4
  use std::convert::Infallible;
5
5
 
6
- use edr_eth::{hex, U256};
6
+ use edr_primitives::{hex, U256};
7
7
  use napi::bindgen_prelude::{BigInt, Either25, FromNapiValue, ToNapiValue, Uint8Array, Undefined};
8
8
  use napi_derive::napi;
9
9
  use serde::{Serialize, Serializer};
@@ -848,7 +848,8 @@ const _: () = {
848
848
  assert_to_from_napi_value::<SolidityStackTraceEntry>();
849
849
  };
850
850
 
851
- /// Serializes a [`BigInt`] that represents an EVM value as a [`edr_eth::U256`].
851
+ /// Serializes a [`BigInt`] that represents an EVM value as a
852
+ /// [`edr_primitives::U256`].
852
853
  fn serialize_evm_value_bigint_using_u256<S>(bigint: &BigInt, s: S) -> Result<S::Ok, S::Error>
853
854
  where
854
855
  S: Serializer,
package/src/trace.rs CHANGED
@@ -7,9 +7,9 @@
7
7
 
8
8
  use std::sync::Arc;
9
9
 
10
- use edr_eth::bytecode::opcode::OpCode;
11
10
  use edr_evm::trace::BeforeMessage;
12
11
  use edr_evm_spec::EvmHaltReason;
12
+ use edr_primitives::bytecode::opcode::OpCode;
13
13
  use napi::bindgen_prelude::{BigInt, Either3, Uint8Array};
14
14
  use napi_derive::napi;
15
15
 
@@ -145,7 +145,7 @@ impl TracingStep {
145
145
  }
146
146
  }
147
147
 
148
- pub(crate) fn u256_to_bigint(v: &edr_eth::U256) -> BigInt {
148
+ pub(crate) fn u256_to_bigint(v: &edr_primitives::U256) -> BigInt {
149
149
  BigInt {
150
150
  sign_bit: false,
151
151
  words: v.into_limbs().to_vec(),
package/src/withdrawal.rs CHANGED
@@ -1,4 +1,4 @@
1
- use edr_eth::Address;
1
+ use edr_primitives::Address;
2
2
  use napi::bindgen_prelude::{BigInt, Uint8Array};
3
3
  use napi_derive::napi;
4
4
 
@@ -16,21 +16,21 @@ pub struct Withdrawal {
16
16
  pub amount: BigInt,
17
17
  }
18
18
 
19
- impl From<edr_eth::withdrawal::Withdrawal> for Withdrawal {
20
- fn from(withdrawal: edr_eth::withdrawal::Withdrawal) -> Self {
19
+ impl From<edr_block_header::Withdrawal> for Withdrawal {
20
+ fn from(withdrawal: edr_block_header::Withdrawal) -> Self {
21
21
  Self {
22
22
  index: BigInt::from(withdrawal.index),
23
23
  validator_index: BigInt::from(withdrawal.validator_index),
24
24
  address: Uint8Array::with_data_copied(withdrawal.address),
25
25
  amount: BigInt {
26
26
  sign_bit: false,
27
- words: withdrawal.amount.as_limbs().to_vec(),
27
+ words: vec![withdrawal.amount],
28
28
  },
29
29
  }
30
30
  }
31
31
  }
32
32
 
33
- impl TryFrom<Withdrawal> for edr_eth::withdrawal::Withdrawal {
33
+ impl TryFrom<Withdrawal> for edr_block_header::Withdrawal {
34
34
  type Error = napi::Error;
35
35
 
36
36
  fn try_from(value: Withdrawal) -> Result<Self, Self::Error> {