@nomicfoundation/edr 0.10.0 → 0.12.0-alpha.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.
- package/Cargo.toml +26 -23
- package/index.d.ts +145 -76
- package/index.js +31 -3
- package/package.json +16 -14
- package/src/account.rs +41 -11
- package/src/block.rs +1 -1
- package/src/call_override.rs +1 -1
- package/src/cast.rs +54 -2
- package/src/chains/generic.rs +51 -0
- package/src/chains/l1.rs +260 -0
- package/src/chains/op.rs +368 -0
- package/src/chains.rs +7 -0
- package/src/config.rs +393 -67
- package/src/context.rs +135 -17
- package/src/lib.rs +28 -14
- package/src/log.rs +2 -2
- package/src/logger.rs +54 -1152
- package/src/provider/factory.rs +22 -0
- package/src/provider/response.rs +70 -0
- package/src/provider.rs +55 -322
- package/src/result.rs +44 -44
- package/src/scenarios.rs +12 -18
- package/src/subscription.rs +32 -0
- package/src/trace/exit.rs +8 -9
- package/src/trace/return_data.rs +1 -1
- package/src/trace/solidity_stack_trace.rs +5 -4
- package/src/trace.rs +9 -7
- package/src/withdrawal.rs +1 -1
- package/src/provider/config.rs +0 -291
- package/src/subscribe.rs +0 -63
package/src/config.rs
CHANGED
|
@@ -1,73 +1,399 @@
|
|
|
1
|
+
use std::{
|
|
2
|
+
num::NonZeroU64,
|
|
3
|
+
time::{Duration, SystemTime},
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
use edr_eth::KECCAK_EMPTY;
|
|
7
|
+
use napi::{
|
|
8
|
+
Either,
|
|
9
|
+
bindgen_prelude::{BigInt, Buffer, Uint8Array},
|
|
10
|
+
};
|
|
1
11
|
use napi_derive::napi;
|
|
2
12
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
///
|
|
13
|
-
|
|
14
|
-
///
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
///
|
|
25
|
-
|
|
26
|
-
///
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
13
|
+
use crate::{
|
|
14
|
+
account::{Account, OwnedAccount, StorageSlot},
|
|
15
|
+
block::BlobGas,
|
|
16
|
+
cast::TryCast,
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/// Configuration for a chain
|
|
20
|
+
#[napi(object)]
|
|
21
|
+
pub struct ChainConfig {
|
|
22
|
+
/// The chain ID
|
|
23
|
+
pub chain_id: BigInt,
|
|
24
|
+
/// The chain's supported hardforks
|
|
25
|
+
pub hardforks: Vec<HardforkActivation>,
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/// Configuration for forking a blockchain
|
|
29
|
+
#[napi(object)]
|
|
30
|
+
pub struct ForkConfig {
|
|
31
|
+
/// The URL of the JSON-RPC endpoint to fork from
|
|
32
|
+
pub json_rpc_url: String,
|
|
33
|
+
/// The block number to fork from. If not provided, the latest safe block is
|
|
34
|
+
/// used.
|
|
35
|
+
pub block_number: Option<BigInt>,
|
|
36
|
+
/// The HTTP headers to use when making requests to the JSON-RPC endpoint
|
|
37
|
+
pub http_headers: Option<Vec<HttpHeader>>,
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
#[napi(object)]
|
|
41
|
+
pub struct HttpHeader {
|
|
42
|
+
pub name: String,
|
|
43
|
+
pub value: String,
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/// Configuration for a hardfork activation
|
|
47
|
+
#[napi(object)]
|
|
48
|
+
pub struct HardforkActivation {
|
|
49
|
+
/// The block number at which the hardfork is activated
|
|
50
|
+
pub block_number: BigInt,
|
|
51
|
+
/// The activated hardfork
|
|
52
|
+
pub spec_id: String,
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
#[napi(string_enum)]
|
|
56
|
+
#[doc = "The type of ordering to use when selecting blocks to mine."]
|
|
57
|
+
pub enum MineOrdering {
|
|
58
|
+
#[doc = "Insertion order"]
|
|
59
|
+
Fifo,
|
|
60
|
+
#[doc = "Effective miner fee"]
|
|
61
|
+
Priority,
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/// Configuration for the provider's mempool.
|
|
65
|
+
#[napi(object)]
|
|
66
|
+
pub struct MemPoolConfig {
|
|
67
|
+
pub order: MineOrdering,
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
#[napi(object)]
|
|
71
|
+
pub struct IntervalRange {
|
|
72
|
+
pub min: BigInt,
|
|
73
|
+
pub max: BigInt,
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/// Configuration for the provider's miner.
|
|
77
|
+
#[napi(object)]
|
|
78
|
+
pub struct MiningConfig {
|
|
79
|
+
pub auto_mine: bool,
|
|
80
|
+
pub interval: Option<Either<BigInt, IntervalRange>>,
|
|
81
|
+
pub mem_pool: MemPoolConfig,
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/// Configuration for a provider
|
|
85
|
+
#[napi(object)]
|
|
86
|
+
pub struct ProviderConfig {
|
|
87
|
+
/// Whether to allow blocks with the same timestamp
|
|
88
|
+
pub allow_blocks_with_same_timestamp: bool,
|
|
89
|
+
/// Whether to allow unlimited contract size
|
|
90
|
+
pub allow_unlimited_contract_size: bool,
|
|
91
|
+
/// Whether to return an `Err` when `eth_call` fails
|
|
92
|
+
pub bail_on_call_failure: bool,
|
|
93
|
+
/// Whether to return an `Err` when a `eth_sendTransaction` fails
|
|
94
|
+
pub bail_on_transaction_failure: bool,
|
|
95
|
+
/// The gas limit of each block
|
|
96
|
+
pub block_gas_limit: BigInt,
|
|
97
|
+
/// The directory to cache remote JSON-RPC responses
|
|
98
|
+
pub cache_dir: Option<String>,
|
|
99
|
+
/// The chain ID of the blockchain
|
|
100
|
+
pub chain_id: BigInt,
|
|
101
|
+
/// The configuration for chains
|
|
102
|
+
pub chains: Vec<ChainConfig>,
|
|
103
|
+
/// The address of the coinbase
|
|
104
|
+
pub coinbase: Buffer,
|
|
105
|
+
/// Enables RIP-7212
|
|
106
|
+
pub enable_rip_7212: bool,
|
|
107
|
+
/// The configuration for forking a blockchain. If not provided, a local
|
|
108
|
+
/// blockchain will be created
|
|
109
|
+
pub fork: Option<ForkConfig>,
|
|
110
|
+
/// The genesis state of the blockchain
|
|
111
|
+
pub genesis_state: Vec<Account>,
|
|
112
|
+
/// The hardfork of the blockchain
|
|
113
|
+
pub hardfork: String,
|
|
114
|
+
/// The initial base fee per gas of the blockchain. Required for EIP-1559
|
|
115
|
+
/// transactions and later
|
|
116
|
+
pub initial_base_fee_per_gas: Option<BigInt>,
|
|
117
|
+
/// The initial blob gas of the blockchain. Required for EIP-4844
|
|
118
|
+
pub initial_blob_gas: Option<BlobGas>,
|
|
119
|
+
/// The initial date of the blockchain, in seconds since the Unix epoch
|
|
120
|
+
pub initial_date: Option<BigInt>,
|
|
121
|
+
/// The initial parent beacon block root of the blockchain. Required for
|
|
122
|
+
/// EIP-4788
|
|
123
|
+
pub initial_parent_beacon_block_root: Option<Buffer>,
|
|
124
|
+
/// The minimum gas price of the next block.
|
|
125
|
+
pub min_gas_price: BigInt,
|
|
126
|
+
/// The configuration for the miner
|
|
127
|
+
pub mining: MiningConfig,
|
|
128
|
+
/// The network ID of the blockchain
|
|
129
|
+
pub network_id: BigInt,
|
|
130
|
+
/// Owned accounts, for which the secret key is known
|
|
131
|
+
pub owned_accounts: Vec<OwnedAccount>,
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
impl TryFrom<ForkConfig> for edr_provider::hardhat_rpc_types::ForkConfig {
|
|
135
|
+
type Error = napi::Error;
|
|
136
|
+
|
|
137
|
+
fn try_from(value: ForkConfig) -> Result<Self, Self::Error> {
|
|
138
|
+
let block_number: Option<u64> = value.block_number.map(TryCast::try_cast).transpose()?;
|
|
139
|
+
let http_headers = value.http_headers.map(|http_headers| {
|
|
140
|
+
http_headers
|
|
141
|
+
.into_iter()
|
|
142
|
+
.map(|HttpHeader { name, value }| (name, value))
|
|
143
|
+
.collect()
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
Ok(Self {
|
|
147
|
+
json_rpc_url: value.json_rpc_url,
|
|
148
|
+
block_number,
|
|
149
|
+
http_headers,
|
|
150
|
+
})
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
impl From<MemPoolConfig> for edr_provider::MemPoolConfig {
|
|
155
|
+
fn from(value: MemPoolConfig) -> Self {
|
|
156
|
+
Self {
|
|
157
|
+
order: value.order.into(),
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
impl From<MineOrdering> for edr_evm::MineOrdering {
|
|
163
|
+
fn from(value: MineOrdering) -> Self {
|
|
50
164
|
match value {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
165
|
+
MineOrdering::Fifo => Self::Fifo,
|
|
166
|
+
MineOrdering::Priority => Self::Priority,
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
impl TryFrom<MiningConfig> for edr_provider::MiningConfig {
|
|
172
|
+
type Error = napi::Error;
|
|
173
|
+
|
|
174
|
+
fn try_from(value: MiningConfig) -> Result<Self, Self::Error> {
|
|
175
|
+
let mem_pool = value.mem_pool.into();
|
|
176
|
+
|
|
177
|
+
let interval = value
|
|
178
|
+
.interval
|
|
179
|
+
.map(|interval| {
|
|
180
|
+
let interval = match interval {
|
|
181
|
+
Either::A(interval) => {
|
|
182
|
+
let interval = interval.try_cast()?;
|
|
183
|
+
let interval = NonZeroU64::new(interval).ok_or_else(|| {
|
|
184
|
+
napi::Error::new(
|
|
185
|
+
napi::Status::GenericFailure,
|
|
186
|
+
"Interval must be greater than 0",
|
|
187
|
+
)
|
|
188
|
+
})?;
|
|
189
|
+
|
|
190
|
+
edr_provider::IntervalConfig::Fixed(interval)
|
|
191
|
+
}
|
|
192
|
+
Either::B(IntervalRange { min, max }) => edr_provider::IntervalConfig::Range {
|
|
193
|
+
min: min.try_cast()?,
|
|
194
|
+
max: max.try_cast()?,
|
|
195
|
+
},
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
napi::Result::Ok(interval)
|
|
199
|
+
})
|
|
200
|
+
.transpose()?;
|
|
201
|
+
|
|
202
|
+
Ok(Self {
|
|
203
|
+
auto_mine: value.auto_mine,
|
|
204
|
+
interval,
|
|
205
|
+
mem_pool,
|
|
206
|
+
})
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
impl TryFrom<ProviderConfig> for edr_napi_core::provider::Config {
|
|
211
|
+
type Error = napi::Error;
|
|
212
|
+
|
|
213
|
+
fn try_from(value: ProviderConfig) -> Result<Self, Self::Error> {
|
|
214
|
+
let accounts = value
|
|
215
|
+
.owned_accounts
|
|
216
|
+
.into_iter()
|
|
217
|
+
.map(edr_provider::config::OwnedAccount::try_from)
|
|
218
|
+
.collect::<napi::Result<Vec<_>>>()?;
|
|
219
|
+
|
|
220
|
+
let block_gas_limit =
|
|
221
|
+
NonZeroU64::new(value.block_gas_limit.try_cast()?).ok_or_else(|| {
|
|
222
|
+
napi::Error::new(
|
|
223
|
+
napi::Status::GenericFailure,
|
|
224
|
+
"Block gas limit must be greater than 0",
|
|
225
|
+
)
|
|
226
|
+
})?;
|
|
227
|
+
|
|
228
|
+
let chains = value
|
|
229
|
+
.chains
|
|
230
|
+
.into_iter()
|
|
231
|
+
.map(
|
|
232
|
+
|ChainConfig {
|
|
233
|
+
chain_id,
|
|
234
|
+
hardforks,
|
|
235
|
+
}| {
|
|
236
|
+
let hardforks = hardforks
|
|
237
|
+
.into_iter()
|
|
238
|
+
.map(
|
|
239
|
+
|HardforkActivation {
|
|
240
|
+
block_number,
|
|
241
|
+
spec_id: hardfork,
|
|
242
|
+
}| {
|
|
243
|
+
let block_number = block_number.try_cast()?;
|
|
244
|
+
|
|
245
|
+
Ok(edr_napi_core::provider::HardforkActivation {
|
|
246
|
+
block_number,
|
|
247
|
+
hardfork,
|
|
248
|
+
})
|
|
249
|
+
},
|
|
250
|
+
)
|
|
251
|
+
.collect::<napi::Result<Vec<_>>>()?;
|
|
252
|
+
|
|
253
|
+
let chain_id = chain_id.try_cast()?;
|
|
254
|
+
Ok((chain_id, hardforks))
|
|
255
|
+
},
|
|
256
|
+
)
|
|
257
|
+
.collect::<napi::Result<_>>()?;
|
|
258
|
+
|
|
259
|
+
let genesis_state = value
|
|
260
|
+
.genesis_state
|
|
261
|
+
.into_iter()
|
|
262
|
+
.map(
|
|
263
|
+
|Account {
|
|
264
|
+
address,
|
|
265
|
+
balance,
|
|
266
|
+
nonce,
|
|
267
|
+
code,
|
|
268
|
+
storage,
|
|
269
|
+
}| {
|
|
270
|
+
let code: Option<edr_eth::Bytecode> =
|
|
271
|
+
code.map(TryCast::try_cast).transpose()?;
|
|
272
|
+
|
|
273
|
+
let code_hash = code
|
|
274
|
+
.as_ref()
|
|
275
|
+
.map_or(KECCAK_EMPTY, edr_eth::Bytecode::hash_slow);
|
|
276
|
+
|
|
277
|
+
let info = edr_eth::account::AccountInfo {
|
|
278
|
+
balance: balance.try_cast()?,
|
|
279
|
+
nonce: nonce.try_cast()?,
|
|
280
|
+
code_hash,
|
|
281
|
+
code,
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
let storage = storage
|
|
285
|
+
.into_iter()
|
|
286
|
+
.map(|StorageSlot { index, value }| {
|
|
287
|
+
let value = value.try_cast()?;
|
|
288
|
+
let slot = edr_evm::state::EvmStorageSlot::new(value);
|
|
289
|
+
|
|
290
|
+
let index: edr_eth::U256 = index.try_cast()?;
|
|
291
|
+
Ok((index, slot))
|
|
292
|
+
})
|
|
293
|
+
.collect::<napi::Result<_>>()?;
|
|
294
|
+
|
|
295
|
+
let address: edr_eth::Address = address.try_cast()?;
|
|
296
|
+
let account = edr_provider::config::Account { info, storage };
|
|
297
|
+
|
|
298
|
+
Ok((address, account))
|
|
299
|
+
},
|
|
300
|
+
)
|
|
301
|
+
.collect::<napi::Result<_>>()?;
|
|
302
|
+
|
|
303
|
+
Ok(Self {
|
|
304
|
+
accounts,
|
|
305
|
+
allow_blocks_with_same_timestamp: value.allow_blocks_with_same_timestamp,
|
|
306
|
+
allow_unlimited_contract_size: value.allow_unlimited_contract_size,
|
|
307
|
+
bail_on_call_failure: value.bail_on_call_failure,
|
|
308
|
+
bail_on_transaction_failure: value.bail_on_transaction_failure,
|
|
309
|
+
block_gas_limit,
|
|
310
|
+
cache_dir: value.cache_dir,
|
|
311
|
+
chain_id: value.chain_id.try_cast()?,
|
|
312
|
+
chains,
|
|
313
|
+
coinbase: value.coinbase.try_cast()?,
|
|
314
|
+
enable_rip_7212: value.enable_rip_7212,
|
|
315
|
+
fork: value.fork.map(TryInto::try_into).transpose()?,
|
|
316
|
+
genesis_state,
|
|
317
|
+
hardfork: value.hardfork,
|
|
318
|
+
initial_base_fee_per_gas: value
|
|
319
|
+
.initial_base_fee_per_gas
|
|
320
|
+
.map(TryCast::try_cast)
|
|
321
|
+
.transpose()?,
|
|
322
|
+
initial_blob_gas: value.initial_blob_gas.map(TryInto::try_into).transpose()?,
|
|
323
|
+
initial_date: value
|
|
324
|
+
.initial_date
|
|
325
|
+
.map(|date| {
|
|
326
|
+
let elapsed_since_epoch = Duration::from_secs(date.try_cast()?);
|
|
327
|
+
napi::Result::Ok(SystemTime::UNIX_EPOCH + elapsed_since_epoch)
|
|
328
|
+
})
|
|
329
|
+
.transpose()?,
|
|
330
|
+
initial_parent_beacon_block_root: value
|
|
331
|
+
.initial_parent_beacon_block_root
|
|
332
|
+
.map(TryCast::try_cast)
|
|
333
|
+
.transpose()?,
|
|
334
|
+
mining: value.mining.try_into()?,
|
|
335
|
+
min_gas_price: value.min_gas_price.try_cast()?,
|
|
336
|
+
network_id: value.network_id.try_cast()?,
|
|
337
|
+
})
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/// Tracing config for Solidity stack trace generation.
|
|
342
|
+
#[napi(object)]
|
|
343
|
+
pub struct TracingConfigWithBuffers {
|
|
344
|
+
/// Build information to use for decoding contracts. Either a Hardhat v2
|
|
345
|
+
/// build info file that contains both input and output or a Hardhat v3
|
|
346
|
+
/// build info file that doesn't contain output and a separate output file.
|
|
347
|
+
pub build_infos: Option<Either<Vec<Uint8Array>, Vec<BuildInfoAndOutput>>>,
|
|
348
|
+
/// Whether to ignore contracts whose name starts with "Ignored".
|
|
349
|
+
pub ignore_contracts: Option<bool>,
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/// Hardhat V3 build info where the compiler output is not part of the build
|
|
353
|
+
/// info file.
|
|
354
|
+
#[napi(object)]
|
|
355
|
+
pub struct BuildInfoAndOutput {
|
|
356
|
+
/// The build info input file
|
|
357
|
+
pub build_info: Uint8Array,
|
|
358
|
+
/// The build info output file
|
|
359
|
+
pub output: Uint8Array,
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
impl<'a> From<&'a BuildInfoAndOutput>
|
|
363
|
+
for edr_solidity::artifacts::BuildInfoBufferSeparateOutput<'a>
|
|
364
|
+
{
|
|
365
|
+
fn from(value: &'a BuildInfoAndOutput) -> Self {
|
|
366
|
+
Self {
|
|
367
|
+
build_info: value.build_info.as_ref(),
|
|
368
|
+
output: value.output.as_ref(),
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
impl<'a> From<&'a TracingConfigWithBuffers>
|
|
374
|
+
for edr_solidity::artifacts::BuildInfoConfigWithBuffers<'a>
|
|
375
|
+
{
|
|
376
|
+
fn from(value: &'a TracingConfigWithBuffers) -> Self {
|
|
377
|
+
use edr_solidity::artifacts::{BuildInfoBufferSeparateOutput, BuildInfoBuffers};
|
|
378
|
+
|
|
379
|
+
let build_infos = value.build_infos.as_ref().map(|infos| match infos {
|
|
380
|
+
Either::A(with_output) => BuildInfoBuffers::WithOutput(
|
|
381
|
+
with_output
|
|
382
|
+
.iter()
|
|
383
|
+
.map(std::convert::AsRef::as_ref)
|
|
384
|
+
.collect(),
|
|
385
|
+
),
|
|
386
|
+
Either::B(separate_output) => BuildInfoBuffers::SeparateInputOutput(
|
|
387
|
+
separate_output
|
|
388
|
+
.iter()
|
|
389
|
+
.map(BuildInfoBufferSeparateOutput::from)
|
|
390
|
+
.collect(),
|
|
391
|
+
),
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
Self {
|
|
395
|
+
build_infos,
|
|
396
|
+
ignore_contracts: value.ignore_contracts,
|
|
71
397
|
}
|
|
72
398
|
}
|
|
73
399
|
}
|
package/src/context.rs
CHANGED
|
@@ -1,22 +1,25 @@
|
|
|
1
|
-
use std::
|
|
1
|
+
use std::sync::Arc;
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
use
|
|
3
|
+
use edr_eth::HashMap;
|
|
4
|
+
use edr_napi_core::provider::{self, SyncProviderFactory};
|
|
5
|
+
use edr_solidity::contract_decoder::ContractDecoder;
|
|
6
|
+
use napi::{
|
|
7
|
+
Env, JsObject,
|
|
8
|
+
tokio::{runtime, sync::Mutex as AsyncMutex},
|
|
9
|
+
};
|
|
5
10
|
use napi_derive::napi;
|
|
6
|
-
use tracing_subscriber::{
|
|
11
|
+
use tracing_subscriber::{EnvFilter, Registry, prelude::*};
|
|
12
|
+
|
|
13
|
+
use crate::{
|
|
14
|
+
config::{ProviderConfig, TracingConfigWithBuffers},
|
|
15
|
+
logger::LoggerConfig,
|
|
16
|
+
provider::{Provider, ProviderFactory},
|
|
17
|
+
subscription::SubscriptionConfig,
|
|
18
|
+
};
|
|
7
19
|
|
|
8
20
|
#[napi]
|
|
9
|
-
#[derive(Debug)]
|
|
10
21
|
pub struct EdrContext {
|
|
11
|
-
inner: Arc<Context
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
impl Deref for EdrContext {
|
|
15
|
-
type Target = Arc<Context>;
|
|
16
|
-
|
|
17
|
-
fn deref(&self) -> &Self::Target {
|
|
18
|
-
&self.inner
|
|
19
|
-
}
|
|
22
|
+
inner: Arc<AsyncMutex<Context>>,
|
|
20
23
|
}
|
|
21
24
|
|
|
22
25
|
#[napi]
|
|
@@ -27,13 +30,91 @@ impl EdrContext {
|
|
|
27
30
|
let context = Context::new()?;
|
|
28
31
|
|
|
29
32
|
Ok(Self {
|
|
30
|
-
inner: Arc::new(context),
|
|
33
|
+
inner: Arc::new(AsyncMutex::new(context)),
|
|
31
34
|
})
|
|
32
35
|
}
|
|
36
|
+
|
|
37
|
+
#[doc = "Constructs a new provider with the provided configuration."]
|
|
38
|
+
#[napi(ts_return_type = "Promise<Provider>")]
|
|
39
|
+
pub fn create_provider(
|
|
40
|
+
&self,
|
|
41
|
+
env: Env,
|
|
42
|
+
chain_type: String,
|
|
43
|
+
provider_config: ProviderConfig,
|
|
44
|
+
logger_config: LoggerConfig,
|
|
45
|
+
subscription_config: SubscriptionConfig,
|
|
46
|
+
tracing_config: TracingConfigWithBuffers,
|
|
47
|
+
) -> napi::Result<JsObject> {
|
|
48
|
+
let provider_config = edr_napi_core::provider::Config::try_from(provider_config)?;
|
|
49
|
+
let logger_config = logger_config.resolve(&env)?;
|
|
50
|
+
|
|
51
|
+
// TODO: https://github.com/NomicFoundation/edr/issues/760
|
|
52
|
+
let build_info_config =
|
|
53
|
+
edr_solidity::artifacts::BuildInfoConfig::parse_from_buffers((&tracing_config).into())
|
|
54
|
+
.map_err(|err| napi::Error::from_reason(err.to_string()))?;
|
|
55
|
+
|
|
56
|
+
let contract_decoder = ContractDecoder::new(&build_info_config).map_or_else(
|
|
57
|
+
|error| Err(napi::Error::from_reason(error.to_string())),
|
|
58
|
+
|contract_decoder| Ok(Arc::new(contract_decoder)),
|
|
59
|
+
)?;
|
|
60
|
+
|
|
61
|
+
#[cfg(feature = "scenarios")]
|
|
62
|
+
let scenario_file =
|
|
63
|
+
runtime::Handle::current().block_on(crate::scenarios::scenario_file(
|
|
64
|
+
chain_type.clone(),
|
|
65
|
+
provider_config.clone(),
|
|
66
|
+
logger_config.enable,
|
|
67
|
+
))?;
|
|
68
|
+
|
|
69
|
+
let runtime = runtime::Handle::current();
|
|
70
|
+
let builder = {
|
|
71
|
+
// TODO: https://github.com/NomicFoundation/edr/issues/760
|
|
72
|
+
// TODO: Don't block the JS event loop
|
|
73
|
+
let context = runtime.block_on(async { self.inner.lock().await });
|
|
74
|
+
|
|
75
|
+
context.create_provider_builder(
|
|
76
|
+
&env,
|
|
77
|
+
&chain_type,
|
|
78
|
+
provider_config,
|
|
79
|
+
logger_config,
|
|
80
|
+
subscription_config.into(),
|
|
81
|
+
&contract_decoder,
|
|
82
|
+
)?
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
let (deferred, promise) = env.create_deferred()?;
|
|
86
|
+
runtime.clone().spawn_blocking(move || {
|
|
87
|
+
let result = builder.build(runtime.clone()).map(|provider| {
|
|
88
|
+
Provider::new(
|
|
89
|
+
provider,
|
|
90
|
+
runtime,
|
|
91
|
+
contract_decoder,
|
|
92
|
+
#[cfg(feature = "scenarios")]
|
|
93
|
+
scenario_file,
|
|
94
|
+
)
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
deferred.resolve(|_env| result);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
Ok(promise)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
#[doc = "Registers a new provider factory for the provided chain type."]
|
|
104
|
+
#[napi]
|
|
105
|
+
pub async fn register_provider_factory(
|
|
106
|
+
&self,
|
|
107
|
+
chain_type: String,
|
|
108
|
+
factory: &ProviderFactory,
|
|
109
|
+
) -> napi::Result<()> {
|
|
110
|
+
let mut context = self.inner.lock().await;
|
|
111
|
+
context.register_provider_factory(chain_type, factory.as_inner().clone());
|
|
112
|
+
Ok(())
|
|
113
|
+
}
|
|
33
114
|
}
|
|
34
115
|
|
|
35
|
-
#[derive(Debug)]
|
|
36
116
|
pub struct Context {
|
|
117
|
+
provider_factories: HashMap<String, Arc<dyn SyncProviderFactory>>,
|
|
37
118
|
#[cfg(feature = "tracing")]
|
|
38
119
|
_tracing_write_guard: tracing_flame::FlushGuard<std::io::BufWriter<std::fs::File>>,
|
|
39
120
|
}
|
|
@@ -56,7 +137,7 @@ impl Context {
|
|
|
56
137
|
let (flame_layer, guard) = tracing_flame::FlameLayer::with_file("tracing.folded")
|
|
57
138
|
.map_err(|err| {
|
|
58
139
|
napi::Error::new(
|
|
59
|
-
Status::GenericFailure,
|
|
140
|
+
napi::Status::GenericFailure,
|
|
60
141
|
format!("Failed to create tracing.folded file with error: {err:?}"),
|
|
61
142
|
)
|
|
62
143
|
})?;
|
|
@@ -76,8 +157,45 @@ impl Context {
|
|
|
76
157
|
}
|
|
77
158
|
|
|
78
159
|
Ok(Self {
|
|
160
|
+
provider_factories: HashMap::new(),
|
|
79
161
|
#[cfg(feature = "tracing")]
|
|
80
162
|
_tracing_write_guard: guard,
|
|
81
163
|
})
|
|
82
164
|
}
|
|
165
|
+
|
|
166
|
+
/// Registers a new provider factory for the provided chain type.
|
|
167
|
+
pub fn register_provider_factory(
|
|
168
|
+
&mut self,
|
|
169
|
+
chain_type: String,
|
|
170
|
+
factory: Arc<dyn SyncProviderFactory>,
|
|
171
|
+
) {
|
|
172
|
+
self.provider_factories.insert(chain_type, factory);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/// Tries to create a new provider for the provided chain type and
|
|
176
|
+
/// configuration.
|
|
177
|
+
pub fn create_provider_builder(
|
|
178
|
+
&self,
|
|
179
|
+
env: &napi::Env,
|
|
180
|
+
chain_type: &str,
|
|
181
|
+
provider_config: edr_napi_core::provider::Config,
|
|
182
|
+
logger_config: edr_napi_core::logger::Config,
|
|
183
|
+
subscription_config: edr_napi_core::subscription::Config,
|
|
184
|
+
contract_decoder: &Arc<ContractDecoder>,
|
|
185
|
+
) -> napi::Result<Box<dyn provider::Builder>> {
|
|
186
|
+
if let Some(factory) = self.provider_factories.get(chain_type) {
|
|
187
|
+
factory.create_provider_builder(
|
|
188
|
+
env,
|
|
189
|
+
provider_config,
|
|
190
|
+
logger_config,
|
|
191
|
+
subscription_config,
|
|
192
|
+
contract_decoder.clone(),
|
|
193
|
+
)
|
|
194
|
+
} else {
|
|
195
|
+
Err(napi::Error::new(
|
|
196
|
+
napi::Status::GenericFailure,
|
|
197
|
+
"Provider for provided chain type does not exist",
|
|
198
|
+
))
|
|
199
|
+
}
|
|
200
|
+
}
|
|
83
201
|
}
|