abstract_std/native/
ibc.rs

1pub mod ibc_client;
2pub mod ibc_host;
3pub mod ica_client;
4pub mod polytone_callbacks;
5
6use cosmwasm_schema::cw_serde;
7use cosmwasm_std::{
8    to_json_binary, wasm_execute, Binary, CosmosMsg, Empty, Event, QueryRequest, StdError,
9    StdResult,
10};
11use cw_storage_plus::PrimaryKey;
12use schemars::JsonSchema;
13use serde::Serialize;
14
15use crate::{
16    base::ExecuteMsg,
17    objects::{module::ModuleInfo, TruncatedChainId},
18};
19use polytone_callbacks::{Callback as PolytoneCallback, ErrorResponse, ExecutionResponse};
20
21pub const PACKET_LIFETIME: u64 = 60 * 60;
22
23/// Callback from modules, that is turned into an IbcResponseMsg by the ibc client
24/// A callback can only be sent to itself
25#[cosmwasm_schema::cw_serde]
26// ANCHOR: callback-info
27pub struct Callback {
28    /// Used to add information to the callback.
29    /// This is usually used to provide information to the ibc callback function for context
30    pub msg: Binary,
31}
32// ANCHOR_END: callback-info
33
34impl Callback {
35    pub fn new<T: Serialize>(msg: &T) -> StdResult<Self> {
36        Ok(Self {
37            msg: to_json_binary(msg)?,
38        })
39    }
40}
41
42/// IbcResponseMsg should be de/serialized under `IbcCallback()` variant in a ExecuteMsg
43#[cosmwasm_schema::cw_serde]
44// ANCHOR: response-msg
45pub struct IbcResponseMsg {
46    /// The msg sent with the callback request.
47    pub callback: Callback,
48    pub result: IbcResult,
49}
50// ANCHOR_END: response-msg
51
52impl IbcResponseMsg {
53    /// serializes the message
54    pub fn into_json_binary(self) -> StdResult<Binary> {
55        let msg = ExecuteMsg::IbcCallback::<Empty, Empty>(self);
56        to_json_binary(&msg)
57    }
58
59    /// creates a cosmos_msg sending this struct to the named contract
60    pub fn into_cosmos_msg<T: Into<String>, C>(self, contract_addr: T) -> StdResult<CosmosMsg<C>>
61    where
62        C: Clone + std::fmt::Debug + PartialEq + JsonSchema,
63    {
64        Ok(wasm_execute(
65            contract_addr.into(),
66            &ExecuteMsg::IbcCallback::<Empty, Empty>(self),
67            vec![],
68        )?
69        .into())
70    }
71}
72
73#[cosmwasm_schema::cw_serde]
74pub enum IbcResult {
75    Query {
76        queries: Vec<QueryRequest<ModuleQuery>>,
77        results: Result<Vec<Binary>, ErrorResponse>,
78    },
79
80    Execute {
81        initiator_msg: Binary,
82        result: Result<ExecutionResponse, String>,
83    },
84
85    /// An error occured that could not be recovered from. The only
86    /// known way that this can occur is message handling running out
87    /// of gas, in which case the error will be `codespace: sdk, code:
88    /// 11`.
89    ///
90    /// This error is not named becuase it could also occur due to a
91    /// panic or unhandled error during message processing. We don't
92    /// expect this to happen and have carefully written the code to
93    /// avoid it.
94    FatalError(String),
95}
96
97impl IbcResult {
98    pub fn from_query(
99        callback: PolytoneCallback,
100        queries: Vec<QueryRequest<ModuleQuery>>,
101    ) -> Result<Self, StdError> {
102        match callback {
103            PolytoneCallback::Query(q) => Ok(Self::Query {
104                queries,
105                results: q,
106            }),
107            PolytoneCallback::Execute(_) => Err(StdError::generic_err(
108                "Expected a query result, got an execute result",
109            )),
110            PolytoneCallback::FatalError(e) => Ok(Self::FatalError(e)),
111        }
112    }
113
114    pub fn from_execute(
115        callback: PolytoneCallback,
116        initiator_msg: Binary,
117    ) -> Result<Self, StdError> {
118        match callback {
119            PolytoneCallback::Query(_) => Err(StdError::generic_err(
120                "Expected an execution result, got a query result",
121            )),
122            PolytoneCallback::Execute(e) => Ok(Self::Execute {
123                initiator_msg,
124                result: e,
125            }),
126            PolytoneCallback::FatalError(e) => Ok(Self::FatalError(e)),
127        }
128    }
129
130    /// Get query result
131    pub fn get_query_result(&self, index: usize) -> StdResult<(QueryRequest<ModuleQuery>, Binary)> {
132        match &self {
133            IbcResult::Query { queries, results } => {
134                let results = results
135                    .as_ref()
136                    .map_err(|err| StdError::generic_err(err.error.clone()))?;
137                Ok((queries[index].clone(), results[index].clone()))
138            }
139            IbcResult::Execute { .. } => Err(StdError::generic_err(
140                "expected query, got execute ibc result",
141            )),
142            IbcResult::FatalError(err) => Err(StdError::generic_err(err.to_owned())),
143        }
144    }
145
146    /// Get execute result
147    pub fn get_execute_events(&self) -> StdResult<Vec<Event>> {
148        match &self {
149            IbcResult::Execute { result, .. } => {
150                let result = result
151                    .as_ref()
152                    .map_err(|err| StdError::generic_err(err.clone()))?;
153                // result should always be size 1 (proxy -> ibc-host --multiple-msgs-> module)
154                let res = result
155                    .result
156                    .first()
157                    .expect("execution response without submsg");
158                Ok(res.events.clone())
159            }
160            IbcResult::Query { .. } => Err(StdError::generic_err(
161                "expected execute, got query ibc result",
162            )),
163            IbcResult::FatalError(err) => Err(StdError::generic_err(err.to_owned())),
164        }
165    }
166}
167
168#[cw_serde]
169pub struct ModuleIbcMsg {
170    /// Sender Module Identification
171    pub src_module_info: ModuleIbcInfo,
172    /// The message sent by the module
173    pub msg: Binary,
174}
175
176// ANCHOR: module_ibc_msg
177#[cw_serde]
178pub struct ModuleIbcInfo {
179    /// Remote chain identification
180    pub chain: TruncatedChainId,
181    /// Information about the module that called ibc action on this module
182    pub module: ModuleInfo,
183}
184// ANCHOR_END: module_ibc_msg
185
186// ANCHOR: module_ibc_query
187#[cw_serde]
188pub struct ModuleQuery {
189    /// Information about the module that gets queried through ibc
190    pub target_module: ibc_client::InstalledModuleIdentification,
191    /// The WasmQuery::Smart request to the module
192    pub msg: Binary,
193}
194// ANCHOR_END: module_ibc_query
195
196// Source: https://github.com/cosmos/ibc-apps/blob/8cb681e31589bc90b47e0ab58173a579825fd56d/modules/ibc-hooks/README.md#interface-for-receiving-the-acks-and-timeouts
197#[cosmwasm_schema::cw_serde]
198pub enum IBCLifecycleComplete {
199    #[serde(rename = "ibc_ack")]
200    IBCAck {
201        /// The source channel of the IBC packet
202        channel: String,
203        /// The sequence number that the packet was sent with
204        sequence: u64,
205        /// String encoded version of the `Ack` as seen by OnAcknowledgementPacket(..)
206        ack: String,
207        /// Weather an `Ack` is a success of failure according to the transfer spec
208        success: bool,
209    },
210    #[serde(rename = "ibc_timeout")]
211    IBCTimeout {
212        /// The source channel of the IBC packet
213        channel: String,
214        /// The sequence number that the packet was sent with
215        sequence: u64,
216    },
217}
218
219#[cosmwasm_schema::cw_serde]
220pub struct ICS20CallbackPayload {
221    pub channel_id: String,
222    pub callback: Callback,
223}
224
225#[cosmwasm_schema::cw_serde]
226pub struct ICS20PacketIdentifier {
227    pub channel_id: String,
228    pub sequence: u64,
229}
230
231impl PrimaryKey<'_> for ICS20PacketIdentifier {
232    /// channel id
233    type Prefix = String;
234
235    /// channel id
236    type SubPrefix = String;
237
238    /// sequence
239    type Suffix = u64;
240
241    // sequence
242    type SuperSuffix = u64;
243
244    fn key(&self) -> Vec<cw_storage_plus::Key> {
245        let mut keys = self.channel_id.key();
246        keys.extend(self.sequence.key());
247        keys
248    }
249}