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