layer_climb_core/querier/
contract.rs1use crate::{contract_helpers::contract_msg_to_vec, prelude::*};
2use layer_climb_address::{AddrKind, CosmosAddr};
3use serde::{de::DeserializeOwned, Serialize};
4use tracing::instrument;
5
6impl QueryClient {
7 #[instrument]
8 pub async fn contract_smart<
9 D: DeserializeOwned + Send + std::fmt::Debug + Sync,
10 S: Serialize + std::fmt::Debug,
11 >(
12 &self,
13 address: &Address,
14 msg: &S,
15 ) -> Result<D> {
16 self.run_with_middleware(ContractSmartReq {
17 address: address.clone(),
18 msg: contract_msg_to_vec(&msg)?,
19 _phantom: std::marker::PhantomData,
20 })
21 .await
22 }
23 #[instrument]
24 pub async fn contract_smart_raw<S: Serialize + std::fmt::Debug>(
25 &self,
26 address: &Address,
27 msg: &S,
28 ) -> Result<Vec<u8>> {
29 self.run_with_middleware(ContractSmartRawReq {
30 address: address.clone(),
31 msg: contract_msg_to_vec(&msg)?,
32 })
33 .await
34 }
35
36 #[instrument]
37 pub async fn contract_code_info(
38 &self,
39 code_id: u64,
40 ) -> Result<layer_climb_proto::wasm::CodeInfoResponse> {
41 self.run_with_middleware(ContractCodeInfoReq { code_id })
42 .await
43 }
44
45 #[instrument]
46 pub async fn contract_info(
47 &self,
48 address: &Address,
49 ) -> Result<layer_climb_proto::wasm::QueryContractInfoResponse> {
50 self.run_with_middleware(ContractInfoReq {
51 address: address.clone(),
52 })
53 .await
54 }
55
56 #[instrument]
57 pub async fn contract_predict_address(
58 &self,
59 code_id: u64,
60 creator: &Address,
61 salt: &[u8],
62 ) -> Result<Address> {
63 let code_info = self.contract_code_info(code_id).await?;
64
65 let checksum = {
66 let data_hash = code_info.data_hash;
67
68 if data_hash.len() != 32 {
69 bail!("Unexpected code data hash length");
70 }
71
72 let mut array = [0u8; 32];
73 array.copy_from_slice(&data_hash);
74 cosmwasm_std::Checksum::from(array)
75 };
76
77 let canonical_addr = cosmwasm_std::instantiate2_address(
78 checksum.as_slice(),
79 &creator.as_bytes().into(),
80 salt,
81 )?;
82
83 let human_addr = CosmosAddr::new_bytes(
84 canonical_addr.into(),
85 match &self.chain_config.address_kind {
86 AddrKind::Cosmos { prefix } => prefix,
87 AddrKind::Evm => {
88 bail!("Cannot convert to human address with EVM address kind");
89 }
90 },
91 )?;
92
93 Ok(human_addr.into())
94 }
95}
96
97#[derive(Debug)]
98struct ContractSmartReq<D> {
99 pub address: Address,
100 pub msg: Vec<u8>,
101 _phantom: std::marker::PhantomData<D>,
102}
103
104impl<D> Clone for ContractSmartReq<D> {
105 fn clone(&self) -> Self {
106 Self {
107 address: self.address.clone(),
108 msg: self.msg.clone(),
109 _phantom: std::marker::PhantomData,
110 }
111 }
112}
113
114impl<D: DeserializeOwned + Send + std::fmt::Debug + Sync> QueryRequest for ContractSmartReq<D> {
115 type QueryResponse = D;
116
117 async fn request(&self, client: QueryClient) -> Result<D> {
118 let res = ContractSmartRawReq {
119 address: self.address.clone(),
120 msg: self.msg.clone(),
121 }
122 .request(client)
123 .await?;
124
125 let res = cosmwasm_std::from_json(res)
126 .map_err(|e| anyhow::anyhow!("couldn't deserialize response {}", e))?;
127
128 Ok(res)
129 }
130}
131
132#[derive(Clone, Debug)]
133struct ContractSmartRawReq {
134 pub address: Address,
135 pub msg: Vec<u8>,
136}
137
138impl QueryRequest for ContractSmartRawReq {
139 type QueryResponse = Vec<u8>;
140
141 async fn request(&self, client: QueryClient) -> Result<Vec<u8>> {
142 let req = layer_climb_proto::wasm::QuerySmartContractStateRequest {
143 address: self.address.to_string(),
144 query_data: self.msg.clone(),
145 };
146
147 let res = match client.get_connection_mode() {
148 ConnectionMode::Grpc => {
149 let mut query_client = layer_climb_proto::wasm::query_client::QueryClient::new(
150 client.clone_grpc_channel()?,
151 );
152
153 query_client
154 .smart_contract_state(req)
155 .await
156 .map(|res| res.into_inner())?
157 }
158 ConnectionMode::Rpc => {
159 client
160 .rpc_client()?
161 .abci_protobuf_query("/cosmwasm.wasm.v1.Query/SmartContractState", req, None)
162 .await?
163 }
164 };
165
166 Ok(res.data)
167 }
168}
169
170#[derive(Clone, Debug)]
171struct ContractCodeInfoReq {
172 pub code_id: u64,
173}
174
175impl QueryRequest for ContractCodeInfoReq {
176 type QueryResponse = layer_climb_proto::wasm::CodeInfoResponse;
177
178 async fn request(
179 &self,
180 client: QueryClient,
181 ) -> Result<layer_climb_proto::wasm::CodeInfoResponse> {
182 let req = layer_climb_proto::wasm::QueryCodeRequest {
183 code_id: self.code_id,
184 };
185
186 let res = match client.get_connection_mode() {
187 ConnectionMode::Grpc => {
188 let mut query_client = layer_climb_proto::wasm::query_client::QueryClient::new(
189 client.clone_grpc_channel()?,
190 );
191
192 query_client.code(req).await.map(|res| res.into_inner())?
193 }
194 ConnectionMode::Rpc => {
195 client
196 .rpc_client()?
197 .abci_protobuf_query("/cosmwasm.wasm.v1.Query/Code", req, None)
198 .await?
199 }
200 };
201
202 res.code_info.context("no code info found")
203 }
204}
205
206#[derive(Clone, Debug)]
207pub struct ContractInfoReq {
208 pub address: Address,
209}
210
211impl QueryRequest for ContractInfoReq {
212 type QueryResponse = layer_climb_proto::wasm::QueryContractInfoResponse;
213
214 async fn request(
215 &self,
216 client: QueryClient,
217 ) -> Result<layer_climb_proto::wasm::QueryContractInfoResponse> {
218 let req = layer_climb_proto::wasm::QueryContractInfoRequest {
219 address: self.address.to_string(),
220 };
221
222 match client.get_connection_mode() {
223 ConnectionMode::Grpc => {
224 let mut query_client = layer_climb_proto::wasm::query_client::QueryClient::new(
225 client.clone_grpc_channel()?,
226 );
227
228 let resp = query_client
229 .contract_info(req)
230 .await
231 .map(|res| res.into_inner())?;
232
233 Ok(resp)
234 }
235 ConnectionMode::Rpc => {
236 let resp = client
237 .rpc_client()?
238 .abci_protobuf_query::<_, layer_climb_proto::wasm::QueryContractInfoResponse>(
239 "/cosmwasm.wasm.v1.Query/ContractInfo",
240 req,
241 None,
242 )
243 .await?;
244
245 Ok(resp)
246 }
247 }
248 }
249}