layer_climb_core/
signing.rs

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