@nomicfoundation/edr 0.11.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/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,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
- /// 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 {
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
- 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,
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::{ops::Deref, sync::Arc};
1
+ use std::sync::Arc;
2
2
 
3
- #[cfg(feature = "tracing")]
4
- use napi::Status;
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::{prelude::*, EnvFilter, Registry};
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
  }