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}