1234567891011121314151617import 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';29import {XcmV2TraitsError, XcmV2TraitsOutcome} from '../interfaces';3031chai.use(chaiAsPromised);32const expect = chai.expect;3334const UNIQUE_CHAIN = 2037;35const ACALA_CHAIN = 2000;36const MOONBEAM_CHAIN = 2004;3738const RELAY_PORT = 9844;39const ACALA_PORT = 9946;40const MOONBEAM_PORT = 9947;4142const ACALA_DECIMALS = 12;4344const TRANSFER_AMOUNT = 2000000000000000000000000n;4546function parachainApiOptions(port: number): ApiOptions {47 return {48 provider: new WsProvider('ws://127.0.0.1:' + port.toString()),49 };50}5152function acalaOptions(): ApiOptions {53 return parachainApiOptions(ACALA_PORT);54}5556function moonbeamOptions(): ApiOptions {57 return parachainApiOptions(MOONBEAM_PORT);58}5960function relayOptions(): ApiOptions {61 return parachainApiOptions(RELAY_PORT);62}6364describe_xcm('[XCM] Integration test: Exchanging tokens with Acala', () => {65 let alice: IKeyringPair;66 let randomAccount: IKeyringPair;6768 let balanceUniqueTokenInit: bigint;69 let balanceUniqueTokenMiddle: bigint;70 let balanceUniqueTokenFinal: bigint;71 let balanceAcalaTokenInit: bigint;72 let balanceAcalaTokenMiddle: bigint;73 let balanceAcalaTokenFinal: bigint;74 let balanceUniqueForeignTokenInit: bigint;75 let balanceUniqueForeignTokenMiddle: bigint;76 let balanceUniqueForeignTokenFinal: bigint;7778 before(async () => {79 await usingApi(async (api, privateKeyWrapper) => {80 const keyringSr25519 = new Keyring({type: 'sr25519'});8182 alice = privateKeyWrapper('//Alice');83 randomAccount = generateKeyringPair(keyringSr25519);84 });8586 87 await usingApi(88 async (api) => {89 const destination = {90 V0: {91 X2: [92 'Parent',93 {94 Parachain: UNIQUE_CHAIN,95 },96 ],97 },98 };99100 const metadata = {101 name: 'UNQ',102 symbol: 'UNQ',103 decimals: 18,104 minimalBalance: 1,105 };106107 const tx = api.tx.assetRegistry.registerForeignAsset(destination, metadata);108 const sudoTx = api.tx.sudo.sudo(tx as any);109 const events = await submitTransactionAsync(alice, sudoTx);110 const result = getGenericResult(events);111 expect(result.success).to.be.true;112113 const tx1 = api.tx.balances.transfer(randomAccount.address, 10000000000000n);114 const events1 = await submitTransactionAsync(alice, tx1);115 const result1 = getGenericResult(events1);116 expect(result1.success).to.be.true;117118 [balanceAcalaTokenInit] = await getBalance(api, [randomAccount.address]);119 {120 const {free} = (await api.query.tokens.accounts(randomAccount.addressRaw, {ForeignAsset: 0})).toJSON() as any;121 balanceUniqueForeignTokenInit = BigInt(free);122 }123 },124 acalaOptions(),125 );126127 128 await usingApi(async (api) => {129 const tx0 = api.tx.balances.transfer(randomAccount.address, 10n * TRANSFER_AMOUNT);130 const events0 = await submitTransactionAsync(alice, tx0);131 const result0 = getGenericResult(events0);132 expect(result0.success).to.be.true;133134 [balanceUniqueTokenInit] = await getBalance(api, [randomAccount.address]);135 });136 });137138 it('Should connect and send UNQ to Acala', async () => {139140 141 await usingApi(async (api) => {142143 const destination = {144 V0: {145 X2: [146 'Parent',147 {148 Parachain: ACALA_CHAIN,149 },150 ],151 },152 };153154 const beneficiary = {155 V0: {156 X1: {157 AccountId32: {158 network: 'Any',159 id: randomAccount.addressRaw,160 },161 },162 },163 };164165 const assets = {166 V1: [167 {168 id: {169 Concrete: {170 parents: 0,171 interior: 'Here',172 },173 },174 fun: {175 Fungible: TRANSFER_AMOUNT,176 },177 },178 ],179 };180181 const feeAssetItem = 0;182183 const weightLimit = {184 Limited: 5000000000,185 };186187 const tx = api.tx.polkadotXcm.limitedReserveTransferAssets(destination, beneficiary, assets, feeAssetItem, weightLimit);188 const events = await submitTransactionAsync(randomAccount, tx);189 const result = getGenericResult(events);190 expect(result.success).to.be.true;191192 [balanceUniqueTokenMiddle] = await getBalance(api, [randomAccount.address]);193194 const unqFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;195 console.log('[Unique -> Acala] transaction fees on Unique: %s UNQ', bigIntToDecimals(unqFees));196 expect(unqFees > 0n).to.be.true;197 });198199 200 await usingApi(201 async (api) => {202 await waitNewBlocks(api, 3);203 const {free} = (await api.query.tokens.accounts(randomAccount.addressRaw, {ForeignAsset: 0})).toJSON() as any;204 balanceUniqueForeignTokenMiddle = BigInt(free);205206 [balanceAcalaTokenMiddle] = await getBalance(api, [randomAccount.address]);207208 const acaFees = balanceAcalaTokenInit - balanceAcalaTokenMiddle;209 const unqIncomeTransfer = balanceUniqueForeignTokenMiddle - balanceUniqueForeignTokenInit;210211 console.log(212 '[Unique -> Acala] transaction fees on Acala: %s ACA',213 bigIntToDecimals(acaFees, ACALA_DECIMALS),214 );215 console.log('[Unique -> Acala] income %s UNQ', bigIntToDecimals(unqIncomeTransfer));216 expect(acaFees == 0n).to.be.true;217 expect(unqIncomeTransfer == TRANSFER_AMOUNT).to.be.true;218 },219 acalaOptions(),220 );221 });222223 it('Should connect to Acala and send UNQ back', async () => {224225 226 await usingApi(227 async (api) => {228 const destination = {229 V1: {230 parents: 1,231 interior: {232 X2: [233 {Parachain: UNIQUE_CHAIN},234 {235 AccountId32: {236 network: 'Any',237 id: randomAccount.addressRaw,238 },239 },240 ],241 },242 },243 };244245 const id = {246 ForeignAsset: 0,247 };248249 const destWeight = 50000000;250251 const tx = api.tx.xTokens.transfer(id as any, TRANSFER_AMOUNT, destination, destWeight);252 const events = await submitTransactionAsync(randomAccount, tx);253 const result = getGenericResult(events);254 expect(result.success).to.be.true;255256 [balanceAcalaTokenFinal] = await getBalance(api, [randomAccount.address]);257 {258 const {free} = (await api.query.tokens.accounts(randomAccount.addressRaw, id)).toJSON() as any;259 balanceUniqueForeignTokenFinal = BigInt(free);260 }261262 const acaFees = balanceAcalaTokenMiddle - balanceAcalaTokenFinal;263 const unqOutcomeTransfer = balanceUniqueForeignTokenMiddle - balanceUniqueForeignTokenFinal;264265 console.log(266 '[Acala -> Unique] transaction fees on Acala: %s ACA',267 bigIntToDecimals(acaFees, ACALA_DECIMALS),268 );269 console.log('[Acala -> Unique] outcome %s UNQ', bigIntToDecimals(unqOutcomeTransfer));270271 expect(acaFees > 0).to.be.true;272 expect(unqOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;273 },274 acalaOptions(),275 );276277 278 await usingApi(async (api) => {279 await waitNewBlocks(api, 3);280281 [balanceUniqueTokenFinal] = await getBalance(api, [randomAccount.address]);282 const actuallyDelivered = balanceUniqueTokenFinal - balanceUniqueTokenMiddle;283 expect(actuallyDelivered > 0).to.be.true;284285 console.log('[Acala -> Unique] actually delivered %s UNQ', bigIntToDecimals(actuallyDelivered));286287 const unqFees = TRANSFER_AMOUNT - actuallyDelivered;288 console.log('[Acala -> Unique] transaction fees on Unique: %s UNQ', bigIntToDecimals(unqFees));289 expect(unqFees == 0n).to.be.true;290 });291 });292});293294295describe_xcm('[XCM] Integration test: Unique rejects non-native tokens', () => {296 let alice: IKeyringPair;297298 before(async () => {299 await usingApi(async (api, privateKeyWrapper) => {300 alice = privateKeyWrapper('//Alice');301 });302 });303304 it('Unique rejects tokens from the Relay', async () => {305 await usingApi(async (api) => {306 const destination = {307 V1: {308 parents: 0,309 interior: {X1: {310 Parachain: UNIQUE_CHAIN,311 },312 },313 }};314315 const beneficiary = {316 V1: {317 parents: 0,318 interior: {X1: {319 AccountId32: {320 network: 'Any',321 id: alice.addressRaw,322 },323 }},324 },325 };326327 const assets = {328 V1: [329 {330 id: {331 Concrete: {332 parents: 0,333 interior: 'Here',334 },335 },336 fun: {337 Fungible: 50_000_000_000_000_000n,338 },339 },340 ],341 };342343 const feeAssetItem = 0;344345 const weightLimit = {346 Limited: 5_000_000_000,347 };348349 const tx = api.tx.xcmPallet.limitedReserveTransferAssets(destination, beneficiary, assets, feeAssetItem, weightLimit);350 const events = await submitTransactionAsync(alice, tx);351 const result = getGenericResult(events);352 expect(result.success).to.be.true;353 }, relayOptions());354355 await usingApi(async api => {356 const maxWaitBlocks = 3;357 const dmpQueueExecutedDownward = await waitEvent(358 api,359 maxWaitBlocks,360 'dmpQueue',361 'ExecutedDownward',362 );363364 expect(365 dmpQueueExecutedDownward != null,366 '[Relay] dmpQueue.ExecutedDownward event is expected',367 ).to.be.true;368369 const event = dmpQueueExecutedDownward!.event;370 const outcome = event.data[1] as XcmV2TraitsOutcome;371372 expect(373 outcome.isIncomplete,374 '[Relay] The outcome of the XCM should be `Incomplete`',375 ).to.be.true;376377 const incomplete = outcome.asIncomplete;378 expect(379 incomplete[1].toString() == 'AssetNotFound',380 '[Relay] The XCM error should be `AssetNotFound`',381 ).to.be.true;382 });383 });384385 it('Unique rejects ACA tokens from Acala', async () => {386 await usingApi(async (api) => {387 const destination = {388 V1: {389 parents: 1,390 interior: {391 X2: [392 {Parachain: UNIQUE_CHAIN},393 {394 AccountId32: {395 network: 'Any',396 id: alice.addressRaw,397 },398 },399 ],400 },401 },402 };403404 const id = {405 Token: 'ACA',406 };407408 const destWeight = 50000000;409410 const tx = api.tx.xTokens.transfer(id as any, 100_000_000_000, destination, destWeight);411 const events = await submitTransactionAsync(alice, tx);412 const result = getGenericResult(events);413 expect(result.success).to.be.true;414 }, acalaOptions());415416 await usingApi(async api => {417 const maxWaitBlocks = 3;418 const xcmpQueueFailEvent = await waitEvent(api, maxWaitBlocks, 'xcmpQueue', 'Fail');419420 expect(421 xcmpQueueFailEvent != null,422 '[Acala] xcmpQueue.FailEvent event is expected',423 ).to.be.true;424425 const event = xcmpQueueFailEvent!.event;426 const outcome = event.data[1] as XcmV2TraitsError;427428 expect(429 outcome.isUntrustedReserveLocation,430 '[Acala] The XCM error should be `UntrustedReserveLocation`',431 ).to.be.true;432 });433 });434});435436describe_xcm('[XCM] Integration test: Exchanging UNQ with Moonbeam', () => {437438 439 let uniqueAlice: IKeyringPair;440 let uniqueAssetLocation;441442 let randomAccountUnique: IKeyringPair;443 let randomAccountMoonbeam: IKeyringPair;444445 446 let assetId: string;447448 const moonbeamKeyring = new Keyring({type: 'ethereum'});449 const alithPrivateKey = '0x5fb92d6e98884f76de468fa3f6278f8807c48bebc13595d45af5bdc4da702133';450 const baltatharPrivateKey = '0x8075991ce870b93a8870eca0c0f91913d12f47948ca0fd25b49c6fa7cdbeee8b';451 const dorothyPrivateKey = '0x39539ab1876910bbf3a223d84a29e28f1cb4e2e456503e7e91ed39b2e7223d68';452453 const alithAccount = moonbeamKeyring.addFromUri(alithPrivateKey, undefined, 'ethereum');454 const baltatharAccount = moonbeamKeyring.addFromUri(baltatharPrivateKey, undefined, 'ethereum');455 const dorothyAccount = moonbeamKeyring.addFromUri(dorothyPrivateKey, undefined, 'ethereum');456457 const councilVotingThreshold = 2;458 const technicalCommitteeThreshold = 2;459 const votingPeriod = 3;460 const delayPeriod = 0;461462 const uniqueAssetMetadata = {463 name: 'xcUnique',464 symbol: 'xcUNQ',465 decimals: 18,466 isFrozen: false,467 minimalBalance: 1,468 };469470 let balanceUniqueTokenInit: bigint;471 let balanceUniqueTokenMiddle: bigint;472 let balanceUniqueTokenFinal: bigint;473 let balanceForeignUnqTokenInit: bigint;474 let balanceForeignUnqTokenMiddle: bigint;475 let balanceForeignUnqTokenFinal: bigint;476 let balanceGlmrTokenInit: bigint;477 let balanceGlmrTokenMiddle: bigint;478 let balanceGlmrTokenFinal: bigint;479480 before(async () => {481 await usingApi(async (api, privateKeyWrapper) => {482 const keyringEth = new Keyring({type: 'ethereum'});483 const keyringSr25519 = new Keyring({type: 'sr25519'});484485 uniqueAlice = privateKeyWrapper('//Alice');486 randomAccountUnique = generateKeyringPair(keyringSr25519);487 randomAccountMoonbeam = generateKeyringPair(keyringEth);488489 balanceForeignUnqTokenInit = 0n;490 });491492 await usingApi(493 async (api) => {494495 496 console.log('Sponsoring Dorothy.......');497 const tx0 = api.tx.balances.transfer(dorothyAccount.address, 11_000_000_000_000_000_000n);498 const events0 = await submitTransactionAsync(alithAccount, tx0);499 const result0 = getGenericResult(events0);500 expect(result0.success).to.be.true;501 console.log('Sponsoring Dorothy.......DONE');502 503504 const sourceLocation: MultiLocation = api.createType(505 'MultiLocation',506 {507 parents: 1,508 interior: {X1: {Parachain: UNIQUE_CHAIN}},509 },510 );511512 uniqueAssetLocation = {XCM: sourceLocation};513 const existentialDeposit = 1;514 const isSufficient = true;515 const unitsPerSecond = '1';516 const numAssetsWeightHint = 0;517518 const registerTx = api.tx.assetManager.registerForeignAsset(519 uniqueAssetLocation,520 uniqueAssetMetadata,521 existentialDeposit,522 isSufficient,523 );524 console.log('Encoded proposal for registerAsset is %s', registerTx.method.toHex() || '');525526 const setUnitsTx = api.tx.assetManager.setAssetUnitsPerSecond(527 uniqueAssetLocation,528 unitsPerSecond,529 numAssetsWeightHint,530 );531 console.log('Encoded proposal for setAssetUnitsPerSecond is %s', setUnitsTx.method.toHex() || '');532533 const batchCall = api.tx.utility.batchAll([registerTx, setUnitsTx]);534 console.log('Encoded proposal for batchCall is %s', batchCall.method.toHex() || '');535536 537 console.log('Note motion preimage.......');538 const encodedProposal = batchCall?.method.toHex() || '';539 const proposalHash = blake2AsHex(encodedProposal);540 console.log('Encoded proposal for batch utility after schedule is %s', encodedProposal);541 console.log('Encoded proposal hash for batch utility after schedule is %s', proposalHash);542 console.log('Encoded length %d', encodedProposal.length);543544 const tx1 = api.tx.democracy.notePreimage(encodedProposal);545 const events1 = await submitTransactionAsync(baltatharAccount, tx1);546 const result1 = getGenericResult(events1);547 expect(result1.success).to.be.true;548 console.log('Note motion preimage.......DONE');549 550551 552 console.log('Propose external motion through council.......');553 const externalMotion = api.tx.democracy.externalProposeMajority(proposalHash);554 const tx2 = api.tx.councilCollective.propose(555 councilVotingThreshold,556 externalMotion,557 externalMotion.encodedLength,558 );559 const events2 = await submitTransactionAsync(baltatharAccount, tx2);560 const result2 = getGenericResult(events2);561 expect(result2.success).to.be.true;562563 const encodedMotion = externalMotion?.method.toHex() || '';564 const motionHash = blake2AsHex(encodedMotion);565 console.log('Motion hash is %s', motionHash);566567 const tx3 = api.tx.councilCollective.vote(motionHash, 0, true);568 {569 const events3 = await submitTransactionAsync(dorothyAccount, tx3);570 const result3 = getGenericResult(events3);571 expect(result3.success).to.be.true;572 }573 {574 const events3 = await submitTransactionAsync(baltatharAccount, tx3);575 const result3 = getGenericResult(events3);576 expect(result3.success).to.be.true;577 }578579 const tx4 = api.tx.councilCollective.close(motionHash, 0, 1_000_000_000, externalMotion.encodedLength);580 const events4 = await submitTransactionAsync(dorothyAccount, tx4);581 const result4 = getGenericResult(events4);582 expect(result4.success).to.be.true;583 console.log('Propose external motion through council.......DONE');584 585586 587 console.log('Fast track proposal through technical committee.......');588 const fastTrack = api.tx.democracy.fastTrack(proposalHash, votingPeriod, delayPeriod);589 const tx5 = api.tx.techCommitteeCollective.propose(590 technicalCommitteeThreshold,591 fastTrack,592 fastTrack.encodedLength,593 );594 const events5 = await submitTransactionAsync(alithAccount, tx5);595 const result5 = getGenericResult(events5);596 expect(result5.success).to.be.true;597598 const encodedFastTrack = fastTrack?.method.toHex() || '';599 const fastTrackHash = blake2AsHex(encodedFastTrack);600 console.log('FastTrack hash is %s', fastTrackHash);601602 const proposalIdx = Number(await api.query.techCommitteeCollective.proposalCount()) - 1;603 const tx6 = api.tx.techCommitteeCollective.vote(fastTrackHash, proposalIdx, true);604 {605 const events6 = await submitTransactionAsync(baltatharAccount, tx6);606 const result6 = getGenericResult(events6);607 expect(result6.success).to.be.true;608 }609 {610 const events6 = await submitTransactionAsync(alithAccount, tx6);611 const result6 = getGenericResult(events6);612 expect(result6.success).to.be.true;613 }614615 const tx7 = api.tx.techCommitteeCollective616 .close(fastTrackHash, proposalIdx, 1_000_000_000, fastTrack.encodedLength);617 const events7 = await submitTransactionAsync(baltatharAccount, tx7);618 const result7 = getGenericResult(events7);619 expect(result7.success).to.be.true;620 console.log('Fast track proposal through technical committee.......DONE');621 622623 624 console.log('Referendum voting.......');625 const tx8 = api.tx.democracy.vote(626 0,627 {Standard: {balance: 10_000_000_000_000_000_000n, vote: {aye: true, conviction: 1}}},628 );629 const events8 = await submitTransactionAsync(dorothyAccount, tx8);630 const result8 = getGenericResult(events8);631 expect(result8.success).to.be.true;632 console.log('Referendum voting.......DONE');633 634635 636 console.log('Acquire Unique AssetId Info on Moonbeam.......');637638 639 await waitNewBlocks(api, 5);640641 assetId = (await api.query.assetManager.assetTypeId({642 XCM: sourceLocation,643 })).toString();644645 console.log('UNQ asset ID is %s', assetId);646 console.log('Acquire Unique AssetId Info on Moonbeam.......DONE');647 648649 650 console.log('Sponsoring random Account.......');651 const tx10 = api.tx.balances.transfer(randomAccountMoonbeam.address, 11_000_000_000_000_000_000n);652 const events10 = await submitTransactionAsync(baltatharAccount, tx10);653 const result10 = getGenericResult(events10);654 expect(result10.success).to.be.true;655 console.log('Sponsoring random Account.......DONE');656 657658 [balanceGlmrTokenInit] = await getBalance(api, [randomAccountMoonbeam.address]);659 },660 moonbeamOptions(),661 );662663 await usingApi(async (api) => {664 const tx0 = api.tx.balances.transfer(randomAccountUnique.address, 10n * TRANSFER_AMOUNT);665 const events0 = await submitTransactionAsync(uniqueAlice, tx0);666 const result0 = getGenericResult(events0);667 expect(result0.success).to.be.true;668669 [balanceUniqueTokenInit] = await getBalance(api, [randomAccountUnique.address]);670 });671 });672673 it('Should connect and send UNQ to Moonbeam', async () => {674 await usingApi(async (api) => {675 const currencyId = {676 NativeAssetId: 'Here',677 };678 const dest = {679 V1: {680 parents: 1,681 interior: {682 X2: [683 {Parachain: MOONBEAM_CHAIN},684 {AccountKey20: {network: 'Any', key: randomAccountMoonbeam.address}},685 ],686 },687 },688 };689 const amount = TRANSFER_AMOUNT;690 const destWeight = 850000000;691692 const tx = api.tx.xTokens.transfer(currencyId, amount, dest, destWeight);693 const events = await submitTransactionAsync(randomAccountUnique, tx);694 const result = getGenericResult(events);695 expect(result.success).to.be.true;696697 [balanceUniqueTokenMiddle] = await getBalance(api, [randomAccountUnique.address]);698 expect(balanceUniqueTokenMiddle < balanceUniqueTokenInit).to.be.true;699700 const transactionFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;701 console.log('[Unique -> Moonbeam] transaction fees on Unique: %s UNQ', bigIntToDecimals(transactionFees));702 expect(transactionFees > 0).to.be.true;703 });704705 await usingApi(706 async (api) => {707 await waitNewBlocks(api, 3);708709 [balanceGlmrTokenMiddle] = await getBalance(api, [randomAccountMoonbeam.address]);710711 const glmrFees = balanceGlmrTokenInit - balanceGlmrTokenMiddle;712 console.log('[Unique -> Moonbeam] transaction fees on Moonbeam: %s GLMR', bigIntToDecimals(glmrFees));713 expect(glmrFees == 0n).to.be.true;714715 const unqRandomAccountAsset = (716 await api.query.assets.account(assetId, randomAccountMoonbeam.address)717 ).toJSON()! as any;718719 balanceForeignUnqTokenMiddle = BigInt(unqRandomAccountAsset['balance']);720 const unqIncomeTransfer = balanceForeignUnqTokenMiddle - balanceForeignUnqTokenInit;721 console.log('[Unique -> Moonbeam] income %s UNQ', bigIntToDecimals(unqIncomeTransfer));722 expect(unqIncomeTransfer == TRANSFER_AMOUNT).to.be.true;723 },724 moonbeamOptions(),725 );726 });727728 it('Should connect to Moonbeam and send UNQ back', async () => {729 await usingApi(730 async (api) => {731 const asset = {732 V1: {733 id: {734 Concrete: {735 parents: 1,736 interior: {737 X1: {Parachain: UNIQUE_CHAIN},738 },739 },740 },741 fun: {742 Fungible: TRANSFER_AMOUNT,743 },744 },745 };746 const destination = {747 V1: {748 parents: 1,749 interior: {750 X2: [751 {Parachain: UNIQUE_CHAIN},752 {AccountId32: {network: 'Any', id: randomAccountUnique.addressRaw}},753 ],754 },755 },756 };757 const destWeight = 50000000;758759 const tx = api.tx.xTokens.transferMultiasset(asset, destination, destWeight);760 const events = await submitTransactionAsync(randomAccountMoonbeam, tx);761 const result = getGenericResult(events);762 expect(result.success).to.be.true;763764 [balanceGlmrTokenFinal] = await getBalance(api, [randomAccountMoonbeam.address]);765766 const glmrFees = balanceGlmrTokenMiddle - balanceGlmrTokenFinal;767 console.log('[Moonbeam -> Unique] transaction fees on Moonbeam: %s GLMR', bigIntToDecimals(glmrFees));768 expect(glmrFees > 0).to.be.true;769770 const unqRandomAccountAsset = (771 await api.query.assets.account(assetId, randomAccountMoonbeam.address)772 ).toJSON()! as any;773774 expect(unqRandomAccountAsset).to.be.null;775776 balanceForeignUnqTokenFinal = 0n;777778 const unqOutcomeTransfer = balanceForeignUnqTokenMiddle - balanceForeignUnqTokenFinal;779 console.log('[Unique -> Moonbeam] outcome %s UNQ', bigIntToDecimals(unqOutcomeTransfer));780 expect(unqOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;781 },782 moonbeamOptions(),783 );784785 await usingApi(async (api) => {786 await waitNewBlocks(api, 3);787788 [balanceUniqueTokenFinal] = await getBalance(api, [randomAccountUnique.address]);789 const actuallyDelivered = balanceUniqueTokenFinal - balanceUniqueTokenMiddle;790 expect(actuallyDelivered > 0).to.be.true;791792 console.log('[Moonbeam -> Unique] actually delivered %s UNQ', bigIntToDecimals(actuallyDelivered));793794 const unqFees = TRANSFER_AMOUNT - actuallyDelivered;795 console.log('[Moonbeam -> Unique] transaction fees on Unique: %s UNQ', bigIntToDecimals(unqFees));796 expect(unqFees == 0n).to.be.true;797 });798 });799});