ibc_app_transfer/handler/
send_transfer.rs

1use ibc_app_transfer_types::error::TokenTransferError;
2use ibc_app_transfer_types::events::TransferEvent;
3use ibc_app_transfer_types::msgs::transfer::MsgTransfer;
4use ibc_app_transfer_types::{is_sender_chain_source, MODULE_ID_STR};
5use ibc_core::channel::context::{SendPacketExecutionContext, SendPacketValidationContext};
6use ibc_core::channel::handler::{send_packet_execute, send_packet_validate};
7use ibc_core::channel::types::packet::Packet;
8use ibc_core::handler::types::events::MessageEvent;
9use ibc_core::host::types::path::{ChannelEndPath, SeqSendPath};
10use ibc_core::primitives::prelude::*;
11use ibc_core::router::types::event::ModuleEvent;
12
13use crate::context::{TokenTransferExecutionContext, TokenTransferValidationContext};
14
15/// Initiate a token transfer. Equivalent to calling [`send_transfer_validate`], followed by [`send_transfer_execute`].
16pub fn send_transfer<SendPacketCtx, TokenCtx>(
17    send_packet_ctx_a: &mut SendPacketCtx,
18    token_ctx_a: &mut TokenCtx,
19    msg: MsgTransfer,
20) -> Result<(), TokenTransferError>
21where
22    SendPacketCtx: SendPacketExecutionContext,
23    TokenCtx: TokenTransferExecutionContext,
24{
25    send_transfer_validate(send_packet_ctx_a, token_ctx_a, msg.clone())?;
26    send_transfer_execute(send_packet_ctx_a, token_ctx_a, msg)
27}
28
29/// Validates the token transfer. If this succeeds, then it is legal to initiate the transfer with [`send_transfer_execute`].
30pub fn send_transfer_validate<SendPacketCtx, TokenCtx>(
31    send_packet_ctx_a: &SendPacketCtx,
32    token_ctx_a: &TokenCtx,
33    msg: MsgTransfer,
34) -> Result<(), TokenTransferError>
35where
36    SendPacketCtx: SendPacketValidationContext,
37    TokenCtx: TokenTransferValidationContext,
38{
39    token_ctx_a.can_send_coins()?;
40
41    let chan_end_path_on_a = ChannelEndPath::new(&msg.port_id_on_a, &msg.chan_id_on_a);
42    let chan_end_on_a = send_packet_ctx_a.channel_end(&chan_end_path_on_a)?;
43
44    let port_id_on_b = chan_end_on_a.counterparty().port_id().clone();
45    let chan_id_on_b = chan_end_on_a
46        .counterparty()
47        .channel_id()
48        .ok_or_else(|| TokenTransferError::MissingDestinationChannel {
49            port_id: msg.port_id_on_a.clone(),
50            channel_id: msg.chan_id_on_a.clone(),
51        })?
52        .clone();
53
54    let seq_send_path_on_a = SeqSendPath::new(&msg.port_id_on_a, &msg.chan_id_on_a);
55    let sequence = send_packet_ctx_a.get_next_sequence_send(&seq_send_path_on_a)?;
56
57    let token = &msg.packet_data.token;
58
59    let sender = token_ctx_a.sender_account(&msg.packet_data.sender)?;
60
61    if is_sender_chain_source(
62        msg.port_id_on_a.clone(),
63        msg.chan_id_on_a.clone(),
64        &token.denom,
65    ) {
66        token_ctx_a.escrow_coins_validate(
67            &sender,
68            &msg.port_id_on_a,
69            &msg.chan_id_on_a,
70            token,
71            &msg.packet_data.memo,
72        )?;
73    } else {
74        token_ctx_a.burn_coins_validate(&sender, token, &msg.packet_data.memo)?;
75    }
76
77    let packet = {
78        let data = serde_json::to_vec(&msg.packet_data)
79            .expect("PacketData's infallible Serialize impl failed");
80
81        Packet {
82            seq_on_a: sequence,
83            port_id_on_a: msg.port_id_on_a,
84            chan_id_on_a: msg.chan_id_on_a,
85            port_id_on_b,
86            chan_id_on_b,
87            data,
88            timeout_height_on_b: msg.timeout_height_on_b,
89            timeout_timestamp_on_b: msg.timeout_timestamp_on_b,
90        }
91    };
92
93    send_packet_validate(send_packet_ctx_a, &packet)?;
94
95    Ok(())
96}
97
98/// Executes the token transfer. A prior call to [`send_transfer_validate`] MUST have succeeded.
99pub fn send_transfer_execute<SendPacketCtx, TokenCtx>(
100    send_packet_ctx_a: &mut SendPacketCtx,
101    token_ctx_a: &mut TokenCtx,
102    msg: MsgTransfer,
103) -> Result<(), TokenTransferError>
104where
105    SendPacketCtx: SendPacketExecutionContext,
106    TokenCtx: TokenTransferExecutionContext,
107{
108    let chan_end_path_on_a = ChannelEndPath::new(&msg.port_id_on_a, &msg.chan_id_on_a);
109    let chan_end_on_a = send_packet_ctx_a.channel_end(&chan_end_path_on_a)?;
110
111    let port_on_b = chan_end_on_a.counterparty().port_id().clone();
112    let chan_on_b = chan_end_on_a
113        .counterparty()
114        .channel_id()
115        .ok_or_else(|| TokenTransferError::MissingDestinationChannel {
116            port_id: msg.port_id_on_a.clone(),
117            channel_id: msg.chan_id_on_a.clone(),
118        })?
119        .clone();
120
121    // get the next sequence
122    let seq_send_path_on_a = SeqSendPath::new(&msg.port_id_on_a, &msg.chan_id_on_a);
123    let sequence = send_packet_ctx_a.get_next_sequence_send(&seq_send_path_on_a)?;
124
125    let token = &msg.packet_data.token;
126
127    let sender = token_ctx_a.sender_account(&msg.packet_data.sender)?;
128
129    if is_sender_chain_source(
130        msg.port_id_on_a.clone(),
131        msg.chan_id_on_a.clone(),
132        &token.denom,
133    ) {
134        token_ctx_a.escrow_coins_execute(
135            &sender,
136            &msg.port_id_on_a,
137            &msg.chan_id_on_a,
138            token,
139            &msg.packet_data.memo,
140        )?;
141    } else {
142        token_ctx_a.burn_coins_execute(&sender, token, &msg.packet_data.memo)?;
143    }
144
145    let packet = {
146        let data = {
147            serde_json::to_vec(&msg.packet_data)
148                .expect("PacketData's infallible Serialize impl failed")
149        };
150
151        Packet {
152            seq_on_a: sequence,
153            port_id_on_a: msg.port_id_on_a,
154            chan_id_on_a: msg.chan_id_on_a,
155            port_id_on_b: port_on_b,
156            chan_id_on_b: chan_on_b,
157            data,
158            timeout_height_on_b: msg.timeout_height_on_b,
159            timeout_timestamp_on_b: msg.timeout_timestamp_on_b,
160        }
161    };
162
163    send_packet_execute(send_packet_ctx_a, packet)?;
164
165    {
166        send_packet_ctx_a.log_message(format!(
167            "IBC fungible token transfer: {} --({})--> {}",
168            msg.packet_data.sender, token, msg.packet_data.receiver
169        ))?;
170
171        let transfer_event = TransferEvent {
172            sender: msg.packet_data.sender,
173            receiver: msg.packet_data.receiver,
174            amount: msg.packet_data.token.amount,
175            denom: msg.packet_data.token.denom,
176            memo: msg.packet_data.memo,
177        };
178        send_packet_ctx_a.emit_ibc_event(ModuleEvent::from(transfer_event).into())?;
179
180        send_packet_ctx_a.emit_ibc_event(MessageEvent::Module(MODULE_ID_STR.to_string()).into())?;
181    }
182
183    Ok(())
184}