git.delta.rocks / unique-network / refs/commits / e02f22f362b0

difftreelog

Merge pull request #915 from UniqueNetwork/test/additional-xcm-tests

Yaroslav Bolyukin2023-04-12parents: #5de8270 #b836b20.patch.diff
in: master
Test/additional xcm tests

4 files changed

modifiedtests/src/util/playgrounds/unique.dev.tsdiffbeforeafterboth
421 return capture;421 return capture;
422 }422 }
423
424 makeXcmProgramWithdrawDeposit(beneficiary: Uint8Array, id: any, amount: bigint) {
425 return {
426 V2: [
427 {
428 WithdrawAsset: [
429 {
430 id,
431 fun: {
432 Fungible: amount,
433 },
434 },
435 ],
436 },
437 {
438 BuyExecution: {
439 fees: {
440 id,
441 fun: {
442 Fungible: amount,
443 },
444 },
445 weightLimit: 'Unlimited',
446 },
447 },
448 {
449 DepositAsset: {
450 assets: {
451 Wild: 'All',
452 },
453 maxAssets: 1,
454 beneficiary: {
455 parents: 0,
456 interior: {
457 X1: {
458 AccountId32: {
459 network: 'Any',
460 id: beneficiary,
461 },
462 },
463 },
464 },
465 },
466 },
467 ],
468 };
469 }
470
471 makeXcmProgramReserveAssetDeposited(beneficiary: Uint8Array, id: any, amount: bigint) {
472 return {
473 V2: [
474 {
475 ReserveAssetDeposited: [
476 {
477 id,
478 fun: {
479 Fungible: amount,
480 },
481 },
482 ],
483 },
484 {
485 BuyExecution: {
486 fees: {
487 id,
488 fun: {
489 Fungible: amount,
490 },
491 },
492 weightLimit: 'Unlimited',
493 },
494 },
495 {
496 DepositAsset: {
497 assets: {
498 Wild: 'All',
499 },
500 maxAssets: 1,
501 beneficiary: {
502 parents: 0,
503 interior: {
504 X1: {
505 AccountId32: {
506 network: 'Any',
507 id: beneficiary,
508 },
509 },
510 },
511 },
512 },
513 },
514 ],
515 };
516 }
423}517}
424518
425class MoonbeamAccountGroup {519class MoonbeamAccountGroup {
633 return promise;727 return promise;
634 }728 }
729
730 async eventOutcome<EventT>(maxBlocksToWait: number, eventSection: string, eventMethod: string) {
731 const eventRecord = await this.event(maxBlocksToWait, eventSection, eventMethod);
732
733 if (eventRecord == null) {
734 return null;
735 }
736
737 const event = eventRecord!.event;
738 const outcome = event.data[1] as EventT;
739
740 return outcome;
741 }
635}742}
636743
637class SessionGroup {744class SessionGroup {
modifiedtests/src/util/playgrounds/unique.tsdiffbeforeafterboth
2446 return this.ethBalanceGroup.getEthereum(address);2446 return this.ethBalanceGroup.getEthereum(address);
2447 }2447 }
2448
2449 async setBalanceSubstrate(signer: TSigner, address: TSubstrateAccount, amount: bigint, reservedAmount = 0n) {
2450 await this.helper.executeExtrinsic(signer, 'api.tx.balances.setBalance', [address, amount, reservedAmount], true);
2451 }
24482452
2449 /**2453 /**
2450 * Transfer tokens to substrate address2454 * Transfer tokens to substrate address
3018 await this.teleportAssets(signer, destination, beneficiary, assets, feeAssetItem);3022 await this.teleportAssets(signer, destination, beneficiary, assets, feeAssetItem);
3019 }3023 }
3024
3025 async send(signer: IKeyringPair, destination: any, message: any) {
3026 await this.helper.executeExtrinsic(
3027 signer,
3028 `api.tx.${this.palletName}.send`,
3029 [
3030 destination,
3031 message,
3032 ],
3033 true,
3034 );
3035 }
3020}3036}
30213037
3022class XTokensGroup<T extends ChainHelperBase> extends HelperGroup<T> {3038class XTokensGroup<T extends ChainHelperBase> extends HelperGroup<T> {
3280 assetRegistry: AcalaAssetRegistryGroup;3296 assetRegistry: AcalaAssetRegistryGroup;
3281 xTokens: XTokensGroup<AcalaHelper>;3297 xTokens: XTokensGroup<AcalaHelper>;
3282 tokens: TokensGroup<AcalaHelper>;3298 tokens: TokensGroup<AcalaHelper>;
3299 xcm: XcmGroup<AcalaHelper>;
32833300
3284 constructor(logger?: ILogger, options: {[key: string]: any} = {}) {3301 constructor(logger?: ILogger, options: {[key: string]: any} = {}) {
3285 super(logger, options.helperBase ?? AcalaHelper);3302 super(logger, options.helperBase ?? AcalaHelper);
3288 this.assetRegistry = new AcalaAssetRegistryGroup(this);3305 this.assetRegistry = new AcalaAssetRegistryGroup(this);
3289 this.xTokens = new XTokensGroup(this);3306 this.xTokens = new XTokensGroup(this);
3290 this.tokens = new TokensGroup(this);3307 this.tokens = new TokensGroup(this);
3308 this.xcm = new XcmGroup(this, 'polkadotXcm');
3291 }3309 }
32923310
3293 getSudo<T extends AcalaHelper>() {3311 getSudo<T extends AcalaHelper>() {
modifiedtests/src/xcm/xcmQuartz.test.tsdiffbeforeafterboth
19import config from '../config';19import config from '../config';
20import {XcmV2TraitsError} from '../interfaces';20import {XcmV2TraitsError} from '../interfaces';
21import {itSub, expect, describeXCM, usingPlaygrounds, usingKaruraPlaygrounds, usingRelayPlaygrounds, usingMoonriverPlaygrounds, usingStateminePlaygrounds, usingShidenPlaygrounds} from '../util';21import {itSub, expect, describeXCM, usingPlaygrounds, usingKaruraPlaygrounds, usingRelayPlaygrounds, usingMoonriverPlaygrounds, usingStateminePlaygrounds, usingShidenPlaygrounds} from '../util';
22import {DevUniqueHelper} from '../util/playgrounds/unique.dev';
2223
23const QUARTZ_CHAIN = 2095;24const QUARTZ_CHAIN = 2095;
24const STATEMINE_CHAIN = 1000;25const STATEMINE_CHAIN = 1000;
642 expect(qtzFees == 0n).to.be.true;643 expect(qtzFees == 0n).to.be.true;
643 });644 });
645
646 itSub('Karura can send only up to its balance', async ({helper}) => {
647 // set Karura's sovereign account's balance
648 const karuraBalance = 10000n * (10n ** QTZ_DECIMALS);
649 const karuraSovereignAccount = helper.address.paraSiblingSovereignAccount(KARURA_CHAIN);
650 await helper.getSudo().balance.setBalanceSubstrate(alice, karuraSovereignAccount, karuraBalance);
651
652 const moreThanKaruraHas = karuraBalance * 2n;
653
654 let targetAccountBalance = 0n;
655 const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);
656
657 const quartzMultilocation = {
658 V1: {
659 parents: 1,
660 interior: {
661 X1: {Parachain: QUARTZ_CHAIN},
662 },
663 },
664 };
665
666 const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(
667 targetAccount.addressRaw,
668 {
669 Concrete: {
670 parents: 0,
671 interior: 'Here',
672 },
673 },
674 moreThanKaruraHas,
675 );
676
677 // Try to trick Quartz
678 await usingKaruraPlaygrounds(karuraUrl, async (helper) => {
679 await helper.getSudo().xcm.send(alice, quartzMultilocation, maliciousXcmProgram);
680 });
681
682 const maxWaitBlocks = 3;
683
684 const xcmpQueueFailEvent = await helper.wait.eventOutcome<XcmV2TraitsError>(
685 maxWaitBlocks,
686 'xcmpQueue',
687 'Fail',
688 );
689
690 expect(
691 xcmpQueueFailEvent != null,
692 '\'xcmpQueue.FailEvent\' event is expected',
693 ).to.be.true;
694
695 expect(
696 xcmpQueueFailEvent!.isFailedToTransactAsset,
697 'The XCM error should be \'FailedToTransactAsset\'',
698 ).to.be.true;
699
700 targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);
701 expect(targetAccountBalance).to.be.equal(0n);
702
703 // But Karura still can send the correct amount
704 const validTransferAmount = karuraBalance / 2n;
705 const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(
706 targetAccount.addressRaw,
707 {
708 Concrete: {
709 parents: 0,
710 interior: 'Here',
711 },
712 },
713 validTransferAmount,
714 );
715
716 await usingKaruraPlaygrounds(karuraUrl, async (helper) => {
717 await helper.getSudo().xcm.send(alice, quartzMultilocation, validXcmProgram);
718 });
719
720 await helper.wait.newBlocks(maxWaitBlocks);
721
722 targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);
723 expect(targetAccountBalance).to.be.equal(validTransferAmount);
724 });
725
726 itSub('Should not accept reserve transfer of QTZ from Karura', async ({helper}) => {
727 const testAmount = 10_000n * (10n ** QTZ_DECIMALS);
728 const [targetAccount] = await helper.arrange.createAccounts([0n], alice);
729
730 const quartzMultilocation = {
731 V1: {
732 parents: 1,
733 interior: {
734 X1: {
735 Parachain: QUARTZ_CHAIN,
736 },
737 },
738 },
739 };
740
741 const maliciousXcmProgram = helper.arrange.makeXcmProgramReserveAssetDeposited(
742 targetAccount.addressRaw,
743 {
744 Concrete: {
745 parents: 1,
746 interior: {
747 X1: {
748 Parachain: QUARTZ_CHAIN,
749 },
750 },
751 },
752 },
753 testAmount,
754 );
755
756 await usingKaruraPlaygrounds(karuraUrl, async (helper) => {
757 await helper.getSudo().xcm.send(alice, quartzMultilocation, maliciousXcmProgram);
758 });
759
760 const maxWaitBlocks = 3;
761
762 const xcmpQueueFailEvent = await helper.wait.eventOutcome<XcmV2TraitsError>(
763 maxWaitBlocks,
764 'xcmpQueue',
765 'Fail',
766 );
767
768 expect(
769 xcmpQueueFailEvent != null,
770 '\'xcmpQueue.FailEvent\' event is expected',
771 ).to.be.true;
772
773 expect(
774 xcmpQueueFailEvent!.isUntrustedReserveLocation,
775 'The XCM error should be \'isUntrustedReserveLocation\'',
776 ).to.be.true;
777
778 const accountBalance = await helper.balance.getSubstrate(targetAccount.address);
779 expect(accountBalance).to.be.equal(0n);
780 });
644});781});
645782
646// These tests are relevant only when the foreign asset pallet is disabled783// These tests are relevant only when
784// the the corresponding foreign assets are not registered
647describeXCM('[XCM] Integration test: Quartz rejects non-native tokens', () => {785describeXCM('[XCM] Integration test: Quartz rejects non-native tokens', () => {
648 let alice: IKeyringPair;786 let alice: IKeyringPair;
787 let alith: IKeyringPair;
788
789 const testAmount = 100_000_000_000n;
790 let quartzParachainJunction;
791 let quartzAccountJunction;
792
793 let quartzParachainMultilocation: any;
794 let quartzAccountMultilocation: any;
795 let quartzCombinedMultilocation: any;
649796
650 before(async () => {797 before(async () => {
651 await usingPlaygrounds(async (helper, privateKey) => {798 await usingPlaygrounds(async (helper, privateKey) => {
652 alice = await privateKey('//Alice');799 alice = await privateKey('//Alice');
800
801 quartzParachainJunction = {Parachain: QUARTZ_CHAIN};
802 quartzAccountJunction = {
803 AccountId32: {
804 network: 'Any',
805 id: alice.addressRaw,
806 },
807 };
808
809 quartzParachainMultilocation = {
810 V1: {
811 parents: 1,
812 interior: {
813 X1: quartzParachainJunction,
814 },
815 },
816 };
817
818 quartzAccountMultilocation = {
819 V1: {
820 parents: 0,
821 interior: {
822 X1: quartzAccountJunction,
823 },
824 },
825 };
826
827 quartzCombinedMultilocation = {
828 V1: {
829 parents: 1,
830 interior: {
831 X2: [quartzParachainJunction, quartzAccountJunction],
832 },
833 },
834 };
653835
654 // Set the default version to wrap the first message to other chains.836 // Set the default version to wrap the first message to other chains.
655 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);837 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);
656 });838 });
839
840 // eslint-disable-next-line require-await
841 await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => {
842 alith = helper.account.alithAccount();
843 });
657 });844 });
845
846 const expectFailedToTransact = async (network: string, helper: DevUniqueHelper) => {
847 const maxWaitBlocks = 3;
848
849 const xcmpQueueFailEvent = await helper.wait.eventOutcome<XcmV2TraitsError>(
850 maxWaitBlocks,
851 'xcmpQueue',
852 'Fail',
853 );
854
855 expect(
856 xcmpQueueFailEvent != null,
857 `[reject ${network} tokens] 'xcmpQueue.FailEvent' event is expected`,
858 ).to.be.true;
859
860 expect(
861 xcmpQueueFailEvent!.isFailedToTransactAsset,
862 `[reject ${network} tokens] The XCM error should be 'FailedToTransactAsset'`,
863 ).to.be.true;
864 };
865
866 itSub('Quartz rejects KAR tokens from Karura', async ({helper}) => {
867 await usingKaruraPlaygrounds(karuraUrl, async (helper) => {
868 const id = {
869 Token: 'KAR',
870 };
871 const destination = quartzCombinedMultilocation;
872 await helper.xTokens.transfer(alice, id, testAmount, destination, 'Unlimited');
873 });
874
875 await expectFailedToTransact('KAR', helper);
876 });
877
878 itSub('Quartz rejects MOVR tokens from Moonriver', async ({helper}) => {
879 await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => {
880 const id = 'SelfReserve';
881 const destination = quartzCombinedMultilocation;
882 await helper.xTokens.transfer(alith, id, testAmount, destination, 'Unlimited');
883 });
884
885 await expectFailedToTransact('MOVR', helper);
886 });
658887
659 itSub('Quartz rejects KAR tokens from Karura', async ({helper}) => {888 itSub('Quartz rejects SDN tokens from Shiden', async ({helper}) => {
660 await usingKaruraPlaygrounds(karuraUrl, async (helper) => {889 await usingShidenPlaygrounds(shidenUrl, async (helper) => {
890 const destinationParachain = quartzParachainMultilocation;
891 const beneficiary = quartzAccountMultilocation;
661 const destination = {892 const assets = {
662 V1: {893 V1: [{
894 id: {
895 Concrete: {
663 parents: 1,896 parents: 0,
664 interior: {897 interior: 'Here',
665 X2: [898 },
666 {Parachain: QUARTZ_CHAIN},899 },
667 {
668 AccountId32: {900 fun: {
669 network: 'Any',901 Fungible: testAmount,
670 id: alice.addressRaw,
671 },902 },
672 },
673 ],
674 },
675 },903 }],
676 };904 };
677
678 const id = {905 const feeAssetItem = 0;
679 Token: 'KAR',
680 };
681906
682 await helper.xTokens.transfer(alice, id, 100_000_000_000n, destination, 'Unlimited');907 await helper.executeExtrinsic(alice, 'api.tx.polkadotXcm.reserveWithdrawAssets', [
908 destinationParachain,
909 beneficiary,
910 assets,
911 feeAssetItem,
912 ]);
683 });913 });
684
685 const maxWaitBlocks = 3;
686914
687 const xcmpQueueFailEvent = await helper.wait.event(maxWaitBlocks, 'xcmpQueue', 'Fail');915 await expectFailedToTransact('SDN', helper);
688
689 expect(
690 xcmpQueueFailEvent != null,
691 '[Karura] xcmpQueue.FailEvent event is expected',
692 ).to.be.true;
693
694 const event = xcmpQueueFailEvent!.event;
695 const outcome = event.data[1] as XcmV2TraitsError;
696
697 expect(
698 outcome.isFailedToTransactAsset,
699 '[Karura] The XCM error should be `FailedToTransactAsset`',
700 ).to.be.true;
701 });916 });
702});917});
703918
982 expect(qtzFees == 0n).to.be.true;1197 expect(qtzFees == 0n).to.be.true;
983 });1198 });
1199
1200 // eslint-disable-next-line require-await
1201 itSub.skip('Moonriver can send only up to its balance', async ({helper}) => {
1202 throw Error('Not yet implemented');
1203 });
1204
1205 // eslint-disable-next-line require-await
1206 itSub.skip('Should not accept reserve transfer of QTZ from Moonriver', async ({helper}) => {
1207 throw Error('Not yet implemented');
1208 });
984});1209});
9851210
986describeXCM('[XCM] Integration test: Exchanging tokens with Shiden', () => {1211describeXCM('[XCM] Integration test: Exchanging tokens with Shiden', () => {
1194 expect(balanceQTZ).to.eq(balanceAfterQuartzToShidenXCM + qtzFromShidenTransfered);1419 expect(balanceQTZ).to.eq(balanceAfterQuartzToShidenXCM + qtzFromShidenTransfered);
1195 });1420 });
1421
1422 itSub('Shiden can send only up to its balance', async ({helper}) => {
1423 // set Shiden's sovereign account's balance
1424 const shidenBalance = 10000n * (10n ** QTZ_DECIMALS);
1425 const shidenSovereignAccount = helper.address.paraSiblingSovereignAccount(SHIDEN_CHAIN);
1426 await helper.getSudo().balance.setBalanceSubstrate(alice, shidenSovereignAccount, shidenBalance);
1427
1428 const moreThanShidenHas = shidenBalance * 2n;
1429
1430 let targetAccountBalance = 0n;
1431 const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);
1432
1433 const quartzMultilocation = {
1434 V1: {
1435 parents: 1,
1436 interior: {
1437 X1: {Parachain: QUARTZ_CHAIN},
1438 },
1439 },
1440 };
1441
1442 const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(
1443 targetAccount.addressRaw,
1444 {
1445 Concrete: {
1446 parents: 0,
1447 interior: 'Here',
1448 },
1449 },
1450 moreThanShidenHas,
1451 );
1452
1453 // Try to trick Quartz
1454 await usingShidenPlaygrounds(shidenUrl, async (helper) => {
1455 await helper.getSudo().xcm.send(alice, quartzMultilocation, maliciousXcmProgram);
1456 });
1457
1458 const maxWaitBlocks = 3;
1459
1460 const xcmpQueueFailEvent = await helper.wait.eventOutcome<XcmV2TraitsError>(
1461 maxWaitBlocks,
1462 'xcmpQueue',
1463 'Fail',
1464 );
1465
1466 expect(
1467 xcmpQueueFailEvent != null,
1468 '\'xcmpQueue.FailEvent\' event is expected',
1469 ).to.be.true;
1470
1471 expect(
1472 xcmpQueueFailEvent!.isFailedToTransactAsset,
1473 'The XCM error should be \'FailedToTransactAsset\'',
1474 ).to.be.true;
1475
1476 targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);
1477 expect(targetAccountBalance).to.be.equal(0n);
1478
1479 // But Shiden still can send the correct amount
1480 const validTransferAmount = shidenBalance / 2n;
1481 const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(
1482 targetAccount.addressRaw,
1483 {
1484 Concrete: {
1485 parents: 0,
1486 interior: 'Here',
1487 },
1488 },
1489 validTransferAmount,
1490 );
1491
1492 await usingShidenPlaygrounds(shidenUrl, async (helper) => {
1493 await helper.getSudo().xcm.send(alice, quartzMultilocation, validXcmProgram);
1494 });
1495
1496 await helper.wait.newBlocks(maxWaitBlocks);
1497
1498 targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);
1499 expect(targetAccountBalance).to.be.equal(validTransferAmount);
1500 });
1501
1502 itSub('Should not accept reserve transfer of QTZ from Shiden', async ({helper}) => {
1503 const testAmount = 10_000n * (10n ** QTZ_DECIMALS);
1504 const [targetAccount] = await helper.arrange.createAccounts([0n], alice);
1505
1506 const quartzMultilocation = {
1507 V1: {
1508 parents: 1,
1509 interior: {
1510 X1: {
1511 Parachain: QUARTZ_CHAIN,
1512 },
1513 },
1514 },
1515 };
1516
1517 const maliciousXcmProgram = helper.arrange.makeXcmProgramReserveAssetDeposited(
1518 targetAccount.addressRaw,
1519 {
1520 Concrete: {
1521 parents: 1,
1522 interior: {
1523 X1: {
1524 Parachain: QUARTZ_CHAIN,
1525 },
1526 },
1527 },
1528 },
1529 testAmount,
1530 );
1531
1532 await usingShidenPlaygrounds(shidenUrl, async (helper) => {
1533 await helper.getSudo().xcm.send(alice, quartzMultilocation, maliciousXcmProgram);
1534 });
1535
1536 const maxWaitBlocks = 3;
1537
1538 const xcmpQueueFailEvent = await helper.wait.eventOutcome<XcmV2TraitsError>(
1539 maxWaitBlocks,
1540 'xcmpQueue',
1541 'Fail',
1542 );
1543
1544 expect(
1545 xcmpQueueFailEvent != null,
1546 '\'xcmpQueue.FailEvent\' event is expected',
1547 ).to.be.true;
1548
1549 expect(
1550 xcmpQueueFailEvent!.isUntrustedReserveLocation,
1551 'The XCM error should be \'isUntrustedReserveLocation\'',
1552 ).to.be.true;
1553
1554 const accountBalance = await helper.balance.getSubstrate(targetAccount.address);
1555 expect(accountBalance).to.be.equal(0n);
1556 });
1196});1557});
11971558
modifiedtests/src/xcm/xcmUnique.test.tsdiffbeforeafterboth
19import config from '../config';19import config from '../config';
20import {XcmV2TraitsError} from '../interfaces';20import {XcmV2TraitsError} from '../interfaces';
21import {itSub, expect, describeXCM, usingPlaygrounds, usingAcalaPlaygrounds, usingRelayPlaygrounds, usingMoonbeamPlaygrounds, usingStatemintPlaygrounds, usingAstarPlaygrounds} from '../util';21import {itSub, expect, describeXCM, usingPlaygrounds, usingAcalaPlaygrounds, usingRelayPlaygrounds, usingMoonbeamPlaygrounds, usingStatemintPlaygrounds, usingAstarPlaygrounds} from '../util';
22import {DevUniqueHelper} from '../util/playgrounds/unique.dev';
2223
23const UNIQUE_CHAIN = 2037;24const UNIQUE_CHAIN = 2037;
24const STATEMINT_CHAIN = 1000;25const STATEMINT_CHAIN = 1000;
644 expect(unqFees == 0n).to.be.true;645 expect(unqFees == 0n).to.be.true;
645 });646 });
647
648 itSub('Acala can send only up to its balance', async ({helper}) => {
649 // set Acala's sovereign account's balance
650 const acalaBalance = 10000n * (10n ** UNQ_DECIMALS);
651 const acalaSovereignAccount = helper.address.paraSiblingSovereignAccount(ACALA_CHAIN);
652 await helper.getSudo().balance.setBalanceSubstrate(alice, acalaSovereignAccount, acalaBalance);
653
654 const moreThanAcalaHas = acalaBalance * 2n;
655
656 let targetAccountBalance = 0n;
657 const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);
658
659 const uniqueMultilocation = {
660 V1: {
661 parents: 1,
662 interior: {
663 X1: {Parachain: UNIQUE_CHAIN},
664 },
665 },
666 };
667
668 const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(
669 targetAccount.addressRaw,
670 {
671 Concrete: {
672 parents: 0,
673 interior: 'Here',
674 },
675 },
676 moreThanAcalaHas,
677 );
678
679 // Try to trick Unique
680 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {
681 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgram);
682 });
683
684 const maxWaitBlocks = 3;
685
686 const xcmpQueueFailEvent = await helper.wait.eventOutcome<XcmV2TraitsError>(
687 maxWaitBlocks,
688 'xcmpQueue',
689 'Fail',
690 );
691
692 expect(
693 xcmpQueueFailEvent != null,
694 '\'xcmpQueue.FailEvent\' event is expected',
695 ).to.be.true;
696
697 expect(
698 xcmpQueueFailEvent!.isFailedToTransactAsset,
699 'The XCM error should be \'FailedToTransactAsset\'',
700 ).to.be.true;
701
702 targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);
703 expect(targetAccountBalance).to.be.equal(0n);
704
705 // But Acala still can send the correct amount
706 const validTransferAmount = acalaBalance / 2n;
707 const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(
708 targetAccount.addressRaw,
709 {
710 Concrete: {
711 parents: 0,
712 interior: 'Here',
713 },
714 },
715 validTransferAmount,
716 );
717
718 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {
719 await helper.getSudo().xcm.send(alice, uniqueMultilocation, validXcmProgram);
720 });
721
722 await helper.wait.newBlocks(maxWaitBlocks);
723
724 targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);
725 expect(targetAccountBalance).to.be.equal(validTransferAmount);
726 });
727
728 itSub('Should not accept reserve transfer of UNQ from Acala', async ({helper}) => {
729 const testAmount = 10_000n * (10n ** UNQ_DECIMALS);
730 const [targetAccount] = await helper.arrange.createAccounts([0n], alice);
731
732 const uniqueMultilocation = {
733 V1: {
734 parents: 1,
735 interior: {
736 X1: {
737 Parachain: UNIQUE_CHAIN,
738 },
739 },
740 },
741 };
742
743 const maliciousXcmProgram = helper.arrange.makeXcmProgramReserveAssetDeposited(
744 targetAccount.addressRaw,
745 {
746 Concrete: {
747 parents: 1,
748 interior: {
749 X1: {
750 Parachain: UNIQUE_CHAIN,
751 },
752 },
753 },
754 },
755 testAmount,
756 );
757
758 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {
759 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgram);
760 });
761
762 const maxWaitBlocks = 3;
763
764 const xcmpQueueFailEvent = await helper.wait.eventOutcome<XcmV2TraitsError>(
765 maxWaitBlocks,
766 'xcmpQueue',
767 'Fail',
768 );
769
770 expect(
771 xcmpQueueFailEvent != null,
772 '\'xcmpQueue.FailEvent\' event is expected',
773 ).to.be.true;
774
775 expect(
776 xcmpQueueFailEvent!.isUntrustedReserveLocation,
777 'The XCM error should be \'isUntrustedReserveLocation\'',
778 ).to.be.true;
779
780 const accountBalance = await helper.balance.getSubstrate(targetAccount.address);
781 expect(accountBalance).to.be.equal(0n);
782 });
646});783});
647784
648// These tests are relevant only when the foreign asset pallet is disabled785// These tests are relevant only when
786// the the corresponding foreign assets are not registered
649describeXCM('[XCM] Integration test: Unique rejects non-native tokens', () => {787describeXCM('[XCM] Integration test: Unique rejects non-native tokens', () => {
650 let alice: IKeyringPair;788 let alice: IKeyringPair;
789 let alith: IKeyringPair;
790
791 const testAmount = 100_000_000_000n;
792 let uniqueParachainJunction;
793 let uniqueAccountJunction;
794
795 let uniqueParachainMultilocation: any;
796 let uniqueAccountMultilocation: any;
797 let uniqueCombinedMultilocation: any;
651798
652 before(async () => {799 before(async () => {
653 await usingPlaygrounds(async (helper, privateKey) => {800 await usingPlaygrounds(async (helper, privateKey) => {
654 alice = await privateKey('//Alice');801 alice = await privateKey('//Alice');
802
803 uniqueParachainJunction = {Parachain: UNIQUE_CHAIN};
804 uniqueAccountJunction = {
805 AccountId32: {
806 network: 'Any',
807 id: alice.addressRaw,
808 },
809 };
810
811 uniqueParachainMultilocation = {
812 V1: {
813 parents: 1,
814 interior: {
815 X1: uniqueParachainJunction,
816 },
817 },
818 };
819
820 uniqueAccountMultilocation = {
821 V1: {
822 parents: 0,
823 interior: {
824 X1: uniqueAccountJunction,
825 },
826 },
827 };
828
829 uniqueCombinedMultilocation = {
830 V1: {
831 parents: 1,
832 interior: {
833 X2: [uniqueParachainJunction, uniqueAccountJunction],
834 },
835 },
836 };
655837
656 // Set the default version to wrap the first message to other chains.838 // Set the default version to wrap the first message to other chains.
657 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);839 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);
658 });840 });
841
842 // eslint-disable-next-line require-await
843 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {
844 alith = helper.account.alithAccount();
845 });
659 });846 });
847
848 const expectFailedToTransact = async (network: string, helper: DevUniqueHelper) => {
849 const maxWaitBlocks = 3;
850
851 const xcmpQueueFailEvent = await helper.wait.eventOutcome<XcmV2TraitsError>(
852 maxWaitBlocks,
853 'xcmpQueue',
854 'Fail',
855 );
856
857 expect(
858 xcmpQueueFailEvent != null,
859 `[reject ${network} tokens] 'xcmpQueue.FailEvent' event is expected`,
860 ).to.be.true;
861
862 expect(
863 xcmpQueueFailEvent!.isFailedToTransactAsset,
864 `[reject ${network} tokens] The XCM error should be 'FailedToTransactAsset'`,
865 ).to.be.true;
866 };
867
868 itSub('Unique rejects ACA tokens from Acala', async ({helper}) => {
869 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {
870 const id = {
871 Token: 'ACA',
872 };
873 const destination = uniqueCombinedMultilocation;
874 await helper.xTokens.transfer(alice, id, testAmount, destination, 'Unlimited');
875 });
876
877 await expectFailedToTransact('ACA', helper);
878 });
879
880 itSub('Unique rejects GLMR tokens from Moonbeam', async ({helper}) => {
881 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {
882 const id = 'SelfReserve';
883 const destination = uniqueCombinedMultilocation;
884 await helper.xTokens.transfer(alith, id, testAmount, destination, 'Unlimited');
885 });
886
887 await expectFailedToTransact('GLMR', helper);
888 });
660889
661 itSub('Unique rejects ACA tokens from Acala', async ({helper}) => {890 itSub('Unique rejects ASTR tokens from Astar', async ({helper}) => {
662 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {891 await usingAstarPlaygrounds(astarUrl, async (helper) => {
892 const destinationParachain = uniqueParachainMultilocation;
893 const beneficiary = uniqueAccountMultilocation;
663 const destination = {894 const assets = {
664 V1: {895 V1: [{
896 id: {
897 Concrete: {
665 parents: 1,898 parents: 0,
666 interior: {899 interior: 'Here',
667 X2: [900 },
668 {Parachain: UNIQUE_CHAIN},901 },
669 {
670 AccountId32: {902 fun: {
671 network: 'Any',903 Fungible: testAmount,
672 id: alice.addressRaw,
673 },904 },
674 },
675 ],
676 },
677 },905 }],
678 };906 };
679
680 const id = {907 const feeAssetItem = 0;
681 Token: 'ACA',
682 };
683908
684 await helper.xTokens.transfer(alice, id, 100_000_000_000n, destination, 'Unlimited');909 await helper.executeExtrinsic(alice, 'api.tx.polkadotXcm.reserveWithdrawAssets', [
910 destinationParachain,
911 beneficiary,
912 assets,
913 feeAssetItem,
914 ]);
685 });915 });
686
687 const maxWaitBlocks = 3;
688916
689 const xcmpQueueFailEvent = await helper.wait.event(maxWaitBlocks, 'xcmpQueue', 'Fail');917 await expectFailedToTransact('ASTR', helper);
690
691 expect(
692 xcmpQueueFailEvent != null,
693 '[Acala] xcmpQueue.FailEvent event is expected',
694 ).to.be.true;
695
696 const event = xcmpQueueFailEvent!.event;
697 const outcome = event.data[1] as XcmV2TraitsError;
698
699 expect(
700 outcome.isFailedToTransactAsset,
701 '[Acala] The XCM error should be `FailedToTransactAsset`',
702 ).to.be.true;
703 });918 });
704});919});
705920
985 expect(unqFees == 0n).to.be.true;1200 expect(unqFees == 0n).to.be.true;
986 });1201 });
1202
1203 // eslint-disable-next-line require-await
1204 itSub.skip('Moonbeam can send only up to its balance', async ({helper}) => {
1205 throw Error('Not yet implemented');
1206 });
1207
1208 // eslint-disable-next-line require-await
1209 itSub.skip('Should not accept reserve transfer of UNQ from Moonbeam', async ({helper}) => {
1210 throw Error('Not yet implemented');
1211 });
987});1212});
9881213
989describeXCM('[XCM] Integration test: Exchanging tokens with Astar', () => {1214describeXCM('[XCM] Integration test: Exchanging tokens with Astar', () => {
1196 expect(balanceUNQ).to.eq(balanceAfterUniqueToAstarXCM + unqFromAstarTransfered);1421 expect(balanceUNQ).to.eq(balanceAfterUniqueToAstarXCM + unqFromAstarTransfered);
1197 });1422 });
11981423
1199 itSub.skip('Should not accept limitedReserveTransfer of UNQ from ASTAR', async ({helper}) => {1424 itSub('Astar can send only up to its balance', async ({helper}) => {
1200 await usingAstarPlaygrounds(astarUrl, async (helper) => {1425 // set Astar's sovereign account's balance
1201 const destination = {1426 const astarBalance = 10000n * (10n ** UNQ_DECIMALS);
1202 V1: {1427 const astarSovereignAccount = helper.address.paraSiblingSovereignAccount(ASTAR_CHAIN);
1203 parents: 1,1428 await helper.getSudo().balance.setBalanceSubstrate(alice, astarSovereignAccount, astarBalance);
1204 interior: {1429
1205 X1: {1430 const moreThanShidenHas = astarBalance * 2n;
1206 Parachain: UNIQUE_CHAIN,1431
1207 },1432 let targetAccountBalance = 0n;
1208 },
1209 },
1210 };
1211
1212 const beneficiary = {1433 const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);
1213 V1: {
1214 parents: 0,
1215 interior: {
1216 X1: {
1217 AccountId32: {
1218 network: 'Any',
1219 id: randomAccount.addressRaw,
1220 },
1221 },
1222 },
1223 },
1224 };
12251434
1226 const assets = {1435 const uniqueMultilocation = {
1227 V1: [1436 V1: {
1228 {
1229 id: {
1230 Concrete: {
1231 parents: 1,1437 parents: 1,
1232 interior: {1438 interior: {
1233 X1: {1439 X1: {Parachain: UNIQUE_CHAIN},
1234 Parachain: UNIQUE_CHAIN,
1235 },
1236 },1440 },
1237 },
1238 },
1239 fun: {
1240 Fungible: unqFromAstarTransfered,
1241 },
1242 },1441 },
1243 ],
1244 };1442 };
12451443
1246 // Initial balance is 1 ASTAR1444 const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(
1445 targetAccount.addressRaw,
1446 {
1447 Concrete: {
1448 parents: 0,
1449 interior: 'Here',
1450 },
1451 },
1452 moreThanShidenHas,
1453 );
1454
1455 // Try to trick Unique
1456 await usingAstarPlaygrounds(astarUrl, async (helper) => {
1457 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgram);
1458 });
1459
1460 const maxWaitBlocks = 3;
1461
1462 const xcmpQueueFailEvent = await helper.wait.eventOutcome<XcmV2TraitsError>(
1463 maxWaitBlocks,
1464 'xcmpQueue',
1465 'Fail',
1466 );
1467
1468 expect(
1469 xcmpQueueFailEvent != null,
1470 '\'xcmpQueue.FailEvent\' event is expected',
1471 ).to.be.true;
1472
1473 expect(
1474 xcmpQueueFailEvent!.isFailedToTransactAsset,
1475 'The XCM error should be \'FailedToTransactAsset\'',
1476 ).to.be.true;
1477
1247 expect(await helper.balance.getSubstrate(randomAccount.address)).to.eq(astarInitialBalance);1478 targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);
12481479 expect(targetAccountBalance).to.be.equal(0n);
1480
1481 // But Astar still can send the correct amount
1482 const validTransferAmount = astarBalance / 2n;
1249 const feeAssetItem = 0;1483 const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(
1250 // TODO: expect rejected:1484 targetAccount.addressRaw,
1485 {
1486 Concrete: {
1487 parents: 0,
1488 interior: 'Here',
1489 },
1490 },
1491 validTransferAmount,
1492 );
1493
1494 await usingAstarPlaygrounds(astarUrl, async (helper) => {
1251 await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited');1495 await helper.getSudo().xcm.send(alice, uniqueMultilocation, validXcmProgram);
1252 });1496 });
1497
1498 await helper.wait.newBlocks(maxWaitBlocks);
1499
1500 targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);
1501 expect(targetAccountBalance).to.be.equal(validTransferAmount);
1253 });1502 });
1503
1504 itSub('Should not accept reserve transfer of UNQ from Astar', async ({helper}) => {
1505 const testAmount = 10_000n * (10n ** UNQ_DECIMALS);
1506 const [targetAccount] = await helper.arrange.createAccounts([0n], alice);
1507
1508 const uniqueMultilocation = {
1509 V1: {
1510 parents: 1,
1511 interior: {
1512 X1: {
1513 Parachain: UNIQUE_CHAIN,
1514 },
1515 },
1516 },
1517 };
1518
1519 const maliciousXcmProgram = helper.arrange.makeXcmProgramReserveAssetDeposited(
1520 targetAccount.addressRaw,
1521 {
1522 Concrete: {
1523 parents: 1,
1524 interior: {
1525 X1: {
1526 Parachain: UNIQUE_CHAIN,
1527 },
1528 },
1529 },
1530 },
1531 testAmount,
1532 );
1533
1534 await usingAstarPlaygrounds(astarUrl, async (helper) => {
1535 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgram);
1536 });
1537
1538 const maxWaitBlocks = 3;
1539
1540 const xcmpQueueFailEvent = await helper.wait.eventOutcome<XcmV2TraitsError>(
1541 maxWaitBlocks,
1542 'xcmpQueue',
1543 'Fail',
1544 );
1545
1546 expect(
1547 xcmpQueueFailEvent != null,
1548 '\'xcmpQueue.FailEvent\' event is expected',
1549 ).to.be.true;
1550
1551 expect(
1552 xcmpQueueFailEvent!.isUntrustedReserveLocation,
1553 'The XCM error should be \'isUntrustedReserveLocation\'',
1554 ).to.be.true;
1555
1556 const accountBalance = await helper.balance.getSubstrate(targetAccount.address);
1557 expect(accountBalance).to.be.equal(0n);
1558 });
1559
1254});1560});
12551561