1use cosmwasm_std::{Binary, StdError};
2use cw_orch_core::environment::CwEnv;
3use cw_orch_core::environment::IndexResponse;
4use ibc_relayer_types::core::{
5 ics04_channel::packet::Sequence,
6 ics24_host::identifier::{ChannelId, PortId},
7};
8
9use crate::{results::NetworkId, tx::TxId};
10
11#[derive(Debug, Clone)]
13pub struct IbcPacketInfo {
14 pub src_port: PortId,
16 pub src_channel: ChannelId,
18 pub sequence: Sequence,
20 pub dst_chain_id: NetworkId,
22}
23
24#[derive(Debug, PartialEq, Clone)]
27#[must_use = "We recommend using `PacketAnalysis::assert()` to assert ibc success"]
28pub enum IbcPacketOutcome<T> {
29 Timeout {
31 timeout_tx: T,
33 },
34 Success {
36 receive_tx: T,
38 ack_tx: T,
40 ack: Binary,
42 },
43}
44
45#[derive(Clone)]
51#[must_use = "We recommend using `PacketAnalysis::assert()` to assert IBC success"]
52pub struct SinglePacketFlow<Chain: CwEnv> {
53 pub send_tx: Option<TxId<Chain>>,
58 pub outcome: IbcPacketOutcome<TxId<Chain>>,
60}
61
62#[derive(Clone)]
71#[must_use = "We recommend using `PacketAnalysis::assert()` to assert IBC success"]
72pub struct NestedPacketsFlow<Chain: CwEnv> {
73 pub tx_id: TxId<Chain>,
75 pub packets: Vec<IbcPacketOutcome<NestedPacketsFlow<Chain>>>,
77}
78
79impl<Chain: CwEnv> IndexResponse for SinglePacketFlow<Chain> {
80 fn events(&self) -> Vec<cosmwasm_std::Event> {
81 let mut events: Vec<_> = self
82 .send_tx
83 .as_ref()
84 .map(|tx| tx.response.events())
85 .unwrap_or_default();
86 let other_events = self.outcome.events();
87 events.extend(other_events);
88
89 events
90 }
91
92 fn event_attr_value(
93 &self,
94 event_type: &str,
95 attr_key: &str,
96 ) -> cosmwasm_std::StdResult<String> {
97 self.send_tx
98 .as_ref()
99 .map(|r| r.event_attr_value(event_type, attr_key))
100 .and_then(|res| res.ok())
101 .or_else(|| self.outcome.event_attr_value(event_type, attr_key).ok())
102 .ok_or(StdError::generic_err(format!(
103 "event of type {event_type} does not have a value at key {attr_key}"
104 )))
105 }
106
107 fn event_attr_values(&self, event_type: &str, attr_key: &str) -> Vec<String> {
108 let mut all_results: Vec<_> = self
109 .send_tx
110 .as_ref()
111 .map(|tx| tx.response.event_attr_values(event_type, attr_key))
112 .unwrap_or_default();
113 let other_results = self.outcome.event_attr_values(event_type, attr_key);
114 all_results.extend(other_results);
115
116 all_results
117 }
118
119 fn data(&self) -> Option<Binary> {
120 unimplemented!("No data fields on Ibc Packet Flow, this is not well defined")
121 }
122}
123
124impl<Chain: CwEnv> IndexResponse for NestedPacketsFlow<Chain> {
125 fn events(&self) -> Vec<cosmwasm_std::Event> {
126 let mut self_events = self.tx_id.response.events();
127 let other_events = self
128 .packets
129 .iter()
130 .flat_map(|packet_result| packet_result.events());
131 self_events.extend(other_events);
132 self_events
133 }
134
135 fn event_attr_value(
136 &self,
137 event_type: &str,
138 attr_key: &str,
139 ) -> cosmwasm_std::StdResult<String> {
140 self.tx_id
141 .response
142 .event_attr_value(event_type, attr_key)
143 .or_else(|_| {
144 self.packets
145 .iter()
146 .find_map(|packet_result| {
147 packet_result.event_attr_value(event_type, attr_key).ok()
148 })
149 .ok_or(StdError::generic_err(format!(
150 "event of type {event_type} does not have a value at key {attr_key}"
151 )))
152 })
153 }
154
155 fn event_attr_values(&self, event_type: &str, attr_key: &str) -> Vec<String> {
156 let mut all_results = self.tx_id.response.event_attr_values(event_type, attr_key);
157
158 all_results.extend(
159 self.packets
160 .iter()
161 .flat_map(|packet_result| packet_result.event_attr_values(event_type, attr_key)),
162 );
163
164 all_results
165 }
166
167 fn data(&self) -> Option<Binary> {
168 unimplemented!("No data fields on Ibc Tx Analysis")
169 }
170}
171
172pub mod success {
173 use crate::ack_parser::IbcHooksAck;
174 use crate::{ack_parser::polytone_callback::Callback, tx::TxId};
175 use cosmwasm_std::{Binary, Empty, StdError};
176 use cw_orch_core::environment::CwEnv;
177 use cw_orch_core::environment::IndexResponse;
178
179 #[derive(Debug, PartialEq, Clone)]
181 #[non_exhaustive]
182 pub enum IbcAppResult<CustomResult = Empty> {
183 Polytone(Callback),
185 Ics20,
187 Ics004(Vec<u8>),
189 IbcHooks(IbcHooksAck),
192 Custom(CustomResult),
194 }
195
196 impl IbcAppResult<Empty> {
197 pub fn into_custom<CustomResult>(self) -> IbcAppResult<CustomResult> {
199 match self {
200 IbcAppResult::Polytone(callback) => IbcAppResult::Polytone(callback),
201 IbcAppResult::Ics20 => IbcAppResult::Ics20,
202 IbcAppResult::Ics004(vec) => IbcAppResult::Ics004(vec),
203 IbcAppResult::IbcHooks(ibc_hooks_ack) => IbcAppResult::IbcHooks(ibc_hooks_ack),
204 IbcAppResult::Custom(_) => unreachable!(),
205 }
206 }
207 }
208
209 #[derive(Debug, PartialEq, Clone)]
212 pub struct IbcPacketResult<T: IndexResponse, CustomResult = Empty> {
213 pub receive_tx: T,
215 pub ack_tx: T,
217 pub ibc_app_result: IbcAppResult<CustomResult>,
219 }
220
221 #[derive(Clone)]
225 pub struct SuccessSinglePacketFlow<Chain: CwEnv, CustomResult = Empty> {
226 pub send_tx: Option<TxId<Chain>>,
231 pub result: IbcPacketResult<TxId<Chain, CustomResult>, CustomResult>,
233 }
234
235 #[derive(Clone)]
239 pub struct SuccessNestedPacketsFlow<Chain: CwEnv, CustomResult = Empty> {
240 pub tx_id: TxId<Chain>,
242 pub packets:
244 Vec<IbcPacketResult<SuccessNestedPacketsFlow<Chain, CustomResult>, CustomResult>>,
245 }
246
247 impl<T: IndexResponse, CustomResult> IndexResponse for IbcPacketResult<T, CustomResult> {
248 fn events(&self) -> Vec<cosmwasm_std::Event> {
249 [self.receive_tx.events(), self.ack_tx.events()].concat()
250 }
251
252 fn event_attr_value(
253 &self,
254 event_type: &str,
255 attr_key: &str,
256 ) -> cosmwasm_std::StdResult<String> {
257 self.receive_tx
258 .event_attr_value(event_type, attr_key)
259 .or_else(|_| self.ack_tx.event_attr_value(event_type, attr_key))
260 }
261
262 fn event_attr_values(&self, event_type: &str, attr_key: &str) -> Vec<String> {
263 [
264 self.receive_tx.event_attr_values(event_type, attr_key),
265 self.ack_tx.event_attr_values(event_type, attr_key),
266 ]
267 .concat()
268 }
269
270 fn data(&self) -> Option<Binary> {
271 unimplemented!("No data fields on Ibc Packet Flow, this is not well defined")
272 }
273 }
274
275 impl<Chain: CwEnv, CustomResult> IndexResponse for SuccessSinglePacketFlow<Chain, CustomResult> {
276 fn events(&self) -> Vec<cosmwasm_std::Event> {
277 let mut events: Vec<_> = self
278 .send_tx
279 .as_ref()
280 .map(|tx| tx.response.events())
281 .unwrap_or_default();
282 let other_events = self.result.events();
283 events.extend(other_events);
284
285 events
286 }
287
288 fn event_attr_value(
289 &self,
290 event_type: &str,
291 attr_key: &str,
292 ) -> cosmwasm_std::StdResult<String> {
293 self.send_tx
294 .as_ref()
295 .map(|r| r.event_attr_value(event_type, attr_key))
296 .and_then(|res| res.ok())
297 .or_else(|| self.result.event_attr_value(event_type, attr_key).ok())
298 .ok_or(StdError::generic_err(format!(
299 "event of type {event_type} does not have a value at key {attr_key}"
300 )))
301 }
302
303 fn event_attr_values(&self, event_type: &str, attr_key: &str) -> Vec<String> {
304 let mut all_results: Vec<_> = self
305 .send_tx
306 .as_ref()
307 .map(|tx| tx.response.event_attr_values(event_type, attr_key))
308 .unwrap_or_default();
309 let other_results = self.result.event_attr_values(event_type, attr_key);
310 all_results.extend(other_results);
311
312 all_results
313 }
314
315 fn data(&self) -> Option<Binary> {
316 unimplemented!("No data fields on SuccessSinglePacketFlow, this is not well defined")
317 }
318 }
319
320 impl<Chain: CwEnv, CustomResult> IndexResponse for SuccessNestedPacketsFlow<Chain, CustomResult> {
321 fn events(&self) -> Vec<cosmwasm_std::Event> {
322 let mut self_events = self.tx_id.response.events();
323 let other_events = self
324 .packets
325 .iter()
326 .flat_map(|packet_result| packet_result.events());
327 self_events.extend(other_events);
328 self_events
329 }
330
331 fn event_attr_value(
332 &self,
333 event_type: &str,
334 attr_key: &str,
335 ) -> cosmwasm_std::StdResult<String> {
336 self.tx_id
337 .response
338 .event_attr_value(event_type, attr_key)
339 .or_else(|_| {
340 self.packets
341 .iter()
342 .find_map(|packet_result| {
343 packet_result.event_attr_value(event_type, attr_key).ok()
344 })
345 .ok_or(StdError::generic_err(format!(
346 "event of type {event_type} does not have a value at key {attr_key}"
347 )))
348 })
349 }
350
351 fn event_attr_values(&self, event_type: &str, attr_key: &str) -> Vec<String> {
352 let mut all_results = self.tx_id.response.event_attr_values(event_type, attr_key);
353
354 all_results.extend(
355 self.packets.iter().flat_map(|packet_result| {
356 packet_result.event_attr_values(event_type, attr_key)
357 }),
358 );
359
360 all_results
361 }
362
363 fn data(&self) -> Option<Binary> {
364 unimplemented!("No data fields on SuccessNestedPacketsFlow")
365 }
366 }
367}
368
369mod debug {
370 use cw_orch_core::environment::CwEnv;
371
372 use super::{
373 success::{SuccessNestedPacketsFlow, SuccessSinglePacketFlow},
374 NestedPacketsFlow, SinglePacketFlow,
375 };
376
377 impl<C: CwEnv> std::fmt::Debug for SinglePacketFlow<C> {
378 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
379 f.debug_struct("SinglePacketFlow")
380 .field("send_tx", &self.send_tx)
381 .field("outcome", &self.outcome)
382 .finish()
383 }
384 }
385
386 impl<C: CwEnv> std::fmt::Debug for NestedPacketsFlow<C> {
387 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
388 f.debug_struct("NestedPacketsFlow")
389 .field("tx_id", &self.tx_id)
390 .field("packets", &self.packets)
391 .finish()
392 }
393 }
394
395 impl<C: CwEnv, CustomResult: std::fmt::Debug> std::fmt::Debug
396 for SuccessSinglePacketFlow<C, CustomResult>
397 {
398 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
399 f.debug_struct("SuccessSinglePacketFlow")
400 .field("sent_tx", &self.send_tx)
401 .field("result", &self.result)
402 .finish()
403 }
404 }
405
406 impl<C: CwEnv, CustomResult: std::fmt::Debug> std::fmt::Debug
407 for SuccessNestedPacketsFlow<C, CustomResult>
408 {
409 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
410 f.debug_struct("SuccessNestedPacketsFlow")
411 .field("tx_id", &self.tx_id)
412 .field("packets", &self.packets)
413 .finish()
414 }
415 }
416}
417
418mod index_response {
419 use cosmwasm_std::Binary;
420 use cw_orch_core::environment::IndexResponse;
421
422 use super::IbcPacketOutcome;
423
424 impl<T: IndexResponse> IndexResponse for IbcPacketOutcome<T> {
425 fn events(&self) -> Vec<cosmwasm_std::Event> {
426 match &self {
427 IbcPacketOutcome::Timeout { timeout_tx } => timeout_tx.events(),
428 IbcPacketOutcome::Success {
429 receive_tx,
430 ack_tx,
431 ack: _,
432 } => [receive_tx.events(), ack_tx.events()].concat(),
433 }
434 }
435
436 fn event_attr_value(
437 &self,
438 event_type: &str,
439 attr_key: &str,
440 ) -> cosmwasm_std::StdResult<String> {
441 match &self {
442 IbcPacketOutcome::Timeout { timeout_tx } => {
443 timeout_tx.event_attr_value(event_type, attr_key)
444 }
445 IbcPacketOutcome::Success {
446 receive_tx,
447 ack_tx,
448 ack: _,
449 } => receive_tx
450 .event_attr_value(event_type, attr_key)
451 .or_else(|_| ack_tx.event_attr_value(event_type, attr_key)),
452 }
453 }
454
455 fn event_attr_values(&self, event_type: &str, attr_key: &str) -> Vec<String> {
456 match &self {
457 IbcPacketOutcome::Timeout { timeout_tx } => {
458 timeout_tx.event_attr_values(event_type, attr_key)
459 }
460 IbcPacketOutcome::Success {
461 receive_tx,
462 ack_tx,
463 ack: _,
464 } => [
465 receive_tx.event_attr_values(event_type, attr_key),
466 ack_tx.event_attr_values(event_type, attr_key),
467 ]
468 .concat(),
469 }
470 }
471
472 fn data(&self) -> Option<Binary> {
473 unimplemented!("No data fields on Ibc Packet Flow, this is not well defined")
474 }
475 }
476}