Skip to main content

sdk/signing_client/
builder.rs

1use std::sync::{Arc, Mutex};
2
3use super::SigningClient;
4use crate::{client::Client, crypto::Signer};
5
6/// Where a signing client sends transactions: a URL to build a fresh [`Client`]
7/// from, or an existing one to reuse.
8enum Endpoint {
9    Url(String),
10    Client(Client),
11}
12
13/// Builds a [`SigningClient`] whose chain id is optional.
14///
15/// Omit [`chain_id`](Self::chain_id) and the client reads it from the endpoint
16/// itself on first submission, then caches it. Supply it and no lookup ever
17/// happens.
18///
19/// # Examples
20///
21/// ```rust,no_run
22/// use sdk::{Client, SigningClient, crypto::Signer};
23///
24/// # fn example(signer: Signer) -> anyhow::Result<()> {
25/// // chain id read from the endpoint
26/// let client = SigningClient::builder(signer.clone())
27///     .url("http://localhost:1789")
28///     .build()?;
29///
30/// // chain id supplied — no round-trip
31/// let client = SigningClient::builder(signer.clone())
32///     .url("http://localhost:1789")
33///     .chain_id("real-t-0001")
34///     .build()?;
35///
36/// // many signers over one connection pool, sharing its chain-id lookup
37/// let shared = Client::new("http://localhost:1789")?;
38/// let client = SigningClient::builder(signer).client(shared).build()?;
39/// # Ok(())
40/// # }
41/// ```
42pub struct SigningClientBuilder {
43    signer: Signer,
44    endpoint: Option<Endpoint>,
45    chain_id: Option<String>,
46}
47
48impl SigningClientBuilder {
49    pub(super) fn new(signer: Signer) -> SigningClientBuilder {
50        return SigningClientBuilder {
51            signer,
52            endpoint: None,
53            chain_id: None,
54        };
55    }
56
57    /// Endpoint to trade against, replacing any endpoint already set.
58    pub fn url(mut self, url: impl Into<String>) -> SigningClientBuilder {
59        self.endpoint = Some(Endpoint::Url(url.into()));
60        return self;
61    }
62
63    /// Reuses an existing [`Client`], sharing its connection pool and its
64    /// chain-id cache. Prefer this when building many signers against one
65    /// endpoint — it avoids connection storms and repeated chain-id lookups.
66    pub fn client(mut self, client: Client) -> SigningClientBuilder {
67        self.endpoint = Some(Endpoint::Client(client));
68        return self;
69    }
70
71    /// Chain id to sign with. Omit to read it from the endpoint instead.
72    pub fn chain_id(mut self, chain_id: impl Into<String>) -> SigningClientBuilder {
73        self.chain_id = Some(chain_id.into());
74        return self;
75    }
76
77    /// # Errors
78    ///
79    /// Fails if no endpoint was set, or if the URL set is not a valid one.
80    pub fn build(self) -> anyhow::Result<SigningClient> {
81        let http_client = match self.endpoint {
82            Some(Endpoint::Client(client)) => client,
83            Some(Endpoint::Url(url)) => Client::new(url)?,
84            None => anyhow::bail!("signing client requires either `url` or `client`"),
85        };
86
87        return Ok(assemble(http_client, self.signer, self.chain_id));
88    }
89}
90
91/// The one place a [`SigningClient`] is assembled. `from_client` is infallible
92/// and so cannot route through [`SigningClientBuilder::build`]; both land here.
93pub(super) fn assemble(
94    http_client: Client,
95    signer: Signer,
96    chain_id: Option<String>,
97) -> SigningClient {
98    return SigningClient {
99        http_client,
100        signer,
101        chain_id,
102        nonce: Arc::new(Mutex::new(SigningClient::now())),
103    };
104}