miden_client/builder.rs
1use alloc::boxed::Box;
2use alloc::sync::Arc;
3use alloc::vec;
4use alloc::vec::Vec;
5
6use miden_protocol::assembly::{DefaultSourceManager, SourceManagerSync};
7use miden_protocol::block::BlockNumber;
8use miden_protocol::crypto::rand::RandomCoin;
9use miden_protocol::{Felt, MAX_TX_EXECUTION_CYCLES, MIN_TX_EXECUTION_CYCLES};
10use miden_tx::auth::TransactionAuthenticator;
11use miden_tx::{ExecutionOptions, LocalTransactionProver};
12use rand::RngExt;
13
14#[cfg(any(feature = "tonic", feature = "std"))]
15use crate::alloc::string::ToString;
16#[cfg(feature = "std")]
17use crate::keystore::FilesystemKeyStore;
18use crate::note_transport::NoteTransportClient;
19use crate::pswap::PswapTransactionObserver;
20use crate::rpc::{Endpoint, NodeRpcClient};
21#[cfg(feature = "tonic")]
22use crate::rpc::{GrpcClient, VerifyingRpcClient};
23use crate::store::{Store, StoreError};
24use crate::transaction::{TransactionObserver, TransactionProver};
25use crate::{Client, ClientError, ClientRng, ClientRngBox, grpc_support};
26
27// CONSTANTS
28// ================================================================================================
29
30/// The default number of blocks after which pending transactions are considered stale and
31/// discarded.
32const TX_DISCARD_DELTA: u32 = 20;
33/// The default number of synced blocks between automatic irrelevant-block pruning runs.
34const IRRELEVANT_BLOCK_PRUNE_INTERVAL: u32 = 1;
35/// Whether the client should cache the current Partial MMR in memory by default.
36const CACHE_PARTIAL_MMR_IN_MEMORY: bool = false;
37
38pub use grpc_support::*;
39
40// STORE BUILDER
41// ================================================================================================
42
43/// Allows the [`ClientBuilder`] to accept either an already built store instance or a factory for
44/// deferring the store instantiation.
45pub enum StoreBuilder {
46 Store(Arc<dyn Store>),
47 Factory(Box<dyn StoreFactory>),
48}
49
50/// Trait for building a store instance.
51#[async_trait::async_trait]
52pub trait StoreFactory {
53 /// Returns a new store instance.
54 async fn build(&self) -> Result<Arc<dyn Store>, StoreError>;
55}
56
57// CLIENT BUILDER
58// ================================================================================================
59
60/// A builder for constructing a Miden client.
61///
62/// This builder allows you to configure the various components required by the client, such as the
63/// RPC endpoint, store, RNG, and authenticator. It is generic over the authenticator type.
64///
65/// ## Network-Aware Constructors
66///
67/// Use one of the network-specific constructors to get sensible defaults for a specific network:
68/// - [`for_mainnet()`](Self::for_mainnet) - Pre-configured for Miden mainnet
69/// - [`for_testnet()`](Self::for_testnet) - Pre-configured for Miden testnet
70/// - [`for_devnet()`](Self::for_devnet) - Pre-configured for Miden devnet
71/// - [`for_localhost()`](Self::for_localhost) - Pre-configured for local development
72///
73/// The builder provides defaults for:
74/// - **RPC endpoint**: Automatically configured based on the network
75/// - **Transaction prover**: Remote for mainnet/testnet/devnet, local for localhost
76/// - **RNG**: Random seed-based prover randomness
77///
78/// ## Components
79///
80/// The client requires several components to function:
81///
82/// - **RPC client** ([`NodeRpcClient`]): Provides connectivity to the Miden node for submitting
83/// transactions, syncing state, and fetching account/note data. Configure via
84/// [`rpc()`](Self::rpc) or [`grpc_client()`](Self::grpc_client).
85///
86/// - **Store** ([`Store`]): Provides persistence for accounts, notes, and transaction history.
87/// Configure via [`store()`](Self::store).
88///
89/// - **RNG** ([`FeltRng`](miden_protocol::crypto::rand::FeltRng)): Provides randomness for
90/// generating keys, serial numbers, and other cryptographic operations. If not provided, a random
91/// seed-based RNG is created automatically. Configure via [`rng()`](Self::rng).
92///
93/// - **Authenticator** ([`TransactionAuthenticator`]): Handles transaction signing when signatures
94/// are requested from within the VM. Configure via [`authenticator()`](Self::authenticator).
95///
96/// - **Transaction prover** ([`TransactionProver`]): Generates proofs for transactions. Defaults to
97/// a local prover if not specified. Configure via [`prover()`](Self::prover).
98///
99/// - **Note transport** ([`NoteTransportClient`]): Optional component for exchanging private notes
100/// through the Miden note transport network. Configure via
101/// [`note_transport()`](Self::note_transport).
102///
103/// - **Transaction discard delta**: Number of blocks after which pending transactions are
104/// considered stale and discarded. Configure via [`tx_discard_delta()`](Self::tx_discard_delta).
105///
106/// - **In-memory Partial MMR cache**: Reuses the current partial blockchain MMR instead of
107/// rebuilding it from store. Disabled by default. Configure via
108/// [`cache_partial_mmr_in_memory()`](Self::cache_partial_mmr_in_memory).
109///
110/// - **Max block number delta**: Maximum number of blocks the client can be behind the network for
111/// transactions and account proofs to be considered valid. Configure via
112/// [`max_block_number_delta()`](Self::max_block_number_delta).
113pub struct ClientBuilder<AUTH> {
114 /// An optional custom RPC client. If provided, this takes precedence over `rpc_endpoint`.
115 rpc_api: Option<Arc<dyn NodeRpcClient>>,
116 /// An optional store provided by the user.
117 pub store: Option<StoreBuilder>,
118 /// An optional RNG provided by the user.
119 rng: Option<ClientRngBox>,
120 /// The authenticator provided by the user.
121 authenticator: Option<Arc<AUTH>>,
122 /// Number of blocks after which pending transactions are considered stale and discarded. If
123 /// `None`, there is no limit and transactions will be kept indefinitely.
124 tx_discard_delta: Option<u32>,
125 /// Number of synced blocks between automatic pruning runs for irrelevant block data. If `None`,
126 /// automatic irrelevant-block pruning is disabled.
127 irrelevant_block_prune_interval: Option<u32>,
128 /// Whether the current Partial MMR should be cached in memory between sync-related operations.
129 cache_partial_mmr_in_memory: bool,
130 /// Maximum number of blocks the client can be behind the network for transactions and account
131 /// proofs to be considered valid.
132 max_block_number_delta: Option<u32>,
133 /// An optional custom note transport client.
134 note_transport_api: Option<Arc<dyn NoteTransportClient>>,
135 /// Configuration for lazy note transport initialization (used by network constructors).
136 #[allow(unused)]
137 note_transport_config: Option<NoteTransportConfig>,
138 /// An optional custom transaction prover.
139 tx_prover: Option<Arc<dyn TransactionProver + Send + Sync>>,
140 /// The endpoint used by the builder for network configuration.
141 endpoint: Option<Endpoint>,
142 /// An optional shared source manager for MASM source information.
143 source_manager: Option<Arc<dyn SourceManagerSync>>,
144}
145
146impl<AUTH> Default for ClientBuilder<AUTH> {
147 fn default() -> Self {
148 Self {
149 rpc_api: None,
150 store: None,
151 rng: None,
152 authenticator: None,
153 tx_discard_delta: Some(TX_DISCARD_DELTA),
154 irrelevant_block_prune_interval: Some(IRRELEVANT_BLOCK_PRUNE_INTERVAL),
155 cache_partial_mmr_in_memory: CACHE_PARTIAL_MMR_IN_MEMORY,
156 max_block_number_delta: None,
157 note_transport_api: None,
158 note_transport_config: None,
159 tx_prover: None,
160 endpoint: None,
161 source_manager: None,
162 }
163 }
164}
165
166/// Network-specific constructors for [`ClientBuilder`].
167///
168/// These constructors automatically configure the builder for a specific network, including RPC
169/// endpoint, transaction prover, and note transport (where applicable).
170#[cfg(feature = "tonic")]
171impl<AUTH> ClientBuilder<AUTH>
172where
173 AUTH: BuilderAuthenticator,
174{
175 /// Creates a `ClientBuilder` pre-configured for Miden mainnet.
176 ///
177 /// This automatically configures:
178 /// - **RPC**: [`Endpoint::mainnet()`]
179 /// - **Prover**: Remote prover at [`MAINNET_PROVER_ENDPOINT`]
180 /// - **Note transport**:
181 /// [`NOTE_TRANSPORT_MAINNET_ENDPOINT`](crate::note_transport::NOTE_TRANSPORT_MAINNET_ENDPOINT)
182 ///
183 /// You still need to provide:
184 /// - A store (via `.store()`)
185 /// - An authenticator (via `.authenticator()`)
186 ///
187 /// All defaults can be overridden by calling the corresponding builder methods after
188 /// `for_mainnet()`.
189 ///
190 /// # Example
191 ///
192 /// ```ignore
193 /// let client = ClientBuilder::for_mainnet()
194 /// .store(store)
195 /// .authenticator(Arc::new(keystore))
196 /// .build()
197 /// .await?;
198 /// ```
199 #[must_use]
200 pub fn for_mainnet() -> Self {
201 let endpoint = Endpoint::mainnet();
202 Self {
203 rpc_api: Some(Arc::new(VerifyingRpcClient::new(GrpcClient::new(
204 &endpoint,
205 DEFAULT_GRPC_TIMEOUT_MS,
206 )))),
207 tx_prover: Some(Arc::new(RemoteTransactionProver::new(
208 MAINNET_PROVER_ENDPOINT.to_string(),
209 ))),
210 note_transport_config: Some(NoteTransportConfig {
211 endpoint: crate::note_transport::NOTE_TRANSPORT_MAINNET_ENDPOINT.to_string(),
212 timeout_ms: DEFAULT_GRPC_TIMEOUT_MS,
213 }),
214 endpoint: Some(endpoint),
215 ..Self::default()
216 }
217 }
218
219 /// Creates a `ClientBuilder` pre-configured for Miden testnet.
220 ///
221 /// This automatically configures:
222 /// - **RPC**: [`Endpoint::testnet()`]
223 /// - **Prover**: Remote prover at [`TESTNET_PROVER_ENDPOINT`]
224 /// - **Note transport**:
225 /// [`NOTE_TRANSPORT_TESTNET_ENDPOINT`](crate::note_transport::NOTE_TRANSPORT_TESTNET_ENDPOINT)
226 ///
227 /// You still need to provide:
228 /// - A store (via `.store()`)
229 /// - An authenticator (via `.authenticator()`)
230 ///
231 /// All defaults can be overridden by calling the corresponding builder methods after
232 /// `for_testnet()`.
233 ///
234 /// # Example
235 ///
236 /// ```ignore
237 /// let client = ClientBuilder::for_testnet()
238 /// .store(store)
239 /// .authenticator(Arc::new(keystore))
240 /// .build()
241 /// .await?;
242 /// ```
243 #[must_use]
244 pub fn for_testnet() -> Self {
245 let endpoint = Endpoint::testnet();
246 Self {
247 rpc_api: Some(Arc::new(VerifyingRpcClient::new(GrpcClient::new(
248 &endpoint,
249 DEFAULT_GRPC_TIMEOUT_MS,
250 )))),
251 tx_prover: Some(Arc::new(RemoteTransactionProver::new(
252 TESTNET_PROVER_ENDPOINT.to_string(),
253 ))),
254 note_transport_config: Some(NoteTransportConfig {
255 endpoint: crate::note_transport::NOTE_TRANSPORT_TESTNET_ENDPOINT.to_string(),
256 timeout_ms: DEFAULT_GRPC_TIMEOUT_MS,
257 }),
258 endpoint: Some(endpoint),
259 ..Self::default()
260 }
261 }
262
263 /// Creates a `ClientBuilder` pre-configured for Miden devnet.
264 ///
265 /// This automatically configures:
266 /// - **RPC**: [`Endpoint::devnet()`]
267 /// - **Prover**: Remote prover at [`DEVNET_PROVER_ENDPOINT`]
268 /// - **Note transport**:
269 /// [`NOTE_TRANSPORT_DEVNET_ENDPOINT`](crate::note_transport::NOTE_TRANSPORT_DEVNET_ENDPOINT)
270 ///
271 /// You still need to provide:
272 /// - A store (via `.store()`)
273 /// - An authenticator (via `.authenticator()`)
274 ///
275 /// All defaults can be overridden by calling the corresponding builder methods after
276 /// `for_devnet()`.
277 ///
278 /// # Example
279 ///
280 /// ```ignore
281 /// let client = ClientBuilder::for_devnet()
282 /// .store(store)
283 /// .authenticator(Arc::new(keystore))
284 /// .build()
285 /// .await?;
286 /// ```
287 #[must_use]
288 pub fn for_devnet() -> Self {
289 let endpoint = Endpoint::devnet();
290 Self {
291 rpc_api: Some(Arc::new(VerifyingRpcClient::new(GrpcClient::new(
292 &endpoint,
293 DEFAULT_GRPC_TIMEOUT_MS,
294 )))),
295 tx_prover: Some(Arc::new(RemoteTransactionProver::new(
296 DEVNET_PROVER_ENDPOINT.to_string(),
297 ))),
298 note_transport_config: Some(NoteTransportConfig {
299 endpoint: crate::note_transport::NOTE_TRANSPORT_DEVNET_ENDPOINT.to_string(),
300 timeout_ms: DEFAULT_GRPC_TIMEOUT_MS,
301 }),
302 endpoint: Some(endpoint),
303 ..Self::default()
304 }
305 }
306
307 /// Creates a `ClientBuilder` pre-configured for localhost.
308 ///
309 /// This automatically configures:
310 /// - **RPC**: `http://localhost:57291`
311 /// - **Prover**: Local (default)
312 ///
313 /// Note transport is not configured by default for localhost.
314 ///
315 /// You still need to provide:
316 /// - A store (via `.store()`)
317 /// - An authenticator (via `.authenticator()`)
318 ///
319 /// All defaults can be overridden by calling the corresponding builder methods after
320 /// `for_localhost()`.
321 ///
322 /// # Example
323 ///
324 /// ```ignore
325 /// let client = ClientBuilder::for_localhost()
326 /// .store(store)
327 /// .authenticator(Arc::new(keystore))
328 /// .build()
329 /// .await?;
330 /// ```
331 #[must_use]
332 pub fn for_localhost() -> Self {
333 let endpoint = Endpoint::localhost();
334 Self {
335 rpc_api: Some(Arc::new(VerifyingRpcClient::new(GrpcClient::new(
336 &endpoint,
337 DEFAULT_GRPC_TIMEOUT_MS,
338 )))),
339 endpoint: Some(endpoint),
340 ..Self::default()
341 }
342 }
343}
344
345impl<AUTH> ClientBuilder<AUTH>
346where
347 AUTH: BuilderAuthenticator,
348{
349 /// Create a new `ClientBuilder` with default settings.
350 #[must_use]
351 pub fn new() -> Self {
352 Self::default()
353 }
354
355 /// Sets a custom RPC client directly.
356 ///
357 /// The client is used as provided: wrap it in [`VerifyingRpcClient`] to have node responses
358 /// verified against the requests.
359 #[must_use]
360 pub fn rpc(mut self, client: Arc<dyn NodeRpcClient>) -> Self {
361 self.rpc_api = Some(client);
362 self
363 }
364
365 /// Sets a gRPC client from the endpoint and optional timeout, wrapped in a
366 /// [`VerifyingRpcClient`] so node responses are verified against the requests.
367 #[must_use]
368 #[cfg(feature = "tonic")]
369 pub fn grpc_client(mut self, endpoint: &Endpoint, timeout_ms: Option<u64>) -> Self {
370 self.rpc_api = Some(Arc::new(VerifyingRpcClient::new(GrpcClient::new(
371 endpoint,
372 timeout_ms.unwrap_or(DEFAULT_GRPC_TIMEOUT_MS),
373 ))));
374 self
375 }
376
377 /// Provide a store to be used by the client.
378 #[must_use]
379 pub fn store(mut self, store: Arc<dyn Store>) -> Self {
380 self.store = Some(StoreBuilder::Store(store));
381 self
382 }
383
384 /// Optionally provide a custom RNG.
385 #[must_use]
386 pub fn rng(mut self, rng: ClientRngBox) -> Self {
387 self.rng = Some(rng);
388 self
389 }
390
391 /// Optionally provide a custom authenticator instance.
392 #[must_use]
393 pub fn authenticator(mut self, authenticator: Arc<AUTH>) -> Self {
394 self.authenticator = Some(authenticator);
395 self
396 }
397
398 /// Overrides the source manager used to retain MASM source information for assembled programs.
399 ///
400 /// If not set, the client uses a default [`DefaultSourceManager`]. The same instance is
401 /// forwarded to the transaction executor and to every script compiled through the client (e.g.
402 /// via [`Client::code_builder`](crate::Client::code_builder)).
403 ///
404 /// Set this explicitly only when scripts or modules are compiled outside the client (for
405 /// example, using an external [`Assembler`](miden_protocol::assembly::Assembler)): pass the
406 /// same `Arc` used by that external assembler so all source spans resolve correctly at runtime.
407 #[must_use]
408 pub fn source_manager(mut self, sm: Arc<dyn SourceManagerSync>) -> Self {
409 self.source_manager = Some(sm);
410 self
411 }
412
413 /// Optionally set a maximum number of blocks that the client can be behind the network. By
414 /// default, there's no maximum.
415 #[must_use]
416 pub fn max_block_number_delta(mut self, delta: u32) -> Self {
417 self.max_block_number_delta = Some(delta);
418 self
419 }
420
421 /// Sets the number of blocks after which pending transactions are considered stale and
422 /// discarded.
423 ///
424 /// If a transaction has not been included in a block within this many blocks after submission,
425 /// it will be discarded. If `None`, transactions will be kept indefinitely.
426 ///
427 /// By default, the delta is set to `TX_DISCARD_DELTA` (20 blocks).
428 #[must_use]
429 pub fn tx_discard_delta(mut self, delta: Option<u32>) -> Self {
430 self.tx_discard_delta = delta;
431 self
432 }
433
434 /// Sets the number of synced blocks between automatic irrelevant-block pruning runs.
435 ///
436 /// Values defer pruning until the client has advanced by at least that many sync blocks since
437 /// the last prune. `None` disables automatic pruning entirely.
438 #[must_use]
439 pub fn irrelevant_block_prune_interval(mut self, interval: Option<u32>) -> Self {
440 self.irrelevant_block_prune_interval = interval;
441 self
442 }
443
444 /// Enables or disables the in-memory Partial MMR cache.
445 ///
446 /// When enabled, the client reuses the current Partial MMR between sync and pruning operations.
447 /// When disabled, it rebuilds the Partial MMR from the store each time it is needed.
448 #[must_use]
449 pub fn cache_partial_mmr_in_memory(mut self, enabled: bool) -> Self {
450 self.cache_partial_mmr_in_memory = enabled;
451 self
452 }
453
454 /// Sets the number of blocks after which pending transactions are considered stale and
455 /// discarded.
456 ///
457 /// This is an alias for [`tx_discard_delta`](Self::tx_discard_delta).
458 #[deprecated(since = "0.10.0", note = "Use `tx_discard_delta` instead")]
459 #[must_use]
460 pub fn tx_graceful_blocks(mut self, delta: Option<u32>) -> Self {
461 self.tx_discard_delta = delta;
462 self
463 }
464
465 /// Sets a custom note transport client directly.
466 #[must_use]
467 pub fn note_transport(mut self, client: Arc<dyn NoteTransportClient>) -> Self {
468 self.note_transport_api = Some(client);
469 self
470 }
471
472 /// Sets a custom transaction prover.
473 #[must_use]
474 pub fn prover(mut self, prover: Arc<dyn TransactionProver + Send + Sync>) -> Self {
475 self.tx_prover = Some(prover);
476 self
477 }
478
479 /// Returns the endpoint configured for this builder, if any.
480 ///
481 /// This is set automatically when using network-specific constructors like
482 /// [`for_mainnet()`](Self::for_mainnet), [`for_testnet()`](Self::for_testnet),
483 /// [`for_devnet()`](Self::for_devnet), or [`for_localhost()`](Self::for_localhost).
484 #[must_use]
485 pub fn endpoint(&self) -> Option<&Endpoint> {
486 self.endpoint.as_ref()
487 }
488
489 /// Build and return the `Client`.
490 ///
491 /// # Errors
492 ///
493 /// - Returns an error if no RPC client was provided.
494 /// - Returns an error if the store cannot be instantiated.
495 #[allow(clippy::unused_async, unused_mut)]
496 pub async fn build(mut self) -> Result<Client<AUTH>, ClientError> {
497 // Determine the RPC client to use.
498 let rpc_api: Arc<dyn NodeRpcClient> = if let Some(client) = self.rpc_api {
499 client
500 } else {
501 return Err(ClientError::ClientInitializationError(
502 "RPC client is required. Call `.rpc(...)` or `.grpc_client(...)`.".into(),
503 ));
504 };
505
506 // Ensure a store was provided.
507 let store = if let Some(store_builder) = self.store {
508 match store_builder {
509 StoreBuilder::Store(store) => store,
510 StoreBuilder::Factory(factory) => factory.build().await?,
511 }
512 } else {
513 return Err(ClientError::ClientInitializationError(
514 "Store must be specified. Call `.store(...)`.".into(),
515 ));
516 };
517
518 // Use the provided RNG, or create a default one.
519 let rng = if let Some(user_rng) = self.rng {
520 user_rng
521 } else {
522 let mut seed_rng = rand::rng();
523 let coin_seed: [u64; 4] = seed_rng.random();
524 Box::new(RandomCoin::new(coin_seed.map(Felt::new_unchecked).into()))
525 };
526
527 let tx_prover: Arc<dyn TransactionProver + Send + Sync> =
528 self.tx_prover.unwrap_or_else(|| Arc::new(LocalTransactionProver::default()));
529
530 let source_manager: Arc<dyn SourceManagerSync> =
531 self.source_manager.unwrap_or_else(|| Arc::new(DefaultSourceManager::default()));
532
533 // Initialize genesis commitment in RPC client
534 if let Some((genesis, _)) = store.get_block_header_by_num(BlockNumber::GENESIS).await? {
535 rpc_api.set_genesis_commitment(genesis.commitment()).await?;
536 }
537
538 // Set the RPC client with persisted limits if available. If not present, they will be
539 // fetched from the node during sync_state.
540 if let Some(limits) = store.get_rpc_limits().await? {
541 rpc_api.set_rpc_limits(limits).await;
542 }
543
544 // Initialize note transport: prefer explicit client, fall back to config (tonic only)
545 #[cfg(feature = "tonic")]
546 if self.note_transport_api.is_none()
547 && let Some(config) = self.note_transport_config
548 {
549 let transport = crate::note_transport::grpc::GrpcNoteTransportClient::new(
550 config.endpoint,
551 config.timeout_ms,
552 );
553
554 self.note_transport_api = Some(Arc::new(transport) as Arc<dyn NoteTransportClient>);
555 }
556
557 // Built-in transaction observers fired by `apply_transaction`. Additional observers can be
558 // attached via `Client::with_transaction_observer`.
559 let transaction_observers: Vec<Arc<dyn TransactionObserver>> =
560 vec![Arc::new(PswapTransactionObserver::new(store.clone()))];
561
562 // Construct and return the Client
563 let client = Client {
564 store,
565 rng: ClientRng::new(rng),
566 rpc_api,
567 tx_prover,
568 authenticator: self.authenticator,
569 source_manager,
570 exec_options: ExecutionOptions::new(
571 Some(MAX_TX_EXECUTION_CYCLES),
572 MIN_TX_EXECUTION_CYCLES,
573 ExecutionOptions::DEFAULT_CORE_TRACE_FRAGMENT_SIZE,
574 )
575 .expect("Default executor's options should always be valid"),
576 tx_discard_delta: self.tx_discard_delta,
577 irrelevant_block_prune_interval: self.irrelevant_block_prune_interval,
578 last_irrelevant_block_prune_sync_height: None,
579 max_block_number_delta: self.max_block_number_delta,
580 note_transport_api: self.note_transport_api.clone(),
581 cache_partial_mmr_in_memory: self.cache_partial_mmr_in_memory,
582 partial_mmr: None,
583 transaction_observers,
584 };
585 Ok(client)
586 }
587}
588
589// BUILDER AUTHENTICATOR
590// ================================================================================================
591
592/// Marker trait for the authenticator type parameter of [`ClientBuilder`].
593///
594/// The builder stores the authenticator and passes it to the client. The client uses it only to
595/// sign transactions, so any [`TransactionAuthenticator`] with a `'static` lifetime qualifies. Key
596/// management is not required. A signer that holds no secret key, such as a remote signing service,
597/// can be used without implementing [`Keystore`](crate::keystore::Keystore).
598pub trait BuilderAuthenticator: TransactionAuthenticator + 'static {}
599impl<T> BuilderAuthenticator for T where T: TransactionAuthenticator + 'static {}
600
601// FILESYSTEM KEYSTORE CONVENIENCE METHOD
602// ================================================================================================
603
604/// Convenience method for [`ClientBuilder`] when using [`FilesystemKeyStore`] as the authenticator.
605#[cfg(feature = "std")]
606impl ClientBuilder<FilesystemKeyStore> {
607 /// Creates a [`FilesystemKeyStore`] from the given path and sets it as the authenticator.
608 ///
609 /// This is a convenience method that creates the keystore and configures it as the
610 /// authenticator in a single call. The keystore provides transaction signing capabilities using
611 /// keys stored on the filesystem.
612 ///
613 /// # Errors
614 ///
615 /// Returns an error if the keystore cannot be created from the given path.
616 ///
617 /// # Example
618 ///
619 /// ```ignore
620 /// let client = ClientBuilder::new()
621 /// .rpc(rpc_client)
622 /// .store(store)
623 /// .filesystem_keystore("path/to/keys")?
624 /// .build()
625 /// .await?;
626 /// ```
627 pub fn filesystem_keystore(
628 self,
629 keystore_path: impl Into<std::path::PathBuf>,
630 ) -> Result<Self, ClientError> {
631 let keystore = FilesystemKeyStore::new(keystore_path.into())
632 .map_err(|e| ClientError::ClientInitializationError(e.to_string()))?;
633 Ok(self.authenticator(Arc::new(keystore)))
634 }
635}