difftreelog
fix parse bitint to decimals
in: master
4 files changed
tests/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) {
tests/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
+});
tests/src/xcm/xcmQuartz.test.tsdiffbeforeafterboth1// 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});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});tests/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;
});
});