1use serde::{de::DeserializeOwned, Serialize};
2
3use cosmwasm_std::{
4 to_binary, Coin, CosmosMsg, CustomQuery, QuerierWrapper, QueryRequest, StdResult, Uint128,
5 WasmMsg, WasmQuery,
6};
7
8use super::space_pad;
9
10pub trait InitCallback: Serialize {
15 const BLOCK_SIZE: usize;
17
18 fn to_cosmos_msg(
31 &self,
32 admin: Option<String>,
33 label: String,
34 code_id: u64,
35 code_hash: String,
36 funds_amount: Option<Uint128>,
37 ) -> StdResult<CosmosMsg> {
38 let mut msg = to_binary(self)?;
39 let padding = if Self::BLOCK_SIZE == 0 {
41 1
42 } else {
43 Self::BLOCK_SIZE
44 };
45 space_pad(&mut msg.0, padding);
46 let mut funds = Vec::new();
47 if let Some(amount) = funds_amount {
48 funds.push(Coin {
49 amount,
50 denom: String::from("uscrt"),
51 });
52 }
53 let init = WasmMsg::Instantiate {
54 admin,
55 code_id,
56 msg,
57 code_hash,
58 funds,
59 label,
60 };
61 Ok(init.into())
62 }
63}
64
65pub trait HandleCallback: Serialize {
70 const BLOCK_SIZE: usize;
72
73 fn to_cosmos_msg(
85 &self,
86 code_hash: String,
87 contract_addr: String,
88 funds_amount: Option<Uint128>,
89 ) -> StdResult<CosmosMsg> {
90 let mut msg = to_binary(self)?;
91 let padding = if Self::BLOCK_SIZE == 0 {
93 1
94 } else {
95 Self::BLOCK_SIZE
96 };
97 space_pad(&mut msg.0, padding);
98 let mut funds = Vec::new();
99 if let Some(amount) = funds_amount {
100 funds.push(Coin {
101 amount,
102 denom: String::from("uscrt"),
103 });
104 }
105 let execute = WasmMsg::Execute {
106 msg,
107 contract_addr,
108 code_hash,
109 funds,
110 };
111 Ok(execute.into())
112 }
113}
114
115pub trait Query: Serialize {
119 const BLOCK_SIZE: usize;
121
122 fn query<C: CustomQuery, T: DeserializeOwned>(
133 &self,
134 querier: QuerierWrapper<C>,
135 code_hash: String,
136 contract_addr: String,
137 ) -> StdResult<T> {
138 let mut msg = to_binary(self)?;
139 let padding = if Self::BLOCK_SIZE == 0 {
141 1
142 } else {
143 Self::BLOCK_SIZE
144 };
145 space_pad(&mut msg.0, padding);
146 querier.query(&QueryRequest::Wasm(WasmQuery::Smart {
147 contract_addr,
148 code_hash,
149 msg,
150 }))
151 }
152}
153
154#[cfg(test)]
155mod tests {
156 use super::*;
157 use cosmwasm_std::{
158 to_vec, Binary, ContractResult, Empty, Querier, QuerierResult, SystemError, SystemResult,
159 };
160 use serde::Deserialize;
161
162 #[derive(Serialize)]
163 struct FooInit {
164 pub f1: i8,
165 pub f2: i8,
166 }
167
168 impl InitCallback for FooInit {
169 const BLOCK_SIZE: usize = 256;
170 }
171
172 #[derive(Serialize)]
173 enum FooHandle {
174 Var1 { f1: i8, f2: i8 },
175 }
176
177 impl HandleCallback for FooHandle {
179 const BLOCK_SIZE: usize = 256;
180 }
181
182 #[derive(Serialize)]
183 enum FooQuery {
184 Query1 { f1: i8, f2: i8 },
185 }
186
187 impl Query for FooQuery {
188 const BLOCK_SIZE: usize = 256;
189 }
190
191 #[test]
192 fn test_handle_callback_implementation_works() -> StdResult<()> {
193 let address = "secret1xyzasdf".to_string();
194 let hash = "asdf".to_string();
195 let amount = Uint128::new(1234);
196
197 let cosmos_message: CosmosMsg = FooHandle::Var1 { f1: 1, f2: 2 }.to_cosmos_msg(
198 hash.clone(),
199 address.clone(),
200 Some(amount),
201 )?;
202
203 match cosmos_message {
204 CosmosMsg::Wasm(WasmMsg::Execute {
205 contract_addr,
206 code_hash,
207 msg,
208 funds,
209 }) => {
210 assert_eq!(contract_addr, address);
211 assert_eq!(code_hash, hash);
212 let mut expected_msg = r#"{"Var1":{"f1":1,"f2":2}}"#.as_bytes().to_vec();
213 space_pad(&mut expected_msg, 256);
214 assert_eq!(msg.0, expected_msg);
215 assert_eq!(funds, vec![Coin::new(amount.u128(), "uscrt")])
216 }
217 other => panic!("unexpected CosmosMsg variant: {:?}", other),
218 };
219
220 Ok(())
221 }
222
223 #[test]
224 fn test_init_callback_implementation_works() -> StdResult<()> {
225 let adm = "addr1".to_string();
226 let lbl = "testlabel".to_string();
227 let id = 17u64;
228 let hash = "asdf".to_string();
229 let amount = Uint128::new(1234);
230
231 let cosmos_message: CosmosMsg = FooInit { f1: 1, f2: 2 }.to_cosmos_msg(
232 Some(adm.clone()),
233 lbl.clone(),
234 id,
235 hash.clone(),
236 Some(amount),
237 )?;
238
239 match cosmos_message {
240 CosmosMsg::Wasm(WasmMsg::Instantiate {
241 admin,
242 code_id,
243 msg,
244 code_hash,
245 funds,
246 label,
247 }) => {
248 assert_eq!(admin, Some(adm));
249 assert_eq!(code_id, id);
250 let mut expected_msg = r#"{"f1":1,"f2":2}"#.as_bytes().to_vec();
251 space_pad(&mut expected_msg, 256);
252 assert_eq!(msg.0, expected_msg);
253 assert_eq!(code_hash, hash);
254 assert_eq!(funds, vec![Coin::new(amount.u128(), "uscrt")]);
255 assert_eq!(label, lbl)
256 }
257 other => panic!("unexpected CosmosMsg variant: {:?}", other),
258 };
259
260 Ok(())
261 }
262
263 #[test]
264 fn test_query_works() -> StdResult<()> {
265 #[derive(Serialize, Deserialize, PartialEq, Debug)]
266 struct QueryResponse {
267 bar1: i8,
268 bar2: i8,
269 }
270
271 struct MyMockQuerier {}
272
273 impl Querier for MyMockQuerier {
274 fn raw_query(&self, request: &[u8]) -> QuerierResult {
275 let mut expected_msg = r#"{"Query1":{"f1":1,"f2":2}}"#.as_bytes().to_vec();
276 space_pad(&mut expected_msg, 256);
277 let expected_request: QueryRequest<FooQuery> =
278 QueryRequest::Wasm(WasmQuery::Smart {
279 contract_addr: "secret1xyzasdf".to_string(),
280 code_hash: "asdf".to_string(),
281 msg: Binary(expected_msg),
282 });
283 let test_req: &[u8] = &to_vec(&expected_request).unwrap();
284 assert_eq!(request, test_req);
285 let response = match to_binary(&QueryResponse { bar1: 1, bar2: 2 }) {
286 Ok(response) => ContractResult::Ok(response),
287 Err(_e) => return SystemResult::Err(SystemError::Unknown {}),
288 };
289 SystemResult::Ok(response)
290 }
291 }
292
293 let querier = QuerierWrapper::<Empty>::new(&MyMockQuerier {});
294 let address = "secret1xyzasdf".to_string();
295 let hash = "asdf".to_string();
296
297 let response: QueryResponse =
298 FooQuery::Query1 { f1: 1, f2: 2 }.query(querier, hash, address)?;
299 assert_eq!(response, QueryResponse { bar1: 1, bar2: 2 });
300
301 Ok(())
302 }
303}