1use crate::{AssetsInHolding, Weight};
18use core::result::Result;
19use xcm::latest::{Asset, Error as XcmError, Location, Result as XcmResult, XcmContext};
20
21pub trait TransactAsset {
31 fn can_check_in(_origin: &Location, _what: &Asset, _context: &XcmContext) -> XcmResult {
35 Err(XcmError::Unimplemented)
36 }
37
38 fn check_in(_origin: &Location, _what: &Asset, _context: &XcmContext) {}
56
57 fn can_check_out(_dest: &Location, _what: &Asset, _context: &XcmContext) -> XcmResult {
61 Err(XcmError::Unimplemented)
62 }
63
64 fn check_out(_dest: &Location, _what: &Asset, _context: &XcmContext) {}
78
79 fn deposit_asset(
83 what: AssetsInHolding,
84 _who: &Location,
85 _context: Option<&XcmContext>,
86 ) -> Result<(), (AssetsInHolding, XcmError)> {
87 Err((what, XcmError::Unimplemented))
88 }
89
90 fn deposit_asset_with_surplus(
95 what: AssetsInHolding,
96 who: &Location,
97 context: Option<&XcmContext>,
98 ) -> Result<Weight, (AssetsInHolding, XcmError)> {
99 Self::deposit_asset(what, who, context).map(|()| Weight::zero())
100 }
101
102 fn withdraw_asset(
112 _what: &Asset,
113 _who: &Location,
114 _maybe_context: Option<&XcmContext>,
115 ) -> Result<AssetsInHolding, XcmError> {
116 Err(XcmError::Unimplemented)
117 }
118
119 fn withdraw_asset_with_surplus(
124 what: &Asset,
125 who: &Location,
126 maybe_context: Option<&XcmContext>,
127 ) -> Result<(AssetsInHolding, Weight), XcmError> {
128 Self::withdraw_asset(what, who, maybe_context).map(|assets| (assets, Weight::zero()))
129 }
130
131 fn internal_transfer_asset(
141 _asset: &Asset,
142 _from: &Location,
143 _to: &Location,
144 _context: &XcmContext,
145 ) -> Result<Asset, XcmError> {
146 Err(XcmError::Unimplemented)
147 }
148
149 fn internal_transfer_asset_with_surplus(
155 asset: &Asset,
156 from: &Location,
157 to: &Location,
158 context: &XcmContext,
159 ) -> Result<(Asset, Weight), XcmError> {
160 Self::internal_transfer_asset(asset, from, to, context).map(|asset| (asset, Weight::zero()))
161 }
162
163 fn transfer_asset(
168 asset: &Asset,
169 from: &Location,
170 to: &Location,
171 context: &XcmContext,
172 ) -> Result<Asset, XcmError> {
173 match Self::internal_transfer_asset(asset, from, to, context) {
174 Err(XcmError::AssetNotFound | XcmError::Unimplemented) => {
175 let credit = Self::withdraw_asset(asset, from, Some(context))?;
176 Self::deposit_asset(credit, to, Some(context)).map_err(|(unspent, error)| {
177 let _ = Self::deposit_asset(unspent, from, Some(context));
179 error
180 })?;
181 Ok(asset.clone())
182 },
183 result => result,
184 }
185 }
186
187 fn transfer_asset_with_surplus(
193 asset: &Asset,
194 from: &Location,
195 to: &Location,
196 context: &XcmContext,
197 ) -> Result<(Asset, Weight), XcmError> {
198 match Self::internal_transfer_asset_with_surplus(asset, from, to, context) {
199 Err(XcmError::AssetNotFound | XcmError::Unimplemented) => {
200 let (credit, withdraw_surplus) =
201 Self::withdraw_asset_with_surplus(asset, from, Some(context))?;
202 let deposit_surplus = Self::deposit_asset_with_surplus(credit, to, Some(context))
203 .map_err(|(unspent, error)| {
204 let _ = Self::deposit_asset(unspent, from, Some(context));
206 error
207 })?;
208 let total_surplus = withdraw_surplus.saturating_add(deposit_surplus);
209 Ok((asset.clone(), total_surplus))
210 },
211 result => result,
212 }
213 }
214
215 fn mint_asset(_what: &Asset, _context: &XcmContext) -> Result<AssetsInHolding, XcmError> {
220 Err(XcmError::Unimplemented)
221 }
222}
223
224#[impl_trait_for_tuples::impl_for_tuples(30)]
225impl TransactAsset for Tuple {
226 fn can_check_in(origin: &Location, what: &Asset, context: &XcmContext) -> XcmResult {
227 for_tuples!( #(
228 match Tuple::can_check_in(origin, what, context) {
229 Err(XcmError::AssetNotFound) | Err(XcmError::Unimplemented) => (),
230 r => return r,
231 }
232 )* );
233 tracing::trace!(
234 target: "xcm::TransactAsset::can_check_in",
235 ?what,
236 ?origin,
237 ?context,
238 "asset not found",
239 );
240 Err(XcmError::AssetNotFound)
241 }
242
243 fn check_in(origin: &Location, what: &Asset, context: &XcmContext) {
244 for_tuples!( #(
245 Tuple::check_in(origin, what, context);
246 )* );
247 }
248
249 fn can_check_out(dest: &Location, what: &Asset, context: &XcmContext) -> XcmResult {
250 for_tuples!( #(
251 match Tuple::can_check_out(dest, what, context) {
252 Err(XcmError::AssetNotFound) | Err(XcmError::Unimplemented) => (),
253 r => return r,
254 }
255 )* );
256 tracing::trace!(
257 target: "xcm::TransactAsset::can_check_out",
258 ?what,
259 ?dest,
260 ?context,
261 "asset not found",
262 );
263 Err(XcmError::AssetNotFound)
264 }
265
266 fn check_out(dest: &Location, what: &Asset, context: &XcmContext) {
267 for_tuples!( #(
268 Tuple::check_out(dest, what, context);
269 )* );
270 }
271
272 fn deposit_asset(
273 mut what: AssetsInHolding,
274 who: &Location,
275 context: Option<&XcmContext>,
276 ) -> Result<(), (AssetsInHolding, XcmError)> {
277 for_tuples!( #(
278 match Tuple::deposit_asset(what, who, context) {
279 Err((unspent, XcmError::AssetNotFound)) | Err((unspent, XcmError::Unimplemented)) => {
280 what = unspent;
281 },
283 r => return r,
284 }
285 )* );
286 tracing::trace!(
287 target: "xcm::TransactAsset::deposit_asset",
288 ?what,
289 ?who,
290 ?context,
291 "did not deposit asset",
292 );
293 Err((what, XcmError::AssetNotFound))
294 }
295
296 fn deposit_asset_with_surplus(
297 mut what: AssetsInHolding,
298 who: &Location,
299 context: Option<&XcmContext>,
300 ) -> Result<Weight, (AssetsInHolding, XcmError)> {
301 for_tuples!( #(
302 match Tuple::deposit_asset_with_surplus(what, who, context) {
303 Err((unspent, XcmError::AssetNotFound)) | Err((unspent, XcmError::Unimplemented)) => {
304 what = unspent;
305 },
307 r => return r,
308 }
309 )* );
310 tracing::trace!(
311 target: "xcm::TransactAsset::deposit_asset_with_surplus",
312 ?what,
313 ?who,
314 ?context,
315 "did not deposit asset",
316 );
317 Err((what, XcmError::AssetNotFound))
318 }
319
320 fn withdraw_asset(
321 what: &Asset,
322 who: &Location,
323 maybe_context: Option<&XcmContext>,
324 ) -> Result<AssetsInHolding, XcmError> {
325 for_tuples!( #(
326 match Tuple::withdraw_asset(what, who, maybe_context) {
327 Err(XcmError::AssetNotFound) | Err(XcmError::Unimplemented) => (),
328 r => return r,
329 }
330 )* );
331 tracing::trace!(
332 target: "xcm::TransactAsset::withdraw_asset",
333 ?what,
334 ?who,
335 ?maybe_context,
336 "did not withdraw asset",
337 );
338 Err(XcmError::AssetNotFound)
339 }
340
341 fn withdraw_asset_with_surplus(
342 what: &Asset,
343 who: &Location,
344 maybe_context: Option<&XcmContext>,
345 ) -> Result<(AssetsInHolding, Weight), XcmError> {
346 for_tuples!( #(
347 match Tuple::withdraw_asset_with_surplus(what, who, maybe_context) {
348 Err(XcmError::AssetNotFound) | Err(XcmError::Unimplemented) => (),
349 r => return r,
350 }
351 )* );
352 tracing::trace!(
353 target: "xcm::TransactAsset::withdraw_asset_with_surplus",
354 ?what,
355 ?who,
356 ?maybe_context,
357 "did not withdraw asset",
358 );
359 Err(XcmError::AssetNotFound)
360 }
361
362 fn internal_transfer_asset(
363 what: &Asset,
364 from: &Location,
365 to: &Location,
366 context: &XcmContext,
367 ) -> Result<Asset, XcmError> {
368 for_tuples!( #(
369 match Tuple::internal_transfer_asset(what, from, to, context) {
370 Err(XcmError::AssetNotFound) | Err(XcmError::Unimplemented) => (),
371 r => return r,
372 }
373 )* );
374 tracing::trace!(
375 target: "xcm::TransactAsset::internal_transfer_asset",
376 ?what,
377 ?from,
378 ?to,
379 ?context,
380 "did not transfer asset",
381 );
382 Err(XcmError::AssetNotFound)
383 }
384
385 fn internal_transfer_asset_with_surplus(
386 what: &Asset,
387 from: &Location,
388 to: &Location,
389 context: &XcmContext,
390 ) -> Result<(Asset, Weight), XcmError> {
391 for_tuples!( #(
392 match Tuple::internal_transfer_asset_with_surplus(what, from, to, context) {
393 Err(XcmError::AssetNotFound) | Err(XcmError::Unimplemented) => (),
394 r => return r,
395 }
396 )* );
397 tracing::trace!(
398 target: "xcm::TransactAsset::internal_transfer_asset_with_surplus",
399 ?what,
400 ?from,
401 ?to,
402 ?context,
403 "did not transfer asset",
404 );
405 Err(XcmError::AssetNotFound)
406 }
407
408 fn mint_asset(what: &Asset, context: &XcmContext) -> Result<AssetsInHolding, XcmError> {
409 for_tuples!( #(
410 match Tuple::mint_asset(what, context) {
411 Err(XcmError::AssetNotFound) | Err(XcmError::Unimplemented) => (),
412 r => return r,
413 }
414 )* );
415 tracing::trace!(
416 target: "xcm::TransactAsset::mint_asset",
417 ?what,
418 ?context,
419 "no match. did not mint asset",
420 );
421 Err(XcmError::AssetNotFound)
422 }
423}
424
425#[cfg(test)]
426mod tests {
427 use super::*;
428 use xcm::latest::{AssetId, Junctions::Here};
429
430 pub struct UnimplementedTransactor;
431 impl TransactAsset for UnimplementedTransactor {}
432
433 pub struct NotFoundTransactor;
434 impl TransactAsset for NotFoundTransactor {
435 fn can_check_in(_origin: &Location, _what: &Asset, _context: &XcmContext) -> XcmResult {
436 Err(XcmError::AssetNotFound)
437 }
438
439 fn can_check_out(_dest: &Location, _what: &Asset, _context: &XcmContext) -> XcmResult {
440 Err(XcmError::AssetNotFound)
441 }
442
443 fn deposit_asset(
444 what: AssetsInHolding,
445 _who: &Location,
446 _context: Option<&XcmContext>,
447 ) -> Result<(), (AssetsInHolding, XcmError)> {
448 Err((what, XcmError::AssetNotFound))
449 }
450
451 fn withdraw_asset(
452 _what: &Asset,
453 _who: &Location,
454 _context: Option<&XcmContext>,
455 ) -> Result<AssetsInHolding, XcmError> {
456 Err(XcmError::AssetNotFound)
457 }
458
459 fn internal_transfer_asset(
460 _what: &Asset,
461 _from: &Location,
462 _to: &Location,
463 _context: &XcmContext,
464 ) -> Result<Asset, XcmError> {
465 Err(XcmError::AssetNotFound)
466 }
467
468 fn mint_asset(_: &Asset, _: &XcmContext) -> Result<AssetsInHolding, XcmError> {
469 Err(XcmError::AssetNotFound)
470 }
471 }
472
473 pub struct OverflowTransactor;
474 impl TransactAsset for OverflowTransactor {
475 fn can_check_in(_origin: &Location, _what: &Asset, _context: &XcmContext) -> XcmResult {
476 Err(XcmError::Overflow)
477 }
478
479 fn can_check_out(_dest: &Location, _what: &Asset, _context: &XcmContext) -> XcmResult {
480 Err(XcmError::Overflow)
481 }
482
483 fn deposit_asset(
484 what: AssetsInHolding,
485 _who: &Location,
486 _context: Option<&XcmContext>,
487 ) -> Result<(), (AssetsInHolding, XcmError)> {
488 Err((what, XcmError::Overflow))
489 }
490
491 fn withdraw_asset(
492 _what: &Asset,
493 _who: &Location,
494 _context: Option<&XcmContext>,
495 ) -> Result<AssetsInHolding, XcmError> {
496 Err(XcmError::Overflow)
497 }
498
499 fn internal_transfer_asset(
500 _what: &Asset,
501 _from: &Location,
502 _to: &Location,
503 _context: &XcmContext,
504 ) -> Result<Asset, XcmError> {
505 Err(XcmError::Overflow)
506 }
507
508 fn mint_asset(_: &Asset, _: &XcmContext) -> Result<AssetsInHolding, XcmError> {
509 Err(XcmError::Overflow)
510 }
511 }
512
513 pub struct SuccessfulTransactor;
514 impl TransactAsset for SuccessfulTransactor {
515 fn can_check_in(_origin: &Location, _what: &Asset, _context: &XcmContext) -> XcmResult {
516 Ok(())
517 }
518
519 fn can_check_out(_dest: &Location, _what: &Asset, _context: &XcmContext) -> XcmResult {
520 Ok(())
521 }
522
523 fn deposit_asset(
524 _what: AssetsInHolding,
525 _who: &Location,
526 _context: Option<&XcmContext>,
527 ) -> Result<(), (AssetsInHolding, XcmError)> {
528 Ok(())
529 }
530
531 fn withdraw_asset(
532 what: &Asset,
533 _who: &Location,
534 _context: Option<&XcmContext>,
535 ) -> Result<AssetsInHolding, XcmError> {
536 Ok(asset_to_holding(what.clone()))
537 }
538
539 fn mint_asset(what: &Asset, _context: &XcmContext) -> Result<AssetsInHolding, XcmError> {
540 Ok(asset_to_holding(what.clone()))
541 }
542
543 fn internal_transfer_asset(
544 _what: &Asset,
545 _from: &Location,
546 _to: &Location,
547 _context: &XcmContext,
548 ) -> Result<Asset, XcmError> {
549 Ok(Asset::from((AssetId(Location::here()), 42u128)))
550 }
551 }
552
553 fn asset_to_holding(asset: Asset) -> AssetsInHolding {
555 crate::test_helpers::mock_asset_to_holding(asset)
556 }
557
558 #[test]
559 fn defaults_to_asset_not_found() {
560 type MultiTransactor =
561 (UnimplementedTransactor, NotFoundTransactor, UnimplementedTransactor);
562
563 let asset: Asset = (Here, 1u128).into();
564 let assets_in_holding: AssetsInHolding = asset_to_holding(asset);
565 assert_eq!(
566 MultiTransactor::deposit_asset(
567 assets_in_holding,
568 &Here.into(),
569 Some(&XcmContext::with_message_id([0; 32])),
570 )
571 .map_err(|(_, e)| e),
572 Err(XcmError::AssetNotFound)
573 );
574 }
575
576 #[test]
577 fn unimplemented_and_not_found_continue_iteration() {
578 type MultiTransactor = (UnimplementedTransactor, NotFoundTransactor, SuccessfulTransactor);
579
580 let asset: Asset = (Here, 1u128).into();
581 let assets_in_holding: AssetsInHolding = asset_to_holding(asset);
582 assert_eq!(
583 MultiTransactor::deposit_asset(
584 assets_in_holding,
585 &Here.into(),
586 Some(&XcmContext::with_message_id([0; 32])),
587 ),
588 Ok(())
589 );
590 }
591
592 #[test]
593 fn unexpected_error_stops_iteration() {
594 type MultiTransactor = (OverflowTransactor, SuccessfulTransactor);
595
596 let asset: Asset = (Here, 1u128).into();
597 let assets_in_holding: AssetsInHolding = asset_to_holding(asset);
598 assert_eq!(
599 MultiTransactor::deposit_asset(
600 assets_in_holding,
601 &Here.into(),
602 Some(&XcmContext::with_message_id([0; 32])),
603 )
604 .map_err(|(_, e)| e),
605 Err(XcmError::Overflow)
606 );
607 }
608
609 #[test]
610 fn success_stops_iteration() {
611 type MultiTransactor = (SuccessfulTransactor, OverflowTransactor);
612
613 let asset: Asset = (Here, 1u128).into();
614 let assets_in_holding: AssetsInHolding = asset_to_holding(asset);
615 assert_eq!(
616 MultiTransactor::deposit_asset(
617 assets_in_holding,
618 &Here.into(),
619 Some(&XcmContext::with_message_id([0; 32])),
620 ),
621 Ok(()),
622 );
623 }
624}