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