difftreelog
Merge pull request #915 from UniqueNetwork/test/additional-xcm-tests
in: master
Test/additional xcm tests
4 files changed
tests/src/util/playgrounds/unique.dev.tsdiffbeforeafterboth--- a/tests/src/util/playgrounds/unique.dev.ts
+++ b/tests/src/util/playgrounds/unique.dev.ts
@@ -420,6 +420,100 @@
return capture;
}
+
+ makeXcmProgramWithdrawDeposit(beneficiary: Uint8Array, id: any, amount: bigint) {
+ return {
+ V2: [
+ {
+ WithdrawAsset: [
+ {
+ id,
+ fun: {
+ Fungible: amount,
+ },
+ },
+ ],
+ },
+ {
+ BuyExecution: {
+ fees: {
+ id,
+ fun: {
+ Fungible: amount,
+ },
+ },
+ weightLimit: 'Unlimited',
+ },
+ },
+ {
+ DepositAsset: {
+ assets: {
+ Wild: 'All',
+ },
+ maxAssets: 1,
+ beneficiary: {
+ parents: 0,
+ interior: {
+ X1: {
+ AccountId32: {
+ network: 'Any',
+ id: beneficiary,
+ },
+ },
+ },
+ },
+ },
+ },
+ ],
+ };
+ }
+
+ makeXcmProgramReserveAssetDeposited(beneficiary: Uint8Array, id: any, amount: bigint) {
+ return {
+ V2: [
+ {
+ ReserveAssetDeposited: [
+ {
+ id,
+ fun: {
+ Fungible: amount,
+ },
+ },
+ ],
+ },
+ {
+ BuyExecution: {
+ fees: {
+ id,
+ fun: {
+ Fungible: amount,
+ },
+ },
+ weightLimit: 'Unlimited',
+ },
+ },
+ {
+ DepositAsset: {
+ assets: {
+ Wild: 'All',
+ },
+ maxAssets: 1,
+ beneficiary: {
+ parents: 0,
+ interior: {
+ X1: {
+ AccountId32: {
+ network: 'Any',
+ id: beneficiary,
+ },
+ },
+ },
+ },
+ },
+ },
+ ],
+ };
+ }
}
class MoonbeamAccountGroup {
@@ -632,6 +726,19 @@
});
return promise;
}
+
+ async eventOutcome<EventT>(maxBlocksToWait: number, eventSection: string, eventMethod: string) {
+ const eventRecord = await this.event(maxBlocksToWait, eventSection, eventMethod);
+
+ if (eventRecord == null) {
+ return null;
+ }
+
+ const event = eventRecord!.event;
+ const outcome = event.data[1] as EventT;
+
+ return outcome;
+ }
}
class SessionGroup {
tests/src/util/playgrounds/unique.tsdiffbeforeafterboth--- a/tests/src/util/playgrounds/unique.ts
+++ b/tests/src/util/playgrounds/unique.ts
@@ -2446,6 +2446,10 @@
return this.ethBalanceGroup.getEthereum(address);
}
+ async setBalanceSubstrate(signer: TSigner, address: TSubstrateAccount, amount: bigint, reservedAmount = 0n) {
+ await this.helper.executeExtrinsic(signer, 'api.tx.balances.setBalance', [address, amount, reservedAmount], true);
+ }
+
/**
* Transfer tokens to substrate address
* @param signer keyring of signer
@@ -3017,6 +3021,18 @@
await this.teleportAssets(signer, destination, beneficiary, assets, feeAssetItem);
}
+
+ async send(signer: IKeyringPair, destination: any, message: any) {
+ await this.helper.executeExtrinsic(
+ signer,
+ `api.tx.${this.palletName}.send`,
+ [
+ destination,
+ message,
+ ],
+ true,
+ );
+ }
}
class XTokensGroup<T extends ChainHelperBase> extends HelperGroup<T> {
@@ -3280,6 +3296,7 @@
assetRegistry: AcalaAssetRegistryGroup;
xTokens: XTokensGroup<AcalaHelper>;
tokens: TokensGroup<AcalaHelper>;
+ xcm: XcmGroup<AcalaHelper>;
constructor(logger?: ILogger, options: {[key: string]: any} = {}) {
super(logger, options.helperBase ?? AcalaHelper);
@@ -3288,6 +3305,7 @@
this.assetRegistry = new AcalaAssetRegistryGroup(this);
this.xTokens = new XTokensGroup(this);
this.tokens = new TokensGroup(this);
+ this.xcm = new XcmGroup(this, 'polkadotXcm');
}
getSudo<T extends AcalaHelper>() {
tests/src/xcm/xcmQuartz.test.tsdiffbeforeafterboth--- a/tests/src/xcm/xcmQuartz.test.ts
+++ b/tests/src/xcm/xcmQuartz.test.ts
@@ -19,6 +19,7 @@
import config from '../config';
import {XcmV2TraitsError} from '../interfaces';
import {itSub, expect, describeXCM, usingPlaygrounds, usingKaruraPlaygrounds, usingRelayPlaygrounds, usingMoonriverPlaygrounds, usingStateminePlaygrounds, usingShidenPlaygrounds} from '../util';
+import {DevUniqueHelper} from '../util/playgrounds/unique.dev';
const QUARTZ_CHAIN = 2095;
const STATEMINE_CHAIN = 1000;
@@ -641,63 +642,277 @@
console.log('[Karura -> Quartz] transaction fees on Quartz: %s QTZ', helper.util.bigIntToDecimals(qtzFees));
expect(qtzFees == 0n).to.be.true;
});
+
+ itSub('Karura can send only up to its balance', async ({helper}) => {
+ // set Karura's sovereign account's balance
+ const karuraBalance = 10000n * (10n ** QTZ_DECIMALS);
+ const karuraSovereignAccount = helper.address.paraSiblingSovereignAccount(KARURA_CHAIN);
+ await helper.getSudo().balance.setBalanceSubstrate(alice, karuraSovereignAccount, karuraBalance);
+
+ const moreThanKaruraHas = karuraBalance * 2n;
+
+ let targetAccountBalance = 0n;
+ const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);
+
+ const quartzMultilocation = {
+ V1: {
+ parents: 1,
+ interior: {
+ X1: {Parachain: QUARTZ_CHAIN},
+ },
+ },
+ };
+
+ const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(
+ targetAccount.addressRaw,
+ {
+ Concrete: {
+ parents: 0,
+ interior: 'Here',
+ },
+ },
+ moreThanKaruraHas,
+ );
+
+ // Try to trick Quartz
+ await usingKaruraPlaygrounds(karuraUrl, async (helper) => {
+ await helper.getSudo().xcm.send(alice, quartzMultilocation, maliciousXcmProgram);
+ });
+
+ const maxWaitBlocks = 3;
+
+ const xcmpQueueFailEvent = await helper.wait.eventOutcome<XcmV2TraitsError>(
+ maxWaitBlocks,
+ 'xcmpQueue',
+ 'Fail',
+ );
+
+ expect(
+ xcmpQueueFailEvent != null,
+ '\'xcmpQueue.FailEvent\' event is expected',
+ ).to.be.true;
+
+ expect(
+ xcmpQueueFailEvent!.isFailedToTransactAsset,
+ 'The XCM error should be \'FailedToTransactAsset\'',
+ ).to.be.true;
+
+ targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);
+ expect(targetAccountBalance).to.be.equal(0n);
+
+ // But Karura still can send the correct amount
+ const validTransferAmount = karuraBalance / 2n;
+ const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(
+ targetAccount.addressRaw,
+ {
+ Concrete: {
+ parents: 0,
+ interior: 'Here',
+ },
+ },
+ validTransferAmount,
+ );
+
+ await usingKaruraPlaygrounds(karuraUrl, async (helper) => {
+ await helper.getSudo().xcm.send(alice, quartzMultilocation, validXcmProgram);
+ });
+
+ await helper.wait.newBlocks(maxWaitBlocks);
+
+ targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);
+ expect(targetAccountBalance).to.be.equal(validTransferAmount);
+ });
+
+ itSub('Should not accept reserve transfer of QTZ from Karura', async ({helper}) => {
+ const testAmount = 10_000n * (10n ** QTZ_DECIMALS);
+ const [targetAccount] = await helper.arrange.createAccounts([0n], alice);
+
+ const quartzMultilocation = {
+ V1: {
+ parents: 1,
+ interior: {
+ X1: {
+ Parachain: QUARTZ_CHAIN,
+ },
+ },
+ },
+ };
+
+ const maliciousXcmProgram = helper.arrange.makeXcmProgramReserveAssetDeposited(
+ targetAccount.addressRaw,
+ {
+ Concrete: {
+ parents: 1,
+ interior: {
+ X1: {
+ Parachain: QUARTZ_CHAIN,
+ },
+ },
+ },
+ },
+ testAmount,
+ );
+
+ await usingKaruraPlaygrounds(karuraUrl, async (helper) => {
+ await helper.getSudo().xcm.send(alice, quartzMultilocation, maliciousXcmProgram);
+ });
+
+ const maxWaitBlocks = 3;
+
+ const xcmpQueueFailEvent = await helper.wait.eventOutcome<XcmV2TraitsError>(
+ maxWaitBlocks,
+ 'xcmpQueue',
+ 'Fail',
+ );
+
+ expect(
+ xcmpQueueFailEvent != null,
+ '\'xcmpQueue.FailEvent\' event is expected',
+ ).to.be.true;
+
+ expect(
+ xcmpQueueFailEvent!.isUntrustedReserveLocation,
+ 'The XCM error should be \'isUntrustedReserveLocation\'',
+ ).to.be.true;
+
+ const accountBalance = await helper.balance.getSubstrate(targetAccount.address);
+ expect(accountBalance).to.be.equal(0n);
+ });
});
-// These tests are relevant only when the foreign asset pallet is disabled
+// These tests are relevant only when
+// the the corresponding foreign assets are not registered
describeXCM('[XCM] Integration test: Quartz rejects non-native tokens', () => {
let alice: IKeyringPair;
+ let alith: IKeyringPair;
+
+ const testAmount = 100_000_000_000n;
+ let quartzParachainJunction;
+ let quartzAccountJunction;
+ let quartzParachainMultilocation: any;
+ let quartzAccountMultilocation: any;
+ let quartzCombinedMultilocation: any;
+
before(async () => {
await usingPlaygrounds(async (helper, privateKey) => {
alice = await privateKey('//Alice');
- // Set the default version to wrap the first message to other chains.
- await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);
- });
- });
+ quartzParachainJunction = {Parachain: QUARTZ_CHAIN};
+ quartzAccountJunction = {
+ AccountId32: {
+ network: 'Any',
+ id: alice.addressRaw,
+ },
+ };
- itSub('Quartz rejects KAR tokens from Karura', async ({helper}) => {
- await usingKaruraPlaygrounds(karuraUrl, async (helper) => {
- const destination = {
+ quartzParachainMultilocation = {
V1: {
parents: 1,
interior: {
- X2: [
- {Parachain: QUARTZ_CHAIN},
- {
- AccountId32: {
- network: 'Any',
- id: alice.addressRaw,
- },
- },
- ],
+ X1: quartzParachainJunction,
+ },
+ },
+ };
+
+ quartzAccountMultilocation = {
+ V1: {
+ parents: 0,
+ interior: {
+ X1: quartzAccountJunction,
},
},
};
- const id = {
- Token: 'KAR',
+ quartzCombinedMultilocation = {
+ V1: {
+ parents: 1,
+ interior: {
+ X2: [quartzParachainJunction, quartzAccountJunction],
+ },
+ },
};
- await helper.xTokens.transfer(alice, id, 100_000_000_000n, destination, 'Unlimited');
+ // Set the default version to wrap the first message to other chains.
+ await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);
+ });
+
+ // eslint-disable-next-line require-await
+ await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => {
+ alith = helper.account.alithAccount();
});
+ });
+ const expectFailedToTransact = async (network: string, helper: DevUniqueHelper) => {
const maxWaitBlocks = 3;
- const xcmpQueueFailEvent = await helper.wait.event(maxWaitBlocks, 'xcmpQueue', 'Fail');
+ const xcmpQueueFailEvent = await helper.wait.eventOutcome<XcmV2TraitsError>(
+ maxWaitBlocks,
+ 'xcmpQueue',
+ 'Fail',
+ );
expect(
xcmpQueueFailEvent != null,
- '[Karura] xcmpQueue.FailEvent event is expected',
+ `[reject ${network} tokens] 'xcmpQueue.FailEvent' event is expected`,
).to.be.true;
- const event = xcmpQueueFailEvent!.event;
- const outcome = event.data[1] as XcmV2TraitsError;
-
expect(
- outcome.isFailedToTransactAsset,
- '[Karura] The XCM error should be `FailedToTransactAsset`',
+ xcmpQueueFailEvent!.isFailedToTransactAsset,
+ `[reject ${network} tokens] The XCM error should be 'FailedToTransactAsset'`,
).to.be.true;
+ };
+
+ itSub('Quartz rejects KAR tokens from Karura', async ({helper}) => {
+ await usingKaruraPlaygrounds(karuraUrl, async (helper) => {
+ const id = {
+ Token: 'KAR',
+ };
+ const destination = quartzCombinedMultilocation;
+ await helper.xTokens.transfer(alice, id, testAmount, destination, 'Unlimited');
+ });
+
+ await expectFailedToTransact('KAR', helper);
+ });
+
+ itSub('Quartz rejects MOVR tokens from Moonriver', async ({helper}) => {
+ await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => {
+ const id = 'SelfReserve';
+ const destination = quartzCombinedMultilocation;
+ await helper.xTokens.transfer(alith, id, testAmount, destination, 'Unlimited');
+ });
+
+ await expectFailedToTransact('MOVR', helper);
+ });
+
+ itSub('Quartz rejects SDN tokens from Shiden', async ({helper}) => {
+ await usingShidenPlaygrounds(shidenUrl, async (helper) => {
+ const destinationParachain = quartzParachainMultilocation;
+ const beneficiary = quartzAccountMultilocation;
+ const assets = {
+ V1: [{
+ id: {
+ Concrete: {
+ parents: 0,
+ interior: 'Here',
+ },
+ },
+ fun: {
+ Fungible: testAmount,
+ },
+ }],
+ };
+ const feeAssetItem = 0;
+
+ await helper.executeExtrinsic(alice, 'api.tx.polkadotXcm.reserveWithdrawAssets', [
+ destinationParachain,
+ beneficiary,
+ assets,
+ feeAssetItem,
+ ]);
+ });
+
+ await expectFailedToTransact('SDN', helper);
});
});
@@ -981,6 +1196,16 @@
console.log('[Moonriver -> Quartz] transaction fees on Quartz: %s QTZ', helper.util.bigIntToDecimals(qtzFees));
expect(qtzFees == 0n).to.be.true;
});
+
+ // eslint-disable-next-line require-await
+ itSub.skip('Moonriver can send only up to its balance', async ({helper}) => {
+ throw Error('Not yet implemented');
+ });
+
+ // eslint-disable-next-line require-await
+ itSub.skip('Should not accept reserve transfer of QTZ from Moonriver', async ({helper}) => {
+ throw Error('Not yet implemented');
+ });
});
describeXCM('[XCM] Integration test: Exchanging tokens with Shiden', () => {
@@ -1193,4 +1418,140 @@
console.log(`QTZ Balance on Quartz after XCM is: ${balanceQTZ}`);
expect(balanceQTZ).to.eq(balanceAfterQuartzToShidenXCM + qtzFromShidenTransfered);
});
+
+ itSub('Shiden can send only up to its balance', async ({helper}) => {
+ // set Shiden's sovereign account's balance
+ const shidenBalance = 10000n * (10n ** QTZ_DECIMALS);
+ const shidenSovereignAccount = helper.address.paraSiblingSovereignAccount(SHIDEN_CHAIN);
+ await helper.getSudo().balance.setBalanceSubstrate(alice, shidenSovereignAccount, shidenBalance);
+
+ const moreThanShidenHas = shidenBalance * 2n;
+
+ let targetAccountBalance = 0n;
+ const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);
+
+ const quartzMultilocation = {
+ V1: {
+ parents: 1,
+ interior: {
+ X1: {Parachain: QUARTZ_CHAIN},
+ },
+ },
+ };
+
+ const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(
+ targetAccount.addressRaw,
+ {
+ Concrete: {
+ parents: 0,
+ interior: 'Here',
+ },
+ },
+ moreThanShidenHas,
+ );
+
+ // Try to trick Quartz
+ await usingShidenPlaygrounds(shidenUrl, async (helper) => {
+ await helper.getSudo().xcm.send(alice, quartzMultilocation, maliciousXcmProgram);
+ });
+
+ const maxWaitBlocks = 3;
+
+ const xcmpQueueFailEvent = await helper.wait.eventOutcome<XcmV2TraitsError>(
+ maxWaitBlocks,
+ 'xcmpQueue',
+ 'Fail',
+ );
+
+ expect(
+ xcmpQueueFailEvent != null,
+ '\'xcmpQueue.FailEvent\' event is expected',
+ ).to.be.true;
+
+ expect(
+ xcmpQueueFailEvent!.isFailedToTransactAsset,
+ 'The XCM error should be \'FailedToTransactAsset\'',
+ ).to.be.true;
+
+ targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);
+ expect(targetAccountBalance).to.be.equal(0n);
+
+ // But Shiden still can send the correct amount
+ const validTransferAmount = shidenBalance / 2n;
+ const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(
+ targetAccount.addressRaw,
+ {
+ Concrete: {
+ parents: 0,
+ interior: 'Here',
+ },
+ },
+ validTransferAmount,
+ );
+
+ await usingShidenPlaygrounds(shidenUrl, async (helper) => {
+ await helper.getSudo().xcm.send(alice, quartzMultilocation, validXcmProgram);
+ });
+
+ await helper.wait.newBlocks(maxWaitBlocks);
+
+ targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);
+ expect(targetAccountBalance).to.be.equal(validTransferAmount);
+ });
+
+ itSub('Should not accept reserve transfer of QTZ from Shiden', async ({helper}) => {
+ const testAmount = 10_000n * (10n ** QTZ_DECIMALS);
+ const [targetAccount] = await helper.arrange.createAccounts([0n], alice);
+
+ const quartzMultilocation = {
+ V1: {
+ parents: 1,
+ interior: {
+ X1: {
+ Parachain: QUARTZ_CHAIN,
+ },
+ },
+ },
+ };
+
+ const maliciousXcmProgram = helper.arrange.makeXcmProgramReserveAssetDeposited(
+ targetAccount.addressRaw,
+ {
+ Concrete: {
+ parents: 1,
+ interior: {
+ X1: {
+ Parachain: QUARTZ_CHAIN,
+ },
+ },
+ },
+ },
+ testAmount,
+ );
+
+ await usingShidenPlaygrounds(shidenUrl, async (helper) => {
+ await helper.getSudo().xcm.send(alice, quartzMultilocation, maliciousXcmProgram);
+ });
+
+ const maxWaitBlocks = 3;
+
+ const xcmpQueueFailEvent = await helper.wait.eventOutcome<XcmV2TraitsError>(
+ maxWaitBlocks,
+ 'xcmpQueue',
+ 'Fail',
+ );
+
+ expect(
+ xcmpQueueFailEvent != null,
+ '\'xcmpQueue.FailEvent\' event is expected',
+ ).to.be.true;
+
+ expect(
+ xcmpQueueFailEvent!.isUntrustedReserveLocation,
+ 'The XCM error should be \'isUntrustedReserveLocation\'',
+ ).to.be.true;
+
+ const accountBalance = await helper.balance.getSubstrate(targetAccount.address);
+ expect(accountBalance).to.be.equal(0n);
+ });
});
tests/src/xcm/xcmUnique.test.tsdiffbeforeafterboth19import 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';222323const 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 });647648 itSub('Acala can send only up to its balance', async ({helper}) => {649 // set Acala's sovereign account's balance650 const acalaBalance = 10000n * (10n ** UNQ_DECIMALS);651 const acalaSovereignAccount = helper.address.paraSiblingSovereignAccount(ACALA_CHAIN);652 await helper.getSudo().balance.setBalanceSubstrate(alice, acalaSovereignAccount, acalaBalance);653654 const moreThanAcalaHas = acalaBalance * 2n;655656 let targetAccountBalance = 0n;657 const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);658659 const uniqueMultilocation = {660 V1: {661 parents: 1,662 interior: {663 X1: {Parachain: UNIQUE_CHAIN},664 },665 },666 };667668 const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(669 targetAccount.addressRaw,670 {671 Concrete: {672 parents: 0,673 interior: 'Here',674 },675 },676 moreThanAcalaHas,677 );678679 // Try to trick Unique680 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {681 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgram);682 });683684 const maxWaitBlocks = 3;685686 const xcmpQueueFailEvent = await helper.wait.eventOutcome<XcmV2TraitsError>(687 maxWaitBlocks,688 'xcmpQueue',689 'Fail',690 );691692 expect(693 xcmpQueueFailEvent != null,694 '\'xcmpQueue.FailEvent\' event is expected',695 ).to.be.true;696697 expect(698 xcmpQueueFailEvent!.isFailedToTransactAsset,699 'The XCM error should be \'FailedToTransactAsset\'',700 ).to.be.true;701702 targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);703 expect(targetAccountBalance).to.be.equal(0n);704705 // But Acala still can send the correct amount706 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 );717718 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {719 await helper.getSudo().xcm.send(alice, uniqueMultilocation, validXcmProgram);720 });721722 await helper.wait.newBlocks(maxWaitBlocks);723724 targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);725 expect(targetAccountBalance).to.be.equal(validTransferAmount);726 });727728 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);731732 const uniqueMultilocation = {733 V1: {734 parents: 1,735 interior: {736 X1: {737 Parachain: UNIQUE_CHAIN,738 },739 },740 },741 };742743 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 );757758 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {759 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgram);760 });761762 const maxWaitBlocks = 3;763764 const xcmpQueueFailEvent = await helper.wait.eventOutcome<XcmV2TraitsError>(765 maxWaitBlocks,766 'xcmpQueue',767 'Fail',768 );769770 expect(771 xcmpQueueFailEvent != null,772 '\'xcmpQueue.FailEvent\' event is expected',773 ).to.be.true;774775 expect(776 xcmpQueueFailEvent!.isUntrustedReserveLocation,777 'The XCM error should be \'isUntrustedReserveLocation\'',778 ).to.be.true;779780 const accountBalance = await helper.balance.getSubstrate(targetAccount.address);781 expect(accountBalance).to.be.equal(0n);782 });646});783});647784648// These tests are relevant only when the foreign asset pallet is disabled785// These tests are relevant only when786// the the corresponding foreign assets are not registered649describeXCM('[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;790791 const testAmount = 100_000_000_000n;792 let uniqueParachainJunction;793 let uniqueAccountJunction;794795 let uniqueParachainMultilocation: any;796 let uniqueAccountMultilocation: any;797 let uniqueCombinedMultilocation: any;651798652 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');802803 uniqueParachainJunction = {Parachain: UNIQUE_CHAIN};804 uniqueAccountJunction = {805 AccountId32: {806 network: 'Any',807 id: alice.addressRaw,808 },809 };810811 uniqueParachainMultilocation = {812 V1: {813 parents: 1,814 interior: {815 X1: uniqueParachainJunction,816 },817 },818 };819820 uniqueAccountMultilocation = {821 V1: {822 parents: 0,823 interior: {824 X1: uniqueAccountJunction,825 },826 },827 };828829 uniqueCombinedMultilocation = {830 V1: {831 parents: 1,832 interior: {833 X2: [uniqueParachainJunction, uniqueAccountJunction],834 },835 },836 };655837656 // 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 });841842 // eslint-disable-next-line require-await843 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {844 alith = helper.account.alithAccount();845 });659 });846 });847848 const expectFailedToTransact = async (network: string, helper: DevUniqueHelper) => {849 const maxWaitBlocks = 3;850851 const xcmpQueueFailEvent = await helper.wait.eventOutcome<XcmV2TraitsError>(852 maxWaitBlocks,853 'xcmpQueue',854 'Fail',855 );856857 expect(858 xcmpQueueFailEvent != null,859 `[reject ${network} tokens] 'xcmpQueue.FailEvent' event is expected`,860 ).to.be.true;861862 expect(863 xcmpQueueFailEvent!.isFailedToTransactAsset,864 `[reject ${network} tokens] The XCM error should be 'FailedToTransactAsset'`,865 ).to.be.true;866 };867868 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 });876877 await expectFailedToTransact('ACA', helper);878 });879880 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 });886887 await expectFailedToTransact('GLMR', helper);888 });660889661 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 };679680 const id = {907 const feeAssetItem = 0;681 Token: 'ACA',682 };683908684 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 });686687 const maxWaitBlocks = 3;688916689 const xcmpQueueFailEvent = await helper.wait.event(maxWaitBlocks, 'xcmpQueue', 'Fail');917 await expectFailedToTransact('ASTR', helper);690691 expect(692 xcmpQueueFailEvent != null,693 '[Acala] xcmpQueue.FailEvent event is expected',694 ).to.be.true;695696 const event = xcmpQueueFailEvent!.event;697 const outcome = event.data[1] as XcmV2TraitsError;698699 expect(700 outcome.isFailedToTransactAsset,701 '[Acala] The XCM error should be `FailedToTransactAsset`',702 ).to.be.true;703 });918 });704});919});705920985 expect(unqFees == 0n).to.be.true;1200 expect(unqFees == 0n).to.be.true;986 });1201 });12021203 // eslint-disable-next-line require-await1204 itSub.skip('Moonbeam can send only up to its balance', async ({helper}) => {1205 throw Error('Not yet implemented');1206 });12071208 // eslint-disable-next-line require-await1209 itSub.skip('Should not accept reserve transfer of UNQ from Moonbeam', async ({helper}) => {1210 throw Error('Not yet implemented');1211 });987});1212});9881213989describeXCM('[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 });119814231199 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 balance1201 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: {14291205 X1: {1430 const moreThanShidenHas = astarBalance * 2n;1206 Parachain: UNIQUE_CHAIN,14311207 },1432 let targetAccountBalance = 0n;1208 },1209 },1210 };12111212 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 };122514341226 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 };124514431246 // 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 );14541455 // Try to trick Unique1456 await usingAstarPlaygrounds(astarUrl, async (helper) => {1457 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgram);1458 });14591460 const maxWaitBlocks = 3;14611462 const xcmpQueueFailEvent = await helper.wait.eventOutcome<XcmV2TraitsError>(1463 maxWaitBlocks,1464 'xcmpQueue',1465 'Fail',1466 );14671468 expect(1469 xcmpQueueFailEvent != null,1470 '\'xcmpQueue.FailEvent\' event is expected',1471 ).to.be.true;14721473 expect(1474 xcmpQueueFailEvent!.isFailedToTransactAsset,1475 'The XCM error should be \'FailedToTransactAsset\'',1476 ).to.be.true;14771247 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);14801481 // But Astar still can send the correct amount1482 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 );14931494 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 });14971498 await helper.wait.newBlocks(maxWaitBlocks);14991500 targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1501 expect(targetAccountBalance).to.be.equal(validTransferAmount);1253 });1502 });15031504 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);15071508 const uniqueMultilocation = {1509 V1: {1510 parents: 1,1511 interior: {1512 X1: {1513 Parachain: UNIQUE_CHAIN,1514 },1515 },1516 },1517 };15181519 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 );15331534 await usingAstarPlaygrounds(astarUrl, async (helper) => {1535 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgram);1536 });15371538 const maxWaitBlocks = 3;15391540 const xcmpQueueFailEvent = await helper.wait.eventOutcome<XcmV2TraitsError>(1541 maxWaitBlocks,1542 'xcmpQueue',1543 'Fail',1544 );15451546 expect(1547 xcmpQueueFailEvent != null,1548 '\'xcmpQueue.FailEvent\' event is expected',1549 ).to.be.true;15501551 expect(1552 xcmpQueueFailEvent!.isUntrustedReserveLocation,1553 'The XCM error should be \'isUntrustedReserveLocation\'',1554 ).to.be.true;15551556 const accountBalance = await helper.balance.getSubstrate(targetAccount.address);1557 expect(accountBalance).to.be.equal(0n);1558 });15591254});1560});12551561