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 #[cfg(feature = "cosmwasm_1_2")]
111 fn instantiate2_contract<M, L, A, S>(
112 &mut self,
113 code_id: u64,
114 sender: Addr,
115 init_msg: &M,
116 funds: &[Coin],
117 label: L,
118 admin: A,
119 salt: S,
120 ) -> AnyResult<Addr>
121 where
122 M: Serialize,
123 L: Into<String>,
124 A: Into<Option<String>>,
125 S: Into<Binary>,
126 {
127 let msg = WasmMsg::Instantiate2 {
128 admin: admin.into(),
129 code_id,
130 msg: to_json_binary(init_msg)?,
131 funds: funds.to_vec(),
132 label: label.into(),
133 salt: salt.into(),
134 };
135 let execute_response = self.execute(sender, msg.into())?;
136 let instantiate_response =
137 parse_instantiate_response_data(execute_response.data.unwrap_or_default().as_slice())?;
138 Ok(Addr::unchecked(instantiate_response.contract_address))
139 }
140
141 fn execute_contract<T: Serialize + Debug>(
146 &mut self,
147 sender: Addr,
148 contract_addr: Addr,
149 msg: &T,
150 send_funds: &[Coin],
151 ) -> AnyResult<AppResponse> {
152 let binary_msg = to_json_binary(msg)?;
153 let wrapped_msg = WasmMsg::Execute {
154 contract_addr: contract_addr.into_string(),
155 msg: binary_msg,
156 funds: send_funds.to_vec(),
157 };
158 let mut res = self.execute(sender, wrapped_msg.into())?;
159 res.data = res
160 .data
161 .and_then(|d| parse_execute_response_data(d.as_slice()).unwrap().data);
162 Ok(res)
163 }
164
165 fn migrate_contract<T: Serialize>(
170 &mut self,
171 sender: Addr,
172 contract_addr: Addr,
173 msg: &T,
174 new_code_id: u64,
175 ) -> AnyResult<AppResponse> {
176 let msg = to_json_binary(msg)?;
177 let msg = WasmMsg::Migrate {
178 contract_addr: contract_addr.into(),
179 msg,
180 new_code_id,
181 };
182 self.execute(sender, msg.into())
183 }
184
185 fn send_tokens(
189 &mut self,
190 sender: Addr,
191 recipient: Addr,
192 amount: &[Coin],
193 ) -> AnyResult<AppResponse> {
194 let msg = BankMsg::Send {
195 to_address: recipient.to_string(),
196 amount: amount.to_vec(),
197 };
198 self.execute(sender, msg.into())
199 }
200}