@nomicfoundation/edr 0.11.3 → 0.12.0-next.0

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.
Files changed (46) hide show
  1. package/Cargo.toml +61 -27
  2. package/LICENSE +5 -1
  3. package/index.d.ts +875 -137
  4. package/index.js +61 -3
  5. package/package.json +20 -16
  6. package/src/account.rs +109 -32
  7. package/src/block.rs +2 -103
  8. package/src/call_override.rs +7 -7
  9. package/src/cast.rs +47 -17
  10. package/src/chains/generic.rs +51 -0
  11. package/src/chains/l1.rs +262 -0
  12. package/src/chains/op.rs +425 -0
  13. package/src/chains.rs +7 -0
  14. package/src/config.rs +537 -67
  15. package/src/context.rs +374 -17
  16. package/src/debug_trace.rs +2 -2
  17. package/src/instrument.rs +109 -0
  18. package/src/lib.rs +38 -14
  19. package/src/log.rs +12 -14
  20. package/src/logger.rs +77 -1177
  21. package/src/mock.rs +68 -0
  22. package/src/precompile.rs +50 -0
  23. package/src/provider/factory.rs +22 -0
  24. package/src/provider/response.rs +73 -0
  25. package/src/provider.rs +64 -325
  26. package/src/result.rs +60 -69
  27. package/src/scenarios.rs +11 -17
  28. package/src/serde.rs +57 -0
  29. package/src/solidity_tests/artifact.rs +184 -0
  30. package/src/solidity_tests/config.rs +725 -0
  31. package/src/solidity_tests/factory.rs +22 -0
  32. package/src/solidity_tests/l1.rs +68 -0
  33. package/src/solidity_tests/op.rs +69 -0
  34. package/src/solidity_tests/runner.rs +51 -0
  35. package/src/solidity_tests/test_results.rs +668 -0
  36. package/src/solidity_tests.rs +56 -0
  37. package/src/subscription.rs +32 -0
  38. package/src/trace/debug.rs +1 -1
  39. package/src/trace/exit.rs +12 -13
  40. package/src/trace/library_utils.rs +1 -1
  41. package/src/trace/return_data.rs +11 -11
  42. package/src/trace/solidity_stack_trace.rs +11 -8
  43. package/src/trace.rs +37 -44
  44. package/src/withdrawal.rs +4 -4
  45. package/src/provider/config.rs +0 -291
  46. package/src/subscribe.rs +0 -63
package/src/chains.rs ADDED
@@ -0,0 +1,7 @@
1
+ /// Types for the generic L1 Ethereum implementation.
2
+ pub mod generic;
3
+ /// Types for L1 Ethereum implementation.
4
+ pub mod l1;
5
+ /// Types for OP implementation.
6
+ #[cfg(feature = "op")]
7
+ pub mod op;
package/src/config.rs CHANGED
@@ -1,73 +1,543 @@
1
+ use core::fmt::{Debug, Display};
2
+ use std::{
3
+ num::NonZeroU64,
4
+ path::PathBuf,
5
+ time::{Duration, SystemTime},
6
+ };
7
+
8
+ use edr_coverage::reporter::SyncOnCollectedCoverageCallback;
9
+ use edr_eth::{
10
+ signature::{secret_key_from_str, SecretKey},
11
+ Bytes, HashMap, HashSet,
12
+ };
13
+ use napi::{
14
+ bindgen_prelude::{BigInt, Promise, Reference, Uint8Array},
15
+ threadsafe_function::{
16
+ ErrorStrategy, ThreadSafeCallContext, ThreadsafeFunction, ThreadsafeFunctionCallMode,
17
+ },
18
+ tokio::runtime,
19
+ Either, JsFunction, JsString, JsStringUtf8,
20
+ };
1
21
  use napi_derive::napi;
2
22
 
3
- /// Identifier for the Ethereum spec.
4
- #[napi]
5
- pub enum SpecId {
6
- /// Frontier
7
- Frontier = 0,
8
- /// Frontier Thawing
9
- FrontierThawing = 1,
10
- /// Homestead
11
- Homestead = 2,
12
- /// DAO Fork
13
- DaoFork = 3,
14
- /// Tangerine
15
- Tangerine = 4,
16
- /// Spurious Dragon
17
- SpuriousDragon = 5,
18
- /// Byzantium
19
- Byzantium = 6,
20
- /// Constantinople
21
- Constantinople = 7,
22
- /// Petersburg
23
- Petersburg = 8,
24
- /// Istanbul
25
- Istanbul = 9,
26
- /// Muir Glacier
27
- MuirGlacier = 10,
28
- /// Berlin
29
- Berlin = 11,
30
- /// London
31
- London = 12,
32
- /// Arrow Glacier
33
- ArrowGlacier = 13,
34
- /// Gray Glacier
35
- GrayGlacier = 14,
36
- /// Merge
37
- Merge = 15,
38
- /// Shanghai
39
- Shanghai = 16,
40
- /// Cancun
41
- Cancun = 17,
42
- /// Prague
43
- Prague = 18,
44
- /// Latest
45
- Latest = 19,
46
- }
47
-
48
- impl From<SpecId> for edr_evm::SpecId {
49
- fn from(value: SpecId) -> Self {
23
+ use crate::{account::AccountOverride, block::BlobGas, cast::TryCast, precompile::Precompile};
24
+
25
+ /// Specification of a chain with possible overrides.
26
+ #[napi(object)]
27
+ pub struct ChainOverride {
28
+ /// The chain ID
29
+ pub chain_id: BigInt,
30
+ /// The chain's name
31
+ pub name: String,
32
+ /// If present, overrides for the chain's supported hardforks
33
+ pub hardfork_activation_overrides: Option<Vec<HardforkActivation>>,
34
+ }
35
+
36
+ /// Configuration for a code coverage reporter.
37
+ #[napi(object)]
38
+ pub struct CodeCoverageConfig {
39
+ /// The callback to be called when coverage has been collected.
40
+ ///
41
+ /// The callback receives an array of unique coverage hit markers (i.e. no
42
+ /// repetition) per transaction.
43
+ ///
44
+ /// Exceptions thrown in the callback will be propagated to the original
45
+ /// caller.
46
+ #[napi(ts_type = "(coverageHits: Uint8Array[]) => Promise<void>")]
47
+ pub on_collected_coverage_callback: JsFunction,
48
+ }
49
+
50
+ /// Configuration for forking a blockchain
51
+ #[napi(object)]
52
+ pub struct ForkConfig {
53
+ /// The block number to fork from. If not provided, the latest safe block is
54
+ /// used.
55
+ pub block_number: Option<BigInt>,
56
+ /// The directory to cache remote JSON-RPC responses
57
+ pub cache_dir: Option<String>,
58
+ /// Overrides for the configuration of chains.
59
+ pub chain_overrides: Option<Vec<ChainOverride>>,
60
+ /// The HTTP headers to use when making requests to the JSON-RPC endpoint
61
+ pub http_headers: Option<Vec<HttpHeader>>,
62
+ /// The URL of the JSON-RPC endpoint to fork from
63
+ pub url: String,
64
+ }
65
+
66
+ #[napi(object)]
67
+ pub struct HttpHeader {
68
+ pub name: String,
69
+ pub value: String,
70
+ }
71
+
72
+ /// Configuration for a hardfork activation
73
+ #[napi(object)]
74
+ pub struct HardforkActivation {
75
+ /// The condition for the hardfork activation
76
+ pub condition: Either<HardforkActivationByBlockNumber, HardforkActivationByTimestamp>,
77
+ /// The activated hardfork
78
+ pub hardfork: String,
79
+ }
80
+
81
+ #[napi(object)]
82
+ pub struct HardforkActivationByBlockNumber {
83
+ /// The block number at which the hardfork is activated
84
+ pub block_number: BigInt,
85
+ }
86
+
87
+ #[napi(object)]
88
+ pub struct HardforkActivationByTimestamp {
89
+ /// The timestamp at which the hardfork is activated
90
+ pub timestamp: BigInt,
91
+ }
92
+
93
+ #[napi(string_enum)]
94
+ #[doc = "The type of ordering to use when selecting blocks to mine."]
95
+ pub enum MineOrdering {
96
+ #[doc = "Insertion order"]
97
+ Fifo,
98
+ #[doc = "Effective miner fee"]
99
+ Priority,
100
+ }
101
+
102
+ /// Configuration for the provider's mempool.
103
+ #[napi(object)]
104
+ pub struct MemPoolConfig {
105
+ pub order: MineOrdering,
106
+ }
107
+
108
+ #[napi(object)]
109
+ pub struct IntervalRange {
110
+ pub min: BigInt,
111
+ pub max: BigInt,
112
+ }
113
+
114
+ /// Configuration for the provider's miner.
115
+ #[napi(object)]
116
+ pub struct MiningConfig {
117
+ pub auto_mine: bool,
118
+ pub interval: Option<Either<BigInt, IntervalRange>>,
119
+ pub mem_pool: MemPoolConfig,
120
+ }
121
+
122
+ /// Configuration for runtime observability.
123
+ #[napi(object)]
124
+ pub struct ObservabilityConfig {
125
+ /// If present, configures runtime observability to collect code coverage.
126
+ pub code_coverage: Option<CodeCoverageConfig>,
127
+ }
128
+
129
+ /// Configuration for a provider
130
+ #[napi(object)]
131
+ pub struct ProviderConfig {
132
+ /// Whether to allow blocks with the same timestamp
133
+ pub allow_blocks_with_same_timestamp: bool,
134
+ /// Whether to allow unlimited contract size
135
+ pub allow_unlimited_contract_size: bool,
136
+ /// Whether to return an `Err` when `eth_call` fails
137
+ pub bail_on_call_failure: bool,
138
+ /// Whether to return an `Err` when a `eth_sendTransaction` fails
139
+ pub bail_on_transaction_failure: bool,
140
+ /// The gas limit of each block
141
+ pub block_gas_limit: BigInt,
142
+ /// The chain ID of the blockchain
143
+ pub chain_id: BigInt,
144
+ /// The address of the coinbase
145
+ pub coinbase: Uint8Array,
146
+ /// The configuration for forking a blockchain. If not provided, a local
147
+ /// blockchain will be created
148
+ pub fork: Option<ForkConfig>,
149
+ /// The genesis state of the blockchain
150
+ pub genesis_state: Vec<AccountOverride>,
151
+ /// The hardfork of the blockchain
152
+ pub hardfork: String,
153
+ /// The initial base fee per gas of the blockchain. Required for EIP-1559
154
+ /// transactions and later
155
+ pub initial_base_fee_per_gas: Option<BigInt>,
156
+ /// The initial blob gas of the blockchain. Required for EIP-4844
157
+ pub initial_blob_gas: Option<BlobGas>,
158
+ /// The initial date of the blockchain, in seconds since the Unix epoch
159
+ pub initial_date: Option<BigInt>,
160
+ /// The initial parent beacon block root of the blockchain. Required for
161
+ /// EIP-4788
162
+ pub initial_parent_beacon_block_root: Option<Uint8Array>,
163
+ /// The minimum gas price of the next block.
164
+ pub min_gas_price: BigInt,
165
+ /// The configuration for the miner
166
+ pub mining: MiningConfig,
167
+ /// The network ID of the blockchain
168
+ pub network_id: BigInt,
169
+ /// The configuration for the provider's observability
170
+ pub observability: ObservabilityConfig,
171
+ // Using JsString here as it doesn't have `Debug`, `Display` and `Serialize` implementation
172
+ // which prevents accidentally leaking the secret keys to error messages and logs.
173
+ /// Secret keys of owned accounts
174
+ pub owned_accounts: Vec<JsString>,
175
+ /// Overrides for precompiles
176
+ pub precompile_overrides: Vec<Reference<Precompile>>,
177
+ }
178
+
179
+ impl TryFrom<ForkConfig> for edr_provider::ForkConfig<String> {
180
+ type Error = napi::Error;
181
+
182
+ fn try_from(value: ForkConfig) -> Result<Self, Self::Error> {
183
+ let block_number: Option<u64> = value.block_number.map(TryCast::try_cast).transpose()?;
184
+
185
+ let cache_dir = PathBuf::from(
186
+ value
187
+ .cache_dir
188
+ .unwrap_or(edr_defaults::CACHE_DIR.to_owned()),
189
+ );
190
+
191
+ let chain_overrides = value
192
+ .chain_overrides
193
+ .map(|chain_overrides| {
194
+ chain_overrides
195
+ .into_iter()
196
+ .map(
197
+ |ChainOverride {
198
+ chain_id,
199
+ name,
200
+ hardfork_activation_overrides,
201
+ }| {
202
+ let hardfork_activation_overrides =
203
+ hardfork_activation_overrides
204
+ .map(|hardfork_activations| {
205
+ hardfork_activations
206
+ .into_iter()
207
+ .map(
208
+ |HardforkActivation {
209
+ condition,
210
+ hardfork,
211
+ }| {
212
+ let condition = match condition {
213
+ Either::A(HardforkActivationByBlockNumber {
214
+ block_number,
215
+ }) => edr_evm::hardfork::ForkCondition::Block(
216
+ block_number.try_cast()?,
217
+ ),
218
+ Either::B(HardforkActivationByTimestamp {
219
+ timestamp,
220
+ }) => edr_evm::hardfork::ForkCondition::Timestamp(
221
+ timestamp.try_cast()?,
222
+ ),
223
+ };
224
+
225
+ Ok(edr_evm::hardfork::Activation {
226
+ condition,
227
+ hardfork,
228
+ })
229
+ },
230
+ )
231
+ .collect::<napi::Result<Vec<_>>>()
232
+ .map(edr_evm::hardfork::Activations::new)
233
+ })
234
+ .transpose()?;
235
+
236
+ let chain_config = edr_evm::hardfork::ChainOverride {
237
+ name,
238
+ hardfork_activation_overrides,
239
+ };
240
+
241
+ let chain_id = chain_id.try_cast()?;
242
+ Ok((chain_id, chain_config))
243
+ },
244
+ )
245
+ .collect::<napi::Result<_>>()
246
+ })
247
+ .transpose()?;
248
+
249
+ let http_headers = value.http_headers.map(|http_headers| {
250
+ http_headers
251
+ .into_iter()
252
+ .map(|HttpHeader { name, value }| (name, value))
253
+ .collect()
254
+ });
255
+
256
+ Ok(Self {
257
+ block_number,
258
+ cache_dir,
259
+ chain_overrides: chain_overrides.unwrap_or_default(),
260
+ http_headers,
261
+ url: value.url,
262
+ })
263
+ }
264
+ }
265
+
266
+ impl From<MemPoolConfig> for edr_provider::MemPoolConfig {
267
+ fn from(value: MemPoolConfig) -> Self {
268
+ Self {
269
+ order: value.order.into(),
270
+ }
271
+ }
272
+ }
273
+
274
+ impl From<MineOrdering> for edr_evm::MineOrdering {
275
+ fn from(value: MineOrdering) -> Self {
50
276
  match value {
51
- SpecId::Frontier => edr_evm::SpecId::FRONTIER,
52
- SpecId::FrontierThawing => edr_evm::SpecId::FRONTIER_THAWING,
53
- SpecId::Homestead => edr_evm::SpecId::HOMESTEAD,
54
- SpecId::DaoFork => edr_evm::SpecId::DAO_FORK,
55
- SpecId::Tangerine => edr_evm::SpecId::TANGERINE,
56
- SpecId::SpuriousDragon => edr_evm::SpecId::SPURIOUS_DRAGON,
57
- SpecId::Byzantium => edr_evm::SpecId::BYZANTIUM,
58
- SpecId::Constantinople => edr_evm::SpecId::CONSTANTINOPLE,
59
- SpecId::Petersburg => edr_evm::SpecId::PETERSBURG,
60
- SpecId::Istanbul => edr_evm::SpecId::ISTANBUL,
61
- SpecId::MuirGlacier => edr_evm::SpecId::MUIR_GLACIER,
62
- SpecId::Berlin => edr_evm::SpecId::BERLIN,
63
- SpecId::London => edr_evm::SpecId::LONDON,
64
- SpecId::ArrowGlacier => edr_evm::SpecId::ARROW_GLACIER,
65
- SpecId::GrayGlacier => edr_evm::SpecId::GRAY_GLACIER,
66
- SpecId::Merge => edr_evm::SpecId::MERGE,
67
- SpecId::Shanghai => edr_evm::SpecId::SHANGHAI,
68
- SpecId::Cancun => edr_evm::SpecId::CANCUN,
69
- SpecId::Prague => edr_evm::SpecId::PRAGUE,
70
- SpecId::Latest => edr_evm::SpecId::LATEST,
277
+ MineOrdering::Fifo => Self::Fifo,
278
+ MineOrdering::Priority => Self::Priority,
279
+ }
280
+ }
281
+ }
282
+
283
+ impl TryFrom<MiningConfig> for edr_provider::MiningConfig {
284
+ type Error = napi::Error;
285
+
286
+ fn try_from(value: MiningConfig) -> Result<Self, Self::Error> {
287
+ let mem_pool = value.mem_pool.into();
288
+
289
+ let interval = value
290
+ .interval
291
+ .map(|interval| {
292
+ let interval = match interval {
293
+ Either::A(interval) => {
294
+ let interval = interval.try_cast()?;
295
+ let interval = NonZeroU64::new(interval).ok_or_else(|| {
296
+ napi::Error::new(
297
+ napi::Status::GenericFailure,
298
+ "Interval must be greater than 0",
299
+ )
300
+ })?;
301
+
302
+ edr_provider::IntervalConfig::Fixed(interval)
303
+ }
304
+ Either::B(IntervalRange { min, max }) => edr_provider::IntervalConfig::Range {
305
+ min: min.try_cast()?,
306
+ max: max.try_cast()?,
307
+ },
308
+ };
309
+
310
+ napi::Result::Ok(interval)
311
+ })
312
+ .transpose()?;
313
+
314
+ Ok(Self {
315
+ auto_mine: value.auto_mine,
316
+ interval,
317
+ mem_pool,
318
+ })
319
+ }
320
+ }
321
+
322
+ impl ObservabilityConfig {
323
+ /// Resolves the instance, converting it to a
324
+ /// [`edr_provider::observability::Config`].
325
+ pub fn resolve(
326
+ self,
327
+ env: &napi::Env,
328
+ runtime: runtime::Handle,
329
+ ) -> napi::Result<edr_provider::observability::Config> {
330
+ let on_collected_coverage_fn = self
331
+ .code_coverage
332
+ .map(
333
+ |code_coverage| -> napi::Result<Box<dyn SyncOnCollectedCoverageCallback>> {
334
+ let mut on_collected_coverage_callback: ThreadsafeFunction<
335
+ _,
336
+ ErrorStrategy::Fatal,
337
+ > = code_coverage
338
+ .on_collected_coverage_callback
339
+ .create_threadsafe_function(
340
+ 0,
341
+ |ctx: ThreadSafeCallContext<HashSet<Bytes>>| {
342
+ let hits = ctx
343
+ .env
344
+ .create_array_with_length(ctx.value.len())
345
+ .and_then(|mut hits| {
346
+ for (idx, hit) in ctx.value.into_iter().enumerate() {
347
+ ctx.env
348
+ .create_buffer_with_data(hit.to_vec())
349
+ .and_then(|hit| {
350
+ let idx = u32::try_from(idx).unwrap_or_else(|_| panic!("Number of hits should not exceed '{}'",
351
+ u32::MAX));
352
+
353
+ hits.set_element(idx, hit.into_raw())
354
+ })?;
355
+ }
356
+ Ok(hits)
357
+ })?;
358
+
359
+ Ok(vec![hits])
360
+ },
361
+ )?;
362
+
363
+ // Maintain a weak reference to the function to avoid blocking the event loop
364
+ // from exiting.
365
+ on_collected_coverage_callback.unref(env)?;
366
+
367
+ let on_collected_coverage_fn: Box<dyn SyncOnCollectedCoverageCallback> =
368
+ Box::new(move |hits| {
369
+ let runtime = runtime.clone();
370
+
371
+ let (sender, receiver) = std::sync::mpsc::channel();
372
+
373
+ let status = on_collected_coverage_callback
374
+ .call_with_return_value(hits, ThreadsafeFunctionCallMode::Blocking, move |result: Promise<()>| {
375
+ // We spawn a background task to handle the async callback
376
+ runtime.spawn(async move {
377
+ let result = result.await;
378
+ sender.send(result).map_err(|_error| {
379
+ napi::Error::new(
380
+ napi::Status::GenericFailure,
381
+ "Failed to send result from on_collected_coverage_callback",
382
+ )
383
+ })
384
+ });
385
+ Ok(())
386
+ });
387
+
388
+ let () = receiver.recv().expect("Receive can only fail if the channel is closed")?;
389
+
390
+ assert_eq!(status, napi::Status::Ok);
391
+
392
+ Ok(())
393
+ });
394
+
395
+ Ok(on_collected_coverage_fn)
396
+ },
397
+ )
398
+ .transpose()?;
399
+
400
+ Ok(edr_provider::observability::Config {
401
+ on_collected_coverage_fn,
402
+ ..edr_provider::observability::Config::default()
403
+ })
404
+ }
405
+ }
406
+
407
+ impl ProviderConfig {
408
+ /// Resolves the instance to a [`edr_napi_core::provider::Config`].
409
+ pub fn resolve(
410
+ self,
411
+ env: &napi::Env,
412
+ runtime: runtime::Handle,
413
+ ) -> napi::Result<edr_napi_core::provider::Config> {
414
+ let owned_accounts = self
415
+ .owned_accounts
416
+ .into_iter()
417
+ .map(|secret_key| {
418
+ // This is the only place in production code where it's allowed to use
419
+ // `DangerousSecretKeyStr`.
420
+ #[allow(deprecated)]
421
+ use edr_eth::signature::DangerousSecretKeyStr;
422
+
423
+ static_assertions::assert_not_impl_all!(JsString: Debug, Display, serde::Serialize);
424
+ static_assertions::assert_not_impl_all!(JsStringUtf8: Debug, Display, serde::Serialize);
425
+ // `SecretKey` has `Debug` implementation, but it's opaque (only shows the
426
+ // type name)
427
+ static_assertions::assert_not_impl_any!(SecretKey: Display, serde::Serialize);
428
+
429
+ let secret_key = secret_key.into_utf8()?;
430
+ // This is the only place in production code where it's allowed to use
431
+ // `DangerousSecretKeyStr`.
432
+ #[allow(deprecated)]
433
+ let secret_key_str = DangerousSecretKeyStr(secret_key.as_str()?);
434
+ let secret_key: SecretKey = secret_key_from_str(secret_key_str)
435
+ .map_err(|error| napi::Error::new(napi::Status::InvalidArg, error))?;
436
+
437
+ Ok(secret_key)
438
+ })
439
+ .collect::<napi::Result<Vec<_>>>()?;
440
+
441
+ let block_gas_limit =
442
+ NonZeroU64::new(self.block_gas_limit.try_cast()?).ok_or_else(|| {
443
+ napi::Error::new(
444
+ napi::Status::GenericFailure,
445
+ "Block gas limit must be greater than 0",
446
+ )
447
+ })?;
448
+
449
+ let genesis_state = self
450
+ .genesis_state
451
+ .into_iter()
452
+ .map(TryInto::try_into)
453
+ .collect::<napi::Result<HashMap<edr_eth::Address, edr_provider::AccountOverride>>>()?;
454
+
455
+ let precompile_overrides = self
456
+ .precompile_overrides
457
+ .into_iter()
458
+ .map(|precompile| precompile.to_tuple())
459
+ .collect();
460
+
461
+ Ok(edr_napi_core::provider::Config {
462
+ allow_blocks_with_same_timestamp: self.allow_blocks_with_same_timestamp,
463
+ allow_unlimited_contract_size: self.allow_unlimited_contract_size,
464
+ bail_on_call_failure: self.bail_on_call_failure,
465
+ bail_on_transaction_failure: self.bail_on_transaction_failure,
466
+ block_gas_limit,
467
+ chain_id: self.chain_id.try_cast()?,
468
+ coinbase: self.coinbase.try_cast()?,
469
+ fork: self.fork.map(TryInto::try_into).transpose()?,
470
+ genesis_state,
471
+ hardfork: self.hardfork,
472
+ initial_base_fee_per_gas: self
473
+ .initial_base_fee_per_gas
474
+ .map(TryCast::try_cast)
475
+ .transpose()?,
476
+ initial_blob_gas: self.initial_blob_gas.map(TryInto::try_into).transpose()?,
477
+ initial_date: self
478
+ .initial_date
479
+ .map(|date| {
480
+ let elapsed_since_epoch = Duration::from_secs(date.try_cast()?);
481
+ napi::Result::Ok(SystemTime::UNIX_EPOCH + elapsed_since_epoch)
482
+ })
483
+ .transpose()?,
484
+ initial_parent_beacon_block_root: self
485
+ .initial_parent_beacon_block_root
486
+ .map(TryCast::try_cast)
487
+ .transpose()?,
488
+ mining: self.mining.try_into()?,
489
+ min_gas_price: self.min_gas_price.try_cast()?,
490
+ network_id: self.network_id.try_cast()?,
491
+ observability: self.observability.resolve(env, runtime)?,
492
+ owned_accounts,
493
+ precompile_overrides,
494
+ })
495
+ }
496
+ }
497
+
498
+ /// Tracing config for Solidity stack trace generation.
499
+ #[napi(object)]
500
+ pub struct TracingConfigWithBuffers {
501
+ /// Build information to use for decoding contracts. Either a Hardhat v2
502
+ /// build info file that contains both input and output or a Hardhat v3
503
+ /// build info file that doesn't contain output and a separate output file.
504
+ pub build_infos: Option<Either<Vec<Uint8Array>, Vec<BuildInfoAndOutput>>>,
505
+ /// Whether to ignore contracts whose name starts with "Ignored".
506
+ pub ignore_contracts: Option<bool>,
507
+ }
508
+
509
+ impl From<TracingConfigWithBuffers> for edr_napi_core::solidity::config::TracingConfigWithBuffers {
510
+ fn from(value: TracingConfigWithBuffers) -> Self {
511
+ edr_napi_core::solidity::config::TracingConfigWithBuffers {
512
+ build_infos: value.build_infos.map(|infos| match infos {
513
+ Either::A(with_output) => Either::A(with_output),
514
+ Either::B(separate_output) => Either::B(
515
+ separate_output
516
+ .into_iter()
517
+ .map(edr_napi_core::solidity::config::BuildInfoAndOutput::from)
518
+ .collect(),
519
+ ),
520
+ }),
521
+ ignore_contracts: value.ignore_contracts,
522
+ }
523
+ }
524
+ }
525
+
526
+ /// Hardhat V3 build info where the compiler output is not part of the build
527
+ /// info file.
528
+ #[napi(object)]
529
+ pub struct BuildInfoAndOutput {
530
+ /// The build info input file
531
+ pub build_info: Uint8Array,
532
+ /// The build info output file
533
+ pub output: Uint8Array,
534
+ }
535
+
536
+ impl From<BuildInfoAndOutput> for edr_napi_core::solidity::config::BuildInfoAndOutput {
537
+ fn from(value: BuildInfoAndOutput) -> Self {
538
+ Self {
539
+ build_info: value.build_info,
540
+ output: value.output,
71
541
  }
72
542
  }
73
543
  }