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

difftreelog

test run eslint --fix

Yaroslav Bolyukin2023-05-30parent: #833ab94.patch.diff
in: master

20 files changed

modifiedtests/src/benchmarks/mintFee/index.tsdiffbeforeafterboth
--- a/tests/src/benchmarks/mintFee/index.ts
+++ b/tests/src/benchmarks/mintFee/index.ts
@@ -222,9 +222,7 @@
       await collection.mintToken(
         donor,
         {Substrate: susbstrateReceiver.address},
-        PROPERTIES.slice(0, setup.propertiesNumber).map((p) => {
-          return {key: p.key, value: Buffer.from(p.value).toString()};
-        }),
+        PROPERTIES.slice(0, setup.propertiesNumber).map((p) => ({key: p.key, value: Buffer.from(p.value).toString()})),
       );
     },
   );
modifiedtests/src/benchmarks/opsFee/index.tsdiffbeforeafterboth
--- a/tests/src/benchmarks/opsFee/index.ts
+++ b/tests/src/benchmarks/opsFee/index.ts
@@ -379,7 +379,7 @@
   res['setCollectionProperties'].substrate = convertToTokens((await helper.arrange.calculcateFee(
     {Substrate: donor.address},
     () => collection.setProperties(donor, PROPERTIES.slice(0, 1)
-      .map(p => { return {key: p.key, value: p.value.toString()}; })),
+      .map(p => ({key: p.key, value: p.value.toString()}))),
   )));
 
   res['deleteCollectionProperties'].substrate = convertToTokens((await helper.arrange.calculcateFee(
@@ -755,7 +755,7 @@
   res['setCollectionProperties'].substrate = convertToTokens((await helper.arrange.calculcateFee(
     {Substrate: donor.address},
     () => collection.setProperties(donor, PROPERTIES.slice(0, 1)
-      .map(p => { return {key: p.key, value: p.value.toString()}; })),
+      .map(p => ({key: p.key, value: p.value.toString()}))),
   )));
 
   res['deleteCollectionProperties'].substrate = convertToTokens((await helper.arrange.calculcateFee(
modifiedtests/src/benchmarks/utils/common.tsdiffbeforeafterboth
--- a/tests/src/benchmarks/utils/common.ts
+++ b/tests/src/benchmarks/utils/common.ts
@@ -5,32 +5,26 @@
 
 export const PROPERTIES = Array(40)
   .fill(0)
-  .map((_, i) => {
-    return {
-      key: `key_${i}`,
-      value: Uint8Array.from(Buffer.from(`value_${i}`)),
-    };
-  });
+  .map((_, i) => ({
+    key: `key_${i}`,
+    value: Uint8Array.from(Buffer.from(`value_${i}`)),
+  }));
 
 export const SUBS_PROPERTIES = Array(40)
   .fill(0)
-  .map((_, i) => {
-    return {
-      key: `key_${i}`,
-      value: `value_${i}`,
-    };
-  });
+  .map((_, i) => ({
+    key: `key_${i}`,
+    value: `value_${i}`,
+  }));
 
-export const PERMISSIONS: ITokenPropertyPermission[] = PROPERTIES.map((p) => {
-  return {
-    key: p.key,
-    permission: {
-      tokenOwner: true,
-      collectionAdmin: true,
-      mutable: true,
-    },
-  };
-});
+export const PERMISSIONS: ITokenPropertyPermission[] = PROPERTIES.map((p) => ({
+  key: p.key,
+  permission: {
+    tokenOwner: true,
+    collectionAdmin: true,
+    mutable: true,
+  },
+}));
 
 export function convertToTokens(value: bigint, nominal = 1000_000_000_000_000_000n): number {
   return Number((value * 1000n) / nominal) / 1000;
modifiedtests/src/eth/collectionAdmin.test.tsdiffbeforeafterboth
--- a/tests/src/eth/collectionAdmin.test.ts
+++ b/tests/src/eth/collectionAdmin.test.ts
@@ -76,9 +76,7 @@
 
       // 2. Expect methods.collectionAdmins == api.rpc.unique.adminlist
       let adminListEth = await collectionEvm.methods.collectionAdmins().call();
-      adminListEth = adminListEth.map((element: IEthCrossAccountId) => {
-        return helper.address.convertCrossAccountFromEthCrossAccount(element);
-      });
+      adminListEth = adminListEth.map((element: IEthCrossAccountId) => helper.address.convertCrossAccountFromEthCrossAccount(element));
       expect(adminListRpc).to.be.like(adminListEth);
 
       // 3. check isOwnerOrAdminCross returns true:
modifiedtests/src/eth/collectionProperties.test.tsdiffbeforeafterboth
--- a/tests/src/eth/collectionProperties.test.ts
+++ b/tests/src/eth/collectionProperties.test.ts
@@ -181,17 +181,11 @@
       const propertyPermissions = data2?.raw.tokenPropertyPermissions;
       expect(propertyPermissions?.length).to.equal(2);
 
-      expect(propertyPermissions.find((tpp: ITokenPropertyPermission) => {
-        return tpp.key === 'URI' && tpp.permission.mutable && tpp.permission.collectionAdmin && !tpp.permission.tokenOwner;
-      })).to.be.not.null;
+      expect(propertyPermissions.find((tpp: ITokenPropertyPermission) => tpp.key === 'URI' && tpp.permission.mutable && tpp.permission.collectionAdmin && !tpp.permission.tokenOwner)).to.be.not.null;
 
-      expect(propertyPermissions.find((tpp: ITokenPropertyPermission) => {
-        return tpp.key === 'URISuffix' && tpp.permission.mutable && tpp.permission.collectionAdmin && !tpp.permission.tokenOwner;
-      })).to.be.not.null;
+      expect(propertyPermissions.find((tpp: ITokenPropertyPermission) => tpp.key === 'URISuffix' && tpp.permission.mutable && tpp.permission.collectionAdmin && !tpp.permission.tokenOwner)).to.be.not.null;
 
-      expect(data2?.raw.properties?.find((property: IProperty) => {
-        return property.key === 'baseURI' && property.value === BASE_URI;
-      })).to.be.not.null;
+      expect(data2?.raw.properties?.find((property: IProperty) => property.key === 'baseURI' && property.value === BASE_URI)).to.be.not.null;
 
       const token1Result = await contract.methods.mint(bruh).send();
       const tokenId1 = token1Result.events.Transfer.returnValues.tokenId;
modifiedtests/src/eth/evmCoder.test.tsdiffbeforeafterboth
--- a/tests/src/eth/evmCoder.test.ts
+++ b/tests/src/eth/evmCoder.test.ts
@@ -17,8 +17,7 @@
 import {IKeyringPair} from '@polkadot/types/types';
 import {itEth, expect, usingEthPlaygrounds} from './util';
 
-const getContractSource = (collectionAddress: string, contractAddress: string): string => {
-  return `
+const getContractSource = (collectionAddress: string, contractAddress: string): string => `
   // SPDX-License-Identifier: MIT
   pragma solidity ^0.8.0;
   interface ITest {
@@ -51,7 +50,6 @@
     }
   }
   `;
-};
 
 
 describe('Evm Coder tests', () => {
modifiedtests/src/eth/nonFungible.test.tsdiffbeforeafterboth
--- a/tests/src/eth/nonFungible.test.ts
+++ b/tests/src/eth/nonFungible.test.ts
@@ -102,17 +102,15 @@
       const receiverCrossSub = helper.ethCrossAccount.fromKeyringPair(receiverSub);
 
       // const receiverCross = helper.ethCrossAccount.fromKeyringPair(bob);
-      const properties = Array(5).fill(0).map((_, i) => { return {key: `key_${i}`, value: Buffer.from(`value_${i}`)}; });
+      const properties = Array(5).fill(0).map((_, i) => ({key: `key_${i}`, value: Buffer.from(`value_${i}`)}));
       const permissions: ITokenPropertyPermission[] = properties
-        .map(p => {
-          return {
-            key: p.key, permission: {
-              tokenOwner: false,
-              collectionAdmin: true,
-              mutable: false,
-            },
-          };
-        });
+        .map(p => ({
+          key: p.key, permission: {
+            tokenOwner: false,
+            collectionAdmin: true,
+            mutable: false,
+          },
+        }));
 
       const collection = await helper.nft.mintCollection(minter, {
         tokenPrefix: 'ethp',
@@ -146,7 +144,7 @@
       expect(tokenId).to.be.equal(expectedTokenId);
 
       expect(await contract.methods.properties(tokenId, []).call()).to.be.like(properties
-        .map(p => { return helper.ethProperty.property(p.key, p.value.toString()); }));
+        .map(p => helper.ethProperty.property(p.key, p.value.toString())));
 
       expect(await helper.nft.getTokenOwner(collection.collectionId, tokenId))
         .to.deep.eq(testCase === 'ethereum' ? {Ethereum: receiverEth.toLowerCase()} : {Substrate: receiverSub.address});
modifiedtests/src/eth/reFungible.test.tsdiffbeforeafterboth
--- a/tests/src/eth/reFungible.test.ts
+++ b/tests/src/eth/reFungible.test.ts
@@ -46,12 +46,11 @@
       const receiverSub = bob;
       const receiverCrossSub = helper.ethCrossAccount.fromKeyringPair(receiverSub);
 
-      const properties = Array(5).fill(0).map((_, i) => { return {key: `key_${i}`, value: Buffer.from(`value_${i}`)}; });
-      const permissions: ITokenPropertyPermission[] = properties.map(p => { return {key: p.key, permission: {
+      const properties = Array(5).fill(0).map((_, i) => ({key: `key_${i}`, value: Buffer.from(`value_${i}`)}));
+      const permissions: ITokenPropertyPermission[] = properties.map(p => ({key: p.key, permission: {
         tokenOwner: false,
         collectionAdmin: true,
-        mutable: false}};
-      });
+        mutable: false}}));
 
 
       const collection = await helper.rft.mintCollection(minter, {
@@ -86,7 +85,7 @@
       expect(tokenId).to.be.equal(expectedTokenId);
 
       expect(await contract.methods.properties(tokenId, []).call()).to.be.like(properties
-        .map(p => { return helper.ethProperty.property(p.key, p.value.toString()); }));
+        .map(p => helper.ethProperty.property(p.key, p.value.toString())));
 
       expect(await helper.nft.getTokenOwner(collection.collectionId, tokenId))
         .to.deep.eq(testCase === 'ethereum' ? {Ethereum: receiverEth.toLowerCase()} : {Substrate: receiverSub.address});
modifiedtests/src/eth/tokenProperties.test.tsdiffbeforeafterboth
--- a/tests/src/eth/tokenProperties.test.ts
+++ b/tests/src/eth/tokenProperties.test.ts
@@ -241,10 +241,10 @@
     itEth.ifWithPallets(`Can be multiple set/read for ${testCase.mode}`, testCase.requiredPallets, async({helper}) => {
       const caller = await helper.eth.createAccountWithBalance(donor);
 
-      const properties = Array(5).fill(0).map((_, i) => { return {key: `key_${i}`, value: Buffer.from(`value_${i}`)}; });
-      const permissions: ITokenPropertyPermission[] = properties.map(p => { return {key: p.key, permission: {tokenOwner: true,
+      const properties = Array(5).fill(0).map((_, i) => ({key: `key_${i}`, value: Buffer.from(`value_${i}`)}));
+      const permissions: ITokenPropertyPermission[] = properties.map(p => ({key: p.key, permission: {tokenOwner: true,
         collectionAdmin: true,
-        mutable: true}}; });
+        mutable: true}}));
 
       const collection = await helper[testCase.mode].mintCollection(alice, {
         tokenPrefix: 'ethp',
@@ -267,10 +267,10 @@
       await contract.methods.setProperties(token.tokenId, properties).send({from: caller});
 
       const values = await token.getProperties(properties.map(p => p.key));
-      expect(values).to.be.deep.equal(properties.map(p => { return {key: p.key, value: p.value.toString()}; }));
+      expect(values).to.be.deep.equal(properties.map(p => ({key: p.key, value: p.value.toString()})));
 
       expect(await contract.methods.properties(token.tokenId, []).call()).to.be.like(properties
-        .map(p => { return helper.ethProperty.property(p.key, p.value.toString()); }));
+        .map(p => helper.ethProperty.property(p.key, p.value.toString())));
 
       expect(await contract.methods.properties(token.tokenId, [properties[0].key]).call())
         .to.be.like([helper.ethProperty.property(properties[0].key, properties[0].value.toString())]);
modifiedtests/src/fungible.test.tsdiffbeforeafterboth
--- a/tests/src/fungible.test.ts
+++ b/tests/src/fungible.test.ts
@@ -46,7 +46,7 @@
 
   itSub('RPC method tokenOnewrs for fungible collection and token', async ({helper}) => {
     const ethAcc = {Ethereum: '0x67fb3503a61b284dc83fa96dceec4192db47dc7c'};
-    const facelessCrowd = (await helper.arrange.createAccounts(Array(7).fill(0n), donor)).map(keyring => {return {Substrate: keyring.address};});
+    const facelessCrowd = (await helper.arrange.createAccounts(Array(7).fill(0n), donor)).map(keyring => ({Substrate: keyring.address}));
 
     const collection = await helper.ft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
 
modifiedtests/src/getPropertiesRpc.test.tsdiffbeforeafterboth
--- a/tests/src/getPropertiesRpc.test.ts
+++ b/tests/src/getPropertiesRpc.test.ts
@@ -48,17 +48,13 @@
 describe('query properties RPC', () => {
   let alice: IKeyringPair;
 
-  const mintCollection = async (helper: UniqueHelper) => {
-    return await helper.nft.mintCollection(alice, {
-      tokenPrefix: 'prps',
-      properties: collectionProps,
-      tokenPropertyPermissions: tokenPropPermissions,
-    });
-  };
+  const mintCollection = async (helper: UniqueHelper) => await helper.nft.mintCollection(alice, {
+    tokenPrefix: 'prps',
+    properties: collectionProps,
+    tokenPropertyPermissions: tokenPropPermissions,
+  });
 
-  const mintToken = async (collection: UniqueNFTCollection) => {
-    return await collection.mintToken(alice, {Substrate: alice.address}, tokenProps);
-  };
+  const mintToken = async (collection: UniqueNFTCollection) => await collection.mintToken(alice, {Substrate: alice.address}, tokenProps);
 
 
   before(async () => {
modifiedtests/src/nesting/tokenProperties.test.tsdiffbeforeafterboth
--- a/tests/src/nesting/tokenProperties.test.ts
+++ b/tests/src/nesting/tokenProperties.test.ts
@@ -44,7 +44,7 @@
   async function mintCollectionWithAllPermissionsAndToken(helper: UniqueHelper, mode: 'NFT' | 'RFT'): Promise<[UniqueNFToken | UniqueRFToken, bigint]> {
     const collection = await (mode == 'NFT' ? helper.nft : helper.rft).mintCollection(alice, {
       tokenPropertyPermissions: permissions.flatMap(({permission, signers}, i) =>
-        signers.map(signer => {return {key: `${i+1}_${signer.address}`, permission};})),
+        signers.map(signer => ({key: `${i+1}_${signer.address}`, permission}))),
     });
     return mode == 'NFT' ? [await collection.mintToken(alice), 1n] : [await collection.mintToken(alice, 100n as any), 100n];
   }
@@ -201,7 +201,7 @@
     const collectionA = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}});
     const collectionB = await helper.nft.mintCollection(alice, {
       tokenPropertyPermissions: permissions.flatMap(({permission, signers}, i) =>
-        signers.map(signer => {return {key: `${i+1}_${signer.address}`, permission};})),
+        signers.map(signer => ({key: `${i+1}_${signer.address}`, permission}))),
     });
     const targetToken = await collectionA.mintToken(alice);
     const nestedToken = await collectionB.mintToken(alice, targetToken.nestingAccount());
@@ -239,7 +239,7 @@
     const collectionA = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}});
     const collectionB = await helper.nft.mintCollection(alice, {
       tokenPropertyPermissions: permissions.flatMap(({permission, signers}, i) =>
-        signers.map(signer => {return {key: `${i+1}_${signer.address}`, permission};})),
+        signers.map(signer => ({key: `${i+1}_${signer.address}`, permission}))),
     });
     const targetToken = await collectionA.mintToken(alice);
     const nestedToken = await collectionB.mintToken(alice, targetToken.nestingAccount());
@@ -284,7 +284,7 @@
     const collectionA = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}});
     const collectionB = await helper.nft.mintCollection(alice, {
       tokenPropertyPermissions: permissions.flatMap(({permission, signers}, i) =>
-        signers.map(signer => {return {key: `${i+1}_${signer.address}`, permission};})),
+        signers.map(signer => ({key: `${i+1}_${signer.address}`, permission}))),
     });
     const targetToken = await collectionA.mintToken(alice);
     const nestedToken = await collectionB.mintToken(alice, targetToken.nestingAccount());
@@ -477,7 +477,7 @@
 
   async function mintCollectionWithAllPermissionsAndToken(helper: UniqueHelper, mode: 'NFT' | 'RFT'): Promise<[UniqueNFToken | UniqueRFToken, bigint]> {
     const collection = await (mode == 'NFT' ? helper.nft : helper.rft).mintCollection(alice, {
-      tokenPropertyPermissions: constitution.map(({permission}, i) => {return {key: `${i+1}`, permission};}),
+      tokenPropertyPermissions: constitution.map(({permission}, i) => ({key: `${i+1}`, permission})),
     });
     return mode == 'NFT' ? [await collection.mintToken(alice), 1n] : [await collection.mintToken(alice, 100n as any), 100n];
   }
modifiedtests/src/refungible.test.tsdiffbeforeafterboth
--- a/tests/src/refungible.test.ts
+++ b/tests/src/refungible.test.ts
@@ -64,7 +64,7 @@
 
   itSub('RPC method tokenOwners for refungible collection and token', async ({helper}) => {
     const ethAcc = {Ethereum: '0x67fb3503a61b284dc83fa96dceec4192db47dc7c'};
-    const facelessCrowd = (await helper.arrange.createAccounts(Array(7).fill(0n), donor)).map(keyring => {return {Substrate: keyring.address};});
+    const facelessCrowd = (await helper.arrange.createAccounts(Array(7).fill(0n), donor)).map(keyring => ({Substrate: keyring.address}));
 
     const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
 
modifiedtests/src/rpc.test.tsdiffbeforeafterboth
--- a/tests/src/rpc.test.ts
+++ b/tests/src/rpc.test.ts
@@ -40,7 +40,7 @@
     // Set-up a few token owners of all stripes
     const ethAcc = {Ethereum: '0x67fb3503a61b284dc83fa96dceec4192db47dc7c'};
     const facelessCrowd = (await helper.arrange.createAccounts([0n, 0n, 0n, 0n, 0n, 0n, 0n], donor))
-      .map(i => {return {Substrate: i.address};});
+      .map(i => ({Substrate: i.address}));
 
     const collection = await helper.ft.mintCollection(alice, {name: 'RPC-2', tokenPrefix: 'RPC'});
     // mint some maximum (u128) amounts of tokens possible
modifiedtests/src/sub/appPromotion/appPromotion.test.tsdiffbeforeafterboth
--- a/tests/src/sub/appPromotion/appPromotion.test.ts
+++ b/tests/src/sub/appPromotion/appPromotion.test.ts
@@ -357,11 +357,9 @@
         const stakers = await getAccounts(3);
 
         await Promise.all(stakers.map(staker => helper.staking.stake(staker, 100n * nominal)));
-        await Promise.all(stakers.map(staker => {
-          return testCase.method === 'unstakeAll'
-            ? helper.staking.unstakeAll(staker)
-            : helper.staking.unstakePartial(staker, 100n * nominal);
-        }));
+        await Promise.all(stakers.map(staker => testCase.method === 'unstakeAll'
+          ? helper.staking.unstakeAll(staker)
+          : helper.staking.unstakePartial(staker, 100n * nominal)));
 
         await Promise.all(stakers.map(async (staker) => {
           expect(await helper.staking.getPendingUnstake({Substrate: staker.address})).to.be.equal(100n * nominal);
@@ -375,11 +373,9 @@
         const stakers = await getAccounts(10);
 
         await Promise.all(stakers.map(staker => helper.staking.stake(staker, 100n * nominal)));
-        const unstakingResults = await Promise.allSettled(stakers.map((staker, i) => {
-          return i % 2 === 0
-            ? helper.staking.unstakeAll(staker)
-            : helper.staking.unstakePartial(staker, 100n * nominal);
-        }));
+        const unstakingResults = await Promise.allSettled(stakers.map((staker, i) => i % 2 === 0
+          ? helper.staking.unstakeAll(staker)
+          : helper.staking.unstakePartial(staker, 100n * nominal)));
 
         const successfulUnstakes = unstakingResults.filter(result => result.status === 'fulfilled');
         expect(successfulUnstakes).to.have.length(3);
modifiedtests/src/util/index.tsdiffbeforeafterboth
--- a/tests/src/util/index.ts
+++ b/tests/src/util/index.ts
@@ -19,13 +19,9 @@
 chai.use(chaiSubset);
 export const expect = chai.expect;
 
-const getTestHash = (filename: string) => {
-  return crypto.createHash('md5').update(filename).digest('hex');
-};
+const getTestHash = (filename: string) => crypto.createHash('md5').update(filename).digest('hex');
 
-export const getTestSeed = (filename: string) => {
-  return `//Alice+${getTestHash(filename)}`;
-};
+export const getTestSeed = (filename: string) => `//Alice+${getTestHash(filename)}`;
 
 async function usingPlaygroundsGeneral<T extends ChainHelperBase>(helperType: new(logger: ILogger) => T, url: string, code: (helper: T, privateKey: (seed: string | {filename?: string, url?: string, ignoreFundsPresence?: boolean}) => Promise<IKeyringPair>) => Promise<void>) {
   const silentConsole = new SilentConsole();
@@ -65,49 +61,27 @@
   }
 }
 
-export const usingPlaygrounds = (code: (helper: DevUniqueHelper, privateKey: (seed: string | {filename?: string, url?: string, ignoreFundsPresence?: boolean}) => Promise<IKeyringPair>) => Promise<void>, url: string = config.substrateUrl) => {
-  return usingPlaygroundsGeneral<DevUniqueHelper>(DevUniqueHelper, url, code);
-};
+export const usingPlaygrounds = (code: (helper: DevUniqueHelper, privateKey: (seed: string | {filename?: string, url?: string, ignoreFundsPresence?: boolean}) => Promise<IKeyringPair>) => Promise<void>, url: string = config.substrateUrl) => usingPlaygroundsGeneral<DevUniqueHelper>(DevUniqueHelper, url, code);
 
-export const usingWestmintPlaygrounds = (url: string, code: (helper: DevWestmintHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => {
-  return usingPlaygroundsGeneral<DevWestmintHelper>(DevWestmintHelper, url, code);
-};
+export const usingWestmintPlaygrounds = (url: string, code: (helper: DevWestmintHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => usingPlaygroundsGeneral<DevWestmintHelper>(DevWestmintHelper, url, code);
 
-export const usingStateminePlaygrounds = (url: string, code: (helper: DevWestmintHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => {
-  return usingPlaygroundsGeneral<DevStatemineHelper>(DevWestmintHelper, url, code);
-};
+export const usingStateminePlaygrounds = (url: string, code: (helper: DevWestmintHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => usingPlaygroundsGeneral<DevStatemineHelper>(DevWestmintHelper, url, code);
 
-export const usingStatemintPlaygrounds = (url: string, code: (helper: DevWestmintHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => {
-  return usingPlaygroundsGeneral<DevStatemintHelper>(DevWestmintHelper, url, code);
-};
+export const usingStatemintPlaygrounds = (url: string, code: (helper: DevWestmintHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => usingPlaygroundsGeneral<DevStatemintHelper>(DevWestmintHelper, url, code);
 
-export const usingRelayPlaygrounds = (url: string, code: (helper: DevRelayHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => {
-  return usingPlaygroundsGeneral<DevRelayHelper>(DevRelayHelper, url, code);
-};
+export const usingRelayPlaygrounds = (url: string, code: (helper: DevRelayHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => usingPlaygroundsGeneral<DevRelayHelper>(DevRelayHelper, url, code);
 
-export const usingAcalaPlaygrounds = (url: string, code: (helper: DevAcalaHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => {
-  return usingPlaygroundsGeneral<DevAcalaHelper>(DevAcalaHelper, url, code);
-};
+export const usingAcalaPlaygrounds = (url: string, code: (helper: DevAcalaHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => usingPlaygroundsGeneral<DevAcalaHelper>(DevAcalaHelper, url, code);
 
-export const usingKaruraPlaygrounds = (url: string, code: (helper: DevKaruraHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => {
-  return usingPlaygroundsGeneral<DevKaruraHelper>(DevAcalaHelper, url, code);
-};
+export const usingKaruraPlaygrounds = (url: string, code: (helper: DevKaruraHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => usingPlaygroundsGeneral<DevKaruraHelper>(DevAcalaHelper, url, code);
 
-export const usingMoonbeamPlaygrounds = (url: string, code: (helper: DevMoonbeamHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => {
-  return usingPlaygroundsGeneral<DevMoonbeamHelper>(DevMoonbeamHelper, url, code);
-};
+export const usingMoonbeamPlaygrounds = (url: string, code: (helper: DevMoonbeamHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => usingPlaygroundsGeneral<DevMoonbeamHelper>(DevMoonbeamHelper, url, code);
 
-export const usingMoonriverPlaygrounds = (url: string, code: (helper: DevMoonbeamHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => {
-  return usingPlaygroundsGeneral<DevMoonriverHelper>(DevMoonriverHelper, url, code);
-};
+export const usingMoonriverPlaygrounds = (url: string, code: (helper: DevMoonbeamHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => usingPlaygroundsGeneral<DevMoonriverHelper>(DevMoonriverHelper, url, code);
 
-export const usingAstarPlaygrounds = (url: string, code: (helper: DevAstarHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => {
-  return usingPlaygroundsGeneral<DevAstarHelper>(DevAstarHelper, url, code);
-};
+export const usingAstarPlaygrounds = (url: string, code: (helper: DevAstarHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => usingPlaygroundsGeneral<DevAstarHelper>(DevAstarHelper, url, code);
 
-export const usingShidenPlaygrounds = (url: string, code: (helper: DevShidenHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => {
-  return usingPlaygroundsGeneral<DevShidenHelper>(DevShidenHelper, url, code);
-};
+export const usingShidenPlaygrounds = (url: string, code: (helper: DevShidenHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => usingPlaygroundsGeneral<DevShidenHelper>(DevShidenHelper, url, code);
 
 export const MINIMUM_DONOR_FUND = 100_000n;
 export const DONOR_FUNDING = 2_000_000n;
modifiedtests/src/util/playgrounds/unique.dev.tsdiffbeforeafterboth
--- a/tests/src/util/playgrounds/unique.dev.ts
+++ b/tests/src/util/playgrounds/unique.dev.ts
@@ -771,9 +771,7 @@
     // <<< Referendum voting <<<
 
     // Wait the proposal to pass
-    await this.helper.wait.expectEvent(3, Event.Democracy.Passed, event => {
-      return event.referendumIndex() == referendumIndex;
-    });
+    await this.helper.wait.expectEvent(3, Event.Democracy.Passed, event => event.referendumIndex() == referendumIndex);
 
     await this.helper.wait.newBlocks(1);
 
@@ -924,7 +922,7 @@
   event<T extends IEventHelper>(
     maxBlocksToWait: number,
     eventHelperType: new () => T,
-    filter: (_: T) => boolean = () => { return true; },
+    filter: (_: T) => boolean = () => true,
   ) {
     // eslint-disable-next-line no-async-promise-executor
     const promise = new Promise<T | null>(async (resolve) => {
@@ -970,7 +968,7 @@
   async expectEvent<T extends IEventHelper>(
     maxBlocksToWait: number,
     eventHelperType: new () => T,
-    filter: (e: T) => boolean = () => { return true; },
+    filter: (e: T) => boolean = () => true,
   ) {
     const e = await this.event(maxBlocksToWait, eventHelperType, filter);
     if (e == null) {
@@ -1077,9 +1075,7 @@
   async startCapture() {
     this.stopCapture();
     this.unsubscribe = (await this.helper.getApi().query.system.events((eventRecords: FrameSystemEventRecord[]) => {
-      const newEvents = eventRecords.filter(r => {
-        return r.event.section == this.eventSection && r.event.method == this.eventMethod;
-      });
+      const newEvents = eventRecords.filter(r => r.event.section == this.eventSection && r.event.method == this.eventMethod);
 
       this.events.push(...newEvents);
     })) as any;
@@ -1105,12 +1101,10 @@
 
   async payoutStakers(signer: IKeyringPair, stakersToPayout: number):  Promise<{staker: string, stake: bigint, payout: bigint}[]> {
     const payoutResult = await this.helper.executeExtrinsic(signer, 'api.tx.appPromotion.payoutStakers', [stakersToPayout], true);
-    return payoutResult.result.events.filter(e => e.event.method === 'StakingRecalculation').map(e => {
-      return {
-        staker: e.event.data[0].toString(),
-        stake: e.event.data[1].toBigInt(),
-        payout: e.event.data[2].toBigInt(),
-      };
-    });
+    return payoutResult.result.events.filter(e => e.event.method === 'StakingRecalculation').map(e => ({
+      staker: e.event.data[0].toString(),
+      stake: e.event.data[1].toBigInt(),
+      payout: e.event.data[2].toBigInt(),
+    }));
   }
 }
modifiedtests/src/util/playgrounds/unique.tsdiffbeforeafterboth
--- a/tests/src/util/playgrounds/unique.ts
+++ b/tests/src/util/playgrounds/unique.ts
@@ -2395,7 +2395,7 @@
     return total.toBigInt();
   }
 
-  async getLocked(address: TSubstrateAccount): Promise<[{ id: string, amount: bigint, reason: string }]> {
+  async getLocked(address: TSubstrateAccount): Promise<{ id: string, amount: bigint, reason: string }[]> {
     const locks = (await this.helper.callRpc('api.query.balances.locks', [address])).toHuman();
     return locks.map((lock: any) => ({id: lock.id, amount: BigInt(lock.amount.replace(/,/g, '')), reasons: lock.reasons}));
   }
@@ -2581,14 +2581,12 @@
    */
   async getVestingSchedules(address: TSubstrateAccount): Promise<{ start: bigint, period: bigint, periodCount: bigint, perPeriod: bigint }[]> {
     const schedule = (await this.helper.callRpc('api.query.vesting.vestingSchedules', [address])).toJSON();
-    return schedule.map((schedule: any) => {
-      return {
-        start: BigInt(schedule.start),
-        period: BigInt(schedule.period),
-        periodCount: BigInt(schedule.periodCount),
-        perPeriod: BigInt(schedule.perPeriod),
-      };
-    });
+    return schedule.map((schedule: any) => ({
+      start: BigInt(schedule.start),
+      period: BigInt(schedule.period),
+      periodCount: BigInt(schedule.periodCount),
+      perPeriod: BigInt(schedule.perPeriod),
+    }));
   }
 
   /**
@@ -2804,12 +2802,10 @@
    */
   async getTotalStakedPerBlock(address: ICrossAccountId): Promise<IStakingInfo[]> {
     const rawTotalStakerdPerBlock = await this.helper.callRpc('api.rpc.appPromotion.totalStakedPerBlock', [address]);
-    return rawTotalStakerdPerBlock.map(([block, amount]: any[]) => {
-      return {
-        block: block.toBigInt(),
-        amount: amount.toBigInt(),
-      };
-    });
+    return rawTotalStakerdPerBlock.map(([block, amount]: any[]) => ({
+      block: block.toBigInt(),
+      amount: amount.toBigInt(),
+    }));
   }
 
   /**
@@ -2828,12 +2824,10 @@
    */
   async getPendingUnstakePerBlock(address: ICrossAccountId): Promise<IStakingInfo[]> {
     const rawUnstakedPerBlock = await this.helper.callRpc('api.rpc.appPromotion.pendingUnstakePerBlock', [address]);
-    const result = rawUnstakedPerBlock.map(([block, amount]: any[]) => {
-      return {
-        block: block.toBigInt(),
-        amount: amount.toBigInt(),
-      };
-    });
+    const result = rawUnstakedPerBlock.map(([block, amount]: any[]) => ({
+      block: block.toBigInt(),
+      amount: amount.toBigInt(),
+    }));
     return result;
   }
 }
modifiedtests/src/xcm/xcmQuartz.test.tsdiffbeforeafterboth
--- a/tests/src/xcm/xcmQuartz.test.ts
+++ b/tests/src/xcm/xcmQuartz.test.ts
@@ -682,10 +682,8 @@
       maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
     });
 
-    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => {
-      return event.messageHash() == maliciousXcmProgramSent.messageHash()
-        && event.outcome().isFailedToTransactAsset;
-    });
+    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash() == maliciousXcmProgramSent.messageHash()
+        && event.outcome().isFailedToTransactAsset);
 
     targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);
     expect(targetAccountBalance).to.be.equal(0n);
@@ -765,10 +763,8 @@
       maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
     });
 
-    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => {
-      return event.messageHash() == maliciousXcmProgramFullIdSent.messageHash()
-        && event.outcome().isUntrustedReserveLocation;
-    });
+    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash() == maliciousXcmProgramFullIdSent.messageHash()
+        && event.outcome().isUntrustedReserveLocation);
 
     let accountBalance = await helper.balance.getSubstrate(targetAccount.address);
     expect(accountBalance).to.be.equal(0n);
@@ -780,10 +776,8 @@
       maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
     });
 
-    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => {
-      return event.messageHash() == maliciousXcmProgramHereIdSent.messageHash()
-        && event.outcome().isUntrustedReserveLocation;
-    });
+    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash() == maliciousXcmProgramHereIdSent.messageHash()
+        && event.outcome().isUntrustedReserveLocation);
 
     accountBalance = await helper.balance.getSubstrate(targetAccount.address);
     expect(accountBalance).to.be.equal(0n);
@@ -858,10 +852,8 @@
   });
 
   const expectFailedToTransact = async (helper: DevUniqueHelper, messageSent: any) => {
-    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => {
-      return event.messageHash() == messageSent.messageHash()
-        && event.outcome().isFailedToTransactAsset;
-    });
+    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash() == messageSent.messageHash()
+        && event.outcome().isFailedToTransactAsset);
   };
 
   itSub('Quartz rejects KAR tokens from Karura', async ({helper}) => {
@@ -1172,10 +1164,8 @@
       maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
     });
 
-    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => {
-      return event.messageHash() == maliciousXcmProgramSent.messageHash()
-        && event.outcome().isFailedToTransactAsset;
-    });
+    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash() == maliciousXcmProgramSent.messageHash()
+        && event.outcome().isFailedToTransactAsset);
 
     targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);
     expect(targetAccountBalance).to.be.equal(0n);
@@ -1263,10 +1253,8 @@
       maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
     });
 
-    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => {
-      return event.messageHash() == maliciousXcmProgramFullIdSent.messageHash()
-        && event.outcome().isUntrustedReserveLocation;
-    });
+    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash() == maliciousXcmProgramFullIdSent.messageHash()
+        && event.outcome().isUntrustedReserveLocation);
 
     let accountBalance = await helper.balance.getSubstrate(targetAccount.address);
     expect(accountBalance).to.be.equal(0n);
@@ -1282,10 +1270,8 @@
       maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
     });
 
-    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => {
-      return event.messageHash() == maliciousXcmProgramHereIdSent.messageHash()
-        && event.outcome().isUntrustedReserveLocation;
-    });
+    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash() == maliciousXcmProgramHereIdSent.messageHash()
+        && event.outcome().isUntrustedReserveLocation);
 
     accountBalance = await helper.balance.getSubstrate(targetAccount.address);
     expect(accountBalance).to.be.equal(0n);
@@ -1544,10 +1530,8 @@
       maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
     });
 
-    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => {
-      return event.messageHash() == maliciousXcmProgramSent.messageHash()
-        && event.outcome().isFailedToTransactAsset;
-    });
+    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash() == maliciousXcmProgramSent.messageHash()
+        && event.outcome().isFailedToTransactAsset);
 
     targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);
     expect(targetAccountBalance).to.be.equal(0n);
@@ -1627,10 +1611,8 @@
       maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
     });
 
-    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => {
-      return event.messageHash() == maliciousXcmProgramFullIdSent.messageHash()
-        && event.outcome().isUntrustedReserveLocation;
-    });
+    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash() == maliciousXcmProgramFullIdSent.messageHash()
+        && event.outcome().isUntrustedReserveLocation);
 
     let accountBalance = await helper.balance.getSubstrate(targetAccount.address);
     expect(accountBalance).to.be.equal(0n);
@@ -1642,10 +1624,8 @@
       maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
     });
 
-    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => {
-      return event.messageHash() == maliciousXcmProgramHereIdSent.messageHash()
-        && event.outcome().isUntrustedReserveLocation;
-    });
+    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash() == maliciousXcmProgramHereIdSent.messageHash()
+        && event.outcome().isUntrustedReserveLocation);
 
     accountBalance = await helper.balance.getSubstrate(targetAccount.address);
     expect(accountBalance).to.be.equal(0n);
modifiedtests/src/xcm/xcmUnique.test.tsdiffbeforeafterboth
before · tests/src/xcm/xcmUnique.test.ts
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617import {IKeyringPair} from '@polkadot/types/types';18import config from '../config';19import {itSub, expect, describeXCM, usingPlaygrounds, usingAcalaPlaygrounds, usingRelayPlaygrounds, usingMoonbeamPlaygrounds, usingStatemintPlaygrounds, usingAstarPlaygrounds} from '../util';20import {DevUniqueHelper, Event} from '../util/playgrounds/unique.dev';2122const UNIQUE_CHAIN = 2037;23const STATEMINT_CHAIN = 1000;24const ACALA_CHAIN = 2000;25const MOONBEAM_CHAIN = 2004;26const ASTAR_CHAIN = 2006;2728const STATEMINT_PALLET_INSTANCE = 50;2930const relayUrl = config.relayUrl;31const statemintUrl = config.statemintUrl;32const acalaUrl = config.acalaUrl;33const moonbeamUrl = config.moonbeamUrl;34const astarUrl = config.astarUrl;3536const RELAY_DECIMALS = 12;37const STATEMINT_DECIMALS = 12;38const ACALA_DECIMALS = 12;39const ASTAR_DECIMALS = 18n;40const UNQ_DECIMALS = 18n;4142const TRANSFER_AMOUNT = 2000000000000000000000000n;4344const FUNDING_AMOUNT = 3_500_000_0000_000_000n;4546const TRANSFER_AMOUNT_RELAY = 50_000_000_000_000_000n;4748const USDT_ASSET_ID = 100;49const USDT_ASSET_METADATA_DECIMALS = 18;50const USDT_ASSET_METADATA_NAME = 'USDT';51const USDT_ASSET_METADATA_DESCRIPTION = 'USDT';52const USDT_ASSET_METADATA_MINIMAL_BALANCE = 1n;53const USDT_ASSET_AMOUNT = 10_000_000_000_000_000_000_000_000n;5455const SAFE_XCM_VERSION = 2;5657describeXCM('[XCM] Integration test: Exchanging USDT with Statemint', () => {58  let alice: IKeyringPair;59  let bob: IKeyringPair;6061  let balanceStmnBefore: bigint;62  let balanceStmnAfter: bigint;6364  let balanceUniqueBefore: bigint;65  let balanceUniqueAfter: bigint;66  let balanceUniqueFinal: bigint;6768  let balanceBobBefore: bigint;69  let balanceBobAfter: bigint;70  let balanceBobFinal: bigint;7172  let balanceBobRelayTokenBefore: bigint;73  let balanceBobRelayTokenAfter: bigint;747576  before(async () => {77    await usingPlaygrounds(async (helper, privateKey) => {78      alice = await privateKey('//Alice');79      bob = await privateKey('//Bob'); // sovereign account on Statemint funds donor8081      // Set the default version to wrap the first message to other chains.82      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);83    });8485    await usingRelayPlaygrounds(relayUrl, async (helper) => {86      // Fund accounts on Statemint87      const relayXcmVersion = 2;88      await helper.xcm.teleportNativeAsset(alice, STATEMINT_CHAIN, alice.addressRaw, FUNDING_AMOUNT, relayXcmVersion);89      await helper.xcm.teleportNativeAsset(alice, STATEMINT_CHAIN, bob.addressRaw, FUNDING_AMOUNT, relayXcmVersion);90    });9192    await usingStatemintPlaygrounds(statemintUrl, async (helper) => {93      const sovereignFundingAmount = 3_500_000_000n;9495      await helper.assets.create(96        alice,97        USDT_ASSET_ID,98        alice.address,99        USDT_ASSET_METADATA_MINIMAL_BALANCE,100      );101      await helper.assets.setMetadata(102        alice,103        USDT_ASSET_ID,104        USDT_ASSET_METADATA_NAME,105        USDT_ASSET_METADATA_DESCRIPTION,106        USDT_ASSET_METADATA_DECIMALS,107      );108      await helper.assets.mint(109        alice,110        USDT_ASSET_ID,111        alice.address,112        USDT_ASSET_AMOUNT,113      );114115      // funding parachain sovereing account on Statemint.116      // The sovereign account should be created before any action117      // (the assets pallet on Statemint check if the sovereign account exists)118      const parachainSovereingAccount = helper.address.paraSiblingSovereignAccount(UNIQUE_CHAIN);119      await helper.balance.transferToSubstrate(bob, parachainSovereingAccount, sovereignFundingAmount);120    });121122123    await usingPlaygrounds(async (helper) => {124      const location = {125        V2: {126          parents: 1,127          interior: {X3: [128            {129              Parachain: STATEMINT_CHAIN,130            },131            {132              PalletInstance: STATEMINT_PALLET_INSTANCE,133            },134            {135              GeneralIndex: USDT_ASSET_ID,136            },137          ]},138        },139      };140141      const metadata =142      {143        name: USDT_ASSET_ID,144        symbol: USDT_ASSET_METADATA_NAME,145        decimals: USDT_ASSET_METADATA_DECIMALS,146        minimalBalance: USDT_ASSET_METADATA_MINIMAL_BALANCE,147      };148      await helper.getSudo().foreignAssets.register(alice, alice.address, location, metadata);149      balanceUniqueBefore = await helper.balance.getSubstrate(alice.address);150    });151152153    // Providing the relay currency to the unique sender account154    // (fee for USDT XCM are paid in relay tokens)155    await usingRelayPlaygrounds(relayUrl, async (helper) => {156      const destination = {157        V1: {158          parents: 0,159          interior: {X1: {160            Parachain: UNIQUE_CHAIN,161          },162          },163        }};164165      const beneficiary = {166        V1: {167          parents: 0,168          interior: {X1: {169            AccountId32: {170              network: 'Any',171              id: alice.addressRaw,172            },173          }},174        },175      };176177      const assets = {178        V1: [179          {180            id: {181              Concrete: {182                parents: 0,183                interior: 'Here',184              },185            },186            fun: {187              Fungible: TRANSFER_AMOUNT_RELAY,188            },189          },190        ],191      };192193      const feeAssetItem = 0;194195      await helper.xcm.limitedReserveTransferAssets(alice, destination, beneficiary, assets, feeAssetItem, 'Unlimited');196    });197198  });199200  itSub('Should connect and send USDT from Statemint to Unique', async ({helper}) => {201    await usingStatemintPlaygrounds(statemintUrl, async (helper) => {202      const dest = {203        V1: {204          parents: 1,205          interior: {X1: {206            Parachain: UNIQUE_CHAIN,207          },208          },209        }};210211      const beneficiary = {212        V1: {213          parents: 0,214          interior: {X1: {215            AccountId32: {216              network: 'Any',217              id: alice.addressRaw,218            },219          }},220        },221      };222223      const assets = {224        V1: [225          {226            id: {227              Concrete: {228                parents: 0,229                interior: {230                  X2: [231                    {232                      PalletInstance: STATEMINT_PALLET_INSTANCE,233                    },234                    {235                      GeneralIndex: USDT_ASSET_ID,236                    },237                  ]},238              },239            },240            fun: {241              Fungible: TRANSFER_AMOUNT,242            },243          },244        ],245      };246247      const feeAssetItem = 0;248249      balanceStmnBefore = await helper.balance.getSubstrate(alice.address);250      await helper.xcm.limitedReserveTransferAssets(alice, dest, beneficiary, assets, feeAssetItem, 'Unlimited');251252      balanceStmnAfter = await helper.balance.getSubstrate(alice.address);253254      // common good parachain take commission in it native token255      console.log(256        '[Statemint -> Unique] transaction fees on Statemint: %s WND',257        helper.util.bigIntToDecimals(balanceStmnBefore - balanceStmnAfter, STATEMINT_DECIMALS),258      );259      expect(balanceStmnBefore > balanceStmnAfter).to.be.true;260261    });262263264    // ensure that asset has been delivered265    await helper.wait.newBlocks(3);266267    // expext collection id will be with id 1268    const free = await helper.ft.getBalance(1, {Substrate: alice.address});269270    balanceUniqueAfter = await helper.balance.getSubstrate(alice.address);271272    console.log(273      '[Statemint -> Unique] transaction fees on Unique: %s USDT',274      helper.util.bigIntToDecimals(TRANSFER_AMOUNT - free, USDT_ASSET_METADATA_DECIMALS),275    );276    console.log(277      '[Statemint -> Unique] transaction fees on Unique: %s UNQ',278      helper.util.bigIntToDecimals(balanceUniqueAfter - balanceUniqueBefore),279    );280    // commission has not paid in USDT token281    expect(free).to.be.equal(TRANSFER_AMOUNT);282    // ... and parachain native token283    expect(balanceUniqueAfter == balanceUniqueBefore).to.be.true;284  });285286  itSub('Should connect and send USDT from Unique to Statemint back', async ({helper}) => {287    const destination = {288      V2: {289        parents: 1,290        interior: {X2: [291          {292            Parachain: STATEMINT_CHAIN,293          },294          {295            AccountId32: {296              network: 'Any',297              id: alice.addressRaw,298            },299          },300        ]},301      },302    };303304    const relayFee = 400_000_000_000_000n;305    const currencies: [any, bigint][] = [306      [307        {308          ForeignAssetId: 0,309        },310        TRANSFER_AMOUNT,311      ],312      [313        {314          NativeAssetId: 'Parent',315        },316        relayFee,317      ],318    ];319320    const feeItem = 1;321322    await helper.xTokens.transferMulticurrencies(alice, currencies, feeItem, destination, 'Unlimited');323324    // the commission has been paid in parachain native token325    balanceUniqueFinal = await helper.balance.getSubstrate(alice.address);326    console.log('[Unique -> Statemint] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(balanceUniqueAfter - balanceUniqueFinal));327    expect(balanceUniqueAfter > balanceUniqueFinal).to.be.true;328329    await usingStatemintPlaygrounds(statemintUrl, async (helper) => {330      await helper.wait.newBlocks(3);331332      // The USDT token never paid fees. Its amount not changed from begin value.333      // Also check that xcm transfer has been succeeded334      expect((await helper.assets.account(USDT_ASSET_ID, alice.address))! == USDT_ASSET_AMOUNT).to.be.true;335    });336  });337338  itSub('Should connect and send Relay token to Unique', async ({helper}) => {339    balanceBobBefore = await helper.balance.getSubstrate(bob.address);340    balanceBobRelayTokenBefore = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'});341342    await usingRelayPlaygrounds(relayUrl, async (helper) => {343      const destination = {344        V1: {345          parents: 0,346          interior: {X1: {347            Parachain: UNIQUE_CHAIN,348          },349          },350        }};351352      const beneficiary = {353        V1: {354          parents: 0,355          interior: {X1: {356            AccountId32: {357              network: 'Any',358              id: bob.addressRaw,359            },360          }},361        },362      };363364      const assets = {365        V1: [366          {367            id: {368              Concrete: {369                parents: 0,370                interior: 'Here',371              },372            },373            fun: {374              Fungible: TRANSFER_AMOUNT_RELAY,375            },376          },377        ],378      };379380      const feeAssetItem = 0;381382      await helper.xcm.limitedReserveTransferAssets(bob, destination, beneficiary, assets, feeAssetItem, 'Unlimited');383    });384385    await helper.wait.newBlocks(3);386387    balanceBobAfter = await helper.balance.getSubstrate(bob.address);388    balanceBobRelayTokenAfter = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'});389390    const wndFeeOnUnique = balanceBobRelayTokenAfter - TRANSFER_AMOUNT_RELAY - balanceBobRelayTokenBefore;391    const wndDiffOnUnique = balanceBobRelayTokenAfter - balanceBobRelayTokenBefore;392    console.log(393      '[Relay (Westend) -> Unique] transaction fees: %s UNQ',394      helper.util.bigIntToDecimals(balanceBobAfter - balanceBobBefore),395    );396    console.log(397      '[Relay (Westend) -> Unique] transaction fees: %s WND',398      helper.util.bigIntToDecimals(wndFeeOnUnique, STATEMINT_DECIMALS),399    );400    console.log('[Relay (Westend) -> Unique] actually delivered: %s WND', wndDiffOnUnique);401    expect(wndFeeOnUnique == 0n, 'No incoming WND fees should be taken').to.be.true;402    expect(balanceBobBefore == balanceBobAfter, 'No incoming UNQ fees should be taken').to.be.true;403  });404405  itSub('Should connect and send Relay token back', async ({helper}) => {406    let relayTokenBalanceBefore: bigint;407    let relayTokenBalanceAfter: bigint;408    await usingRelayPlaygrounds(relayUrl, async (helper) => {409      relayTokenBalanceBefore = await helper.balance.getSubstrate(bob.address);410    });411412    const destination = {413      V2: {414        parents: 1,415        interior: {416          X1:{417            AccountId32: {418              network: 'Any',419              id: bob.addressRaw,420            },421          },422        },423      },424    };425426    const currencies: any = [427      [428        {429          NativeAssetId: 'Parent',430        },431        TRANSFER_AMOUNT_RELAY,432      ],433    ];434435    const feeItem = 0;436437    await helper.xTokens.transferMulticurrencies(bob, currencies, feeItem, destination, 'Unlimited');438439    balanceBobFinal = await helper.balance.getSubstrate(bob.address);440    console.log('[Unique -> Relay (Westend)] transaction fees: %s UNQ',  helper.util.bigIntToDecimals(balanceBobAfter - balanceBobFinal));441442    await usingRelayPlaygrounds(relayUrl, async (helper) => {443      await helper.wait.newBlocks(10);444      relayTokenBalanceAfter = await helper.balance.getSubstrate(bob.address);445446      const diff = relayTokenBalanceAfter - relayTokenBalanceBefore;447      console.log('[Unique -> Relay (Westend)] actually delivered: %s WND', helper.util.bigIntToDecimals(diff, RELAY_DECIMALS));448      expect(diff > 0, 'Relay tokens was not delivered back').to.be.true;449    });450  });451});452453describeXCM('[XCM] Integration test: Exchanging tokens with Acala', () => {454  let alice: IKeyringPair;455  let randomAccount: IKeyringPair;456457  let balanceUniqueTokenInit: bigint;458  let balanceUniqueTokenMiddle: bigint;459  let balanceUniqueTokenFinal: bigint;460  let balanceAcalaTokenInit: bigint;461  let balanceAcalaTokenMiddle: bigint;462  let balanceAcalaTokenFinal: bigint;463  let balanceUniqueForeignTokenInit: bigint;464  let balanceUniqueForeignTokenMiddle: bigint;465  let balanceUniqueForeignTokenFinal: bigint;466467  // computed by a test transfer from prod Unique to prod Acala.468  // 2 UNQ sent https://unique.subscan.io/xcm_message/polkadot-bad0b68847e2398af25d482e9ee6f9c1f9ec2a48469  // 1.898970000000000000 UNQ received (you can check Acala's chain state in the corresponding block)470  const expectedAcalaIncomeFee = 2000000000000000000n - 1898970000000000000n;471472  const ACALA_BACKWARD_TRANSFER_AMOUNT = TRANSFER_AMOUNT - expectedAcalaIncomeFee;473474  before(async () => {475    await usingPlaygrounds(async (helper, privateKey) => {476      alice = await privateKey('//Alice');477      [randomAccount] = await helper.arrange.createAccounts([0n], alice);478479      // Set the default version to wrap the first message to other chains.480      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);481    });482483    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {484      const destination = {485        V1: {486          parents: 1,487          interior: {488            X1: {489              Parachain: UNIQUE_CHAIN,490            },491          },492        },493      };494495      const metadata = {496        name: 'Unique Network',497        symbol: 'UNQ',498        decimals: 18,499        minimalBalance: 1250000000000000000n,500      };501502      await helper.getSudo().assetRegistry.registerForeignAsset(alice, destination, metadata);503      await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n);504      balanceAcalaTokenInit = await helper.balance.getSubstrate(randomAccount.address);505      balanceUniqueForeignTokenInit = await helper.tokens.accounts(randomAccount.address, {ForeignAsset: 0});506    });507508    await usingPlaygrounds(async (helper) => {509      await helper.balance.transferToSubstrate(alice, randomAccount.address, 10n * TRANSFER_AMOUNT);510      balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccount.address);511    });512  });513514  itSub('Should connect and send UNQ to Acala', async ({helper}) => {515516    const destination = {517      V2: {518        parents: 1,519        interior: {520          X1: {521            Parachain: ACALA_CHAIN,522          },523        },524      },525    };526527    const beneficiary = {528      V2: {529        parents: 0,530        interior: {531          X1: {532            AccountId32: {533              network: 'Any',534              id: randomAccount.addressRaw,535            },536          },537        },538      },539    };540541    const assets = {542      V2: [543        {544          id: {545            Concrete: {546              parents: 0,547              interior: 'Here',548            },549          },550          fun: {551            Fungible: TRANSFER_AMOUNT,552          },553        },554      ],555    };556557    const feeAssetItem = 0;558559    await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited');560    balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);561562    const unqFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;563    console.log('[Unique -> Acala] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));564    expect(unqFees > 0n, 'Negative fees UNQ, looks like nothing was transferred').to.be.true;565566    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {567      await helper.wait.newBlocks(3);568569      balanceUniqueForeignTokenMiddle = await helper.tokens.accounts(randomAccount.address, {ForeignAsset: 0});570      balanceAcalaTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);571572      const acaFees = balanceAcalaTokenInit - balanceAcalaTokenMiddle;573      const unqIncomeTransfer = balanceUniqueForeignTokenMiddle - balanceUniqueForeignTokenInit;574      const acaUnqFees = TRANSFER_AMOUNT - unqIncomeTransfer;575576      console.log(577        '[Unique -> Acala] transaction fees on Acala: %s ACA',578        helper.util.bigIntToDecimals(acaFees, ACALA_DECIMALS),579      );580      console.log(581        '[Unique -> Acala] transaction fees on Acala: %s UNQ',582        helper.util.bigIntToDecimals(acaUnqFees),583      );584      console.log('[Unique -> Acala] income %s UNQ', helper.util.bigIntToDecimals(unqIncomeTransfer));585      expect(acaFees == 0n).to.be.true;586      expect(587        acaUnqFees == expectedAcalaIncomeFee,588        'Acala took different income fee, check the Acala foreign asset config',589      ).to.be.true;590    });591  });592593  itSub('Should connect to Acala and send UNQ back', async ({helper}) => {594    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {595      const destination = {596        V1: {597          parents: 1,598          interior: {599            X2: [600              {Parachain: UNIQUE_CHAIN},601              {602                AccountId32: {603                  network: 'Any',604                  id: randomAccount.addressRaw,605                },606              },607            ],608          },609        },610      };611612      const id = {613        ForeignAsset: 0,614      };615616      await helper.xTokens.transfer(randomAccount, id, ACALA_BACKWARD_TRANSFER_AMOUNT, destination, 'Unlimited');617      balanceAcalaTokenFinal = await helper.balance.getSubstrate(randomAccount.address);618      balanceUniqueForeignTokenFinal = await helper.tokens.accounts(randomAccount.address, id);619620      const acaFees = balanceAcalaTokenMiddle - balanceAcalaTokenFinal;621      const unqOutcomeTransfer = balanceUniqueForeignTokenMiddle - balanceUniqueForeignTokenFinal;622623      console.log(624        '[Acala -> Unique] transaction fees on Acala: %s ACA',625        helper.util.bigIntToDecimals(acaFees, ACALA_DECIMALS),626      );627      console.log('[Acala -> Unique] outcome %s UNQ', helper.util.bigIntToDecimals(unqOutcomeTransfer));628629      expect(acaFees > 0, 'Negative fees ACA, looks like nothing was transferred').to.be.true;630      expect(unqOutcomeTransfer == ACALA_BACKWARD_TRANSFER_AMOUNT).to.be.true;631    });632633    await helper.wait.newBlocks(3);634635    balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccount.address);636    const actuallyDelivered = balanceUniqueTokenFinal - balanceUniqueTokenMiddle;637    expect(actuallyDelivered > 0).to.be.true;638639    console.log('[Acala -> Unique] actually delivered %s UNQ', helper.util.bigIntToDecimals(actuallyDelivered));640641    const unqFees = ACALA_BACKWARD_TRANSFER_AMOUNT - actuallyDelivered;642    console.log('[Acala -> Unique] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));643    expect(unqFees == 0n).to.be.true;644  });645646  itSub('Acala can send only up to its balance', async ({helper}) => {647    // set Acala's sovereign account's balance648    const acalaBalance = 10000n * (10n ** UNQ_DECIMALS);649    const acalaSovereignAccount = helper.address.paraSiblingSovereignAccount(ACALA_CHAIN);650    await helper.getSudo().balance.setBalanceSubstrate(alice, acalaSovereignAccount, acalaBalance);651652    const moreThanAcalaHas = acalaBalance * 2n;653654    let targetAccountBalance = 0n;655    const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);656657    const uniqueMultilocation = {658      V1: {659        parents: 1,660        interior: {661          X1: {Parachain: UNIQUE_CHAIN},662        },663      },664    };665666    const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(667      targetAccount.addressRaw,668      {669        Concrete: {670          parents: 0,671          interior: 'Here',672        },673      },674      moreThanAcalaHas,675    );676677    let maliciousXcmProgramSent: any;678    const maxWaitBlocks = 3;679680    // Try to trick Unique681    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {682      await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgram);683684      maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);685    });686687    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => {688      return event.messageHash() == maliciousXcmProgramSent.messageHash()689        && event.outcome().isFailedToTransactAsset;690    });691692    targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);693    expect(targetAccountBalance).to.be.equal(0n);694695    // But Acala still can send the correct amount696    const validTransferAmount = acalaBalance / 2n;697    const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(698      targetAccount.addressRaw,699      {700        Concrete: {701          parents: 0,702          interior: 'Here',703        },704      },705      validTransferAmount,706    );707708    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {709      await helper.getSudo().xcm.send(alice, uniqueMultilocation, validXcmProgram);710    });711712    await helper.wait.newBlocks(maxWaitBlocks);713714    targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);715    expect(targetAccountBalance).to.be.equal(validTransferAmount);716  });717718  itSub('Should not accept reserve transfer of UNQ from Acala', async ({helper}) => {719    const testAmount = 10_000n * (10n ** UNQ_DECIMALS);720    const [targetAccount] = await helper.arrange.createAccounts([0n], alice);721722    const uniqueMultilocation = {723      V1: {724        parents: 1,725        interior: {726          X1: {727            Parachain: UNIQUE_CHAIN,728          },729        },730      },731    };732733    const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(734      targetAccount.addressRaw,735      {736        Concrete: {737          parents: 1,738          interior: {739            X1: {740              Parachain: UNIQUE_CHAIN,741            },742          },743        },744      },745      testAmount,746    );747748    const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(749      targetAccount.addressRaw,750      {751        Concrete: {752          parents: 0,753          interior: 'Here',754        },755      },756      testAmount,757    );758759    let maliciousXcmProgramFullIdSent: any;760    let maliciousXcmProgramHereIdSent: any;761    const maxWaitBlocks = 3;762763    // Try to trick Unique using full UNQ identification764    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {765      await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramFullId);766767      maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);768    });769770    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => {771      return event.messageHash() == maliciousXcmProgramFullIdSent.messageHash()772        && event.outcome().isUntrustedReserveLocation;773    });774775    let accountBalance = await helper.balance.getSubstrate(targetAccount.address);776    expect(accountBalance).to.be.equal(0n);777778    // Try to trick Unique using shortened UNQ identification779    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {780      await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramHereId);781782      maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);783    });784785    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => {786      return event.messageHash() == maliciousXcmProgramHereIdSent.messageHash()787        && event.outcome().isUntrustedReserveLocation;788    });789790    accountBalance = await helper.balance.getSubstrate(targetAccount.address);791    expect(accountBalance).to.be.equal(0n);792  });793});794795// These tests are relevant only when796// the the corresponding foreign assets are not registered797describeXCM('[XCM] Integration test: Unique rejects non-native tokens', () => {798  let alice: IKeyringPair;799  let alith: IKeyringPair;800801  const testAmount = 100_000_000_000n;802  let uniqueParachainJunction;803  let uniqueAccountJunction;804805  let uniqueParachainMultilocation: any;806  let uniqueAccountMultilocation: any;807  let uniqueCombinedMultilocation: any;808809  let messageSent: any;810811  const maxWaitBlocks = 3;812813  before(async () => {814    await usingPlaygrounds(async (helper, privateKey) => {815      alice = await privateKey('//Alice');816817      uniqueParachainJunction = {Parachain: UNIQUE_CHAIN};818      uniqueAccountJunction = {819        AccountId32: {820          network: 'Any',821          id: alice.addressRaw,822        },823      };824825      uniqueParachainMultilocation = {826        V1: {827          parents: 1,828          interior: {829            X1: uniqueParachainJunction,830          },831        },832      };833834      uniqueAccountMultilocation = {835        V1: {836          parents: 0,837          interior: {838            X1: uniqueAccountJunction,839          },840        },841      };842843      uniqueCombinedMultilocation = {844        V1: {845          parents: 1,846          interior: {847            X2: [uniqueParachainJunction, uniqueAccountJunction],848          },849        },850      };851852      // Set the default version to wrap the first message to other chains.853      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);854    });855856    // eslint-disable-next-line require-await857    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {858      alith = helper.account.alithAccount();859    });860  });861862  const expectFailedToTransact = async (helper: DevUniqueHelper, messageSent: any) => {863    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => {864      return event.messageHash() == messageSent.messageHash()865        && event.outcome().isFailedToTransactAsset;866    });867  };868869  itSub('Unique rejects ACA tokens from Acala', async ({helper}) => {870    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {871      const id = {872        Token: 'ACA',873      };874      const destination = uniqueCombinedMultilocation;875      await helper.xTokens.transfer(alice, id, testAmount, destination, 'Unlimited');876877      messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);878    });879880    await expectFailedToTransact(helper, messageSent);881  });882883  itSub('Unique rejects GLMR tokens from Moonbeam', async ({helper}) => {884    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {885      const id = 'SelfReserve';886      const destination = uniqueCombinedMultilocation;887      await helper.xTokens.transfer(alith, id, testAmount, destination, 'Unlimited');888889      messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);890    });891892    await expectFailedToTransact(helper, messageSent);893  });894895  itSub('Unique rejects ASTR tokens from Astar', async ({helper}) => {896    await usingAstarPlaygrounds(astarUrl, async (helper) => {897      const destinationParachain = uniqueParachainMultilocation;898      const beneficiary = uniqueAccountMultilocation;899      const assets = {900        V1: [{901          id: {902            Concrete: {903              parents: 0,904              interior: 'Here',905            },906          },907          fun: {908            Fungible: testAmount,909          },910        }],911      };912      const feeAssetItem = 0;913914      await helper.executeExtrinsic(alice, 'api.tx.polkadotXcm.reserveWithdrawAssets', [915        destinationParachain,916        beneficiary,917        assets,918        feeAssetItem,919      ]);920921      messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);922    });923924    await expectFailedToTransact(helper, messageSent);925  });926});927928describeXCM('[XCM] Integration test: Exchanging UNQ with Moonbeam', () => {929  // Unique constants930  let alice: IKeyringPair;931  let uniqueAssetLocation;932933  let randomAccountUnique: IKeyringPair;934  let randomAccountMoonbeam: IKeyringPair;935936  // Moonbeam constants937  let assetId: string;938939  const uniqueAssetMetadata = {940    name: 'xcUnique',941    symbol: 'xcUNQ',942    decimals: 18,943    isFrozen: false,944    minimalBalance: 1n,945  };946947  let balanceUniqueTokenInit: bigint;948  let balanceUniqueTokenMiddle: bigint;949  let balanceUniqueTokenFinal: bigint;950  let balanceForeignUnqTokenInit: bigint;951  let balanceForeignUnqTokenMiddle: bigint;952  let balanceForeignUnqTokenFinal: bigint;953  let balanceGlmrTokenInit: bigint;954  let balanceGlmrTokenMiddle: bigint;955  let balanceGlmrTokenFinal: bigint;956957  before(async () => {958    await usingPlaygrounds(async (helper, privateKey) => {959      alice = await privateKey('//Alice');960      [randomAccountUnique] = await helper.arrange.createAccounts([0n], alice);961962      balanceForeignUnqTokenInit = 0n;963964      // Set the default version to wrap the first message to other chains.965      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);966    });967968    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {969      const alithAccount = helper.account.alithAccount();970      const baltatharAccount = helper.account.baltatharAccount();971      const dorothyAccount = helper.account.dorothyAccount();972973      randomAccountMoonbeam = helper.account.create();974975      // >>> Sponsoring Dorothy >>>976      console.log('Sponsoring Dorothy.......');977      await helper.balance.transferToEthereum(alithAccount, dorothyAccount.address, 11_000_000_000_000_000_000n);978      console.log('Sponsoring Dorothy.......DONE');979      // <<< Sponsoring Dorothy <<<980981      uniqueAssetLocation = {982        XCM: {983          parents: 1,984          interior: {X1: {Parachain: UNIQUE_CHAIN}},985        },986      };987      const existentialDeposit = 1n;988      const isSufficient = true;989      const unitsPerSecond = 1n;990      const numAssetsWeightHint = 0;991992      const encodedProposal = helper.assetManager.makeRegisterForeignAssetProposal({993        location: uniqueAssetLocation,994        metadata: uniqueAssetMetadata,995        existentialDeposit,996        isSufficient,997        unitsPerSecond,998        numAssetsWeightHint,999      });10001001      console.log('Encoded proposal for registerForeignAsset & setAssetUnitsPerSecond is %s', encodedProposal);10021003      await helper.fastDemocracy.executeProposal('register UNQ foreign asset', encodedProposal);10041005      // >>> Acquire Unique AssetId Info on Moonbeam >>>1006      console.log('Acquire Unique AssetId Info on Moonbeam.......');10071008      assetId = (await helper.assetManager.assetTypeId(uniqueAssetLocation)).toString();10091010      console.log('UNQ asset ID is %s', assetId);1011      console.log('Acquire Unique AssetId Info on Moonbeam.......DONE');1012      // >>> Acquire Unique AssetId Info on Moonbeam >>>10131014      // >>> Sponsoring random Account >>>1015      console.log('Sponsoring random Account.......');1016      await helper.balance.transferToEthereum(baltatharAccount, randomAccountMoonbeam.address, 11_000_000_000_000_000_000n);1017      console.log('Sponsoring random Account.......DONE');1018      // <<< Sponsoring random Account <<<10191020      balanceGlmrTokenInit = await helper.balance.getEthereum(randomAccountMoonbeam.address);1021    });10221023    await usingPlaygrounds(async (helper) => {1024      await helper.balance.transferToSubstrate(alice, randomAccountUnique.address, 10n * TRANSFER_AMOUNT);1025      balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccountUnique.address);1026    });1027  });10281029  itSub('Should connect and send UNQ to Moonbeam', async ({helper}) => {1030    const currencyId = {1031      NativeAssetId: 'Here',1032    };1033    const dest = {1034      V2: {1035        parents: 1,1036        interior: {1037          X2: [1038            {Parachain: MOONBEAM_CHAIN},1039            {AccountKey20: {network: 'Any', key: randomAccountMoonbeam.address}},1040          ],1041        },1042      },1043    };1044    const amount = TRANSFER_AMOUNT;10451046    await helper.xTokens.transfer(randomAccountUnique, currencyId, amount, dest, 'Unlimited');10471048    balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccountUnique.address);1049    expect(balanceUniqueTokenMiddle < balanceUniqueTokenInit).to.be.true;10501051    const transactionFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;1052    console.log('[Unique -> Moonbeam] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(transactionFees));1053    expect(transactionFees > 0, 'Negative fees UNQ, looks like nothing was transferred').to.be.true;10541055    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1056      await helper.wait.newBlocks(3);10571058      balanceGlmrTokenMiddle = await helper.balance.getEthereum(randomAccountMoonbeam.address);10591060      const glmrFees = balanceGlmrTokenInit - balanceGlmrTokenMiddle;1061      console.log('[Unique -> Moonbeam] transaction fees on Moonbeam: %s GLMR', helper.util.bigIntToDecimals(glmrFees));1062      expect(glmrFees == 0n).to.be.true;10631064      balanceForeignUnqTokenMiddle = (await helper.assets.account(assetId, randomAccountMoonbeam.address))!;10651066      const unqIncomeTransfer = balanceForeignUnqTokenMiddle - balanceForeignUnqTokenInit;1067      console.log('[Unique -> Moonbeam] income %s UNQ', helper.util.bigIntToDecimals(unqIncomeTransfer));1068      expect(unqIncomeTransfer == TRANSFER_AMOUNT).to.be.true;1069    });1070  });10711072  itSub('Should connect to Moonbeam and send UNQ back', async ({helper}) => {1073    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1074      const asset = {1075        V1: {1076          id: {1077            Concrete: {1078              parents: 1,1079              interior: {1080                X1: {Parachain: UNIQUE_CHAIN},1081              },1082            },1083          },1084          fun: {1085            Fungible: TRANSFER_AMOUNT,1086          },1087        },1088      };1089      const destination = {1090        V1: {1091          parents: 1,1092          interior: {1093            X2: [1094              {Parachain: UNIQUE_CHAIN},1095              {AccountId32: {network: 'Any', id: randomAccountUnique.addressRaw}},1096            ],1097          },1098        },1099      };11001101      await helper.xTokens.transferMultiasset(randomAccountMoonbeam, asset, destination, 'Unlimited');11021103      balanceGlmrTokenFinal = await helper.balance.getEthereum(randomAccountMoonbeam.address);11041105      const glmrFees = balanceGlmrTokenMiddle - balanceGlmrTokenFinal;1106      console.log('[Moonbeam -> Unique] transaction fees on Moonbeam: %s GLMR', helper.util.bigIntToDecimals(glmrFees));1107      expect(glmrFees > 0, 'Negative fees GLMR, looks like nothing was transferred').to.be.true;11081109      const unqRandomAccountAsset = await helper.assets.account(assetId, randomAccountMoonbeam.address);11101111      expect(unqRandomAccountAsset).to.be.null;11121113      balanceForeignUnqTokenFinal = 0n;11141115      const unqOutcomeTransfer = balanceForeignUnqTokenMiddle - balanceForeignUnqTokenFinal;1116      console.log('[Unique -> Moonbeam] outcome %s UNQ', helper.util.bigIntToDecimals(unqOutcomeTransfer));1117      expect(unqOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;1118    });11191120    await helper.wait.newBlocks(3);11211122    balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccountUnique.address);1123    const actuallyDelivered = balanceUniqueTokenFinal - balanceUniqueTokenMiddle;1124    expect(actuallyDelivered > 0).to.be.true;11251126    console.log('[Moonbeam -> Unique] actually delivered %s UNQ', helper.util.bigIntToDecimals(actuallyDelivered));11271128    const unqFees = TRANSFER_AMOUNT - actuallyDelivered;1129    console.log('[Moonbeam -> Unique] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));1130    expect(unqFees == 0n).to.be.true;1131  });11321133  itSub('Moonbeam can send only up to its balance', async ({helper}) => {1134    // set Moonbeam's sovereign account's balance1135    const moonbeamBalance = 10000n * (10n ** UNQ_DECIMALS);1136    const moonbeamSovereignAccount = helper.address.paraSiblingSovereignAccount(MOONBEAM_CHAIN);1137    await helper.getSudo().balance.setBalanceSubstrate(alice, moonbeamSovereignAccount, moonbeamBalance);11381139    const moreThanMoonbeamHas = moonbeamBalance * 2n;11401141    let targetAccountBalance = 0n;1142    const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);11431144    const uniqueMultilocation = {1145      V1: {1146        parents: 1,1147        interior: {1148          X1: {Parachain: UNIQUE_CHAIN},1149        },1150      },1151    };11521153    const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(1154      targetAccount.addressRaw,1155      {1156        Concrete: {1157          parents: 0,1158          interior: 'Here',1159        },1160      },1161      moreThanMoonbeamHas,1162    );11631164    let maliciousXcmProgramSent: any;1165    const maxWaitBlocks = 3;11661167    // Try to trick Unique1168    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1169      const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueMultilocation, maliciousXcmProgram]);11701171      // Needed to bypass the call filter.1172      const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);1173      await helper.fastDemocracy.executeProposal('try to spend more UNQ than Moonbeam has', batchCall);11741175      maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1176    });11771178    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => {1179      return event.messageHash() == maliciousXcmProgramSent.messageHash()1180        && event.outcome().isFailedToTransactAsset;1181    });11821183    targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1184    expect(targetAccountBalance).to.be.equal(0n);11851186    // But Moonbeam still can send the correct amount1187    const validTransferAmount = moonbeamBalance / 2n;1188    const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(1189      targetAccount.addressRaw,1190      {1191        Concrete: {1192          parents: 0,1193          interior: 'Here',1194        },1195      },1196      validTransferAmount,1197    );11981199    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1200      const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueMultilocation, validXcmProgram]);12011202      // Needed to bypass the call filter.1203      const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);1204      await helper.fastDemocracy.executeProposal('Spend the correct amount of UNQ', batchCall);1205    });12061207    await helper.wait.newBlocks(maxWaitBlocks);12081209    targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1210    expect(targetAccountBalance).to.be.equal(validTransferAmount);1211  });12121213  itSub('Should not accept reserve transfer of UNQ from Moonbeam', async ({helper}) => {1214    const testAmount = 10_000n * (10n ** UNQ_DECIMALS);1215    const [targetAccount] = await helper.arrange.createAccounts([0n], alice);12161217    const uniqueMultilocation = {1218      V1: {1219        parents: 1,1220        interior: {1221          X1: {1222            Parachain: UNIQUE_CHAIN,1223          },1224        },1225      },1226    };12271228    const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(1229      targetAccount.addressRaw,1230      {1231        Concrete: {1232          parents: 0,1233          interior: {1234            X1: {1235              Parachain: UNIQUE_CHAIN,1236            },1237          },1238        },1239      },1240      testAmount,1241    );12421243    const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(1244      targetAccount.addressRaw,1245      {1246        Concrete: {1247          parents: 0,1248          interior: 'Here',1249        },1250      },1251      testAmount,1252    );12531254    let maliciousXcmProgramFullIdSent: any;1255    let maliciousXcmProgramHereIdSent: any;1256    const maxWaitBlocks = 3;12571258    // Try to trick Unique using full UNQ identification1259    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1260      const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueMultilocation, maliciousXcmProgramFullId]);12611262      // Needed to bypass the call filter.1263      const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);1264      await helper.fastDemocracy.executeProposal('try to act like a reserve location for UNQ using path asset identification', batchCall);12651266      maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1267    });12681269    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => {1270      return event.messageHash() == maliciousXcmProgramFullIdSent.messageHash()1271        && event.outcome().isUntrustedReserveLocation;1272    });12731274    let accountBalance = await helper.balance.getSubstrate(targetAccount.address);1275    expect(accountBalance).to.be.equal(0n);12761277    // Try to trick Unique using shortened UNQ identification1278    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1279      const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueMultilocation, maliciousXcmProgramHereId]);12801281      // Needed to bypass the call filter.1282      const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);1283      await helper.fastDemocracy.executeProposal('try to act like a reserve location for UNQ using "here" asset identification', batchCall);12841285      maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1286    });12871288    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => {1289      return event.messageHash() == maliciousXcmProgramHereIdSent.messageHash()1290        && event.outcome().isUntrustedReserveLocation;1291    });12921293    accountBalance = await helper.balance.getSubstrate(targetAccount.address);1294    expect(accountBalance).to.be.equal(0n);1295  });1296});12971298describeXCM('[XCM] Integration test: Exchanging tokens with Astar', () => {1299  let alice: IKeyringPair;1300  let randomAccount: IKeyringPair;13011302  const UNQ_ASSET_ID_ON_ASTAR = 1;1303  const UNQ_MINIMAL_BALANCE_ON_ASTAR = 1n;13041305  // Unique -> Astar1306  const astarInitialBalance = 1n * (10n ** ASTAR_DECIMALS); // 1 ASTR, existential deposit required to actually create the account on Astar.1307  const unitsPerSecond = 228_000_000_000n; // This is Phala's value. What will be ours?1308  const unqToAstarTransferred = 10n * (10n ** UNQ_DECIMALS); // 10 UNQ1309  const unqToAstarArrived = 9_999_999_999_088_000_000n; // 9.999 ... UNQ, Astar takes a commision in foreign tokens13101311  // Astar -> Unique1312  const unqFromAstarTransfered = 5n * (10n ** UNQ_DECIMALS); // 5 UNQ1313  const unqOnAstarLeft = unqToAstarArrived - unqFromAstarTransfered; // 4.999_999_999_088_000_000n UNQ13141315  let balanceAfterUniqueToAstarXCM: bigint;13161317  before(async () => {1318    await usingPlaygrounds(async (helper, privateKey) => {1319      alice = await privateKey('//Alice');1320      [randomAccount] = await helper.arrange.createAccounts([100n], alice);1321      console.log('randomAccount', randomAccount.address);13221323      // Set the default version to wrap the first message to other chains.1324      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);1325    });13261327    await usingAstarPlaygrounds(astarUrl, async (helper) => {1328      console.log('1. Create foreign asset and metadata');1329      // TODO update metadata with values from production1330      await helper.assets.create(1331        alice,1332        UNQ_ASSET_ID_ON_ASTAR,1333        alice.address,1334        UNQ_MINIMAL_BALANCE_ON_ASTAR,1335      );13361337      await helper.assets.setMetadata(1338        alice,1339        UNQ_ASSET_ID_ON_ASTAR,1340        'Cross chain UNQ',1341        'xcUNQ',1342        Number(UNQ_DECIMALS),1343      );13441345      console.log('2. Register asset location on Astar');1346      const assetLocation = {1347        V1: {1348          parents: 1,1349          interior: {1350            X1: {1351              Parachain: UNIQUE_CHAIN,1352            },1353          },1354        },1355      };13561357      await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.registerAssetLocation', [assetLocation, UNQ_ASSET_ID_ON_ASTAR]);13581359      console.log('3. Set UNQ payment for XCM execution on Astar');1360      await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.setAssetUnitsPerSecond', [assetLocation, unitsPerSecond]);13611362      console.log('4. Transfer 1 ASTR to recipient to create the account (needed due to existential balance)');1363      await helper.balance.transferToSubstrate(alice, randomAccount.address, astarInitialBalance);1364    });1365  });13661367  itSub('Should connect and send UNQ to Astar', async ({helper}) => {1368    const destination = {1369      V2: {1370        parents: 1,1371        interior: {1372          X1: {1373            Parachain: ASTAR_CHAIN,1374          },1375        },1376      },1377    };13781379    const beneficiary = {1380      V2: {1381        parents: 0,1382        interior: {1383          X1: {1384            AccountId32: {1385              network: 'Any',1386              id: randomAccount.addressRaw,1387            },1388          },1389        },1390      },1391    };13921393    const assets = {1394      V2: [1395        {1396          id: {1397            Concrete: {1398              parents: 0,1399              interior: 'Here',1400            },1401          },1402          fun: {1403            Fungible: unqToAstarTransferred,1404          },1405        },1406      ],1407    };14081409    // Initial balance is 100 UNQ1410    const balanceBefore = await helper.balance.getSubstrate(randomAccount.address);1411    console.log(`Initial balance is: ${balanceBefore}`);14121413    const feeAssetItem = 0;1414    await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited');14151416    // Balance after reserve transfer is less than 901417    balanceAfterUniqueToAstarXCM = await helper.balance.getSubstrate(randomAccount.address);1418    console.log(`UNQ Balance on Unique after XCM is: ${balanceAfterUniqueToAstarXCM}`);1419    console.log(`Unique's UNQ commission is: ${balanceBefore - balanceAfterUniqueToAstarXCM}`);1420    expect(balanceBefore - balanceAfterUniqueToAstarXCM > 0).to.be.true;14211422    await usingAstarPlaygrounds(astarUrl, async (helper) => {1423      await helper.wait.newBlocks(3);1424      const xcUNQbalance = await helper.assets.account(UNQ_ASSET_ID_ON_ASTAR, randomAccount.address);1425      const astarBalance = await helper.balance.getSubstrate(randomAccount.address);14261427      console.log(`xcUNQ balance on Astar after XCM is: ${xcUNQbalance}`);1428      console.log(`Astar's UNQ commission is: ${unqToAstarTransferred - xcUNQbalance!}`);14291430      expect(xcUNQbalance).to.eq(unqToAstarArrived);1431      // Astar balance does not changed1432      expect(astarBalance).to.eq(astarInitialBalance);1433    });1434  });14351436  itSub('Should connect to Astar and send UNQ back', async ({helper}) => {1437    await usingAstarPlaygrounds(astarUrl, async (helper) => {1438      const destination = {1439        V1: {1440          parents: 1,1441          interior: {1442            X1: {1443              Parachain: UNIQUE_CHAIN,1444            },1445          },1446        },1447      };14481449      const beneficiary = {1450        V1: {1451          parents: 0,1452          interior: {1453            X1: {1454              AccountId32: {1455                network: 'Any',1456                id: randomAccount.addressRaw,1457              },1458            },1459          },1460        },1461      };14621463      const assets = {1464        V1: [1465          {1466            id: {1467              Concrete: {1468                parents: 1,1469                interior: {1470                  X1: {1471                    Parachain: UNIQUE_CHAIN,1472                  },1473                },1474              },1475            },1476            fun: {1477              Fungible: unqFromAstarTransfered,1478            },1479          },1480        ],1481      };14821483      // Initial balance is 1 ASTR1484      const balanceASTRbefore = await helper.balance.getSubstrate(randomAccount.address);1485      console.log(`ASTR balance is: ${balanceASTRbefore}, it does not changed`);1486      expect(balanceASTRbefore).to.eq(astarInitialBalance);14871488      const feeAssetItem = 0;1489      // this is non-standard polkadotXcm extension for Astar only. It calls InitiateReserveWithdraw1490      await helper.executeExtrinsic(randomAccount, 'api.tx.polkadotXcm.reserveWithdrawAssets', [destination, beneficiary, assets, feeAssetItem]);14911492      const xcUNQbalance = await helper.assets.account(UNQ_ASSET_ID_ON_ASTAR, randomAccount.address);1493      const balanceAstar = await helper.balance.getSubstrate(randomAccount.address);1494      console.log(`xcUNQ balance on Astar after XCM is: ${xcUNQbalance}`);14951496      // Assert: xcUNQ balance correctly decreased1497      expect(xcUNQbalance).to.eq(unqOnAstarLeft);1498      // Assert: ASTR balance is 0.996...1499      expect(balanceAstar / (10n ** (ASTAR_DECIMALS - 3n))).to.eq(996n);1500    });15011502    await helper.wait.newBlocks(3);1503    const balanceUNQ = await helper.balance.getSubstrate(randomAccount.address);1504    console.log(`UNQ Balance on Unique after XCM is: ${balanceUNQ}`);1505    expect(balanceUNQ).to.eq(balanceAfterUniqueToAstarXCM + unqFromAstarTransfered);1506  });15071508  itSub('Astar can send only up to its balance', async ({helper}) => {1509    // set Astar's sovereign account's balance1510    const astarBalance = 10000n * (10n ** UNQ_DECIMALS);1511    const astarSovereignAccount = helper.address.paraSiblingSovereignAccount(ASTAR_CHAIN);1512    await helper.getSudo().balance.setBalanceSubstrate(alice, astarSovereignAccount, astarBalance);15131514    const moreThanAstarHas = astarBalance * 2n;15151516    let targetAccountBalance = 0n;1517    const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);15181519    const uniqueMultilocation = {1520      V1: {1521        parents: 1,1522        interior: {1523          X1: {Parachain: UNIQUE_CHAIN},1524        },1525      },1526    };15271528    const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(1529      targetAccount.addressRaw,1530      {1531        Concrete: {1532          parents: 0,1533          interior: 'Here',1534        },1535      },1536      moreThanAstarHas,1537    );15381539    let maliciousXcmProgramSent: any;1540    const maxWaitBlocks = 3;15411542    // Try to trick Unique1543    await usingAstarPlaygrounds(astarUrl, async (helper) => {1544      await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgram);15451546      maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1547    });15481549    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => {1550      return event.messageHash() == maliciousXcmProgramSent.messageHash()1551        && event.outcome().isFailedToTransactAsset;1552    });15531554    targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1555    expect(targetAccountBalance).to.be.equal(0n);15561557    // But Astar still can send the correct amount1558    const validTransferAmount = astarBalance / 2n;1559    const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(1560      targetAccount.addressRaw,1561      {1562        Concrete: {1563          parents: 0,1564          interior: 'Here',1565        },1566      },1567      validTransferAmount,1568    );15691570    await usingAstarPlaygrounds(astarUrl, async (helper) => {1571      await helper.getSudo().xcm.send(alice, uniqueMultilocation, validXcmProgram);1572    });15731574    await helper.wait.newBlocks(maxWaitBlocks);15751576    targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1577    expect(targetAccountBalance).to.be.equal(validTransferAmount);1578  });15791580  itSub('Should not accept reserve transfer of UNQ from Astar', async ({helper}) => {1581    const testAmount = 10_000n * (10n ** UNQ_DECIMALS);1582    const [targetAccount] = await helper.arrange.createAccounts([0n], alice);15831584    const uniqueMultilocation = {1585      V1: {1586        parents: 1,1587        interior: {1588          X1: {1589            Parachain: UNIQUE_CHAIN,1590          },1591        },1592      },1593    };15941595    const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(1596      targetAccount.addressRaw,1597      {1598        Concrete: {1599          parents: 1,1600          interior: {1601            X1: {1602              Parachain: UNIQUE_CHAIN,1603            },1604          },1605        },1606      },1607      testAmount,1608    );16091610    const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(1611      targetAccount.addressRaw,1612      {1613        Concrete: {1614          parents: 0,1615          interior: 'Here',1616        },1617      },1618      testAmount,1619    );16201621    let maliciousXcmProgramFullIdSent: any;1622    let maliciousXcmProgramHereIdSent: any;1623    const maxWaitBlocks = 3;16241625    // Try to trick Unique using full UNQ identification1626    await usingAstarPlaygrounds(astarUrl, async (helper) => {1627      await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramFullId);16281629      maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1630    });16311632    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => {1633      return event.messageHash() == maliciousXcmProgramFullIdSent.messageHash()1634        && event.outcome().isUntrustedReserveLocation;1635    });16361637    let accountBalance = await helper.balance.getSubstrate(targetAccount.address);1638    expect(accountBalance).to.be.equal(0n);16391640    // Try to trick Unique using shortened UNQ identification1641    await usingAstarPlaygrounds(astarUrl, async (helper) => {1642      await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramHereId);16431644      maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1645    });16461647    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => {1648      return event.messageHash() == maliciousXcmProgramHereIdSent.messageHash()1649        && event.outcome().isUntrustedReserveLocation;1650    });16511652    accountBalance = await helper.balance.getSubstrate(targetAccount.address);1653    expect(accountBalance).to.be.equal(0n);1654  });16551656});
after · tests/src/xcm/xcmUnique.test.ts
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617import {IKeyringPair} from '@polkadot/types/types';18import config from '../config';19import {itSub, expect, describeXCM, usingPlaygrounds, usingAcalaPlaygrounds, usingRelayPlaygrounds, usingMoonbeamPlaygrounds, usingStatemintPlaygrounds, usingAstarPlaygrounds} from '../util';20import {DevUniqueHelper, Event} from '../util/playgrounds/unique.dev';2122const UNIQUE_CHAIN = 2037;23const STATEMINT_CHAIN = 1000;24const ACALA_CHAIN = 2000;25const MOONBEAM_CHAIN = 2004;26const ASTAR_CHAIN = 2006;2728const STATEMINT_PALLET_INSTANCE = 50;2930const relayUrl = config.relayUrl;31const statemintUrl = config.statemintUrl;32const acalaUrl = config.acalaUrl;33const moonbeamUrl = config.moonbeamUrl;34const astarUrl = config.astarUrl;3536const RELAY_DECIMALS = 12;37const STATEMINT_DECIMALS = 12;38const ACALA_DECIMALS = 12;39const ASTAR_DECIMALS = 18n;40const UNQ_DECIMALS = 18n;4142const TRANSFER_AMOUNT = 2000000000000000000000000n;4344const FUNDING_AMOUNT = 3_500_000_0000_000_000n;4546const TRANSFER_AMOUNT_RELAY = 50_000_000_000_000_000n;4748const USDT_ASSET_ID = 100;49const USDT_ASSET_METADATA_DECIMALS = 18;50const USDT_ASSET_METADATA_NAME = 'USDT';51const USDT_ASSET_METADATA_DESCRIPTION = 'USDT';52const USDT_ASSET_METADATA_MINIMAL_BALANCE = 1n;53const USDT_ASSET_AMOUNT = 10_000_000_000_000_000_000_000_000n;5455const SAFE_XCM_VERSION = 2;5657describeXCM('[XCM] Integration test: Exchanging USDT with Statemint', () => {58  let alice: IKeyringPair;59  let bob: IKeyringPair;6061  let balanceStmnBefore: bigint;62  let balanceStmnAfter: bigint;6364  let balanceUniqueBefore: bigint;65  let balanceUniqueAfter: bigint;66  let balanceUniqueFinal: bigint;6768  let balanceBobBefore: bigint;69  let balanceBobAfter: bigint;70  let balanceBobFinal: bigint;7172  let balanceBobRelayTokenBefore: bigint;73  let balanceBobRelayTokenAfter: bigint;747576  before(async () => {77    await usingPlaygrounds(async (helper, privateKey) => {78      alice = await privateKey('//Alice');79      bob = await privateKey('//Bob'); // sovereign account on Statemint funds donor8081      // Set the default version to wrap the first message to other chains.82      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);83    });8485    await usingRelayPlaygrounds(relayUrl, async (helper) => {86      // Fund accounts on Statemint87      const relayXcmVersion = 2;88      await helper.xcm.teleportNativeAsset(alice, STATEMINT_CHAIN, alice.addressRaw, FUNDING_AMOUNT, relayXcmVersion);89      await helper.xcm.teleportNativeAsset(alice, STATEMINT_CHAIN, bob.addressRaw, FUNDING_AMOUNT, relayXcmVersion);90    });9192    await usingStatemintPlaygrounds(statemintUrl, async (helper) => {93      const sovereignFundingAmount = 3_500_000_000n;9495      await helper.assets.create(96        alice,97        USDT_ASSET_ID,98        alice.address,99        USDT_ASSET_METADATA_MINIMAL_BALANCE,100      );101      await helper.assets.setMetadata(102        alice,103        USDT_ASSET_ID,104        USDT_ASSET_METADATA_NAME,105        USDT_ASSET_METADATA_DESCRIPTION,106        USDT_ASSET_METADATA_DECIMALS,107      );108      await helper.assets.mint(109        alice,110        USDT_ASSET_ID,111        alice.address,112        USDT_ASSET_AMOUNT,113      );114115      // funding parachain sovereing account on Statemint.116      // The sovereign account should be created before any action117      // (the assets pallet on Statemint check if the sovereign account exists)118      const parachainSovereingAccount = helper.address.paraSiblingSovereignAccount(UNIQUE_CHAIN);119      await helper.balance.transferToSubstrate(bob, parachainSovereingAccount, sovereignFundingAmount);120    });121122123    await usingPlaygrounds(async (helper) => {124      const location = {125        V2: {126          parents: 1,127          interior: {X3: [128            {129              Parachain: STATEMINT_CHAIN,130            },131            {132              PalletInstance: STATEMINT_PALLET_INSTANCE,133            },134            {135              GeneralIndex: USDT_ASSET_ID,136            },137          ]},138        },139      };140141      const metadata =142      {143        name: USDT_ASSET_ID,144        symbol: USDT_ASSET_METADATA_NAME,145        decimals: USDT_ASSET_METADATA_DECIMALS,146        minimalBalance: USDT_ASSET_METADATA_MINIMAL_BALANCE,147      };148      await helper.getSudo().foreignAssets.register(alice, alice.address, location, metadata);149      balanceUniqueBefore = await helper.balance.getSubstrate(alice.address);150    });151152153    // Providing the relay currency to the unique sender account154    // (fee for USDT XCM are paid in relay tokens)155    await usingRelayPlaygrounds(relayUrl, async (helper) => {156      const destination = {157        V1: {158          parents: 0,159          interior: {X1: {160            Parachain: UNIQUE_CHAIN,161          },162          },163        }};164165      const beneficiary = {166        V1: {167          parents: 0,168          interior: {X1: {169            AccountId32: {170              network: 'Any',171              id: alice.addressRaw,172            },173          }},174        },175      };176177      const assets = {178        V1: [179          {180            id: {181              Concrete: {182                parents: 0,183                interior: 'Here',184              },185            },186            fun: {187              Fungible: TRANSFER_AMOUNT_RELAY,188            },189          },190        ],191      };192193      const feeAssetItem = 0;194195      await helper.xcm.limitedReserveTransferAssets(alice, destination, beneficiary, assets, feeAssetItem, 'Unlimited');196    });197198  });199200  itSub('Should connect and send USDT from Statemint to Unique', async ({helper}) => {201    await usingStatemintPlaygrounds(statemintUrl, async (helper) => {202      const dest = {203        V1: {204          parents: 1,205          interior: {X1: {206            Parachain: UNIQUE_CHAIN,207          },208          },209        }};210211      const beneficiary = {212        V1: {213          parents: 0,214          interior: {X1: {215            AccountId32: {216              network: 'Any',217              id: alice.addressRaw,218            },219          }},220        },221      };222223      const assets = {224        V1: [225          {226            id: {227              Concrete: {228                parents: 0,229                interior: {230                  X2: [231                    {232                      PalletInstance: STATEMINT_PALLET_INSTANCE,233                    },234                    {235                      GeneralIndex: USDT_ASSET_ID,236                    },237                  ]},238              },239            },240            fun: {241              Fungible: TRANSFER_AMOUNT,242            },243          },244        ],245      };246247      const feeAssetItem = 0;248249      balanceStmnBefore = await helper.balance.getSubstrate(alice.address);250      await helper.xcm.limitedReserveTransferAssets(alice, dest, beneficiary, assets, feeAssetItem, 'Unlimited');251252      balanceStmnAfter = await helper.balance.getSubstrate(alice.address);253254      // common good parachain take commission in it native token255      console.log(256        '[Statemint -> Unique] transaction fees on Statemint: %s WND',257        helper.util.bigIntToDecimals(balanceStmnBefore - balanceStmnAfter, STATEMINT_DECIMALS),258      );259      expect(balanceStmnBefore > balanceStmnAfter).to.be.true;260261    });262263264    // ensure that asset has been delivered265    await helper.wait.newBlocks(3);266267    // expext collection id will be with id 1268    const free = await helper.ft.getBalance(1, {Substrate: alice.address});269270    balanceUniqueAfter = await helper.balance.getSubstrate(alice.address);271272    console.log(273      '[Statemint -> Unique] transaction fees on Unique: %s USDT',274      helper.util.bigIntToDecimals(TRANSFER_AMOUNT - free, USDT_ASSET_METADATA_DECIMALS),275    );276    console.log(277      '[Statemint -> Unique] transaction fees on Unique: %s UNQ',278      helper.util.bigIntToDecimals(balanceUniqueAfter - balanceUniqueBefore),279    );280    // commission has not paid in USDT token281    expect(free).to.be.equal(TRANSFER_AMOUNT);282    // ... and parachain native token283    expect(balanceUniqueAfter == balanceUniqueBefore).to.be.true;284  });285286  itSub('Should connect and send USDT from Unique to Statemint back', async ({helper}) => {287    const destination = {288      V2: {289        parents: 1,290        interior: {X2: [291          {292            Parachain: STATEMINT_CHAIN,293          },294          {295            AccountId32: {296              network: 'Any',297              id: alice.addressRaw,298            },299          },300        ]},301      },302    };303304    const relayFee = 400_000_000_000_000n;305    const currencies: [any, bigint][] = [306      [307        {308          ForeignAssetId: 0,309        },310        TRANSFER_AMOUNT,311      ],312      [313        {314          NativeAssetId: 'Parent',315        },316        relayFee,317      ],318    ];319320    const feeItem = 1;321322    await helper.xTokens.transferMulticurrencies(alice, currencies, feeItem, destination, 'Unlimited');323324    // the commission has been paid in parachain native token325    balanceUniqueFinal = await helper.balance.getSubstrate(alice.address);326    console.log('[Unique -> Statemint] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(balanceUniqueAfter - balanceUniqueFinal));327    expect(balanceUniqueAfter > balanceUniqueFinal).to.be.true;328329    await usingStatemintPlaygrounds(statemintUrl, async (helper) => {330      await helper.wait.newBlocks(3);331332      // The USDT token never paid fees. Its amount not changed from begin value.333      // Also check that xcm transfer has been succeeded334      expect((await helper.assets.account(USDT_ASSET_ID, alice.address))! == USDT_ASSET_AMOUNT).to.be.true;335    });336  });337338  itSub('Should connect and send Relay token to Unique', async ({helper}) => {339    balanceBobBefore = await helper.balance.getSubstrate(bob.address);340    balanceBobRelayTokenBefore = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'});341342    await usingRelayPlaygrounds(relayUrl, async (helper) => {343      const destination = {344        V1: {345          parents: 0,346          interior: {X1: {347            Parachain: UNIQUE_CHAIN,348          },349          },350        }};351352      const beneficiary = {353        V1: {354          parents: 0,355          interior: {X1: {356            AccountId32: {357              network: 'Any',358              id: bob.addressRaw,359            },360          }},361        },362      };363364      const assets = {365        V1: [366          {367            id: {368              Concrete: {369                parents: 0,370                interior: 'Here',371              },372            },373            fun: {374              Fungible: TRANSFER_AMOUNT_RELAY,375            },376          },377        ],378      };379380      const feeAssetItem = 0;381382      await helper.xcm.limitedReserveTransferAssets(bob, destination, beneficiary, assets, feeAssetItem, 'Unlimited');383    });384385    await helper.wait.newBlocks(3);386387    balanceBobAfter = await helper.balance.getSubstrate(bob.address);388    balanceBobRelayTokenAfter = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'});389390    const wndFeeOnUnique = balanceBobRelayTokenAfter - TRANSFER_AMOUNT_RELAY - balanceBobRelayTokenBefore;391    const wndDiffOnUnique = balanceBobRelayTokenAfter - balanceBobRelayTokenBefore;392    console.log(393      '[Relay (Westend) -> Unique] transaction fees: %s UNQ',394      helper.util.bigIntToDecimals(balanceBobAfter - balanceBobBefore),395    );396    console.log(397      '[Relay (Westend) -> Unique] transaction fees: %s WND',398      helper.util.bigIntToDecimals(wndFeeOnUnique, STATEMINT_DECIMALS),399    );400    console.log('[Relay (Westend) -> Unique] actually delivered: %s WND', wndDiffOnUnique);401    expect(wndFeeOnUnique == 0n, 'No incoming WND fees should be taken').to.be.true;402    expect(balanceBobBefore == balanceBobAfter, 'No incoming UNQ fees should be taken').to.be.true;403  });404405  itSub('Should connect and send Relay token back', async ({helper}) => {406    let relayTokenBalanceBefore: bigint;407    let relayTokenBalanceAfter: bigint;408    await usingRelayPlaygrounds(relayUrl, async (helper) => {409      relayTokenBalanceBefore = await helper.balance.getSubstrate(bob.address);410    });411412    const destination = {413      V2: {414        parents: 1,415        interior: {416          X1:{417            AccountId32: {418              network: 'Any',419              id: bob.addressRaw,420            },421          },422        },423      },424    };425426    const currencies: any = [427      [428        {429          NativeAssetId: 'Parent',430        },431        TRANSFER_AMOUNT_RELAY,432      ],433    ];434435    const feeItem = 0;436437    await helper.xTokens.transferMulticurrencies(bob, currencies, feeItem, destination, 'Unlimited');438439    balanceBobFinal = await helper.balance.getSubstrate(bob.address);440    console.log('[Unique -> Relay (Westend)] transaction fees: %s UNQ',  helper.util.bigIntToDecimals(balanceBobAfter - balanceBobFinal));441442    await usingRelayPlaygrounds(relayUrl, async (helper) => {443      await helper.wait.newBlocks(10);444      relayTokenBalanceAfter = await helper.balance.getSubstrate(bob.address);445446      const diff = relayTokenBalanceAfter - relayTokenBalanceBefore;447      console.log('[Unique -> Relay (Westend)] actually delivered: %s WND', helper.util.bigIntToDecimals(diff, RELAY_DECIMALS));448      expect(diff > 0, 'Relay tokens was not delivered back').to.be.true;449    });450  });451});452453describeXCM('[XCM] Integration test: Exchanging tokens with Acala', () => {454  let alice: IKeyringPair;455  let randomAccount: IKeyringPair;456457  let balanceUniqueTokenInit: bigint;458  let balanceUniqueTokenMiddle: bigint;459  let balanceUniqueTokenFinal: bigint;460  let balanceAcalaTokenInit: bigint;461  let balanceAcalaTokenMiddle: bigint;462  let balanceAcalaTokenFinal: bigint;463  let balanceUniqueForeignTokenInit: bigint;464  let balanceUniqueForeignTokenMiddle: bigint;465  let balanceUniqueForeignTokenFinal: bigint;466467  // computed by a test transfer from prod Unique to prod Acala.468  // 2 UNQ sent https://unique.subscan.io/xcm_message/polkadot-bad0b68847e2398af25d482e9ee6f9c1f9ec2a48469  // 1.898970000000000000 UNQ received (you can check Acala's chain state in the corresponding block)470  const expectedAcalaIncomeFee = 2000000000000000000n - 1898970000000000000n;471472  const ACALA_BACKWARD_TRANSFER_AMOUNT = TRANSFER_AMOUNT - expectedAcalaIncomeFee;473474  before(async () => {475    await usingPlaygrounds(async (helper, privateKey) => {476      alice = await privateKey('//Alice');477      [randomAccount] = await helper.arrange.createAccounts([0n], alice);478479      // Set the default version to wrap the first message to other chains.480      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);481    });482483    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {484      const destination = {485        V1: {486          parents: 1,487          interior: {488            X1: {489              Parachain: UNIQUE_CHAIN,490            },491          },492        },493      };494495      const metadata = {496        name: 'Unique Network',497        symbol: 'UNQ',498        decimals: 18,499        minimalBalance: 1250000000000000000n,500      };501502      await helper.getSudo().assetRegistry.registerForeignAsset(alice, destination, metadata);503      await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n);504      balanceAcalaTokenInit = await helper.balance.getSubstrate(randomAccount.address);505      balanceUniqueForeignTokenInit = await helper.tokens.accounts(randomAccount.address, {ForeignAsset: 0});506    });507508    await usingPlaygrounds(async (helper) => {509      await helper.balance.transferToSubstrate(alice, randomAccount.address, 10n * TRANSFER_AMOUNT);510      balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccount.address);511    });512  });513514  itSub('Should connect and send UNQ to Acala', async ({helper}) => {515516    const destination = {517      V2: {518        parents: 1,519        interior: {520          X1: {521            Parachain: ACALA_CHAIN,522          },523        },524      },525    };526527    const beneficiary = {528      V2: {529        parents: 0,530        interior: {531          X1: {532            AccountId32: {533              network: 'Any',534              id: randomAccount.addressRaw,535            },536          },537        },538      },539    };540541    const assets = {542      V2: [543        {544          id: {545            Concrete: {546              parents: 0,547              interior: 'Here',548            },549          },550          fun: {551            Fungible: TRANSFER_AMOUNT,552          },553        },554      ],555    };556557    const feeAssetItem = 0;558559    await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited');560    balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);561562    const unqFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;563    console.log('[Unique -> Acala] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));564    expect(unqFees > 0n, 'Negative fees UNQ, looks like nothing was transferred').to.be.true;565566    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {567      await helper.wait.newBlocks(3);568569      balanceUniqueForeignTokenMiddle = await helper.tokens.accounts(randomAccount.address, {ForeignAsset: 0});570      balanceAcalaTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);571572      const acaFees = balanceAcalaTokenInit - balanceAcalaTokenMiddle;573      const unqIncomeTransfer = balanceUniqueForeignTokenMiddle - balanceUniqueForeignTokenInit;574      const acaUnqFees = TRANSFER_AMOUNT - unqIncomeTransfer;575576      console.log(577        '[Unique -> Acala] transaction fees on Acala: %s ACA',578        helper.util.bigIntToDecimals(acaFees, ACALA_DECIMALS),579      );580      console.log(581        '[Unique -> Acala] transaction fees on Acala: %s UNQ',582        helper.util.bigIntToDecimals(acaUnqFees),583      );584      console.log('[Unique -> Acala] income %s UNQ', helper.util.bigIntToDecimals(unqIncomeTransfer));585      expect(acaFees == 0n).to.be.true;586      expect(587        acaUnqFees == expectedAcalaIncomeFee,588        'Acala took different income fee, check the Acala foreign asset config',589      ).to.be.true;590    });591  });592593  itSub('Should connect to Acala and send UNQ back', async ({helper}) => {594    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {595      const destination = {596        V1: {597          parents: 1,598          interior: {599            X2: [600              {Parachain: UNIQUE_CHAIN},601              {602                AccountId32: {603                  network: 'Any',604                  id: randomAccount.addressRaw,605                },606              },607            ],608          },609        },610      };611612      const id = {613        ForeignAsset: 0,614      };615616      await helper.xTokens.transfer(randomAccount, id, ACALA_BACKWARD_TRANSFER_AMOUNT, destination, 'Unlimited');617      balanceAcalaTokenFinal = await helper.balance.getSubstrate(randomAccount.address);618      balanceUniqueForeignTokenFinal = await helper.tokens.accounts(randomAccount.address, id);619620      const acaFees = balanceAcalaTokenMiddle - balanceAcalaTokenFinal;621      const unqOutcomeTransfer = balanceUniqueForeignTokenMiddle - balanceUniqueForeignTokenFinal;622623      console.log(624        '[Acala -> Unique] transaction fees on Acala: %s ACA',625        helper.util.bigIntToDecimals(acaFees, ACALA_DECIMALS),626      );627      console.log('[Acala -> Unique] outcome %s UNQ', helper.util.bigIntToDecimals(unqOutcomeTransfer));628629      expect(acaFees > 0, 'Negative fees ACA, looks like nothing was transferred').to.be.true;630      expect(unqOutcomeTransfer == ACALA_BACKWARD_TRANSFER_AMOUNT).to.be.true;631    });632633    await helper.wait.newBlocks(3);634635    balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccount.address);636    const actuallyDelivered = balanceUniqueTokenFinal - balanceUniqueTokenMiddle;637    expect(actuallyDelivered > 0).to.be.true;638639    console.log('[Acala -> Unique] actually delivered %s UNQ', helper.util.bigIntToDecimals(actuallyDelivered));640641    const unqFees = ACALA_BACKWARD_TRANSFER_AMOUNT - actuallyDelivered;642    console.log('[Acala -> Unique] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));643    expect(unqFees == 0n).to.be.true;644  });645646  itSub('Acala can send only up to its balance', async ({helper}) => {647    // set Acala's sovereign account's balance648    const acalaBalance = 10000n * (10n ** UNQ_DECIMALS);649    const acalaSovereignAccount = helper.address.paraSiblingSovereignAccount(ACALA_CHAIN);650    await helper.getSudo().balance.setBalanceSubstrate(alice, acalaSovereignAccount, acalaBalance);651652    const moreThanAcalaHas = acalaBalance * 2n;653654    let targetAccountBalance = 0n;655    const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);656657    const uniqueMultilocation = {658      V1: {659        parents: 1,660        interior: {661          X1: {Parachain: UNIQUE_CHAIN},662        },663      },664    };665666    const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(667      targetAccount.addressRaw,668      {669        Concrete: {670          parents: 0,671          interior: 'Here',672        },673      },674      moreThanAcalaHas,675    );676677    let maliciousXcmProgramSent: any;678    const maxWaitBlocks = 3;679680    // Try to trick Unique681    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {682      await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgram);683684      maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);685    });686687    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash() == maliciousXcmProgramSent.messageHash()688        && event.outcome().isFailedToTransactAsset);689690    targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);691    expect(targetAccountBalance).to.be.equal(0n);692693    // But Acala still can send the correct amount694    const validTransferAmount = acalaBalance / 2n;695    const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(696      targetAccount.addressRaw,697      {698        Concrete: {699          parents: 0,700          interior: 'Here',701        },702      },703      validTransferAmount,704    );705706    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {707      await helper.getSudo().xcm.send(alice, uniqueMultilocation, validXcmProgram);708    });709710    await helper.wait.newBlocks(maxWaitBlocks);711712    targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);713    expect(targetAccountBalance).to.be.equal(validTransferAmount);714  });715716  itSub('Should not accept reserve transfer of UNQ from Acala', async ({helper}) => {717    const testAmount = 10_000n * (10n ** UNQ_DECIMALS);718    const [targetAccount] = await helper.arrange.createAccounts([0n], alice);719720    const uniqueMultilocation = {721      V1: {722        parents: 1,723        interior: {724          X1: {725            Parachain: UNIQUE_CHAIN,726          },727        },728      },729    };730731    const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(732      targetAccount.addressRaw,733      {734        Concrete: {735          parents: 1,736          interior: {737            X1: {738              Parachain: UNIQUE_CHAIN,739            },740          },741        },742      },743      testAmount,744    );745746    const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(747      targetAccount.addressRaw,748      {749        Concrete: {750          parents: 0,751          interior: 'Here',752        },753      },754      testAmount,755    );756757    let maliciousXcmProgramFullIdSent: any;758    let maliciousXcmProgramHereIdSent: any;759    const maxWaitBlocks = 3;760761    // Try to trick Unique using full UNQ identification762    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {763      await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramFullId);764765      maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);766    });767768    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash() == maliciousXcmProgramFullIdSent.messageHash()769        && event.outcome().isUntrustedReserveLocation);770771    let accountBalance = await helper.balance.getSubstrate(targetAccount.address);772    expect(accountBalance).to.be.equal(0n);773774    // Try to trick Unique using shortened UNQ identification775    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {776      await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramHereId);777778      maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);779    });780781    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash() == maliciousXcmProgramHereIdSent.messageHash()782        && event.outcome().isUntrustedReserveLocation);783784    accountBalance = await helper.balance.getSubstrate(targetAccount.address);785    expect(accountBalance).to.be.equal(0n);786  });787});788789// These tests are relevant only when790// the the corresponding foreign assets are not registered791describeXCM('[XCM] Integration test: Unique rejects non-native tokens', () => {792  let alice: IKeyringPair;793  let alith: IKeyringPair;794795  const testAmount = 100_000_000_000n;796  let uniqueParachainJunction;797  let uniqueAccountJunction;798799  let uniqueParachainMultilocation: any;800  let uniqueAccountMultilocation: any;801  let uniqueCombinedMultilocation: any;802803  let messageSent: any;804805  const maxWaitBlocks = 3;806807  before(async () => {808    await usingPlaygrounds(async (helper, privateKey) => {809      alice = await privateKey('//Alice');810811      uniqueParachainJunction = {Parachain: UNIQUE_CHAIN};812      uniqueAccountJunction = {813        AccountId32: {814          network: 'Any',815          id: alice.addressRaw,816        },817      };818819      uniqueParachainMultilocation = {820        V1: {821          parents: 1,822          interior: {823            X1: uniqueParachainJunction,824          },825        },826      };827828      uniqueAccountMultilocation = {829        V1: {830          parents: 0,831          interior: {832            X1: uniqueAccountJunction,833          },834        },835      };836837      uniqueCombinedMultilocation = {838        V1: {839          parents: 1,840          interior: {841            X2: [uniqueParachainJunction, uniqueAccountJunction],842          },843        },844      };845846      // Set the default version to wrap the first message to other chains.847      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);848    });849850    // eslint-disable-next-line require-await851    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {852      alith = helper.account.alithAccount();853    });854  });855856  const expectFailedToTransact = async (helper: DevUniqueHelper, messageSent: any) => {857    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash() == messageSent.messageHash()858        && event.outcome().isFailedToTransactAsset);859  };860861  itSub('Unique rejects ACA tokens from Acala', async ({helper}) => {862    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {863      const id = {864        Token: 'ACA',865      };866      const destination = uniqueCombinedMultilocation;867      await helper.xTokens.transfer(alice, id, testAmount, destination, 'Unlimited');868869      messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);870    });871872    await expectFailedToTransact(helper, messageSent);873  });874875  itSub('Unique rejects GLMR tokens from Moonbeam', async ({helper}) => {876    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {877      const id = 'SelfReserve';878      const destination = uniqueCombinedMultilocation;879      await helper.xTokens.transfer(alith, id, testAmount, destination, 'Unlimited');880881      messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);882    });883884    await expectFailedToTransact(helper, messageSent);885  });886887  itSub('Unique rejects ASTR tokens from Astar', async ({helper}) => {888    await usingAstarPlaygrounds(astarUrl, async (helper) => {889      const destinationParachain = uniqueParachainMultilocation;890      const beneficiary = uniqueAccountMultilocation;891      const assets = {892        V1: [{893          id: {894            Concrete: {895              parents: 0,896              interior: 'Here',897            },898          },899          fun: {900            Fungible: testAmount,901          },902        }],903      };904      const feeAssetItem = 0;905906      await helper.executeExtrinsic(alice, 'api.tx.polkadotXcm.reserveWithdrawAssets', [907        destinationParachain,908        beneficiary,909        assets,910        feeAssetItem,911      ]);912913      messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);914    });915916    await expectFailedToTransact(helper, messageSent);917  });918});919920describeXCM('[XCM] Integration test: Exchanging UNQ with Moonbeam', () => {921  // Unique constants922  let alice: IKeyringPair;923  let uniqueAssetLocation;924925  let randomAccountUnique: IKeyringPair;926  let randomAccountMoonbeam: IKeyringPair;927928  // Moonbeam constants929  let assetId: string;930931  const uniqueAssetMetadata = {932    name: 'xcUnique',933    symbol: 'xcUNQ',934    decimals: 18,935    isFrozen: false,936    minimalBalance: 1n,937  };938939  let balanceUniqueTokenInit: bigint;940  let balanceUniqueTokenMiddle: bigint;941  let balanceUniqueTokenFinal: bigint;942  let balanceForeignUnqTokenInit: bigint;943  let balanceForeignUnqTokenMiddle: bigint;944  let balanceForeignUnqTokenFinal: bigint;945  let balanceGlmrTokenInit: bigint;946  let balanceGlmrTokenMiddle: bigint;947  let balanceGlmrTokenFinal: bigint;948949  before(async () => {950    await usingPlaygrounds(async (helper, privateKey) => {951      alice = await privateKey('//Alice');952      [randomAccountUnique] = await helper.arrange.createAccounts([0n], alice);953954      balanceForeignUnqTokenInit = 0n;955956      // Set the default version to wrap the first message to other chains.957      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);958    });959960    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {961      const alithAccount = helper.account.alithAccount();962      const baltatharAccount = helper.account.baltatharAccount();963      const dorothyAccount = helper.account.dorothyAccount();964965      randomAccountMoonbeam = helper.account.create();966967      // >>> Sponsoring Dorothy >>>968      console.log('Sponsoring Dorothy.......');969      await helper.balance.transferToEthereum(alithAccount, dorothyAccount.address, 11_000_000_000_000_000_000n);970      console.log('Sponsoring Dorothy.......DONE');971      // <<< Sponsoring Dorothy <<<972973      uniqueAssetLocation = {974        XCM: {975          parents: 1,976          interior: {X1: {Parachain: UNIQUE_CHAIN}},977        },978      };979      const existentialDeposit = 1n;980      const isSufficient = true;981      const unitsPerSecond = 1n;982      const numAssetsWeightHint = 0;983984      const encodedProposal = helper.assetManager.makeRegisterForeignAssetProposal({985        location: uniqueAssetLocation,986        metadata: uniqueAssetMetadata,987        existentialDeposit,988        isSufficient,989        unitsPerSecond,990        numAssetsWeightHint,991      });992993      console.log('Encoded proposal for registerForeignAsset & setAssetUnitsPerSecond is %s', encodedProposal);994995      await helper.fastDemocracy.executeProposal('register UNQ foreign asset', encodedProposal);996997      // >>> Acquire Unique AssetId Info on Moonbeam >>>998      console.log('Acquire Unique AssetId Info on Moonbeam.......');9991000      assetId = (await helper.assetManager.assetTypeId(uniqueAssetLocation)).toString();10011002      console.log('UNQ asset ID is %s', assetId);1003      console.log('Acquire Unique AssetId Info on Moonbeam.......DONE');1004      // >>> Acquire Unique AssetId Info on Moonbeam >>>10051006      // >>> Sponsoring random Account >>>1007      console.log('Sponsoring random Account.......');1008      await helper.balance.transferToEthereum(baltatharAccount, randomAccountMoonbeam.address, 11_000_000_000_000_000_000n);1009      console.log('Sponsoring random Account.......DONE');1010      // <<< Sponsoring random Account <<<10111012      balanceGlmrTokenInit = await helper.balance.getEthereum(randomAccountMoonbeam.address);1013    });10141015    await usingPlaygrounds(async (helper) => {1016      await helper.balance.transferToSubstrate(alice, randomAccountUnique.address, 10n * TRANSFER_AMOUNT);1017      balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccountUnique.address);1018    });1019  });10201021  itSub('Should connect and send UNQ to Moonbeam', async ({helper}) => {1022    const currencyId = {1023      NativeAssetId: 'Here',1024    };1025    const dest = {1026      V2: {1027        parents: 1,1028        interior: {1029          X2: [1030            {Parachain: MOONBEAM_CHAIN},1031            {AccountKey20: {network: 'Any', key: randomAccountMoonbeam.address}},1032          ],1033        },1034      },1035    };1036    const amount = TRANSFER_AMOUNT;10371038    await helper.xTokens.transfer(randomAccountUnique, currencyId, amount, dest, 'Unlimited');10391040    balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccountUnique.address);1041    expect(balanceUniqueTokenMiddle < balanceUniqueTokenInit).to.be.true;10421043    const transactionFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;1044    console.log('[Unique -> Moonbeam] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(transactionFees));1045    expect(transactionFees > 0, 'Negative fees UNQ, looks like nothing was transferred').to.be.true;10461047    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1048      await helper.wait.newBlocks(3);10491050      balanceGlmrTokenMiddle = await helper.balance.getEthereum(randomAccountMoonbeam.address);10511052      const glmrFees = balanceGlmrTokenInit - balanceGlmrTokenMiddle;1053      console.log('[Unique -> Moonbeam] transaction fees on Moonbeam: %s GLMR', helper.util.bigIntToDecimals(glmrFees));1054      expect(glmrFees == 0n).to.be.true;10551056      balanceForeignUnqTokenMiddle = (await helper.assets.account(assetId, randomAccountMoonbeam.address))!;10571058      const unqIncomeTransfer = balanceForeignUnqTokenMiddle - balanceForeignUnqTokenInit;1059      console.log('[Unique -> Moonbeam] income %s UNQ', helper.util.bigIntToDecimals(unqIncomeTransfer));1060      expect(unqIncomeTransfer == TRANSFER_AMOUNT).to.be.true;1061    });1062  });10631064  itSub('Should connect to Moonbeam and send UNQ back', async ({helper}) => {1065    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1066      const asset = {1067        V1: {1068          id: {1069            Concrete: {1070              parents: 1,1071              interior: {1072                X1: {Parachain: UNIQUE_CHAIN},1073              },1074            },1075          },1076          fun: {1077            Fungible: TRANSFER_AMOUNT,1078          },1079        },1080      };1081      const destination = {1082        V1: {1083          parents: 1,1084          interior: {1085            X2: [1086              {Parachain: UNIQUE_CHAIN},1087              {AccountId32: {network: 'Any', id: randomAccountUnique.addressRaw}},1088            ],1089          },1090        },1091      };10921093      await helper.xTokens.transferMultiasset(randomAccountMoonbeam, asset, destination, 'Unlimited');10941095      balanceGlmrTokenFinal = await helper.balance.getEthereum(randomAccountMoonbeam.address);10961097      const glmrFees = balanceGlmrTokenMiddle - balanceGlmrTokenFinal;1098      console.log('[Moonbeam -> Unique] transaction fees on Moonbeam: %s GLMR', helper.util.bigIntToDecimals(glmrFees));1099      expect(glmrFees > 0, 'Negative fees GLMR, looks like nothing was transferred').to.be.true;11001101      const unqRandomAccountAsset = await helper.assets.account(assetId, randomAccountMoonbeam.address);11021103      expect(unqRandomAccountAsset).to.be.null;11041105      balanceForeignUnqTokenFinal = 0n;11061107      const unqOutcomeTransfer = balanceForeignUnqTokenMiddle - balanceForeignUnqTokenFinal;1108      console.log('[Unique -> Moonbeam] outcome %s UNQ', helper.util.bigIntToDecimals(unqOutcomeTransfer));1109      expect(unqOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;1110    });11111112    await helper.wait.newBlocks(3);11131114    balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccountUnique.address);1115    const actuallyDelivered = balanceUniqueTokenFinal - balanceUniqueTokenMiddle;1116    expect(actuallyDelivered > 0).to.be.true;11171118    console.log('[Moonbeam -> Unique] actually delivered %s UNQ', helper.util.bigIntToDecimals(actuallyDelivered));11191120    const unqFees = TRANSFER_AMOUNT - actuallyDelivered;1121    console.log('[Moonbeam -> Unique] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));1122    expect(unqFees == 0n).to.be.true;1123  });11241125  itSub('Moonbeam can send only up to its balance', async ({helper}) => {1126    // set Moonbeam's sovereign account's balance1127    const moonbeamBalance = 10000n * (10n ** UNQ_DECIMALS);1128    const moonbeamSovereignAccount = helper.address.paraSiblingSovereignAccount(MOONBEAM_CHAIN);1129    await helper.getSudo().balance.setBalanceSubstrate(alice, moonbeamSovereignAccount, moonbeamBalance);11301131    const moreThanMoonbeamHas = moonbeamBalance * 2n;11321133    let targetAccountBalance = 0n;1134    const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);11351136    const uniqueMultilocation = {1137      V1: {1138        parents: 1,1139        interior: {1140          X1: {Parachain: UNIQUE_CHAIN},1141        },1142      },1143    };11441145    const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(1146      targetAccount.addressRaw,1147      {1148        Concrete: {1149          parents: 0,1150          interior: 'Here',1151        },1152      },1153      moreThanMoonbeamHas,1154    );11551156    let maliciousXcmProgramSent: any;1157    const maxWaitBlocks = 3;11581159    // Try to trick Unique1160    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1161      const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueMultilocation, maliciousXcmProgram]);11621163      // Needed to bypass the call filter.1164      const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);1165      await helper.fastDemocracy.executeProposal('try to spend more UNQ than Moonbeam has', batchCall);11661167      maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1168    });11691170    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash() == maliciousXcmProgramSent.messageHash()1171        && event.outcome().isFailedToTransactAsset);11721173    targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1174    expect(targetAccountBalance).to.be.equal(0n);11751176    // But Moonbeam still can send the correct amount1177    const validTransferAmount = moonbeamBalance / 2n;1178    const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(1179      targetAccount.addressRaw,1180      {1181        Concrete: {1182          parents: 0,1183          interior: 'Here',1184        },1185      },1186      validTransferAmount,1187    );11881189    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1190      const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueMultilocation, validXcmProgram]);11911192      // Needed to bypass the call filter.1193      const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);1194      await helper.fastDemocracy.executeProposal('Spend the correct amount of UNQ', batchCall);1195    });11961197    await helper.wait.newBlocks(maxWaitBlocks);11981199    targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1200    expect(targetAccountBalance).to.be.equal(validTransferAmount);1201  });12021203  itSub('Should not accept reserve transfer of UNQ from Moonbeam', async ({helper}) => {1204    const testAmount = 10_000n * (10n ** UNQ_DECIMALS);1205    const [targetAccount] = await helper.arrange.createAccounts([0n], alice);12061207    const uniqueMultilocation = {1208      V1: {1209        parents: 1,1210        interior: {1211          X1: {1212            Parachain: UNIQUE_CHAIN,1213          },1214        },1215      },1216    };12171218    const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(1219      targetAccount.addressRaw,1220      {1221        Concrete: {1222          parents: 0,1223          interior: {1224            X1: {1225              Parachain: UNIQUE_CHAIN,1226            },1227          },1228        },1229      },1230      testAmount,1231    );12321233    const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(1234      targetAccount.addressRaw,1235      {1236        Concrete: {1237          parents: 0,1238          interior: 'Here',1239        },1240      },1241      testAmount,1242    );12431244    let maliciousXcmProgramFullIdSent: any;1245    let maliciousXcmProgramHereIdSent: any;1246    const maxWaitBlocks = 3;12471248    // Try to trick Unique using full UNQ identification1249    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1250      const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueMultilocation, maliciousXcmProgramFullId]);12511252      // Needed to bypass the call filter.1253      const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);1254      await helper.fastDemocracy.executeProposal('try to act like a reserve location for UNQ using path asset identification', batchCall);12551256      maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1257    });12581259    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash() == maliciousXcmProgramFullIdSent.messageHash()1260        && event.outcome().isUntrustedReserveLocation);12611262    let accountBalance = await helper.balance.getSubstrate(targetAccount.address);1263    expect(accountBalance).to.be.equal(0n);12641265    // Try to trick Unique using shortened UNQ identification1266    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1267      const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueMultilocation, maliciousXcmProgramHereId]);12681269      // Needed to bypass the call filter.1270      const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);1271      await helper.fastDemocracy.executeProposal('try to act like a reserve location for UNQ using "here" asset identification', batchCall);12721273      maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1274    });12751276    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash() == maliciousXcmProgramHereIdSent.messageHash()1277        && event.outcome().isUntrustedReserveLocation);12781279    accountBalance = await helper.balance.getSubstrate(targetAccount.address);1280    expect(accountBalance).to.be.equal(0n);1281  });1282});12831284describeXCM('[XCM] Integration test: Exchanging tokens with Astar', () => {1285  let alice: IKeyringPair;1286  let randomAccount: IKeyringPair;12871288  const UNQ_ASSET_ID_ON_ASTAR = 1;1289  const UNQ_MINIMAL_BALANCE_ON_ASTAR = 1n;12901291  // Unique -> Astar1292  const astarInitialBalance = 1n * (10n ** ASTAR_DECIMALS); // 1 ASTR, existential deposit required to actually create the account on Astar.1293  const unitsPerSecond = 228_000_000_000n; // This is Phala's value. What will be ours?1294  const unqToAstarTransferred = 10n * (10n ** UNQ_DECIMALS); // 10 UNQ1295  const unqToAstarArrived = 9_999_999_999_088_000_000n; // 9.999 ... UNQ, Astar takes a commision in foreign tokens12961297  // Astar -> Unique1298  const unqFromAstarTransfered = 5n * (10n ** UNQ_DECIMALS); // 5 UNQ1299  const unqOnAstarLeft = unqToAstarArrived - unqFromAstarTransfered; // 4.999_999_999_088_000_000n UNQ13001301  let balanceAfterUniqueToAstarXCM: bigint;13021303  before(async () => {1304    await usingPlaygrounds(async (helper, privateKey) => {1305      alice = await privateKey('//Alice');1306      [randomAccount] = await helper.arrange.createAccounts([100n], alice);1307      console.log('randomAccount', randomAccount.address);13081309      // Set the default version to wrap the first message to other chains.1310      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);1311    });13121313    await usingAstarPlaygrounds(astarUrl, async (helper) => {1314      console.log('1. Create foreign asset and metadata');1315      // TODO update metadata with values from production1316      await helper.assets.create(1317        alice,1318        UNQ_ASSET_ID_ON_ASTAR,1319        alice.address,1320        UNQ_MINIMAL_BALANCE_ON_ASTAR,1321      );13221323      await helper.assets.setMetadata(1324        alice,1325        UNQ_ASSET_ID_ON_ASTAR,1326        'Cross chain UNQ',1327        'xcUNQ',1328        Number(UNQ_DECIMALS),1329      );13301331      console.log('2. Register asset location on Astar');1332      const assetLocation = {1333        V1: {1334          parents: 1,1335          interior: {1336            X1: {1337              Parachain: UNIQUE_CHAIN,1338            },1339          },1340        },1341      };13421343      await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.registerAssetLocation', [assetLocation, UNQ_ASSET_ID_ON_ASTAR]);13441345      console.log('3. Set UNQ payment for XCM execution on Astar');1346      await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.setAssetUnitsPerSecond', [assetLocation, unitsPerSecond]);13471348      console.log('4. Transfer 1 ASTR to recipient to create the account (needed due to existential balance)');1349      await helper.balance.transferToSubstrate(alice, randomAccount.address, astarInitialBalance);1350    });1351  });13521353  itSub('Should connect and send UNQ to Astar', async ({helper}) => {1354    const destination = {1355      V2: {1356        parents: 1,1357        interior: {1358          X1: {1359            Parachain: ASTAR_CHAIN,1360          },1361        },1362      },1363    };13641365    const beneficiary = {1366      V2: {1367        parents: 0,1368        interior: {1369          X1: {1370            AccountId32: {1371              network: 'Any',1372              id: randomAccount.addressRaw,1373            },1374          },1375        },1376      },1377    };13781379    const assets = {1380      V2: [1381        {1382          id: {1383            Concrete: {1384              parents: 0,1385              interior: 'Here',1386            },1387          },1388          fun: {1389            Fungible: unqToAstarTransferred,1390          },1391        },1392      ],1393    };13941395    // Initial balance is 100 UNQ1396    const balanceBefore = await helper.balance.getSubstrate(randomAccount.address);1397    console.log(`Initial balance is: ${balanceBefore}`);13981399    const feeAssetItem = 0;1400    await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited');14011402    // Balance after reserve transfer is less than 901403    balanceAfterUniqueToAstarXCM = await helper.balance.getSubstrate(randomAccount.address);1404    console.log(`UNQ Balance on Unique after XCM is: ${balanceAfterUniqueToAstarXCM}`);1405    console.log(`Unique's UNQ commission is: ${balanceBefore - balanceAfterUniqueToAstarXCM}`);1406    expect(balanceBefore - balanceAfterUniqueToAstarXCM > 0).to.be.true;14071408    await usingAstarPlaygrounds(astarUrl, async (helper) => {1409      await helper.wait.newBlocks(3);1410      const xcUNQbalance = await helper.assets.account(UNQ_ASSET_ID_ON_ASTAR, randomAccount.address);1411      const astarBalance = await helper.balance.getSubstrate(randomAccount.address);14121413      console.log(`xcUNQ balance on Astar after XCM is: ${xcUNQbalance}`);1414      console.log(`Astar's UNQ commission is: ${unqToAstarTransferred - xcUNQbalance!}`);14151416      expect(xcUNQbalance).to.eq(unqToAstarArrived);1417      // Astar balance does not changed1418      expect(astarBalance).to.eq(astarInitialBalance);1419    });1420  });14211422  itSub('Should connect to Astar and send UNQ back', async ({helper}) => {1423    await usingAstarPlaygrounds(astarUrl, async (helper) => {1424      const destination = {1425        V1: {1426          parents: 1,1427          interior: {1428            X1: {1429              Parachain: UNIQUE_CHAIN,1430            },1431          },1432        },1433      };14341435      const beneficiary = {1436        V1: {1437          parents: 0,1438          interior: {1439            X1: {1440              AccountId32: {1441                network: 'Any',1442                id: randomAccount.addressRaw,1443              },1444            },1445          },1446        },1447      };14481449      const assets = {1450        V1: [1451          {1452            id: {1453              Concrete: {1454                parents: 1,1455                interior: {1456                  X1: {1457                    Parachain: UNIQUE_CHAIN,1458                  },1459                },1460              },1461            },1462            fun: {1463              Fungible: unqFromAstarTransfered,1464            },1465          },1466        ],1467      };14681469      // Initial balance is 1 ASTR1470      const balanceASTRbefore = await helper.balance.getSubstrate(randomAccount.address);1471      console.log(`ASTR balance is: ${balanceASTRbefore}, it does not changed`);1472      expect(balanceASTRbefore).to.eq(astarInitialBalance);14731474      const feeAssetItem = 0;1475      // this is non-standard polkadotXcm extension for Astar only. It calls InitiateReserveWithdraw1476      await helper.executeExtrinsic(randomAccount, 'api.tx.polkadotXcm.reserveWithdrawAssets', [destination, beneficiary, assets, feeAssetItem]);14771478      const xcUNQbalance = await helper.assets.account(UNQ_ASSET_ID_ON_ASTAR, randomAccount.address);1479      const balanceAstar = await helper.balance.getSubstrate(randomAccount.address);1480      console.log(`xcUNQ balance on Astar after XCM is: ${xcUNQbalance}`);14811482      // Assert: xcUNQ balance correctly decreased1483      expect(xcUNQbalance).to.eq(unqOnAstarLeft);1484      // Assert: ASTR balance is 0.996...1485      expect(balanceAstar / (10n ** (ASTAR_DECIMALS - 3n))).to.eq(996n);1486    });14871488    await helper.wait.newBlocks(3);1489    const balanceUNQ = await helper.balance.getSubstrate(randomAccount.address);1490    console.log(`UNQ Balance on Unique after XCM is: ${balanceUNQ}`);1491    expect(balanceUNQ).to.eq(balanceAfterUniqueToAstarXCM + unqFromAstarTransfered);1492  });14931494  itSub('Astar can send only up to its balance', async ({helper}) => {1495    // set Astar's sovereign account's balance1496    const astarBalance = 10000n * (10n ** UNQ_DECIMALS);1497    const astarSovereignAccount = helper.address.paraSiblingSovereignAccount(ASTAR_CHAIN);1498    await helper.getSudo().balance.setBalanceSubstrate(alice, astarSovereignAccount, astarBalance);14991500    const moreThanAstarHas = astarBalance * 2n;15011502    let targetAccountBalance = 0n;1503    const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);15041505    const uniqueMultilocation = {1506      V1: {1507        parents: 1,1508        interior: {1509          X1: {Parachain: UNIQUE_CHAIN},1510        },1511      },1512    };15131514    const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(1515      targetAccount.addressRaw,1516      {1517        Concrete: {1518          parents: 0,1519          interior: 'Here',1520        },1521      },1522      moreThanAstarHas,1523    );15241525    let maliciousXcmProgramSent: any;1526    const maxWaitBlocks = 3;15271528    // Try to trick Unique1529    await usingAstarPlaygrounds(astarUrl, async (helper) => {1530      await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgram);15311532      maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1533    });15341535    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash() == maliciousXcmProgramSent.messageHash()1536        && event.outcome().isFailedToTransactAsset);15371538    targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1539    expect(targetAccountBalance).to.be.equal(0n);15401541    // But Astar still can send the correct amount1542    const validTransferAmount = astarBalance / 2n;1543    const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(1544      targetAccount.addressRaw,1545      {1546        Concrete: {1547          parents: 0,1548          interior: 'Here',1549        },1550      },1551      validTransferAmount,1552    );15531554    await usingAstarPlaygrounds(astarUrl, async (helper) => {1555      await helper.getSudo().xcm.send(alice, uniqueMultilocation, validXcmProgram);1556    });15571558    await helper.wait.newBlocks(maxWaitBlocks);15591560    targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1561    expect(targetAccountBalance).to.be.equal(validTransferAmount);1562  });15631564  itSub('Should not accept reserve transfer of UNQ from Astar', async ({helper}) => {1565    const testAmount = 10_000n * (10n ** UNQ_DECIMALS);1566    const [targetAccount] = await helper.arrange.createAccounts([0n], alice);15671568    const uniqueMultilocation = {1569      V1: {1570        parents: 1,1571        interior: {1572          X1: {1573            Parachain: UNIQUE_CHAIN,1574          },1575        },1576      },1577    };15781579    const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(1580      targetAccount.addressRaw,1581      {1582        Concrete: {1583          parents: 1,1584          interior: {1585            X1: {1586              Parachain: UNIQUE_CHAIN,1587            },1588          },1589        },1590      },1591      testAmount,1592    );15931594    const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(1595      targetAccount.addressRaw,1596      {1597        Concrete: {1598          parents: 0,1599          interior: 'Here',1600        },1601      },1602      testAmount,1603    );16041605    let maliciousXcmProgramFullIdSent: any;1606    let maliciousXcmProgramHereIdSent: any;1607    const maxWaitBlocks = 3;16081609    // Try to trick Unique using full UNQ identification1610    await usingAstarPlaygrounds(astarUrl, async (helper) => {1611      await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramFullId);16121613      maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1614    });16151616    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash() == maliciousXcmProgramFullIdSent.messageHash()1617        && event.outcome().isUntrustedReserveLocation);16181619    let accountBalance = await helper.balance.getSubstrate(targetAccount.address);1620    expect(accountBalance).to.be.equal(0n);16211622    // Try to trick Unique using shortened UNQ identification1623    await usingAstarPlaygrounds(astarUrl, async (helper) => {1624      await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramHereId);16251626      maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1627    });16281629    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash() == maliciousXcmProgramHereIdSent.messageHash()1630        && event.outcome().isUntrustedReserveLocation);16311632    accountBalance = await helper.balance.getSubstrate(targetAccount.address);1633    expect(accountBalance).to.be.equal(0n);1634  });16351636});