osmo_reflect/
contract.rs

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/// This just stores the result for future query
107#[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        // we can just call .unwrap() to assert this was a success
195        let res = instantiate(deps.as_mut(), mock_env(), info, msg).unwrap();
196        assert_eq!(0, res.messages.len());
197
198        // it worked, let's query the state
199        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        // signer is not owner
235        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        // should change state
310        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        // with bank query
366        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        // TODO? or better in multitest?
378        // // with custom query
379        // let msg = QueryMsg::Chain {
380        //     request: OsmosisQuery::Ping {}.into(),
381        // };
382        // let response = query(deps.as_ref(), mock_env(), msg).unwrap();
383        // let outer: ChainResponse = from_binary(&response).unwrap();
384        // let inner: SpecialResponse = from_binary(&outer.data).unwrap();
385        // assert_eq!(inner.msg, "pong");
386    }
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    // this mocks out what happens after reflect_subcall
416    #[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        // query for a non-existant id
436        let qres = query(
437            deps.as_ref(),
438            mock_env(),
439            QueryMsg::SubMsgResult { id: 65432 },
440        );
441        assert!(qres.is_err());
442
443        // query for the real id
444        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}