1use crate::error::AnyResult;
2use cosmwasm_std::{
3 to_json_binary, Addr, Attribute, BankMsg, Binary, Coin, CosmosMsg, CustomMsg, Event,
4 SubMsgResponse, WasmMsg,
5};
6use cw_utils::{parse_execute_response_data, parse_instantiate_response_data};
7use serde::Serialize;
8use std::fmt::Debug;
9
10#[derive(Default, Clone, Debug)]
13pub struct AppResponse {
14 pub events: Vec<Event>,
16 pub data: Option<Binary>,
18}
19
20impl AppResponse {
21 #[track_caller]
25 pub fn custom_attrs(&self, idx: usize) -> &[Attribute] {
26 assert_eq!(self.events[idx].ty.as_str(), "wasm");
27 &self.events[idx].attributes[1..]
28 }
29
30 pub fn has_event(&self, expected: &Event) -> bool {
35 self.events.iter().any(|ev| {
36 expected.ty == ev.ty
37 && expected
38 .attributes
39 .iter()
40 .all(|at| ev.attributes.contains(at))
41 })
42 }
43
44 #[track_caller]
46 pub fn assert_event(&self, expected: &Event) {
47 assert!(
48 self.has_event(expected),
49 "Expected to find an event {:?}, but received: {:?}",
50 expected,
51 self.events
52 );
53 }
54}
55
56impl From<SubMsgResponse> for AppResponse {
59 fn from(reply: SubMsgResponse) -> Self {
60 AppResponse {
61 #[allow(deprecated)]
62 data: reply.data,
63 events: reply.events,
64 }
65 }
66}
67pub trait Executor<C>
73where
74 C: CustomMsg + 'static,
75{
76 fn execute(&mut self, sender: Addr, msg: CosmosMsg<C>) -> AnyResult<AppResponse>;
81
82 fn instantiate_contract<T: Serialize, U: Into<String>>(
85 &mut self,
86 code_id: u64,
87 sender: Addr,
88 init_msg: &T,
89 send_funds: &[Coin],
90 label: U,
91 admin: Option<String>,
92 ) -> AnyResult<Addr> {
93 let init_msg = to_json_binary(init_msg)?;
95 let msg = WasmMsg::Instantiate {
96 admin,
97 code_id,
98 msg: init_msg,
99 funds: send_funds.to_vec(),
100 label: label.into(),
101 };
102 let res = self.execute(sender, msg.into())?;
103 let data = parse_instantiate_response_data(res.data.unwrap_or_default().as_slice())?;
104 Ok(Addr::unchecked(data.contract_address))
105 }
106
107 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 ) -> AnyResult<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 ) -> AnyResult<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 ) -> AnyResult<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 ) -> AnyResult<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}