ibc_app_transfer/handler/
on_recv_packet.rs

1use ibc_app_transfer_types::error::TokenTransferError;
2use ibc_app_transfer_types::events::DenomTraceEvent;
3use ibc_app_transfer_types::packet::PacketData;
4use ibc_app_transfer_types::{is_receiver_chain_source, TracePrefix};
5use ibc_core::channel::types::packet::Packet;
6use ibc_core::primitives::prelude::*;
7use ibc_core::router::types::module::ModuleExtras;
8
9use crate::context::TokenTransferExecutionContext;
10
11/// This function handles the transfer receiving logic.
12///
13/// Note that `send/mint_coins_validate` steps are performed on the host chain
14/// to validate accounts and token info. But the result is then used for
15/// execution on the IBC side, including storing acknowledgements and emitting
16/// events.
17pub fn process_recv_packet_execute<Ctx: TokenTransferExecutionContext>(
18    ctx_b: &mut Ctx,
19    packet: &Packet,
20    data: PacketData,
21) -> Result<ModuleExtras, (ModuleExtras, TokenTransferError)> {
22    ctx_b
23        .can_receive_coins()
24        .map_err(|err| (ModuleExtras::empty(), err.into()))?;
25
26    let receiver_account = ctx_b
27        .receiver_account(&data.receiver)
28        .map_err(|err| (ModuleExtras::empty(), err.into()))?;
29
30    let extras = if is_receiver_chain_source(
31        packet.port_id_on_a.clone(),
32        packet.chan_id_on_a.clone(),
33        &data.token.denom,
34    ) {
35        // sender chain is not the source, unescrow tokens
36        let prefix = TracePrefix::new(packet.port_id_on_a.clone(), packet.chan_id_on_a.clone());
37        let coin = {
38            let mut c = data.token;
39            c.denom.remove_trace_prefix(&prefix);
40            c
41        };
42
43        // Note: it is correct to do the validation here because `recv_packet()`
44        // works slightly differently. We do not have a
45        // `on_recv_packet_validate()` callback because regardless of whether or
46        // not the app succeeds to receive the packet, we want to run the
47        // `execute()` phase. And this is because the app failing to receive
48        // does not constitute a failure of the message processing.
49        // Specifically, when the app fails to receive, we need to return
50        // a `TokenTransferAcknowledgement::Error` acknowledgement, which
51        // gets relayed back to the sender so that the escrowed tokens
52        // can be refunded.
53        ctx_b
54            .unescrow_coins_validate(
55                &receiver_account,
56                &packet.port_id_on_b,
57                &packet.chan_id_on_b,
58                &coin,
59            )
60            .map_err(|err| (ModuleExtras::empty(), err.into()))?;
61        ctx_b
62            .unescrow_coins_execute(
63                &receiver_account,
64                &packet.port_id_on_b,
65                &packet.chan_id_on_b,
66                &coin,
67            )
68            .map_err(|err| (ModuleExtras::empty(), err.into()))?;
69
70        ModuleExtras::empty()
71    } else {
72        // sender chain is the source, mint vouchers
73        let prefix = TracePrefix::new(packet.port_id_on_b.clone(), packet.chan_id_on_b.clone());
74        let coin = {
75            let mut c = data.token;
76            c.denom.add_trace_prefix(prefix);
77            c
78        };
79
80        let extras = {
81            let denom_trace_event = DenomTraceEvent {
82                trace_hash: ctx_b.denom_hash_string(&coin.denom),
83                denom: coin.denom.clone(),
84            };
85            ModuleExtras {
86                events: vec![denom_trace_event.into()],
87                log: Vec::new(),
88            }
89        };
90
91        // Note: it is correct to do the validation here because `recv_packet()`
92        // works slightly differently. We do not have a
93        // `on_recv_packet_validate()` callback because regardless of whether or
94        // not the app succeeds to receive the packet, we want to run the
95        // `execute()` phase. And this is because the app failing to receive
96        // does not constitute a failure of the message processing.
97        // Specifically, when the app fails to receive, we need to return
98        // a `TokenTransferAcknowledgement::Error` acknowledgement, which
99        // gets relayed back to the sender so that the escrowed tokens
100        // can be refunded.
101        ctx_b
102            .mint_coins_validate(&receiver_account, &coin)
103            .map_err(|err| (extras.clone(), err.into()))?;
104
105        ctx_b
106            .mint_coins_execute(&receiver_account, &coin)
107            .map_err(|err| (extras.clone(), err.into()))?;
108
109        extras
110    };
111
112    Ok(extras)
113}