ibc_app_transfer/
module.rs

1use ibc_app_transfer_types::error::TokenTransferError;
2use ibc_app_transfer_types::events::{AckEvent, AckStatusEvent, RecvEvent, TimeoutEvent};
3use ibc_app_transfer_types::packet::PacketData;
4use ibc_app_transfer_types::{ack_success_b64, VERSION};
5use ibc_core::channel::types::acknowledgement::{Acknowledgement, AcknowledgementStatus};
6use ibc_core::channel::types::channel::{Counterparty, Order};
7use ibc_core::channel::types::packet::Packet;
8use ibc_core::channel::types::Version;
9use ibc_core::host::types::identifiers::{ChannelId, ConnectionId, PortId};
10use ibc_core::primitives::prelude::*;
11use ibc_core::primitives::Signer;
12use ibc_core::router::types::module::ModuleExtras;
13
14use crate::context::{TokenTransferExecutionContext, TokenTransferValidationContext};
15use crate::handler::{
16    process_recv_packet_execute, refund_packet_token_execute, refund_packet_token_validate,
17};
18
19pub fn on_chan_open_init_validate(
20    ctx: &impl TokenTransferValidationContext,
21    order: Order,
22    _connection_hops: &[ConnectionId],
23    port_id: &PortId,
24    _channel_id: &ChannelId,
25    _counterparty: &Counterparty,
26    version: &Version,
27) -> Result<(), TokenTransferError> {
28    if order != Order::Unordered {
29        return Err(TokenTransferError::MismatchedChannelOrders {
30            expected: Order::Unordered,
31            actual: order,
32        });
33    }
34    let bound_port = ctx.get_port()?;
35    if port_id != &bound_port {
36        return Err(TokenTransferError::MismatchedPortIds {
37            actual: port_id.clone(),
38            expected: bound_port,
39        });
40    }
41
42    if !version.is_empty() {
43        version.verify_is_expected(Version::new(VERSION.to_string()))?;
44    }
45
46    Ok(())
47}
48
49pub fn on_chan_open_init_execute(
50    _ctx: &mut impl TokenTransferExecutionContext,
51    _order: Order,
52    _connection_hops: &[ConnectionId],
53    _port_id: &PortId,
54    _channel_id: &ChannelId,
55    _counterparty: &Counterparty,
56    _version: &Version,
57) -> Result<(ModuleExtras, Version), TokenTransferError> {
58    Ok((ModuleExtras::empty(), Version::new(VERSION.to_string())))
59}
60
61pub fn on_chan_open_try_validate(
62    _ctx: &impl TokenTransferValidationContext,
63    order: Order,
64    _connection_hops: &[ConnectionId],
65    _port_id: &PortId,
66    _channel_id: &ChannelId,
67    _counterparty: &Counterparty,
68    counterparty_version: &Version,
69) -> Result<(), TokenTransferError> {
70    if order != Order::Unordered {
71        return Err(TokenTransferError::MismatchedChannelOrders {
72            expected: Order::Unordered,
73            actual: order,
74        });
75    }
76
77    counterparty_version.verify_is_expected(Version::new(VERSION.to_string()))?;
78
79    Ok(())
80}
81
82pub fn on_chan_open_try_execute(
83    _ctx: &mut impl TokenTransferExecutionContext,
84    _order: Order,
85    _connection_hops: &[ConnectionId],
86    _port_id: &PortId,
87    _channel_id: &ChannelId,
88    _counterparty: &Counterparty,
89    _counterparty_version: &Version,
90) -> Result<(ModuleExtras, Version), TokenTransferError> {
91    Ok((ModuleExtras::empty(), Version::new(VERSION.to_string())))
92}
93
94pub fn on_chan_open_ack_validate(
95    _ctx: &impl TokenTransferValidationContext,
96    _port_id: &PortId,
97    _channel_id: &ChannelId,
98    counterparty_version: &Version,
99) -> Result<(), TokenTransferError> {
100    counterparty_version.verify_is_expected(Version::new(VERSION.to_string()))?;
101
102    Ok(())
103}
104
105pub fn on_chan_open_ack_execute(
106    _ctx: &mut impl TokenTransferExecutionContext,
107    _port_id: &PortId,
108    _channel_id: &ChannelId,
109    _counterparty_version: &Version,
110) -> Result<ModuleExtras, TokenTransferError> {
111    Ok(ModuleExtras::empty())
112}
113
114pub fn on_chan_open_confirm_validate(
115    _ctx: &impl TokenTransferValidationContext,
116    _port_id: &PortId,
117    _channel_id: &ChannelId,
118) -> Result<(), TokenTransferError> {
119    Ok(())
120}
121
122pub fn on_chan_open_confirm_execute(
123    _ctx: &mut impl TokenTransferExecutionContext,
124    _port_id: &PortId,
125    _channel_id: &ChannelId,
126) -> Result<ModuleExtras, TokenTransferError> {
127    Ok(ModuleExtras::empty())
128}
129
130pub fn on_chan_close_init_validate(
131    _ctx: &impl TokenTransferValidationContext,
132    _port_id: &PortId,
133    _channel_id: &ChannelId,
134) -> Result<(), TokenTransferError> {
135    Err(TokenTransferError::InvalidClosedChannel)
136}
137
138pub fn on_chan_close_init_execute(
139    _ctx: &mut impl TokenTransferExecutionContext,
140    _port_id: &PortId,
141    _channel_id: &ChannelId,
142) -> Result<ModuleExtras, TokenTransferError> {
143    Err(TokenTransferError::InvalidClosedChannel)
144}
145
146pub fn on_chan_close_confirm_validate(
147    _ctx: &impl TokenTransferValidationContext,
148    _port_id: &PortId,
149    _channel_id: &ChannelId,
150) -> Result<(), TokenTransferError> {
151    Ok(())
152}
153
154pub fn on_chan_close_confirm_execute(
155    _ctx: &mut impl TokenTransferExecutionContext,
156    _port_id: &PortId,
157    _channel_id: &ChannelId,
158) -> Result<ModuleExtras, TokenTransferError> {
159    Ok(ModuleExtras::empty())
160}
161
162pub fn on_recv_packet_execute(
163    ctx_b: &mut impl TokenTransferExecutionContext,
164    packet: &Packet,
165) -> (ModuleExtras, Acknowledgement) {
166    let Ok(data) = serde_json::from_slice::<PacketData>(&packet.data) else {
167        let ack =
168            AcknowledgementStatus::error(TokenTransferError::FailedToDeserializePacketData.into());
169        return (ModuleExtras::empty(), ack.into());
170    };
171
172    let (mut extras, ack) = match process_recv_packet_execute(ctx_b, packet, data.clone()) {
173        Ok(extras) => (extras, AcknowledgementStatus::success(ack_success_b64())),
174        Err((extras, error)) => (extras, AcknowledgementStatus::error(error.into())),
175    };
176
177    let recv_event = RecvEvent {
178        sender: data.sender,
179        receiver: data.receiver,
180        denom: data.token.denom,
181        amount: data.token.amount,
182        memo: data.memo,
183        success: ack.is_successful(),
184    };
185    extras.events.push(recv_event.into());
186
187    (extras, ack.into())
188}
189
190pub fn on_acknowledgement_packet_validate<Ctx>(
191    ctx: &Ctx,
192    packet: &Packet,
193    acknowledgement: &Acknowledgement,
194    _relayer: &Signer,
195) -> Result<(), TokenTransferError>
196where
197    Ctx: TokenTransferValidationContext,
198{
199    let data = serde_json::from_slice::<PacketData>(&packet.data)
200        .map_err(|_| TokenTransferError::FailedToDeserializePacketData)?;
201
202    let acknowledgement = serde_json::from_slice::<AcknowledgementStatus>(acknowledgement.as_ref())
203        .map_err(|_| TokenTransferError::FailedToDeserializeAck)?;
204
205    if !acknowledgement.is_successful() {
206        refund_packet_token_validate(ctx, packet, &data)?;
207    }
208
209    Ok(())
210}
211
212pub fn on_acknowledgement_packet_execute(
213    ctx: &mut impl TokenTransferExecutionContext,
214    packet: &Packet,
215    acknowledgement: &Acknowledgement,
216    _relayer: &Signer,
217) -> (ModuleExtras, Result<(), TokenTransferError>) {
218    let Ok(data) = serde_json::from_slice::<PacketData>(&packet.data) else {
219        return (
220            ModuleExtras::empty(),
221            Err(TokenTransferError::FailedToDeserializePacketData),
222        );
223    };
224
225    let Ok(acknowledgement) =
226        serde_json::from_slice::<AcknowledgementStatus>(acknowledgement.as_ref())
227    else {
228        return (
229            ModuleExtras::empty(),
230            Err(TokenTransferError::FailedToDeserializeAck),
231        );
232    };
233
234    if !acknowledgement.is_successful() {
235        if let Err(err) = refund_packet_token_execute(ctx, packet, &data) {
236            return (ModuleExtras::empty(), Err(err));
237        }
238    }
239
240    let ack_event = AckEvent {
241        sender: data.sender,
242        receiver: data.receiver,
243        denom: data.token.denom,
244        amount: data.token.amount,
245        memo: data.memo,
246        acknowledgement: acknowledgement.clone(),
247    };
248
249    let extras = ModuleExtras {
250        events: vec![ack_event.into(), AckStatusEvent { acknowledgement }.into()],
251        log: Vec::new(),
252    };
253
254    (extras, Ok(()))
255}
256
257pub fn on_timeout_packet_validate<Ctx>(
258    ctx: &Ctx,
259    packet: &Packet,
260    _relayer: &Signer,
261) -> Result<(), TokenTransferError>
262where
263    Ctx: TokenTransferValidationContext,
264{
265    let data = serde_json::from_slice::<PacketData>(&packet.data)
266        .map_err(|_| TokenTransferError::FailedToDeserializePacketData)?;
267
268    refund_packet_token_validate(ctx, packet, &data)?;
269
270    Ok(())
271}
272
273pub fn on_timeout_packet_execute(
274    ctx: &mut impl TokenTransferExecutionContext,
275    packet: &Packet,
276    _relayer: &Signer,
277) -> (ModuleExtras, Result<(), TokenTransferError>) {
278    let Ok(data) = serde_json::from_slice::<PacketData>(&packet.data) else {
279        return (
280            ModuleExtras::empty(),
281            Err(TokenTransferError::FailedToDeserializePacketData),
282        );
283    };
284
285    if let Err(err) = refund_packet_token_execute(ctx, packet, &data) {
286        return (ModuleExtras::empty(), Err(err));
287    }
288
289    let timeout_event = TimeoutEvent {
290        refund_receiver: data.sender,
291        refund_denom: data.token.denom,
292        refund_amount: data.token.amount,
293        memo: data.memo,
294    };
295
296    let extras = ModuleExtras {
297        events: vec![timeout_event.into()],
298        log: Vec::new(),
299    };
300
301    (extras, Ok(()))
302}
303
304#[cfg(test)]
305mod test {
306    use super::*;
307
308    #[test]
309    fn test_ack_ser() {
310        fn ser_json_assert_eq(ack: AcknowledgementStatus, json_str: &str) {
311            let ser = serde_json::to_string(&ack).unwrap();
312            assert_eq!(ser, json_str)
313        }
314
315        ser_json_assert_eq(
316            AcknowledgementStatus::success(ack_success_b64()),
317            r#"{"result":"AQ=="}"#,
318        );
319        ser_json_assert_eq(
320            AcknowledgementStatus::error(TokenTransferError::FailedToDeserializePacketData.into()),
321            r#"{"error":"failed to deserialize packet data"}"#,
322        );
323    }
324
325    #[test]
326    fn test_ack_success_to_vec() {
327        let ack_success: Vec<u8> = AcknowledgementStatus::success(ack_success_b64()).into();
328
329        // Check that it's the same output as ibc-go
330        // Note: this also implicitly checks that the ack bytes are non-empty,
331        // which would make the conversion to `Acknowledgement` panic
332        assert_eq!(ack_success, br#"{"result":"AQ=="}"#);
333    }
334
335    #[test]
336    fn test_ack_error_to_vec() {
337        let ack_error: Vec<u8> =
338            AcknowledgementStatus::error(TokenTransferError::FailedToDeserializePacketData.into())
339                .into();
340
341        // Check that it's the same output as ibc-go
342        // Note: this also implicitly checks that the ack bytes are non-empty,
343        // which would make the conversion to `Acknowledgement` panic
344        assert_eq!(
345            ack_error,
346            br#"{"error":"failed to deserialize packet data"}"#
347        );
348    }
349
350    #[test]
351    fn test_ack_de() {
352        fn de_json_assert_eq(json_str: &str, ack: AcknowledgementStatus) {
353            let de = serde_json::from_str::<AcknowledgementStatus>(json_str).unwrap();
354            assert_eq!(de, ack)
355        }
356
357        de_json_assert_eq(
358            r#"{"result":"AQ=="}"#,
359            AcknowledgementStatus::success(ack_success_b64()),
360        );
361        de_json_assert_eq(
362            r#"{"error":"failed to deserialize packet data"}"#,
363            AcknowledgementStatus::error(TokenTransferError::FailedToDeserializePacketData.into()),
364        );
365
366        assert!(serde_json::from_str::<AcknowledgementStatus>(r#"{"success":"AQ=="}"#).is_err());
367    }
368}