layer_climb_core/
signing.rs

1pub mod contract;
2pub mod ibc;
3pub mod middleware;
4pub mod msg;
5
6use crate::{
7    cache::ClimbCache,
8    prelude::*,
9    querier::Connection,
10    transaction::{SequenceStrategy, SequenceStrategyKind},
11};
12use layer_climb_address::TxSigner;
13use middleware::{SigningMiddlewareMapBody, SigningMiddlewareMapResp};
14use std::sync::Arc;
15use tracing::instrument;
16
17// Cloning a SigningClient is pretty cheap
18#[derive(Clone)]
19pub struct SigningClient {
20    pub querier: QueryClient,
21    pub signer: Arc<dyn TxSigner>,
22    pub addr: Address,
23    pub account_number: u64,
24    /// Middleware to run before the tx is broadcast
25    pub middleware_map_body: Arc<Vec<SigningMiddlewareMapBody>>,
26    /// Middleware to run after the tx is broadcast
27    pub middleware_map_resp: Arc<Vec<SigningMiddlewareMapResp>>,
28    /// Strategy for determining the sequence number for txs
29    /// it will be applied when calling `tx_builder()`
30    /// (i.e. it's always possible to manually construct a TxBuilder and override it)
31    /// Default is `SequenceStrategyKind::Query`
32    pub sequence_strategy: SequenceStrategy,
33}
34
35impl std::fmt::Debug for SigningClient {
36    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
37        f.debug_struct("SigningClient")
38            .field("chain_id", &self.querier.chain_config.chain_id)
39            .field("addr", &self.addr)
40            .finish()
41    }
42}
43
44impl SigningClient {
45    pub async fn new(
46        chain_config: ChainConfig,
47        signer: impl TxSigner + 'static,
48        connection: Option<Connection>,
49    ) -> Result<Self> {
50        let connection = connection.unwrap_or_default();
51
52        Self::new_with_cache(
53            chain_config,
54            signer,
55            ClimbCache::new(connection.rpc.clone()),
56            Some(connection),
57        )
58        .await
59    }
60
61    pub async fn new_with_cache(
62        chain_config: ChainConfig,
63        signer: impl TxSigner + 'static,
64        cache: ClimbCache,
65        connection: Option<Connection>,
66    ) -> Result<Self> {
67        let addr = chain_config.address_from_pub_key(&signer.public_key().await?)?;
68
69        let querier = QueryClient::new_with_cache(chain_config.clone(), cache, connection).await?;
70
71        let base_account = querier.base_account(&addr).await?;
72
73        Ok(Self {
74            signer: Arc::new(signer),
75            querier,
76            addr,
77            account_number: base_account.account_number,
78            middleware_map_body: Arc::new(middleware::SigningMiddlewareMapBody::default_list()),
79            middleware_map_resp: Arc::new(middleware::SigningMiddlewareMapResp::default_list()),
80            sequence_strategy: SequenceStrategy::new(SequenceStrategyKind::Query),
81        })
82    }
83
84    // This is especially useful if the signer gets its public key at runtime
85    // such as when using browser-based async wallets like Keplr
86    #[instrument]
87    pub async fn refresh_signer(&mut self) -> Result<()> {
88        self.addr = self
89            .querier
90            .chain_config
91            .address_from_pub_key(&self.signer.public_key().await?)?;
92
93        self.account_number = self.querier.base_account(&self.addr).await?.account_number;
94
95        Ok(())
96    }
97
98    pub fn chain_id(&self) -> &ChainId {
99        &self.querier.chain_config.chain_id
100    }
101
102    pub fn sequence_strategy_kind(&self) -> &SequenceStrategyKind {
103        &self.sequence_strategy.kind
104    }
105
106    pub fn tx_builder(&self) -> TxBuilder<'_> {
107        let mut tx_builder = TxBuilder::new(&self.querier, self.signer.as_ref());
108
109        tx_builder
110            .set_sender(self.addr.clone())
111            .set_account_number(self.account_number)
112            .set_sequence_strategy(self.sequence_strategy.clone());
113
114        if !self.middleware_map_body.is_empty() {
115            tx_builder.set_middleware_map_body(self.middleware_map_body.clone());
116        }
117
118        if !self.middleware_map_resp.is_empty() {
119            tx_builder.set_middleware_map_resp(self.middleware_map_resp.clone());
120        }
121
122        tx_builder
123    }
124
125    pub async fn transfer<'a, D: Into<Option<&'a str>> + std::fmt::Debug>(
126        &self,
127        amount: u128,
128        recipient: &Address,
129        denom: D,
130        tx_builder: Option<TxBuilder<'_>>,
131    ) -> Result<layer_climb_proto::abci::TxResponse> {
132        tracing::debug!("transfering {} to {} from {}", amount, recipient, self.addr);
133
134        let res = tx_builder
135            .unwrap_or_else(|| self.tx_builder())
136            .broadcast([proto_into_any(
137                &self.transfer_msg(amount, recipient, denom)?,
138            )?])
139            .await?;
140
141        Ok(res)
142    }
143}