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
20pub 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 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 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 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 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 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 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 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 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 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 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 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 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}