1use cosmwasm_std::{
2 to_json_binary, Addr, Attribute, BankMsg, Binary, Coin, CosmosMsg, CustomMsg, Event, StdResult,
3 SubMsgResponse, WasmMsg,
4};
5use cw_utils::{parse_execute_response_data, parse_instantiate_response_data};
6use serde::Serialize;
7use std::fmt::Debug;
8
9#[derive(Default, Clone, Debug)]
12pub struct AppResponse {
13 pub events: Vec<Event>,
15 pub data: Option<Binary>,
17}
18
19impl AppResponse {
20 #[track_caller]
24 pub fn custom_attrs(&self, idx: usize) -> &[Attribute] {
25 assert_eq!(self.events[idx].ty.as_str(), "wasm");
26 &self.events[idx].attributes[1..]
27 }
28
29 pub fn has_event(&self, expected: &Event) -> bool {
34 self.events.iter().any(|ev| {
35 expected.ty == ev.ty
36 && expected
37 .attributes
38 .iter()
39 .all(|at| ev.attributes.contains(at))
40 })
41 }
42
43 #[track_caller]
45 pub fn assert_event(&self, expected: &Event) {
46 assert!(
47 self.has_event(expected),
48 "Expected to find an event {:?}, but received: {:?}",
49 expected,
50 self.events
51 );
52 }
53}
54
55impl From<SubMsgResponse> for AppResponse {
58 fn from(reply: SubMsgResponse) -> Self {
59 AppResponse {
60 #[allow(deprecated)]
61 data: reply.data,
62 events: reply.events,
63 }
64 }
65}
66pub trait Executor<C>
72where
73 C: CustomMsg + 'static,
74{
75 fn execute(&mut self, sender: Addr, msg: CosmosMsg<C>) -> StdResult<AppResponse>;
80
81 fn instantiate_contract<T: Serialize, U: Into<String>>(
84 &mut self,
85 code_id: u64,
86 sender: Addr,
87 init_msg: &T,
88 send_funds: &[Coin],
89 label: U,
90 admin: Option<String>,
91 ) -> StdResult<Addr> {
92 let init_msg = to_json_binary(init_msg)?;
94 let msg = WasmMsg::Instantiate {
95 admin,
96 code_id,
97 msg: init_msg,
98 funds: send_funds.to_vec(),
99 label: label.into(),
100 };
101 let res = self.execute(sender, msg.into())?;
102 let data = parse_instantiate_response_data(res.data.unwrap_or_default().as_slice())?;
103 Ok(Addr::unchecked(data.contract_address))
104 }
105
106 #[cfg(feature = "cosmwasm_1_2")]
110 fn instantiate2_contract<M, L, A, S>(
111 &mut self,
112 code_id: u64,
113 sender: Addr,
114 init_msg: &M,
115 funds: &[Coin],
116 label: L,
117 admin: A,
118 salt: S,
119 ) -> StdResult<Addr>
120 where
121 M: Serialize,
122 L: Into<String>,
123 A: Into<Option<String>>,
124 S: Into<Binary>,
125 {
126 let msg = WasmMsg::Instantiate2 {
127 admin: admin.into(),
128 code_id,
129 msg: to_json_binary(init_msg)?,
130 funds: funds.to_vec(),
131 label: label.into(),
132 salt: salt.into(),
133 };
134 let execute_response = self.execute(sender, msg.into())?;
135 let instantiate_response =
136 parse_instantiate_response_data(execute_response.data.unwrap_or_default().as_slice())?;
137 Ok(Addr::unchecked(instantiate_response.contract_address))
138 }
139
140 fn execute_contract<T: Serialize + Debug>(
145 &mut self,
146 sender: Addr,
147 contract_addr: Addr,
148 msg: &T,
149 send_funds: &[Coin],
150 ) -> StdResult<AppResponse> {
151 let binary_msg = to_json_binary(msg)?;
152 let wrapped_msg = WasmMsg::Execute {
153 contract_addr: contract_addr.into_string(),
154 msg: binary_msg,
155 funds: send_funds.to_vec(),
156 };
157 let mut res = self.execute(sender, wrapped_msg.into())?;
158 res.data = res
159 .data
160 .and_then(|d| parse_execute_response_data(d.as_slice()).unwrap().data);
161 Ok(res)
162 }
163
164 fn migrate_contract<T: Serialize>(
169 &mut self,
170 sender: Addr,
171 contract_addr: Addr,
172 msg: &T,
173 new_code_id: u64,
174 ) -> StdResult<AppResponse> {
175 let msg = to_json_binary(msg)?;
176 let msg = WasmMsg::Migrate {
177 contract_addr: contract_addr.into(),
178 msg,
179 new_code_id,
180 };
181 self.execute(sender, msg.into())
182 }
183
184 fn send_tokens(
188 &mut self,
189 sender: Addr,
190 recipient: Addr,
191 amount: &[Coin],
192 ) -> StdResult<AppResponse> {
193 let msg = BankMsg::Send {
194 to_address: recipient.to_string(),
195 amount: amount.to_vec(),
196 };
197 self.execute(sender, msg.into())
198 }
199}