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

difftreelog

source

tests/src/xcm/lowLevelXcmUnique.test.ts26.9 KiBsourcehistory
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, usingMoonbeamPlaygrounds, usingAstarPlaygrounds, usingPolkadexPlaygrounds} from '../util';20import {DevUniqueHelper, Event} from '../util/playgrounds/unique.dev';21import {nToBigInt} from '@polkadot/util';22import {hexToString} from '@polkadot/util';2324const UNIQUE_CHAIN = +(process.env.RELAY_UNIQUE_ID || 2037);25const ACALA_CHAIN = +(process.env.RELAY_ACALA_ID || 2000);26const MOONBEAM_CHAIN = +(process.env.RELAY_MOONBEAM_ID || 2004);27const ASTAR_CHAIN = +(process.env.RELAY_ASTAR_ID || 2006);28const POLKADEX_CHAIN = +(process.env.RELAY_POLKADEX_ID || 2040);29303132const acalaUrl = config.acalaUrl;33const moonbeamUrl = config.moonbeamUrl;34const astarUrl = config.astarUrl;35const polkadexUrl = config.polkadexUrl;3637const ASTAR_DECIMALS = 18n;38const UNQ_DECIMALS = 18n;3940const TRANSFER_AMOUNT = 2000000_000_000_000_000_000_000n;41const SENDER_BUDGET = 2n * TRANSFER_AMOUNT;42const SENDBACK_AMOUNT = TRANSFER_AMOUNT / 2n;43const STAYED_ON_TARGET_CHAIN = TRANSFER_AMOUNT - SENDBACK_AMOUNT;44const TARGET_CHAIN_TOKEN_TRANSFER_AMOUNT = 100_000_000_000n;4546const SAFE_XCM_VERSION = 2;47const maxWaitBlocks = 6;4849const uniqueMultilocation = {50  V2: {51    parents: 1,52    interior: {53      X1: {54        Parachain: UNIQUE_CHAIN,55      },56    },57  },58};5960let balanceUniqueTokenInit: bigint;61let balanceUniqueTokenMiddle: bigint;62let balanceUniqueTokenFinal: bigint;63let unqFees: bigint;6465const expectFailedToTransact = async (helper: DevUniqueHelper, messageSent: any) => {66  await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == messageSent.messageHash67        && event.outcome.isFailedToTransactAsset);68};69const expectUntrustedReserveLocationFail = async (helper: DevUniqueHelper, messageSent: any) => {70  await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == messageSent.messageHash71         && event.outcome.isUntrustedReserveLocation);72};7374const NETWORKS = {75  acala: usingAcalaPlaygrounds,76  astar: usingAstarPlaygrounds,77  polkadex: usingPolkadexPlaygrounds,78  moonbeam: usingMoonbeamPlaygrounds,79} as const;8081function mapToChainId(networkName: keyof typeof NETWORKS) {82  switch (networkName) {83    case 'acala':84      return ACALA_CHAIN;85    case 'astar':86      return ASTAR_CHAIN;87    case 'moonbeam':88      return MOONBEAM_CHAIN;89    case 'polkadex':90      return POLKADEX_CHAIN;91  }92}9394function mapToChainUrl(networkName: keyof typeof NETWORKS): string {95  switch (networkName) {96    case 'acala':97      return acalaUrl;98    case 'astar':99      return astarUrl;100    case 'moonbeam':101      return moonbeamUrl;102    case 'polkadex':103      return polkadexUrl;104  }105}106107function getDevPlayground<T extends keyof typeof NETWORKS>(name: T) {108  return NETWORKS[name];109}110111112async function genericSendUnqTo(113  networkName: keyof typeof NETWORKS,114  randomAccount: IKeyringPair,115  randomAccountOnTargetChain = randomAccount,116) {117  const networkUrl = mapToChainUrl(networkName);118  const targetPlayground = getDevPlayground(networkName);119  await usingPlaygrounds(async (helper) => {120    balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccount.address);121    const destination = {122      V2: {123        parents: 1,124        interior: {125          X1: {126            Parachain: mapToChainId(networkName),127          },128        },129      },130    };131132    const beneficiary = {133      V2: {134        parents: 0,135        interior: {136          X1: (137            networkName == 'moonbeam' ?138              {139                AccountKey20: {140                  network: 'Any',141                  key: randomAccountOnTargetChain.address,142                },143              }144              :145              {146                AccountId32: {147                  network: 'Any',148                  id: randomAccountOnTargetChain.addressRaw,149                },150              }151          ),152        },153      },154    };155156    const assets = {157      V2: [158        {159          id: {160            Concrete: {161              parents: 0,162              interior: 'Here',163            },164          },165          fun: {166            Fungible: TRANSFER_AMOUNT,167          },168        },169      ],170    };171    const feeAssetItem = 0;172173    await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited');174    const messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);175    balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);176177    unqFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;178    console.log('[Unique -> %s] transaction fees on Unique: %s UNQ', networkName, helper.util.bigIntToDecimals(unqFees));179    expect(unqFees > 0n, 'Negative fees UNQ, looks like nothing was transferred').to.be.true;180181    await targetPlayground(networkUrl, async (helper) => {182      /*183        Since only the parachain part of the Polkadex184        infrastructure is launched (without their185        solochain validators), processing incoming186        assets will lead to an error.187        This error indicates that the Polkadex chain188        received a message from the Unique network,189        since the hash is being checked to ensure190        it matches what was sent.191      */192      if(networkName == 'polkadex') {193        await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == messageSent.messageHash);194      } else {195        await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Success, event => event.messageHash == messageSent.messageHash);196      }197    });198199  });200}201202async function genericSendUnqBack(203  networkName: keyof typeof NETWORKS,204  sudoer: IKeyringPair,205  randomAccountOnUnq: IKeyringPair,206) {207  const networkUrl = mapToChainUrl(networkName);208209  const targetPlayground = getDevPlayground(networkName);210  await usingPlaygrounds(async (helper) => {211212    const xcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(213      randomAccountOnUnq.addressRaw,214      {215        Concrete: {216          parents: 1,217          interior: {218            X1: {Parachain: UNIQUE_CHAIN},219          },220        },221      },222      SENDBACK_AMOUNT,223    );224225    let xcmProgramSent: any;226227228    await targetPlayground(networkUrl, async (helper) => {229      if('getSudo' in helper) {230        await helper.getSudo().xcm.send(sudoer, uniqueMultilocation, xcmProgram);231        xcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);232      } else if('fastDemocracy' in helper) {233        const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueMultilocation, xcmProgram]);234        // Needed to bypass the call filter.235        const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);236        await helper.fastDemocracy.executeProposal('sending MoonBeam -> Unique via XCM program', batchCall);237        xcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);238      }239    });240241    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Success, event => event.messageHash == xcmProgramSent.messageHash);242243    balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccountOnUnq.address);244245    expect(balanceUniqueTokenFinal).to.be.equal(balanceUniqueTokenInit - unqFees - STAYED_ON_TARGET_CHAIN);246247  });248}249250async function genericSendOnlyOwnedBalance(251  networkName: keyof typeof NETWORKS,252  sudoer: IKeyringPair,253) {254  const networkUrl = mapToChainUrl(networkName);255  const targetPlayground = getDevPlayground(networkName);256257  const targetChainBalance = 10000n * (10n ** UNQ_DECIMALS);258259  await usingPlaygrounds(async (helper) => {260    const targetChainSovereignAccount = helper.address.paraSiblingSovereignAccount(mapToChainId(networkName));261    await helper.getSudo().balance.setBalanceSubstrate(sudoer, targetChainSovereignAccount, targetChainBalance);262    const moreThanTargetChainHas = 2n * targetChainBalance;263264    const targetAccount = helper.arrange.createEmptyAccount();265266    const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(267      targetAccount.addressRaw,268      {269        Concrete: {270          parents: 0,271          interior: 'Here',272        },273      },274      moreThanTargetChainHas,275    );276277    let maliciousXcmProgramSent: any;278279280    await targetPlayground(networkUrl, async (helper) => {281      if('getSudo' in helper) {282        await helper.getSudo().xcm.send(sudoer, uniqueMultilocation, maliciousXcmProgram);283        maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);284      } else if('fastDemocracy' in helper) {285        const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueMultilocation, maliciousXcmProgram]);286        // Needed to bypass the call filter.287        const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);288        await helper.fastDemocracy.executeProposal('sending MoonBeam -> Unique via XCM program', batchCall);289        maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);290      }291    });292293    await expectFailedToTransact(helper, maliciousXcmProgramSent);294295    const targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);296    expect(targetAccountBalance).to.be.equal(0n);297  });298}299300async function genericReserveTransferUNQfrom(netwokrName: keyof typeof NETWORKS, sudoer: IKeyringPair) {301  const networkUrl = mapToChainUrl(netwokrName);302  const targetPlayground = getDevPlayground(netwokrName);303304  await usingPlaygrounds(async (helper) => {305    const testAmount = 10_000n * (10n ** UNQ_DECIMALS);306    const targetAccount = helper.arrange.createEmptyAccount();307308    const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(309      targetAccount.addressRaw,310      {311        Concrete: {312          parents: 1,313          interior: {314            X1: {315              Parachain: UNIQUE_CHAIN,316            },317          },318        },319      },320      testAmount,321    );322323    const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(324      targetAccount.addressRaw,325      {326        Concrete: {327          parents: 0,328          interior: 'Here',329        },330      },331      testAmount,332    );333334    let maliciousXcmProgramFullIdSent: any;335    let maliciousXcmProgramHereIdSent: any;336    const maxWaitBlocks = 3;337338    // Try to trick Unique using full UNQ identification339    await targetPlayground(networkUrl, async (helper) => {340      if('getSudo' in helper) {341        await helper.getSudo().xcm.send(sudoer, uniqueMultilocation, maliciousXcmProgramFullId);342        maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);343      }344      // Moonbeam case345      else if('fastDemocracy' in helper) {346        const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueMultilocation, maliciousXcmProgramFullId]);347348        // Needed to bypass the call filter.349        const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);350        await helper.fastDemocracy.executeProposal('try to act like a reserve location for UNQ using path asset identification', batchCall);351352        maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);353      }354    });355356357    await expectUntrustedReserveLocationFail(helper, maliciousXcmProgramFullIdSent);358359    let accountBalance = await helper.balance.getSubstrate(targetAccount.address);360    expect(accountBalance).to.be.equal(0n);361362    // Try to trick Unique using shortened UNQ identification363    await targetPlayground(networkUrl, async (helper) => {364      if('getSudo' in helper) {365        await helper.getSudo().xcm.send(sudoer, uniqueMultilocation, maliciousXcmProgramHereId);366        maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);367      }368      else if('fastDemocracy' in helper) {369        const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueMultilocation, maliciousXcmProgramHereId]);370371        // Needed to bypass the call filter.372        const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);373        await helper.fastDemocracy.executeProposal('try to act like a reserve location for UNQ using "here" asset identification', batchCall);374375        maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);376      }377    });378379    await expectUntrustedReserveLocationFail(helper, maliciousXcmProgramHereIdSent);380381    accountBalance = await helper.balance.getSubstrate(targetAccount.address);382    expect(accountBalance).to.be.equal(0n);383  });384}385386async function genericRejectNativeToknsFrom(netwokrName: keyof typeof NETWORKS, sudoerOnTargetChain: IKeyringPair) {387  const networkUrl = mapToChainUrl(netwokrName);388  const targetPlayground = getDevPlayground(netwokrName);389  let messageSent: any;390391  await usingPlaygrounds(async (helper) => {392    const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(393      helper.arrange.createEmptyAccount().addressRaw,394      {395        Concrete: {396          parents: 1,397          interior: {398            X1: {399              Parachain: mapToChainId(netwokrName),400            },401          },402        },403      },404      TARGET_CHAIN_TOKEN_TRANSFER_AMOUNT,405    );406    await targetPlayground(networkUrl, async (helper) => {407      if('getSudo' in helper) {408        await helper.getSudo().xcm.send(sudoerOnTargetChain, uniqueMultilocation, maliciousXcmProgramFullId);409        messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);410      } else if('fastDemocracy' in helper) {411        const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueMultilocation, maliciousXcmProgramFullId]);412413        // Needed to bypass the call filter.414        const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);415        await helper.fastDemocracy.executeProposal('sending native tokens to the Unique via fast democracy', batchCall);416417        messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);418      }419    });420421    await expectFailedToTransact(helper, messageSent);422  });423}424425426describeXCM('[XCMLL] Integration test: Exchanging tokens with Acala', () => {427  let alice: IKeyringPair;428  let randomAccount: IKeyringPair;429430  before(async () => {431    await usingPlaygrounds(async (helper, privateKey) => {432      alice = await privateKey('//Alice');433      console.log(config.acalaUrl);434      randomAccount = helper.arrange.createEmptyAccount();435436      // Set the default version to wrap the first message to other chains.437      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);438    });439440    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {441      const destination = {442        V2: {443          parents: 1,444          interior: {445            X1: {446              Parachain: UNIQUE_CHAIN,447            },448          },449        },450      };451452      const metadata = {453        name: 'Unique Network',454        symbol: 'UNQ',455        decimals: 18,456        minimalBalance: 1250_000_000_000_000_000n,457      };458      const assets = (await (helper.callRpc('api.query.assetRegistry.assetMetadatas.entries'))).map(([_k, v] : [any, any]) =>459        hexToString(v.toJSON()['symbol'])) as string[];460461      if(!assets.includes('UNQ')) {462        await helper.getSudo().assetRegistry.registerForeignAsset(alice, destination, metadata);463      } else {464        console.log('UNQ token already registered on Acala assetRegistry pallet');465      }466      await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n);467    });468469    await usingPlaygrounds(async (helper) => {470      await helper.balance.transferToSubstrate(alice, randomAccount.address, SENDER_BUDGET);471      balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccount.address);472    });473  });474475  itSub('Should connect and send UNQ to Acala', async () => {476    await genericSendUnqTo('acala', randomAccount);477  });478479  itSub('Should connect to Acala and send UNQ back', async () => {480    await genericSendUnqBack('acala', alice, randomAccount);481  });482483  itSub('Acala can send only up to its balance', async () => {484    await genericSendOnlyOwnedBalance('acala', alice);485  });486487  itSub('Should not accept reserve transfer of UNQ from Acala', async () => {488    await genericReserveTransferUNQfrom('acala', alice);489  });490});491492describeXCM('[XCMLL] Integration test: Exchanging tokens with Polkadex', () => {493  let alice: IKeyringPair;494  let randomAccount: IKeyringPair;495496  const uniqueAssetId = {497    Concrete: {498      parents: 1,499      interior: {500        X1: {501          Parachain: UNIQUE_CHAIN,502        },503      },504    },505  };506507  before(async () => {508    await usingPlaygrounds(async (helper, privateKey) => {509      alice = await privateKey('//Alice');510      randomAccount = helper.arrange.createEmptyAccount();511512      // Set the default version to wrap the first message to other chains.513      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);514    });515516    await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {517      const isWhitelisted = ((await helper.callRpc('api.query.xcmHelper.whitelistedTokens', []))518        .toJSON() as [])519        .map(nToBigInt).length != 0;520      /*521      Check whether the Unique token has been added522      to the whitelist, since an error will occur523      if it is added again. Needed for debugging524      when this test is run multiple times.525      */526      if(!isWhitelisted) {527        await helper.getSudo().xcmHelper.whitelistToken(alice, uniqueAssetId);528      }529530      await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n);531    });532533    await usingPlaygrounds(async (helper) => {534      await helper.balance.transferToSubstrate(alice, randomAccount.address, SENDER_BUDGET);535      balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccount.address);536    });537  });538539  itSub('Should connect and send UNQ to Polkadex', async () => {540    await genericSendUnqTo('polkadex', randomAccount);541  });542543544  itSub('Should connect to Polkadex and send UNQ back', async () => {545    await genericSendUnqBack('polkadex', alice, randomAccount);546  });547548  itSub('Polkadex can send only up to its balance', async () => {549    await genericSendOnlyOwnedBalance('polkadex', alice);550  });551552  itSub('Should not accept reserve transfer of UNQ from Polkadex', async () => {553    await genericReserveTransferUNQfrom('polkadex', alice);554  });555});556557// These tests are relevant only when558// the the corresponding foreign assets are not registered559describeXCM('[XCMLL] Integration test: Unique rejects non-native tokens', () => {560  let alice: IKeyringPair;561562  before(async () => {563    await usingPlaygrounds(async (helper, privateKey) => {564      alice = await privateKey('//Alice');565566      // Set the default version to wrap the first message to other chains.567      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);568    });569  });570571  itSub('Unique rejects ACA tokens from Acala', async () => {572    await genericRejectNativeToknsFrom('acala', alice);573  });574575  itSub('Unique rejects GLMR tokens from Moonbeam', async () => {576    await genericRejectNativeToknsFrom('moonbeam', alice);577  });578579  itSub('Unique rejects ASTR tokens from Astar', async () => {580    await genericRejectNativeToknsFrom('astar', alice);581  });582583  itSub('Unique rejects PDX tokens from Polkadex', async () => {584    await genericRejectNativeToknsFrom('polkadex', alice);585  });586});587588describeXCM('[XCMLL] Integration test: Exchanging UNQ with Moonbeam', () => {589  // Unique constants590  let alice: IKeyringPair;591  let uniqueAssetLocation;592593  let randomAccountUnique: IKeyringPair;594  let randomAccountMoonbeam: IKeyringPair;595596  // Moonbeam constants597  let assetId: string;598599  const uniqueAssetMetadata = {600    name: 'xcUnique',601    symbol: 'xcUNQ',602    decimals: 18,603    isFrozen: false,604    minimalBalance: 1n,605  };606607608  before(async () => {609    await usingPlaygrounds(async (helper, privateKey) => {610      alice = await privateKey('//Alice');611      [randomAccountUnique] = await helper.arrange.createAccounts([0n], alice);612613614      // Set the default version to wrap the first message to other chains.615      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);616    });617618    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {619      const alithAccount = helper.account.alithAccount();620      const baltatharAccount = helper.account.baltatharAccount();621      const dorothyAccount = helper.account.dorothyAccount();622623      randomAccountMoonbeam = helper.account.create();624625      // >>> Sponsoring Dorothy >>>626      console.log('Sponsoring Dorothy.......');627      await helper.balance.transferToEthereum(alithAccount, dorothyAccount.address, 11_000_000_000_000_000_000n);628      console.log('Sponsoring Dorothy.......DONE');629      // <<< Sponsoring Dorothy <<<630      uniqueAssetLocation = {631        XCM: {632          parents: 1,633          interior: {X1: {Parachain: UNIQUE_CHAIN}},634        },635      };636      const existentialDeposit = 1n;637      const isSufficient = true;638      const unitsPerSecond = 1n;639      const numAssetsWeightHint = 0;640641      if((await helper.assetManager.assetTypeId(uniqueAssetLocation)).toJSON()) {642        console.log('Unique asset already registered');643      } else {644        const encodedProposal = helper.assetManager.makeRegisterForeignAssetProposal({645          location: uniqueAssetLocation,646          metadata: uniqueAssetMetadata,647          existentialDeposit,648          isSufficient,649          unitsPerSecond,650          numAssetsWeightHint,651        });652653        console.log('Encoded proposal for registerForeignAsset & setAssetUnitsPerSecond is %s', encodedProposal);654655        await helper.fastDemocracy.executeProposal('register UNQ foreign asset', encodedProposal);656657        // >>> Acquire Unique AssetId Info on Moonbeam >>>658        console.log('Acquire Unique AssetId Info on Moonbeam.......');659660        assetId = (await helper.assetManager.assetTypeId(uniqueAssetLocation)).toString();661662        console.log('UNQ asset ID is %s', assetId);663        console.log('Acquire Unique AssetId Info on Moonbeam.......DONE');664      }665      // >>> Acquire Unique AssetId Info on Moonbeam >>>666667      // >>> Sponsoring random Account >>>668      console.log('Sponsoring random Account.......');669      await helper.balance.transferToEthereum(baltatharAccount, randomAccountMoonbeam.address, 11_000_000_000_000_000_000n);670      console.log('Sponsoring random Account.......DONE');671      // <<< Sponsoring random Account <<<672    });673674    await usingPlaygrounds(async (helper) => {675      await helper.balance.transferToSubstrate(alice, randomAccountUnique.address, SENDER_BUDGET);676      balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccountUnique.address);677    });678  });679680  itSub('Should connect and send UNQ to Moonbeam', async () => {681    await genericSendUnqTo('moonbeam', randomAccountUnique, randomAccountMoonbeam);682  });683684  itSub('Should connect to Moonbeam and send UNQ back', async () => {685    await genericSendUnqBack('moonbeam', alice, randomAccountUnique);686  });687688  itSub('Moonbeam can send only up to its balance', async () => {689    await genericSendOnlyOwnedBalance('moonbeam', alice);690  });691692  itSub('Should not accept reserve transfer of UNQ from Moonbeam', async () => {693    await genericReserveTransferUNQfrom('moonbeam', alice);694  });695});696697describeXCM('[XCMLL] Integration test: Exchanging tokens with Astar', () => {698  let alice: IKeyringPair;699  let randomAccount: IKeyringPair;700701  const UNQ_ASSET_ID_ON_ASTAR = 1;702  const UNQ_MINIMAL_BALANCE_ON_ASTAR = 1n;703704  // Unique -> Astar705  const astarInitialBalance = 1n * (10n ** ASTAR_DECIMALS); // 1 ASTR, existential deposit required to actually create the account on Astar.706  const unitsPerSecond = 228_000_000_000n; // This is Phala's value. What will be ours?707708  before(async () => {709    await usingPlaygrounds(async (helper, privateKey) => {710      alice = await privateKey('//Alice');711      randomAccount = helper.arrange.createEmptyAccount();712      await helper.balance.transferToSubstrate(alice, randomAccount.address, SENDER_BUDGET);713      console.log('randomAccount', randomAccount.address);714715      // Set the default version to wrap the first message to other chains.716      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);717    });718719    await usingAstarPlaygrounds(astarUrl, async (helper) => {720      if(!(await helper.callRpc('api.query.assets.asset', [UNQ_ASSET_ID_ON_ASTAR])).toJSON()) {721        console.log('1. Create foreign asset and metadata');722        // TODO update metadata with values from production723        await helper.assets.create(724          alice,725          UNQ_ASSET_ID_ON_ASTAR,726          alice.address,727          UNQ_MINIMAL_BALANCE_ON_ASTAR,728        );729730        await helper.assets.setMetadata(731          alice,732          UNQ_ASSET_ID_ON_ASTAR,733          'Cross chain UNQ',734          'xcUNQ',735          Number(UNQ_DECIMALS),736        );737738        console.log('2. Register asset location on Astar');739        const assetLocation = {740          V2: {741            parents: 1,742            interior: {743              X1: {744                Parachain: UNIQUE_CHAIN,745              },746            },747          },748        };749750        await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.registerAssetLocation', [assetLocation, UNQ_ASSET_ID_ON_ASTAR]);751752        console.log('3. Set UNQ payment for XCM execution on Astar');753        await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.setAssetUnitsPerSecond', [assetLocation, unitsPerSecond]);754      }755      console.log('4. Transfer 1 ASTR to recipient to create the account (needed due to existential balance)');756      await helper.balance.transferToSubstrate(alice, randomAccount.address, astarInitialBalance);757    });758  });759760  itSub('Should connect and send UNQ to Astar', async () => {761    await genericSendUnqTo('astar', randomAccount);762  });763764  itSub('Should connect to Astar and send UNQ back', async () => {765    await genericSendUnqBack('astar', alice, randomAccount);766  });767768  itSub('Astar can send only up to its balance', async () => {769    await genericSendOnlyOwnedBalance('astar', alice);770  });771772  itSub('Should not accept reserve transfer of UNQ from Astar', async () => {773    await genericReserveTransferUNQfrom('astar', alice);774  });775});