1234567891011121314151617import {Keyring} from '@polkadot/api';18import {IKeyringPair} from '@polkadot/types/types';19import {submitTransactionAsync} from '../substrate/substrate-api';20import {getGenericResult, generateKeyringPair, waitEvent, bigIntToDecimals} from '../deprecated-helpers/helpers';21import {MultiLocation} from '@polkadot/types/interfaces';22import {blake2AsHex} from '@polkadot/util-crypto';23import getBalance from '../substrate/get-balance';24import {XcmV2TraitsOutcome, XcmV2TraitsError} from '../interfaces';25import {itSub, expect, describeXcm, usingPlaygrounds} from '../util/playgrounds';2627const QUARTZ_CHAIN = 2095;28const KARURA_CHAIN = 2000;29const MOONRIVER_CHAIN = 2023;3031const RELAY_PORT = 9844;32const KARURA_PORT = 9946;33const MOONRIVER_PORT = 9947;3435const relayUrl = 'ws://127.0.0.1:' + RELAY_PORT;36const karuraUrl = 'ws://127.0.0.1:' + KARURA_PORT;37const moonriverUrl = 'ws://127.0.0.1:' + MOONRIVER_PORT;3839const KARURA_DECIMALS = 12;4041const TRANSFER_AMOUNT = 2000000000000000000000000n;4243describeXcm('[XCM] Integration test: Exchanging tokens with Karura', () => {44 let alice: IKeyringPair;45 let randomAccount: IKeyringPair;4647 let balanceQuartzTokenInit: bigint;48 let balanceQuartzTokenMiddle: bigint;49 let balanceQuartzTokenFinal: bigint;50 let balanceKaruraTokenInit: bigint;51 let balanceKaruraTokenMiddle: bigint;52 let balanceKaruraTokenFinal: bigint;53 let balanceQuartzForeignTokenInit: bigint;54 let balanceQuartzForeignTokenMiddle: bigint;55 let balanceQuartzForeignTokenFinal: bigint;5657 before(async () => {58 await usingPlaygrounds(async (helper, privateKey) => {59 const keyringSr25519 = new Keyring({type: 'sr25519'});6061 alice = privateKey('//Alice');62 randomAccount = generateKeyringPair(keyringSr25519);63 });6465 66 await usingPlaygrounds.atUrl(karuraUrl, async (helper) => {67 const api = helper.getApi();6869 const destination = {70 V0: {71 X2: [72 'Parent',73 {74 Parachain: QUARTZ_CHAIN,75 },76 ],77 },78 };7980 const metadata = {81 name: 'QTZ',82 symbol: 'QTZ',83 decimals: 18,84 minimalBalance: 1,85 };8687 const tx = api.tx.assetRegistry.registerForeignAsset(destination, metadata);88 const sudoTx = api.tx.sudo.sudo(tx as any);89 const events = await submitTransactionAsync(alice, sudoTx);90 const result = getGenericResult(events);91 expect(result.success).to.be.true;9293 const tx1 = api.tx.balances.transfer(randomAccount.address, 10000000000000n);94 const events1 = await submitTransactionAsync(alice, tx1);95 const result1 = getGenericResult(events1);96 expect(result1.success).to.be.true;9798 [balanceKaruraTokenInit] = await getBalance(api, [randomAccount.address]);99 {100 const {free} = (await api.query.tokens.accounts(randomAccount.addressRaw, {ForeignAsset: 0})).toJSON() as any;101 balanceQuartzForeignTokenInit = BigInt(free);102 }103 });104105 106 await usingPlaygrounds(async (helper) => {107 108 109 110 111 await helper.balance.transferToSubstrate(alice, randomAccount.address, 10n * TRANSFER_AMOUNT);112113 114 balanceQuartzTokenInit = await helper.balance.getSubstrate(randomAccount.address);115 });116 });117118 itSub('Should connect and send QTZ to Karura', async ({helper}) => {119120 121 const destination = {122 V0: {123 X2: [124 'Parent',125 {126 Parachain: KARURA_CHAIN,127 },128 ],129 },130 };131132 const beneficiary = {133 V0: {134 X1: {135 AccountId32: {136 network: 'Any',137 id: randomAccount.addressRaw,138 },139 },140 },141 };142143 const assets = {144 V1: [145 {146 id: {147 Concrete: {148 parents: 0,149 interior: 'Here',150 },151 },152 fun: {153 Fungible: TRANSFER_AMOUNT,154 },155 },156 ],157 };158159 const feeAssetItem = 0;160161 const weightLimit = {162 Limited: 5000000000,163 };164165 166 const tx = helper.getApi().tx.polkadotXcm.limitedReserveTransferAssets(destination, beneficiary, assets, feeAssetItem, weightLimit);167 const events = await submitTransactionAsync(randomAccount, tx);168 const result = getGenericResult(events);169 expect(result.success).to.be.true;170171 172 balanceQuartzTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);173174 const qtzFees = balanceQuartzTokenInit - balanceQuartzTokenMiddle - TRANSFER_AMOUNT;175 console.log('[Quartz -> Karura] transaction fees on Quartz: %s QTZ', bigIntToDecimals(qtzFees));176 expect(qtzFees > 0n).to.be.true;177178 179 await usingPlaygrounds.atUrl(karuraUrl, async (helper) => {180 181 await helper.wait.newBlocks(3);182183 184 const {free} = (await helper.getApi().query.tokens.accounts(randomAccount.addressRaw, {ForeignAsset: 0})).toJSON() as any;185 balanceQuartzForeignTokenMiddle = BigInt(free);186187 188 balanceKaruraTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);189190 const karFees = balanceKaruraTokenInit - balanceKaruraTokenMiddle;191 const qtzIncomeTransfer = balanceQuartzForeignTokenMiddle - balanceQuartzForeignTokenInit;192193 console.log(194 '[Quartz -> Karura] transaction fees on Karura: %s KAR',195 bigIntToDecimals(karFees, KARURA_DECIMALS),196 );197 console.log('[Quartz -> Karura] income %s QTZ', bigIntToDecimals(qtzIncomeTransfer));198 expect(karFees == 0n).to.be.true;199 expect(qtzIncomeTransfer == TRANSFER_AMOUNT).to.be.true;200 });201 });202203 itSub('Should connect to Karura and send QTZ back', async ({helper}) => {204205 206 await usingPlaygrounds.atUrl(karuraUrl, async (helper) => {207 const destination = {208 V1: {209 parents: 1,210 interior: {211 X2: [212 {Parachain: QUARTZ_CHAIN},213 {214 AccountId32: {215 network: 'Any',216 id: randomAccount.addressRaw,217 },218 },219 ],220 },221 },222 };223224 const id = {225 ForeignAsset: 0,226 };227228 const destWeight = 50000000;229230 231 const tx = helper.getApi().tx.xTokens.transfer(id as any, TRANSFER_AMOUNT, destination, destWeight);232 const events = await submitTransactionAsync(randomAccount, tx);233 const result = getGenericResult(events);234 expect(result.success).to.be.true;235236 237 balanceKaruraTokenFinal = await helper.balance.getSubstrate(randomAccount.address);238 {239 240 const {free} = (await helper.getApi().query.tokens.accounts(randomAccount.addressRaw, id)).toJSON() as any;241 balanceQuartzForeignTokenFinal = BigInt(free);242 }243244 const karFees = balanceKaruraTokenMiddle - balanceKaruraTokenFinal;245 const qtzOutcomeTransfer = balanceQuartzForeignTokenMiddle - balanceQuartzForeignTokenFinal;246247 console.log(248 '[Karura -> Quartz] transaction fees on Karura: %s KAR',249 bigIntToDecimals(karFees, KARURA_DECIMALS),250 );251 console.log('[Karura -> Quartz] outcome %s QTZ', bigIntToDecimals(qtzOutcomeTransfer));252253 expect(karFees > 0).to.be.true;254 expect(qtzOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;255 });256257 258 259 await helper.wait.newBlocks(3);260261 262 balanceQuartzTokenFinal = await helper.balance.getSubstrate(randomAccount.address);263 const actuallyDelivered = balanceQuartzTokenFinal - balanceQuartzTokenMiddle;264 expect(actuallyDelivered > 0).to.be.true;265266 console.log('[Karura -> Quartz] actually delivered %s QTZ', bigIntToDecimals(actuallyDelivered));267268 const qtzFees = TRANSFER_AMOUNT - actuallyDelivered;269 console.log('[Karura -> Quartz] transaction fees on Quartz: %s QTZ', bigIntToDecimals(qtzFees));270 expect(qtzFees == 0n).to.be.true;271 });272});273274275describeXcm('[XCM] Integration test: Quartz rejects non-native tokens', () => {276 let alice: IKeyringPair;277278 before(async () => {279 await usingPlaygrounds(async (_helper, privateKey) => {280 alice = privateKey('//Alice');281 });282 });283284 itSub('Quartz rejects tokens from the Relay', async ({helper}) => {285 await usingPlaygrounds.atUrl(relayUrl, async (helper) => {286 const destination = {287 V1: {288 parents: 0,289 interior: {X1: {290 Parachain: QUARTZ_CHAIN,291 },292 },293 }};294295 const beneficiary = {296 V1: {297 parents: 0,298 interior: {X1: {299 AccountId32: {300 network: 'Any',301 id: alice.addressRaw,302 },303 }},304 },305 };306307 const assets = {308 V1: [309 {310 id: {311 Concrete: {312 parents: 0,313 interior: 'Here',314 },315 },316 fun: {317 Fungible: 50_000_000_000_000_000n,318 },319 },320 ],321 };322323 const feeAssetItem = 0;324325 const weightLimit = {326 Limited: 5_000_000_000,327 };328329 330 const tx = helper.getApi().tx.xcmPallet.limitedReserveTransferAssets(destination, beneficiary, assets, feeAssetItem, weightLimit);331 const events = await submitTransactionAsync(alice, tx);332 const result = getGenericResult(events);333 expect(result.success).to.be.true;334 });335336 const maxWaitBlocks = 3;337338 339 const dmpQueueExecutedDownward = await waitEvent(340 helper.getApi(),341 maxWaitBlocks,342 'dmpQueue',343 'ExecutedDownward',344 );345346 expect(347 dmpQueueExecutedDownward != null,348 '[Relay] dmpQueue.ExecutedDownward event is expected',349 ).to.be.true;350351 const event = dmpQueueExecutedDownward!.event;352 const outcome = event.data[1] as XcmV2TraitsOutcome;353354 expect(355 outcome.isIncomplete,356 '[Relay] The outcome of the XCM should be `Incomplete`',357 ).to.be.true;358359 const incomplete = outcome.asIncomplete;360 expect(361 incomplete[1].toString() == 'AssetNotFound',362 '[Relay] The XCM error should be `AssetNotFound`',363 ).to.be.true;364 });365366 itSub('Quartz rejects KAR tokens from Karura', async ({helper}) => {367 await usingPlaygrounds.atUrl(karuraUrl, async (helper) => {368 const destination = {369 V1: {370 parents: 1,371 interior: {372 X2: [373 {Parachain: QUARTZ_CHAIN},374 {375 AccountId32: {376 network: 'Any',377 id: alice.addressRaw,378 },379 },380 ],381 },382 },383 };384385 const id = {386 Token: 'KAR',387 };388389 const destWeight = 50000000;390391 392 const tx = helper.getApi().tx.xTokens.transfer(id as any, 100_000_000_000, destination, destWeight);393 const events = await submitTransactionAsync(alice, tx);394 const result = getGenericResult(events);395 expect(result.success).to.be.true;396 });397398 const maxWaitBlocks = 3;399400 401 const xcmpQueueFailEvent = await waitEvent(helper.getApi(), maxWaitBlocks, 'xcmpQueue', 'Fail');402403 expect(404 xcmpQueueFailEvent != null,405 '[Karura] xcmpQueue.FailEvent event is expected',406 ).to.be.true;407408 const event = xcmpQueueFailEvent!.event;409 const outcome = event.data[1] as XcmV2TraitsError;410411 expect(412 outcome.isUntrustedReserveLocation,413 '[Karura] The XCM error should be `UntrustedReserveLocation`',414 ).to.be.true;415 });416});417418describeXcm('[XCM] Integration test: Exchanging QTZ with Moonriver', () => {419420 421 let quartzAlice: IKeyringPair;422 let quartzAssetLocation;423424 let randomAccountQuartz: IKeyringPair;425 let randomAccountMoonriver: IKeyringPair;426427 428 let assetId: string;429430 const moonriverKeyring = new Keyring({type: 'ethereum'});431 const alithPrivateKey = '0x5fb92d6e98884f76de468fa3f6278f8807c48bebc13595d45af5bdc4da702133';432 const baltatharPrivateKey = '0x8075991ce870b93a8870eca0c0f91913d12f47948ca0fd25b49c6fa7cdbeee8b';433 const dorothyPrivateKey = '0x39539ab1876910bbf3a223d84a29e28f1cb4e2e456503e7e91ed39b2e7223d68';434435 const alithAccount = moonriverKeyring.addFromUri(alithPrivateKey, undefined, 'ethereum');436 const baltatharAccount = moonriverKeyring.addFromUri(baltatharPrivateKey, undefined, 'ethereum');437 const dorothyAccount = moonriverKeyring.addFromUri(dorothyPrivateKey, undefined, 'ethereum');438439 const councilVotingThreshold = 2;440 const technicalCommitteeThreshold = 2;441 const votingPeriod = 3;442 const delayPeriod = 0;443444 const quartzAssetMetadata = {445 name: 'xcQuartz',446 symbol: 'xcQTZ',447 decimals: 18,448 isFrozen: false,449 minimalBalance: 1,450 };451452 let balanceQuartzTokenInit: bigint;453 let balanceQuartzTokenMiddle: bigint;454 let balanceQuartzTokenFinal: bigint;455 let balanceForeignQtzTokenInit: bigint;456 let balanceForeignQtzTokenMiddle: bigint;457 let balanceForeignQtzTokenFinal: bigint;458 let balanceMovrTokenInit: bigint;459 let balanceMovrTokenMiddle: bigint;460 let balanceMovrTokenFinal: bigint;461462 before(async () => {463 await usingPlaygrounds(async (_helper, privateKey) => {464 const keyringEth = new Keyring({type: 'ethereum'});465 const keyringSr25519 = new Keyring({type: 'sr25519'});466467 quartzAlice = privateKey('//Alice');468 randomAccountQuartz = generateKeyringPair(keyringSr25519);469 randomAccountMoonriver = generateKeyringPair(keyringEth);470471 balanceForeignQtzTokenInit = 0n;472 });473474 await usingPlaygrounds.atUrl(moonriverUrl, async (helper) => {475 476477 const api = helper.getApi();478479 480 console.log('Sponsoring Dorothy.......');481 482 483 484 485 await helper.balance.transferToSubstrate(alithAccount, dorothyAccount.address, 11_000_000_000_000_000_000n);486 console.log('Sponsoring Dorothy.......DONE');487 488489 const sourceLocation: MultiLocation = api.createType(490 'MultiLocation',491 {492 parents: 1,493 interior: {X1: {Parachain: QUARTZ_CHAIN}},494 },495 );496497 quartzAssetLocation = {XCM: sourceLocation};498 const existentialDeposit = 1;499 const isSufficient = true;500 const unitsPerSecond = '1';501 const numAssetsWeightHint = 0;502503 const registerTx = api.tx.assetManager.registerForeignAsset(504 quartzAssetLocation,505 quartzAssetMetadata,506 existentialDeposit,507 isSufficient,508 );509 console.log('Encoded proposal for registerAsset is %s', registerTx.method.toHex() || '');510511 const setUnitsTx = api.tx.assetManager.setAssetUnitsPerSecond(512 quartzAssetLocation,513 unitsPerSecond,514 numAssetsWeightHint,515 );516 console.log('Encoded proposal for setAssetUnitsPerSecond is %s', setUnitsTx.method.toHex() || '');517518 const batchCall = api.tx.utility.batchAll([registerTx, setUnitsTx]);519 console.log('Encoded proposal for batchCall is %s', batchCall.method.toHex() || '');520521 522 console.log('Note motion preimage.......');523 const encodedProposal = batchCall?.method.toHex() || '';524 const proposalHash = blake2AsHex(encodedProposal);525 console.log('Encoded proposal for batch utility after schedule is %s', encodedProposal);526 console.log('Encoded proposal hash for batch utility after schedule is %s', proposalHash);527 console.log('Encoded length %d', encodedProposal.length);528529 const tx1 = api.tx.democracy.notePreimage(encodedProposal);530 const events1 = await submitTransactionAsync(baltatharAccount, tx1);531 const result1 = getGenericResult(events1);532 expect(result1.success).to.be.true;533 console.log('Note motion preimage.......DONE');534 535536 537 console.log('Propose external motion through council.......');538 const externalMotion = api.tx.democracy.externalProposeMajority(proposalHash);539 const tx2 = api.tx.councilCollective.propose(540 councilVotingThreshold,541 externalMotion,542 externalMotion.encodedLength,543 );544 const events2 = await submitTransactionAsync(baltatharAccount, tx2);545 const result2 = getGenericResult(events2);546 expect(result2.success).to.be.true;547548 const encodedMotion = externalMotion?.method.toHex() || '';549 const motionHash = blake2AsHex(encodedMotion);550 console.log('Motion hash is %s', motionHash);551552 const tx3 = api.tx.councilCollective.vote(motionHash, 0, true);553 {554 const events3 = await submitTransactionAsync(dorothyAccount, tx3);555 const result3 = getGenericResult(events3);556 expect(result3.success).to.be.true;557 }558 {559 const events3 = await submitTransactionAsync(baltatharAccount, tx3);560 const result3 = getGenericResult(events3);561 expect(result3.success).to.be.true;562 }563564 const tx4 = api.tx.councilCollective.close(motionHash, 0, 1_000_000_000, externalMotion.encodedLength);565 const events4 = await submitTransactionAsync(dorothyAccount, tx4);566 const result4 = getGenericResult(events4);567 expect(result4.success).to.be.true;568 console.log('Propose external motion through council.......DONE');569 570571 572 console.log('Fast track proposal through technical committee.......');573 const fastTrack = api.tx.democracy.fastTrack(proposalHash, votingPeriod, delayPeriod);574 const tx5 = api.tx.techCommitteeCollective.propose(575 technicalCommitteeThreshold,576 fastTrack,577 fastTrack.encodedLength,578 );579 const events5 = await submitTransactionAsync(alithAccount, tx5);580 const result5 = getGenericResult(events5);581 expect(result5.success).to.be.true;582583 const encodedFastTrack = fastTrack?.method.toHex() || '';584 const fastTrackHash = blake2AsHex(encodedFastTrack);585 console.log('FastTrack hash is %s', fastTrackHash);586587 const proposalIdx = Number(await api.query.techCommitteeCollective.proposalCount()) - 1;588 const tx6 = api.tx.techCommitteeCollective.vote(fastTrackHash, proposalIdx, true);589 {590 const events6 = await submitTransactionAsync(baltatharAccount, tx6);591 const result6 = getGenericResult(events6);592 expect(result6.success).to.be.true;593 }594 {595 const events6 = await submitTransactionAsync(alithAccount, tx6);596 const result6 = getGenericResult(events6);597 expect(result6.success).to.be.true;598 }599600 const tx7 = api.tx.techCommitteeCollective601 .close(fastTrackHash, proposalIdx, 1_000_000_000, fastTrack.encodedLength);602 const events7 = await submitTransactionAsync(baltatharAccount, tx7);603 const result7 = getGenericResult(events7);604 expect(result7.success).to.be.true;605 console.log('Fast track proposal through technical committee.......DONE');606 607608 609 console.log('Referendum voting.......');610 const tx8 = api.tx.democracy.vote(611 0,612 {Standard: {balance: 10_000_000_000_000_000_000n, vote: {aye: true, conviction: 1}}},613 );614 const events8 = await submitTransactionAsync(dorothyAccount, tx8);615 const result8 = getGenericResult(events8);616 expect(result8.success).to.be.true;617 console.log('Referendum voting.......DONE');618 619620 621 console.log('Acquire Quartz AssetId Info on Moonriver.......');622623 624 625 await helper.wait.newBlocks(5);626627 assetId = (await api.query.assetManager.assetTypeId({628 XCM: sourceLocation,629 })).toString();630631 console.log('QTZ asset ID is %s', assetId);632 console.log('Acquire Quartz AssetId Info on Moonriver.......DONE');633 634635 636 console.log('Sponsoring random Account.......');637 638 639 640 641 await helper.balance.transferToSubstrate(baltatharAccount, randomAccountMoonriver.address, 11_000_000_000_000_000_000n);642 console.log('Sponsoring random Account.......DONE');643 644645 646 balanceMovrTokenInit = await helper.balance.getSubstrate(randomAccountMoonriver.address);647 });648649 await usingPlaygrounds(async (helper) => {650 651 652 653 654 await helper.balance.transferToSubstrate(quartzAlice, randomAccountQuartz.address, 10n * TRANSFER_AMOUNT);655656 657 balanceQuartzTokenInit = await helper.balance.getSubstrate(randomAccountQuartz.address);658 });659 });660661 itSub('Should connect and send QTZ to Moonriver', async ({helper}) => {662 const currencyId = {663 NativeAssetId: 'Here',664 };665 const dest = {666 V1: {667 parents: 1,668 interior: {669 X2: [670 {Parachain: MOONRIVER_CHAIN},671 {AccountKey20: {network: 'Any', key: randomAccountMoonriver.address}},672 ],673 },674 },675 };676 const amount = TRANSFER_AMOUNT;677 const destWeight = 850000000;678679 680 const tx = helper.getApi().tx.xTokens.transfer(currencyId, amount, dest, destWeight);681 const events = await submitTransactionAsync(randomAccountQuartz, tx);682 const result = getGenericResult(events);683 expect(result.success).to.be.true;684685 686 balanceQuartzTokenMiddle = await helper.balance.getSubstrate(randomAccountQuartz.address);687 expect(balanceQuartzTokenMiddle < balanceQuartzTokenInit).to.be.true;688689 const transactionFees = balanceQuartzTokenInit - balanceQuartzTokenMiddle - TRANSFER_AMOUNT;690 console.log('[Quartz -> Moonriver] transaction fees on Quartz: %s QTZ', bigIntToDecimals(transactionFees));691 expect(transactionFees > 0).to.be.true;692693 await usingPlaygrounds.atUrl(moonriverUrl, async (helper) => {694 695 await helper.wait.newBlocks(3);696697 698 balanceMovrTokenMiddle = await helper.balance.getSubstrate(randomAccountMoonriver.address);699700 const movrFees = balanceMovrTokenInit - balanceMovrTokenMiddle;701 console.log('[Quartz -> Moonriver] transaction fees on Moonriver: %s MOVR',bigIntToDecimals(movrFees));702 expect(movrFees == 0n).to.be.true;703704 705 const qtzRandomAccountAsset = (706 await helper.getApi().query.assets.account(assetId, randomAccountMoonriver.address)707 ).toJSON()! as any;708709 balanceForeignQtzTokenMiddle = BigInt(qtzRandomAccountAsset['balance']);710 const qtzIncomeTransfer = balanceForeignQtzTokenMiddle - balanceForeignQtzTokenInit;711 console.log('[Quartz -> Moonriver] income %s QTZ', bigIntToDecimals(qtzIncomeTransfer));712 expect(qtzIncomeTransfer == TRANSFER_AMOUNT).to.be.true;713 });714 });715716 itSub('Should connect to Moonriver and send QTZ back', async ({helper}) => {717 await usingPlaygrounds.atUrl(moonriverUrl, async (helper) => {718 const asset = {719 V1: {720 id: {721 Concrete: {722 parents: 1,723 interior: {724 X1: {Parachain: QUARTZ_CHAIN},725 },726 },727 },728 fun: {729 Fungible: TRANSFER_AMOUNT,730 },731 },732 };733 const destination = {734 V1: {735 parents: 1,736 interior: {737 X2: [738 {Parachain: QUARTZ_CHAIN},739 {AccountId32: {network: 'Any', id: randomAccountQuartz.addressRaw}},740 ],741 },742 },743 };744 const destWeight = 50000000;745746 747 const tx = helper.getApi().tx.xTokens.transferMultiasset(asset, destination, destWeight);748 const events = await submitTransactionAsync(randomAccountMoonriver, tx);749 const result = getGenericResult(events);750 expect(result.success).to.be.true;751752 753 balanceMovrTokenFinal = await helper.balance.getSubstrate(randomAccountMoonriver.address);754755 const movrFees = balanceMovrTokenMiddle - balanceMovrTokenFinal;756 console.log('[Moonriver -> Quartz] transaction fees on Moonriver: %s MOVR', bigIntToDecimals(movrFees));757 expect(movrFees > 0).to.be.true;758759 const qtzRandomAccountAsset = (760 await helper.getApi().query.assets.account(assetId, randomAccountMoonriver.address)761 ).toJSON()! as any;762763 expect(qtzRandomAccountAsset).to.be.null;764765 balanceForeignQtzTokenFinal = 0n;766767 const qtzOutcomeTransfer = balanceForeignQtzTokenMiddle - balanceForeignQtzTokenFinal;768 console.log('[Quartz -> Moonriver] outcome %s QTZ', bigIntToDecimals(qtzOutcomeTransfer));769 expect(qtzOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;770 });771772 773 await helper.wait.newBlocks(3);774775 776 balanceQuartzTokenFinal = await helper.balance.getSubstrate(randomAccountQuartz.address);777 const actuallyDelivered = balanceQuartzTokenFinal - balanceQuartzTokenMiddle;778 expect(actuallyDelivered > 0).to.be.true;779780 console.log('[Moonriver -> Quartz] actually delivered %s QTZ', bigIntToDecimals(actuallyDelivered));781782 const qtzFees = TRANSFER_AMOUNT - actuallyDelivered;783 console.log('[Moonriver -> Quartz] transaction fees on Quartz: %s QTZ', bigIntToDecimals(qtzFees));784 expect(qtzFees == 0n).to.be.true;785 });786});