git.delta.rocks / unique-network / refs/commits / 68bf2bb2d784

difftreelog

fix use config for xcm urls

Daniel Shiposha2022-10-14parent: #294182f.patch.diff
in: master

4 files changed

modifiedtests/src/config.tsdiffbeforeafterboth
--- a/tests/src/config.ts
+++ b/tests/src/config.ts
@@ -19,6 +19,12 @@
 const config = {
   substrateUrl: process.env.substrateUrl || 'ws://127.0.0.1:9944',
   frontierUrl: process.env.frontierUrl || 'http://127.0.0.1:9933',
+  relayUrl: process.env.relayUrl || 'ws://127.0.0.1:9844',
+  acalaUrl: process.env.acalaUrl || 'ws://127.0.0.1:9946',
+  karuraUrl: process.env.acalaUrl || 'ws://127.0.0.1:9946',
+  moonbeamUrl: process.env.moonbeamUrl || 'ws://127.0.0.1:9947',
+  moonriverUrl: process.env.moonbeamUrl || 'ws://127.0.0.1:9947',
+  westmintUrl: process.env.westmintUrl || 'ws://127.0.0.1:9948',
 };
 
-export default config;
\ No newline at end of file
+export default config;
modifiedtests/src/xcm/xcmOpal.test.tsdiffbeforeafterboth
--- a/tests/src/xcm/xcmOpal.test.ts
+++ b/tests/src/xcm/xcmOpal.test.ts
@@ -15,16 +15,14 @@
 // along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
 
 import {IKeyringPair} from '@polkadot/types/types';
+import config from '../config';
 import {itSub, expect, describeXcm, usingPlaygrounds, usingWestmintPlaygrounds, usingRelayPlaygrounds} from '../util/playgrounds';
 
 const STATEMINE_CHAIN = 1000;
 const UNIQUE_CHAIN = 2095;
-
-const RELAY_PORT = '9844';
-const STATEMINE_PORT = '9948';
 
-const relayUrl = 'ws://127.0.0.1:' + RELAY_PORT;
-const statemineUrl = 'ws://127.0.0.1:' + STATEMINE_PORT;
+const relayUrl = config.relayUrl;
+const westmintUrl = config.westmintUrl;
 
 const STATEMINE_PALLET_INSTANCE = 50;
 const ASSET_ID = 100;
@@ -65,7 +63,7 @@
       bob = privateKey('//Bob'); // funds donor
     });
 
-    await usingWestmintPlaygrounds(statemineUrl, async (helper) => {
+    await usingWestmintPlaygrounds(westmintUrl, async (helper) => {
       // 350.00 (three hundred fifty) DOT
       const fundingAmount = 3_500_000_000_000n; 
 
@@ -157,7 +155,7 @@
   });
 
   itSub('Should connect and send USDT from Westmint to Opal', async ({helper}) => {
-    await usingWestmintPlaygrounds(statemineUrl, async (helper) => {
+    await usingWestmintPlaygrounds(westmintUrl, async (helper) => {
       const dest = {
         V1: {
           parents: 1,
@@ -286,7 +284,7 @@
     balanceOpalFinal = await helper.balance.getSubstrate(alice.address);
     expect(balanceOpalAfter > balanceOpalFinal).to.be.true;
 
-    await usingWestmintPlaygrounds(statemineUrl, async (helper) => {
+    await usingWestmintPlaygrounds(westmintUrl, async (helper) => {
       await helper.wait.newBlocks(3);
       
       // The USDT token never paid fees. Its amount not changed from begin value.
modifiedtests/src/xcm/xcmQuartz.test.tsdiffbeforeafterboth
--- a/tests/src/xcm/xcmQuartz.test.ts
+++ b/tests/src/xcm/xcmQuartz.test.ts
@@ -16,20 +16,17 @@
 
 import {IKeyringPair} from '@polkadot/types/types';
 import {blake2AsHex} from '@polkadot/util-crypto';
+import config from '../config';
 import {XcmV2TraitsOutcome, XcmV2TraitsError} from '../interfaces';
 import {itSub, expect, describeXcm, usingPlaygrounds, usingKaruraPlaygrounds, usingRelayPlaygrounds, usingMoonriverPlaygrounds} from '../util/playgrounds';
 
 const QUARTZ_CHAIN = 2095;
 const KARURA_CHAIN = 2000;
 const MOONRIVER_CHAIN = 2023;
-
-const RELAY_PORT = 9844;
-const KARURA_PORT = 9946;
-const MOONRIVER_PORT = 9947;
 
-const relayUrl = 'ws://127.0.0.1:' + RELAY_PORT;
-const karuraUrl = 'ws://127.0.0.1:' + KARURA_PORT;
-const moonriverUrl = 'ws://127.0.0.1:' + MOONRIVER_PORT;
+const relayUrl = config.relayUrl;
+const karuraUrl = config.karuraUrl;
+const moonriverUrl = config.moonriverUrl;
 
 const KARURA_DECIMALS = 12;
 
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 {blake2AsHex} from '@polkadot/util-crypto';19import {XcmV2TraitsError, XcmV2TraitsOutcome} from '../interfaces';20import {itSub, expect, describeXcm, usingPlaygrounds, usingAcalaPlaygrounds, usingRelayPlaygrounds, usingMoonbeamPlaygrounds} from '../util/playgrounds';2122const UNIQUE_CHAIN = 2037;23const ACALA_CHAIN = 2000;24const MOONBEAM_CHAIN = 2004;2526const RELAY_PORT = 9844;27const ACALA_PORT = 9946;28const MOONBEAM_PORT = 9947;2930const relayUrl = 'ws://127.0.0.1:' + RELAY_PORT;31const acalaUrl = 'ws://127.0.0.1:' + ACALA_PORT;32const moonbeamUrl = 'ws://127.0.0.1:' + MOONBEAM_PORT;3334const ACALA_DECIMALS = 12;3536const TRANSFER_AMOUNT = 2000000000000000000000000n;3738describeXcm('[XCM] Integration test: Exchanging tokens with Acala', () => {39  let alice: IKeyringPair;40  let randomAccount: IKeyringPair;4142  let balanceUniqueTokenInit: bigint;43  let balanceUniqueTokenMiddle: bigint;44  let balanceUniqueTokenFinal: bigint;45  let balanceAcalaTokenInit: bigint;46  let balanceAcalaTokenMiddle: bigint;47  let balanceAcalaTokenFinal: bigint;48  let balanceUniqueForeignTokenInit: bigint;49  let balanceUniqueForeignTokenMiddle: bigint;50  let balanceUniqueForeignTokenFinal: bigint;5152  before(async () => {53    await usingPlaygrounds(async (helper, privateKey) => {54      alice = privateKey('//Alice');55      [randomAccount] = await helper.arrange.createAccounts([0n], alice);56    });5758    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {59      const destination = {60        V0: {61          X2: [62            'Parent',63            {64              Parachain: UNIQUE_CHAIN,65            },66          ],67        },68      };6970      const metadata = {71        name: 'UNQ',72        symbol: 'UNQ',73        decimals: 18,74        minimalBalance: 1n,75      };7677      await helper.getSudo().assetRegistry.registerForeignAsset(alice, destination, metadata);78      await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n);79      balanceAcalaTokenInit = await helper.balance.getSubstrate(randomAccount.address);80      balanceUniqueForeignTokenInit = await helper.tokens.accounts(randomAccount.address, {ForeignAsset: 0});81    });8283    await usingPlaygrounds(async (helper) => {84      await helper.balance.transferToSubstrate(alice, randomAccount.address, 10n * TRANSFER_AMOUNT);85      balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccount.address);86    });87  });8889  itSub('Should connect and send UNQ to Acala', async ({helper}) => {9091    const destination = {92      V0: {93        X2: [94          'Parent',95          {96            Parachain: ACALA_CHAIN,97          },98        ],99      },100    };101102    const beneficiary = {103      V0: {104        X1: {105          AccountId32: {106            network: 'Any',107            id: randomAccount.addressRaw,108          },109        },110      },111    };112113    const assets = {114      V1: [115        {116          id: {117            Concrete: {118              parents: 0,119              interior: 'Here',120            },121          },122          fun: {123            Fungible: TRANSFER_AMOUNT,124          },125        },126      ],127    };128129    const feeAssetItem = 0;130    const weightLimit = 5000000000;131132    await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, weightLimit);133134    balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);135136    const unqFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;137    console.log('[Unique -> Acala] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));138    expect(unqFees > 0n).to.be.true;139140    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {141      await helper.wait.newBlocks(3);142143      balanceUniqueForeignTokenMiddle = await helper.tokens.accounts(randomAccount.address, {ForeignAsset: 0});144      balanceAcalaTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);145146      const acaFees = balanceAcalaTokenInit - balanceAcalaTokenMiddle;147      const unqIncomeTransfer = balanceUniqueForeignTokenMiddle - balanceUniqueForeignTokenInit;148149      console.log(150        '[Unique -> Acala] transaction fees on Acala: %s ACA',151        helper.util.bigIntToDecimals(acaFees, ACALA_DECIMALS),152      );153      console.log('[Unique -> Acala] income %s UNQ', helper.util.bigIntToDecimals(unqIncomeTransfer));154      expect(acaFees == 0n).to.be.true;155      expect(unqIncomeTransfer == TRANSFER_AMOUNT).to.be.true;156    });157  });158159  itSub('Should connect to Acala and send UNQ back', async ({helper}) => {160    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {161      const destination = {162        V1: {163          parents: 1,164          interior: {165            X2: [166              {Parachain: UNIQUE_CHAIN},167              {168                AccountId32: {169                  network: 'Any',170                  id: randomAccount.addressRaw,171                },172              },173            ],174          },175        },176      };177178      const id = {179        ForeignAsset: 0,180      };181182      const destWeight = 50000000;183184      await helper.xTokens.transfer(randomAccount, id, TRANSFER_AMOUNT, destination, destWeight);185186      balanceAcalaTokenFinal = await helper.balance.getSubstrate(randomAccount.address);187      balanceUniqueForeignTokenFinal = await helper.tokens.accounts(randomAccount.address, id);188189      const acaFees = balanceAcalaTokenMiddle - balanceAcalaTokenFinal;190      const unqOutcomeTransfer = balanceUniqueForeignTokenMiddle - balanceUniqueForeignTokenFinal;191192      console.log(193        '[Acala -> Unique] transaction fees on Acala: %s ACA',194        helper.util.bigIntToDecimals(acaFees, ACALA_DECIMALS),195      );196      console.log('[Acala -> Unique] outcome %s UNQ', helper.util.bigIntToDecimals(unqOutcomeTransfer));197198      expect(acaFees > 0).to.be.true;199      expect(unqOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;200    });201202    await helper.wait.newBlocks(3);203204    balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccount.address);205    const actuallyDelivered = balanceUniqueTokenFinal - balanceUniqueTokenMiddle;206    expect(actuallyDelivered > 0).to.be.true;207208    console.log('[Acala -> Unique] actually delivered %s UNQ', helper.util.bigIntToDecimals(actuallyDelivered));209210    const unqFees = TRANSFER_AMOUNT - actuallyDelivered;211    console.log('[Acala -> Unique] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));212    expect(unqFees == 0n).to.be.true;213  });214});215216// These tests are relevant only when the foreign asset pallet is disabled217describeXcm('[XCM] Integration test: Unique rejects non-native tokens', () => {218  let alice: IKeyringPair;219220  before(async () => {221    await usingPlaygrounds(async (_helper, privateKey) => {222      alice = privateKey('//Alice');223    });224  });225226  itSub('Unique rejects tokens from the Relay', async ({helper}) => {227    await usingRelayPlaygrounds(relayUrl, async (helper) => {228      const destination = {229        V1: {230          parents: 0,231          interior: {X1: {232            Parachain: UNIQUE_CHAIN,233          },234          },235        }};236237      const beneficiary = {238        V1: {239          parents: 0,240          interior: {X1: {241            AccountId32: {242              network: 'Any',243              id: alice.addressRaw,244            },245          }},246        },247      };248249      const assets = {250        V1: [251          {252            id: {253              Concrete: {254                parents: 0,255                interior: 'Here',256              },257            },258            fun: {259              Fungible: 50_000_000_000_000_000n,260            },261          },262        ],263      };264265      const feeAssetItem = 0;266      const weightLimit = 5_000_000_000;267268      await helper.xcm.limitedReserveTransferAssets(alice, destination, beneficiary, assets, feeAssetItem, weightLimit);269    });270271    const maxWaitBlocks = 3;272273    const dmpQueueExecutedDownward = await helper.wait.event(maxWaitBlocks, 'dmpQueue', 'ExecutedDownward');274275    expect(276      dmpQueueExecutedDownward != null,277      '[Relay] dmpQueue.ExecutedDownward event is expected',278    ).to.be.true;279280    const event = dmpQueueExecutedDownward!.event;281    const outcome = event.data[1] as XcmV2TraitsOutcome;282283    expect(284      outcome.isIncomplete,285      '[Relay] The outcome of the XCM should be `Incomplete`',286    ).to.be.true;287288    const incomplete = outcome.asIncomplete;289    expect(290      incomplete[1].toString() == 'AssetNotFound',291      '[Relay] The XCM error should be `AssetNotFound`',292    ).to.be.true;293  });294295  itSub('Unique rejects ACA tokens from Acala', async ({helper}) => {296    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {297      const destination = {298        V1: {299          parents: 1,300          interior: {301            X2: [302              {Parachain: UNIQUE_CHAIN},303              {304                AccountId32: {305                  network: 'Any',306                  id: alice.addressRaw,307                },308              },309            ],310          },311        },312      };313314      const id = {315        Token: 'ACA',316      };317318      const destWeight = 50000000;319320      await helper.xTokens.transfer(alice, id, 100_000_000_000n, destination, destWeight);321    });322323    const maxWaitBlocks = 3;324325    const xcmpQueueFailEvent = await helper.wait.event(maxWaitBlocks, 'xcmpQueue', 'Fail');326327    expect(328      xcmpQueueFailEvent != null,329      '[Acala] xcmpQueue.FailEvent event is expected',330    ).to.be.true;331332    const event = xcmpQueueFailEvent!.event;333    const outcome = event.data[1] as XcmV2TraitsError;334335    expect(336      outcome.isUntrustedReserveLocation,337      '[Acala] The XCM error should be `UntrustedReserveLocation`',338    ).to.be.true;339  });340});341342describeXcm('[XCM] Integration test: Exchanging UNQ with Moonbeam', () => {343344  // Unique constants345  let uniqueDonor: IKeyringPair;346  let uniqueAssetLocation;347348  let randomAccountUnique: IKeyringPair;349  let randomAccountMoonbeam: IKeyringPair;350351  // Moonbeam constants352  let assetId: string;353354  const councilVotingThreshold = 2;355  const technicalCommitteeThreshold = 2;356  const votingPeriod = 3;357  const delayPeriod = 0;358359  const uniqueAssetMetadata = {360    name: 'xcUnique',361    symbol: 'xcUNQ',362    decimals: 18,363    isFrozen: false,364    minimalBalance: 1n,365  };366367  let balanceUniqueTokenInit: bigint;368  let balanceUniqueTokenMiddle: bigint;369  let balanceUniqueTokenFinal: bigint;370  let balanceForeignUnqTokenInit: bigint;371  let balanceForeignUnqTokenMiddle: bigint;372  let balanceForeignUnqTokenFinal: bigint;373  let balanceGlmrTokenInit: bigint;374  let balanceGlmrTokenMiddle: bigint;375  let balanceGlmrTokenFinal: bigint;376377  before(async () => {378    await usingPlaygrounds(async (helper, privateKey) => {379      uniqueDonor = privateKey('//Alice');380      [randomAccountUnique] = await helper.arrange.createAccounts([0n], uniqueDonor);381382      balanceForeignUnqTokenInit = 0n;383    });384385    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {386      const alithAccount = helper.account.alithAccount();387      const baltatharAccount = helper.account.baltatharAccount();388      const dorothyAccount = helper.account.dorothyAccount();389390      randomAccountMoonbeam = helper.account.create();391392      // >>> Sponsoring Dorothy >>>393      console.log('Sponsoring Dorothy.......');394      await helper.balance.transferToEthereum(alithAccount, dorothyAccount.address, 11_000_000_000_000_000_000n);395      console.log('Sponsoring Dorothy.......DONE');396      // <<< Sponsoring Dorothy <<<397398      uniqueAssetLocation = {399        XCM: {400          parents: 1,401          interior: {X1: {Parachain: UNIQUE_CHAIN}},402        },403      };404      const existentialDeposit = 1n;405      const isSufficient = true;406      const unitsPerSecond = 1n;407      const numAssetsWeightHint = 0;408409      const encodedProposal = helper.assetManager.makeRegisterForeignAssetProposal({410        location: uniqueAssetLocation,411        metadata: uniqueAssetMetadata,412        existentialDeposit,413        isSufficient,414        unitsPerSecond,415        numAssetsWeightHint,416      });417      const proposalHash = blake2AsHex(encodedProposal);418419      console.log('Encoded proposal for registerForeignAsset & setAssetUnitsPerSecond is %s', encodedProposal);420      console.log('Encoded length %d', encodedProposal.length);421      console.log('Encoded proposal hash for batch utility after schedule is %s', proposalHash);422423      // >>> Note motion preimage >>>424      console.log('Note motion preimage.......');425      await helper.democracy.notePreimage(baltatharAccount, encodedProposal);426      console.log('Note motion preimage.......DONE');427      // <<< Note motion preimage <<<428429      // >>> Propose external motion through council >>>430      console.log('Propose external motion through council.......');431      const externalMotion = helper.democracy.externalProposeMajority(proposalHash);432      const encodedMotion = externalMotion?.method.toHex() || '';433      const motionHash = blake2AsHex(encodedMotion);434      console.log('Motion hash is %s', motionHash);435436      await helper.collective.council.propose(baltatharAccount, councilVotingThreshold, externalMotion, externalMotion.encodedLength);437438      const councilProposalIdx = await helper.collective.council.proposalCount() - 1;439      await helper.collective.council.vote(dorothyAccount, motionHash, councilProposalIdx, true);440      await helper.collective.council.vote(baltatharAccount, motionHash, councilProposalIdx, true);441442      await helper.collective.council.close(dorothyAccount, motionHash, councilProposalIdx, 1_000_000_000, externalMotion.encodedLength);443      console.log('Propose external motion through council.......DONE');444      // <<< Propose external motion through council <<<445446      // >>> Fast track proposal through technical committee >>>447      console.log('Fast track proposal through technical committee.......');448      const fastTrack = helper.democracy.fastTrack(proposalHash, votingPeriod, delayPeriod);449      const encodedFastTrack = fastTrack?.method.toHex() || '';450      const fastTrackHash = blake2AsHex(encodedFastTrack);451      console.log('FastTrack hash is %s', fastTrackHash);452453      await helper.collective.techCommittee.propose(alithAccount, technicalCommitteeThreshold, fastTrack, fastTrack.encodedLength);454455      const techProposalIdx = await helper.collective.techCommittee.proposalCount() - 1;456      await helper.collective.techCommittee.vote(baltatharAccount, fastTrackHash, techProposalIdx, true);457      await helper.collective.techCommittee.vote(alithAccount, fastTrackHash, techProposalIdx, true);458459      await helper.collective.techCommittee.close(baltatharAccount, fastTrackHash, techProposalIdx, 1_000_000_000, fastTrack.encodedLength);460      console.log('Fast track proposal through technical committee.......DONE');461      // <<< Fast track proposal through technical committee <<<462463      // >>> Referendum voting >>>464      console.log('Referendum voting.......');465      await helper.democracy.referendumVote(dorothyAccount, 0, {466        balance: 10_000_000_000_000_000_000n,467        vote: {aye: true, conviction: 1},468      });469      console.log('Referendum voting.......DONE');470      // <<< Referendum voting <<<471472      // >>> Acquire Unique AssetId Info on Moonbeam >>>473      console.log('Acquire Unique AssetId Info on Moonbeam.......');474475      // Wait for the democracy execute476      await helper.wait.newBlocks(5);477478      assetId = (await helper.assetManager.assetTypeId(uniqueAssetLocation)).toString();479480      console.log('UNQ asset ID is %s', assetId);481      console.log('Acquire Unique AssetId Info on Moonbeam.......DONE');482      // >>> Acquire Unique AssetId Info on Moonbeam >>>483484      // >>> Sponsoring random Account >>>485      console.log('Sponsoring random Account.......');486      await helper.balance.transferToEthereum(baltatharAccount, randomAccountMoonbeam.address, 11_000_000_000_000_000_000n);487      console.log('Sponsoring random Account.......DONE');488      // <<< Sponsoring random Account <<<489490      balanceGlmrTokenInit = await helper.balance.getEthereum(randomAccountMoonbeam.address);491    });492493    await usingPlaygrounds(async (helper) => {494      await helper.balance.transferToSubstrate(uniqueDonor, randomAccountUnique.address, 10n * TRANSFER_AMOUNT);495      balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccountUnique.address);496    });497  });498499  itSub('Should connect and send UNQ to Moonbeam', async ({helper}) => {500    const currencyId = {501      NativeAssetId: 'Here',502    };503    const dest = {504      V1: {505        parents: 1,506        interior: {507          X2: [508            {Parachain: MOONBEAM_CHAIN},509            {AccountKey20: {network: 'Any', key: randomAccountMoonbeam.address}},510          ],511        },512      },513    };514    const amount = TRANSFER_AMOUNT;515    const destWeight = 850000000;516517    await helper.xTokens.transfer(randomAccountUnique, currencyId, amount, dest, destWeight);518519    balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccountUnique.address);520    expect(balanceUniqueTokenMiddle < balanceUniqueTokenInit).to.be.true;521522    const transactionFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;523    console.log('[Unique -> Moonbeam] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(transactionFees));524    expect(transactionFees > 0).to.be.true;525526    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {527      await helper.wait.newBlocks(3);528529      balanceGlmrTokenMiddle = await helper.balance.getEthereum(randomAccountMoonbeam.address);530531      const glmrFees = balanceGlmrTokenInit - balanceGlmrTokenMiddle;532      console.log('[Unique -> Moonbeam] transaction fees on Moonbeam: %s GLMR', helper.util.bigIntToDecimals(glmrFees));533      expect(glmrFees == 0n).to.be.true;534535      balanceForeignUnqTokenMiddle = (await helper.assets.account(assetId, randomAccountMoonbeam.address))!;536537      const unqIncomeTransfer = balanceForeignUnqTokenMiddle - balanceForeignUnqTokenInit;538      console.log('[Unique -> Moonbeam] income %s UNQ', helper.util.bigIntToDecimals(unqIncomeTransfer));539      expect(unqIncomeTransfer == TRANSFER_AMOUNT).to.be.true;540    });541  });542543  itSub('Should connect to Moonbeam and send UNQ back', async ({helper}) => {544    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {545      const asset = {546        V1: {547          id: {548            Concrete: {549              parents: 1,550              interior: {551                X1: {Parachain: UNIQUE_CHAIN},552              },553            },554          },555          fun: {556            Fungible: TRANSFER_AMOUNT,557          },558        },559      };560      const destination = {561        V1: {562          parents: 1,563          interior: {564            X2: [565              {Parachain: UNIQUE_CHAIN},566              {AccountId32: {network: 'Any', id: randomAccountUnique.addressRaw}},567            ],568          },569        },570      };571      const destWeight = 50000000;572573      await helper.xTokens.transferMultiasset(randomAccountMoonbeam, asset, destination, destWeight);574575      balanceGlmrTokenFinal = await helper.balance.getEthereum(randomAccountMoonbeam.address);576577      const glmrFees = balanceGlmrTokenMiddle - balanceGlmrTokenFinal;578      console.log('[Moonbeam -> Unique] transaction fees on Moonbeam: %s GLMR', helper.util.bigIntToDecimals(glmrFees));579      expect(glmrFees > 0).to.be.true;580581      const unqRandomAccountAsset = await helper.assets.account(assetId, randomAccountMoonbeam.address);582583      expect(unqRandomAccountAsset).to.be.null;584      585      balanceForeignUnqTokenFinal = 0n;586587      const unqOutcomeTransfer = balanceForeignUnqTokenMiddle - balanceForeignUnqTokenFinal;588      console.log('[Unique -> Moonbeam] outcome %s UNQ', helper.util.bigIntToDecimals(unqOutcomeTransfer));589      expect(unqOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;590    });591592    await helper.wait.newBlocks(3);593594    balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccountUnique.address);595    const actuallyDelivered = balanceUniqueTokenFinal - balanceUniqueTokenMiddle;596    expect(actuallyDelivered > 0).to.be.true;597598    console.log('[Moonbeam -> Unique] actually delivered %s UNQ', helper.util.bigIntToDecimals(actuallyDelivered));599600    const unqFees = TRANSFER_AMOUNT - actuallyDelivered;601    console.log('[Moonbeam -> Unique] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));602    expect(unqFees == 0n).to.be.true;603  });604});
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 {blake2AsHex} from '@polkadot/util-crypto';19import config from '../config';20import {XcmV2TraitsError, XcmV2TraitsOutcome} from '../interfaces';21import {itSub, expect, describeXcm, usingPlaygrounds, usingAcalaPlaygrounds, usingRelayPlaygrounds, usingMoonbeamPlaygrounds} from '../util/playgrounds';2223const UNIQUE_CHAIN = 2037;24const ACALA_CHAIN = 2000;25const MOONBEAM_CHAIN = 2004;2627const relayUrl = config.relayUrl;28const acalaUrl = config.acalaUrl;29const moonbeamUrl = config.moonbeamUrl;3031const ACALA_DECIMALS = 12;3233const TRANSFER_AMOUNT = 2000000000000000000000000n;3435describeXcm('[XCM] Integration test: Exchanging tokens with Acala', () => {36  let alice: IKeyringPair;37  let randomAccount: IKeyringPair;3839  let balanceUniqueTokenInit: bigint;40  let balanceUniqueTokenMiddle: bigint;41  let balanceUniqueTokenFinal: bigint;42  let balanceAcalaTokenInit: bigint;43  let balanceAcalaTokenMiddle: bigint;44  let balanceAcalaTokenFinal: bigint;45  let balanceUniqueForeignTokenInit: bigint;46  let balanceUniqueForeignTokenMiddle: bigint;47  let balanceUniqueForeignTokenFinal: bigint;4849  before(async () => {50    await usingPlaygrounds(async (helper, privateKey) => {51      alice = privateKey('//Alice');52      [randomAccount] = await helper.arrange.createAccounts([0n], alice);53    });5455    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {56      const destination = {57        V0: {58          X2: [59            'Parent',60            {61              Parachain: UNIQUE_CHAIN,62            },63          ],64        },65      };6667      const metadata = {68        name: 'UNQ',69        symbol: 'UNQ',70        decimals: 18,71        minimalBalance: 1n,72      };7374      await helper.getSudo().assetRegistry.registerForeignAsset(alice, destination, metadata);75      await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n);76      balanceAcalaTokenInit = await helper.balance.getSubstrate(randomAccount.address);77      balanceUniqueForeignTokenInit = await helper.tokens.accounts(randomAccount.address, {ForeignAsset: 0});78    });7980    await usingPlaygrounds(async (helper) => {81      await helper.balance.transferToSubstrate(alice, randomAccount.address, 10n * TRANSFER_AMOUNT);82      balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccount.address);83    });84  });8586  itSub('Should connect and send UNQ to Acala', async ({helper}) => {8788    const destination = {89      V0: {90        X2: [91          'Parent',92          {93            Parachain: ACALA_CHAIN,94          },95        ],96      },97    };9899    const beneficiary = {100      V0: {101        X1: {102          AccountId32: {103            network: 'Any',104            id: randomAccount.addressRaw,105          },106        },107      },108    };109110    const assets = {111      V1: [112        {113          id: {114            Concrete: {115              parents: 0,116              interior: 'Here',117            },118          },119          fun: {120            Fungible: TRANSFER_AMOUNT,121          },122        },123      ],124    };125126    const feeAssetItem = 0;127    const weightLimit = 5000000000;128129    await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, weightLimit);130131    balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);132133    const unqFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;134    console.log('[Unique -> Acala] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));135    expect(unqFees > 0n).to.be.true;136137    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {138      await helper.wait.newBlocks(3);139140      balanceUniqueForeignTokenMiddle = await helper.tokens.accounts(randomAccount.address, {ForeignAsset: 0});141      balanceAcalaTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);142143      const acaFees = balanceAcalaTokenInit - balanceAcalaTokenMiddle;144      const unqIncomeTransfer = balanceUniqueForeignTokenMiddle - balanceUniqueForeignTokenInit;145146      console.log(147        '[Unique -> Acala] transaction fees on Acala: %s ACA',148        helper.util.bigIntToDecimals(acaFees, ACALA_DECIMALS),149      );150      console.log('[Unique -> Acala] income %s UNQ', helper.util.bigIntToDecimals(unqIncomeTransfer));151      expect(acaFees == 0n).to.be.true;152      expect(unqIncomeTransfer == TRANSFER_AMOUNT).to.be.true;153    });154  });155156  itSub('Should connect to Acala and send UNQ back', async ({helper}) => {157    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {158      const destination = {159        V1: {160          parents: 1,161          interior: {162            X2: [163              {Parachain: UNIQUE_CHAIN},164              {165                AccountId32: {166                  network: 'Any',167                  id: randomAccount.addressRaw,168                },169              },170            ],171          },172        },173      };174175      const id = {176        ForeignAsset: 0,177      };178179      const destWeight = 50000000;180181      await helper.xTokens.transfer(randomAccount, id, TRANSFER_AMOUNT, destination, destWeight);182183      balanceAcalaTokenFinal = await helper.balance.getSubstrate(randomAccount.address);184      balanceUniqueForeignTokenFinal = await helper.tokens.accounts(randomAccount.address, id);185186      const acaFees = balanceAcalaTokenMiddle - balanceAcalaTokenFinal;187      const unqOutcomeTransfer = balanceUniqueForeignTokenMiddle - balanceUniqueForeignTokenFinal;188189      console.log(190        '[Acala -> Unique] transaction fees on Acala: %s ACA',191        helper.util.bigIntToDecimals(acaFees, ACALA_DECIMALS),192      );193      console.log('[Acala -> Unique] outcome %s UNQ', helper.util.bigIntToDecimals(unqOutcomeTransfer));194195      expect(acaFees > 0).to.be.true;196      expect(unqOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;197    });198199    await helper.wait.newBlocks(3);200201    balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccount.address);202    const actuallyDelivered = balanceUniqueTokenFinal - balanceUniqueTokenMiddle;203    expect(actuallyDelivered > 0).to.be.true;204205    console.log('[Acala -> Unique] actually delivered %s UNQ', helper.util.bigIntToDecimals(actuallyDelivered));206207    const unqFees = TRANSFER_AMOUNT - actuallyDelivered;208    console.log('[Acala -> Unique] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));209    expect(unqFees == 0n).to.be.true;210  });211});212213// These tests are relevant only when the foreign asset pallet is disabled214describeXcm('[XCM] Integration test: Unique rejects non-native tokens', () => {215  let alice: IKeyringPair;216217  before(async () => {218    await usingPlaygrounds(async (_helper, privateKey) => {219      alice = privateKey('//Alice');220    });221  });222223  itSub('Unique rejects tokens from the Relay', async ({helper}) => {224    await usingRelayPlaygrounds(relayUrl, async (helper) => {225      const destination = {226        V1: {227          parents: 0,228          interior: {X1: {229            Parachain: UNIQUE_CHAIN,230          },231          },232        }};233234      const beneficiary = {235        V1: {236          parents: 0,237          interior: {X1: {238            AccountId32: {239              network: 'Any',240              id: alice.addressRaw,241            },242          }},243        },244      };245246      const assets = {247        V1: [248          {249            id: {250              Concrete: {251                parents: 0,252                interior: 'Here',253              },254            },255            fun: {256              Fungible: 50_000_000_000_000_000n,257            },258          },259        ],260      };261262      const feeAssetItem = 0;263      const weightLimit = 5_000_000_000;264265      await helper.xcm.limitedReserveTransferAssets(alice, destination, beneficiary, assets, feeAssetItem, weightLimit);266    });267268    const maxWaitBlocks = 3;269270    const dmpQueueExecutedDownward = await helper.wait.event(maxWaitBlocks, 'dmpQueue', 'ExecutedDownward');271272    expect(273      dmpQueueExecutedDownward != null,274      '[Relay] dmpQueue.ExecutedDownward event is expected',275    ).to.be.true;276277    const event = dmpQueueExecutedDownward!.event;278    const outcome = event.data[1] as XcmV2TraitsOutcome;279280    expect(281      outcome.isIncomplete,282      '[Relay] The outcome of the XCM should be `Incomplete`',283    ).to.be.true;284285    const incomplete = outcome.asIncomplete;286    expect(287      incomplete[1].toString() == 'AssetNotFound',288      '[Relay] The XCM error should be `AssetNotFound`',289    ).to.be.true;290  });291292  itSub('Unique rejects ACA tokens from Acala', async ({helper}) => {293    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {294      const destination = {295        V1: {296          parents: 1,297          interior: {298            X2: [299              {Parachain: UNIQUE_CHAIN},300              {301                AccountId32: {302                  network: 'Any',303                  id: alice.addressRaw,304                },305              },306            ],307          },308        },309      };310311      const id = {312        Token: 'ACA',313      };314315      const destWeight = 50000000;316317      await helper.xTokens.transfer(alice, id, 100_000_000_000n, destination, destWeight);318    });319320    const maxWaitBlocks = 3;321322    const xcmpQueueFailEvent = await helper.wait.event(maxWaitBlocks, 'xcmpQueue', 'Fail');323324    expect(325      xcmpQueueFailEvent != null,326      '[Acala] xcmpQueue.FailEvent event is expected',327    ).to.be.true;328329    const event = xcmpQueueFailEvent!.event;330    const outcome = event.data[1] as XcmV2TraitsError;331332    expect(333      outcome.isUntrustedReserveLocation,334      '[Acala] The XCM error should be `UntrustedReserveLocation`',335    ).to.be.true;336  });337});338339describeXcm('[XCM] Integration test: Exchanging UNQ with Moonbeam', () => {340341  // Unique constants342  let uniqueDonor: IKeyringPair;343  let uniqueAssetLocation;344345  let randomAccountUnique: IKeyringPair;346  let randomAccountMoonbeam: IKeyringPair;347348  // Moonbeam constants349  let assetId: string;350351  const councilVotingThreshold = 2;352  const technicalCommitteeThreshold = 2;353  const votingPeriod = 3;354  const delayPeriod = 0;355356  const uniqueAssetMetadata = {357    name: 'xcUnique',358    symbol: 'xcUNQ',359    decimals: 18,360    isFrozen: false,361    minimalBalance: 1n,362  };363364  let balanceUniqueTokenInit: bigint;365  let balanceUniqueTokenMiddle: bigint;366  let balanceUniqueTokenFinal: bigint;367  let balanceForeignUnqTokenInit: bigint;368  let balanceForeignUnqTokenMiddle: bigint;369  let balanceForeignUnqTokenFinal: bigint;370  let balanceGlmrTokenInit: bigint;371  let balanceGlmrTokenMiddle: bigint;372  let balanceGlmrTokenFinal: bigint;373374  before(async () => {375    await usingPlaygrounds(async (helper, privateKey) => {376      uniqueDonor = privateKey('//Alice');377      [randomAccountUnique] = await helper.arrange.createAccounts([0n], uniqueDonor);378379      balanceForeignUnqTokenInit = 0n;380    });381382    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {383      const alithAccount = helper.account.alithAccount();384      const baltatharAccount = helper.account.baltatharAccount();385      const dorothyAccount = helper.account.dorothyAccount();386387      randomAccountMoonbeam = helper.account.create();388389      // >>> Sponsoring Dorothy >>>390      console.log('Sponsoring Dorothy.......');391      await helper.balance.transferToEthereum(alithAccount, dorothyAccount.address, 11_000_000_000_000_000_000n);392      console.log('Sponsoring Dorothy.......DONE');393      // <<< Sponsoring Dorothy <<<394395      uniqueAssetLocation = {396        XCM: {397          parents: 1,398          interior: {X1: {Parachain: UNIQUE_CHAIN}},399        },400      };401      const existentialDeposit = 1n;402      const isSufficient = true;403      const unitsPerSecond = 1n;404      const numAssetsWeightHint = 0;405406      const encodedProposal = helper.assetManager.makeRegisterForeignAssetProposal({407        location: uniqueAssetLocation,408        metadata: uniqueAssetMetadata,409        existentialDeposit,410        isSufficient,411        unitsPerSecond,412        numAssetsWeightHint,413      });414      const proposalHash = blake2AsHex(encodedProposal);415416      console.log('Encoded proposal for registerForeignAsset & setAssetUnitsPerSecond is %s', encodedProposal);417      console.log('Encoded length %d', encodedProposal.length);418      console.log('Encoded proposal hash for batch utility after schedule is %s', proposalHash);419420      // >>> Note motion preimage >>>421      console.log('Note motion preimage.......');422      await helper.democracy.notePreimage(baltatharAccount, encodedProposal);423      console.log('Note motion preimage.......DONE');424      // <<< Note motion preimage <<<425426      // >>> Propose external motion through council >>>427      console.log('Propose external motion through council.......');428      const externalMotion = helper.democracy.externalProposeMajority(proposalHash);429      const encodedMotion = externalMotion?.method.toHex() || '';430      const motionHash = blake2AsHex(encodedMotion);431      console.log('Motion hash is %s', motionHash);432433      await helper.collective.council.propose(baltatharAccount, councilVotingThreshold, externalMotion, externalMotion.encodedLength);434435      const councilProposalIdx = await helper.collective.council.proposalCount() - 1;436      await helper.collective.council.vote(dorothyAccount, motionHash, councilProposalIdx, true);437      await helper.collective.council.vote(baltatharAccount, motionHash, councilProposalIdx, true);438439      await helper.collective.council.close(dorothyAccount, motionHash, councilProposalIdx, 1_000_000_000, externalMotion.encodedLength);440      console.log('Propose external motion through council.......DONE');441      // <<< Propose external motion through council <<<442443      // >>> Fast track proposal through technical committee >>>444      console.log('Fast track proposal through technical committee.......');445      const fastTrack = helper.democracy.fastTrack(proposalHash, votingPeriod, delayPeriod);446      const encodedFastTrack = fastTrack?.method.toHex() || '';447      const fastTrackHash = blake2AsHex(encodedFastTrack);448      console.log('FastTrack hash is %s', fastTrackHash);449450      await helper.collective.techCommittee.propose(alithAccount, technicalCommitteeThreshold, fastTrack, fastTrack.encodedLength);451452      const techProposalIdx = await helper.collective.techCommittee.proposalCount() - 1;453      await helper.collective.techCommittee.vote(baltatharAccount, fastTrackHash, techProposalIdx, true);454      await helper.collective.techCommittee.vote(alithAccount, fastTrackHash, techProposalIdx, true);455456      await helper.collective.techCommittee.close(baltatharAccount, fastTrackHash, techProposalIdx, 1_000_000_000, fastTrack.encodedLength);457      console.log('Fast track proposal through technical committee.......DONE');458      // <<< Fast track proposal through technical committee <<<459460      // >>> Referendum voting >>>461      console.log('Referendum voting.......');462      await helper.democracy.referendumVote(dorothyAccount, 0, {463        balance: 10_000_000_000_000_000_000n,464        vote: {aye: true, conviction: 1},465      });466      console.log('Referendum voting.......DONE');467      // <<< Referendum voting <<<468469      // >>> Acquire Unique AssetId Info on Moonbeam >>>470      console.log('Acquire Unique AssetId Info on Moonbeam.......');471472      // Wait for the democracy execute473      await helper.wait.newBlocks(5);474475      assetId = (await helper.assetManager.assetTypeId(uniqueAssetLocation)).toString();476477      console.log('UNQ asset ID is %s', assetId);478      console.log('Acquire Unique AssetId Info on Moonbeam.......DONE');479      // >>> Acquire Unique AssetId Info on Moonbeam >>>480481      // >>> Sponsoring random Account >>>482      console.log('Sponsoring random Account.......');483      await helper.balance.transferToEthereum(baltatharAccount, randomAccountMoonbeam.address, 11_000_000_000_000_000_000n);484      console.log('Sponsoring random Account.......DONE');485      // <<< Sponsoring random Account <<<486487      balanceGlmrTokenInit = await helper.balance.getEthereum(randomAccountMoonbeam.address);488    });489490    await usingPlaygrounds(async (helper) => {491      await helper.balance.transferToSubstrate(uniqueDonor, randomAccountUnique.address, 10n * TRANSFER_AMOUNT);492      balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccountUnique.address);493    });494  });495496  itSub('Should connect and send UNQ to Moonbeam', async ({helper}) => {497    const currencyId = {498      NativeAssetId: 'Here',499    };500    const dest = {501      V1: {502        parents: 1,503        interior: {504          X2: [505            {Parachain: MOONBEAM_CHAIN},506            {AccountKey20: {network: 'Any', key: randomAccountMoonbeam.address}},507          ],508        },509      },510    };511    const amount = TRANSFER_AMOUNT;512    const destWeight = 850000000;513514    await helper.xTokens.transfer(randomAccountUnique, currencyId, amount, dest, destWeight);515516    balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccountUnique.address);517    expect(balanceUniqueTokenMiddle < balanceUniqueTokenInit).to.be.true;518519    const transactionFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;520    console.log('[Unique -> Moonbeam] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(transactionFees));521    expect(transactionFees > 0).to.be.true;522523    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {524      await helper.wait.newBlocks(3);525526      balanceGlmrTokenMiddle = await helper.balance.getEthereum(randomAccountMoonbeam.address);527528      const glmrFees = balanceGlmrTokenInit - balanceGlmrTokenMiddle;529      console.log('[Unique -> Moonbeam] transaction fees on Moonbeam: %s GLMR', helper.util.bigIntToDecimals(glmrFees));530      expect(glmrFees == 0n).to.be.true;531532      balanceForeignUnqTokenMiddle = (await helper.assets.account(assetId, randomAccountMoonbeam.address))!;533534      const unqIncomeTransfer = balanceForeignUnqTokenMiddle - balanceForeignUnqTokenInit;535      console.log('[Unique -> Moonbeam] income %s UNQ', helper.util.bigIntToDecimals(unqIncomeTransfer));536      expect(unqIncomeTransfer == TRANSFER_AMOUNT).to.be.true;537    });538  });539540  itSub('Should connect to Moonbeam and send UNQ back', async ({helper}) => {541    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {542      const asset = {543        V1: {544          id: {545            Concrete: {546              parents: 1,547              interior: {548                X1: {Parachain: UNIQUE_CHAIN},549              },550            },551          },552          fun: {553            Fungible: TRANSFER_AMOUNT,554          },555        },556      };557      const destination = {558        V1: {559          parents: 1,560          interior: {561            X2: [562              {Parachain: UNIQUE_CHAIN},563              {AccountId32: {network: 'Any', id: randomAccountUnique.addressRaw}},564            ],565          },566        },567      };568      const destWeight = 50000000;569570      await helper.xTokens.transferMultiasset(randomAccountMoonbeam, asset, destination, destWeight);571572      balanceGlmrTokenFinal = await helper.balance.getEthereum(randomAccountMoonbeam.address);573574      const glmrFees = balanceGlmrTokenMiddle - balanceGlmrTokenFinal;575      console.log('[Moonbeam -> Unique] transaction fees on Moonbeam: %s GLMR', helper.util.bigIntToDecimals(glmrFees));576      expect(glmrFees > 0).to.be.true;577578      const unqRandomAccountAsset = await helper.assets.account(assetId, randomAccountMoonbeam.address);579580      expect(unqRandomAccountAsset).to.be.null;581      582      balanceForeignUnqTokenFinal = 0n;583584      const unqOutcomeTransfer = balanceForeignUnqTokenMiddle - balanceForeignUnqTokenFinal;585      console.log('[Unique -> Moonbeam] outcome %s UNQ', helper.util.bigIntToDecimals(unqOutcomeTransfer));586      expect(unqOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;587    });588589    await helper.wait.newBlocks(3);590591    balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccountUnique.address);592    const actuallyDelivered = balanceUniqueTokenFinal - balanceUniqueTokenMiddle;593    expect(actuallyDelivered > 0).to.be.true;594595    console.log('[Moonbeam -> Unique] actually delivered %s UNQ', helper.util.bigIntToDecimals(actuallyDelivered));596597    const unqFees = TRANSFER_AMOUNT - actuallyDelivered;598    console.log('[Moonbeam -> Unique] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));599    expect(unqFees == 0n).to.be.true;600  });601});