1use crate::error::AnyResult;
2use cosmwasm_std::{
3 to_json_binary, Addr, Attribute, BankMsg, Binary, Coin, CosmosMsg, CustomMsg, Event,
4 MsgResponse, 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 pub msg_responses: Vec<MsgResponse>,
20}
21
22impl AppResponse {
23 #[track_caller]
27 pub fn custom_attrs(&self, idx: usize) -> &[Attribute] {
28 assert_eq!(self.events[idx].ty.as_str(), "wasm");
29 &self.events[idx].attributes[1..]
30 }
31
32 pub fn has_event(&self, expected: &Event) -> bool {
37 self.events.iter().any(|ev| {
38 expected.ty == ev.ty
39 && expected
40 .attributes
41 .iter()
42 .all(|at| ev.attributes.contains(at))
43 })
44 }
45
46 #[track_caller]
48 pub fn assert_event(&self, expected: &Event) {
49 assert!(
50 self.has_event(expected),
51 "Expected to find an event {:?}, but received: {:?}",
52 expected,
53 self.events
54 );
55 }
56}
57
58impl From<SubMsgResponse> for AppResponse {
61 fn from(reply: SubMsgResponse) -> Self {
62 AppResponse {
63 events: reply.events,
64 #[allow(deprecated)]
65 data: reply.data,
66 msg_responses: reply.msg_responses,
67 }
68 }
69}
70pub trait Executor<C>
76where
77 C: CustomMsg + 'static,
78{
79 fn execute(&mut self, sender: Addr, msg: CosmosMsg<C>) -> AnyResult<AppResponse>;
84
85 fn instantiate_contract<T: Serialize, U: Into<String>>(
88 &mut self,
89 code_id: u64,
90 sender: Addr,
91 init_msg: &T,
92 send_funds: &[Coin],
93 label: U,
94 admin: Option<String>,
95 ) -> AnyResult<Addr> {
96 let init_msg = to_json_binary(init_msg)?;
98 let msg = WasmMsg::Instantiate {
99 admin,
100 code_id,
101 msg: init_msg,
102 funds: send_funds.to_vec(),
103 label: label.into(),
104 };
105 let res = self.execute(sender, msg.into())?;
106 let data = parse_instantiate_response_data(res.data.unwrap_or_default().as_slice())?;
107 Ok(Addr::unchecked(data.contract_address))
108 }
109
110 #[cfg(feature = "cosmwasm_1_2")]
114 fn instantiate2_contract<M, L, A, S>(
115 &mut self,
116 code_id: u64,
117 sender: Addr,
118 init_msg: &M,
119 funds: &[Coin],
120 label: L,
121 admin: A,
122 salt: S,
123 ) -> AnyResult<Addr>
124 where
125 M: Serialize,
126 L: Into<String>,
127 A: Into<Option<String>>,
128 S: Into<Binary>,
129 {
130 let msg = WasmMsg::Instantiate2 {
131 admin: admin.into(),
132 code_id,
133 msg: to_json_binary(init_msg)?,
134 funds: funds.to_vec(),
135 label: label.into(),
136 salt: salt.into(),
137 };
138 let execute_response = self.execute(sender, msg.into())?;
139 let instantiate_response =
140 parse_instantiate_response_data(execute_response.data.unwrap_or_default().as_slice())?;
141 Ok(Addr::unchecked(instantiate_response.contract_address))
142 }
143
144 fn execute_contract<T: Serialize + Debug>(
149 &mut self,
150 sender: Addr,
151 contract_addr: Addr,
152 msg: &T,
153 send_funds: &[Coin],
154 ) -> AnyResult<AppResponse> {
155 let binary_msg = to_json_binary(msg)?;
156 let wrapped_msg = WasmMsg::Execute {
157 contract_addr: contract_addr.into_string(),
158 msg: binary_msg,
159 funds: send_funds.to_vec(),
160 };
161 let mut res = self.execute(sender, wrapped_msg.into())?;
162 res.data = res
163 .data
164 .and_then(|d| parse_execute_response_data(d.as_slice()).unwrap().data);
165 Ok(res)
166 }
167
168 fn migrate_contract<T: Serialize>(
173 &mut self,
174 sender: Addr,
175 contract_addr: Addr,
176 msg: &T,
177 new_code_id: u64,
178 ) -> AnyResult<AppResponse> {
179 let msg = to_json_binary(msg)?;
180 let msg = WasmMsg::Migrate {
181 contract_addr: contract_addr.into(),
182 msg,
183 new_code_id,
184 };
185 self.execute(sender, msg.into())
186 }
187
188 fn send_tokens(
192 &mut self,
193 sender: Addr,
194 recipient: Addr,
195 amount: &[Coin],
196 ) -> AnyResult<AppResponse> {
197 let msg = BankMsg::Send {
198 to_address: recipient.to_string(),
199 amount: amount.to_vec(),
200 };
201 self.execute(sender, msg.into())
202 }
203}