1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
use std::{cell::RefCell, rc::Rc};

use cosmwasm_std::{
    Addr, Api, Binary, BlockInfo, IbcChannelConnectMsg, IbcChannelOpenMsg, IbcMsg,
    IbcPacketReceiveMsg, Storage,
};
use cw_multi_test::{AppResponse, MockApiBech32};

use crate::{
    error::AppResult,
    ibc::IbcChannelWrapper,
    ibc_module::{AckPacket, TimeoutPacket},
    iper_app::InfallibleResult,
    router::RouterWrapper,
};

/// This trait identifies a generic `IBC application`(e.g., [`IC20`](crate::ibc_applications::Ics20), `ICA`, etc.) that is managed by the [`IperIbcModule`](crate::ibc_module::IperIbcModule).
/// The [`IperIbcModule`](crate::ibc_module::IperIbcModule) is a structure implementing both [`Ibc`](cw_multi_test::Ibc) and [`Module`](cw_multi_test::Module)
/// traits and serves as the `IBC module` for the [`App`](cw_multi_test::App) class of an [`IperApp`](crate::IperApp).
///
/// `IperIbcModule` will invoke a function implemented by this trait under the following conditions:
///
/// - **handle_outgoing_packet**: An `IBC packet` is emitted and the source `channel-id` is this [`IbcApplication`].
/// - **packet_receive**: An `IBC packet` is received and the destination `channel-id` is this [`IbcApplication`].
/// - **packet_ack**: An `acknowledgment packet` returns and the source `channel-id` was this [`IbcApplication`].
/// - **packet_timeout**: A `timeout packet` returns and the source `channel-id` was this [`IbcApplication`].
/// - **open_channel**: An `IBC channel` is being opened that carries this [`IbcApplication`].
/// - **channel_connect**: An `IBC channel` is being connected that carries this [`IbcApplication`].
///
/// ## Implementation of the trait:
/// In order to be implemented, the struct has to implement also [`IbcPortInterface`]
///
/// Use the derive macro `IbcPort` `from cw-iper-test-macros`
/// ## Example:
/// ```ignore
/// #[derive(IbcPort)]
/// #[ibc_port = "transfer"]
/// pub struct Ics20;
pub trait IbcApplication: IbcPortInterface {
    /// An `IBC packet` is emitted and the source `channel-id` is this [`IbcApplication`].
    ///
    /// For example. on [`Ics20`](crate::ibc_applications::Ics20) the transfer amount has to be burned/locked from the sender address
    #[allow(clippy::too_many_arguments)]

    fn handle_outgoing_packet(
        &self,
        api: &dyn Api,
        block: &BlockInfo,
        sender: Addr,
        router: &RouterWrapper,
        storage: Rc<RefCell<&mut dyn Storage>>,
        msg: IbcMsg,
        channel: IbcChannelWrapper,
    ) -> AppResult<AppResponse>;

    /// An `IBC packet` is received and the destination `channel-id` is this [[`IbcApplication`]].
    ///
    /// The return type is [`InfallibleResult`], allowing to revert any Storage changes without raising errors.
    fn packet_receive(
        &self,
        api: &dyn Api,
        block: &BlockInfo,
        router: &RouterWrapper,
        storage: Rc<RefCell<&mut dyn Storage>>,
        msg: IbcPacketReceiveMsg,
    ) -> InfallibleResult<PacketReceiveOk, PacketReceiveFailing>;

    /// An `acknowledgment packet` returns and the source `channel-id` was this [`IbcApplication`].
    fn packet_ack(
        &self,
        api: &dyn Api,
        block: &BlockInfo,
        router: &RouterWrapper,
        storage: Rc<RefCell<&mut dyn Storage>>,
        msg: AckPacket,
    ) -> AppResult<AppResponse>;
    /// A `timeout packet` returns and the source `channel-id` was this [`IbcApplication`].
    fn packet_timeout(
        &self,
        api: &dyn Api,
        block: &BlockInfo,
        router: &RouterWrapper,
        storage: Rc<RefCell<&mut dyn Storage>>,
        msg: TimeoutPacket,
    ) -> AppResult<AppResponse>;

    /// An `IBC channel` is being opened that carries this [`IbcApplication`].
    fn open_channel(
        &self,
        api: &dyn Api,
        block: &BlockInfo,
        router: &RouterWrapper,
        storage: Rc<RefCell<&mut dyn Storage>>,
        msg: IbcChannelOpenMsg,
    ) -> AppResult<AppResponse>;

    /// An `IBC channel` is being connected that carries this [`IbcApplication`].
    fn channel_connect(
        &self,
        api: &dyn Api,
        block: &BlockInfo,
        router: &RouterWrapper,
        storage: Rc<RefCell<&mut dyn Storage>>,
        msg: IbcChannelConnectMsg,
    ) -> AppResult<AppResponse>;

    ///
    fn init(&self, api: &MockApiBech32, storage: &mut dyn Storage);
}

/// This trait is used to implement the `port_name` for a [`IbcApplication`].
///
/// For implement this trait, use the derive macro `IbcPort` `from cw-iper-test-macros`
/// ## Example:
/// ```ignore
/// #[derive(IbcPort)]
/// #[ibc_port = "transfer"]
/// pub struct Ics20;
pub trait IbcPortInterface {
    /// return the `port` name of an [`IbcApplication`]
    fn port_name(&self) -> String;
}

/// `Ok` [`InfallibleResult`] from [`IbcApplication::packet_receive`].
///
/// if [`PacketReceiveOk::ack`] is [`Some`], an [`IbcPacketType::AckPacket`](crate::ibc_module::IbcPacketType) is emitted and ready to be `relayed`
#[derive(Debug, Clone)]
pub struct PacketReceiveOk {
    /// AppResponse of the [`IbcApplication::packet_receive`] execution.
    ///
    ///  When the packet will be relayed from [`Ecosystem`](crate::ecosystem::Ecosystem) this error text will be returned
    pub response: AppResponse,
    /// if [`Some`], an [`IbcPacketType::AckPacket`](crate::ibc_module::IbcPacketType) is emitted and ready to be `relayed`
    pub ack: Option<Binary>,
}

/// `Err` [`InfallibleResult`] from [`IbcApplication::packet_receive`].
/// Any changes on the `Storage` emitted during [`IbcApplication::packet_receive`] will be reverted.
///
/// if [`PacketReceiveOk::ack`] is [`Some`], an [`IbcPacketType::AckPacket`](crate::ibc_module::IbcPacketType) is emitted and ready to be `relayed`
#[derive(Debug, Clone)]
pub struct PacketReceiveFailing {
    /// Stringed error. When the packet will be relayed from [`Ecosystem`](crate::ecosystem::Ecosystem) this error text will be returned
    pub error: String,
    /// if [`Some`], an [`IbcPacketType::AckPacket`](crate::ibc_module::IbcPacketType) is emitted and ready to be `relayed`
    pub ack: Option<Binary>,
}