1
// Copyright (C) Moondance Labs Ltd.
2
// This file is part of Tanssi.
3

            
4
// Tanssi is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8

            
9
// Tanssi is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13

            
14
// You should have received a copy of the GNU General Public License
15
// along with Tanssi.  If not, see <http://www.gnu.org/licenses/>
16

            
17
use {
18
    crate::{
19
        assert_expected_events,
20
        tests::common::xcm::{
21
            mocknets::{
22
                DanceboxPara as Dancebox, DanceboxParaPallet, DanceboxSender,
23
                SimpleTemplateEmptyReceiver, SimpleTemplatePara as SimpleTemplate,
24
                SimpleTemplateParaPallet, SimpleTemplateSender, WestendRelay as Westend,
25
                WestendRelayPallet, WestendSender,
26
            },
27
            *,
28
        },
29
    },
30
    frame_support::{
31
        assert_noop, assert_ok,
32
        traits::PalletInfoAccess,
33
        weights::{Weight, WeightToFee},
34
    },
35
    sp_runtime::FixedU128,
36
    staging_xcm::{
37
        latest::prelude::{Junctions::*, *},
38
        VersionedLocation,
39
    },
40
    xcm_emulator::Chain,
41
};
42

            
43
#[allow(unused_assignments)]
44
#[test]
45
1
fn transfer_assets_single_asset_fee_and_asset_reserves() {
46
1
    // XcmPallet reserve transfer arguments
47
1
    let alice_origin = <Dancebox as Chain>::RuntimeOrigin::signed(DanceboxSender::get());
48
1

            
49
1
    // Parents 1 this time
50
1
    let simple_template_dest: VersionedLocation = Location {
51
1
        parents: 1,
52
1
        interior: X1([Parachain(2002u32)].into()),
53
1
    }
54
1
    .into();
55
1

            
56
1
    let simple_template_beneficiary: VersionedLocation = Location {
57
1
        parents: 0,
58
1
        interior: X1([AccountId32 {
59
1
            network: None,
60
1
            id: SimpleTemplateEmptyReceiver::get().into(),
61
1
        }]
62
1
        .into()),
63
1
    }
64
1
    .into();
65
1

            
66
1
    let amount_to_send: crate::Balance = crate::ExistentialDeposit::get() * 1000;
67
1

            
68
1
    let dancebox_pallet_info_junction = PalletInstance(
69
1
        <<Dancebox as DanceboxParaPallet>::Balances as PalletInfoAccess>::index() as u8,
70
1
    );
71
1
    let assets: Assets = (X1([dancebox_pallet_info_junction].into()), amount_to_send).into();
72
1
    let fee_asset_item = 0;
73
1
    let dancebox_token_asset_id = 1u16;
74
1

            
75
1
    // Register the asset first
76
1
    SimpleTemplate::execute_with(|| {
77
1
        let root_origin = <SimpleTemplate as Chain>::RuntimeOrigin::root();
78
1

            
79
1
        assert_ok!(
80
1
            <SimpleTemplate as SimpleTemplateParaPallet>::ForeignAssetsCreator::create_foreign_asset(
81
1
                root_origin.clone(),
82
1
                Location {
83
1
                    parents: 1,
84
1
                    interior: X2([Parachain(2000), dancebox_pallet_info_junction].into())
85
1
                },
86
1
                dancebox_token_asset_id,
87
1
                SimpleTemplateEmptyReceiver::get(),
88
1
                true,
89
1
                1
90
1
            )
91
1
        );
92

            
93
1
        assert_ok!(
94
1
            <SimpleTemplate as SimpleTemplateParaPallet>::AssetRate::create(
95
1
                root_origin,
96
1
                bx!(1),
97
1
                FixedU128::from_u32(1)
98
1
            )
99
1
        );
100
1
    });
101
1

            
102
1
    // Send XCM message from Dancebox
103
1
    Dancebox::execute_with(|| {
104
1
        assert_ok!(
105
1
            <Dancebox as DanceboxParaPallet>::PolkadotXcm::transfer_assets(
106
1
                alice_origin,
107
1
                bx!(simple_template_dest),
108
1
                bx!(simple_template_beneficiary),
109
1
                bx!(assets.into()),
110
1
                fee_asset_item,
111
1
                WeightLimit::Unlimited,
112
1
            )
113
1
        );
114
1
    });
115
1
    // We should have received the tokens
116
1
    SimpleTemplate::execute_with(|| {
117
        type RuntimeEvent = <SimpleTemplate as Chain>::RuntimeEvent;
118
1
        let mut outcome_weight = Weight::default();
119
1
        assert_expected_events!(
120
            SimpleTemplate,
121
            vec![
122
1
                RuntimeEvent::MessageQueue(
123
1
                    pallet_message_queue::Event::Processed {
124
1
                        success: true,
125
1
                        weight_used,
126
1
                        ..
127
1
                    }) => {
128
                        weight_used: {
129
1
                            outcome_weight = *weight_used;
130
1
                            weight_used.all_gte(Weight::from_parts(0,0))
131
                        },
132
                    },
133
            ]
134
        );
135
        type ForeignAssets = <SimpleTemplate as SimpleTemplateParaPallet>::ForeignAssets;
136

            
137
        // We should have charged an amount of tokens that is identical to the weight spent
138
1
        let native_balance =
139
1
            container_chain_template_simple_runtime::WeightToFee::weight_to_fee(&outcome_weight);
140
1

            
141
1
        // Assert empty receiver received funds
142
1
        assert_eq!(
143
1
            <ForeignAssets as frame_support::traits::fungibles::Inspect<_>>::balance(
144
1
                dancebox_token_asset_id,
145
1
                &SimpleTemplateEmptyReceiver::get(),
146
1
            ),
147
1
            amount_to_send - native_balance
148
1
        );
149
1
    });
150
1
}
151

            
152
#[allow(unused_assignments)]
153
#[test]
154
1
fn transfer_assets_relay_tanssi() {
155
1
    // XcmPallet reserve transfer arguments
156
1
    let alice_dancebox_origin = <Dancebox as Chain>::RuntimeOrigin::signed(DanceboxSender::get());
157
1
    let alice_relay_origin = <Westend as Chain>::RuntimeOrigin::signed(WestendSender::get());
158
1

            
159
1
    // Parents 1 this time
160
1
    let simple_template_dest: VersionedLocation = Location {
161
1
        parents: 1,
162
1
        interior: X1([Parachain(2002u32)].into()),
163
1
    }
164
1
    .into();
165
1

            
166
1
    let simple_template_beneficiary: VersionedLocation = Location {
167
1
        parents: 0,
168
1
        interior: X1([AccountId32 {
169
1
            network: None,
170
1
            id: SimpleTemplateEmptyReceiver::get().into(),
171
1
        }]
172
1
        .into()),
173
1
    }
174
1
    .into();
175
1

            
176
1
    let dancebox_dest: VersionedLocation = Location {
177
1
        parents: 0,
178
1
        interior: X1([Parachain(2000u32)].into()),
179
1
    }
180
1
    .into();
181
1

            
182
1
    let dancebox_beneficiary: VersionedLocation = Location {
183
1
        parents: 0,
184
1
        interior: X1([AccountId32 {
185
1
            network: None,
186
1
            id: DanceboxSender::get().into(),
187
1
        }]
188
1
        .into()),
189
1
    }
190
1
    .into();
191
1

            
192
1
    let dancebox_amount_to_send: crate::Balance = crate::ExistentialDeposit::get() * 1000;
193
1

            
194
1
    let dancebox_pallet_info_junction = PalletInstance(
195
1
        <<Dancebox as DanceboxParaPallet>::Balances as PalletInfoAccess>::index() as u8,
196
1
    );
197
1
    let dancebox_assets = (
198
1
        X1([dancebox_pallet_info_junction].into()),
199
1
        dancebox_amount_to_send,
200
1
    );
201
1
    let relay_amount_to_send: crate::Balance = westend_runtime::ExistentialDeposit::get() * 1000;
202
1

            
203
1
    let relay_assets: Assets = (Here, relay_amount_to_send).into();
204
1

            
205
1
    let fee_asset_item = 0;
206
1
    let dancebox_token_asset_id = 1u16;
207
1
    let westend_token_asset_id = 2u16;
208
1

            
209
1
    // Register the assets first
210
1
    SimpleTemplate::execute_with(|| {
211
1
        let root_origin = <SimpleTemplate as Chain>::RuntimeOrigin::root();
212
1

            
213
1
        assert_ok!(
214
1
            <SimpleTemplate as SimpleTemplateParaPallet>::ForeignAssetsCreator::create_foreign_asset(
215
1
                root_origin.clone(),
216
1
                Location {
217
1
                    parents: 1,
218
1
                    interior: X2([Parachain(2000), dancebox_pallet_info_junction].into())
219
1
                },
220
1
                dancebox_token_asset_id,
221
1
                SimpleTemplateEmptyReceiver::get(),
222
1
                true,
223
1
                1
224
1
            )
225
1
        );
226

            
227
1
        assert_ok!(
228
1
            <SimpleTemplate as SimpleTemplateParaPallet>::AssetRate::create(
229
1
                root_origin.clone(),
230
1
                bx!(1),
231
1
                FixedU128::from_u32(1)
232
1
            )
233
1
        );
234

            
235
1
        assert_ok!(
236
1
            <SimpleTemplate as SimpleTemplateParaPallet>::ForeignAssetsCreator::create_foreign_asset(
237
1
                root_origin.clone(),
238
1
                Location::parent(),
239
1
                westend_token_asset_id,
240
1
                SimpleTemplateEmptyReceiver::get(),
241
1
                true,
242
1
                1
243
1
            )
244
1
        );
245

            
246
1
        assert_ok!(
247
1
            <SimpleTemplate as SimpleTemplateParaPallet>::AssetRate::create(
248
1
                root_origin,
249
1
                bx!(westend_token_asset_id),
250
1
                FixedU128::from_u32(1)
251
1
            )
252
1
        );
253
1
    });
254
1

            
255
1
    // Register the relay asset first
256
1
    Dancebox::execute_with(|| {
257
1
        let root_origin = <Dancebox as Chain>::RuntimeOrigin::root();
258
1

            
259
1
        assert_ok!(
260
1
            <Dancebox as DanceboxParaPallet>::ForeignAssetsCreator::create_foreign_asset(
261
1
                root_origin.clone(),
262
1
                Location::parent(),
263
1
                westend_token_asset_id,
264
1
                DanceboxSender::get(),
265
1
                true,
266
1
                1
267
1
            )
268
1
        );
269

            
270
1
        assert_ok!(<Dancebox as DanceboxParaPallet>::AssetRate::create(
271
1
            root_origin,
272
1
            bx!(westend_token_asset_id),
273
1
            FixedU128::from_u32(1)
274
1
        ));
275
1
    });
276
1

            
277
1
    // Relay sends to dancebox first
278
1
    Westend::execute_with(|| {
279
1
        assert_ok!(
280
1
            <Westend as WestendRelayPallet>::XcmPallet::limited_reserve_transfer_assets(
281
1
                alice_relay_origin,
282
1
                bx!(dancebox_dest),
283
1
                bx!(dancebox_beneficiary),
284
1
                bx!(relay_assets.into()),
285
1
                fee_asset_item,
286
1
                WeightLimit::Unlimited,
287
1
            )
288
1
        );
289
1
    });
290
1

            
291
1
    // We should have received the tokens
292
1
    let mut native_balance = 0u128;
293
1
    Dancebox::execute_with(|| {
294
        type RuntimeEvent = <Dancebox as Chain>::RuntimeEvent;
295
1
        let mut outcome_weight = Weight::default();
296
1
        assert_expected_events!(
297
            Dancebox,
298
            vec![
299
1
                RuntimeEvent::MessageQueue(
300
1
                    pallet_message_queue::Event::Processed {
301
1
                        success: true,
302
1
                        weight_used,
303
1
                        ..
304
1
                    }) => {
305
                        weight_used: {
306
1
                            outcome_weight = *weight_used;
307
1
                            weight_used.all_gte(Weight::from_parts(0,0))
308
                        },
309
                    },
310
            ]
311
        );
312
        type ForeignAssets = <Dancebox as DanceboxParaPallet>::ForeignAssets;
313

            
314
        // We should have charged an amount of tokens that is identical to the weight spent
315
1
        native_balance = crate::WeightToFee::weight_to_fee(&outcome_weight);
316
1

            
317
1
        // Assert empty receiver received funds
318
1
        assert_eq!(
319
1
            <ForeignAssets as frame_support::traits::fungibles::Inspect<_>>::balance(
320
1
                westend_token_asset_id,
321
1
                &DanceboxSender::get(),
322
1
            ),
323
1
            relay_amount_to_send - native_balance
324
1
        );
325
1
    });
326
1

            
327
1
    let relay_tokens_to_send_simple_template = (relay_amount_to_send - native_balance) / 2;
328
1
    // We just send half of the DOT received
329
1
    let combined_assets: Assets = vec![
330
1
        dancebox_assets.into(),
331
1
        (Location::parent(), relay_tokens_to_send_simple_template).into(),
332
1
    ]
333
1
    .into();
334
1

            
335
1
    // Now we need to send both to simple template
336
1
    // Send XCM message from Dancebox
337
1
    // Let's try to use dot as the fee
338
1
    // This should not work as we are trying to send two assets
339
1
    // with different XCM paths (one goes to the relay, the other one does not)
340
1
    Dancebox::execute_with(|| {
341
1
        assert_noop!(
342
1
            <Dancebox as DanceboxParaPallet>::PolkadotXcm::transfer_assets(
343
1
                alice_dancebox_origin.clone(),
344
1
                bx!(simple_template_dest.clone()),
345
1
                bx!(simple_template_beneficiary.clone()),
346
1
                bx!(combined_assets.clone().into()),
347
1
                1,
348
1
                WeightLimit::Unlimited,
349
1
            ),
350
1
            pallet_xcm::Error::<crate::Runtime>::InvalidAssetUnsupportedReserve
351
1
        );
352
1
    });
353
1
}
354

            
355
#[allow(unused_assignments)]
356
#[test]
357
1
fn transfer_assets_container_token_tanssi() {
358
1
    // XcmPallet reserve transfer arguments
359
1
    let alice_dancebox_origin = <Dancebox as Chain>::RuntimeOrigin::signed(DanceboxSender::get());
360
1
    let alice_simple_template_origin =
361
1
        <SimpleTemplate as Chain>::RuntimeOrigin::signed(SimpleTemplateSender::get());
362
1

            
363
1
    // Parents 1 this time
364
1
    let simple_template_dest: VersionedLocation = Location {
365
1
        parents: 1,
366
1
        interior: X1([Parachain(2002u32)].into()),
367
1
    }
368
1
    .into();
369
1

            
370
1
    let simple_template_beneficiary: VersionedLocation = Location {
371
1
        parents: 0,
372
1
        interior: X1([AccountId32 {
373
1
            network: None,
374
1
            id: SimpleTemplateEmptyReceiver::get().into(),
375
1
        }]
376
1
        .into()),
377
1
    }
378
1
    .into();
379
1

            
380
1
    let dancebox_dest: VersionedLocation = Location {
381
1
        parents: 1,
382
1
        interior: X1([Parachain(2000u32)].into()),
383
1
    }
384
1
    .into();
385
1

            
386
1
    let dancebox_beneficiary: VersionedLocation = Location {
387
1
        parents: 0,
388
1
        interior: X1([AccountId32 {
389
1
            network: None,
390
1
            id: DanceboxSender::get().into(),
391
1
        }]
392
1
        .into()),
393
1
    }
394
1
    .into();
395
1

            
396
1
    let dancebox_amount_to_send: crate::Balance = crate::ExistentialDeposit::get() * 1000;
397
1

            
398
1
    let dancebox_pallet_info_junction = PalletInstance(
399
1
        <<Dancebox as DanceboxParaPallet>::Balances as PalletInfoAccess>::index() as u8,
400
1
    );
401
1
    let dancebox_assets = (
402
1
        X1([dancebox_pallet_info_junction].into()),
403
1
        dancebox_amount_to_send,
404
1
    );
405
1
    let simple_template_amount_to_send: crate::Balance =
406
1
        container_chain_template_simple_runtime::ExistentialDeposit::get() * 1000;
407
1

            
408
1
    let simple_template_pallet_info_junction = PalletInstance(
409
1
        <<SimpleTemplate as SimpleTemplateParaPallet>::Balances as PalletInfoAccess>::index() as u8,
410
1
    );
411
1

            
412
1
    let simple_template_assets: Assets = (
413
1
        simple_template_pallet_info_junction,
414
1
        simple_template_amount_to_send,
415
1
    )
416
1
        .into();
417
1

            
418
1
    let fee_asset_item = 0;
419
1
    let dancebox_token_asset_id = 1u16;
420
1
    let simple_template_token_asset_id = 2u16;
421
1

            
422
1
    // Register the assets first
423
1
    SimpleTemplate::execute_with(|| {
424
1
        let root_origin = <SimpleTemplate as Chain>::RuntimeOrigin::root();
425
1

            
426
1
        assert_ok!(
427
1
            <SimpleTemplate as SimpleTemplateParaPallet>::ForeignAssetsCreator::create_foreign_asset(
428
1
                root_origin.clone(),
429
1
                Location {
430
1
                    parents: 1,
431
1
                    interior: X2([Parachain(2000), dancebox_pallet_info_junction].into())
432
1
                },
433
1
                dancebox_token_asset_id,
434
1
                SimpleTemplateEmptyReceiver::get(),
435
1
                true,
436
1
                1
437
1
            )
438
1
        );
439

            
440
1
        assert_ok!(
441
1
            <SimpleTemplate as SimpleTemplateParaPallet>::AssetRate::create(
442
1
                root_origin.clone(),
443
1
                bx!(1),
444
1
                FixedU128::from_u32(1)
445
1
            )
446
1
        );
447
1
    });
448
1

            
449
1
    // Register the simple template asset first
450
1
    Dancebox::execute_with(|| {
451
1
        let root_origin = <Dancebox as Chain>::RuntimeOrigin::root();
452
1

            
453
1
        assert_ok!(
454
1
            <Dancebox as DanceboxParaPallet>::ForeignAssetsCreator::create_foreign_asset(
455
1
                root_origin.clone(),
456
1
                Location {
457
1
                    parents: 1,
458
1
                    interior: X2([Parachain(2002), simple_template_pallet_info_junction].into())
459
1
                },
460
1
                simple_template_token_asset_id,
461
1
                DanceboxSender::get(),
462
1
                true,
463
1
                1
464
1
            )
465
1
        );
466

            
467
1
        assert_ok!(<Dancebox as DanceboxParaPallet>::AssetRate::create(
468
1
            root_origin,
469
1
            bx!(simple_template_token_asset_id),
470
1
            FixedU128::from_u32(1)
471
1
        ));
472
1
    });
473
1

            
474
1
    // Simple Template sends to dancebox first
475
1
    SimpleTemplate::execute_with(|| {
476
1
        assert_ok!(
477
1
            <SimpleTemplate as SimpleTemplateParaPallet>::PolkadotXcm::limited_reserve_transfer_assets(
478
1
                alice_simple_template_origin,
479
1
                bx!(dancebox_dest),
480
1
                bx!(dancebox_beneficiary),
481
1
                bx!(simple_template_assets.into()),
482
1
                fee_asset_item,
483
1
                WeightLimit::Unlimited,
484
1
            )
485
1
        );
486
1
    });
487
1

            
488
1
    // We should have received the tokens
489
1
    let mut native_balance = 0u128;
490
1
    Dancebox::execute_with(|| {
491
        type RuntimeEvent = <Dancebox as Chain>::RuntimeEvent;
492
1
        let mut outcome_weight = Weight::default();
493
1
        assert_expected_events!(
494
            Dancebox,
495
            vec![
496
1
                RuntimeEvent::MessageQueue(
497
1
                    pallet_message_queue::Event::Processed {
498
1
                        success: true,
499
1
                        weight_used,
500
1
                        ..
501
1
                    }) => {
502
                        weight_used: {
503
1
                            outcome_weight = *weight_used;
504
1
                            weight_used.all_gte(Weight::from_parts(0,0))
505
                        },
506
                    },
507
            ]
508
        );
509
        type ForeignAssets = <Dancebox as DanceboxParaPallet>::ForeignAssets;
510

            
511
        // We should have charged an amount of tokens that is identical to the weight spent
512
1
        native_balance = crate::WeightToFee::weight_to_fee(&outcome_weight);
513
1

            
514
1
        // Assert empty receiver received funds
515
1
        assert_eq!(
516
1
            <ForeignAssets as frame_support::traits::fungibles::Inspect<_>>::balance(
517
1
                simple_template_token_asset_id,
518
1
                &DanceboxSender::get(),
519
1
            ),
520
1
            simple_template_amount_to_send - native_balance
521
1
        );
522
1
    });
523
1

            
524
1
    let simple_template_tokens_to_send_simple_template =
525
1
        (simple_template_amount_to_send - native_balance) / 2;
526
1
    // We just send half of the DOT received
527
1
    let combined_assets: Assets = vec![
528
1
        dancebox_assets.into(),
529
1
        (
530
1
            Location {
531
1
                parents: 1,
532
1
                interior: X2([Parachain(2002), simple_template_pallet_info_junction].into()),
533
1
            },
534
1
            simple_template_tokens_to_send_simple_template,
535
1
        )
536
1
            .into(),
537
1
    ]
538
1
    .into();
539
1

            
540
1
    // Now we need to send both to simple template
541
1
    // Send XCM message from Dancebox
542
1
    // Let's try to use dot as the fee
543
1
    // This should work as we are trying to send two assets that follow the same XCM path
544
1
    Dancebox::execute_with(|| {
545
1
        assert_ok!(
546
1
            <Dancebox as DanceboxParaPallet>::PolkadotXcm::transfer_assets(
547
1
                alice_dancebox_origin.clone(),
548
1
                bx!(simple_template_dest.clone()),
549
1
                bx!(simple_template_beneficiary.clone()),
550
1
                bx!(combined_assets.clone().into()),
551
1
                1,
552
1
                WeightLimit::Unlimited,
553
1
            )
554
1
        );
555
1
    });
556
1

            
557
1
    // Let's assert we received them
558
1
    SimpleTemplate::execute_with(|| {
559
        type RuntimeEvent = <SimpleTemplate as Chain>::RuntimeEvent;
560
1
        let mut outcome_weight = Weight::default();
561
1
        assert_expected_events!(
562
            SimpleTemplate,
563
            vec![
564
1
                RuntimeEvent::MessageQueue(
565
1
                    pallet_message_queue::Event::Processed {
566
1
                        success: true,
567
1
                        weight_used,
568
1
                        ..
569
1
                    }) => {
570
                        weight_used: {
571
1
                            outcome_weight = *weight_used;
572
1
                            weight_used.all_gte(Weight::from_parts(0,0))
573
                        },
574
                    },
575
            ]
576
        );
577
        type ForeignAssets = <SimpleTemplate as SimpleTemplateParaPallet>::ForeignAssets;
578

            
579
        // We should have charged an amount of tokens that is identical to the weight spent
580
1
        let charged_tokens =
581
1
            container_chain_template_simple_runtime::WeightToFee::weight_to_fee(&outcome_weight);
582
1

            
583
1
        // Assert empty receiver received funds for dancebox asset
584
1
        assert_eq!(
585
1
            <ForeignAssets as frame_support::traits::fungibles::Inspect<_>>::balance(
586
1
                dancebox_token_asset_id,
587
1
                &SimpleTemplateEmptyReceiver::get(),
588
1
            ),
589
1
            dancebox_amount_to_send
590
1
        );
591

            
592
        // Assert empty receiver received funds for native asset
593
1
        assert_eq!(
594
1
            <SimpleTemplate as SimpleTemplateParaPallet>::System::account(
595
1
                SimpleTemplateEmptyReceiver::get()
596
1
            )
597
1
            .data
598
1
            .free,
599
1
            simple_template_tokens_to_send_simple_template - charged_tokens
600
1
        );
601
1
    });
602
1
}
603

            
604
#[allow(unused_assignments)]
605
#[test]
606
1
fn transfer_asset_relay_token_across_tanssi_container() {
607
1
    // XcmPallet reserve transfer arguments
608
1
    let alice_dancebox_origin = <Dancebox as Chain>::RuntimeOrigin::signed(DanceboxSender::get());
609
1
    let alice_relay_origin = <Westend as Chain>::RuntimeOrigin::signed(WestendSender::get());
610
1

            
611
1
    // Parents 1 this time
612
1
    let simple_template_dest: VersionedLocation = Location {
613
1
        parents: 1,
614
1
        interior: X1([Parachain(2002u32)].into()),
615
1
    }
616
1
    .into();
617
1

            
618
1
    let simple_template_beneficiary: VersionedLocation = Location {
619
1
        parents: 0,
620
1
        interior: X1([AccountId32 {
621
1
            network: None,
622
1
            id: SimpleTemplateEmptyReceiver::get().into(),
623
1
        }]
624
1
        .into()),
625
1
    }
626
1
    .into();
627
1

            
628
1
    let dancebox_dest: VersionedLocation = Location {
629
1
        parents: 0,
630
1
        interior: X1([Parachain(2000u32)].into()),
631
1
    }
632
1
    .into();
633
1

            
634
1
    let dancebox_beneficiary: VersionedLocation = Location {
635
1
        parents: 0,
636
1
        interior: X1([AccountId32 {
637
1
            network: None,
638
1
            id: DanceboxSender::get().into(),
639
1
        }]
640
1
        .into()),
641
1
    }
642
1
    .into();
643
1

            
644
1
    let relay_amount_to_send: crate::Balance = westend_runtime::ExistentialDeposit::get() * 1000;
645
1

            
646
1
    let relay_assets: Assets = (Here, relay_amount_to_send).into();
647
1

            
648
1
    let fee_asset_item = 0;
649
1
    let westend_token_asset_id = 1u16;
650
1

            
651
1
    // Register the relay asset first
652
1
    SimpleTemplate::execute_with(|| {
653
1
        let root_origin = <SimpleTemplate as Chain>::RuntimeOrigin::root();
654
1

            
655
1
        assert_ok!(
656
1
            <SimpleTemplate as SimpleTemplateParaPallet>::ForeignAssetsCreator::create_foreign_asset(
657
1
                root_origin.clone(),
658
1
                Location::parent(),
659
1
                westend_token_asset_id,
660
1
                SimpleTemplateEmptyReceiver::get(),
661
1
                true,
662
1
                1
663
1
            )
664
1
        );
665

            
666
1
        assert_ok!(
667
1
            <SimpleTemplate as SimpleTemplateParaPallet>::AssetRate::create(
668
1
                root_origin,
669
1
                bx!(westend_token_asset_id),
670
1
                FixedU128::from_u32(1)
671
1
            )
672
1
        );
673
1
    });
674
1

            
675
1
    // Register the relay asset first
676
1
    Dancebox::execute_with(|| {
677
1
        let root_origin = <Dancebox as Chain>::RuntimeOrigin::root();
678
1

            
679
1
        assert_ok!(
680
1
            <Dancebox as DanceboxParaPallet>::ForeignAssetsCreator::create_foreign_asset(
681
1
                root_origin.clone(),
682
1
                Location::parent(),
683
1
                westend_token_asset_id,
684
1
                DanceboxSender::get(),
685
1
                true,
686
1
                1
687
1
            )
688
1
        );
689

            
690
1
        assert_ok!(<Dancebox as DanceboxParaPallet>::AssetRate::create(
691
1
            root_origin,
692
1
            bx!(westend_token_asset_id),
693
1
            FixedU128::from_u32(1)
694
1
        ));
695
1
    });
696
1

            
697
1
    // Relay sends to dancebox first
698
1
    Westend::execute_with(|| {
699
1
        assert_ok!(
700
1
            <Westend as WestendRelayPallet>::XcmPallet::limited_reserve_transfer_assets(
701
1
                alice_relay_origin,
702
1
                bx!(dancebox_dest),
703
1
                bx!(dancebox_beneficiary),
704
1
                bx!(relay_assets.into()),
705
1
                fee_asset_item,
706
1
                WeightLimit::Unlimited,
707
1
            )
708
1
        );
709
1
    });
710
1

            
711
1
    // We should have received the tokens
712
1
    let mut native_balance = 0u128;
713
1
    Dancebox::execute_with(|| {
714
        type RuntimeEvent = <Dancebox as Chain>::RuntimeEvent;
715
1
        let mut outcome_weight = Weight::default();
716
1
        assert_expected_events!(
717
            Dancebox,
718
            vec![
719
1
                RuntimeEvent::MessageQueue(
720
1
                    pallet_message_queue::Event::Processed {
721
1
                        success: true,
722
1
                        weight_used,
723
1
                        ..
724
1
                    }) => {
725
                        weight_used: {
726
1
                            outcome_weight = *weight_used;
727
1
                            weight_used.all_gte(Weight::from_parts(0,0))
728
                        },
729
                    },
730
            ]
731
        );
732
        type ForeignAssets = <Dancebox as DanceboxParaPallet>::ForeignAssets;
733

            
734
        // We should have charged an amount of tokens that is identical to the weight spent
735
1
        native_balance = crate::WeightToFee::weight_to_fee(&outcome_weight);
736
1

            
737
1
        // Assert empty receiver received funds
738
1
        assert_eq!(
739
1
            <ForeignAssets as frame_support::traits::fungibles::Inspect<_>>::balance(
740
1
                westend_token_asset_id,
741
1
                &DanceboxSender::get(),
742
1
            ),
743
1
            relay_amount_to_send - native_balance
744
1
        );
745
1
    });
746
1

            
747
1
    let relay_tokens_to_send_simple_template = (relay_amount_to_send - native_balance) / 2;
748
1
    // We just send half of the DOT received
749
1
    let relay_assets_to_send: Assets =
750
1
        vec![(Location::parent(), relay_tokens_to_send_simple_template).into()].into();
751
1

            
752
1
    // Now we need to send the relay asset to simple template
753
1
    // Send XCM message from Dancebox
754
1
    // Let's try to use dot as the fee
755
1
    // This should work as we are trying to send a single asset
756
1
    Dancebox::execute_with(|| {
757
1
        assert_ok!(
758
1
            <Dancebox as DanceboxParaPallet>::PolkadotXcm::transfer_assets(
759
1
                alice_dancebox_origin.clone(),
760
1
                bx!(simple_template_dest.clone()),
761
1
                bx!(simple_template_beneficiary.clone()),
762
1
                bx!(relay_assets_to_send.into()),
763
1
                0,
764
1
                WeightLimit::Unlimited,
765
1
            )
766
1
        );
767
1
    });
768
1

            
769
1
    Westend::execute_with(|| {
770
        type RuntimeEvent = <Westend as Chain>::RuntimeEvent;
771
1
        assert_expected_events!(
772
            Westend,
773
            vec![
774
1
                RuntimeEvent::MessageQueue(
775
1
                    pallet_message_queue::Event::Processed {
776
1
                        success: true,
777
1
                        ..
778
1
                    }) => {},
779
            ]
780
        );
781
1
    });
782
1

            
783
1
    // Let's assert we received them
784
1
    SimpleTemplate::execute_with(|| {
785
        type RuntimeEvent = <SimpleTemplate as Chain>::RuntimeEvent;
786
1
        let mut outcome_weight = Weight::default();
787
1
        assert_expected_events!(
788
            SimpleTemplate,
789
            vec![
790
1
                RuntimeEvent::MessageQueue(
791
1
                    pallet_message_queue::Event::Processed {
792
1
                        success: true,
793
1
                        weight_used,
794
1
                        ..
795
1
                    }) => {
796
                        weight_used: {
797
1
                            outcome_weight = *weight_used;
798
1
                            weight_used.all_gte(Weight::from_parts(0,0))
799
                        },
800
                    },
801
            ]
802
        );
803
        type ForeignAssets = <SimpleTemplate as SimpleTemplateParaPallet>::ForeignAssets;
804

            
805
        // We should have charged an amount of tokens that is identical to the weight spent
806
1
        let charged_tokens =
807
1
            container_chain_template_simple_runtime::WeightToFee::weight_to_fee(&outcome_weight);
808
1

            
809
1
        // Substract delivery fees
810
1
        // There is no easy way to calculate this, but we know at least they should be more
811
1
        // than the base delivery fee
812
1
        let westend_base_delivery_fee = westend_runtime::xcm_config::BaseDeliveryFee::get();
813
1

            
814
1
        // Assert empty receiver received funds for relay asset
815
1
        assert!(
816
1
            <ForeignAssets as frame_support::traits::fungibles::Inspect<_>>::balance(
817
1
                westend_token_asset_id,
818
1
                &SimpleTemplateEmptyReceiver::get(),
819
1
            ) < relay_tokens_to_send_simple_template - charged_tokens - westend_base_delivery_fee
820
1
        );
821
1
    });
822
1
}