@nomicfoundation/edr 0.2.0-alpha.2 → 0.2.0-alpha.3

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 (38) hide show
  1. package/Cargo.toml +3 -0
  2. package/artifacts/bindings-aarch64-apple-darwin/edr.darwin-arm64.node +0 -0
  3. package/artifacts/bindings-aarch64-pc-windows-msvc/edr.win32-arm64-msvc.node +0 -0
  4. package/artifacts/bindings-aarch64-unknown-linux-gnu/edr.linux-arm64-gnu.node +0 -0
  5. package/artifacts/bindings-aarch64-unknown-linux-musl/edr.linux-arm64-musl.node +0 -0
  6. package/artifacts/bindings-i686-pc-windows-msvc/edr.win32-ia32-msvc.node +0 -0
  7. package/artifacts/bindings-x86_64-apple-darwin/edr.darwin-x64.node +0 -0
  8. package/artifacts/bindings-x86_64-pc-windows-msvc/edr.win32-x64-msvc.node +0 -0
  9. package/artifacts/bindings-x86_64-unknown-linux-gnu/edr.linux-x64-gnu.node +0 -0
  10. package/artifacts/bindings-x86_64-unknown-linux-musl/edr.linux-x64-musl.node +0 -0
  11. package/index.d.ts +20 -2
  12. package/index.js +43 -0
  13. package/npm/darwin-arm64/edr.darwin-arm64.node +0 -0
  14. package/npm/darwin-arm64/package.json +1 -1
  15. package/npm/darwin-x64/edr.darwin-x64.node +0 -0
  16. package/npm/darwin-x64/package.json +1 -1
  17. package/npm/linux-arm64-gnu/edr.linux-arm64-gnu.node +0 -0
  18. package/npm/linux-arm64-gnu/package.json +1 -1
  19. package/npm/linux-arm64-musl/edr.linux-arm64-musl.node +0 -0
  20. package/npm/linux-arm64-musl/package.json +1 -1
  21. package/npm/linux-x64-gnu/edr.linux-x64-gnu.node +0 -0
  22. package/npm/linux-x64-gnu/package.json +1 -1
  23. package/npm/linux-x64-musl/edr.linux-x64-musl.node +0 -0
  24. package/npm/linux-x64-musl/package.json +1 -1
  25. package/npm/win32-arm64-msvc/edr.win32-arm64-msvc.node +0 -0
  26. package/npm/win32-arm64-msvc/package.json +1 -1
  27. package/npm/win32-ia32-msvc/edr.win32-ia32-msvc.node +0 -0
  28. package/npm/win32-ia32-msvc/package.json +1 -1
  29. package/npm/win32-x64-msvc/edr.win32-x64-msvc.node +0 -0
  30. package/npm/win32-x64-msvc/package.json +1 -1
  31. package/package.json +11 -10
  32. package/src/block.rs +17 -6
  33. package/src/call_override.rs +102 -0
  34. package/src/cast.rs +17 -1
  35. package/src/lib.rs +4 -0
  36. package/src/provider.rs +38 -0
  37. package/src/scenarios.rs +59 -0
  38. package/src/withdrawal.rs +49 -0
package/Cargo.toml CHANGED
@@ -27,6 +27,8 @@ tracing-flame = { version = "0.2.0", default-features = false, features = ["smal
27
27
  tracing-subscriber = { version = "0.3.18", default-features = false, features = ["ansi", "env-filter", "fmt", "parking_lot", "smallvec", "std"] }
28
28
  parking_lot = { version = "0.12.1", default-features = false }
29
29
  lazy_static = { version = "1.4.0", features = [] }
30
+ rand = { version = "0.8.4", optional = true }
31
+ serde = { version = "1.0.189", features = ["derive"] }
30
32
 
31
33
  [target.x86_64-unknown-linux-gnu.dependencies]
32
34
  openssl-sys = { version = "0.9.93", features = ["vendored"] }
@@ -45,6 +47,7 @@ napi-build = "2.0.1"
45
47
 
46
48
  [features]
47
49
  tracing = ["edr_evm/tracing"]
50
+ scenarios = ["rand"]
48
51
 
49
52
  [profile.release]
50
53
  lto = true
package/index.d.ts CHANGED
@@ -33,8 +33,10 @@ export interface BlockOptions {
33
33
  nonce?: Buffer
34
34
  /** The block's base gas fee */
35
35
  baseFee?: bigint
36
- /** The block's withdrawals root */
37
- withdrawalsRoot?: Buffer
36
+ /** The block's withdrawals */
37
+ withdrawals?: Array<Withdrawal>
38
+ /** Blob gas was added by EIP-4844 and is ignored in older headers. */
39
+ blobGas?: BlobGas
38
40
  /**
39
41
  * The hash tree root of the parent beacon block for the given execution
40
42
  * block (EIP-4788).
@@ -56,6 +58,11 @@ export interface BlobGas {
56
58
  */
57
59
  excessGas: bigint
58
60
  }
61
+ /** The result of executing a call override. */
62
+ export interface CallOverrideResult {
63
+ result: Buffer
64
+ shouldRevert: boolean
65
+ }
59
66
  /** Identifier for the Ethereum spec. */
60
67
  export const enum SpecId {
61
68
  /** Frontier */
@@ -362,6 +369,16 @@ export interface TracingMessageResult {
362
369
  /** Execution result */
363
370
  readonly executionResult: ExecutionResult
364
371
  }
372
+ export interface Withdrawal {
373
+ /** The index of withdrawal */
374
+ index: bigint
375
+ /** The index of the validator that generated the withdrawal */
376
+ validatorIndex: bigint
377
+ /** The recipient address for withdrawal value */
378
+ address: Buffer
379
+ /** The value contained in withdrawal */
380
+ amount: bigint
381
+ }
365
382
  export class EdrContext {
366
383
  /**Creates a new [`EdrContext`] instance. Should only be called once! */
367
384
  constructor()
@@ -372,6 +389,7 @@ export class Provider {
372
389
  static withConfig(context: EdrContext, config: ProviderConfig, loggerConfig: LoggerConfig, subscriberCallback: (event: SubscriptionEvent) => void): Promise<Provider>
373
390
  /**Handles a JSON-RPC request and returns a JSON-RPC response. */
374
391
  handleRequest(jsonRequest: string): Promise<Response>
392
+ setCallOverrideCallback(callOverrideCallback: (contract_address: Buffer, data: Buffer) => Promise<CallOverrideResult | undefined>): void
375
393
  }
376
394
  export class Response {
377
395
  get json(): string
package/index.js CHANGED
@@ -237,6 +237,49 @@ switch (platform) {
237
237
  loadError = e
238
238
  }
239
239
  break
240
+ case 'riscv64':
241
+ if (isMusl()) {
242
+ localFileExisted = existsSync(
243
+ join(__dirname, 'edr.linux-riscv64-musl.node')
244
+ )
245
+ try {
246
+ if (localFileExisted) {
247
+ nativeBinding = require('./edr.linux-riscv64-musl.node')
248
+ } else {
249
+ nativeBinding = require('@nomicfoundation/edr-linux-riscv64-musl')
250
+ }
251
+ } catch (e) {
252
+ loadError = e
253
+ }
254
+ } else {
255
+ localFileExisted = existsSync(
256
+ join(__dirname, 'edr.linux-riscv64-gnu.node')
257
+ )
258
+ try {
259
+ if (localFileExisted) {
260
+ nativeBinding = require('./edr.linux-riscv64-gnu.node')
261
+ } else {
262
+ nativeBinding = require('@nomicfoundation/edr-linux-riscv64-gnu')
263
+ }
264
+ } catch (e) {
265
+ loadError = e
266
+ }
267
+ }
268
+ break
269
+ case 's390x':
270
+ localFileExisted = existsSync(
271
+ join(__dirname, 'edr.linux-s390x-gnu.node')
272
+ )
273
+ try {
274
+ if (localFileExisted) {
275
+ nativeBinding = require('./edr.linux-s390x-gnu.node')
276
+ } else {
277
+ nativeBinding = require('@nomicfoundation/edr-linux-s390x-gnu')
278
+ }
279
+ } catch (e) {
280
+ loadError = e
281
+ }
282
+ break
240
283
  default:
241
284
  throw new Error(`Unsupported architecture on Linux: ${arch}`)
242
285
  }
@@ -4,7 +4,7 @@
4
4
  "url": "https://github.com/NomicFoundation/hardhat.git",
5
5
  "type": "git"
6
6
  },
7
- "version": "0.2.0-alpha.2",
7
+ "version": "0.2.0-alpha.3",
8
8
  "os": [
9
9
  "darwin"
10
10
  ],
Binary file
@@ -4,7 +4,7 @@
4
4
  "url": "https://github.com/NomicFoundation/hardhat.git",
5
5
  "type": "git"
6
6
  },
7
- "version": "0.2.0-alpha.2",
7
+ "version": "0.2.0-alpha.3",
8
8
  "os": [
9
9
  "darwin"
10
10
  ],
@@ -4,7 +4,7 @@
4
4
  "url": "https://github.com/NomicFoundation/hardhat.git",
5
5
  "type": "git"
6
6
  },
7
- "version": "0.2.0-alpha.2",
7
+ "version": "0.2.0-alpha.3",
8
8
  "os": [
9
9
  "linux"
10
10
  ],
@@ -4,7 +4,7 @@
4
4
  "url": "https://github.com/NomicFoundation/hardhat.git",
5
5
  "type": "git"
6
6
  },
7
- "version": "0.2.0-alpha.2",
7
+ "version": "0.2.0-alpha.3",
8
8
  "os": [
9
9
  "linux"
10
10
  ],
@@ -4,7 +4,7 @@
4
4
  "url": "https://github.com/NomicFoundation/hardhat.git",
5
5
  "type": "git"
6
6
  },
7
- "version": "0.2.0-alpha.2",
7
+ "version": "0.2.0-alpha.3",
8
8
  "os": [
9
9
  "linux"
10
10
  ],
@@ -4,7 +4,7 @@
4
4
  "url": "https://github.com/NomicFoundation/hardhat.git",
5
5
  "type": "git"
6
6
  },
7
- "version": "0.2.0-alpha.2",
7
+ "version": "0.2.0-alpha.3",
8
8
  "os": [
9
9
  "linux"
10
10
  ],
@@ -4,7 +4,7 @@
4
4
  "url": "https://github.com/NomicFoundation/hardhat.git",
5
5
  "type": "git"
6
6
  },
7
- "version": "0.2.0-alpha.2",
7
+ "version": "0.2.0-alpha.3",
8
8
  "os": [
9
9
  "win32"
10
10
  ],
@@ -4,7 +4,7 @@
4
4
  "url": "https://github.com/NomicFoundation/hardhat.git",
5
5
  "type": "git"
6
6
  },
7
- "version": "0.2.0-alpha.2",
7
+ "version": "0.2.0-alpha.3",
8
8
  "os": [
9
9
  "win32"
10
10
  ],
@@ -4,7 +4,7 @@
4
4
  "url": "https://github.com/NomicFoundation/hardhat.git",
5
5
  "type": "git"
6
6
  },
7
- "version": "0.2.0-alpha.2",
7
+ "version": "0.2.0-alpha.3",
8
8
  "os": [
9
9
  "win32"
10
10
  ],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nomicfoundation/edr",
3
- "version": "0.2.0-alpha.2",
3
+ "version": "0.2.0-alpha.3",
4
4
  "main": "index.js",
5
5
  "types": "index.d.ts",
6
6
  "repository": {
@@ -36,21 +36,22 @@
36
36
  "node": ">= 18"
37
37
  },
38
38
  "optionalDependencies": {
39
- "@nomicfoundation/edr-win32-x64-msvc": "0.2.0-alpha.2",
40
- "@nomicfoundation/edr-darwin-x64": "0.2.0-alpha.2",
41
- "@nomicfoundation/edr-linux-x64-gnu": "0.2.0-alpha.2",
42
- "@nomicfoundation/edr-darwin-arm64": "0.2.0-alpha.2",
43
- "@nomicfoundation/edr-win32-arm64-msvc": "0.2.0-alpha.2",
44
- "@nomicfoundation/edr-linux-arm64-gnu": "0.2.0-alpha.2",
45
- "@nomicfoundation/edr-linux-arm64-musl": "0.2.0-alpha.2",
46
- "@nomicfoundation/edr-linux-x64-musl": "0.2.0-alpha.2",
47
- "@nomicfoundation/edr-win32-ia32-msvc": "0.2.0-alpha.2"
39
+ "@nomicfoundation/edr-win32-x64-msvc": "0.2.0-alpha.3",
40
+ "@nomicfoundation/edr-darwin-x64": "0.2.0-alpha.3",
41
+ "@nomicfoundation/edr-linux-x64-gnu": "0.2.0-alpha.3",
42
+ "@nomicfoundation/edr-darwin-arm64": "0.2.0-alpha.3",
43
+ "@nomicfoundation/edr-win32-arm64-msvc": "0.2.0-alpha.3",
44
+ "@nomicfoundation/edr-linux-arm64-gnu": "0.2.0-alpha.3",
45
+ "@nomicfoundation/edr-linux-arm64-musl": "0.2.0-alpha.3",
46
+ "@nomicfoundation/edr-linux-x64-musl": "0.2.0-alpha.3",
47
+ "@nomicfoundation/edr-win32-ia32-msvc": "0.2.0-alpha.3"
48
48
  },
49
49
  "scripts": {
50
50
  "artifacts": "napi artifacts",
51
51
  "build": "napi build --platform --release",
52
52
  "build:debug": "napi build --platform",
53
53
  "build:tracing": "napi build --release --features tracing",
54
+ "build:scenarios": "napi build --release --features scenarios",
54
55
  "universal": "napi universal",
55
56
  "version": "napi version",
56
57
  "pretest": "pnpm build",
package/src/block.rs CHANGED
@@ -2,7 +2,7 @@ use edr_eth::{Address, Bytes, B256, B64};
2
2
  use napi::bindgen_prelude::{BigInt, Buffer};
3
3
  use napi_derive::napi;
4
4
 
5
- use crate::cast::TryCast;
5
+ use crate::{cast::TryCast, withdrawal::Withdrawal};
6
6
 
7
7
  #[napi(object)]
8
8
  pub struct BlockOptions {
@@ -28,8 +28,10 @@ pub struct BlockOptions {
28
28
  pub nonce: Option<Buffer>,
29
29
  /// The block's base gas fee
30
30
  pub base_fee: Option<BigInt>,
31
- /// The block's withdrawals root
32
- pub withdrawals_root: Option<Buffer>,
31
+ /// The block's withdrawals
32
+ pub withdrawals: Option<Vec<Withdrawal>>,
33
+ /// Blob gas was added by EIP-4844 and is ignored in older headers.
34
+ pub blob_gas: Option<BlobGas>,
33
35
  /// The hash tree root of the parent beacon block for the given execution
34
36
  /// block (EIP-4788).
35
37
  pub parent_beacon_block_root: Option<Buffer>,
@@ -73,9 +75,18 @@ impl TryFrom<BlockOptions> for edr_eth::block::BlockOptions {
73
75
  base_fee: value
74
76
  .base_fee
75
77
  .map_or(Ok(None), |basefee| basefee.try_cast().map(Some))?,
76
- withdrawals_root: value
77
- .withdrawals_root
78
- .map(TryCast::<B256>::try_cast)
78
+ withdrawals: value
79
+ .withdrawals
80
+ .map(|withdrawals| {
81
+ withdrawals
82
+ .into_iter()
83
+ .map(edr_eth::withdrawal::Withdrawal::try_from)
84
+ .collect()
85
+ })
86
+ .transpose()?,
87
+ blob_gas: value
88
+ .blob_gas
89
+ .map(edr_eth::block::BlobGas::try_from)
79
90
  .transpose()?,
80
91
  parent_beacon_block_root: value
81
92
  .parent_beacon_block_root
@@ -0,0 +1,102 @@
1
+ use std::sync::mpsc::{channel, Sender};
2
+
3
+ use edr_eth::{Address, Bytes};
4
+ use napi::{bindgen_prelude::Buffer, Env, JsFunction, NapiRaw, Status};
5
+ use napi_derive::napi;
6
+
7
+ use crate::{
8
+ cast::TryCast,
9
+ sync::{await_promise, handle_error},
10
+ threadsafe_function::{ThreadSafeCallContext, ThreadsafeFunction, ThreadsafeFunctionCallMode},
11
+ };
12
+
13
+ /// The result of executing a call override.
14
+ #[napi(object)]
15
+ pub struct CallOverrideResult {
16
+ pub result: Buffer,
17
+ pub should_revert: bool,
18
+ }
19
+
20
+ impl TryCast<Option<edr_provider::CallOverrideResult>> for Option<CallOverrideResult> {
21
+ type Error = napi::Error;
22
+
23
+ fn try_cast(self) -> Result<Option<edr_provider::CallOverrideResult>, Self::Error> {
24
+ match self {
25
+ None => Ok(None),
26
+ Some(result) => Ok(Some(edr_provider::CallOverrideResult {
27
+ result: result.result.try_cast()?,
28
+ should_revert: result.should_revert,
29
+ })),
30
+ }
31
+ }
32
+ }
33
+
34
+ struct CallOverrideCall {
35
+ contract_address: Address,
36
+ data: Bytes,
37
+ sender: Sender<napi::Result<Option<edr_provider::CallOverrideResult>>>,
38
+ }
39
+
40
+ #[derive(Clone)]
41
+ pub struct CallOverrideCallback {
42
+ call_override_callback_fn: ThreadsafeFunction<CallOverrideCall>,
43
+ }
44
+
45
+ impl CallOverrideCallback {
46
+ pub fn new(env: &Env, call_override_callback: JsFunction) -> napi::Result<Self> {
47
+ let call_override_callback_fn = ThreadsafeFunction::create(
48
+ env.raw(),
49
+ // SAFETY: The callback is guaranteed to be valid for the lifetime of the inspector.
50
+ unsafe { call_override_callback.raw() },
51
+ 0,
52
+ |ctx: ThreadSafeCallContext<CallOverrideCall>| {
53
+ let address = ctx
54
+ .env
55
+ .create_buffer_with_data(ctx.value.contract_address.to_vec())?
56
+ .into_raw();
57
+
58
+ let data = ctx
59
+ .env
60
+ .create_buffer_with_data(ctx.value.data.to_vec())?
61
+ .into_raw();
62
+
63
+ let sender = ctx.value.sender.clone();
64
+ let promise = ctx.callback.call(None, &[address, data])?;
65
+ let result = await_promise::<
66
+ Option<CallOverrideResult>,
67
+ Option<edr_provider::CallOverrideResult>,
68
+ >(ctx.env, promise, ctx.value.sender);
69
+
70
+ handle_error(sender, result)
71
+ },
72
+ )?;
73
+
74
+ Ok(Self {
75
+ call_override_callback_fn,
76
+ })
77
+ }
78
+
79
+ pub fn call_override(
80
+ &self,
81
+ contract_address: Address,
82
+ data: Bytes,
83
+ ) -> Option<edr_provider::CallOverrideResult> {
84
+ let (sender, receiver) = channel();
85
+
86
+ let status = self.call_override_callback_fn.call(
87
+ CallOverrideCall {
88
+ contract_address,
89
+ data,
90
+ sender,
91
+ },
92
+ ThreadsafeFunctionCallMode::Blocking,
93
+ );
94
+
95
+ assert_eq!(status, Status::Ok, "Call override callback failed");
96
+
97
+ receiver
98
+ .recv()
99
+ .unwrap()
100
+ .expect("Failed call to call_override_callback")
101
+ }
102
+ }
package/src/cast.rs CHANGED
@@ -1,4 +1,4 @@
1
- use edr_eth::{Address, B256, B64, U256};
1
+ use edr_eth::{Address, Bytes, B256, B64, U256};
2
2
  use napi::{
3
3
  bindgen_prelude::{BigInt, Buffer},
4
4
  Status,
@@ -117,3 +117,19 @@ impl<T> TryCast<T> for T {
117
117
  Ok(self)
118
118
  }
119
119
  }
120
+
121
+ impl TryCast<Bytes> for Buffer {
122
+ type Error = napi::Error;
123
+
124
+ fn try_cast(self) -> Result<Bytes, Self::Error> {
125
+ Ok(Bytes::copy_from_slice(&self))
126
+ }
127
+ }
128
+
129
+ impl TryCast<Option<Bytes>> for Option<Buffer> {
130
+ type Error = napi::Error;
131
+
132
+ fn try_cast(self) -> Result<Option<Bytes>, Self::Error> {
133
+ Ok(self.map(|buffer| Bytes::copy_from_slice(&buffer)))
134
+ }
135
+ }
package/src/lib.rs CHANGED
@@ -4,6 +4,7 @@
4
4
 
5
5
  mod account;
6
6
  mod block;
7
+ mod call_override;
7
8
  mod cast;
8
9
  mod config;
9
10
  mod context;
@@ -12,7 +13,10 @@ mod log;
12
13
  mod logger;
13
14
  mod provider;
14
15
  mod result;
16
+ #[cfg(feature = "scenarios")]
17
+ mod scenarios;
15
18
  mod subscribe;
16
19
  mod sync;
17
20
  mod threadsafe_function;
18
21
  mod trace;
22
+ mod withdrawal;
package/src/provider.rs CHANGED
@@ -9,6 +9,7 @@ use napi_derive::napi;
9
9
 
10
10
  use self::config::ProviderConfig;
11
11
  use crate::{
12
+ call_override::CallOverrideCallback,
12
13
  context::EdrContext,
13
14
  logger::{Logger, LoggerConfig, LoggerError},
14
15
  subscribe::SubscriberCallback,
@@ -19,6 +20,8 @@ use crate::{
19
20
  #[napi]
20
21
  pub struct Provider {
21
22
  provider: Arc<edr_provider::Provider<LoggerError>>,
23
+ #[cfg(feature = "scenarios")]
24
+ scenario_file: Option<napi::tokio::sync::Mutex<napi::tokio::fs::File>>,
22
25
  }
23
26
 
24
27
  #[napi]
@@ -42,17 +45,27 @@ impl Provider {
42
45
 
43
46
  let (deferred, promise) = env.create_deferred()?;
44
47
  runtime.clone().spawn_blocking(move || {
48
+ #[cfg(feature = "scenarios")]
49
+ let scenario_file =
50
+ runtime::Handle::current().block_on(crate::scenarios::scenario_file(
51
+ &config,
52
+ edr_provider::Logger::is_enabled(&*logger),
53
+ ))?;
54
+
45
55
  let result = edr_provider::Provider::new(runtime, logger, subscriber_callback, config)
46
56
  .map_or_else(
47
57
  |error| Err(napi::Error::new(Status::GenericFailure, error.to_string())),
48
58
  |provider| {
49
59
  Ok(Provider {
50
60
  provider: Arc::new(provider),
61
+ #[cfg(feature = "scenarios")]
62
+ scenario_file,
51
63
  })
52
64
  },
53
65
  );
54
66
 
55
67
  deferred.resolve(|_env| result);
68
+ Ok::<_, napi::Error>(())
56
69
  });
57
70
 
58
71
  Ok(promise)
@@ -107,6 +120,11 @@ impl Provider {
107
120
  }
108
121
  };
109
122
 
123
+ #[cfg(feature = "scenarios")]
124
+ if let Some(scenario_file) = &self.scenario_file {
125
+ crate::scenarios::write_request(scenario_file, &request).await?;
126
+ }
127
+
110
128
  let mut response = runtime::Handle::current()
111
129
  .spawn_blocking(move || provider.handle_request(request))
112
130
  .await
@@ -149,6 +167,26 @@ impl Provider {
149
167
  traces: traces.into_iter().map(Arc::new).collect(),
150
168
  })
151
169
  }
170
+
171
+ #[napi(ts_return_type = "void")]
172
+ pub fn set_call_override_callback(
173
+ &self,
174
+ env: Env,
175
+ #[napi(
176
+ ts_arg_type = "(contract_address: Buffer, data: Buffer) => Promise<CallOverrideResult | undefined>"
177
+ )]
178
+ call_override_callback: JsFunction,
179
+ ) -> napi::Result<()> {
180
+ let provider = self.provider.clone();
181
+
182
+ let call_override_callback = CallOverrideCallback::new(&env, call_override_callback)?;
183
+ let call_override_callback =
184
+ Arc::new(move |address, data| call_override_callback.call_override(address, data));
185
+
186
+ provider.set_call_override_callback(Some(call_override_callback));
187
+
188
+ Ok(())
189
+ }
152
190
  }
153
191
 
154
192
  #[napi]
@@ -0,0 +1,59 @@
1
+ use std::time::{SystemTime, UNIX_EPOCH};
2
+
3
+ use edr_provider::ProviderRequest;
4
+ use napi::tokio::{fs::File, io::AsyncWriteExt, sync::Mutex};
5
+ use rand::{distributions::Alphanumeric, Rng};
6
+ use serde::Serialize;
7
+
8
+ const SCENARIO_FILE_PREFIX: &str = "EDR_SCENARIO_PREFIX";
9
+
10
+ #[derive(Clone, Debug, Serialize)]
11
+ struct ScenarioConfig<'a> {
12
+ provider_config: &'a edr_provider::ProviderConfig,
13
+ logger_enabled: bool,
14
+ }
15
+
16
+ pub(crate) async fn scenario_file(
17
+ provider_config: &edr_provider::ProviderConfig,
18
+ logger_enabled: bool,
19
+ ) -> Result<Option<Mutex<File>>, napi::Error> {
20
+ if let Ok(scenario_prefix) = std::env::var(SCENARIO_FILE_PREFIX) {
21
+ let timestamp = SystemTime::now()
22
+ .duration_since(UNIX_EPOCH)
23
+ .expect("Time went backwards")
24
+ .as_secs();
25
+ let suffix = rand::thread_rng()
26
+ .sample_iter(&Alphanumeric)
27
+ .take(4)
28
+ .map(char::from)
29
+ .collect::<String>();
30
+
31
+ let mut scenario_file =
32
+ File::create(format!("{scenario_prefix}_{timestamp}_{suffix}.json")).await?;
33
+
34
+ let config = ScenarioConfig {
35
+ provider_config,
36
+ logger_enabled,
37
+ };
38
+ let mut line = serde_json::to_string(&config)?;
39
+ line.push('\n');
40
+ scenario_file.write_all(line.as_bytes()).await?;
41
+
42
+ Ok(Some(Mutex::new(scenario_file)))
43
+ } else {
44
+ Ok(None)
45
+ }
46
+ }
47
+
48
+ pub(crate) async fn write_request(
49
+ scenario_file: &Mutex<File>,
50
+ request: &ProviderRequest,
51
+ ) -> napi::Result<()> {
52
+ let mut line = serde_json::to_string(request)?;
53
+ line.push('\n');
54
+ {
55
+ let mut scenario_file = scenario_file.lock().await;
56
+ scenario_file.write_all(line.as_bytes()).await?;
57
+ }
58
+ Ok(())
59
+ }
@@ -0,0 +1,49 @@
1
+ use edr_eth::Address;
2
+ use napi::bindgen_prelude::{BigInt, Buffer};
3
+ use napi_derive::napi;
4
+
5
+ use crate::cast::TryCast;
6
+
7
+ #[napi(object)]
8
+ pub struct Withdrawal {
9
+ /// The index of withdrawal
10
+ pub index: BigInt,
11
+ /// The index of the validator that generated the withdrawal
12
+ pub validator_index: BigInt,
13
+ /// The recipient address for withdrawal value
14
+ pub address: Buffer,
15
+ /// The value contained in withdrawal
16
+ pub amount: BigInt,
17
+ }
18
+
19
+ impl From<edr_eth::withdrawal::Withdrawal> for Withdrawal {
20
+ fn from(withdrawal: edr_eth::withdrawal::Withdrawal) -> Self {
21
+ Self {
22
+ index: BigInt::from(withdrawal.index),
23
+ validator_index: BigInt::from(withdrawal.validator_index),
24
+ address: Buffer::from(withdrawal.address.as_slice()),
25
+ amount: BigInt {
26
+ sign_bit: false,
27
+ words: withdrawal.amount.as_limbs().to_vec(),
28
+ },
29
+ }
30
+ }
31
+ }
32
+
33
+ impl TryFrom<Withdrawal> for edr_eth::withdrawal::Withdrawal {
34
+ type Error = napi::Error;
35
+
36
+ fn try_from(value: Withdrawal) -> Result<Self, Self::Error> {
37
+ let index: u64 = BigInt::try_cast(value.index)?;
38
+ let validator_index: u64 = BigInt::try_cast(value.validator_index)?;
39
+ let amount = BigInt::try_cast(value.amount)?;
40
+ let address = Address::from_slice(&value.address);
41
+
42
+ Ok(Self {
43
+ index,
44
+ validator_index,
45
+ address,
46
+ amount,
47
+ })
48
+ }
49
+ }