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

difftreelog

fix add/extend xcm tests for moonbeam, extend untrusted reserve location

Daniel Shiposha2023-04-13parent: #e02f22f.patch.diff
in: master

4 files changed

modifiedtests/src/util/playgrounds/unique.dev.tsdiffbeforeafterboth
2// SPDX-License-Identifier: Apache-2.02// SPDX-License-Identifier: Apache-2.0
33
4import {stringToU8a} from '@polkadot/util';4import {stringToU8a} from '@polkadot/util';
5import {encodeAddress, mnemonicGenerate} from '@polkadot/util-crypto';5import {blake2AsHex, encodeAddress, mnemonicGenerate} from '@polkadot/util-crypto';
6import {UniqueHelper, MoonbeamHelper, ChainHelperBase, AcalaHelper, RelayHelper, WestmintHelper, AstarHelper} from './unique';6import {UniqueHelper, MoonbeamHelper, ChainHelperBase, AcalaHelper, RelayHelper, WestmintHelper, AstarHelper} from './unique';
7import {ApiPromise, Keyring, WsProvider} from '@polkadot/api';7import {ApiPromise, Keyring, WsProvider} from '@polkadot/api';
8import * as defs from '../../interfaces/definitions';8import * as defs from '../../interfaces/definitions';
155export class DevMoonbeamHelper extends MoonbeamHelper {155export class DevMoonbeamHelper extends MoonbeamHelper {
156 account: MoonbeamAccountGroup;156 account: MoonbeamAccountGroup;
157 wait: WaitGroup;157 wait: WaitGroup;
158 fastDemocracy: MoonbeamFastDemocracyGroup;
158159
159 constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) {160 constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) {
160 options.helperBase = options.helperBase ?? DevMoonbeamHelper;161 options.helperBase = options.helperBase ?? DevMoonbeamHelper;
163 super(logger, options);164 super(logger, options);
164 this.account = new MoonbeamAccountGroup(this);165 this.account = new MoonbeamAccountGroup(this);
165 this.wait = new WaitGroup(this);166 this.wait = new WaitGroup(this);
167 this.fastDemocracy = new MoonbeamFastDemocracyGroup(this);
166 }168 }
167}169}
168170
554 }556 }
555}557}
558
559class MoonbeamFastDemocracyGroup {
560 helper: DevMoonbeamHelper;
561
562 constructor(helper: DevMoonbeamHelper) {
563 this.helper = helper;
564 }
565
566 async executeProposal(proposalDesciption: string, encodedProposal: string) {
567 const proposalHash = blake2AsHex(encodedProposal);
568
569 const alithAccount = this.helper.account.alithAccount();
570 const baltatharAccount = this.helper.account.baltatharAccount();
571 const dorothyAccount = this.helper.account.dorothyAccount();
572
573 const councilVotingThreshold = 2;
574 const technicalCommitteeThreshold = 2;
575 const fastTrackVotingPeriod = 3;
576 const fastTrackDelayPeriod = 0;
577
578 console.log(`[democracy] executing '${proposalDesciption}' proposal`);
579
580 // >>> Propose external motion through council >>>
581 console.log('\t* Propose external motion through council.......');
582 const externalMotion = this.helper.democracy.externalProposeMajority({Inline: encodedProposal});
583 const encodedMotion = externalMotion?.method.toHex() || '';
584 const motionHash = blake2AsHex(encodedMotion);
585 console.log('\t* Motion hash is %s', motionHash);
586
587 await this.helper.collective.council.propose(
588 baltatharAccount,
589 councilVotingThreshold,
590 externalMotion,
591 externalMotion.encodedLength,
592 );
593
594 const councilProposalIdx = await this.helper.collective.council.proposalCount() - 1;
595 await this.helper.collective.council.vote(dorothyAccount, motionHash, councilProposalIdx, true);
596 await this.helper.collective.council.vote(baltatharAccount, motionHash, councilProposalIdx, true);
597
598 await this.helper.collective.council.close(
599 dorothyAccount,
600 motionHash,
601 councilProposalIdx,
602 {
603 refTime: 1_000_000_000,
604 proofSize: 1_000_000,
605 },
606 externalMotion.encodedLength,
607 );
608 console.log('\t* Propose external motion through council.......DONE');
609 // <<< Propose external motion through council <<<
610
611 // >>> Fast track proposal through technical committee >>>
612 console.log('\t* Fast track proposal through technical committee.......');
613 const fastTrack = this.helper.democracy.fastTrack(proposalHash, fastTrackVotingPeriod, fastTrackDelayPeriod);
614 const encodedFastTrack = fastTrack?.method.toHex() || '';
615 const fastTrackHash = blake2AsHex(encodedFastTrack);
616 console.log('\t* FastTrack hash is %s', fastTrackHash);
617
618 await this.helper.collective.techCommittee.propose(alithAccount, technicalCommitteeThreshold, fastTrack, fastTrack.encodedLength);
619
620 const techProposalIdx = await this.helper.collective.techCommittee.proposalCount() - 1;
621 await this.helper.collective.techCommittee.vote(baltatharAccount, fastTrackHash, techProposalIdx, true);
622 await this.helper.collective.techCommittee.vote(alithAccount, fastTrackHash, techProposalIdx, true);
623
624 await this.helper.collective.techCommittee.close(
625 baltatharAccount,
626 fastTrackHash,
627 techProposalIdx,
628 {
629 refTime: 1_000_000_000,
630 proofSize: 1_000_000,
631 },
632 fastTrack.encodedLength,
633 );
634 console.log('\t* Fast track proposal through technical committee.......DONE');
635 // <<< Fast track proposal through technical committee <<<
636
637 const refIndexField = 0;
638 const referendumIndex = await this.helper.wait.eventData<any>(3, 'democracy', 'Started', refIndexField);
639
640 // >>> Referendum voting >>>
641 console.log(`\t* Referendum #${referendumIndex} voting.......`);
642 await this.helper.democracy.referendumVote(dorothyAccount, referendumIndex, {
643 balance: 10_000_000_000_000_000_000n,
644 vote: {aye: true, conviction: 1},
645 });
646 console.log(`\t* Referendum #${referendumIndex} voting.......DONE`);
647 // <<< Referendum voting <<<
648
649 // Wait for the democracy execute
650 await this.helper.wait.newBlocks(5);
651
652 console.log(`[democracy] executing '${proposalDesciption}' proposal.......DONE`);
653 }
654}
556655
557class WaitGroup {656class WaitGroup {
558 helper: ChainHelperBase;657 helper: ChainHelperBase;
727 return promise;826 return promise;
728 }827 }
729828
730 async eventOutcome<EventT>(maxBlocksToWait: number, eventSection: string, eventMethod: string) {829 async eventData<EventT>(maxBlocksToWait: number, eventSection: string, eventMethod: string, fieldIndex: number) {
731 const eventRecord = await this.event(maxBlocksToWait, eventSection, eventMethod);830 const eventRecord = await this.event(maxBlocksToWait, eventSection, eventMethod);
732831
733 if (eventRecord == null) {832 if (eventRecord == null) {
734 return null;833 return null;
735 }834 }
736835
737 const event = eventRecord!.event;836 const event = eventRecord!.event;
738 const outcome = event.data[1] as EventT;837 const data = event.data[fieldIndex] as EventT;
739838
740 return outcome;839 return data;
741 }840 }
742}841}
743842
modifiedtests/src/util/playgrounds/unique.tsdiffbeforeafterboth
642 return call(...params);642 return call(...params);
643 }643 }
644
645 encodeApiCall(apiCall: string, params: any[]) {
646 return this.constructApiCall(apiCall, params).method.toHex();
647 }
644648
645 async executeExtrinsic(sender: TSigner, extrinsic: string, params: any[], expectSuccess=true, options: Partial<SignerOptions>|null = null/*, failureMessage='expected success'*/) {649 async executeExtrinsic(sender: TSigner, extrinsic: string, params: any[], expectSuccess=true, options: Partial<SignerOptions>|null = null/*, failureMessage='expected success'*/) {
646 if(this.api === null) throw Error('API not initialized');650 if(this.api === null) throw Error('API not initialized');
modifiedtests/src/xcm/xcmQuartz.test.tsdiffbeforeafterboth
15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
1616
17import {IKeyringPair} from '@polkadot/types/types';17import {IKeyringPair} from '@polkadot/types/types';
18import {blake2AsHex} from '@polkadot/util-crypto';
19import config from '../config';18import config from '../config';
20import {XcmV2TraitsError} from '../interfaces';19import {XcmV2TraitsError} from '../interfaces';
21import {itSub, expect, describeXCM, usingPlaygrounds, usingKaruraPlaygrounds, usingRelayPlaygrounds, usingMoonriverPlaygrounds, usingStateminePlaygrounds, usingShidenPlaygrounds} from '../util';20import {itSub, expect, describeXCM, usingPlaygrounds, usingKaruraPlaygrounds, usingRelayPlaygrounds, usingMoonriverPlaygrounds, usingStateminePlaygrounds, usingShidenPlaygrounds} from '../util';
680 });679 });
681680
682 const maxWaitBlocks = 3;681 const maxWaitBlocks = 3;
682 const outcomeField = 1;
683683
684 const xcmpQueueFailEvent = await helper.wait.eventOutcome<XcmV2TraitsError>(684 const xcmpQueueFailEvent = await helper.wait.eventData<XcmV2TraitsError>(
685 maxWaitBlocks,685 maxWaitBlocks,
686 'xcmpQueue',686 'xcmpQueue',
687 'Fail',687 'Fail',
688 outcomeField,
688 );689 );
689690
690 expect(691 expect(
738 },739 },
739 };740 };
740741
741 const maliciousXcmProgram = helper.arrange.makeXcmProgramReserveAssetDeposited(742 const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(
742 targetAccount.addressRaw,743 targetAccount.addressRaw,
743 {744 {
744 Concrete: {745 Concrete: {
753 testAmount,754 testAmount,
754 );755 );
755756
757 const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(
758 targetAccount.addressRaw,
759 {
760 Concrete: {
761 parents: 0,
762 interior: 'Here',
763 },
764 },
765 testAmount,
766 );
767
768 // Try to trick Quartz using full QTZ identification
756 await usingKaruraPlaygrounds(karuraUrl, async (helper) => {769 await usingKaruraPlaygrounds(karuraUrl, async (helper) => {
757 await helper.getSudo().xcm.send(alice, quartzMultilocation, maliciousXcmProgram);770 await helper.getSudo().xcm.send(alice, quartzMultilocation, maliciousXcmProgramFullId);
758 });771 });
759772
760 const maxWaitBlocks = 3;773 const maxWaitBlocks = 3;
761
762 const xcmpQueueFailEvent = await helper.wait.eventOutcome<XcmV2TraitsError>(774 const outcomeField = 1;
775
776 let xcmpQueueFailEvent = await helper.wait.eventData<XcmV2TraitsError>(
763 maxWaitBlocks,777 maxWaitBlocks,
764 'xcmpQueue',778 'xcmpQueue',
765 'Fail',779 'Fail',
780 outcomeField,
766 );781 );
767782
768 expect(783 expect(
775 'The XCM error should be \'isUntrustedReserveLocation\'',790 'The XCM error should be \'isUntrustedReserveLocation\'',
776 ).to.be.true;791 ).to.be.true;
777792
778 const accountBalance = await helper.balance.getSubstrate(targetAccount.address);793 let accountBalance = await helper.balance.getSubstrate(targetAccount.address);
779 expect(accountBalance).to.be.equal(0n);794 expect(accountBalance).to.be.equal(0n);
795
796 // Try to trick Quartz using shortened QTZ identification
797 await usingKaruraPlaygrounds(karuraUrl, async (helper) => {
798 await helper.getSudo().xcm.send(alice, quartzMultilocation, maliciousXcmProgramHereId);
799 });
800
801 xcmpQueueFailEvent = await helper.wait.eventData<XcmV2TraitsError>(
802 maxWaitBlocks,
803 'xcmpQueue',
804 'Fail',
805 outcomeField,
806 );
807
808 expect(
809 xcmpQueueFailEvent != null,
810 '\'xcmpQueue.FailEvent\' event is expected',
811 ).to.be.true;
812
813 expect(
814 xcmpQueueFailEvent!.isUntrustedReserveLocation,
815 'The XCM error should be \'isUntrustedReserveLocation\'',
816 ).to.be.true;
817
818 accountBalance = await helper.balance.getSubstrate(targetAccount.address);
819 expect(accountBalance).to.be.equal(0n);
780 });820 });
781});821});
782822
845885
846 const expectFailedToTransact = async (network: string, helper: DevUniqueHelper) => {886 const expectFailedToTransact = async (network: string, helper: DevUniqueHelper) => {
847 const maxWaitBlocks = 3;887 const maxWaitBlocks = 3;
888 const outcomeField = 1;
848889
849 const xcmpQueueFailEvent = await helper.wait.eventOutcome<XcmV2TraitsError>(890 const xcmpQueueFailEvent = await helper.wait.eventData<XcmV2TraitsError>(
850 maxWaitBlocks,891 maxWaitBlocks,
851 'xcmpQueue',892 'xcmpQueue',
852 'Fail',893 'Fail',
894 outcomeField,
853 );895 );
854896
855 expect(897 expect(
918960
919describeXCM('[XCM] Integration test: Exchanging QTZ with Moonriver', () => {961describeXCM('[XCM] Integration test: Exchanging QTZ with Moonriver', () => {
920 // Quartz constants962 // Quartz constants
921 let quartzDonor: IKeyringPair;963 let alice: IKeyringPair;
922 let quartzAssetLocation;964 let quartzAssetLocation;
923965
924 let randomAccountQuartz: IKeyringPair;966 let randomAccountQuartz: IKeyringPair;
927 // Moonriver constants969 // Moonriver constants
928 let assetId: string;970 let assetId: string;
929
930 const councilVotingThreshold = 2;
931 const technicalCommitteeThreshold = 2;
932 const votingPeriod = 3;
933 const delayPeriod = 0;
934971
935 const quartzAssetMetadata = {972 const quartzAssetMetadata = {
936 name: 'xcQuartz',973 name: 'xcQuartz',
952989
953 before(async () => {990 before(async () => {
954 await usingPlaygrounds(async (helper, privateKey) => {991 await usingPlaygrounds(async (helper, privateKey) => {
955 quartzDonor = await privateKey('//Alice');992 alice = await privateKey('//Alice');
956 [randomAccountQuartz] = await helper.arrange.createAccounts([0n], quartzDonor);993 [randomAccountQuartz] = await helper.arrange.createAccounts([0n], alice);
957994
958 balanceForeignQtzTokenInit = 0n;995 balanceForeignQtzTokenInit = 0n;
959996
960 // Set the default version to wrap the first message to other chains.997 // Set the default version to wrap the first message to other chains.
961 const alice = quartzDonor;
962 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);998 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);
963 });999 });
9641000
994 unitsPerSecond,1030 unitsPerSecond,
995 numAssetsWeightHint,1031 numAssetsWeightHint,
996 });1032 });
997 const proposalHash = blake2AsHex(encodedProposal);
9981033
999 console.log('Encoded proposal for registerForeignAsset & setAssetUnitsPerSecond is %s', encodedProposal);1034 console.log('Encoded proposal for registerForeignAsset & setAssetUnitsPerSecond is %s', encodedProposal);
1000 console.log('Encoded length %d', encodedProposal.length);1035
1001 console.log('Encoded proposal hash for batch utility after schedule is %s', proposalHash);
1002
1003 // >>> Note motion preimage >>>
1004 console.log('Note motion preimage.......');
1005 await helper.democracy.notePreimage(baltatharAccount, encodedProposal);1036 await helper.fastDemocracy.executeProposal('register QTZ foreign asset', encodedProposal);
1006 console.log('Note motion preimage.......DONE');
1007 // <<< Note motion preimage <<<
1008
1009 // >>> Propose external motion through council >>>
1010 console.log('Propose external motion through council.......');
1011 const externalMotion = helper.democracy.externalProposeMajority({Legacy: proposalHash});
1012 const encodedMotion = externalMotion?.method.toHex() || '';
1013 const motionHash = blake2AsHex(encodedMotion);
1014 console.log('Motion hash is %s', motionHash);
1015
1016 await helper.collective.council.propose(baltatharAccount, councilVotingThreshold, externalMotion, externalMotion.encodedLength);
1017
1018 const councilProposalIdx = await helper.collective.council.proposalCount() - 1;
1019 await helper.collective.council.vote(dorothyAccount, motionHash, councilProposalIdx, true);
1020 await helper.collective.council.vote(baltatharAccount, motionHash, councilProposalIdx, true);
1021
1022 await helper.collective.council.close(
1023 dorothyAccount,
1024 motionHash,
1025 councilProposalIdx,
1026 {
1027 refTime: 1_000_000_000,
1028 proofSize: 1_000_000,
1029 },
1030 externalMotion.encodedLength,
1031 );
1032 console.log('Propose external motion through council.......DONE');
1033 // <<< Propose external motion through council <<<
1034
1035 // >>> Fast track proposal through technical committee >>>
1036 console.log('Fast track proposal through technical committee.......');
1037 const fastTrack = helper.democracy.fastTrack(proposalHash, votingPeriod, delayPeriod);
1038 const encodedFastTrack = fastTrack?.method.toHex() || '';
1039 const fastTrackHash = blake2AsHex(encodedFastTrack);
1040 console.log('FastTrack hash is %s', fastTrackHash);
1041
1042 await helper.collective.techCommittee.propose(alithAccount, technicalCommitteeThreshold, fastTrack, fastTrack.encodedLength);
1043
1044 const techProposalIdx = await helper.collective.techCommittee.proposalCount() - 1;
1045 await helper.collective.techCommittee.vote(baltatharAccount, fastTrackHash, techProposalIdx, true);
1046 await helper.collective.techCommittee.vote(alithAccount, fastTrackHash, techProposalIdx, true);
1047
1048 await helper.collective.techCommittee.close(
1049 baltatharAccount,
1050 fastTrackHash,
1051 techProposalIdx,
1052 {
1053 refTime: 1_000_000_000,
1054 proofSize: 1_000_000,
1055 },
1056 fastTrack.encodedLength,
1057 );
1058 console.log('Fast track proposal through technical committee.......DONE');
1059 // <<< Fast track proposal through technical committee <<<
1060
1061 // >>> Referendum voting >>>
1062 console.log('Referendum voting.......');
1063 await helper.democracy.referendumVote(dorothyAccount, 0, {
1064 balance: 10_000_000_000_000_000_000n,
1065 vote: {aye: true, conviction: 1},
1066 });
1067 console.log('Referendum voting.......DONE');
1068 // <<< Referendum voting <<<
10691037
1070 // >>> Acquire Quartz AssetId Info on Moonriver >>>1038 // >>> Acquire Quartz AssetId Info on Moonriver >>>
1071 console.log('Acquire Quartz AssetId Info on Moonriver.......');1039 console.log('Acquire Quartz AssetId Info on Moonriver.......');
1072
1073 // Wait for the democracy execute
1074 await helper.wait.newBlocks(5);
10751040
1076 assetId = (await helper.assetManager.assetTypeId(quartzAssetLocation)).toString();1041 assetId = (await helper.assetManager.assetTypeId(quartzAssetLocation)).toString();
10771042
1089 });1054 });
10901055
1091 await usingPlaygrounds(async (helper) => {1056 await usingPlaygrounds(async (helper) => {
1092 await helper.balance.transferToSubstrate(quartzDonor, randomAccountQuartz.address, 10n * TRANSFER_AMOUNT);1057 await helper.balance.transferToSubstrate(alice, randomAccountQuartz.address, 10n * TRANSFER_AMOUNT);
1093 balanceQuartzTokenInit = await helper.balance.getSubstrate(randomAccountQuartz.address);1058 balanceQuartzTokenInit = await helper.balance.getSubstrate(randomAccountQuartz.address);
1094 });1059 });
1095 });1060 });
1197 expect(qtzFees == 0n).to.be.true;1162 expect(qtzFees == 0n).to.be.true;
1198 });1163 });
11991164
1200 // eslint-disable-next-line require-await
1201 itSub.skip('Moonriver can send only up to its balance', async ({helper}) => {1165 itSub('Moonriver can send only up to its balance', async ({helper}) => {
1166 // set Moonriver's sovereign account's balance
1167 const moonriverBalance = 10000n * (10n ** QTZ_DECIMALS);
1168 const moonriverSovereignAccount = helper.address.paraSiblingSovereignAccount(MOONRIVER_CHAIN);
1169 await helper.getSudo().balance.setBalanceSubstrate(alice, moonriverSovereignAccount, moonriverBalance);
1170
1171 const moreThanMoonriverHas = moonriverBalance * 2n;
1172
1173 let targetAccountBalance = 0n;
1174 const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);
1175
1176 const quartzMultilocation = {
1177 V1: {
1178 parents: 1,
1179 interior: {
1180 X1: {Parachain: QUARTZ_CHAIN},
1181 },
1182 },
1183 };
1184
1185 const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(
1186 targetAccount.addressRaw,
1187 {
1188 Concrete: {
1189 parents: 0,
1190 interior: 'Here',
1191 },
1192 },
1193 moreThanMoonriverHas,
1194 );
1195
1196 // Try to trick Quartz
1197 await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => {
1198 const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [quartzMultilocation, maliciousXcmProgram]);
1199
1200 // Needed to bypass the call filter.
1201 const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);
1202 await helper.fastDemocracy.executeProposal('try to spend more QTZ than Moonriver has', batchCall);
1203 });
1204
1205 const maxWaitBlocks = 3;
1206 const outcomeField = 1;
1207
1202 throw Error('Not yet implemented');1208 const xcmpQueueFailEvent = await helper.wait.eventData<XcmV2TraitsError>(
1209 maxWaitBlocks,
1210 'xcmpQueue',
1211 'Fail',
1212 outcomeField,
1213 );
1214
1215 expect(
1216 xcmpQueueFailEvent != null,
1217 '\'xcmpQueue.FailEvent\' event is expected',
1218 ).to.be.true;
1219
1220 expect(
1221 xcmpQueueFailEvent!.isFailedToTransactAsset,
1222 'The XCM error should be \'FailedToTransactAsset\'',
1223 ).to.be.true;
1224
1225 targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);
1226 expect(targetAccountBalance).to.be.equal(0n);
1227
1228 // But Moonriver still can send the correct amount
1229 const validTransferAmount = moonriverBalance / 2n;
1230 const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(
1231 targetAccount.addressRaw,
1232 {
1233 Concrete: {
1234 parents: 0,
1235 interior: 'Here',
1236 },
1237 },
1238 validTransferAmount,
1239 );
1240
1241 await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => {
1242 const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [quartzMultilocation, validXcmProgram]);
1243
1244 // Needed to bypass the call filter.
1245 const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);
1246 await helper.fastDemocracy.executeProposal('Spend the correct amount of QTZ', batchCall);
1247 });
1248
1249 await helper.wait.newBlocks(maxWaitBlocks);
1250
1251 targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);
1252 expect(targetAccountBalance).to.be.equal(validTransferAmount);
1203 });1253 });
12041254
1205 // eslint-disable-next-line require-await
1206 itSub.skip('Should not accept reserve transfer of QTZ from Moonriver', async ({helper}) => {1255 itSub('Should not accept reserve transfer of QTZ from Moonriver', async ({helper}) => {
1256 const testAmount = 10_000n * (10n ** QTZ_DECIMALS);
1257 const [targetAccount] = await helper.arrange.createAccounts([0n], alice);
1258
1259 const quartzMultilocation = {
1260 V1: {
1261 parents: 1,
1262 interior: {
1263 X1: {
1264 Parachain: QUARTZ_CHAIN,
1265 },
1266 },
1267 },
1268 };
1269
1270 const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(
1271 targetAccount.addressRaw,
1272 {
1273 Concrete: {
1274 parents: 0,
1275 interior: {
1276 X1: {
1277 Parachain: QUARTZ_CHAIN,
1278 },
1279 },
1280 },
1281 },
1282 testAmount,
1283 );
1284
1285 const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(
1286 targetAccount.addressRaw,
1287 {
1288 Concrete: {
1289 parents: 0,
1290 interior: 'Here',
1291 },
1292 },
1293 testAmount,
1294 );
1295
1296 // Try to trick Quartz using full QTZ identification
1297 await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => {
1298 const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [quartzMultilocation, maliciousXcmProgramFullId]);
1299
1300 // Needed to bypass the call filter.
1301 const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);
1302 await helper.fastDemocracy.executeProposal('try to act like a reserve location for QTZ using path asset identification', batchCall);
1303 });
1304
1305 const maxWaitBlocks = 3;
1306 const outcomeField = 1;
1307
1207 throw Error('Not yet implemented');1308 let xcmpQueueFailEvent = await helper.wait.eventData<XcmV2TraitsError>(
1309 maxWaitBlocks,
1310 'xcmpQueue',
1311 'Fail',
1312 outcomeField,
1313 );
1314
1315 expect(
1316 xcmpQueueFailEvent != null,
1317 '\'xcmpQueue.FailEvent\' event is expected',
1318 ).to.be.true;
1319
1320 expect(
1321 xcmpQueueFailEvent!.isUntrustedReserveLocation,
1322 'The XCM error should be \'isUntrustedReserveLocation\'',
1323 ).to.be.true;
1324
1325 let accountBalance = await helper.balance.getSubstrate(targetAccount.address);
1326 expect(accountBalance).to.be.equal(0n);
1327
1328 // Try to trick Quartz using shortened QTZ identification
1329 await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => {
1330 const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [quartzMultilocation, maliciousXcmProgramHereId]);
1331
1332 // Needed to bypass the call filter.
1333 const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);
1334 await helper.fastDemocracy.executeProposal('try to act like a reserve location for QTZ using "here" asset identification', batchCall);
1335 });
1336
1337 xcmpQueueFailEvent = await helper.wait.eventData<XcmV2TraitsError>(
1338 maxWaitBlocks,
1339 'xcmpQueue',
1340 'Fail',
1341 outcomeField,
1342 );
1343
1344 expect(
1345 xcmpQueueFailEvent != null,
1346 '\'xcmpQueue.FailEvent\' event is expected',
1347 ).to.be.true;
1348
1349 expect(
1350 xcmpQueueFailEvent!.isUntrustedReserveLocation,
1351 'The XCM error should be \'isUntrustedReserveLocation\'',
1352 ).to.be.true;
1353
1354 accountBalance = await helper.balance.getSubstrate(targetAccount.address);
1355 expect(accountBalance).to.be.equal(0n);
1208 });1356 });
1209});1357});
12101358
1456 });1604 });
14571605
1458 const maxWaitBlocks = 3;1606 const maxWaitBlocks = 3;
1607 const outcomeField = 1;
14591608
1460 const xcmpQueueFailEvent = await helper.wait.eventOutcome<XcmV2TraitsError>(1609 const xcmpQueueFailEvent = await helper.wait.eventData<XcmV2TraitsError>(
1461 maxWaitBlocks,1610 maxWaitBlocks,
1462 'xcmpQueue',1611 'xcmpQueue',
1463 'Fail',1612 'Fail',
1613 outcomeField,
1464 );1614 );
14651615
1466 expect(1616 expect(
1514 },1664 },
1515 };1665 };
15161666
1517 const maliciousXcmProgram = helper.arrange.makeXcmProgramReserveAssetDeposited(1667 const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(
1518 targetAccount.addressRaw,1668 targetAccount.addressRaw,
1519 {1669 {
1520 Concrete: {1670 Concrete: {
1529 testAmount,1679 testAmount,
1530 );1680 );
15311681
1682 const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(
1683 targetAccount.addressRaw,
1684 {
1685 Concrete: {
1686 parents: 0,
1687 interior: 'Here',
1688 },
1689 },
1690 testAmount,
1691 );
1692
1693 // Try to trick Quartz using full QTZ identification
1532 await usingShidenPlaygrounds(shidenUrl, async (helper) => {1694 await usingShidenPlaygrounds(shidenUrl, async (helper) => {
1533 await helper.getSudo().xcm.send(alice, quartzMultilocation, maliciousXcmProgram);1695 await helper.getSudo().xcm.send(alice, quartzMultilocation, maliciousXcmProgramFullId);
1534 });1696 });
15351697
1536 const maxWaitBlocks = 3;1698 const maxWaitBlocks = 3;
1537
1538 const xcmpQueueFailEvent = await helper.wait.eventOutcome<XcmV2TraitsError>(1699 const outcomeField = 1;
1700
1701 let xcmpQueueFailEvent = await helper.wait.eventData<XcmV2TraitsError>(
1539 maxWaitBlocks,1702 maxWaitBlocks,
1540 'xcmpQueue',1703 'xcmpQueue',
1541 'Fail',1704 'Fail',
1705 outcomeField,
1542 );1706 );
15431707
1544 expect(1708 expect(
1551 'The XCM error should be \'isUntrustedReserveLocation\'',1715 'The XCM error should be \'isUntrustedReserveLocation\'',
1552 ).to.be.true;1716 ).to.be.true;
15531717
1554 const accountBalance = await helper.balance.getSubstrate(targetAccount.address);1718 let accountBalance = await helper.balance.getSubstrate(targetAccount.address);
1555 expect(accountBalance).to.be.equal(0n);1719 expect(accountBalance).to.be.equal(0n);
1720
1721 // Try to trick Quartz using shortened QTZ identification
1722 await usingShidenPlaygrounds(shidenUrl, async (helper) => {
1723 await helper.getSudo().xcm.send(alice, quartzMultilocation, maliciousXcmProgramHereId);
1724 });
1725
1726 xcmpQueueFailEvent = await helper.wait.eventData<XcmV2TraitsError>(
1727 maxWaitBlocks,
1728 'xcmpQueue',
1729 'Fail',
1730 outcomeField,
1731 );
1732
1733 expect(
1734 xcmpQueueFailEvent != null,
1735 '\'xcmpQueue.FailEvent\' event is expected',
1736 ).to.be.true;
1737
1738 expect(
1739 xcmpQueueFailEvent!.isUntrustedReserveLocation,
1740 'The XCM error should be \'isUntrustedReserveLocation\'',
1741 ).to.be.true;
1742
1743 accountBalance = await helper.balance.getSubstrate(targetAccount.address);
1744 expect(accountBalance).to.be.equal(0n);
1556 });1745 });
1557});1746});
15581747
modifiedtests/src/xcm/xcmUnique.test.tsdiffbeforeafterboth
15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
1616
17import {IKeyringPair} from '@polkadot/types/types';17import {IKeyringPair} from '@polkadot/types/types';
18import {blake2AsHex} from '@polkadot/util-crypto';
19import config from '../config';18import config from '../config';
20import {XcmV2TraitsError} from '../interfaces';19import {XcmV2TraitsError} from '../interfaces';
21import {itSub, expect, describeXCM, usingPlaygrounds, usingAcalaPlaygrounds, usingRelayPlaygrounds, usingMoonbeamPlaygrounds, usingStatemintPlaygrounds, usingAstarPlaygrounds} from '../util';20import {itSub, expect, describeXCM, usingPlaygrounds, usingAcalaPlaygrounds, usingRelayPlaygrounds, usingMoonbeamPlaygrounds, usingStatemintPlaygrounds, usingAstarPlaygrounds} from '../util';
682 });681 });
683682
684 const maxWaitBlocks = 3;683 const maxWaitBlocks = 3;
684 const outcomeField = 1;
685685
686 const xcmpQueueFailEvent = await helper.wait.eventOutcome<XcmV2TraitsError>(686 const xcmpQueueFailEvent = await helper.wait.eventData<XcmV2TraitsError>(
687 maxWaitBlocks,687 maxWaitBlocks,
688 'xcmpQueue',688 'xcmpQueue',
689 'Fail',689 'Fail',
690 outcomeField,
690 );691 );
691692
692 expect(693 expect(
725 expect(targetAccountBalance).to.be.equal(validTransferAmount);726 expect(targetAccountBalance).to.be.equal(validTransferAmount);
726 });727 });
727728
728 itSub('Should not accept reserve transfer of UNQ from Acala', async ({helper}) => {729 itSub.only('Should not accept reserve transfer of UNQ from Acala', async ({helper}) => {
729 const testAmount = 10_000n * (10n ** UNQ_DECIMALS);730 const testAmount = 10_000n * (10n ** UNQ_DECIMALS);
730 const [targetAccount] = await helper.arrange.createAccounts([0n], alice);731 const [targetAccount] = await helper.arrange.createAccounts([0n], alice);
731732
740 },741 },
741 };742 };
742743
743 const maliciousXcmProgram = helper.arrange.makeXcmProgramReserveAssetDeposited(744 const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(
744 targetAccount.addressRaw,745 targetAccount.addressRaw,
745 {746 {
746 Concrete: {747 Concrete: {
755 testAmount,756 testAmount,
756 );757 );
757758
759 const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(
760 targetAccount.addressRaw,
761 {
762 Concrete: {
763 parents: 0,
764 interior: 'Here',
765 },
766 },
767 testAmount,
768 );
769
770 // Try to trick Unique using full UNQ identification
758 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {771 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {
759 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgram);772 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramFullId);
760 });773 });
761774
762 const maxWaitBlocks = 3;775 const maxWaitBlocks = 3;
763
764 const xcmpQueueFailEvent = await helper.wait.eventOutcome<XcmV2TraitsError>(776 const outcomeField = 1;
777
778 let xcmpQueueFailEvent = await helper.wait.eventData<XcmV2TraitsError>(
765 maxWaitBlocks,779 maxWaitBlocks,
766 'xcmpQueue',780 'xcmpQueue',
767 'Fail',781 'Fail',
782 outcomeField,
768 );783 );
769784
770 expect(785 expect(
777 'The XCM error should be \'isUntrustedReserveLocation\'',792 'The XCM error should be \'isUntrustedReserveLocation\'',
778 ).to.be.true;793 ).to.be.true;
779794
780 const accountBalance = await helper.balance.getSubstrate(targetAccount.address);795 let accountBalance = await helper.balance.getSubstrate(targetAccount.address);
781 expect(accountBalance).to.be.equal(0n);796 expect(accountBalance).to.be.equal(0n);
797
798 // Try to trick Unique using shortened UNQ identification
799 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {
800 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramHereId);
801 });
802
803 xcmpQueueFailEvent = await helper.wait.eventData<XcmV2TraitsError>(
804 maxWaitBlocks,
805 'xcmpQueue',
806 'Fail',
807 outcomeField,
808 );
809
810 expect(
811 xcmpQueueFailEvent != null,
812 '\'xcmpQueue.FailEvent\' event is expected',
813 ).to.be.true;
814
815 expect(
816 xcmpQueueFailEvent!.isUntrustedReserveLocation,
817 'The XCM error should be \'isUntrustedReserveLocation\'',
818 ).to.be.true;
819
820 accountBalance = await helper.balance.getSubstrate(targetAccount.address);
821 expect(accountBalance).to.be.equal(0n);
782 });822 });
783});823});
784824
847887
848 const expectFailedToTransact = async (network: string, helper: DevUniqueHelper) => {888 const expectFailedToTransact = async (network: string, helper: DevUniqueHelper) => {
849 const maxWaitBlocks = 3;889 const maxWaitBlocks = 3;
890 const outcomeField = 1;
850891
851 const xcmpQueueFailEvent = await helper.wait.eventOutcome<XcmV2TraitsError>(892 const xcmpQueueFailEvent = await helper.wait.eventData<XcmV2TraitsError>(
852 maxWaitBlocks,893 maxWaitBlocks,
853 'xcmpQueue',894 'xcmpQueue',
854 'Fail',895 'Fail',
896 outcomeField,
855 );897 );
856898
857 expect(899 expect(
920962
921describeXCM('[XCM] Integration test: Exchanging UNQ with Moonbeam', () => {963describeXCM('[XCM] Integration test: Exchanging UNQ with Moonbeam', () => {
922 // Unique constants964 // Unique constants
923 let uniqueDonor: IKeyringPair;965 let alice: IKeyringPair;
924 let uniqueAssetLocation;966 let uniqueAssetLocation;
925967
926 let randomAccountUnique: IKeyringPair;968 let randomAccountUnique: IKeyringPair;
929 // Moonbeam constants971 // Moonbeam constants
930 let assetId: string;972 let assetId: string;
931
932 const councilVotingThreshold = 2;
933 const technicalCommitteeThreshold = 2;
934 const votingPeriod = 3;
935 const delayPeriod = 0;
936973
937 const uniqueAssetMetadata = {974 const uniqueAssetMetadata = {
938 name: 'xcUnique',975 name: 'xcUnique',
954991
955 before(async () => {992 before(async () => {
956 await usingPlaygrounds(async (helper, privateKey) => {993 await usingPlaygrounds(async (helper, privateKey) => {
957 uniqueDonor = await privateKey('//Alice');994 alice = await privateKey('//Alice');
958 [randomAccountUnique] = await helper.arrange.createAccounts([0n], uniqueDonor);995 [randomAccountUnique] = await helper.arrange.createAccounts([0n], alice);
959996
960 balanceForeignUnqTokenInit = 0n;997 balanceForeignUnqTokenInit = 0n;
961998
962 // Set the default version to wrap the first message to other chains.999 // Set the default version to wrap the first message to other chains.
963 const alice = uniqueDonor;
964 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);1000 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);
965 });1001 });
9661002
996 unitsPerSecond,1032 unitsPerSecond,
997 numAssetsWeightHint,1033 numAssetsWeightHint,
998 });1034 });
999 const proposalHash = blake2AsHex(encodedProposal);
10001035
1001 console.log('Encoded proposal for registerForeignAsset & setAssetUnitsPerSecond is %s', encodedProposal);1036 console.log('Encoded proposal for registerForeignAsset & setAssetUnitsPerSecond is %s', encodedProposal);
1002 console.log('Encoded length %d', encodedProposal.length);1037
1003 console.log('Encoded proposal hash for batch utility after schedule is %s', proposalHash);
1004
1005 // >>> Note motion preimage >>>
1006 console.log('Note motion preimage.......');
1007 await helper.democracy.notePreimage(baltatharAccount, encodedProposal);1038 await helper.fastDemocracy.executeProposal('register UNQ foreign asset', encodedProposal);
1008 console.log('Note motion preimage.......DONE');
1009 // <<< Note motion preimage <<<
1010
1011 // >>> Propose external motion through council >>>
1012 console.log('Propose external motion through council.......');
1013 const externalMotion = helper.democracy.externalProposeMajority({Legacy: proposalHash});
1014 const encodedMotion = externalMotion?.method.toHex() || '';
1015 const motionHash = blake2AsHex(encodedMotion);
1016 console.log('Motion hash is %s', motionHash);
1017
1018 await helper.collective.council.propose(baltatharAccount, councilVotingThreshold, externalMotion, externalMotion.encodedLength);
1019
1020 const councilProposalIdx = await helper.collective.council.proposalCount() - 1;
1021 await helper.collective.council.vote(dorothyAccount, motionHash, councilProposalIdx, true);
1022 await helper.collective.council.vote(baltatharAccount, motionHash, councilProposalIdx, true);
1023
1024 await helper.collective.council.close(
1025 dorothyAccount,
1026 motionHash,
1027 councilProposalIdx,
1028 {
1029 refTime: 1_000_000_000,
1030 proofSize: 1_000_000,
1031 },
1032 externalMotion.encodedLength,
1033 );
1034 console.log('Propose external motion through council.......DONE');
1035 // <<< Propose external motion through council <<<
1036
1037 // >>> Fast track proposal through technical committee >>>
1038 console.log('Fast track proposal through technical committee.......');
1039 const fastTrack = helper.democracy.fastTrack(proposalHash, votingPeriod, delayPeriod);
1040 const encodedFastTrack = fastTrack?.method.toHex() || '';
1041 const fastTrackHash = blake2AsHex(encodedFastTrack);
1042 console.log('FastTrack hash is %s', fastTrackHash);
1043
1044 await helper.collective.techCommittee.propose(alithAccount, technicalCommitteeThreshold, fastTrack, fastTrack.encodedLength);
1045
1046 const techProposalIdx = await helper.collective.techCommittee.proposalCount() - 1;
1047 await helper.collective.techCommittee.vote(baltatharAccount, fastTrackHash, techProposalIdx, true);
1048 await helper.collective.techCommittee.vote(alithAccount, fastTrackHash, techProposalIdx, true);
1049
1050 await helper.collective.techCommittee.close(
1051 baltatharAccount,
1052 fastTrackHash,
1053 techProposalIdx,
1054 {
1055 refTime: 1_000_000_000,
1056 proofSize: 1_000_000,
1057 },
1058 fastTrack.encodedLength,
1059 );
1060 console.log('Fast track proposal through technical committee.......DONE');
1061 // <<< Fast track proposal through technical committee <<<
1062
1063 // >>> Referendum voting >>>
1064 console.log('Referendum voting.......');
1065 await helper.democracy.referendumVote(dorothyAccount, 0, {
1066 balance: 10_000_000_000_000_000_000n,
1067 vote: {aye: true, conviction: 1},
1068 });
1069 console.log('Referendum voting.......DONE');
1070 // <<< Referendum voting <<<
10711039
1072 // >>> Acquire Unique AssetId Info on Moonbeam >>>1040 // >>> Acquire Unique AssetId Info on Moonbeam >>>
1073 console.log('Acquire Unique AssetId Info on Moonbeam.......');1041 console.log('Acquire Unique AssetId Info on Moonbeam.......');
1074
1075 // Wait for the democracy execute
1076 await helper.wait.newBlocks(5);
10771042
1078 assetId = (await helper.assetManager.assetTypeId(uniqueAssetLocation)).toString();1043 assetId = (await helper.assetManager.assetTypeId(uniqueAssetLocation)).toString();
10791044
1091 });1056 });
10921057
1093 await usingPlaygrounds(async (helper) => {1058 await usingPlaygrounds(async (helper) => {
1094 await helper.balance.transferToSubstrate(uniqueDonor, randomAccountUnique.address, 10n * TRANSFER_AMOUNT);1059 await helper.balance.transferToSubstrate(alice, randomAccountUnique.address, 10n * TRANSFER_AMOUNT);
1095 balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccountUnique.address);1060 balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccountUnique.address);
1096 });1061 });
1097 });1062 });
1200 expect(unqFees == 0n).to.be.true;1165 expect(unqFees == 0n).to.be.true;
1201 });1166 });
12021167
1203 // eslint-disable-next-line require-await
1204 itSub.skip('Moonbeam can send only up to its balance', async ({helper}) => {1168 itSub('Moonbeam can send only up to its balance', async ({helper}) => {
1169 // set Moonbeam's sovereign account's balance
1170 const moonbeamBalance = 10000n * (10n ** UNQ_DECIMALS);
1171 const moonbeamSovereignAccount = helper.address.paraSiblingSovereignAccount(MOONBEAM_CHAIN);
1172 await helper.getSudo().balance.setBalanceSubstrate(alice, moonbeamSovereignAccount, moonbeamBalance);
1173
1174 const moreThanMoonbeamHas = moonbeamBalance * 2n;
1175
1176 let targetAccountBalance = 0n;
1177 const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);
1178
1179 const uniqueMultilocation = {
1180 V1: {
1181 parents: 1,
1182 interior: {
1183 X1: {Parachain: UNIQUE_CHAIN},
1184 },
1185 },
1186 };
1187
1188 const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(
1189 targetAccount.addressRaw,
1190 {
1191 Concrete: {
1192 parents: 0,
1193 interior: 'Here',
1194 },
1195 },
1196 moreThanMoonbeamHas,
1197 );
1198
1199 // Try to trick Unique
1200 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {
1201 const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueMultilocation, maliciousXcmProgram]);
1202
1203 // Needed to bypass the call filter.
1204 const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);
1205 await helper.fastDemocracy.executeProposal('try to spend more UNQ than Moonbeam has', batchCall);
1206 });
1207
1208 const maxWaitBlocks = 3;
1209 const outcomeField = 1;
1210
1205 throw Error('Not yet implemented');1211 const xcmpQueueFailEvent = await helper.wait.eventData<XcmV2TraitsError>(
1212 maxWaitBlocks,
1213 'xcmpQueue',
1214 'Fail',
1215 outcomeField,
1216 );
1217
1218 expect(
1219 xcmpQueueFailEvent != null,
1220 '\'xcmpQueue.FailEvent\' event is expected',
1221 ).to.be.true;
1222
1223 expect(
1224 xcmpQueueFailEvent!.isFailedToTransactAsset,
1225 'The XCM error should be \'FailedToTransactAsset\'',
1226 ).to.be.true;
1227
1228 targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);
1229 expect(targetAccountBalance).to.be.equal(0n);
1230
1231 // But Moonbeam still can send the correct amount
1232 const validTransferAmount = moonbeamBalance / 2n;
1233 const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(
1234 targetAccount.addressRaw,
1235 {
1236 Concrete: {
1237 parents: 0,
1238 interior: 'Here',
1239 },
1240 },
1241 validTransferAmount,
1242 );
1243
1244 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {
1245 const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueMultilocation, validXcmProgram]);
1246
1247 // Needed to bypass the call filter.
1248 const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);
1249 await helper.fastDemocracy.executeProposal('Spend the correct amount of UNQ', batchCall);
1250 });
1251
1252 await helper.wait.newBlocks(maxWaitBlocks);
1253
1254 targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);
1255 expect(targetAccountBalance).to.be.equal(validTransferAmount);
1206 });1256 });
12071257
1208 // eslint-disable-next-line require-await
1209 itSub.skip('Should not accept reserve transfer of UNQ from Moonbeam', async ({helper}) => {1258 itSub.only('Should not accept reserve transfer of UNQ from Moonbeam', async ({helper}) => {
1259 const testAmount = 10_000n * (10n ** UNQ_DECIMALS);
1260 const [targetAccount] = await helper.arrange.createAccounts([0n], alice);
1261
1262 const uniqueMultilocation = {
1263 V1: {
1264 parents: 1,
1265 interior: {
1266 X1: {
1267 Parachain: UNIQUE_CHAIN,
1268 },
1269 },
1270 },
1271 };
1272
1273 const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(
1274 targetAccount.addressRaw,
1275 {
1276 Concrete: {
1277 parents: 0,
1278 interior: {
1279 X1: {
1280 Parachain: UNIQUE_CHAIN,
1281 },
1282 },
1283 },
1284 },
1285 testAmount,
1286 );
1287
1288 const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(
1289 targetAccount.addressRaw,
1290 {
1291 Concrete: {
1292 parents: 0,
1293 interior: 'Here',
1294 },
1295 },
1296 testAmount,
1297 );
1298
1299 // Try to trick Unique using full UNQ identification
1300 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {
1301 const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueMultilocation, maliciousXcmProgramFullId]);
1302
1303 // Needed to bypass the call filter.
1304 const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);
1305 await helper.fastDemocracy.executeProposal('try to act like a reserve location for UNQ using path asset identification', batchCall);
1306 });
1307
1308 const maxWaitBlocks = 3;
1309 const outcomeField = 1;
1310
1210 throw Error('Not yet implemented');1311 let xcmpQueueFailEvent = await helper.wait.eventData<XcmV2TraitsError>(
1312 maxWaitBlocks,
1313 'xcmpQueue',
1314 'Fail',
1315 outcomeField,
1316 );
1317
1318 expect(
1319 xcmpQueueFailEvent != null,
1320 '\'xcmpQueue.FailEvent\' event is expected',
1321 ).to.be.true;
1322
1323 expect(
1324 xcmpQueueFailEvent!.isUntrustedReserveLocation,
1325 'The XCM error should be \'isUntrustedReserveLocation\'',
1326 ).to.be.true;
1327
1328 let accountBalance = await helper.balance.getSubstrate(targetAccount.address);
1329 expect(accountBalance).to.be.equal(0n);
1330
1331 // Try to trick Unique using shortened UNQ identification
1332 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {
1333 const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueMultilocation, maliciousXcmProgramHereId]);
1334
1335 // Needed to bypass the call filter.
1336 const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);
1337 await helper.fastDemocracy.executeProposal('try to act like a reserve location for UNQ using "here" asset identification', batchCall);
1338 });
1339
1340 xcmpQueueFailEvent = await helper.wait.eventData<XcmV2TraitsError>(
1341 maxWaitBlocks,
1342 'xcmpQueue',
1343 'Fail',
1344 outcomeField,
1345 );
1346
1347 expect(
1348 xcmpQueueFailEvent != null,
1349 '\'xcmpQueue.FailEvent\' event is expected',
1350 ).to.be.true;
1351
1352 expect(
1353 xcmpQueueFailEvent!.isUntrustedReserveLocation,
1354 'The XCM error should be \'isUntrustedReserveLocation\'',
1355 ).to.be.true;
1356
1357 accountBalance = await helper.balance.getSubstrate(targetAccount.address);
1358 expect(accountBalance).to.be.equal(0n);
1211 });1359 });
1212});1360});
12131361
1214describeXCM('[XCM] Integration test: Exchanging tokens with Astar', () => {1362describeXCM('[XCM] Integration test: Exchanging tokens with Astar', () => {
1215 let alice: IKeyringPair;1363 let alice: IKeyringPair;
1216 let randomAccount: IKeyringPair;1364 let randomAccount: IKeyringPair;
12171365
1218 const UNQ_ASSET_ID_ON_SHIDEN = 1;1366 const UNQ_ASSET_ID_ON_ASTAR = 1;
1219 const UNQ_MINIMAL_BALANCE_ON_SHIDEN = 1n;1367 const UNQ_MINIMAL_BALANCE_ON_ASTAR = 1n;
12201368
1221 // Unique -> Astar1369 // Unique -> Astar
1222 const astarInitialBalance = 1n * (10n ** ASTAR_DECIMALS); // 1 ASTR, existential deposit required to actually create the account on Shiden.1370 const astarInitialBalance = 1n * (10n ** ASTAR_DECIMALS); // 1 ASTR, existential deposit required to actually create the account on Astar.
1223 const unitsPerSecond = 228_000_000_000n; // This is Phala's value. What will be ours?1371 const unitsPerSecond = 228_000_000_000n; // This is Phala's value. What will be ours?
1224 const unqToAstarTransferred = 10n * (10n ** UNQ_DECIMALS); // 10 UNQ1372 const unqToAstarTransferred = 10n * (10n ** UNQ_DECIMALS); // 10 UNQ
1225 const unqToAstarArrived = 9_999_999_999_088_000_000n; // 9.999 ... UNQ, Shiden takes a commision in foreign tokens1373 const unqToAstarArrived = 9_999_999_999_088_000_000n; // 9.999 ... UNQ, Astar takes a commision in foreign tokens
12261374
1227 // Astar -> Unique1375 // Astar -> Unique
1228 const unqFromAstarTransfered = 5n * (10n ** UNQ_DECIMALS); // 5 UNQ1376 const unqFromAstarTransfered = 5n * (10n ** UNQ_DECIMALS); // 5 UNQ
1245 // TODO update metadata with values from production1393 // TODO update metadata with values from production
1246 await helper.assets.create(1394 await helper.assets.create(
1247 alice,1395 alice,
1248 UNQ_ASSET_ID_ON_SHIDEN,1396 UNQ_ASSET_ID_ON_ASTAR,
1249 alice.address,1397 alice.address,
1250 UNQ_MINIMAL_BALANCE_ON_SHIDEN,1398 UNQ_MINIMAL_BALANCE_ON_ASTAR,
1251 );1399 );
12521400
1253 await helper.assets.setMetadata(1401 await helper.assets.setMetadata(
1254 alice,1402 alice,
1255 UNQ_ASSET_ID_ON_SHIDEN,1403 UNQ_ASSET_ID_ON_ASTAR,
1256 'Cross chain UNQ',1404 'Cross chain UNQ',
1257 'xcUNQ',1405 'xcUNQ',
1258 Number(UNQ_DECIMALS),1406 Number(UNQ_DECIMALS),
1270 },1418 },
1271 };1419 };
12721420
1273 await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.registerAssetLocation', [assetLocation, UNQ_ASSET_ID_ON_SHIDEN]);1421 await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.registerAssetLocation', [assetLocation, UNQ_ASSET_ID_ON_ASTAR]);
12741422
1275 console.log('3. Set UNQ payment for XCM execution on Astar');1423 console.log('3. Set UNQ payment for XCM execution on Astar');
1276 await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.setAssetUnitsPerSecond', [assetLocation, unitsPerSecond]);1424 await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.setAssetUnitsPerSecond', [assetLocation, unitsPerSecond]);
13371485
1338 await usingAstarPlaygrounds(astarUrl, async (helper) => {1486 await usingAstarPlaygrounds(astarUrl, async (helper) => {
1339 await helper.wait.newBlocks(3);1487 await helper.wait.newBlocks(3);
1340 const xcUNQbalance = await helper.assets.account(UNQ_ASSET_ID_ON_SHIDEN, randomAccount.address);1488 const xcUNQbalance = await helper.assets.account(UNQ_ASSET_ID_ON_ASTAR, randomAccount.address);
1341 const astarBalance = await helper.balance.getSubstrate(randomAccount.address);1489 const astarBalance = await helper.balance.getSubstrate(randomAccount.address);
13421490
1343 console.log(`xcUNQ balance on Astar after XCM is: ${xcUNQbalance}`);1491 console.log(`xcUNQ balance on Astar after XCM is: ${xcUNQbalance}`);
1405 // this is non-standard polkadotXcm extension for Astar only. It calls InitiateReserveWithdraw1553 // this is non-standard polkadotXcm extension for Astar only. It calls InitiateReserveWithdraw
1406 await helper.executeExtrinsic(randomAccount, 'api.tx.polkadotXcm.reserveWithdrawAssets', [destination, beneficiary, assets, feeAssetItem]);1554 await helper.executeExtrinsic(randomAccount, 'api.tx.polkadotXcm.reserveWithdrawAssets', [destination, beneficiary, assets, feeAssetItem]);
14071555
1408 const xcUNQbalance = await helper.assets.account(UNQ_ASSET_ID_ON_SHIDEN, randomAccount.address);1556 const xcUNQbalance = await helper.assets.account(UNQ_ASSET_ID_ON_ASTAR, randomAccount.address);
1409 const balanceAstar = await helper.balance.getSubstrate(randomAccount.address);1557 const balanceAstar = await helper.balance.getSubstrate(randomAccount.address);
1410 console.log(`xcUNQ balance on Astar after XCM is: ${xcUNQbalance}`);1558 console.log(`xcUNQ balance on Astar after XCM is: ${xcUNQbalance}`);
14111559
1427 const astarSovereignAccount = helper.address.paraSiblingSovereignAccount(ASTAR_CHAIN);1575 const astarSovereignAccount = helper.address.paraSiblingSovereignAccount(ASTAR_CHAIN);
1428 await helper.getSudo().balance.setBalanceSubstrate(alice, astarSovereignAccount, astarBalance);1576 await helper.getSudo().balance.setBalanceSubstrate(alice, astarSovereignAccount, astarBalance);
14291577
1430 const moreThanShidenHas = astarBalance * 2n;1578 const moreThanAstarHas = astarBalance * 2n;
14311579
1432 let targetAccountBalance = 0n;1580 let targetAccountBalance = 0n;
1433 const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);1581 const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);
1449 interior: 'Here',1597 interior: 'Here',
1450 },1598 },
1451 },1599 },
1452 moreThanShidenHas,1600 moreThanAstarHas,
1453 );1601 );
14541602
1455 // Try to trick Unique1603 // Try to trick Unique
1458 });1606 });
14591607
1460 const maxWaitBlocks = 3;1608 const maxWaitBlocks = 3;
1609 const outcomeField = 1;
14611610
1462 const xcmpQueueFailEvent = await helper.wait.eventOutcome<XcmV2TraitsError>(1611 const xcmpQueueFailEvent = await helper.wait.eventData<XcmV2TraitsError>(
1463 maxWaitBlocks,1612 maxWaitBlocks,
1464 'xcmpQueue',1613 'xcmpQueue',
1465 'Fail',1614 'Fail',
1615 outcomeField,
1466 );1616 );
14671617
1468 expect(1618 expect(
1501 expect(targetAccountBalance).to.be.equal(validTransferAmount);1651 expect(targetAccountBalance).to.be.equal(validTransferAmount);
1502 });1652 });
15031653
1504 itSub('Should not accept reserve transfer of UNQ from Astar', async ({helper}) => {1654 itSub.only('Should not accept reserve transfer of UNQ from Astar', async ({helper}) => {
1505 const testAmount = 10_000n * (10n ** UNQ_DECIMALS);1655 const testAmount = 10_000n * (10n ** UNQ_DECIMALS);
1506 const [targetAccount] = await helper.arrange.createAccounts([0n], alice);1656 const [targetAccount] = await helper.arrange.createAccounts([0n], alice);
15071657
1516 },1666 },
1517 };1667 };
15181668
1519 const maliciousXcmProgram = helper.arrange.makeXcmProgramReserveAssetDeposited(1669 const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(
1520 targetAccount.addressRaw,1670 targetAccount.addressRaw,
1521 {1671 {
1522 Concrete: {1672 Concrete: {
1531 testAmount,1681 testAmount,
1532 );1682 );
15331683
1684 const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(
1685 targetAccount.addressRaw,
1686 {
1687 Concrete: {
1688 parents: 0,
1689 interior: 'Here',
1690 },
1691 },
1692 testAmount,
1693 );
1694
1695 // Try to trick Unique using full UNQ identification
1534 await usingAstarPlaygrounds(astarUrl, async (helper) => {1696 await usingAstarPlaygrounds(astarUrl, async (helper) => {
1535 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgram);1697 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramFullId);
1536 });1698 });
15371699
1538 const maxWaitBlocks = 3;1700 const maxWaitBlocks = 3;
1539
1540 const xcmpQueueFailEvent = await helper.wait.eventOutcome<XcmV2TraitsError>(1701 const outcomeField = 1;
1702
1703 let xcmpQueueFailEvent = await helper.wait.eventData<XcmV2TraitsError>(
1541 maxWaitBlocks,1704 maxWaitBlocks,
1542 'xcmpQueue',1705 'xcmpQueue',
1543 'Fail',1706 'Fail',
1707 outcomeField,
1544 );1708 );
15451709
1546 expect(1710 expect(
1553 'The XCM error should be \'isUntrustedReserveLocation\'',1717 'The XCM error should be \'isUntrustedReserveLocation\'',
1554 ).to.be.true;1718 ).to.be.true;
15551719
1556 const accountBalance = await helper.balance.getSubstrate(targetAccount.address);1720 let accountBalance = await helper.balance.getSubstrate(targetAccount.address);
1557 expect(accountBalance).to.be.equal(0n);1721 expect(accountBalance).to.be.equal(0n);
1722
1723 // Try to trick Unique using shortened UNQ identification
1724 await usingAstarPlaygrounds(astarUrl, async (helper) => {
1725 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramHereId);
1726 });
1727
1728 xcmpQueueFailEvent = await helper.wait.eventData<XcmV2TraitsError>(
1729 maxWaitBlocks,
1730 'xcmpQueue',
1731 'Fail',
1732 outcomeField,
1733 );
1734
1735 expect(
1736 xcmpQueueFailEvent != null,
1737 '\'xcmpQueue.FailEvent\' event is expected',
1738 ).to.be.true;
1739
1740 expect(
1741 xcmpQueueFailEvent!.isUntrustedReserveLocation,
1742 'The XCM error should be \'isUntrustedReserveLocation\'',
1743 ).to.be.true;
1744
1745 accountBalance = await helper.balance.getSubstrate(targetAccount.address);
1746 expect(accountBalance).to.be.equal(0n);
1558 });1747 });
15591748
1560});1749});