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

difftreelog

fix parse bitint to decimals

Daniel Shiposha2022-09-06parent: #1a6fe8b.patch.diff
in: master

4 files changed

modifiedtests/src/util/helpers.tsdiffbeforeafterboth
--- a/tests/src/util/helpers.ts
+++ b/tests/src/util/helpers.ts
@@ -103,6 +103,24 @@
   return api.registry.createType('AccountId', '0x' + number.toString(16).padStart(64, '0')).toJSON();
 }
 
+export function bigIntToDecimals(number: bigint, decimals = 18): string {
+  let numberStr = number.toString();
+  console.log('[0] str = ', numberStr);
+
+  // Get rid of `n` at the end
+  numberStr = numberStr.substring(0, numberStr.length - 1);
+  console.log('[1] str = ', numberStr);
+
+  const dotPos = numberStr.length - decimals;
+  if (dotPos <= 0) {
+    return '0.' + numberStr;
+  } else {
+    const intPart = numberStr.substring(0, dotPos);
+    const fractPart = numberStr.substring(dotPos);
+    return intPart + '.' + fractPart;
+  }
+}
+
 export function normalizeAccountId(input: string | AccountId | CrossAccountId | IKeyringPair): CrossAccountId {
   if (typeof input === 'string') {
     if (input.length >= 47) {
modifiedtests/src/xcm/xcmOpal.test.tsdiffbeforeafterboth
--- a/tests/src/xcm/xcmOpal.test.ts
+++ b/tests/src/xcm/xcmOpal.test.ts
@@ -21,7 +21,7 @@
 import {ApiOptions} from '@polkadot/api/types';
 import {IKeyringPair} from '@polkadot/types/types';
 import usingApi, {submitTransactionAsync} from './../substrate/substrate-api';
-import {getGenericResult, paraSiblingSovereignAccount} from './../util/helpers';
+import {bigIntToDecimals, getGenericResult, paraSiblingSovereignAccount} from './../util/helpers';
 import waitNewBlocks from './../substrate/wait-new-blocks';
 import {normalizeAccountId} from './../util/helpers';
 import getBalance from './../substrate/get-balance';
@@ -43,6 +43,8 @@
 const ASSET_METADATA_DESCRIPTION = 'USDT';
 const ASSET_METADATA_MINIMAL_BALANCE = 1;
 
+const WESTMINT_DECIMALS = 12;
+
 const TRANSFER_AMOUNT = 1_000_000_000_000_000_000n;
 
 // 10,000.00 (ten thousands) USDT
@@ -281,7 +283,10 @@
       [balanceStmnAfter] = await getBalance(api, [alice.address]);
 
       // common good parachain take commission in it native token
-      console.log('Opal to Westmint transaction fees on Westmint: %s WND', balanceStmnBefore - balanceStmnAfter);
+      console.log(
+        'Opal to Westmint transaction fees on Westmint: %s WND',
+        bigIntToDecimals(balanceStmnBefore - balanceStmnAfter, WESTMINT_DECIMALS),
+      );
       expect(balanceStmnBefore > balanceStmnAfter).to.be.true;
 
     }, statemineApiOptions);
@@ -297,10 +302,16 @@
 
       // commission has not paid in USDT token
       expect(free == TRANSFER_AMOUNT).to.be.true;
-      console.log('Opal to Westmint transaction fees on Opal: %s USDT', TRANSFER_AMOUNT - free);
+      console.log(
+        'Opal to Westmint transaction fees on Opal: %s USDT',
+        bigIntToDecimals(TRANSFER_AMOUNT - free),
+      );
       // ... and parachain native token
       expect(balanceOpalAfter == balanceOpalBefore).to.be.true;
-      console.log('Opal to Westmint transaction fees on Opal: %s WND', balanceOpalAfter - balanceOpalBefore);
+      console.log(
+        'Opal to Westmint transaction fees on Opal: %s WND',
+        bigIntToDecimals(balanceOpalAfter - balanceOpalBefore, WESTMINT_DECIMALS),
+      );
 
     }, uniqueApiOptions);
     
@@ -451,8 +462,14 @@
       [balanceBobAfter] = await getBalance(api, [bob.address]);
       balanceBobRelayTokenAfter = BigInt(((await api.query.tokens.accounts(bob.addressRaw, {NativeAssetId: 'Parent'})).toJSON() as any).free);
       const wndFee = balanceBobRelayTokenAfter - TRANSFER_AMOUNT_RELAY - balanceBobRelayTokenBefore; 
-      console.log('Relay (Westend) to Opal transaction fees: %s OPL', balanceBobAfter - balanceBobBefore);
-      console.log('Relay (Westend) to Opal transaction fees: %s WND', wndFee);
+      console.log(
+        'Relay (Westend) to Opal transaction fees: %s OPL',
+        bigIntToDecimals(balanceBobAfter - balanceBobBefore),
+      );
+      console.log(
+        'Relay (Westend) to Opal transaction fees: %s WND',
+        bigIntToDecimals(wndFee, WESTMINT_DECIMALS),
+      );
       expect(balanceBobBefore == balanceBobAfter).to.be.true;
       expect(balanceBobRelayTokenBefore < balanceBobRelayTokenAfter).to.be.true;
     }, uniqueApiOptions2);
@@ -504,4 +521,4 @@
     }, uniqueApiOptions);
   });
 
-});
\ No newline at end of file
+});
modifiedtests/src/xcm/xcmQuartz.test.tsdiffbeforeafterboth
before · tests/src/xcm/xcmQuartz.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 chai from 'chai';18import chaiAsPromised from 'chai-as-promised';1920import {WsProvider, Keyring} from '@polkadot/api';21import {ApiOptions} from '@polkadot/api/types';22import {IKeyringPair} from '@polkadot/types/types';23import usingApi, {submitTransactionAsync} from '../substrate/substrate-api';24import {getGenericResult, generateKeyringPair, waitEvent, describe_xcm} from '../util/helpers';25import {MultiLocation} from '@polkadot/types/interfaces';26import {blake2AsHex} from '@polkadot/util-crypto';27import waitNewBlocks from '../substrate/wait-new-blocks';28import getBalance from '../substrate/get-balance';2930chai.use(chaiAsPromised);31const expect = chai.expect;3233const QUARTZ_CHAIN = 2095;34const KARURA_CHAIN = 2000;35const MOONRIVER_CHAIN = 2023;3637const KARURA_PORT = 9946;38const MOONRIVER_PORT = 9947;3940const TRANSFER_AMOUNT = 2000000000000000000000000n;4142function parachainApiOptions(port: number): ApiOptions {43  return {44    provider: new WsProvider('ws://127.0.0.1:' + port.toString()),45  };46}4748function karuraOptions(): ApiOptions {49  return parachainApiOptions(KARURA_PORT);50}5152function moonriverOptions(): ApiOptions {53  return parachainApiOptions(MOONRIVER_PORT);54}5556describe_xcm('Integration test: Exchanging tokens with Karura', () => {57  let alice: IKeyringPair;58  let randomAccount: IKeyringPair;59  60  let balanceQuartzTokenInit: bigint;61  let balanceQuartzTokenMiddle: bigint;62  let balanceQuartzTokenFinal: bigint;63  let balanceKaruraTokenInit: bigint;64  let balanceKaruraTokenMiddle: bigint;65  let balanceKaruraTokenFinal: bigint;66  let balanceQuartzForeignTokenInit: bigint;67  let balanceQuartzForeignTokenMiddle: bigint;68  let balanceQuartzForeignTokenFinal: bigint;69  70  before(async () => {71    await usingApi(async (api, privateKeyWrapper) => {72      alice = privateKeyWrapper('//Alice');73      randomAccount = generateKeyringPair();74    });7576    // Karura side77    await usingApi(78      async (api) => {79        const destination = {80          V0: {81            X2: [82              'Parent',83              {84                Parachain: QUARTZ_CHAIN,85              },86            ],87          },88        };89  90        const metadata = {91          name: 'QTZ',92          symbol: 'QTZ',93          decimals: 18,94          minimalBalance: 1,95        };96  97        const tx = api.tx.assetRegistry.registerForeignAsset(destination, metadata);98        const sudoTx = api.tx.sudo.sudo(tx as any);99        const events = await submitTransactionAsync(alice, sudoTx);100        const result = getGenericResult(events);101        expect(result.success).to.be.true;102  103        const tx1 = api.tx.balances.transfer(randomAccount.address, 10000000000000n);104        const events1 = await submitTransactionAsync(alice, tx1);105        const result1 = getGenericResult(events1);106        expect(result1.success).to.be.true;107  108        [balanceKaruraTokenInit] = await getBalance(api, [randomAccount.address]);109        {110          const {free} = (await api.query.tokens.accounts(randomAccount.addressRaw, {ForeignAsset: 0})).toJSON() as any;111          balanceQuartzForeignTokenInit = BigInt(free);112        }113      },114      karuraOptions(),115    );116  117    // Quartz side118    await usingApi(async (api) => {119      const tx0 = api.tx.balances.transfer(randomAccount.address, 10n * TRANSFER_AMOUNT);120      const events0 = await submitTransactionAsync(alice, tx0);121      const result0 = getGenericResult(events0);122      expect(result0.success).to.be.true;123  124      [balanceQuartzTokenInit] = await getBalance(api, [randomAccount.address]);125    });126  });127  128  it('Should connect and send QTZ to Karura', async () => {129  130    // Quartz side131    await usingApi(async (api) => {132  133      const destination = {134        V0: {135          X2: [136            'Parent',137            {138              Parachain: KARURA_CHAIN,139            },140          ],141        },142      };143  144      const beneficiary = {145        V0: {146          X1: {147            AccountId32: {148              network: 'Any',149              id: randomAccount.addressRaw,150            },151          },152        },153      };154  155      const assets = {156        V1: [157          {158            id: {159              Concrete: {160                parents: 0,161                interior: 'Here',162              },163            },164            fun: {165              Fungible: TRANSFER_AMOUNT,166            },167          },168        ],169      };170  171      const feeAssetItem = 0;172  173      const weightLimit = {174        Limited: 5000000000,175      };176  177      const tx = api.tx.polkadotXcm.limitedReserveTransferAssets(destination, beneficiary, assets, feeAssetItem, weightLimit);178      const events = await submitTransactionAsync(randomAccount, tx);179      const result = getGenericResult(events);180      expect(result.success).to.be.true;181  182      [balanceQuartzTokenMiddle] = await getBalance(api, [randomAccount.address]);183  184      const qtzFees = balanceQuartzTokenInit - balanceQuartzTokenMiddle - TRANSFER_AMOUNT;185      console.log('[Quartz -> Karura] transaction fees on Quartz: %s QTZ', qtzFees);186      expect(qtzFees > 0n).to.be.true;187    });188  189    // Karura side190    await usingApi(191      async (api) => {192        await waitNewBlocks(api, 3);193        const {free} = (await api.query.tokens.accounts(randomAccount.addressRaw, {ForeignAsset: 0})).toJSON() as any;194        balanceQuartzForeignTokenMiddle = BigInt(free);195  196        [balanceKaruraTokenMiddle] = await getBalance(api, [randomAccount.address]);197198        const karFees = balanceKaruraTokenInit - balanceKaruraTokenMiddle;199        const qtzIncomeTransfer = balanceQuartzForeignTokenMiddle - balanceQuartzForeignTokenInit;200201        console.log('[Quartz -> Karura] transaction fees on Karura: %s KAR', karFees);202        console.log('[Quartz -> Karura] income %s QTZ', qtzIncomeTransfer);203        expect(karFees == 0n).to.be.true;204        expect(qtzIncomeTransfer == TRANSFER_AMOUNT).to.be.true;205      },206      karuraOptions(),207    );208  });209  210  it('Should connect to Karura and send QTZ back', async () => {211  212    // Karura side213    await usingApi(214      async (api) => {215        const destination = {216          V1: {217            parents: 1,218            interior: {219              X2: [220                {Parachain: QUARTZ_CHAIN},221                {222                  AccountId32: {223                    network: 'Any',224                    id: randomAccount.addressRaw,225                  },226                },227              ],228            },229          },230        };231  232        const id = {233          ForeignAsset: 0,234        };235236        const destWeight = 50000000;237  238        const tx = api.tx.xTokens.transfer(id, TRANSFER_AMOUNT, destination, destWeight);239        const events = await submitTransactionAsync(randomAccount, tx);240        const result = getGenericResult(events);241        expect(result.success).to.be.true;242  243        [balanceKaruraTokenFinal] = await getBalance(api, [randomAccount.address]);244        {245          const {free} = (await api.query.tokens.accounts(randomAccount.addressRaw, {ForeignAsset: 0})).toJSON() as any;246          balanceQuartzForeignTokenFinal = BigInt(free);247        }248  249        const karFees = balanceKaruraTokenMiddle - balanceKaruraTokenFinal;250        const qtzOutcomeTransfer = balanceQuartzForeignTokenMiddle - balanceQuartzForeignTokenFinal;251252        console.log('[Karura -> Quartz] transaction fees on Karura: %s KAR', karFees);253        console.log('[Karura -> Quartz] outcome %s QTZ', qtzOutcomeTransfer);254255        expect(karFees > 0).to.be.true;256        expect(qtzOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;257      },258      karuraOptions(),259    );260261    // Quartz side262    await usingApi(async (api) => {263      await waitNewBlocks(api, 3);264  265      [balanceQuartzTokenFinal] = await getBalance(api, [randomAccount.address]);266      const actuallyDelivered = balanceQuartzTokenFinal - balanceQuartzTokenMiddle;267      expect(actuallyDelivered > 0).to.be.true;268269      console.log('[Karura -> Quartz] actually delivered %s QTZ', actuallyDelivered);270  271      const qtzFees = TRANSFER_AMOUNT - actuallyDelivered;272      console.log('[Karura -> Quartz] transaction fees on Quartz: %s QTZ', qtzFees);273      expect(qtzFees == 0n).to.be.true;274    });275  });276277  it('Quartz rejects KAR tokens from Karura', async () => {278    // This test is relevant only when the foreign asset pallet is disabled279280    await usingApi(async (api) => {281      const destination = {282        V1: {283          parents: 1,284          interior: {285            X2: [286              {Parachain: QUARTZ_CHAIN},287              {288                AccountId32: {289                  network: 'Any',290                  id: randomAccount.addressRaw,291                },292              },293            ],294          },295        },296      };297298      const id = {299        Token: 'KAR',300      };301302      const destWeight = 50000000;303304      const tx = api.tx.xTokens.transfer(id, 100_000_000_000, destination, destWeight);305      const events = await submitTransactionAsync(alice, tx);306      const result = getGenericResult(events);307      expect(result.success).to.be.true;308    }, karuraOptions());309310    await usingApi(async api => {311      const maxWaitBlocks = 3;312      const xcmpQueueFailEvent = await waitEvent(api, maxWaitBlocks, 'xcmpQueue', 'Fail');313314      expect(315        xcmpQueueFailEvent != null,316        'Only native token is supported when the Foreign-Assets pallet is not connected',317      ).to.be.true;318    });319  });320});321322describe_xcm('Integration test: Exchanging QTZ with Moonriver', () => {323324  // Quartz constants325  let quartzAlice: IKeyringPair;326  let quartzAssetLocation;327328  let randomAccountQuartz: IKeyringPair;329  let randomAccountMoonriver: IKeyringPair;330331  // Moonriver constants332  let assetId: string;333334  const moonriverKeyring = new Keyring({type: 'ethereum'});335  const alithPrivateKey = '0x5fb92d6e98884f76de468fa3f6278f8807c48bebc13595d45af5bdc4da702133';336  const baltatharPrivateKey = '0x8075991ce870b93a8870eca0c0f91913d12f47948ca0fd25b49c6fa7cdbeee8b';337  const dorothyPrivateKey = '0x39539ab1876910bbf3a223d84a29e28f1cb4e2e456503e7e91ed39b2e7223d68';338339  const alithAccount = moonriverKeyring.addFromUri(alithPrivateKey, undefined, 'ethereum');340  const baltatharAccount = moonriverKeyring.addFromUri(baltatharPrivateKey, undefined, 'ethereum');341  const dorothyAccount = moonriverKeyring.addFromUri(dorothyPrivateKey, undefined, 'ethereum');342343  const councilVotingThreshold = 2;344  const technicalCommitteeThreshold = 2;345  const votingPeriod = 3;346  const delayPeriod = 0;347348  const quartzAssetMetadata = {349    name: 'xcQuartz',350    symbol: 'xcQTZ',351    decimals: 18,352    isFrozen: false,353    minimalBalance: 1,354  };355356  let balanceQuartzTokenInit: bigint;357  let balanceQuartzTokenMiddle: bigint;358  let balanceQuartzTokenFinal: bigint;359  let balanceForeignQtzTokenInit: bigint;360  let balanceForeignQtzTokenMiddle: bigint;361  let balanceForeignQtzTokenFinal: bigint;362  let balanceMovrTokenInit: bigint;363  let balanceMovrTokenMiddle: bigint;364  let balanceMovrTokenFinal: bigint;365366  before(async () => {367    await usingApi(async (api, privateKeyWrapper) => {368      quartzAlice = privateKeyWrapper('//Alice');369      randomAccountQuartz = generateKeyringPair();370      randomAccountMoonriver = generateKeyringPair('ethereum');371372      balanceForeignQtzTokenInit = 0n;373    });374375    await usingApi(376      async (api) => {377378        // >>> Sponsoring Dorothy >>>379        console.log('Sponsoring Dorothy.......');380        const tx0 = api.tx.balances.transfer(dorothyAccount.address, 11_000_000_000_000_000_000n);381        const events0 = await submitTransactionAsync(alithAccount, tx0);382        const result0 = getGenericResult(events0);383        expect(result0.success).to.be.true;384        console.log('Sponsoring Dorothy.......DONE');385        // <<< Sponsoring Dorothy <<<386387        const sourceLocation: MultiLocation = api.createType(388          'MultiLocation',389          {390            parents: 1,391            interior: {X1: {Parachain: QUARTZ_CHAIN}},392          },393        );394395        quartzAssetLocation = {XCM: sourceLocation};396        const existentialDeposit = 1;397        const isSufficient = true;398        const unitsPerSecond = '1';399        const numAssetsWeightHint = 0;400401        const registerTx = api.tx.assetManager.registerForeignAsset(402          quartzAssetLocation,403          quartzAssetMetadata,404          existentialDeposit,405          isSufficient,406        );407        console.log('Encoded proposal for registerAsset is %s', registerTx.method.toHex() || '');408409        const setUnitsTx = api.tx.assetManager.setAssetUnitsPerSecond(410          quartzAssetLocation,411          unitsPerSecond,412          numAssetsWeightHint,413        );414        console.log('Encoded proposal for setAssetUnitsPerSecond is %s', setUnitsTx.method.toHex() || '');415416        const batchCall = api.tx.utility.batchAll([registerTx, setUnitsTx]);417        console.log('Encoded proposal for batchCall is %s', batchCall.method.toHex() || '');418419        // >>> Note motion preimage >>>420        console.log('Note motion preimage.......');421        const encodedProposal = batchCall?.method.toHex() || '';422        const proposalHash = blake2AsHex(encodedProposal);423        console.log('Encoded proposal for batch utility after schedule is %s', encodedProposal);424        console.log('Encoded proposal hash for batch utility after schedule is %s', proposalHash);425        console.log('Encoded length %d', encodedProposal.length);426427        const tx1 = api.tx.democracy.notePreimage(encodedProposal);428        const events1 = await submitTransactionAsync(baltatharAccount, tx1);429        const result1 = getGenericResult(events1);430        expect(result1.success).to.be.true;431        console.log('Note motion preimage.......DONE');432        // <<< Note motion preimage <<<433434        // >>> Propose external motion through council >>>435        console.log('Propose external motion through council.......');436        const externalMotion = api.tx.democracy.externalProposeMajority(proposalHash);437        const tx2 = api.tx.councilCollective.propose(438          councilVotingThreshold,439          externalMotion,440          externalMotion.encodedLength,441        );442        const events2 = await submitTransactionAsync(baltatharAccount, tx2);443        const result2 = getGenericResult(events2);444        expect(result2.success).to.be.true;445446        const encodedMotion = externalMotion?.method.toHex() || '';447        const motionHash = blake2AsHex(encodedMotion);448        console.log('Motion hash is %s', motionHash);449450        const tx3 = api.tx.councilCollective.vote(motionHash, 0, true);451        {452          const events3 = await submitTransactionAsync(dorothyAccount, tx3);453          const result3 = getGenericResult(events3);454          expect(result3.success).to.be.true;455        }456        {457          const events3 = await submitTransactionAsync(baltatharAccount, tx3);458          const result3 = getGenericResult(events3);459          expect(result3.success).to.be.true;460        }461462        const tx4 = api.tx.councilCollective.close(motionHash, 0, 1_000_000_000, externalMotion.encodedLength);463        const events4 = await submitTransactionAsync(dorothyAccount, tx4);464        const result4 = getGenericResult(events4);465        expect(result4.success).to.be.true;466        console.log('Propose external motion through council.......DONE');467        // <<< Propose external motion through council <<<468469        // >>> Fast track proposal through technical committee >>>470        console.log('Fast track proposal through technical committee.......');471        const fastTrack = api.tx.democracy.fastTrack(proposalHash, votingPeriod, delayPeriod);472        const tx5 = api.tx.techCommitteeCollective.propose(473          technicalCommitteeThreshold,474          fastTrack,475          fastTrack.encodedLength,476        );477        const events5 = await submitTransactionAsync(alithAccount, tx5);478        const result5 = getGenericResult(events5);479        expect(result5.success).to.be.true;480481        const encodedFastTrack = fastTrack?.method.toHex() || '';482        const fastTrackHash = blake2AsHex(encodedFastTrack);483        console.log('FastTrack hash is %s', fastTrackHash);484485        const proposalIdx = Number(await api.query.techCommitteeCollective.proposalCount()) - 1;486        const tx6 = api.tx.techCommitteeCollective.vote(fastTrackHash, proposalIdx, true);487        {488          const events6 = await submitTransactionAsync(baltatharAccount, tx6);489          const result6 = getGenericResult(events6);490          expect(result6.success).to.be.true;491        }492        {493          const events6 = await submitTransactionAsync(alithAccount, tx6);494          const result6 = getGenericResult(events6);495          expect(result6.success).to.be.true;496        }497498        const tx7 = api.tx.techCommitteeCollective499          .close(fastTrackHash, proposalIdx, 1_000_000_000, fastTrack.encodedLength);500        const events7 = await submitTransactionAsync(baltatharAccount, tx7);501        const result7 = getGenericResult(events7);502        expect(result7.success).to.be.true;503        console.log('Fast track proposal through technical committee.......DONE');504        // <<< Fast track proposal through technical committee <<<505506        // >>> Referendum voting >>>507        console.log('Referendum voting.......');508        const tx8 = api.tx.democracy.vote(509          0,510          {Standard: {balance: 10_000_000_000_000_000_000n, vote: {aye: true, conviction: 1}}},511        );512        const events8 = await submitTransactionAsync(dorothyAccount, tx8);513        const result8 = getGenericResult(events8);514        expect(result8.success).to.be.true;515        console.log('Referendum voting.......DONE');516        // <<< Referendum voting <<<517518        // >>> Acquire Quartz AssetId Info on Moonriver >>>519        console.log('Acquire Quartz AssetId Info on Moonriver.......');520521        // Wait for the democracy execute522        await waitNewBlocks(api, 5);523524        assetId = (await api.query.assetManager.assetTypeId({525          XCM: sourceLocation,526        })).toString();527528        console.log('QTZ asset ID is %s', assetId);529        console.log('Acquire Quartz AssetId Info on Moonriver.......DONE');530        // >>> Acquire Quartz AssetId Info on Moonriver >>>531532        // >>> Sponsoring random Account >>>533        console.log('Sponsoring random Account.......');534        const tx10 = api.tx.balances.transfer(randomAccountMoonriver.address, 11_000_000_000_000_000_000n);535        const events10 = await submitTransactionAsync(baltatharAccount, tx10);536        const result10 = getGenericResult(events10);537        expect(result10.success).to.be.true;538        console.log('Sponsoring random Account.......DONE');539        // <<< Sponsoring random Account <<<540541        [balanceMovrTokenInit] = await getBalance(api, [randomAccountMoonriver.address]);542      },543      moonriverOptions(),544    );545546    await usingApi(async (api) => {547      const tx0 = api.tx.balances.transfer(randomAccountQuartz.address, 10n * TRANSFER_AMOUNT);548      const events0 = await submitTransactionAsync(quartzAlice, tx0);549      const result0 = getGenericResult(events0);550      expect(result0.success).to.be.true;551552      [balanceQuartzTokenInit] = await getBalance(api, [randomAccountQuartz.address]);553    });554  });555556  it('Should connect and send QTZ to Moonriver', async () => {557    await usingApi(async (api) => {558      const currencyId = {559        NativeAssetId: 'Here',560      };561      const dest = {562        V1: {563          parents: 1,564          interior: {565            X2: [566              {Parachain: MOONRIVER_CHAIN},567              {AccountKey20: {network: 'Any', key: randomAccountMoonriver.address}},568            ],569          },570        },571      };572      const amount = TRANSFER_AMOUNT;573      const destWeight = 850000000;574575      const tx = api.tx.xTokens.transfer(currencyId, amount, dest, destWeight);576      const events = await submitTransactionAsync(randomAccountQuartz, tx);577      const result = getGenericResult(events);578      expect(result.success).to.be.true;579580      [balanceQuartzTokenMiddle] = await getBalance(api, [randomAccountQuartz.address]);581      expect(balanceQuartzTokenMiddle < balanceQuartzTokenInit).to.be.true;582583      const transactionFees = balanceQuartzTokenInit - balanceQuartzTokenMiddle - TRANSFER_AMOUNT;584      console.log('[Quartz -> Moonriver] transaction fees on Quartz: %s QTZ', transactionFees);585      expect(transactionFees > 0).to.be.true;586    });587588    await usingApi(589      async (api) => {590        await waitNewBlocks(api, 3);591592        [balanceMovrTokenMiddle] = await getBalance(api, [randomAccountMoonriver.address]);593594        const movrFees = balanceMovrTokenInit - balanceMovrTokenMiddle;595        console.log('[Quartz -> Moonriver] transaction fees on Moonriver: %s MOVR', movrFees);596        expect(movrFees == 0n).to.be.true;597598        const qtzRandomAccountAsset = (599          await api.query.assets.account(assetId, randomAccountMoonriver.address)600        ).toJSON()! as any;601602        balanceForeignQtzTokenMiddle = BigInt(qtzRandomAccountAsset['balance']);603        const qtzIncomeTransfer = balanceForeignQtzTokenMiddle - balanceForeignQtzTokenInit;604        console.log('[Quartz -> Moonriver] income %s QTZ', qtzIncomeTransfer);605        expect(qtzIncomeTransfer == TRANSFER_AMOUNT).to.be.true;606      },607      moonriverOptions(),608    );609  });610611  it('Should connect to Moonriver and send QTZ back', async () => {612    await usingApi(613      async (api) => {614        const asset = {615          V1: {616            id: {617              Concrete: {618                parents: 1,619                interior: {620                  X1: {Parachain: QUARTZ_CHAIN},621                },622              },623            },624            fun: {625              Fungible: TRANSFER_AMOUNT,626            },627          },628        };629        const destination = {630          V1: {631            parents: 1,632            interior: {633              X2: [634                {Parachain: QUARTZ_CHAIN},635                {AccountId32: {network: 'Any', id: randomAccountQuartz.addressRaw}},636              ],637            },638          },639        };640        const destWeight = 50000000;641642        const tx = api.tx.xTokens.transferMultiasset(asset, destination, destWeight);643        const events = await submitTransactionAsync(randomAccountMoonriver, tx);644        const result = getGenericResult(events);645        expect(result.success).to.be.true;646647        [balanceMovrTokenFinal] = await getBalance(api, [randomAccountMoonriver.address]);648649        const movrFees = balanceMovrTokenMiddle - balanceMovrTokenFinal;650        console.log('[Moonriver -> Quartz] transaction fees on Moonriver: %s MOVR', movrFees);651        expect(movrFees > 0).to.be.true;652653        const qtzRandomAccountAsset = (654          await api.query.assets.account(assetId, randomAccountMoonriver.address)655        ).toJSON()! as any;656657        expect(qtzRandomAccountAsset).to.be.null;658659        balanceForeignQtzTokenFinal = 0n;660661        const qtzOutcomeTransfer = balanceForeignQtzTokenMiddle - balanceForeignQtzTokenFinal;662        console.log('[Quartz -> Moonriver] outcome %s QTZ', qtzOutcomeTransfer);663        expect(qtzOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;664      },665      moonriverOptions(),666    );667668    await usingApi(async (api) => {669      await waitNewBlocks(api, 3);670671      [balanceQuartzTokenFinal] = await getBalance(api, [randomAccountQuartz.address]);672      const actuallyDelivered = balanceQuartzTokenFinal - balanceQuartzTokenMiddle;673      expect(actuallyDelivered > 0).to.be.true;674675      console.log('[Moonriver -> Quartz] actually delivered %s QTZ', actuallyDelivered);676677      const qtzFees = TRANSFER_AMOUNT - actuallyDelivered;678      console.log('[Moonriver -> Quartz] transaction fees on Quartz: %s QTZ', qtzFees);679      expect(qtzFees == 0n).to.be.true;680    });681  });682});
after · tests/src/xcm/xcmQuartz.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 chai from 'chai';18import chaiAsPromised from 'chai-as-promised';1920import {WsProvider, Keyring} from '@polkadot/api';21import {ApiOptions} from '@polkadot/api/types';22import {IKeyringPair} from '@polkadot/types/types';23import usingApi, {submitTransactionAsync} from '../substrate/substrate-api';24import {getGenericResult, generateKeyringPair, waitEvent, describe_xcm, bigIntToDecimals} from '../util/helpers';25import {MultiLocation} from '@polkadot/types/interfaces';26import {blake2AsHex} from '@polkadot/util-crypto';27import waitNewBlocks from '../substrate/wait-new-blocks';28import getBalance from '../substrate/get-balance';2930chai.use(chaiAsPromised);31const expect = chai.expect;3233const QUARTZ_CHAIN = 2095;34const KARURA_CHAIN = 2000;35const MOONRIVER_CHAIN = 2023;3637const KARURA_PORT = 9946;38const MOONRIVER_PORT = 9947;3940const KARURA_DECIMALS = 12;4142const TRANSFER_AMOUNT = 2000000000000000000000000n;4344function parachainApiOptions(port: number): ApiOptions {45  return {46    provider: new WsProvider('ws://127.0.0.1:' + port.toString()),47  };48}4950function karuraOptions(): ApiOptions {51  return parachainApiOptions(KARURA_PORT);52}5354function moonriverOptions(): ApiOptions {55  return parachainApiOptions(MOONRIVER_PORT);56}5758describe_xcm('Integration test: Exchanging tokens with Karura', () => {59  let alice: IKeyringPair;60  let randomAccount: IKeyringPair;61  62  let balanceQuartzTokenInit: bigint;63  let balanceQuartzTokenMiddle: bigint;64  let balanceQuartzTokenFinal: bigint;65  let balanceKaruraTokenInit: bigint;66  let balanceKaruraTokenMiddle: bigint;67  let balanceKaruraTokenFinal: bigint;68  let balanceQuartzForeignTokenInit: bigint;69  let balanceQuartzForeignTokenMiddle: bigint;70  let balanceQuartzForeignTokenFinal: bigint;71  72  before(async () => {73    await usingApi(async (api, privateKeyWrapper) => {74      alice = privateKeyWrapper('//Alice');75      randomAccount = generateKeyringPair();76    });7778    // Karura side79    await usingApi(80      async (api) => {81        const destination = {82          V0: {83            X2: [84              'Parent',85              {86                Parachain: QUARTZ_CHAIN,87              },88            ],89          },90        };91  92        const metadata = {93          name: 'QTZ',94          symbol: 'QTZ',95          decimals: 18,96          minimalBalance: 1,97        };98  99        const tx = api.tx.assetRegistry.registerForeignAsset(destination, metadata);100        const sudoTx = api.tx.sudo.sudo(tx as any);101        const events = await submitTransactionAsync(alice, sudoTx);102        const result = getGenericResult(events);103        expect(result.success).to.be.true;104  105        const tx1 = api.tx.balances.transfer(randomAccount.address, 10000000000000n);106        const events1 = await submitTransactionAsync(alice, tx1);107        const result1 = getGenericResult(events1);108        expect(result1.success).to.be.true;109  110        [balanceKaruraTokenInit] = await getBalance(api, [randomAccount.address]);111        {112          const {free} = (await api.query.tokens.accounts(randomAccount.addressRaw, {ForeignAsset: 0})).toJSON() as any;113          balanceQuartzForeignTokenInit = BigInt(free);114        }115      },116      karuraOptions(),117    );118  119    // Quartz side120    await usingApi(async (api) => {121      const tx0 = api.tx.balances.transfer(randomAccount.address, 10n * TRANSFER_AMOUNT);122      const events0 = await submitTransactionAsync(alice, tx0);123      const result0 = getGenericResult(events0);124      expect(result0.success).to.be.true;125  126      [balanceQuartzTokenInit] = await getBalance(api, [randomAccount.address]);127    });128  });129  130  it('Should connect and send QTZ to Karura', async () => {131  132    // Quartz side133    await usingApi(async (api) => {134  135      const destination = {136        V0: {137          X2: [138            'Parent',139            {140              Parachain: KARURA_CHAIN,141            },142          ],143        },144      };145  146      const beneficiary = {147        V0: {148          X1: {149            AccountId32: {150              network: 'Any',151              id: randomAccount.addressRaw,152            },153          },154        },155      };156  157      const assets = {158        V1: [159          {160            id: {161              Concrete: {162                parents: 0,163                interior: 'Here',164              },165            },166            fun: {167              Fungible: TRANSFER_AMOUNT,168            },169          },170        ],171      };172  173      const feeAssetItem = 0;174  175      const weightLimit = {176        Limited: 5000000000,177      };178  179      const tx = api.tx.polkadotXcm.limitedReserveTransferAssets(destination, beneficiary, assets, feeAssetItem, weightLimit);180      const events = await submitTransactionAsync(randomAccount, tx);181      const result = getGenericResult(events);182      expect(result.success).to.be.true;183  184      [balanceQuartzTokenMiddle] = await getBalance(api, [randomAccount.address]);185  186      const qtzFees = balanceQuartzTokenInit - balanceQuartzTokenMiddle - TRANSFER_AMOUNT;187      console.log('[Quartz -> Karura] transaction fees on Quartz: %s QTZ', bigIntToDecimals(qtzFees));188      expect(qtzFees > 0n).to.be.true;189    });190  191    // Karura side192    await usingApi(193      async (api) => {194        await waitNewBlocks(api, 3);195        const {free} = (await api.query.tokens.accounts(randomAccount.addressRaw, {ForeignAsset: 0})).toJSON() as any;196        balanceQuartzForeignTokenMiddle = BigInt(free);197  198        [balanceKaruraTokenMiddle] = await getBalance(api, [randomAccount.address]);199200        const karFees = balanceKaruraTokenInit - balanceKaruraTokenMiddle;201        const qtzIncomeTransfer = balanceQuartzForeignTokenMiddle - balanceQuartzForeignTokenInit;202203        console.log('204          [Quartz -> Karura] transaction fees on Karura: %s KAR',205          bigIntToDecimals(karFees, KARURA_DECIMALS),206        );207        console.log('[Quartz -> Karura] income %s QTZ', bigIntToDecimals(qtzIncomeTransfer));208        expect(karFees == 0n).to.be.true;209        expect(qtzIncomeTransfer == TRANSFER_AMOUNT).to.be.true;210      },211      karuraOptions(),212    );213  });214  215  it('Should connect to Karura and send QTZ back', async () => {216  217    // Karura side218    await usingApi(219      async (api) => {220        const destination = {221          V1: {222            parents: 1,223            interior: {224              X2: [225                {Parachain: QUARTZ_CHAIN},226                {227                  AccountId32: {228                    network: 'Any',229                    id: randomAccount.addressRaw,230                  },231                },232              ],233            },234          },235        };236  237        const id = {238          ForeignAsset: 0,239        };240241        const destWeight = 50000000;242  243        const tx = api.tx.xTokens.transfer(id, TRANSFER_AMOUNT, destination, destWeight);244        const events = await submitTransactionAsync(randomAccount, tx);245        const result = getGenericResult(events);246        expect(result.success).to.be.true;247  248        [balanceKaruraTokenFinal] = await getBalance(api, [randomAccount.address]);249        {250          const {free} = (await api.query.tokens.accounts(randomAccount.addressRaw, {ForeignAsset: 0})).toJSON() as any;251          balanceQuartzForeignTokenFinal = BigInt(free);252        }253  254        const karFees = balanceKaruraTokenMiddle - balanceKaruraTokenFinal;255        const qtzOutcomeTransfer = balanceQuartzForeignTokenMiddle - balanceQuartzForeignTokenFinal;256257        console.log(258          '[Karura -> Quartz] transaction fees on Karura: %s KAR',259          bigIntToDecimals(karFees, KARURA_DECIMALS),260        );261        console.log('[Karura -> Quartz] outcome %s QTZ', bigIntToDecimals(qtzOutcomeTransfer));262263        expect(karFees > 0).to.be.true;264        expect(qtzOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;265      },266      karuraOptions(),267    );268269    // Quartz side270    await usingApi(async (api) => {271      await waitNewBlocks(api, 3);272  273      [balanceQuartzTokenFinal] = await getBalance(api, [randomAccount.address]);274      const actuallyDelivered = balanceQuartzTokenFinal - balanceQuartzTokenMiddle;275      expect(actuallyDelivered > 0).to.be.true;276277      console.log('[Karura -> Quartz] actually delivered %s QTZ', bigIntToDecimals(actuallyDelivered));278  279      const qtzFees = TRANSFER_AMOUNT - actuallyDelivered;280      console.log('[Karura -> Quartz] transaction fees on Quartz: %s QTZ', bigIntToDecimals(qtzFees));281      expect(qtzFees == 0n).to.be.true;282    });283  });284285  it('Quartz rejects KAR tokens from Karura', async () => {286    // This test is relevant only when the foreign asset pallet is disabled287288    await usingApi(async (api) => {289      const destination = {290        V1: {291          parents: 1,292          interior: {293            X2: [294              {Parachain: QUARTZ_CHAIN},295              {296                AccountId32: {297                  network: 'Any',298                  id: randomAccount.addressRaw,299                },300              },301            ],302          },303        },304      };305306      const id = {307        Token: 'KAR',308      };309310      const destWeight = 50000000;311312      const tx = api.tx.xTokens.transfer(id, 100_000_000_000, destination, destWeight);313      const events = await submitTransactionAsync(alice, tx);314      const result = getGenericResult(events);315      expect(result.success).to.be.true;316    }, karuraOptions());317318    await usingApi(async api => {319      const maxWaitBlocks = 3;320      const xcmpQueueFailEvent = await waitEvent(api, maxWaitBlocks, 'xcmpQueue', 'Fail');321322      expect(323        xcmpQueueFailEvent != null,324        'Only native token is supported when the Foreign-Assets pallet is not connected',325      ).to.be.true;326    });327  });328});329330describe_xcm('Integration test: Exchanging QTZ with Moonriver', () => {331332  // Quartz constants333  let quartzAlice: IKeyringPair;334  let quartzAssetLocation;335336  let randomAccountQuartz: IKeyringPair;337  let randomAccountMoonriver: IKeyringPair;338339  // Moonriver constants340  let assetId: string;341342  const moonriverKeyring = new Keyring({type: 'ethereum'});343  const alithPrivateKey = '0x5fb92d6e98884f76de468fa3f6278f8807c48bebc13595d45af5bdc4da702133';344  const baltatharPrivateKey = '0x8075991ce870b93a8870eca0c0f91913d12f47948ca0fd25b49c6fa7cdbeee8b';345  const dorothyPrivateKey = '0x39539ab1876910bbf3a223d84a29e28f1cb4e2e456503e7e91ed39b2e7223d68';346347  const alithAccount = moonriverKeyring.addFromUri(alithPrivateKey, undefined, 'ethereum');348  const baltatharAccount = moonriverKeyring.addFromUri(baltatharPrivateKey, undefined, 'ethereum');349  const dorothyAccount = moonriverKeyring.addFromUri(dorothyPrivateKey, undefined, 'ethereum');350351  const councilVotingThreshold = 2;352  const technicalCommitteeThreshold = 2;353  const votingPeriod = 3;354  const delayPeriod = 0;355356  const quartzAssetMetadata = {357    name: 'xcQuartz',358    symbol: 'xcQTZ',359    decimals: 18,360    isFrozen: false,361    minimalBalance: 1,362  };363364  let balanceQuartzTokenInit: bigint;365  let balanceQuartzTokenMiddle: bigint;366  let balanceQuartzTokenFinal: bigint;367  let balanceForeignQtzTokenInit: bigint;368  let balanceForeignQtzTokenMiddle: bigint;369  let balanceForeignQtzTokenFinal: bigint;370  let balanceMovrTokenInit: bigint;371  let balanceMovrTokenMiddle: bigint;372  let balanceMovrTokenFinal: bigint;373374  before(async () => {375    await usingApi(async (api, privateKeyWrapper) => {376      quartzAlice = privateKeyWrapper('//Alice');377      randomAccountQuartz = generateKeyringPair();378      randomAccountMoonriver = generateKeyringPair('ethereum');379380      balanceForeignQtzTokenInit = 0n;381    });382383    await usingApi(384      async (api) => {385386        // >>> Sponsoring Dorothy >>>387        console.log('Sponsoring Dorothy.......');388        const tx0 = api.tx.balances.transfer(dorothyAccount.address, 11_000_000_000_000_000_000n);389        const events0 = await submitTransactionAsync(alithAccount, tx0);390        const result0 = getGenericResult(events0);391        expect(result0.success).to.be.true;392        console.log('Sponsoring Dorothy.......DONE');393        // <<< Sponsoring Dorothy <<<394395        const sourceLocation: MultiLocation = api.createType(396          'MultiLocation',397          {398            parents: 1,399            interior: {X1: {Parachain: QUARTZ_CHAIN}},400          },401        );402403        quartzAssetLocation = {XCM: sourceLocation};404        const existentialDeposit = 1;405        const isSufficient = true;406        const unitsPerSecond = '1';407        const numAssetsWeightHint = 0;408409        const registerTx = api.tx.assetManager.registerForeignAsset(410          quartzAssetLocation,411          quartzAssetMetadata,412          existentialDeposit,413          isSufficient,414        );415        console.log('Encoded proposal for registerAsset is %s', registerTx.method.toHex() || '');416417        const setUnitsTx = api.tx.assetManager.setAssetUnitsPerSecond(418          quartzAssetLocation,419          unitsPerSecond,420          numAssetsWeightHint,421        );422        console.log('Encoded proposal for setAssetUnitsPerSecond is %s', setUnitsTx.method.toHex() || '');423424        const batchCall = api.tx.utility.batchAll([registerTx, setUnitsTx]);425        console.log('Encoded proposal for batchCall is %s', batchCall.method.toHex() || '');426427        // >>> Note motion preimage >>>428        console.log('Note motion preimage.......');429        const encodedProposal = batchCall?.method.toHex() || '';430        const proposalHash = blake2AsHex(encodedProposal);431        console.log('Encoded proposal for batch utility after schedule is %s', encodedProposal);432        console.log('Encoded proposal hash for batch utility after schedule is %s', proposalHash);433        console.log('Encoded length %d', encodedProposal.length);434435        const tx1 = api.tx.democracy.notePreimage(encodedProposal);436        const events1 = await submitTransactionAsync(baltatharAccount, tx1);437        const result1 = getGenericResult(events1);438        expect(result1.success).to.be.true;439        console.log('Note motion preimage.......DONE');440        // <<< Note motion preimage <<<441442        // >>> Propose external motion through council >>>443        console.log('Propose external motion through council.......');444        const externalMotion = api.tx.democracy.externalProposeMajority(proposalHash);445        const tx2 = api.tx.councilCollective.propose(446          councilVotingThreshold,447          externalMotion,448          externalMotion.encodedLength,449        );450        const events2 = await submitTransactionAsync(baltatharAccount, tx2);451        const result2 = getGenericResult(events2);452        expect(result2.success).to.be.true;453454        const encodedMotion = externalMotion?.method.toHex() || '';455        const motionHash = blake2AsHex(encodedMotion);456        console.log('Motion hash is %s', motionHash);457458        const tx3 = api.tx.councilCollective.vote(motionHash, 0, true);459        {460          const events3 = await submitTransactionAsync(dorothyAccount, tx3);461          const result3 = getGenericResult(events3);462          expect(result3.success).to.be.true;463        }464        {465          const events3 = await submitTransactionAsync(baltatharAccount, tx3);466          const result3 = getGenericResult(events3);467          expect(result3.success).to.be.true;468        }469470        const tx4 = api.tx.councilCollective.close(motionHash, 0, 1_000_000_000, externalMotion.encodedLength);471        const events4 = await submitTransactionAsync(dorothyAccount, tx4);472        const result4 = getGenericResult(events4);473        expect(result4.success).to.be.true;474        console.log('Propose external motion through council.......DONE');475        // <<< Propose external motion through council <<<476477        // >>> Fast track proposal through technical committee >>>478        console.log('Fast track proposal through technical committee.......');479        const fastTrack = api.tx.democracy.fastTrack(proposalHash, votingPeriod, delayPeriod);480        const tx5 = api.tx.techCommitteeCollective.propose(481          technicalCommitteeThreshold,482          fastTrack,483          fastTrack.encodedLength,484        );485        const events5 = await submitTransactionAsync(alithAccount, tx5);486        const result5 = getGenericResult(events5);487        expect(result5.success).to.be.true;488489        const encodedFastTrack = fastTrack?.method.toHex() || '';490        const fastTrackHash = blake2AsHex(encodedFastTrack);491        console.log('FastTrack hash is %s', fastTrackHash);492493        const proposalIdx = Number(await api.query.techCommitteeCollective.proposalCount()) - 1;494        const tx6 = api.tx.techCommitteeCollective.vote(fastTrackHash, proposalIdx, true);495        {496          const events6 = await submitTransactionAsync(baltatharAccount, tx6);497          const result6 = getGenericResult(events6);498          expect(result6.success).to.be.true;499        }500        {501          const events6 = await submitTransactionAsync(alithAccount, tx6);502          const result6 = getGenericResult(events6);503          expect(result6.success).to.be.true;504        }505506        const tx7 = api.tx.techCommitteeCollective507          .close(fastTrackHash, proposalIdx, 1_000_000_000, fastTrack.encodedLength);508        const events7 = await submitTransactionAsync(baltatharAccount, tx7);509        const result7 = getGenericResult(events7);510        expect(result7.success).to.be.true;511        console.log('Fast track proposal through technical committee.......DONE');512        // <<< Fast track proposal through technical committee <<<513514        // >>> Referendum voting >>>515        console.log('Referendum voting.......');516        const tx8 = api.tx.democracy.vote(517          0,518          {Standard: {balance: 10_000_000_000_000_000_000n, vote: {aye: true, conviction: 1}}},519        );520        const events8 = await submitTransactionAsync(dorothyAccount, tx8);521        const result8 = getGenericResult(events8);522        expect(result8.success).to.be.true;523        console.log('Referendum voting.......DONE');524        // <<< Referendum voting <<<525526        // >>> Acquire Quartz AssetId Info on Moonriver >>>527        console.log('Acquire Quartz AssetId Info on Moonriver.......');528529        // Wait for the democracy execute530        await waitNewBlocks(api, 5);531532        assetId = (await api.query.assetManager.assetTypeId({533          XCM: sourceLocation,534        })).toString();535536        console.log('QTZ asset ID is %s', assetId);537        console.log('Acquire Quartz AssetId Info on Moonriver.......DONE');538        // >>> Acquire Quartz AssetId Info on Moonriver >>>539540        // >>> Sponsoring random Account >>>541        console.log('Sponsoring random Account.......');542        const tx10 = api.tx.balances.transfer(randomAccountMoonriver.address, 11_000_000_000_000_000_000n);543        const events10 = await submitTransactionAsync(baltatharAccount, tx10);544        const result10 = getGenericResult(events10);545        expect(result10.success).to.be.true;546        console.log('Sponsoring random Account.......DONE');547        // <<< Sponsoring random Account <<<548549        [balanceMovrTokenInit] = await getBalance(api, [randomAccountMoonriver.address]);550      },551      moonriverOptions(),552    );553554    await usingApi(async (api) => {555      const tx0 = api.tx.balances.transfer(randomAccountQuartz.address, 10n * TRANSFER_AMOUNT);556      const events0 = await submitTransactionAsync(quartzAlice, tx0);557      const result0 = getGenericResult(events0);558      expect(result0.success).to.be.true;559560      [balanceQuartzTokenInit] = await getBalance(api, [randomAccountQuartz.address]);561    });562  });563564  it('Should connect and send QTZ to Moonriver', async () => {565    await usingApi(async (api) => {566      const currencyId = {567        NativeAssetId: 'Here',568      };569      const dest = {570        V1: {571          parents: 1,572          interior: {573            X2: [574              {Parachain: MOONRIVER_CHAIN},575              {AccountKey20: {network: 'Any', key: randomAccountMoonriver.address}},576            ],577          },578        },579      };580      const amount = TRANSFER_AMOUNT;581      const destWeight = 850000000;582583      const tx = api.tx.xTokens.transfer(currencyId, amount, dest, destWeight);584      const events = await submitTransactionAsync(randomAccountQuartz, tx);585      const result = getGenericResult(events);586      expect(result.success).to.be.true;587588      [balanceQuartzTokenMiddle] = await getBalance(api, [randomAccountQuartz.address]);589      expect(balanceQuartzTokenMiddle < balanceQuartzTokenInit).to.be.true;590591      const transactionFees = balanceQuartzTokenInit - balanceQuartzTokenMiddle - TRANSFER_AMOUNT;592      console.log('[Quartz -> Moonriver] transaction fees on Quartz: %s QTZ', bigIntToDecimals(transactionFees));593      expect(transactionFees > 0).to.be.true;594    });595596    await usingApi(597      async (api) => {598        await waitNewBlocks(api, 3);599600        [balanceMovrTokenMiddle] = await getBalance(api, [randomAccountMoonriver.address]);601602        const movrFees = balanceMovrTokenInit - balanceMovrTokenMiddle;603        console.log('[Quartz -> Moonriver] transaction fees on Moonriver: %s MOVR',bigIntToDecimals(movrFees));604        expect(movrFees == 0n).to.be.true;605606        const qtzRandomAccountAsset = (607          await api.query.assets.account(assetId, randomAccountMoonriver.address)608        ).toJSON()! as any;609610        balanceForeignQtzTokenMiddle = BigInt(qtzRandomAccountAsset['balance']);611        const qtzIncomeTransfer = balanceForeignQtzTokenMiddle - balanceForeignQtzTokenInit;612        console.log('[Quartz -> Moonriver] income %s QTZ', bigIntToDecimals(qtzIncomeTransfer));613        expect(qtzIncomeTransfer == TRANSFER_AMOUNT).to.be.true;614      },615      moonriverOptions(),616    );617  });618619  it('Should connect to Moonriver and send QTZ back', async () => {620    await usingApi(621      async (api) => {622        const asset = {623          V1: {624            id: {625              Concrete: {626                parents: 1,627                interior: {628                  X1: {Parachain: QUARTZ_CHAIN},629                },630              },631            },632            fun: {633              Fungible: TRANSFER_AMOUNT,634            },635          },636        };637        const destination = {638          V1: {639            parents: 1,640            interior: {641              X2: [642                {Parachain: QUARTZ_CHAIN},643                {AccountId32: {network: 'Any', id: randomAccountQuartz.addressRaw}},644              ],645            },646          },647        };648        const destWeight = 50000000;649650        const tx = api.tx.xTokens.transferMultiasset(asset, destination, destWeight);651        const events = await submitTransactionAsync(randomAccountMoonriver, tx);652        const result = getGenericResult(events);653        expect(result.success).to.be.true;654655        [balanceMovrTokenFinal] = await getBalance(api, [randomAccountMoonriver.address]);656657        const movrFees = balanceMovrTokenMiddle - balanceMovrTokenFinal;658        console.log('[Moonriver -> Quartz] transaction fees on Moonriver: %s MOVR', bigIntToDecimals(movrFees));659        expect(movrFees > 0).to.be.true;660661        const qtzRandomAccountAsset = (662          await api.query.assets.account(assetId, randomAccountMoonriver.address)663        ).toJSON()! as any;664665        expect(qtzRandomAccountAsset).to.be.null;666667        balanceForeignQtzTokenFinal = 0n;668669        const qtzOutcomeTransfer = balanceForeignQtzTokenMiddle - balanceForeignQtzTokenFinal;670        console.log('[Quartz -> Moonriver] outcome %s QTZ', bigIntToDecimals(qtzOutcomeTransfer));671        expect(qtzOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;672      },673      moonriverOptions(),674    );675676    await usingApi(async (api) => {677      await waitNewBlocks(api, 3);678679      [balanceQuartzTokenFinal] = await getBalance(api, [randomAccountQuartz.address]);680      const actuallyDelivered = balanceQuartzTokenFinal - balanceQuartzTokenMiddle;681      expect(actuallyDelivered > 0).to.be.true;682683      console.log('[Moonriver -> Quartz] actually delivered %s QTZ', bigIntToDecimals(actuallyDelivered));684685      const qtzFees = TRANSFER_AMOUNT - actuallyDelivered;686      console.log('[Moonriver -> Quartz] transaction fees on Quartz: %s QTZ', bigIntToDecimals(qtzFees));687      expect(qtzFees == 0n).to.be.true;688    });689  });690});
modifiedtests/src/xcm/xcmUnique.test.tsdiffbeforeafterboth
--- a/tests/src/xcm/xcmUnique.test.ts
+++ b/tests/src/xcm/xcmUnique.test.ts
@@ -21,7 +21,7 @@
 import {ApiOptions} from '@polkadot/api/types';
 import {IKeyringPair} from '@polkadot/types/types';
 import usingApi, {submitTransactionAsync} from '../substrate/substrate-api';
-import {getGenericResult, generateKeyringPair, waitEvent, describe_xcm} from '../util/helpers';
+import {getGenericResult, generateKeyringPair, waitEvent, describe_xcm, bigIntToDecimals} from '../util/helpers';
 import {MultiLocation} from '@polkadot/types/interfaces';
 import {blake2AsHex} from '@polkadot/util-crypto';
 import waitNewBlocks from '../substrate/wait-new-blocks';
@@ -37,6 +37,8 @@
 const ACALA_PORT = 9946;
 const MOONBEAM_PORT = 9947;
 
+const ACALA_DECIMALS = 12;
+
 const TRANSFER_AMOUNT = 2000000000000000000000000n;
 
 function parachainApiOptions(port: number): ApiOptions {
@@ -182,7 +184,7 @@
       [balanceUniqueTokenMiddle] = await getBalance(api, [randomAccount.address]);
   
       const unqFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;
-      console.log('[Unique -> Acala] transaction fees on Unique: %s UNQ', unqFees);
+      console.log('[Unique -> Acala] transaction fees on Unique: %s UNQ', bigIntToDecimals(unqFees));
       expect(unqFees > 0n).to.be.true;
     });
   
@@ -198,8 +200,11 @@
         const acaFees = balanceAcalaTokenInit - balanceAcalaTokenMiddle;
         const unqIncomeTransfer = balanceUniqueForeignTokenMiddle - balanceUniqueForeignTokenInit;
 
-        console.log('[Unique -> Acala] transaction fees on Acala: %s ACA', acaFees);
-        console.log('[Unique -> Acala] income %s UNQ', unqIncomeTransfer);
+        console.log(
+          '[Unique -> Acala] transaction fees on Acala: %s ACA',
+          bigIntToDecimals(acaFees, ACALA_DECIMALS),
+        );
+        console.log('[Unique -> Acala] income %s UNQ', bigIntToDecimals(unqIncomeTransfer));
         expect(acaFees == 0n).to.be.true;
         expect(unqIncomeTransfer == TRANSFER_AMOUNT).to.be.true;
       },
@@ -249,8 +254,11 @@
         const acaFees = balanceAcalaTokenMiddle - balanceAcalaTokenFinal;
         const unqOutcomeTransfer = balanceUniqueForeignTokenMiddle - balanceUniqueForeignTokenFinal;
 
-        console.log('[Acala -> Unique] transaction fees on Acala: %s ACA', acaFees);
-        console.log('[Acala -> Unique] outcome %s UNQ', unqOutcomeTransfer);
+        console.log(
+          '[Acala -> Unique] transaction fees on Acala: %s ACA',
+          bigIntToDecimals(acaFees, ACALA_DECIMALS),
+        );
+        console.log('[Acala -> Unique] outcome %s UNQ', bigIntToDecimals(unqOutcomeTransfer));
 
         expect(acaFees > 0).to.be.true;
         expect(unqOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;
@@ -266,10 +274,10 @@
       const actuallyDelivered = balanceUniqueTokenFinal - balanceUniqueTokenMiddle;
       expect(actuallyDelivered > 0).to.be.true;
 
-      console.log('[Acala -> Unique] actually delivered %s UNQ', actuallyDelivered);
+      console.log('[Acala -> Unique] actually delivered %s UNQ', bigIntToDecimals(actuallyDelivered));
   
       const unqFees = TRANSFER_AMOUNT - actuallyDelivered;
-      console.log('[Acala -> Unique] transaction fees on Unique: %s UNQ', unqFees);
+      console.log('[Acala -> Unique] transaction fees on Unique: %s UNQ', bigIntToDecimals(unqFees));
       expect(unqFees == 0n).to.be.true;
     });
   });
@@ -581,7 +589,7 @@
       expect(balanceUniqueTokenMiddle < balanceUniqueTokenInit).to.be.true;
 
       const transactionFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;
-      console.log('[Unique -> Moonbeam] transaction fees on Unique: %s UNQ', transactionFees);
+      console.log('[Unique -> Moonbeam] transaction fees on Unique: %s UNQ', bigIntToDecimals(transactionFees));
       expect(transactionFees > 0).to.be.true;
     });
 
@@ -592,7 +600,7 @@
         [balanceGlmrTokenMiddle] = await getBalance(api, [randomAccountMoonbeam.address]);
 
         const glmrFees = balanceGlmrTokenInit - balanceGlmrTokenMiddle;
-        console.log('[Unique -> Moonbeam] transaction fees on Moonbeam: %s GLMR', glmrFees);
+        console.log('[Unique -> Moonbeam] transaction fees on Moonbeam: %s GLMR', bigIntToDecimals(glmrFees));
         expect(glmrFees == 0n).to.be.true;
 
         const unqRandomAccountAsset = (
@@ -601,7 +609,7 @@
 
         balanceForeignUnqTokenMiddle = BigInt(unqRandomAccountAsset['balance']);
         const unqIncomeTransfer = balanceForeignUnqTokenMiddle - balanceForeignUnqTokenInit;
-        console.log('[Unique -> Moonbeam] income %s UNQ', unqIncomeTransfer);
+        console.log('[Unique -> Moonbeam] income %s UNQ', bigIntToDecimals(unqIncomeTransfer));
         expect(unqIncomeTransfer == TRANSFER_AMOUNT).to.be.true;
       },
       moonbeamOptions(),
@@ -647,7 +655,7 @@
         [balanceGlmrTokenFinal] = await getBalance(api, [randomAccountMoonbeam.address]);
 
         const glmrFees = balanceGlmrTokenMiddle - balanceGlmrTokenFinal;
-        console.log('[Moonbeam -> Unique] transaction fees on Moonbeam: %s GLMR', glmrFees);
+        console.log('[Moonbeam -> Unique] transaction fees on Moonbeam: %s GLMR', bigIntToDecimals(glmrFees));
         expect(glmrFees > 0).to.be.true;
 
         const unqRandomAccountAsset = (
@@ -659,7 +667,7 @@
         balanceForeignUnqTokenFinal = 0n;
 
         const unqOutcomeTransfer = balanceForeignUnqTokenMiddle - balanceForeignUnqTokenFinal;
-        console.log('[Unique -> Moonbeam] outcome %s UNQ', unqOutcomeTransfer);
+        console.log('[Unique -> Moonbeam] outcome %s UNQ', bigIntToDecimals(unqOutcomeTransfer));
         expect(unqOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;
       },
       moonbeamOptions(),
@@ -672,10 +680,10 @@
       const actuallyDelivered = balanceUniqueTokenFinal - balanceUniqueTokenMiddle;
       expect(actuallyDelivered > 0).to.be.true;
 
-      console.log('[Moonbeam -> Unique] actually delivered %s UNQ', actuallyDelivered);
+      console.log('[Moonbeam -> Unique] actually delivered %s UNQ', bigIntToDecimals(actuallyDelivered));
 
       const unqFees = TRANSFER_AMOUNT - actuallyDelivered;
-      console.log('[Moonbeam -> Unique] transaction fees on Unique: %s UNQ', unqFees);
+      console.log('[Moonbeam -> Unique] transaction fees on Unique: %s UNQ', bigIntToDecimals(unqFees));
       expect(unqFees == 0n).to.be.true;
     });
   });