ibc_app_nft_transfer/handler/
send_transfer.rs1use ibc_core::channel::context::{SendPacketExecutionContext, SendPacketValidationContext};
2use ibc_core::channel::handler::{send_packet_execute, send_packet_validate};
3use ibc_core::channel::types::packet::Packet;
4use ibc_core::handler::types::events::MessageEvent;
5use ibc_core::host::types::path::{ChannelEndPath, SeqSendPath};
6use ibc_core::primitives::prelude::*;
7use ibc_core::router::types::event::ModuleEvent;
8
9use crate::context::{
10 NftClassContext, NftContext, NftTransferExecutionContext, NftTransferValidationContext,
11};
12use crate::types::error::NftTransferError;
13use crate::types::events::TransferEvent;
14use crate::types::msgs::transfer::MsgTransfer;
15use crate::types::{is_sender_chain_source, MODULE_ID_STR};
16
17pub fn send_nft_transfer<SendPacketCtx, TransferCtx>(
19 send_packet_ctx_a: &mut SendPacketCtx,
20 transfer_ctx: &mut TransferCtx,
21 msg: MsgTransfer,
22) -> Result<(), NftTransferError>
23where
24 SendPacketCtx: SendPacketExecutionContext,
25 TransferCtx: NftTransferExecutionContext,
26{
27 send_nft_transfer_validate(send_packet_ctx_a, transfer_ctx, msg.clone())?;
28 send_nft_transfer_execute(send_packet_ctx_a, transfer_ctx, msg)
29}
30
31pub fn send_nft_transfer_validate<SendPacketCtx, TransferCtx>(
33 send_packet_ctx_a: &SendPacketCtx,
34 transfer_ctx: &TransferCtx,
35 msg: MsgTransfer,
36) -> Result<(), NftTransferError>
37where
38 SendPacketCtx: SendPacketValidationContext,
39 TransferCtx: NftTransferValidationContext,
40{
41 transfer_ctx.can_send_nft()?;
42
43 let chan_end_path_on_a = ChannelEndPath::new(&msg.port_id_on_a, &msg.chan_id_on_a);
44 let chan_end_on_a = send_packet_ctx_a.channel_end(&chan_end_path_on_a)?;
45
46 let port_id_on_b = chan_end_on_a.counterparty().port_id().clone();
47 let chan_id_on_b = chan_end_on_a
48 .counterparty()
49 .channel_id()
50 .ok_or_else(|| NftTransferError::MissingDestinationChannel {
51 port_id: msg.port_id_on_a.clone(),
52 channel_id: msg.chan_id_on_a.clone(),
53 })?
54 .clone();
55
56 let seq_send_path_on_a = SeqSendPath::new(&msg.port_id_on_a, &msg.chan_id_on_a);
57 let sequence = send_packet_ctx_a.get_next_sequence_send(&seq_send_path_on_a)?;
58
59 let sender: TransferCtx::AccountId = transfer_ctx.sender_account(&msg.packet_data.sender)?;
60
61 let mut packet_data = msg.packet_data;
62 let class_id = &packet_data.class_id;
63 let token_ids = &packet_data.token_ids;
64 if let Some(uris) = &mut packet_data.token_uris {
66 uris.clear();
67 }
68 if let Some(data) = &mut packet_data.token_data {
69 data.clear();
70 }
71 for token_id in token_ids.as_ref() {
72 if is_sender_chain_source(msg.port_id_on_a.clone(), msg.chan_id_on_a.clone(), class_id) {
73 transfer_ctx.escrow_nft_validate(
74 &sender,
75 &msg.port_id_on_a,
76 &msg.chan_id_on_a,
77 class_id,
78 token_id,
79 &packet_data.memo.clone().unwrap_or("".into()),
80 )?;
81 } else {
82 transfer_ctx.burn_nft_validate(
83 &sender,
84 class_id,
85 token_id,
86 &packet_data.memo.clone().unwrap_or("".into()),
87 )?;
88 }
89 let nft = transfer_ctx.get_nft(class_id, token_id)?;
90 if let (Some(uri), Some(data)) = (nft.get_uri(), nft.get_data()) {
92 match &mut packet_data.token_uris {
93 Some(uris) => uris.push(uri.clone()),
94 None => packet_data.token_uris = Some(vec![uri.clone()]),
95 }
96 match &mut packet_data.token_data {
97 Some(token_data) => token_data.push(data.clone()),
98 None => packet_data.token_data = Some(vec![data.clone()]),
99 }
100 }
101 }
102
103 packet_data.validate_basic()?;
104
105 let nft_class = transfer_ctx.get_nft_class(class_id)?;
106 packet_data.class_uri = nft_class.get_uri().cloned();
107 packet_data.class_data = nft_class.get_data().cloned();
108
109 let packet = {
110 let data = serde_json::to_vec(&packet_data)
111 .expect("PacketData's infallible Serialize impl failed");
112
113 Packet {
114 seq_on_a: sequence,
115 port_id_on_a: msg.port_id_on_a,
116 chan_id_on_a: msg.chan_id_on_a,
117 port_id_on_b,
118 chan_id_on_b,
119 data,
120 timeout_height_on_b: msg.timeout_height_on_b,
121 timeout_timestamp_on_b: msg.timeout_timestamp_on_b,
122 }
123 };
124
125 send_packet_validate(send_packet_ctx_a, &packet)?;
126
127 Ok(())
128}
129
130pub fn send_nft_transfer_execute<SendPacketCtx, TransferCtx>(
132 send_packet_ctx_a: &mut SendPacketCtx,
133 transfer_ctx: &mut TransferCtx,
134 msg: MsgTransfer,
135) -> Result<(), NftTransferError>
136where
137 SendPacketCtx: SendPacketExecutionContext,
138 TransferCtx: NftTransferExecutionContext,
139{
140 let chan_end_path_on_a = ChannelEndPath::new(&msg.port_id_on_a, &msg.chan_id_on_a);
141 let chan_end_on_a = send_packet_ctx_a.channel_end(&chan_end_path_on_a)?;
142
143 let port_on_b = chan_end_on_a.counterparty().port_id().clone();
144 let chan_on_b = chan_end_on_a
145 .counterparty()
146 .channel_id()
147 .ok_or_else(|| NftTransferError::MissingDestinationChannel {
148 port_id: msg.port_id_on_a.clone(),
149 channel_id: msg.chan_id_on_a.clone(),
150 })?
151 .clone();
152
153 let seq_send_path_on_a = SeqSendPath::new(&msg.port_id_on_a, &msg.chan_id_on_a);
155 let sequence = send_packet_ctx_a.get_next_sequence_send(&seq_send_path_on_a)?;
156
157 let sender = transfer_ctx.sender_account(&msg.packet_data.sender)?;
158
159 let mut packet_data = msg.packet_data;
160 let class_id = &packet_data.class_id;
161 let token_ids = &packet_data.token_ids;
162 if let Some(uris) = &mut packet_data.token_uris {
164 uris.clear();
165 uris.reserve_exact(token_ids.0.len());
166 }
167 if let Some(data) = &mut packet_data.token_data {
168 data.clear();
169 data.reserve_exact(token_ids.0.len());
170 }
171 for token_id in token_ids.as_ref() {
172 if is_sender_chain_source(msg.port_id_on_a.clone(), msg.chan_id_on_a.clone(), class_id) {
173 transfer_ctx.escrow_nft_execute(
174 &sender,
175 &msg.port_id_on_a,
176 &msg.chan_id_on_a,
177 class_id,
178 token_id,
179 &packet_data.memo.clone().unwrap_or("".into()),
180 )?;
181 } else {
182 transfer_ctx.burn_nft_execute(
183 &sender,
184 class_id,
185 token_id,
186 &packet_data.memo.clone().unwrap_or("".into()),
187 )?;
188 }
189 let nft = transfer_ctx.get_nft(class_id, token_id)?;
190 if let (Some(uri), Some(data)) = (nft.get_uri(), nft.get_data()) {
192 packet_data
193 .token_uris
194 .get_or_insert_with(|| Vec::with_capacity(token_ids.0.len()))
195 .push(uri.clone());
196 packet_data
197 .token_data
198 .get_or_insert_with(|| Vec::with_capacity(token_ids.0.len()))
199 .push(data.clone());
200 }
201 }
202
203 let nft_class = transfer_ctx.get_nft_class(class_id)?;
204 packet_data.class_uri = nft_class.get_uri().cloned();
205 packet_data.class_data = nft_class.get_data().cloned();
206
207 let packet = {
208 let data = {
209 serde_json::to_vec(&packet_data).expect("PacketData's infallible Serialize impl failed")
210 };
211
212 Packet {
213 seq_on_a: sequence,
214 port_id_on_a: msg.port_id_on_a,
215 chan_id_on_a: msg.chan_id_on_a,
216 port_id_on_b: port_on_b,
217 chan_id_on_b: chan_on_b,
218 data,
219 timeout_height_on_b: msg.timeout_height_on_b,
220 timeout_timestamp_on_b: msg.timeout_timestamp_on_b,
221 }
222 };
223
224 send_packet_execute(send_packet_ctx_a, packet)?;
225
226 {
227 send_packet_ctx_a.log_message(format!(
228 "IBC NFT transfer: {} --({}, [{}])--> {}",
229 packet_data.sender, class_id, token_ids, packet_data.receiver
230 ))?;
231
232 let transfer_event = TransferEvent {
233 sender: packet_data.sender,
234 receiver: packet_data.receiver,
235 class: packet_data.class_id,
236 tokens: packet_data.token_ids,
237 memo: packet_data.memo.unwrap_or("".into()),
238 };
239 send_packet_ctx_a.emit_ibc_event(ModuleEvent::from(transfer_event).into())?;
240
241 send_packet_ctx_a.emit_ibc_event(MessageEvent::Module(MODULE_ID_STR.to_string()).into())?;
242 }
243
244 Ok(())
245}