1use crate::error::AnyResult;
2use cosmwasm_std::{
3 to_json_binary, Addr, Attribute, BankMsg, Binary, Coin, CosmosMsg, Event, SubMsgResponse,
4 WasmMsg,
5};
6use cw_utils::{parse_execute_response_data, parse_instantiate_response_data};
7use schemars::JsonSchema;
8use serde::Serialize;
9use std::fmt::Debug;
10
11#[derive(Default, Clone, Debug)]
12pub struct AppResponse {
13 pub events: Vec<Event>,
14 pub data: Option<Binary>,
15}
16
17impl AppResponse {
18 #[track_caller]
21 pub fn custom_attrs(&self, idx: usize) -> &[Attribute] {
22 assert_eq!(self.events[idx].ty.as_str(), "wasm");
23 &self.events[idx].attributes[1..]
24 }
25
26 pub fn has_event(&self, expected: &Event) -> bool {
30 self.events.iter().any(|ev| {
31 expected.ty == ev.ty
32 && expected
33 .attributes
34 .iter()
35 .all(|at| ev.attributes.contains(at))
36 })
37 }
38
39 #[track_caller]
41 pub fn assert_event(&self, expected: &Event) {
42 assert!(
43 self.has_event(expected),
44 "Expected to find an event {:?}, but received: {:?}",
45 expected,
46 self.events
47 );
48 }
49}
50
51impl From<SubMsgResponse> for AppResponse {
54 fn from(reply: SubMsgResponse) -> Self {
55 AppResponse {
56 #[allow(deprecated)]
57 data: reply.data,
58 events: reply.events,
59 }
60 }
61}
62
63pub trait Executor<C>
64where
65 C: Clone + Debug + PartialEq + JsonSchema + 'static,
66{
67 fn execute(&mut self, sender: Addr, msg: CosmosMsg<C>) -> AnyResult<AppResponse>;
71
72 fn instantiate_contract<T: Serialize, U: Into<String>>(
75 &mut self,
76 code_id: u64,
77 sender: Addr,
78 init_msg: &T,
79 send_funds: &[Coin],
80 label: U,
81 admin: Option<String>,
82 ) -> AnyResult<Addr> {
83 let init_msg = to_json_binary(init_msg)?;
85 let msg = WasmMsg::Instantiate {
86 admin,
87 code_id,
88 msg: init_msg,
89 funds: send_funds.to_vec(),
90 label: label.into(),
91 };
92 let res = self.execute(sender, msg.into())?;
93 let data = parse_instantiate_response_data(res.data.unwrap_or_default().as_slice())?;
94 Ok(Addr::unchecked(data.contract_address))
95 }
96
97 fn instantiate2_contract<M, L, A, S>(
101 &mut self,
102 code_id: u64,
103 sender: Addr,
104 init_msg: &M,
105 funds: &[Coin],
106 label: L,
107 admin: A,
108 salt: S,
109 ) -> AnyResult<Addr>
110 where
111 M: Serialize,
112 L: Into<String>,
113 A: Into<Option<String>>,
114 S: Into<Binary>,
115 {
116 let msg = WasmMsg::Instantiate2 {
117 admin: admin.into(),
118 code_id,
119 msg: to_json_binary(init_msg)?,
120 funds: funds.to_vec(),
121 label: label.into(),
122 salt: salt.into(),
123 };
124 let execute_response = self.execute(sender, msg.into())?;
125 let instantiate_response =
126 parse_instantiate_response_data(execute_response.data.unwrap_or_default().as_slice())?;
127 Ok(Addr::unchecked(instantiate_response.contract_address))
128 }
129
130 fn execute_contract<T: Serialize + Debug>(
134 &mut self,
135 sender: Addr,
136 contract_addr: Addr,
137 msg: &T,
138 send_funds: &[Coin],
139 ) -> AnyResult<AppResponse> {
140 let binary_msg = to_json_binary(msg)?;
141 let wrapped_msg = WasmMsg::Execute {
142 contract_addr: contract_addr.into_string(),
143 msg: binary_msg,
144 funds: send_funds.to_vec(),
145 };
146 let mut res = self.execute(sender, wrapped_msg.into())?;
147 res.data = res
148 .data
149 .and_then(|d| parse_execute_response_data(d.as_slice()).unwrap().data);
150 Ok(res)
151 }
152
153 fn migrate_contract<T: Serialize>(
156 &mut self,
157 sender: Addr,
158 contract_addr: Addr,
159 msg: &T,
160 new_code_id: u64,
161 ) -> AnyResult<AppResponse> {
162 let msg = to_json_binary(msg)?;
163 let msg = WasmMsg::Migrate {
164 contract_addr: contract_addr.into(),
165 msg,
166 new_code_id,
167 };
168 self.execute(sender, msg.into())
169 }
170
171 fn send_tokens(
172 &mut self,
173 sender: Addr,
174 recipient: Addr,
175 amount: &[Coin],
176 ) -> AnyResult<AppResponse> {
177 let msg = BankMsg::Send {
178 to_address: recipient.to_string(),
179 amount: amount.to_vec(),
180 };
181 self.execute(sender, msg.into())
182 }
183}