1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
use crate::error::AnyResult;
use cosmwasm_std::{
    to_json_binary, Addr, Attribute, BankMsg, Binary, Coin, CosmosMsg, Event, SubMsgResponse,
    WasmMsg,
};
use cw_utils::{parse_execute_response_data, parse_instantiate_response_data};
use schemars::JsonSchema;
use serde::Serialize;
use std::fmt::Debug;

#[derive(Default, Clone, Debug)]
pub struct AppResponse {
    pub events: Vec<Event>,
    pub data: Option<Binary>,
}

impl AppResponse {
    // Return all custom attributes returned by the contract in the `idx` event.
    // We assert the type is wasm, and skip the contract_address attribute.
    #[track_caller]
    pub fn custom_attrs(&self, idx: usize) -> &[Attribute] {
        assert_eq!(self.events[idx].ty.as_str(), "wasm");
        &self.events[idx].attributes[1..]
    }

    /// Check if there is an Event that is a super-set of this.
    /// It has the same type, and all compare.attributes are included in it as well.
    /// You don't need to specify them all.
    pub fn has_event(&self, expected: &Event) -> bool {
        self.events.iter().any(|ev| {
            expected.ty == ev.ty
                && expected
                    .attributes
                    .iter()
                    .all(|at| ev.attributes.contains(at))
        })
    }

    /// Like has_event but panics if no match
    #[track_caller]
    pub fn assert_event(&self, expected: &Event) {
        assert!(
            self.has_event(expected),
            "Expected to find an event {:?}, but received: {:?}",
            expected,
            self.events
        );
    }
}

/// They have the same shape, SubMsgExecutionResponse is what is returned in reply.
/// This is just to make some test cases easier.
impl From<SubMsgResponse> for AppResponse {
    fn from(reply: SubMsgResponse) -> Self {
        AppResponse {
            data: reply.data,
            events: reply.events,
        }
    }
}

pub trait Executor<C>
where
    C: Clone + Debug + PartialEq + JsonSchema + 'static,
{
    /// Runs arbitrary CosmosMsg.
    /// This will create a cache before the execution, so no state changes are persisted if this
    /// returns an error, but all are persisted on success.
    fn execute(&mut self, sender: Addr, msg: CosmosMsg<C>) -> AnyResult<AppResponse>;

    /// Create a contract and get the new address.
    /// This is just a helper around execute()
    fn instantiate_contract<T: Serialize, U: Into<String>>(
        &mut self,
        code_id: u64,
        sender: Addr,
        init_msg: &T,
        send_funds: &[Coin],
        label: U,
        admin: Option<String>,
    ) -> AnyResult<Addr> {
        // instantiate contract
        let init_msg = to_json_binary(init_msg)?;
        let msg = WasmMsg::Instantiate {
            admin,
            code_id,
            msg: init_msg,
            funds: send_funds.to_vec(),
            label: label.into(),
        };
        let res = self.execute(sender, msg.into())?;
        let data = parse_instantiate_response_data(res.data.unwrap_or_default().as_slice())?;
        Ok(Addr::unchecked(data.contract_address))
    }

    /// Instantiates a new contract and returns its predictable address.
    /// This is a helper function around [execute][Self::execute] function
    /// with `WasmMsg::Instantiate2` message.
    #[cfg(feature = "cosmwasm_1_2")]
    fn instantiate2_contract<M, L, A, S>(
        &mut self,
        code_id: u64,
        sender: Addr,
        init_msg: &M,
        funds: &[Coin],
        label: L,
        admin: A,
        salt: S,
    ) -> AnyResult<Addr>
    where
        M: Serialize,
        L: Into<String>,
        A: Into<Option<String>>,
        S: Into<Binary>,
    {
        let msg = WasmMsg::Instantiate2 {
            admin: admin.into(),
            code_id,
            msg: to_json_binary(init_msg)?,
            funds: funds.to_vec(),
            label: label.into(),
            salt: salt.into(),
        };
        let execute_response = self.execute(sender, msg.into())?;
        let instantiate_response =
            parse_instantiate_response_data(execute_response.data.unwrap_or_default().as_slice())?;
        Ok(Addr::unchecked(instantiate_response.contract_address))
    }

    /// Execute a contract and process all returned messages.
    /// This is just a helper around execute(),
    /// but we parse out the data field to that what is returned by the contract (not the protobuf wrapper)
    fn execute_contract<T: Serialize + Debug>(
        &mut self,
        sender: Addr,
        contract_addr: Addr,
        msg: &T,
        send_funds: &[Coin],
    ) -> AnyResult<AppResponse> {
        let binary_msg = to_json_binary(msg)?;
        let wrapped_msg = WasmMsg::Execute {
            contract_addr: contract_addr.into_string(),
            msg: binary_msg,
            funds: send_funds.to_vec(),
        };
        let mut res = self.execute(sender, wrapped_msg.into())?;
        res.data = res
            .data
            .and_then(|d| parse_execute_response_data(d.as_slice()).unwrap().data);
        Ok(res)
    }

    /// Migrate a contract. Sender must be registered admin.
    /// This is just a helper around execute()
    fn migrate_contract<T: Serialize>(
        &mut self,
        sender: Addr,
        contract_addr: Addr,
        msg: &T,
        new_code_id: u64,
    ) -> AnyResult<AppResponse> {
        let msg = to_json_binary(msg)?;
        let msg = WasmMsg::Migrate {
            contract_addr: contract_addr.into(),
            msg,
            new_code_id,
        };
        self.execute(sender, msg.into())
    }

    fn send_tokens(
        &mut self,
        sender: Addr,
        recipient: Addr,
        amount: &[Coin],
    ) -> AnyResult<AppResponse> {
        let msg = BankMsg::Send {
            to_address: recipient.to_string(),
            amount: amount.to_vec(),
        };
        self.execute(sender, msg.into())
    }
}