1use cosmwasm_std::{
2 entry_point, to_binary, to_vec, ContractResult, CosmosMsg, Deps, DepsMut, Env, MessageInfo,
3 QueryRequest, QueryResponse, Reply, Response, StdError, StdResult, SubMsg, SystemResult,
4};
5use osmo_bindings::{OsmosisMsg, OsmosisQuery};
6
7use crate::errors::ReflectError;
8use crate::msg::{ChainResponse, ExecuteMsg, InstantiateMsg, OwnerResponse, QueryMsg};
9use crate::state::{config, config_read, replies, replies_read, State};
10
11#[entry_point]
12pub fn instantiate(
13 deps: DepsMut<OsmosisQuery>,
14 _env: Env,
15 info: MessageInfo,
16 _msg: InstantiateMsg,
17) -> StdResult<Response<OsmosisMsg>> {
18 let state = State { owner: info.sender };
19 config(deps.storage).save(&state)?;
20 Ok(Response::default())
21}
22
23#[entry_point]
24pub fn execute(
25 deps: DepsMut<OsmosisQuery>,
26 env: Env,
27 info: MessageInfo,
28 msg: ExecuteMsg,
29) -> Result<Response<OsmosisMsg>, ReflectError> {
30 match msg {
31 ExecuteMsg::ReflectMsg { msgs } => execute_reflect(deps, env, info, msgs),
32 ExecuteMsg::ReflectSubMsg { msgs } => execute_reflect_subcall(deps, env, info, msgs),
33 ExecuteMsg::ChangeOwner { owner } => execute_change_owner(deps, env, info, owner),
34 }
35}
36
37pub fn execute_reflect(
38 deps: DepsMut<OsmosisQuery>,
39 _env: Env,
40 info: MessageInfo,
41 msgs: Vec<CosmosMsg<OsmosisMsg>>,
42) -> Result<Response<OsmosisMsg>, ReflectError> {
43 let state = config(deps.storage).load()?;
44
45 if info.sender != state.owner {
46 return Err(ReflectError::NotCurrentOwner {
47 expected: state.owner.into(),
48 actual: info.sender.into(),
49 });
50 }
51
52 if msgs.is_empty() {
53 return Err(ReflectError::MessagesEmpty);
54 }
55
56 Ok(Response::new()
57 .add_attribute("action", "reflect")
58 .add_messages(msgs))
59}
60
61pub fn execute_reflect_subcall(
62 deps: DepsMut<OsmosisQuery>,
63 _env: Env,
64 info: MessageInfo,
65 msgs: Vec<SubMsg<OsmosisMsg>>,
66) -> Result<Response<OsmosisMsg>, ReflectError> {
67 let state = config(deps.storage).load()?;
68 if info.sender != state.owner {
69 return Err(ReflectError::NotCurrentOwner {
70 expected: state.owner.into(),
71 actual: info.sender.into(),
72 });
73 }
74
75 if msgs.is_empty() {
76 return Err(ReflectError::MessagesEmpty);
77 }
78
79 Ok(Response::new()
80 .add_attribute("action", "reflect_subcall")
81 .add_submessages(msgs))
82}
83
84pub fn execute_change_owner(
85 deps: DepsMut<OsmosisQuery>,
86 _env: Env,
87 info: MessageInfo,
88 new_owner: String,
89) -> Result<Response<OsmosisMsg>, ReflectError> {
90 let api = deps.api;
91 config(deps.storage).update(|mut state| {
92 if info.sender != state.owner {
93 return Err(ReflectError::NotCurrentOwner {
94 expected: state.owner.into(),
95 actual: info.sender.into(),
96 });
97 }
98 state.owner = api.addr_validate(&new_owner)?;
99 Ok(state)
100 })?;
101 Ok(Response::new()
102 .add_attribute("action", "change_owner")
103 .add_attribute("owner", new_owner))
104}
105
106#[entry_point]
108pub fn reply(deps: DepsMut<OsmosisQuery>, _env: Env, msg: Reply) -> Result<Response, ReflectError> {
109 let key = msg.id.to_be_bytes();
110 replies(deps.storage).save(&key, &msg)?;
111 Ok(Response::default())
112}
113
114#[entry_point]
115pub fn query(deps: Deps<OsmosisQuery>, _env: Env, msg: QueryMsg) -> StdResult<QueryResponse> {
116 match msg {
117 QueryMsg::Owner {} => to_binary(&query_owner(deps)?),
118 QueryMsg::Chain { request } => to_binary(&query_chain(deps, &request)?),
119 QueryMsg::SubMsgResult { id } => to_binary(&query_subcall(deps, id)?),
120 }
121}
122
123fn query_owner(deps: Deps<OsmosisQuery>) -> StdResult<OwnerResponse> {
124 let state = config_read(deps.storage).load()?;
125 let resp = OwnerResponse {
126 owner: state.owner.into(),
127 };
128 Ok(resp)
129}
130
131fn query_subcall(deps: Deps<OsmosisQuery>, id: u64) -> StdResult<Reply> {
132 let key = id.to_be_bytes();
133 replies_read(deps.storage).load(&key)
134}
135
136fn query_chain(
137 deps: Deps<OsmosisQuery>,
138 request: &QueryRequest<OsmosisQuery>,
139) -> StdResult<ChainResponse> {
140 let raw = to_vec(request).map_err(|serialize_err| {
141 StdError::generic_err(format!("Serializing QueryRequest: {}", serialize_err))
142 })?;
143 match deps.querier.raw_query(&raw) {
144 SystemResult::Err(system_err) => Err(StdError::generic_err(format!(
145 "Querier system error: {}",
146 system_err
147 ))),
148 SystemResult::Ok(ContractResult::Err(contract_err)) => Err(StdError::generic_err(format!(
149 "Querier contract error: {}",
150 contract_err
151 ))),
152 SystemResult::Ok(ContractResult::Ok(value)) => Ok(ChainResponse { data: value }),
153 }
154}
155
156#[cfg(test)]
157mod tests {
158 use super::*;
159 use cosmwasm_std::testing::{
160 mock_env, mock_info, MockApi, MockQuerier, MockStorage, MOCK_CONTRACT_ADDR,
161 };
162 use cosmwasm_std::{
163 coin, coins, from_binary, AllBalanceResponse, BankMsg, BankQuery, Binary, Coin, Event,
164 StakingMsg, StdError, SubMsgExecutionResponse,
165 };
166 use cosmwasm_std::{OwnedDeps, SubMsgResult, SystemError};
167 use std::marker::PhantomData;
168
169 pub fn mock_dependencies(
170 contract_balance: &[Coin],
171 ) -> OwnedDeps<MockStorage, MockApi, MockQuerier<OsmosisQuery>, OsmosisQuery> {
172 let custom_querier: MockQuerier<OsmosisQuery> =
173 MockQuerier::new(&[(MOCK_CONTRACT_ADDR, contract_balance)]).with_custom_handler(|_| {
174 SystemResult::Err(SystemError::InvalidRequest {
175 error: "not implemented".to_string(),
176 request: Default::default(),
177 })
178 });
179 OwnedDeps {
180 storage: MockStorage::default(),
181 api: MockApi::default(),
182 querier: custom_querier,
183 custom_query_type: PhantomData,
184 }
185 }
186
187 #[test]
188 fn proper_instantialization() {
189 let mut deps = mock_dependencies(&[]);
190
191 let msg = InstantiateMsg {};
192 let info = mock_info("creator", &coins(1000, "earth"));
193
194 let res = instantiate(deps.as_mut(), mock_env(), info, msg).unwrap();
196 assert_eq!(0, res.messages.len());
197
198 let value = query_owner(deps.as_ref()).unwrap();
200 assert_eq!("creator", value.owner.as_str());
201 }
202
203 #[test]
204 fn reflect() {
205 let mut deps = mock_dependencies(&[]);
206
207 let msg = InstantiateMsg {};
208 let info = mock_info("creator", &coins(2, "token"));
209 let _res = instantiate(deps.as_mut(), mock_env(), info, msg).unwrap();
210
211 let payload = vec![BankMsg::Send {
212 to_address: String::from("friend"),
213 amount: coins(1, "token"),
214 }
215 .into()];
216
217 let msg = ExecuteMsg::ReflectMsg {
218 msgs: payload.clone(),
219 };
220 let info = mock_info("creator", &[]);
221 let res = execute(deps.as_mut(), mock_env(), info, msg).unwrap();
222 let payload: Vec<_> = payload.into_iter().map(SubMsg::new).collect();
223 assert_eq!(payload, res.messages);
224 }
225
226 #[test]
227 fn reflect_requires_owner() {
228 let mut deps = mock_dependencies(&[]);
229
230 let msg = InstantiateMsg {};
231 let info = mock_info("creator", &coins(2, "token"));
232 let _res = instantiate(deps.as_mut(), mock_env(), info, msg).unwrap();
233
234 let payload = vec![BankMsg::Send {
236 to_address: String::from("friend"),
237 amount: coins(1, "token"),
238 }
239 .into()];
240 let msg = ExecuteMsg::ReflectMsg { msgs: payload };
241
242 let info = mock_info("random", &[]);
243 let res = execute(deps.as_mut(), mock_env(), info, msg);
244 match res.unwrap_err() {
245 ReflectError::NotCurrentOwner { .. } => {}
246 err => panic!("Unexpected error: {:?}", err),
247 }
248 }
249
250 #[test]
251 fn reflect_reject_empty_msgs() {
252 let mut deps = mock_dependencies(&[]);
253
254 let msg = InstantiateMsg {};
255 let info = mock_info("creator", &coins(2, "token"));
256 let _res = instantiate(deps.as_mut(), mock_env(), info, msg).unwrap();
257
258 let info = mock_info("creator", &[]);
259 let payload = vec![];
260
261 let msg = ExecuteMsg::ReflectMsg { msgs: payload };
262 let err = execute(deps.as_mut(), mock_env(), info, msg).unwrap_err();
263 assert_eq!(err, ReflectError::MessagesEmpty);
264 }
265
266 #[test]
267 fn reflect_multiple_messages() {
268 let mut deps = mock_dependencies(&[]);
269
270 let msg = InstantiateMsg {};
271 let info = mock_info("creator", &coins(2, "token"));
272 let _res = instantiate(deps.as_mut(), mock_env(), info, msg).unwrap();
273
274 let payload = vec![
275 BankMsg::Send {
276 to_address: String::from("friend"),
277 amount: coins(1, "token"),
278 }
279 .into(),
280 StakingMsg::Delegate {
281 validator: String::from("validator"),
282 amount: coin(100, "ustake"),
283 }
284 .into(),
285 ];
286
287 let msg = ExecuteMsg::ReflectMsg {
288 msgs: payload.clone(),
289 };
290 let info = mock_info("creator", &[]);
291 let res = execute(deps.as_mut(), mock_env(), info, msg).unwrap();
292 let payload: Vec<_> = payload.into_iter().map(SubMsg::new).collect();
293 assert_eq!(payload, res.messages);
294 }
295
296 #[test]
297 fn change_owner_works() {
298 let mut deps = mock_dependencies(&[]);
299
300 let msg = InstantiateMsg {};
301 let info = mock_info("creator", &coins(2, "token"));
302 let _res = instantiate(deps.as_mut(), mock_env(), info, msg).unwrap();
303
304 let info = mock_info("creator", &[]);
305 let new_owner = String::from("friend");
306 let msg = ExecuteMsg::ChangeOwner { owner: new_owner };
307 let res = execute(deps.as_mut(), mock_env(), info, msg).unwrap();
308
309 assert_eq!(0, res.messages.len());
311 let value = query_owner(deps.as_ref()).unwrap();
312 assert_eq!("friend", value.owner.as_str());
313 }
314
315 #[test]
316 fn change_owner_requires_current_owner_as_sender() {
317 let mut deps = mock_dependencies(&[]);
318
319 let msg = InstantiateMsg {};
320 let creator = String::from("creator");
321 let info = mock_info(&creator, &coins(2, "token"));
322 let _res = instantiate(deps.as_mut(), mock_env(), info, msg).unwrap();
323
324 let random = String::from("random");
325 let info = mock_info(&random, &[]);
326 let new_owner = String::from("friend");
327 let msg = ExecuteMsg::ChangeOwner { owner: new_owner };
328
329 let err = execute(deps.as_mut(), mock_env(), info, msg).unwrap_err();
330 assert_eq!(
331 err,
332 ReflectError::NotCurrentOwner {
333 expected: creator,
334 actual: random
335 }
336 );
337 }
338
339 #[test]
340 fn change_owner_errors_for_invalid_new_address() {
341 let mut deps = mock_dependencies(&[]);
342 let creator = String::from("creator");
343
344 let msg = InstantiateMsg {};
345 let info = mock_info(&creator, &coins(2, "token"));
346 let _res = instantiate(deps.as_mut(), mock_env(), info, msg).unwrap();
347
348 let info = mock_info(&creator, &[]);
349 let msg = ExecuteMsg::ChangeOwner {
350 owner: String::from("x"),
351 };
352 let err = execute(deps.as_mut(), mock_env(), info, msg).unwrap_err();
353 match err {
354 ReflectError::Std(StdError::GenericErr { msg, .. }) => {
355 assert!(msg.contains("human address too short"))
356 }
357 e => panic!("Unexpected error: {:?}", e),
358 }
359 }
360
361 #[test]
362 fn chain_query_works() {
363 let deps = mock_dependencies(&coins(123, "ucosm"));
364
365 let msg = QueryMsg::Chain {
367 request: BankQuery::AllBalances {
368 address: MOCK_CONTRACT_ADDR.to_string(),
369 }
370 .into(),
371 };
372 let response = query(deps.as_ref(), mock_env(), msg).unwrap();
373 let outer: ChainResponse = from_binary(&response).unwrap();
374 let inner: AllBalanceResponse = from_binary(&outer.data).unwrap();
375 assert_eq!(inner.amount, coins(123, "ucosm"));
376
377 }
387
388 #[test]
389 fn reflect_subcall() {
390 let mut deps = mock_dependencies(&[]);
391
392 let msg = InstantiateMsg {};
393 let info = mock_info("creator", &coins(2, "token"));
394 let _res = instantiate(deps.as_mut(), mock_env(), info, msg).unwrap();
395
396 let id = 123u64;
397 let payload = SubMsg::reply_always(
398 BankMsg::Send {
399 to_address: String::from("friend"),
400 amount: coins(1, "token"),
401 },
402 id,
403 );
404
405 let msg = ExecuteMsg::ReflectSubMsg {
406 msgs: vec![payload.clone()],
407 };
408 let info = mock_info("creator", &[]);
409 let mut res = execute(deps.as_mut(), mock_env(), info, msg).unwrap();
410 assert_eq!(1, res.messages.len());
411 let msg = res.messages.pop().expect("must have a message");
412 assert_eq!(payload, msg);
413 }
414
415 #[test]
417 fn reply_and_query() {
418 let mut deps = mock_dependencies(&[]);
419
420 let msg = InstantiateMsg {};
421 let info = mock_info("creator", &coins(2, "token"));
422 let _res = instantiate(deps.as_mut(), mock_env(), info, msg).unwrap();
423
424 let id = 123u64;
425 let data = Binary::from(b"foobar");
426 let events = vec![Event::new("message").add_attribute("signer", "caller-addr")];
427 let result = SubMsgResult::Ok(SubMsgExecutionResponse {
428 events: events.clone(),
429 data: Some(data.clone()),
430 });
431 let subcall = Reply { id, result };
432 let res = reply(deps.as_mut(), mock_env(), subcall).unwrap();
433 assert_eq!(0, res.messages.len());
434
435 let qres = query(
437 deps.as_ref(),
438 mock_env(),
439 QueryMsg::SubMsgResult { id: 65432 },
440 );
441 assert!(qres.is_err());
442
443 let raw = query(deps.as_ref(), mock_env(), QueryMsg::SubMsgResult { id }).unwrap();
445 let qres: Reply = from_binary(&raw).unwrap();
446 assert_eq!(qres.id, id);
447 let result = qres.result.unwrap();
448 assert_eq!(result.data, Some(data));
449 assert_eq!(result.events, events);
450 }
451}