Skip to main content

cw_orch_daemon/queriers/
cosmwasm.rs

1use std::{marker::PhantomData, str::FromStr};
2
3use crate::senders::query::QuerySender;
4use crate::senders::QueryOnlySender;
5use crate::{cosmos_modules, error::DaemonError, DaemonBase};
6use cosmrs::proto::cosmos::base::query::v1beta1::PageRequest;
7use cosmrs::AccountId;
8use cosmwasm_std::{
9    from_json, instantiate2_address, to_json_binary, Addr, Checksum, CodeInfoResponse,
10    ContractInfoResponse,
11};
12use cw_orch_core::environment::Environment;
13use cw_orch_core::{
14    contract::interface_traits::Uploadable,
15    environment::{Querier, QuerierGetter, WasmQuerier},
16};
17use tokio::runtime::Handle;
18use tonic::transport::Channel;
19
20/// Querier for the CosmWasm SDK module
21/// All the async function are prefixed with `_`
22pub struct CosmWasmBase<Sender = QueryOnlySender> {
23    pub channel: Channel,
24    pub rt_handle: Option<Handle>,
25    _sender: PhantomData<Sender>,
26}
27
28pub type CosmWasm = CosmWasmBase<QueryOnlySender>;
29
30impl<Sender: QuerySender> CosmWasmBase<Sender> {
31    pub fn new(daemon: &DaemonBase<Sender>) -> Self {
32        Self {
33            channel: daemon.channel(),
34            rt_handle: Some(daemon.rt_handle.clone()),
35            _sender: PhantomData,
36        }
37    }
38    pub fn new_async(channel: Channel) -> Self {
39        Self {
40            channel,
41            rt_handle: None,
42            _sender: PhantomData,
43        }
44    }
45    pub fn new_sync(channel: Channel, handle: &Handle) -> Self {
46        Self {
47            channel,
48            rt_handle: Some(handle.clone()),
49            _sender: PhantomData,
50        }
51    }
52}
53
54impl<Sender: QuerySender> QuerierGetter<CosmWasmBase<Sender>> for DaemonBase<Sender> {
55    fn querier(&self) -> CosmWasmBase<Sender> {
56        CosmWasmBase::new(self)
57    }
58}
59
60impl<Sender> Querier for CosmWasmBase<Sender> {
61    type Error = DaemonError;
62}
63
64impl<Sender: QuerySender> CosmWasmBase<Sender> {
65    /// Query code_id by hash
66    pub async fn _code_id_hash(&self, code_id: u64) -> Result<Checksum, DaemonError> {
67        use cosmos_modules::cosmwasm::{query_client::*, QueryCodeRequest};
68        let mut client: QueryClient<Channel> = QueryClient::new(self.channel.clone());
69        let request = QueryCodeRequest { code_id };
70        let resp = client.code(request).await?.into_inner();
71        let contract_hash = resp.code_info.unwrap().data_hash;
72        Ok(contract_hash.as_slice().try_into()?)
73    }
74
75    /// Query contract info
76    pub async fn _contract_info(
77        &self,
78        address: &Addr,
79    ) -> Result<ContractInfoResponse, DaemonError> {
80        use cosmos_modules::cosmwasm::{query_client::*, QueryContractInfoRequest};
81        let mut client: QueryClient<Channel> = QueryClient::new(self.channel.clone());
82        let request = QueryContractInfoRequest {
83            address: address.into(),
84        };
85        let resp = client.contract_info(request).await?.into_inner();
86        let contract_info = resp.contract_info.unwrap();
87
88        let c = ContractInfoResponse::new(
89            contract_info.code_id,
90            Addr::unchecked(&contract_info.creator),
91            if contract_info.admin.is_empty() {
92                None
93            } else {
94                Some(Addr::unchecked(contract_info.admin))
95            },
96            false,
97            if contract_info.ibc_port_id.is_empty() {
98                None
99            } else {
100                Some(contract_info.ibc_port_id)
101            },
102        );
103        Ok(c)
104    }
105
106    /// Query contract history
107    pub async fn _contract_history(
108        &self,
109        address: &Addr,
110        pagination: Option<PageRequest>,
111    ) -> Result<cosmos_modules::cosmwasm::QueryContractHistoryResponse, DaemonError> {
112        use cosmos_modules::cosmwasm::{query_client::*, QueryContractHistoryRequest};
113        let mut client: QueryClient<Channel> = QueryClient::new(self.channel.clone());
114        let request = QueryContractHistoryRequest {
115            address: address.into(),
116            pagination,
117        };
118        Ok(client.contract_history(request).await?.into_inner())
119    }
120
121    /// Query contract state
122    pub async fn _contract_state(
123        &self,
124        address: &Addr,
125        query_data: Vec<u8>,
126    ) -> Result<Vec<u8>, DaemonError> {
127        use cosmos_modules::cosmwasm::{query_client::*, QuerySmartContractStateRequest};
128        let mut client: QueryClient<Channel> = QueryClient::new(self.channel.clone());
129        let request = QuerySmartContractStateRequest {
130            address: address.into(),
131            query_data,
132        };
133        Ok(client
134            .smart_contract_state(request)
135            .await?
136            .into_inner()
137            .data)
138    }
139
140    /// Query all contract state
141    pub async fn _all_contract_state(
142        &self,
143        address: &Addr,
144        pagination: Option<PageRequest>,
145    ) -> Result<cosmos_modules::cosmwasm::QueryAllContractStateResponse, DaemonError> {
146        use cosmos_modules::cosmwasm::{query_client::*, QueryAllContractStateRequest};
147        let mut client: QueryClient<Channel> = QueryClient::new(self.channel.clone());
148        let request = QueryAllContractStateRequest {
149            address: address.into(),
150            pagination,
151        };
152        Ok(client.all_contract_state(request).await?.into_inner())
153    }
154
155    /// Query code
156    pub async fn _code(&self, code_id: u64) -> Result<CodeInfoResponse, DaemonError> {
157        use cosmos_modules::cosmwasm::{query_client::*, QueryCodeRequest};
158        let mut client: QueryClient<Channel> = QueryClient::new(self.channel.clone());
159        let request = QueryCodeRequest { code_id };
160        let response = client.code(request).await?.into_inner().code_info.unwrap();
161
162        Ok(cosmrs_to_cosmwasm_code_info(response))
163    }
164
165    /// Query code bytes
166    pub async fn _code_data(&self, code_id: u64) -> Result<Vec<u8>, DaemonError> {
167        use cosmos_modules::cosmwasm::{query_client::*, QueryCodeRequest};
168        let mut client: QueryClient<Channel> = QueryClient::new(self.channel.clone());
169        let request = QueryCodeRequest { code_id };
170        Ok(client.code(request).await?.into_inner().data)
171    }
172
173    /// Query codes
174    pub async fn _codes(
175        &self,
176        pagination: Option<PageRequest>,
177    ) -> Result<Vec<CodeInfoResponse>, DaemonError> {
178        use cosmos_modules::cosmwasm::{query_client::*, QueryCodesRequest};
179        let mut client: QueryClient<Channel> = QueryClient::new(self.channel.clone());
180        let request = QueryCodesRequest { pagination };
181        let response = client.codes(request).await?.into_inner().code_infos;
182
183        Ok(response
184            .into_iter()
185            .map(cosmrs_to_cosmwasm_code_info)
186            .collect())
187    }
188
189    /// Query pinned codes
190    pub async fn _pinned_codes(
191        &self,
192    ) -> Result<cosmos_modules::cosmwasm::QueryPinnedCodesResponse, DaemonError> {
193        use cosmos_modules::cosmwasm::{query_client::*, QueryPinnedCodesRequest};
194        let mut client: QueryClient<Channel> = QueryClient::new(self.channel.clone());
195        let request = QueryPinnedCodesRequest { pagination: None };
196        Ok(client.pinned_codes(request).await?.into_inner())
197    }
198
199    /// Query contracts by code
200    pub async fn _contract_by_codes(
201        &self,
202        code_id: u64,
203    ) -> Result<cosmos_modules::cosmwasm::QueryContractsByCodeResponse, DaemonError> {
204        use cosmos_modules::cosmwasm::{query_client::*, QueryContractsByCodeRequest};
205        let mut client: QueryClient<Channel> = QueryClient::new(self.channel.clone());
206        let request = QueryContractsByCodeRequest {
207            code_id,
208            pagination: None,
209        };
210        Ok(client.contracts_by_code(request).await?.into_inner())
211    }
212
213    /// Query raw contract state
214    pub async fn _contract_raw_state(
215        &self,
216        address: &Addr,
217        query_data: Vec<u8>,
218    ) -> Result<cosmos_modules::cosmwasm::QueryRawContractStateResponse, DaemonError> {
219        use cosmos_modules::cosmwasm::{query_client::*, QueryRawContractStateRequest};
220        let mut client: QueryClient<Channel> = QueryClient::new(self.channel.clone());
221        let request = QueryRawContractStateRequest {
222            address: address.into(),
223            query_data,
224        };
225        Ok(client.raw_contract_state(request).await?.into_inner())
226    }
227
228    /// Query params
229    pub async fn _params(
230        &self,
231    ) -> Result<cosmos_modules::cosmwasm::QueryParamsResponse, DaemonError> {
232        use cosmos_modules::cosmwasm::{query_client::*, QueryParamsRequest};
233        let mut client: QueryClient<Channel> = QueryClient::new(self.channel.clone());
234        Ok(client.params(QueryParamsRequest {}).await?.into_inner())
235    }
236}
237
238impl<Sender: QuerySender> WasmQuerier for CosmWasmBase<Sender> {
239    type Chain = DaemonBase<Sender>;
240    fn code_id_hash(&self, code_id: u64) -> Result<Checksum, Self::Error> {
241        self.rt_handle
242            .as_ref()
243            .ok_or(DaemonError::QuerierNeedRuntime)?
244            .block_on(self._code_id_hash(code_id))
245    }
246
247    fn contract_info(
248        &self,
249        address: &Addr,
250    ) -> Result<cosmwasm_std::ContractInfoResponse, Self::Error> {
251        self.rt_handle
252            .as_ref()
253            .ok_or(DaemonError::QuerierNeedRuntime)?
254            .block_on(self._contract_info(address))
255    }
256
257    fn raw_query(&self, address: &Addr, query_data: Vec<u8>) -> Result<Vec<u8>, Self::Error> {
258        let response = self
259            .rt_handle
260            .as_ref()
261            .ok_or(DaemonError::QuerierNeedRuntime)?
262            .block_on(self._contract_raw_state(address, query_data))?;
263
264        Ok(response.data)
265    }
266
267    fn smart_query<Q: serde::Serialize, T: serde::de::DeserializeOwned>(
268        &self,
269        address: &Addr,
270        query_data: &Q,
271    ) -> Result<T, Self::Error> {
272        let response = self
273            .rt_handle
274            .as_ref()
275            .ok_or(DaemonError::QuerierNeedRuntime)?
276            .block_on(self._contract_state(address, to_json_binary(&query_data)?.to_vec()))?;
277
278        Ok(from_json(response)?)
279    }
280
281    fn code(&self, code_id: u64) -> Result<cosmwasm_std::CodeInfoResponse, Self::Error> {
282        self.rt_handle
283            .as_ref()
284            .ok_or(DaemonError::QuerierNeedRuntime)?
285            .block_on(self._code(code_id))
286    }
287
288    fn instantiate2_addr(
289        &self,
290        code_id: u64,
291        creator: &Addr,
292        salt: cosmwasm_std::Binary,
293    ) -> Result<String, Self::Error> {
294        let account_id = AccountId::from_str(creator.as_str())?;
295        let prefix = account_id.prefix();
296        let canon = account_id.to_bytes();
297        let checksum = self.code_id_hash(code_id)?;
298        let addr = instantiate2_address(checksum.as_slice(), &canon.into(), &salt)?;
299
300        Ok(AccountId::new(prefix, addr.as_slice())?.to_string())
301    }
302
303    fn local_hash<
304        T: cw_orch_core::contract::interface_traits::Uploadable
305            + cw_orch_core::contract::interface_traits::ContractInstance<DaemonBase<Sender>>,
306    >(
307        &self,
308        contract: &T,
309    ) -> Result<Checksum, cw_orch_core::CwEnvError> {
310        <T as Uploadable>::wasm(contract.environment().daemon.chain_info()).checksum()
311    }
312}
313
314pub fn cosmrs_to_cosmwasm_code_info(
315    code_info: cosmrs::proto::cosmwasm::wasm::v1::CodeInfoResponse,
316) -> CodeInfoResponse {
317    let checksum: [u8; 32] = code_info.data_hash.try_into().unwrap();
318    CodeInfoResponse::new(
319        code_info.code_id,
320        Addr::unchecked(code_info.creator),
321        Checksum::from(checksum),
322    )
323}