layer_climb_core/querier/
contract.rs

1use 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}