difftreelog
refactor use playgrounds in unique xcm tests
in: master
1 file changed
tests/src/xcm/xcmUnique.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 {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 {XcmV2TraitsError, XcmV2TraitsOutcome} from '../interfaces';25import {itSub, expect, describeXcm, usingPlaygrounds} from '../util/playgrounds';2627const UNIQUE_CHAIN = 2037;28const ACALA_CHAIN = 2000;29const MOONBEAM_CHAIN = 2004;3031const RELAY_PORT = 9844;32const ACALA_PORT = 9946;33const MOONBEAM_PORT = 9947;3435const relayUrl = 'ws://127.0.0.1:' + RELAY_PORT;36const acalaUrl = 'ws://127.0.0.1:' + ACALA_PORT;37const moonbeamUrl = 'ws://127.0.0.1:' + MOONBEAM_PORT;3839const ACALA_DECIMALS = 12;4041const TRANSFER_AMOUNT = 2000000000000000000000000n;4243describeXcm('[XCM] Integration test: Exchanging tokens with Acala', () => {44 let alice: IKeyringPair;45 let randomAccount: IKeyringPair;4647 let balanceUniqueTokenInit: bigint;48 let balanceUniqueTokenMiddle: bigint;49 let balanceUniqueTokenFinal: bigint;50 let balanceAcalaTokenInit: bigint;51 let balanceAcalaTokenMiddle: bigint;52 let balanceAcalaTokenFinal: bigint;53 let balanceUniqueForeignTokenInit: bigint;54 let balanceUniqueForeignTokenMiddle: bigint;55 let balanceUniqueForeignTokenFinal: 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 // Acala side66 await usingPlaygrounds.atUrl(acalaUrl, async (helper) => {67 const destination = {68 V0: {69 X2: [70 'Parent',71 {72 Parachain: UNIQUE_CHAIN,73 },74 ],75 },76 };7778 const metadata = {79 name: 'UNQ',80 symbol: 'UNQ',81 decimals: 18,82 minimalBalance: 1,83 };8485 // TODO86 const tx = helper.getApi().tx.assetRegistry.registerForeignAsset(destination, metadata);87 const sudoTx = helper.getApi().tx.sudo.sudo(tx as any);88 const events = await submitTransactionAsync(alice, sudoTx);89 const result = getGenericResult(events);90 expect(result.success).to.be.true;9192 // const tx1 = api.tx.balances.transfer(randomAccount.address, 10000000000000n);93 // const events1 = await submitTransactionAsync(alice, tx1);94 // const result1 = getGenericResult(events1);95 // expect(result1.success).to.be.true;96 await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n);9798 // [balanceAcalaTokenInit] = await getBalance(api, [randomAccount.address]);99 balanceAcalaTokenInit = await helper.balance.getSubstrate(randomAccount.address);100 {101 // TODO102 const {free} = (await helper.getApi().query.tokens.accounts(randomAccount.addressRaw, {ForeignAsset: 0})).toJSON() as any;103 balanceUniqueForeignTokenInit = BigInt(free);104 }105 });106107 // Unique side108 await usingPlaygrounds(async (helper) => {109 // const tx0 = api.tx.balances.transfer(randomAccount.address, 10n * TRANSFER_AMOUNT);110 // const events0 = await submitTransactionAsync(alice, tx0);111 // const result0 = getGenericResult(events0);112 // expect(result0.success).to.be.true;113 await helper.balance.transferToSubstrate(alice, randomAccount.address, 10n * TRANSFER_AMOUNT);114115 // [balanceUniqueTokenInit] = await getBalance(api, [randomAccount.address]);116 balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccount.address);117 });118 });119120 itSub('Should connect and send UNQ to Acala', async ({helper}) => {121122 // Unique side123 const destination = {124 V0: {125 X2: [126 'Parent',127 {128 Parachain: ACALA_CHAIN,129 },130 ],131 },132 };133134 const beneficiary = {135 V0: {136 X1: {137 AccountId32: {138 network: 'Any',139 id: randomAccount.addressRaw,140 },141 },142 },143 };144145 const assets = {146 V1: [147 {148 id: {149 Concrete: {150 parents: 0,151 interior: 'Here',152 },153 },154 fun: {155 Fungible: TRANSFER_AMOUNT,156 },157 },158 ],159 };160161 const feeAssetItem = 0;162163 const weightLimit = {164 Limited: 5000000000,165 };166167 // TODO168 const tx = helper.getApi().tx.polkadotXcm.limitedReserveTransferAssets(destination, beneficiary, assets, feeAssetItem, weightLimit);169 const events = await submitTransactionAsync(randomAccount, tx);170 const result = getGenericResult(events);171 expect(result.success).to.be.true;172173 // [balanceUniqueTokenMiddle] = await getBalance(api, [randomAccount.address]);174 balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);175176 const unqFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;177 console.log('[Unique -> Acala] transaction fees on Unique: %s UNQ', bigIntToDecimals(unqFees));178 expect(unqFees > 0n).to.be.true;179180 // Acala side181 await usingPlaygrounds.atUrl(acalaUrl, async (helper) => {182 // await waitNewBlocks(api, 3);183 await helper.wait.newBlocks(3);184185 // TODO186 const {free} = (await helper.getApi().query.tokens.accounts(randomAccount.addressRaw, {ForeignAsset: 0})).toJSON() as any;187 balanceUniqueForeignTokenMiddle = BigInt(free);188189 // [balanceAcalaTokenMiddle] = await getBalance(api, [randomAccount.address]);190 balanceAcalaTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);191192 const acaFees = balanceAcalaTokenInit - balanceAcalaTokenMiddle;193 const unqIncomeTransfer = balanceUniqueForeignTokenMiddle - balanceUniqueForeignTokenInit;194195 console.log(196 '[Unique -> Acala] transaction fees on Acala: %s ACA',197 bigIntToDecimals(acaFees, ACALA_DECIMALS),198 );199 console.log('[Unique -> Acala] income %s UNQ', bigIntToDecimals(unqIncomeTransfer));200 expect(acaFees == 0n).to.be.true;201 expect(unqIncomeTransfer == TRANSFER_AMOUNT).to.be.true;202 });203 });204205 itSub('Should connect to Acala and send UNQ back', async ({helper}) => {206207 // Acala side208 await usingPlaygrounds.atUrl(acalaUrl, async (helper) => {209 const destination = {210 V1: {211 parents: 1,212 interior: {213 X2: [214 {Parachain: UNIQUE_CHAIN},215 {216 AccountId32: {217 network: 'Any',218 id: randomAccount.addressRaw,219 },220 },221 ],222 },223 },224 };225226 const id = {227 ForeignAsset: 0,228 };229230 const destWeight = 50000000;231232 // TODO233 const tx = helper.getApi().tx.xTokens.transfer(id as any, TRANSFER_AMOUNT, destination, destWeight);234 const events = await submitTransactionAsync(randomAccount, tx);235 const result = getGenericResult(events);236 expect(result.success).to.be.true;237238 // [balanceAcalaTokenFinal] = await getBalance(api, [randomAccount.address]);239 balanceAcalaTokenFinal = await helper.balance.getSubstrate(randomAccount.address);240 {241 // TODO242 const {free} = (await helper.getApi().query.tokens.accounts(randomAccount.addressRaw, id)).toJSON() as any;243 balanceUniqueForeignTokenFinal = BigInt(free);244 }245246 const acaFees = balanceAcalaTokenMiddle - balanceAcalaTokenFinal;247 const unqOutcomeTransfer = balanceUniqueForeignTokenMiddle - balanceUniqueForeignTokenFinal;248249 console.log(250 '[Acala -> Unique] transaction fees on Acala: %s ACA',251 bigIntToDecimals(acaFees, ACALA_DECIMALS),252 );253 console.log('[Acala -> Unique] outcome %s UNQ', bigIntToDecimals(unqOutcomeTransfer));254255 expect(acaFees > 0).to.be.true;256 expect(unqOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;257 });258259 // await waitNewBlocks(api, 3);260 await helper.wait.newBlocks(3);261262 // [balanceUniqueTokenFinal] = await getBalance(api, [randomAccount.address]);263 balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccount.address);264 const actuallyDelivered = balanceUniqueTokenFinal - balanceUniqueTokenMiddle;265 expect(actuallyDelivered > 0).to.be.true;266267 console.log('[Acala -> Unique] actually delivered %s UNQ', bigIntToDecimals(actuallyDelivered));268269 const unqFees = TRANSFER_AMOUNT - actuallyDelivered;270 console.log('[Acala -> Unique] transaction fees on Unique: %s UNQ', bigIntToDecimals(unqFees));271 expect(unqFees == 0n).to.be.true;272 });273});274275// These tests are relevant only when the foreign asset pallet is disabled276describeXcm('[XCM] Integration test: Unique rejects non-native tokens', () => {277 let alice: IKeyringPair;278279 before(async () => {280 await usingPlaygrounds(async (_helper, privateKey) => {281 alice = privateKey('//Alice');282 });283 });284285 itSub('Unique rejects tokens from the Relay', async ({helper}) => {286 await usingPlaygrounds.atUrl(relayUrl, async (helper) => {287 const destination = {288 V1: {289 parents: 0,290 interior: {X1: {291 Parachain: UNIQUE_CHAIN,292 },293 },294 }};295296 const beneficiary = {297 V1: {298 parents: 0,299 interior: {X1: {300 AccountId32: {301 network: 'Any',302 id: alice.addressRaw,303 },304 }},305 },306 };307308 const assets = {309 V1: [310 {311 id: {312 Concrete: {313 parents: 0,314 interior: 'Here',315 },316 },317 fun: {318 Fungible: 50_000_000_000_000_000n,319 },320 },321 ],322 };323324 const feeAssetItem = 0;325326 const weightLimit = {327 Limited: 5_000_000_000,328 };329330 // TODO331 const tx = helper.getApi().tx.xcmPallet.limitedReserveTransferAssets(destination, beneficiary, assets, feeAssetItem, weightLimit);332 const events = await submitTransactionAsync(alice, tx);333 const result = getGenericResult(events);334 expect(result.success).to.be.true;335 });336337 const maxWaitBlocks = 3;338339 // TODO340 const dmpQueueExecutedDownward = await waitEvent(341 helper.getApi(),342 maxWaitBlocks,343 'dmpQueue',344 'ExecutedDownward',345 );346347 expect(348 dmpQueueExecutedDownward != null,349 '[Relay] dmpQueue.ExecutedDownward event is expected',350 ).to.be.true;351352 const event = dmpQueueExecutedDownward!.event;353 const outcome = event.data[1] as XcmV2TraitsOutcome;354355 expect(356 outcome.isIncomplete,357 '[Relay] The outcome of the XCM should be `Incomplete`',358 ).to.be.true;359360 const incomplete = outcome.asIncomplete;361 expect(362 incomplete[1].toString() == 'AssetNotFound',363 '[Relay] The XCM error should be `AssetNotFound`',364 ).to.be.true;365 });366367 itSub('Unique rejects ACA tokens from Acala', async ({helper}) => {368 await usingPlaygrounds.atUrl(acalaUrl, async (helper) => {369 const destination = {370 V1: {371 parents: 1,372 interior: {373 X2: [374 {Parachain: UNIQUE_CHAIN},375 {376 AccountId32: {377 network: 'Any',378 id: alice.addressRaw,379 },380 },381 ],382 },383 },384 };385386 const id = {387 Token: 'ACA',388 };389390 const destWeight = 50000000;391392 // TODO393 const tx = helper.getApi().tx.xTokens.transfer(id as any, 100_000_000_000, destination, destWeight);394 const events = await submitTransactionAsync(alice, tx);395 const result = getGenericResult(events);396 expect(result.success).to.be.true;397 });398399 const maxWaitBlocks = 3;400401 // TODO402 const xcmpQueueFailEvent = await waitEvent(helper.getApi(), maxWaitBlocks, 'xcmpQueue', 'Fail');403404 expect(405 xcmpQueueFailEvent != null,406 '[Acala] xcmpQueue.FailEvent event is expected',407 ).to.be.true;408409 const event = xcmpQueueFailEvent!.event;410 const outcome = event.data[1] as XcmV2TraitsError;411412 expect(413 outcome.isUntrustedReserveLocation,414 '[Acala] The XCM error should be `UntrustedReserveLocation`',415 ).to.be.true;416 });417});418419describeXcm('[XCM] Integration test: Exchanging UNQ with Moonbeam', () => {420421 // Unique constants422 let uniqueAlice: IKeyringPair;423 let uniqueAssetLocation;424425 let randomAccountUnique: IKeyringPair;426 let randomAccountMoonbeam: IKeyringPair;427428 // Moonbeam constants429 let assetId: string;430431 const moonbeamKeyring = new Keyring({type: 'ethereum'});432 const alithPrivateKey = '0x5fb92d6e98884f76de468fa3f6278f8807c48bebc13595d45af5bdc4da702133';433 const baltatharPrivateKey = '0x8075991ce870b93a8870eca0c0f91913d12f47948ca0fd25b49c6fa7cdbeee8b';434 const dorothyPrivateKey = '0x39539ab1876910bbf3a223d84a29e28f1cb4e2e456503e7e91ed39b2e7223d68';435436 const alithAccount = moonbeamKeyring.addFromUri(alithPrivateKey, undefined, 'ethereum');437 const baltatharAccount = moonbeamKeyring.addFromUri(baltatharPrivateKey, undefined, 'ethereum');438 const dorothyAccount = moonbeamKeyring.addFromUri(dorothyPrivateKey, undefined, 'ethereum');439440 const councilVotingThreshold = 2;441 const technicalCommitteeThreshold = 2;442 const votingPeriod = 3;443 const delayPeriod = 0;444445 const uniqueAssetMetadata = {446 name: 'xcUnique',447 symbol: 'xcUNQ',448 decimals: 18,449 isFrozen: false,450 minimalBalance: 1,451 };452453 let balanceUniqueTokenInit: bigint;454 let balanceUniqueTokenMiddle: bigint;455 let balanceUniqueTokenFinal: bigint;456 let balanceForeignUnqTokenInit: bigint;457 let balanceForeignUnqTokenMiddle: bigint;458 let balanceForeignUnqTokenFinal: bigint;459 let balanceGlmrTokenInit: bigint;460 let balanceGlmrTokenMiddle: bigint;461 let balanceGlmrTokenFinal: bigint;462463 before(async () => {464 await usingPlaygrounds(async (_helper, privateKey) => {465 const keyringEth = new Keyring({type: 'ethereum'});466 const keyringSr25519 = new Keyring({type: 'sr25519'});467468 uniqueAlice = privateKey('//Alice');469 randomAccountUnique = generateKeyringPair(keyringSr25519);470 randomAccountMoonbeam = generateKeyringPair(keyringEth);471472 balanceForeignUnqTokenInit = 0n;473 });474475 await usingPlaygrounds.atUrl(moonbeamUrl, async (helper) => {476 // TODO477478 const api = helper.getApi();479480 // >>> Sponsoring Dorothy >>>481 console.log('Sponsoring Dorothy.......');482 // const tx0 = api.tx.balances.transfer(dorothyAccount.address, 11_000_000_000_000_000_000n);483 // const events0 = await submitTransactionAsync(alithAccount, tx0);484 // const result0 = getGenericResult(events0);485 // expect(result0.success).to.be.true;486 await helper.balance.transferToSubstrate(alithAccount, dorothyAccount.address, 11_000_000_000_000_000_000n);487 console.log('Sponsoring Dorothy.......DONE');488 // <<< Sponsoring Dorothy <<<489490 const sourceLocation: MultiLocation = api.createType(491 'MultiLocation',492 {493 parents: 1,494 interior: {X1: {Parachain: UNIQUE_CHAIN}},495 },496 );497498 uniqueAssetLocation = {XCM: sourceLocation};499 const existentialDeposit = 1;500 const isSufficient = true;501 const unitsPerSecond = '1';502 const numAssetsWeightHint = 0;503504 const registerTx = api.tx.assetManager.registerForeignAsset(505 uniqueAssetLocation,506 uniqueAssetMetadata,507 existentialDeposit,508 isSufficient,509 );510 console.log('Encoded proposal for registerAsset is %s', registerTx.method.toHex() || '');511512 const setUnitsTx = api.tx.assetManager.setAssetUnitsPerSecond(513 uniqueAssetLocation,514 unitsPerSecond,515 numAssetsWeightHint,516 );517 console.log('Encoded proposal for setAssetUnitsPerSecond is %s', setUnitsTx.method.toHex() || '');518519 const batchCall = api.tx.utility.batchAll([registerTx, setUnitsTx]);520 console.log('Encoded proposal for batchCall is %s', batchCall.method.toHex() || '');521522 // >>> Note motion preimage >>>523 console.log('Note motion preimage.......');524 const encodedProposal = batchCall?.method.toHex() || '';525 const proposalHash = blake2AsHex(encodedProposal);526 console.log('Encoded proposal for batch utility after schedule is %s', encodedProposal);527 console.log('Encoded proposal hash for batch utility after schedule is %s', proposalHash);528 console.log('Encoded length %d', encodedProposal.length);529530 const tx1 = api.tx.democracy.notePreimage(encodedProposal);531 const events1 = await submitTransactionAsync(baltatharAccount, tx1);532 const result1 = getGenericResult(events1);533 expect(result1.success).to.be.true;534 console.log('Note motion preimage.......DONE');535 // <<< Note motion preimage <<<536537 // >>> Propose external motion through council >>>538 console.log('Propose external motion through council.......');539 const externalMotion = api.tx.democracy.externalProposeMajority(proposalHash);540 const tx2 = api.tx.councilCollective.propose(541 councilVotingThreshold,542 externalMotion,543 externalMotion.encodedLength,544 );545 const events2 = await submitTransactionAsync(baltatharAccount, tx2);546 const result2 = getGenericResult(events2);547 expect(result2.success).to.be.true;548549 const encodedMotion = externalMotion?.method.toHex() || '';550 const motionHash = blake2AsHex(encodedMotion);551 console.log('Motion hash is %s', motionHash);552553 const tx3 = api.tx.councilCollective.vote(motionHash, 0, true);554 {555 const events3 = await submitTransactionAsync(dorothyAccount, tx3);556 const result3 = getGenericResult(events3);557 expect(result3.success).to.be.true;558 }559 {560 const events3 = await submitTransactionAsync(baltatharAccount, tx3);561 const result3 = getGenericResult(events3);562 expect(result3.success).to.be.true;563 }564565 const tx4 = api.tx.councilCollective.close(motionHash, 0, 1_000_000_000, externalMotion.encodedLength);566 const events4 = await submitTransactionAsync(dorothyAccount, tx4);567 const result4 = getGenericResult(events4);568 expect(result4.success).to.be.true;569 console.log('Propose external motion through council.......DONE');570 // <<< Propose external motion through council <<<571572 // >>> Fast track proposal through technical committee >>>573 console.log('Fast track proposal through technical committee.......');574 const fastTrack = api.tx.democracy.fastTrack(proposalHash, votingPeriod, delayPeriod);575 const tx5 = api.tx.techCommitteeCollective.propose(576 technicalCommitteeThreshold,577 fastTrack,578 fastTrack.encodedLength,579 );580 const events5 = await submitTransactionAsync(alithAccount, tx5);581 const result5 = getGenericResult(events5);582 expect(result5.success).to.be.true;583584 const encodedFastTrack = fastTrack?.method.toHex() || '';585 const fastTrackHash = blake2AsHex(encodedFastTrack);586 console.log('FastTrack hash is %s', fastTrackHash);587588 const proposalIdx = Number(await api.query.techCommitteeCollective.proposalCount()) - 1;589 const tx6 = api.tx.techCommitteeCollective.vote(fastTrackHash, proposalIdx, true);590 {591 const events6 = await submitTransactionAsync(baltatharAccount, tx6);592 const result6 = getGenericResult(events6);593 expect(result6.success).to.be.true;594 }595 {596 const events6 = await submitTransactionAsync(alithAccount, tx6);597 const result6 = getGenericResult(events6);598 expect(result6.success).to.be.true;599 }600601 const tx7 = api.tx.techCommitteeCollective602 .close(fastTrackHash, proposalIdx, 1_000_000_000, fastTrack.encodedLength);603 const events7 = await submitTransactionAsync(baltatharAccount, tx7);604 const result7 = getGenericResult(events7);605 expect(result7.success).to.be.true;606 console.log('Fast track proposal through technical committee.......DONE');607 // <<< Fast track proposal through technical committee <<<608609 // >>> Referendum voting >>>610 console.log('Referendum voting.......');611 const tx8 = api.tx.democracy.vote(612 0,613 {Standard: {balance: 10_000_000_000_000_000_000n, vote: {aye: true, conviction: 1}}},614 );615 const events8 = await submitTransactionAsync(dorothyAccount, tx8);616 const result8 = getGenericResult(events8);617 expect(result8.success).to.be.true;618 console.log('Referendum voting.......DONE');619 // <<< Referendum voting <<<620621 // >>> Acquire Unique AssetId Info on Moonbeam >>>622 console.log('Acquire Unique AssetId Info on Moonbeam.......');623624 // Wait for the democracy execute625 // await waitNewBlocks(api, 5);626 await helper.wait.newBlocks(5);627628 assetId = (await api.query.assetManager.assetTypeId({629 XCM: sourceLocation,630 })).toString();631632 console.log('UNQ asset ID is %s', assetId);633 console.log('Acquire Unique AssetId Info on Moonbeam.......DONE');634 // >>> Acquire Unique AssetId Info on Moonbeam >>>635636 // >>> Sponsoring random Account >>>637 console.log('Sponsoring random Account.......');638 // const tx10 = api.tx.balances.transfer(randomAccountMoonbeam.address, 11_000_000_000_000_000_000n);639 // const events10 = await submitTransactionAsync(baltatharAccount, tx10);640 // const result10 = getGenericResult(events10);641 // expect(result10.success).to.be.true;642 await helper.balance.transferToSubstrate(baltatharAccount, randomAccountMoonbeam.address, 11_000_000_000_000_000_000n);643 console.log('Sponsoring random Account.......DONE');644 // <<< Sponsoring random Account <<<645646 [balanceGlmrTokenInit] = await getBalance(api, [randomAccountMoonbeam.address]);647 });648649 await usingPlaygrounds(async (helper) => {650 // const tx0 = api.tx.balances.transfer(randomAccountUnique.address, 10n * TRANSFER_AMOUNT);651 // const events0 = await submitTransactionAsync(uniqueAlice, tx0);652 // const result0 = getGenericResult(events0);653 // expect(result0.success).to.be.true;654 await helper.balance.transferToSubstrate(uniqueAlice, randomAccountUnique.address, 10n * TRANSFER_AMOUNT);655656 // [balanceUniqueTokenInit] = await getBalance(api, [randomAccountUnique.address]);657 balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccountUnique.address);658 });659 });660661 itSub('Should connect and send UNQ to Moonbeam', async ({helper}) => {662 const currencyId = {663 NativeAssetId: 'Here',664 };665 const dest = {666 V1: {667 parents: 1,668 interior: {669 X2: [670 {Parachain: MOONBEAM_CHAIN},671 {AccountKey20: {network: 'Any', key: randomAccountMoonbeam.address}},672 ],673 },674 },675 };676 const amount = TRANSFER_AMOUNT;677 const destWeight = 850000000;678679 // TODO680 const tx = helper.getApi().tx.xTokens.transfer(currencyId, amount, dest, destWeight);681 const events = await submitTransactionAsync(randomAccountUnique, tx);682 const result = getGenericResult(events);683 expect(result.success).to.be.true;684685 // [balanceUniqueTokenMiddle] = await getBalance(api, [randomAccountUnique.address]);686 balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccountUnique.address);687 expect(balanceUniqueTokenMiddle < balanceUniqueTokenInit).to.be.true;688689 const transactionFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;690 console.log('[Unique -> Moonbeam] transaction fees on Unique: %s UNQ', bigIntToDecimals(transactionFees));691 expect(transactionFees > 0).to.be.true;692693 await usingPlaygrounds.atUrl(moonbeamUrl, async (helper) => {694 // await waitNewBlocks(api, 3);695 await helper.wait.newBlocks(3);696697 // [balanceGlmrTokenMiddle] = await getBalance(api, [randomAccountMoonbeam.address]);698 balanceGlmrTokenMiddle = await helper.balance.getSubstrate(randomAccountMoonbeam.address);699700 const glmrFees = balanceGlmrTokenInit - balanceGlmrTokenMiddle;701 console.log('[Unique -> Moonbeam] transaction fees on Moonbeam: %s GLMR', bigIntToDecimals(glmrFees));702 expect(glmrFees == 0n).to.be.true;703704 // TODO705 const unqRandomAccountAsset = (706 await helper.getApi().query.assets.account(assetId, randomAccountMoonbeam.address)707 ).toJSON()! as any;708709 balanceForeignUnqTokenMiddle = BigInt(unqRandomAccountAsset['balance']);710 const unqIncomeTransfer = balanceForeignUnqTokenMiddle - balanceForeignUnqTokenInit;711 console.log('[Unique -> Moonbeam] income %s UNQ', bigIntToDecimals(unqIncomeTransfer));712 expect(unqIncomeTransfer == TRANSFER_AMOUNT).to.be.true;713 });714 });715716 itSub('Should connect to Moonbeam and send UNQ back', async ({helper}) => {717 await usingPlaygrounds.atUrl(moonbeamUrl, async (helper) => {718 const asset = {719 V1: {720 id: {721 Concrete: {722 parents: 1,723 interior: {724 X1: {Parachain: UNIQUE_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: UNIQUE_CHAIN},739 {AccountId32: {network: 'Any', id: randomAccountUnique.addressRaw}},740 ],741 },742 },743 };744 const destWeight = 50000000;745746 // TODO747 const tx = helper.getApi().tx.xTokens.transferMultiasset(asset, destination, destWeight);748 const events = await submitTransactionAsync(randomAccountMoonbeam, tx);749 const result = getGenericResult(events);750 expect(result.success).to.be.true;751752 // [balanceGlmrTokenFinal] = await getBalance(api, [randomAccountMoonbeam.address]);753 balanceGlmrTokenFinal = await helper.balance.getSubstrate(randomAccountMoonbeam.address);754755 const glmrFees = balanceGlmrTokenMiddle - balanceGlmrTokenFinal;756 console.log('[Moonbeam -> Unique] transaction fees on Moonbeam: %s GLMR', bigIntToDecimals(glmrFees));757 expect(glmrFees > 0).to.be.true;758759 // TODO760 const unqRandomAccountAsset = (761 await helper.getApi().query.assets.account(assetId, randomAccountMoonbeam.address)762 ).toJSON()! as any;763764 expect(unqRandomAccountAsset).to.be.null;765766 balanceForeignUnqTokenFinal = 0n;767768 const unqOutcomeTransfer = balanceForeignUnqTokenMiddle - balanceForeignUnqTokenFinal;769 console.log('[Unique -> Moonbeam] outcome %s UNQ', bigIntToDecimals(unqOutcomeTransfer));770 expect(unqOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;771 });772773 // await waitNewBlocks(api, 3);774 await helper.wait.newBlocks(3);775776 // [balanceUniqueTokenFinal] = await getBalance(api, [randomAccountUnique.address]);777 balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccountUnique.address);778 const actuallyDelivered = balanceUniqueTokenFinal - balanceUniqueTokenMiddle;779 expect(actuallyDelivered > 0).to.be.true;780781 console.log('[Moonbeam -> Unique] actually delivered %s UNQ', bigIntToDecimals(actuallyDelivered));782783 const unqFees = TRANSFER_AMOUNT - actuallyDelivered;784 console.log('[Moonbeam -> Unique] transaction fees on Unique: %s UNQ', bigIntToDecimals(unqFees));785 expect(unqFees == 0n).to.be.true;786 });787});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 {Keyring} from '@polkadot/api';18import {IKeyringPair} from '@polkadot/types/types';19import {generateKeyringPair, bigIntToDecimals} from '../deprecated-helpers/helpers';20import {blake2AsHex} from '@polkadot/util-crypto';21import {XcmV2TraitsError, XcmV2TraitsOutcome} from '../interfaces';22import {itSub, expect, describeXcm, usingPlaygrounds, usingAcalaPlaygrounds, usingRelayPlaygrounds, usingMoonbeamPlaygrounds} from '../util/playgrounds';2324const UNIQUE_CHAIN = 2037;25const ACALA_CHAIN = 2000;26const MOONBEAM_CHAIN = 2004;2728const RELAY_PORT = 9844;29const ACALA_PORT = 9946;30const MOONBEAM_PORT = 9947;3132const relayUrl = 'ws://127.0.0.1:' + RELAY_PORT;33const acalaUrl = 'ws://127.0.0.1:' + ACALA_PORT;34const moonbeamUrl = 'ws://127.0.0.1:' + MOONBEAM_PORT;3536const ACALA_DECIMALS = 12;3738const TRANSFER_AMOUNT = 2000000000000000000000000n;3940describeXcm('[XCM] Integration test: Exchanging tokens with Acala', () => {41 let alice: IKeyringPair;42 let randomAccount: IKeyringPair;4344 let balanceUniqueTokenInit: bigint;45 let balanceUniqueTokenMiddle: bigint;46 let balanceUniqueTokenFinal: bigint;47 let balanceAcalaTokenInit: bigint;48 let balanceAcalaTokenMiddle: bigint;49 let balanceAcalaTokenFinal: bigint;50 let balanceUniqueForeignTokenInit: bigint;51 let balanceUniqueForeignTokenMiddle: bigint;52 let balanceUniqueForeignTokenFinal: bigint;5354 before(async () => {55 await usingPlaygrounds(async (_helper, privateKey) => {56 const keyringSr25519 = new Keyring({type: 'sr25519'});5758 alice = privateKey('//Alice');59 randomAccount = generateKeyringPair(keyringSr25519);60 });6162 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {63 const destination = {64 V0: {65 X2: [66 'Parent',67 {68 Parachain: UNIQUE_CHAIN,69 },70 ],71 },72 };7374 const metadata = {75 name: 'UNQ',76 symbol: 'UNQ',77 decimals: 18,78 minimalBalance: 1n,79 };8081 await helper.getSudo().assetRegistry.registerForeignAsset(alice, destination, metadata);82 await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n);83 balanceAcalaTokenInit = await helper.balance.getSubstrate(randomAccount.address);84 balanceUniqueForeignTokenInit = await helper.tokens.accounts(randomAccount.address, {ForeignAsset: 0});85 });8687 await usingPlaygrounds(async (helper) => {88 await helper.balance.transferToSubstrate(alice, randomAccount.address, 10n * TRANSFER_AMOUNT);89 balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccount.address);90 });91 });9293 itSub('Should connect and send UNQ to Acala', async ({helper}) => {9495 const destination = {96 V0: {97 X2: [98 'Parent',99 {100 Parachain: ACALA_CHAIN,101 },102 ],103 },104 };105106 const beneficiary = {107 V0: {108 X1: {109 AccountId32: {110 network: 'Any',111 id: randomAccount.addressRaw,112 },113 },114 },115 };116117 const assets = {118 V1: [119 {120 id: {121 Concrete: {122 parents: 0,123 interior: 'Here',124 },125 },126 fun: {127 Fungible: TRANSFER_AMOUNT,128 },129 },130 ],131 };132133 const feeAssetItem = 0;134 const weightLimit = 5000000000;135136 await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, weightLimit);137138 balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);139140 const unqFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;141 console.log('[Unique -> Acala] transaction fees on Unique: %s UNQ', bigIntToDecimals(unqFees));142 expect(unqFees > 0n).to.be.true;143144 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {145 await helper.wait.newBlocks(3);146147 balanceUniqueForeignTokenMiddle = await helper.tokens.accounts(randomAccount.address, {ForeignAsset: 0});148 balanceAcalaTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);149150 const acaFees = balanceAcalaTokenInit - balanceAcalaTokenMiddle;151 const unqIncomeTransfer = balanceUniqueForeignTokenMiddle - balanceUniqueForeignTokenInit;152153 console.log(154 '[Unique -> Acala] transaction fees on Acala: %s ACA',155 bigIntToDecimals(acaFees, ACALA_DECIMALS),156 );157 console.log('[Unique -> Acala] income %s UNQ', bigIntToDecimals(unqIncomeTransfer));158 expect(acaFees == 0n).to.be.true;159 expect(unqIncomeTransfer == TRANSFER_AMOUNT).to.be.true;160 });161 });162163 itSub('Should connect to Acala and send UNQ back', async ({helper}) => {164 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {165 const destination = {166 V1: {167 parents: 1,168 interior: {169 X2: [170 {Parachain: UNIQUE_CHAIN},171 {172 AccountId32: {173 network: 'Any',174 id: randomAccount.addressRaw,175 },176 },177 ],178 },179 },180 };181182 const id = {183 ForeignAsset: 0,184 };185186 const destWeight = 50000000;187188 await helper.xTokens.transfer(randomAccount, id, TRANSFER_AMOUNT, destination, destWeight);189190 balanceAcalaTokenFinal = await helper.balance.getSubstrate(randomAccount.address);191 balanceUniqueForeignTokenFinal = await helper.tokens.accounts(randomAccount.address, id);192193 const acaFees = balanceAcalaTokenMiddle - balanceAcalaTokenFinal;194 const unqOutcomeTransfer = balanceUniqueForeignTokenMiddle - balanceUniqueForeignTokenFinal;195196 console.log(197 '[Acala -> Unique] transaction fees on Acala: %s ACA',198 bigIntToDecimals(acaFees, ACALA_DECIMALS),199 );200 console.log('[Acala -> Unique] outcome %s UNQ', bigIntToDecimals(unqOutcomeTransfer));201202 expect(acaFees > 0).to.be.true;203 expect(unqOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;204 });205206 await helper.wait.newBlocks(3);207208 balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccount.address);209 const actuallyDelivered = balanceUniqueTokenFinal - balanceUniqueTokenMiddle;210 expect(actuallyDelivered > 0).to.be.true;211212 console.log('[Acala -> Unique] actually delivered %s UNQ', bigIntToDecimals(actuallyDelivered));213214 const unqFees = TRANSFER_AMOUNT - actuallyDelivered;215 console.log('[Acala -> Unique] transaction fees on Unique: %s UNQ', bigIntToDecimals(unqFees));216 expect(unqFees == 0n).to.be.true;217 });218});219220// These tests are relevant only when the foreign asset pallet is disabled221describeXcm('[XCM] Integration test: Unique rejects non-native tokens', () => {222 let alice: IKeyringPair;223224 before(async () => {225 await usingPlaygrounds(async (_helper, privateKey) => {226 alice = privateKey('//Alice');227 });228 });229230 itSub('Unique rejects tokens from the Relay', async ({helper}) => {231 await usingRelayPlaygrounds(relayUrl, async (helper) => {232 const destination = {233 V1: {234 parents: 0,235 interior: {X1: {236 Parachain: UNIQUE_CHAIN,237 },238 },239 }};240241 const beneficiary = {242 V1: {243 parents: 0,244 interior: {X1: {245 AccountId32: {246 network: 'Any',247 id: alice.addressRaw,248 },249 }},250 },251 };252253 const assets = {254 V1: [255 {256 id: {257 Concrete: {258 parents: 0,259 interior: 'Here',260 },261 },262 fun: {263 Fungible: 50_000_000_000_000_000n,264 },265 },266 ],267 };268269 const feeAssetItem = 0;270 const weightLimit = 5_000_000_000;271272 await helper.xcm.limitedReserveTransferAssets(alice, destination, beneficiary, assets, feeAssetItem, weightLimit);273 });274275 const maxWaitBlocks = 3;276277 const dmpQueueExecutedDownward = await helper.wait.event(maxWaitBlocks, 'dmpQueue', 'ExecutedDownward');278279 expect(280 dmpQueueExecutedDownward != null,281 '[Relay] dmpQueue.ExecutedDownward event is expected',282 ).to.be.true;283284 const event = dmpQueueExecutedDownward!.event;285 const outcome = event.data[1] as XcmV2TraitsOutcome;286287 expect(288 outcome.isIncomplete,289 '[Relay] The outcome of the XCM should be `Incomplete`',290 ).to.be.true;291292 const incomplete = outcome.asIncomplete;293 expect(294 incomplete[1].toString() == 'AssetNotFound',295 '[Relay] The XCM error should be `AssetNotFound`',296 ).to.be.true;297 });298299 itSub('Unique rejects ACA tokens from Acala', async ({helper}) => {300 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {301 const destination = {302 V1: {303 parents: 1,304 interior: {305 X2: [306 {Parachain: UNIQUE_CHAIN},307 {308 AccountId32: {309 network: 'Any',310 id: alice.addressRaw,311 },312 },313 ],314 },315 },316 };317318 const id = {319 Token: 'ACA',320 };321322 const destWeight = 50000000;323324 await helper.xTokens.transfer(alice, id, 100_000_000_000n, destination, destWeight);325 });326327 const maxWaitBlocks = 3;328329 const xcmpQueueFailEvent = await helper.wait.event(maxWaitBlocks, 'xcmpQueue', 'Fail');330331 expect(332 xcmpQueueFailEvent != null,333 '[Acala] xcmpQueue.FailEvent event is expected',334 ).to.be.true;335336 const event = xcmpQueueFailEvent!.event;337 const outcome = event.data[1] as XcmV2TraitsError;338339 expect(340 outcome.isUntrustedReserveLocation,341 '[Acala] The XCM error should be `UntrustedReserveLocation`',342 ).to.be.true;343 });344});345346describe.only('[XCM] Integration test: Exchanging UNQ with Moonbeam', () => {347348 // Unique constants349 let uniqueAlice: IKeyringPair;350 let uniqueAssetLocation;351352 let randomAccountUnique: IKeyringPair;353 let randomAccountMoonbeam: IKeyringPair;354355 // Moonbeam constants356 let assetId: string;357358 const moonbeamKeyring = new Keyring({type: 'ethereum'});359 const alithPrivateKey = '0x5fb92d6e98884f76de468fa3f6278f8807c48bebc13595d45af5bdc4da702133';360 const baltatharPrivateKey = '0x8075991ce870b93a8870eca0c0f91913d12f47948ca0fd25b49c6fa7cdbeee8b';361 const dorothyPrivateKey = '0x39539ab1876910bbf3a223d84a29e28f1cb4e2e456503e7e91ed39b2e7223d68';362363 const alithAccount = moonbeamKeyring.addFromUri(alithPrivateKey, undefined, 'ethereum');364 const baltatharAccount = moonbeamKeyring.addFromUri(baltatharPrivateKey, undefined, 'ethereum');365 const dorothyAccount = moonbeamKeyring.addFromUri(dorothyPrivateKey, undefined, 'ethereum');366367 const councilVotingThreshold = 2;368 const technicalCommitteeThreshold = 2;369 const votingPeriod = 3;370 const delayPeriod = 0;371372 const uniqueAssetMetadata = {373 name: 'xcUnique',374 symbol: 'xcUNQ',375 decimals: 18,376 isFrozen: false,377 minimalBalance: 1n,378 };379380 let balanceUniqueTokenInit: bigint;381 let balanceUniqueTokenMiddle: bigint;382 let balanceUniqueTokenFinal: bigint;383 let balanceForeignUnqTokenInit: bigint;384 let balanceForeignUnqTokenMiddle: bigint;385 let balanceForeignUnqTokenFinal: bigint;386 let balanceGlmrTokenInit: bigint;387 let balanceGlmrTokenMiddle: bigint;388 let balanceGlmrTokenFinal: bigint;389390 before(async () => {391 await usingPlaygrounds(async (_helper, privateKey) => {392 const keyringEth = new Keyring({type: 'ethereum'});393 const keyringSr25519 = new Keyring({type: 'sr25519'});394395 uniqueAlice = privateKey('//Alice');396 randomAccountUnique = generateKeyringPair(keyringSr25519);397 randomAccountMoonbeam = generateKeyringPair(keyringEth);398399 balanceForeignUnqTokenInit = 0n;400 });401402 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {403 // >>> Sponsoring Dorothy >>>404 console.log('Sponsoring Dorothy.......');405 await helper.balance.transferToEthereum(alithAccount, dorothyAccount.address, 11_000_000_000_000_000_000n);406 console.log('Sponsoring Dorothy.......DONE');407 // <<< Sponsoring Dorothy <<<408409 uniqueAssetLocation = {410 XCM: {411 parents: 1,412 interior: {X1: {Parachain: UNIQUE_CHAIN}},413 },414 };415 const existentialDeposit = 1n;416 const isSufficient = true;417 const unitsPerSecond = 1n;418 const numAssetsWeightHint = 0;419420 const encodedProposal = helper.assetManager.makeRegisterForeignAssetProposal({421 location: uniqueAssetLocation,422 metadata: uniqueAssetMetadata,423 existentialDeposit,424 isSufficient,425 unitsPerSecond,426 numAssetsWeightHint,427 });428 const proposalHash = blake2AsHex(encodedProposal);429430 console.log('Encoded proposal for registerForeignAsset & setAssetUnitsPerSecond is %s', encodedProposal);431 console.log('Encoded length %d', encodedProposal.length);432 console.log('Encoded proposal hash for batch utility after schedule is %s', proposalHash);433434 // >>> Note motion preimage >>>435 console.log('Note motion preimage.......');436 await helper.democracy.notePreimage(baltatharAccount, encodedProposal);437 console.log('Note motion preimage.......DONE');438 // <<< Note motion preimage <<<439440 // >>> Propose external motion through council >>>441 console.log('Propose external motion through council.......');442 const externalMotion = helper.democracy.externalProposeMajority(proposalHash);443 const encodedMotion = externalMotion?.method.toHex() || '';444 const motionHash = blake2AsHex(encodedMotion);445 console.log('Motion hash is %s', motionHash);446447 await helper.collective.council.propose(baltatharAccount, councilVotingThreshold, externalMotion, externalMotion.encodedLength);448449 const councilProposalIdx = await helper.collective.council.proposalCount() - 1;450 await helper.collective.council.vote(dorothyAccount, motionHash, councilProposalIdx, true);451 await helper.collective.council.vote(baltatharAccount, motionHash, councilProposalIdx, true);452453 await helper.collective.council.close(dorothyAccount, motionHash, councilProposalIdx, 1_000_000_000, externalMotion.encodedLength);454 console.log('Propose external motion through council.......DONE');455 // <<< Propose external motion through council <<<456457 // >>> Fast track proposal through technical committee >>>458 console.log('Fast track proposal through technical committee.......');459 const fastTrack = helper.democracy.fastTrack(proposalHash, votingPeriod, delayPeriod);460 const encodedFastTrack = fastTrack?.method.toHex() || '';461 const fastTrackHash = blake2AsHex(encodedFastTrack);462 console.log('FastTrack hash is %s', fastTrackHash);463464 await helper.collective.techCommittee.propose(alithAccount, technicalCommitteeThreshold, fastTrack, fastTrack.encodedLength);465466 const techProposalIdx = await helper.collective.techCommittee.proposalCount() - 1;467 await helper.collective.techCommittee.vote(baltatharAccount, fastTrackHash, techProposalIdx, true);468 await helper.collective.techCommittee.vote(alithAccount, fastTrackHash, techProposalIdx, true);469470 await helper.collective.techCommittee.close(baltatharAccount, fastTrackHash, techProposalIdx, 1_000_000_000, fastTrack.encodedLength);471 console.log('Fast track proposal through technical committee.......DONE');472 // <<< Fast track proposal through technical committee <<<473474 // >>> Referendum voting >>>475 console.log('Referendum voting.......');476 await helper.democracy.referendumVote(dorothyAccount, 0, {477 balance: 10_000_000_000_000_000_000n,478 vote: {aye: true, conviction: 1},479 });480 console.log('Referendum voting.......DONE');481 // <<< Referendum voting <<<482483 // >>> Acquire Unique AssetId Info on Moonbeam >>>484 console.log('Acquire Unique AssetId Info on Moonbeam.......');485486 // Wait for the democracy execute487 await helper.wait.newBlocks(5);488489 assetId = (await helper.assetManager.assetTypeId(uniqueAssetLocation)).toString();490491 console.log('UNQ asset ID is %s', assetId);492 console.log('Acquire Unique AssetId Info on Moonbeam.......DONE');493 // >>> Acquire Unique AssetId Info on Moonbeam >>>494495 // >>> Sponsoring random Account >>>496 console.log('Sponsoring random Account.......');497 await helper.balance.transferToEthereum(baltatharAccount, randomAccountMoonbeam.address, 11_000_000_000_000_000_000n);498 console.log('Sponsoring random Account.......DONE');499 // <<< Sponsoring random Account <<<500501 balanceGlmrTokenInit = await helper.balance.getEthereum(randomAccountMoonbeam.address);502 });503504 await usingPlaygrounds(async (helper) => {505 await helper.balance.transferToSubstrate(uniqueAlice, randomAccountUnique.address, 10n * TRANSFER_AMOUNT);506 balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccountUnique.address);507 });508 });509510 itSub('Should connect and send UNQ to Moonbeam', async ({helper}) => {511 const currencyId = {512 NativeAssetId: 'Here',513 };514 const dest = {515 V1: {516 parents: 1,517 interior: {518 X2: [519 {Parachain: MOONBEAM_CHAIN},520 {AccountKey20: {network: 'Any', key: randomAccountMoonbeam.address}},521 ],522 },523 },524 };525 const amount = TRANSFER_AMOUNT;526 const destWeight = 850000000;527528 await helper.xTokens.transfer(randomAccountUnique, currencyId, amount, dest, destWeight);529530 balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccountUnique.address);531 expect(balanceUniqueTokenMiddle < balanceUniqueTokenInit).to.be.true;532533 const transactionFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;534 console.log('[Unique -> Moonbeam] transaction fees on Unique: %s UNQ', bigIntToDecimals(transactionFees));535 expect(transactionFees > 0).to.be.true;536537 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {538 await helper.wait.newBlocks(3);539540 balanceGlmrTokenMiddle = await helper.balance.getEthereum(randomAccountMoonbeam.address);541542 const glmrFees = balanceGlmrTokenInit - balanceGlmrTokenMiddle;543 console.log('[Unique -> Moonbeam] transaction fees on Moonbeam: %s GLMR', bigIntToDecimals(glmrFees));544 expect(glmrFees == 0n).to.be.true;545546 balanceForeignUnqTokenMiddle = (await helper.assets.account(assetId, randomAccountMoonbeam.address))!;547548 const unqIncomeTransfer = balanceForeignUnqTokenMiddle - balanceForeignUnqTokenInit;549 console.log('[Unique -> Moonbeam] income %s UNQ', bigIntToDecimals(unqIncomeTransfer));550 expect(unqIncomeTransfer == TRANSFER_AMOUNT).to.be.true;551 });552 });553554 itSub('Should connect to Moonbeam and send UNQ back', async ({helper}) => {555 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {556 const asset = {557 V1: {558 id: {559 Concrete: {560 parents: 1,561 interior: {562 X1: {Parachain: UNIQUE_CHAIN},563 },564 },565 },566 fun: {567 Fungible: TRANSFER_AMOUNT,568 },569 },570 };571 const destination = {572 V1: {573 parents: 1,574 interior: {575 X2: [576 {Parachain: UNIQUE_CHAIN},577 {AccountId32: {network: 'Any', id: randomAccountUnique.addressRaw}},578 ],579 },580 },581 };582 const destWeight = 50000000;583584 await helper.xTokens.transferMultiasset(randomAccountMoonbeam, asset, destination, destWeight);585586 balanceGlmrTokenFinal = await helper.balance.getEthereum(randomAccountMoonbeam.address);587588 const glmrFees = balanceGlmrTokenMiddle - balanceGlmrTokenFinal;589 console.log('[Moonbeam -> Unique] transaction fees on Moonbeam: %s GLMR', bigIntToDecimals(glmrFees));590 expect(glmrFees > 0).to.be.true;591592 const unqRandomAccountAsset = await helper.assets.account(assetId, randomAccountMoonbeam.address);593594 expect(unqRandomAccountAsset).to.be.null;595 596 balanceForeignUnqTokenFinal = 0n;597598 const unqOutcomeTransfer = balanceForeignUnqTokenMiddle - balanceForeignUnqTokenFinal;599 console.log('[Unique -> Moonbeam] outcome %s UNQ', bigIntToDecimals(unqOutcomeTransfer));600 expect(unqOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;601 });602603 await helper.wait.newBlocks(3);604605 balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccountUnique.address);606 const actuallyDelivered = balanceUniqueTokenFinal - balanceUniqueTokenMiddle;607 expect(actuallyDelivered > 0).to.be.true;608609 console.log('[Moonbeam -> Unique] actually delivered %s UNQ', bigIntToDecimals(actuallyDelivered));610611 const unqFees = TRANSFER_AMOUNT - actuallyDelivered;612 console.log('[Moonbeam -> Unique] transaction fees on Unique: %s UNQ', bigIntToDecimals(unqFees));613 expect(unqFees == 0n).to.be.true;614 });615});