Skip to main content

cw_orch_daemon/sync/
core.rs

1use std::{fmt::Debug, ops::DerefMut};
2
3use super::super::senders::Wallet;
4use crate::{
5    queriers::{Bank, CosmWasmBase, Node},
6    senders::{builder::SenderBuilder, query::QuerySender},
7    CosmTxResponse, DaemonAsyncBase, DaemonBuilder, DaemonError, DaemonState,
8};
9use cosmwasm_std::{Addr, Coin};
10use cw_orch_core::{
11    contract::{interface_traits::Uploadable, WasmPath},
12    environment::{ChainInfoOwned, ChainState, DefaultQueriers, QueryHandler, TxHandler},
13};
14use cw_orch_traits::stargate::Stargate;
15use serde::Serialize;
16use tokio::runtime::Handle;
17use tonic::transport::Channel;
18
19use crate::senders::tx::TxSender;
20
21pub type Daemon = DaemonBase<Wallet>;
22
23#[derive(Clone)]
24/**
25Represents a blockchain node.
26Is constructed with the [DaemonBuilder].
27
28## Usage
29
30```rust,no_run
31use cw_orch_daemon::{Daemon, networks};
32use tokio::runtime::Runtime;
33
34let rt = Runtime::new().unwrap();
35let daemon: Daemon = Daemon::builder(networks::JUNO_1)
36    .build()
37    .unwrap();
38```
39## Environment Execution
40
41The Daemon implements [`TxHandler`] which allows you to perform transactions on the chain.
42
43## Querying
44
45Different Cosmos SDK modules can be queried through the daemon by calling the [`Daemon.query_client<Querier>`] method with a specific querier.
46See [Querier](crate::queriers) for examples.
47*/
48pub struct DaemonBase<Sender> {
49    pub daemon: DaemonAsyncBase<Sender>,
50    /// Runtime handle to execute async tasks
51    pub rt_handle: Handle,
52}
53
54impl<Sender> DaemonBase<Sender> {
55    /// Get the daemon builder
56    pub fn builder(chain: impl Into<ChainInfoOwned>) -> DaemonBuilder {
57        DaemonBuilder::new(chain)
58    }
59
60    /// Get the mutable Sender object
61    pub fn sender_mut(&mut self) -> &mut Sender {
62        self.daemon.sender_mut()
63    }
64
65    /// Get a read-only Sender
66    pub fn sender(&self) -> &Sender {
67        self.daemon.sender()
68    }
69
70    /// Set the Sender for use with this Daemon
71    /// The sender will be configured with the chain's data.
72    pub fn new_sender<T: SenderBuilder>(
73        self,
74        sender_options: T,
75    ) -> DaemonBase<<T as SenderBuilder>::Sender> {
76        let new_daemon = self
77            .rt_handle
78            .block_on(self.daemon.new_sender(sender_options));
79        DaemonBase {
80            daemon: new_daemon,
81            rt_handle: self.rt_handle.clone(),
82        }
83    }
84
85    /// Flushes all the state related to the current chain
86    /// Only works on Local networks
87    pub fn flush_state(&mut self) -> Result<(), DaemonError> {
88        self.daemon.flush_state()
89    }
90
91    /// Return the chain info for this daemon
92    pub fn chain_info(&self) -> &ChainInfoOwned {
93        self.daemon.chain_info()
94    }
95}
96
97impl<Sender: QuerySender> DaemonBase<Sender> {
98    /// Get the channel configured for this Daemon
99    pub fn channel(&self) -> Channel {
100        self.daemon.sender().channel()
101    }
102
103    /// Returns a new [`DaemonBuilder`] with the current configuration.
104    /// **Does not copy the `Sender`**
105    /// Does not consume the original [`Daemon`].
106    pub fn rebuild(&self) -> DaemonBuilder {
107        DaemonBuilder {
108            state: Some(self.state()),
109            chain: self.daemon.chain_info().clone(),
110            deployment_id: Some(self.daemon.state.deployment_id.clone()),
111            state_path: None,
112            write_on_change: None,
113            handle: Some(self.rt_handle.clone()),
114            mnemonic: None,
115            // If it was test it will just use same tempfile as state
116            is_test: false,
117            // Uses same ChainInfo
118            load_network: false,
119        }
120    }
121}
122
123// Helpers for Daemon with [`Wallet`] sender.
124impl Daemon {
125    #[deprecated = "Use `self.sender_mut().set_authz_granter(granter)` or change the sender builder options instead"]
126    /// Specifies wether authz should be used with this daemon
127    pub fn authz_granter(&mut self, granter: &Addr) -> &mut Self {
128        self.sender_mut().set_authz_granter(granter);
129        self
130    }
131
132    #[deprecated = "Use `self.sender_mut().set_fee_granter(granter)` or change the sender builder options instead"]
133    /// Specifies wether feegrant should be used with this daemon
134    pub fn fee_granter(&mut self, granter: &Addr) -> &mut Self {
135        self.sender_mut().set_fee_granter(granter);
136        self
137    }
138}
139
140impl<Sender> ChainState for DaemonBase<Sender> {
141    type Out = DaemonState;
142
143    fn state(&self) -> Self::Out {
144        self.daemon.state.clone()
145    }
146
147    fn can_load_state_from_state_file(&self) -> bool {
148        true
149    }
150}
151
152// Execute on the real chain, returns tx response
153impl<Sender: TxSender> TxHandler for DaemonBase<Sender> {
154    type Response = CosmTxResponse;
155    type Error = DaemonError;
156    type ContractSource = WasmPath;
157    type Sender = Sender;
158
159    fn sender(&self) -> &Self::Sender {
160        self.daemon.sender()
161    }
162
163    fn sender_addr(&self) -> Addr {
164        self.daemon.sender_addr()
165    }
166
167    /// Overwrite the sender manually, could result in unexpected behavior.
168    /// Use native [`Daemon::new_sender`] instead!
169    fn set_sender(&mut self, sender: Self::Sender) {
170        let mut daemon_sender = self.daemon.sender_mut();
171        (*daemon_sender.deref_mut()) = sender;
172    }
173
174    fn upload<T: Uploadable>(&self, uploadable: &T) -> Result<Self::Response, DaemonError> {
175        self.rt_handle.block_on(self.daemon.upload(uploadable))
176    }
177
178    fn execute<E: Serialize>(
179        &self,
180        exec_msg: &E,
181        coins: &[cosmwasm_std::Coin],
182        contract_address: &Addr,
183    ) -> Result<Self::Response, DaemonError> {
184        self.rt_handle
185            .block_on(self.daemon.execute(exec_msg, coins, contract_address))
186    }
187
188    fn instantiate<I: Serialize + Debug>(
189        &self,
190        code_id: u64,
191        init_msg: &I,
192        label: Option<&str>,
193        admin: Option<&Addr>,
194        coins: &[Coin],
195    ) -> Result<Self::Response, DaemonError> {
196        self.rt_handle.block_on(
197            self.daemon
198                .instantiate(code_id, init_msg, label, admin, coins),
199        )
200    }
201
202    fn migrate<M: Serialize + Debug>(
203        &self,
204        migrate_msg: &M,
205        new_code_id: u64,
206        contract_address: &Addr,
207    ) -> Result<Self::Response, DaemonError> {
208        self.rt_handle.block_on(
209            self.daemon
210                .migrate(migrate_msg, new_code_id, contract_address),
211        )
212    }
213
214    fn instantiate2<I: Serialize + Debug>(
215        &self,
216        code_id: u64,
217        init_msg: &I,
218        label: Option<&str>,
219        admin: Option<&Addr>,
220        coins: &[cosmwasm_std::Coin],
221        salt: cosmwasm_std::Binary,
222    ) -> Result<Self::Response, Self::Error> {
223        self.rt_handle.block_on(
224            self.daemon
225                .instantiate2(code_id, init_msg, label, admin, coins, salt),
226        )
227    }
228
229    fn upload_with_access_config<T: Uploadable>(
230        &self,
231        contract_source: &T,
232        access_config: Option<cw_orch_core::environment::AccessConfig>,
233    ) -> Result<Self::Response, Self::Error> {
234        self.rt_handle.block_on(
235            self.daemon
236                .upload_with_access_config(contract_source, access_config),
237        )
238    }
239
240    fn bank_send(
241        &self,
242        receiver: &Addr,
243        amount: &[cosmwasm_std::Coin],
244    ) -> Result<Self::Response, Self::Error> {
245        self.rt_handle
246            .block_on(self.sender().bank_send(receiver, amount))
247            .map_err(Into::into)
248            .map(Into::into)
249    }
250}
251
252impl<Sender: TxSender> Stargate for DaemonBase<Sender> {
253    fn commit_any(
254        &self,
255        msgs: Vec<prost_types::Any>,
256        memo: Option<&str>,
257    ) -> Result<Self::Response, Self::Error> {
258        self.rt_handle
259            .block_on(
260                self.sender().commit_tx_any(
261                    msgs.iter()
262                        .map(|msg| cosmrs::Any {
263                            type_url: msg.type_url.clone(),
264                            value: msg.value.clone(),
265                        })
266                        .collect(),
267                    memo,
268                ),
269            )
270            .map_err(Into::into)
271    }
272}
273
274impl<Sender: QuerySender> QueryHandler for DaemonBase<Sender> {
275    type Error = DaemonError;
276
277    fn wait_blocks(&self, amount: u64) -> Result<(), DaemonError> {
278        self.rt_handle.block_on(self.daemon.wait_blocks(amount))?;
279
280        Ok(())
281    }
282
283    fn wait_seconds(&self, secs: u64) -> Result<(), DaemonError> {
284        self.rt_handle.block_on(self.daemon.wait_seconds(secs))?;
285
286        Ok(())
287    }
288
289    fn next_block(&self) -> Result<(), DaemonError> {
290        self.rt_handle.block_on(self.daemon.next_block())?;
291
292        Ok(())
293    }
294}
295
296impl<Sender: QuerySender> DefaultQueriers for DaemonBase<Sender> {
297    type Bank = Bank;
298    type Wasm = CosmWasmBase<Sender>;
299    type Node = Node;
300}