difftreelog
fix remove redundant comment
in: master
1 file changed
tests/src/xcm/xcmQuartz.test.tsdiffbeforeafterboth1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617import {Keyring} from '@polkadot/api';18import {IKeyringPair} from '@polkadot/types/types';19import {generateKeyringPair, bigIntToDecimals} from '../deprecated-helpers/helpers';20import {blake2AsHex} from '@polkadot/util-crypto';21import {XcmV2TraitsOutcome, XcmV2TraitsError} from '../interfaces';22import {itSub, expect, describeXcm, usingPlaygrounds, usingKaruraPlaygrounds, usingRelayPlaygrounds, usingMoonriverPlaygrounds} from '../util/playgrounds';2324const QUARTZ_CHAIN = 2095;25const KARURA_CHAIN = 2000;26const MOONRIVER_CHAIN = 2023;2728const RELAY_PORT = 9844;29const KARURA_PORT = 9946;30const MOONRIVER_PORT = 9947;3132const relayUrl = 'ws://127.0.0.1:' + RELAY_PORT;33const karuraUrl = 'ws://127.0.0.1:' + KARURA_PORT;34const moonriverUrl = 'ws://127.0.0.1:' + MOONRIVER_PORT;3536const KARURA_DECIMALS = 12;3738const TRANSFER_AMOUNT = 2000000000000000000000000n;3940describeXcm('[XCM] Integration test: Exchanging tokens with Karura', () => {41 let alice: IKeyringPair;42 let randomAccount: IKeyringPair;4344 let balanceQuartzTokenInit: bigint;45 let balanceQuartzTokenMiddle: bigint;46 let balanceQuartzTokenFinal: bigint;47 let balanceKaruraTokenInit: bigint;48 let balanceKaruraTokenMiddle: bigint;49 let balanceKaruraTokenFinal: bigint;50 let balanceQuartzForeignTokenInit: bigint;51 let balanceQuartzForeignTokenMiddle: bigint;52 let balanceQuartzForeignTokenFinal: 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 // Karura side63 await usingKaruraPlaygrounds(karuraUrl, async (helper) => {64 const destination = {65 V0: {66 X2: [67 'Parent',68 {69 Parachain: QUARTZ_CHAIN,70 },71 ],72 },73 };7475 const metadata = {76 name: 'QTZ',77 symbol: 'QTZ',78 decimals: 18,79 minimalBalance: 1n,80 };8182 await helper.getSudo().assetRegistry.registerForeignAsset(alice, destination, metadata);83 await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n);84 balanceKaruraTokenInit = await helper.balance.getSubstrate(randomAccount.address);85 balanceQuartzForeignTokenInit = await helper.tokens.accounts(randomAccount.address, {ForeignAsset: 0});86 });8788 await usingPlaygrounds(async (helper) => {89 await helper.balance.transferToSubstrate(alice, randomAccount.address, 10n * TRANSFER_AMOUNT);90 balanceQuartzTokenInit = await helper.balance.getSubstrate(randomAccount.address);91 });92 });9394 itSub('Should connect and send QTZ to Karura', async ({helper}) => {95 const destination = {96 V0: {97 X2: [98 'Parent',99 {100 Parachain: KARURA_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);137 balanceQuartzTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);138139 const qtzFees = balanceQuartzTokenInit - balanceQuartzTokenMiddle - TRANSFER_AMOUNT;140 console.log('[Quartz -> Karura] transaction fees on Quartz: %s QTZ', bigIntToDecimals(qtzFees));141 expect(qtzFees > 0n).to.be.true;142143 await usingKaruraPlaygrounds(karuraUrl, async (helper) => {144 await helper.wait.newBlocks(3);145 balanceQuartzForeignTokenMiddle = await helper.tokens.accounts(randomAccount.address, {ForeignAsset: 0});146 balanceKaruraTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);147148 const karFees = balanceKaruraTokenInit - balanceKaruraTokenMiddle;149 const qtzIncomeTransfer = balanceQuartzForeignTokenMiddle - balanceQuartzForeignTokenInit;150151 console.log(152 '[Quartz -> Karura] transaction fees on Karura: %s KAR',153 bigIntToDecimals(karFees, KARURA_DECIMALS),154 );155 console.log('[Quartz -> Karura] income %s QTZ', bigIntToDecimals(qtzIncomeTransfer));156 expect(karFees == 0n).to.be.true;157 expect(qtzIncomeTransfer == TRANSFER_AMOUNT).to.be.true;158 });159 });160161 itSub('Should connect to Karura and send QTZ back', async ({helper}) => {162 await usingKaruraPlaygrounds(karuraUrl, async (helper) => {163 const destination = {164 V1: {165 parents: 1,166 interior: {167 X2: [168 {Parachain: QUARTZ_CHAIN},169 {170 AccountId32: {171 network: 'Any',172 id: randomAccount.addressRaw,173 },174 },175 ],176 },177 },178 };179180 const id = {181 ForeignAsset: 0,182 };183184 const destWeight = 50000000;185186 await helper.xTokens.transfer(randomAccount, id, TRANSFER_AMOUNT, destination, destWeight);187 balanceKaruraTokenFinal = await helper.balance.getSubstrate(randomAccount.address);188 balanceQuartzForeignTokenFinal = await helper.tokens.accounts(randomAccount.address, id);189190 const karFees = balanceKaruraTokenMiddle - balanceKaruraTokenFinal;191 const qtzOutcomeTransfer = balanceQuartzForeignTokenMiddle - balanceQuartzForeignTokenFinal;192193 console.log(194 '[Karura -> Quartz] transaction fees on Karura: %s KAR',195 bigIntToDecimals(karFees, KARURA_DECIMALS),196 );197 console.log('[Karura -> Quartz] outcome %s QTZ', bigIntToDecimals(qtzOutcomeTransfer));198199 expect(karFees > 0).to.be.true;200 expect(qtzOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;201 });202203 await helper.wait.newBlocks(3);204205 balanceQuartzTokenFinal = await helper.balance.getSubstrate(randomAccount.address);206 const actuallyDelivered = balanceQuartzTokenFinal - balanceQuartzTokenMiddle;207 expect(actuallyDelivered > 0).to.be.true;208209 console.log('[Karura -> Quartz] actually delivered %s QTZ', bigIntToDecimals(actuallyDelivered));210211 const qtzFees = TRANSFER_AMOUNT - actuallyDelivered;212 console.log('[Karura -> Quartz] transaction fees on Quartz: %s QTZ', bigIntToDecimals(qtzFees));213 expect(qtzFees == 0n).to.be.true;214 });215});216217// These tests are relevant only when the foreign asset pallet is disabled218describeXcm('[XCM] Integration test: Quartz rejects non-native tokens', () => {219 let alice: IKeyringPair;220221 before(async () => {222 await usingPlaygrounds(async (_helper, privateKey) => {223 alice = privateKey('//Alice');224 });225 });226227 itSub('Quartz rejects tokens from the Relay', async ({helper}) => {228 await usingRelayPlaygrounds(relayUrl, async (helper) => {229 const destination = {230 V1: {231 parents: 0,232 interior: {X1: {233 Parachain: QUARTZ_CHAIN,234 },235 },236 }};237238 const beneficiary = {239 V1: {240 parents: 0,241 interior: {X1: {242 AccountId32: {243 network: 'Any',244 id: alice.addressRaw,245 },246 }},247 },248 };249250 const assets = {251 V1: [252 {253 id: {254 Concrete: {255 parents: 0,256 interior: 'Here',257 },258 },259 fun: {260 Fungible: 50_000_000_000_000_000n,261 },262 },263 ],264 };265266 const feeAssetItem = 0;267 const weightLimit = 5_000_000_000;268269 await helper.xcm.limitedReserveTransferAssets(alice, destination, beneficiary, assets, feeAssetItem, weightLimit);270 });271272 const maxWaitBlocks = 3;273274 const dmpQueueExecutedDownward = await helper.wait.event(maxWaitBlocks, 'dmpQueue', 'ExecutedDownward');275276 expect(277 dmpQueueExecutedDownward != null,278 '[Relay] dmpQueue.ExecutedDownward event is expected',279 ).to.be.true;280281 const event = dmpQueueExecutedDownward!.event;282 const outcome = event.data[1] as XcmV2TraitsOutcome;283284 expect(285 outcome.isIncomplete,286 '[Relay] The outcome of the XCM should be `Incomplete`',287 ).to.be.true;288289 const incomplete = outcome.asIncomplete;290 expect(291 incomplete[1].toString() == 'AssetNotFound',292 '[Relay] The XCM error should be `AssetNotFound`',293 ).to.be.true;294 });295296 itSub('Quartz rejects KAR tokens from Karura', async ({helper}) => {297 await usingKaruraPlaygrounds(karuraUrl, async (helper) => {298 const destination = {299 V1: {300 parents: 1,301 interior: {302 X2: [303 {Parachain: QUARTZ_CHAIN},304 {305 AccountId32: {306 network: 'Any',307 id: alice.addressRaw,308 },309 },310 ],311 },312 },313 };314315 const id = {316 Token: 'KAR',317 };318319 const destWeight = 50000000;320321 await helper.xTokens.transfer(alice, id, 100_000_000_000n, destination, destWeight);322 });323324 const maxWaitBlocks = 3;325326 const xcmpQueueFailEvent = await helper.wait.event(maxWaitBlocks, 'xcmpQueue', 'Fail');327328 expect(329 xcmpQueueFailEvent != null,330 '[Karura] xcmpQueue.FailEvent event is expected',331 ).to.be.true;332333 const event = xcmpQueueFailEvent!.event;334 const outcome = event.data[1] as XcmV2TraitsError;335336 expect(337 outcome.isUntrustedReserveLocation,338 '[Karura] The XCM error should be `UntrustedReserveLocation`',339 ).to.be.true;340 });341});342343describeXcm('[XCM] Integration test: Exchanging QTZ with Moonriver', () => {344345 // Quartz constants346 let quartzAlice: IKeyringPair;347 let quartzAssetLocation;348349 let randomAccountQuartz: IKeyringPair;350 let randomAccountMoonriver: IKeyringPair;351352 // Moonriver constants353 let assetId: string;354355 const councilVotingThreshold = 2;356 const technicalCommitteeThreshold = 2;357 const votingPeriod = 3;358 const delayPeriod = 0;359360 const quartzAssetMetadata = {361 name: 'xcQuartz',362 symbol: 'xcQTZ',363 decimals: 18,364 isFrozen: false,365 minimalBalance: 1n,366 };367368 let balanceQuartzTokenInit: bigint;369 let balanceQuartzTokenMiddle: bigint;370 let balanceQuartzTokenFinal: bigint;371 let balanceForeignQtzTokenInit: bigint;372 let balanceForeignQtzTokenMiddle: bigint;373 let balanceForeignQtzTokenFinal: bigint;374 let balanceMovrTokenInit: bigint;375 let balanceMovrTokenMiddle: bigint;376 let balanceMovrTokenFinal: bigint;377378 before(async () => {379 await usingPlaygrounds(async (_helper, privateKey) => {380 const keyringEth = new Keyring({type: 'ethereum'});381 const keyringSr25519 = new Keyring({type: 'sr25519'});382383 quartzAlice = privateKey('//Alice');384 randomAccountQuartz = generateKeyringPair(keyringSr25519);385 randomAccountMoonriver = generateKeyringPair(keyringEth);386387 balanceForeignQtzTokenInit = 0n;388 });389390 await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => {391 const alithAccount = helper.account.alithAccount();392 const baltatharAccount = helper.account.baltatharAccount();393 const dorothyAccount = helper.account.dorothyAccount();394395 // >>> Sponsoring Dorothy >>>396 console.log('Sponsoring Dorothy.......');397 await helper.balance.transferToEthereum(alithAccount, dorothyAccount.address, 11_000_000_000_000_000_000n);398 console.log('Sponsoring Dorothy.......DONE');399 // <<< Sponsoring Dorothy <<<400401 quartzAssetLocation = {402 XCM: {403 parents: 1,404 interior: {X1: {Parachain: QUARTZ_CHAIN}},405 },406 };407 const existentialDeposit = 1n;408 const isSufficient = true;409 const unitsPerSecond = 1n;410 const numAssetsWeightHint = 0;411412 const encodedProposal = helper.assetManager.makeRegisterForeignAssetProposal({413 location: quartzAssetLocation,414 metadata: quartzAssetMetadata,415 existentialDeposit,416 isSufficient,417 unitsPerSecond,418 numAssetsWeightHint,419 });420 const proposalHash = blake2AsHex(encodedProposal);421422 console.log('Encoded proposal for registerForeignAsset & setAssetUnitsPerSecond is %s', encodedProposal);423 console.log('Encoded length %d', encodedProposal.length);424 console.log('Encoded proposal hash for batch utility after schedule is %s', proposalHash);425426 // >>> Note motion preimage >>>427 console.log('Note motion preimage.......');428 await helper.democracy.notePreimage(baltatharAccount, encodedProposal);429 console.log('Note motion preimage.......DONE');430 // <<< Note motion preimage <<<431432 // >>> Propose external motion through council >>>433 console.log('Propose external motion through council.......');434 const externalMotion = helper.democracy.externalProposeMajority(proposalHash);435 const encodedMotion = externalMotion?.method.toHex() || '';436 const motionHash = blake2AsHex(encodedMotion);437 console.log('Motion hash is %s', motionHash);438439 await helper.collective.council.propose(baltatharAccount, councilVotingThreshold, externalMotion, externalMotion.encodedLength);440441 const councilProposalIdx = await helper.collective.council.proposalCount() - 1;442 await helper.collective.council.vote(dorothyAccount, motionHash, councilProposalIdx, true);443 await helper.collective.council.vote(baltatharAccount, motionHash, councilProposalIdx, true);444445 await helper.collective.council.close(dorothyAccount, motionHash, councilProposalIdx, 1_000_000_000, externalMotion.encodedLength);446 console.log('Propose external motion through council.......DONE');447 // <<< Propose external motion through council <<<448449 // >>> Fast track proposal through technical committee >>>450 console.log('Fast track proposal through technical committee.......');451 const fastTrack = helper.democracy.fastTrack(proposalHash, votingPeriod, delayPeriod);452 const encodedFastTrack = fastTrack?.method.toHex() || '';453 const fastTrackHash = blake2AsHex(encodedFastTrack);454 console.log('FastTrack hash is %s', fastTrackHash);455456 await helper.collective.techCommittee.propose(alithAccount, technicalCommitteeThreshold, fastTrack, fastTrack.encodedLength);457458 const techProposalIdx = await helper.collective.techCommittee.proposalCount() - 1;459 await helper.collective.techCommittee.vote(baltatharAccount, fastTrackHash, techProposalIdx, true);460 await helper.collective.techCommittee.vote(alithAccount, fastTrackHash, techProposalIdx, true);461462 await helper.collective.techCommittee.close(baltatharAccount, fastTrackHash, techProposalIdx, 1_000_000_000, fastTrack.encodedLength);463 console.log('Fast track proposal through technical committee.......DONE');464 // <<< Fast track proposal through technical committee <<<465466 // >>> Referendum voting >>>467 console.log('Referendum voting.......');468 await helper.democracy.referendumVote(dorothyAccount, 0, {469 balance: 10_000_000_000_000_000_000n,470 vote: {aye: true, conviction: 1},471 });472 console.log('Referendum voting.......DONE');473 // <<< Referendum voting <<<474475 // >>> Acquire Quartz AssetId Info on Moonriver >>>476 console.log('Acquire Quartz AssetId Info on Moonriver.......');477478 // Wait for the democracy execute479 await helper.wait.newBlocks(5);480481 assetId = (await helper.assetManager.assetTypeId(quartzAssetLocation)).toString();482483 console.log('QTZ asset ID is %s', assetId);484 console.log('Acquire Quartz AssetId Info on Moonriver.......DONE');485 // >>> Acquire Quartz AssetId Info on Moonriver >>>486487 // >>> Sponsoring random Account >>>488 console.log('Sponsoring random Account.......');489 await helper.balance.transferToEthereum(baltatharAccount, randomAccountMoonriver.address, 11_000_000_000_000_000_000n);490 console.log('Sponsoring random Account.......DONE');491 // <<< Sponsoring random Account <<<492493 balanceMovrTokenInit = await helper.balance.getEthereum(randomAccountMoonriver.address);494 });495496 await usingPlaygrounds(async (helper) => {497 await helper.balance.transferToSubstrate(quartzAlice, randomAccountQuartz.address, 10n * TRANSFER_AMOUNT);498 balanceQuartzTokenInit = await helper.balance.getSubstrate(randomAccountQuartz.address);499 });500 });501502 itSub('Should connect and send QTZ to Moonriver', async ({helper}) => {503 const currencyId = {504 NativeAssetId: 'Here',505 };506 const dest = {507 V1: {508 parents: 1,509 interior: {510 X2: [511 {Parachain: MOONRIVER_CHAIN},512 {AccountKey20: {network: 'Any', key: randomAccountMoonriver.address}},513 ],514 },515 },516 };517 const amount = TRANSFER_AMOUNT;518 const destWeight = 850000000;519520 await helper.xTokens.transfer(randomAccountQuartz, currencyId, amount, dest, destWeight);521522 balanceQuartzTokenMiddle = await helper.balance.getSubstrate(randomAccountQuartz.address);523 expect(balanceQuartzTokenMiddle < balanceQuartzTokenInit).to.be.true;524525 const transactionFees = balanceQuartzTokenInit - balanceQuartzTokenMiddle - TRANSFER_AMOUNT;526 console.log('[Quartz -> Moonriver] transaction fees on Quartz: %s QTZ', bigIntToDecimals(transactionFees));527 expect(transactionFees > 0).to.be.true;528529 await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => {530 await helper.wait.newBlocks(3);531532 balanceMovrTokenMiddle = await helper.balance.getEthereum(randomAccountMoonriver.address);533534 const movrFees = balanceMovrTokenInit - balanceMovrTokenMiddle;535 console.log('[Quartz -> Moonriver] transaction fees on Moonriver: %s MOVR',bigIntToDecimals(movrFees));536 expect(movrFees == 0n).to.be.true;537538 balanceForeignQtzTokenMiddle = (await helper.assets.account(assetId, randomAccountMoonriver.address))!; // BigInt(qtzRandomAccountAsset['balance']);539 const qtzIncomeTransfer = balanceForeignQtzTokenMiddle - balanceForeignQtzTokenInit;540 console.log('[Quartz -> Moonriver] income %s QTZ', bigIntToDecimals(qtzIncomeTransfer));541 expect(qtzIncomeTransfer == TRANSFER_AMOUNT).to.be.true;542 });543 });544545 itSub('Should connect to Moonriver and send QTZ back', async ({helper}) => {546 await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => {547 const asset = {548 V1: {549 id: {550 Concrete: {551 parents: 1,552 interior: {553 X1: {Parachain: QUARTZ_CHAIN},554 },555 },556 },557 fun: {558 Fungible: TRANSFER_AMOUNT,559 },560 },561 };562 const destination = {563 V1: {564 parents: 1,565 interior: {566 X2: [567 {Parachain: QUARTZ_CHAIN},568 {AccountId32: {network: 'Any', id: randomAccountQuartz.addressRaw}},569 ],570 },571 },572 };573 const destWeight = 50000000;574575 await helper.xTokens.transferMultiasset(randomAccountMoonriver, asset, destination, destWeight);576577 balanceMovrTokenFinal = await helper.balance.getEthereum(randomAccountMoonriver.address);578579 const movrFees = balanceMovrTokenMiddle - balanceMovrTokenFinal;580 console.log('[Moonriver -> Quartz] transaction fees on Moonriver: %s MOVR', bigIntToDecimals(movrFees));581 expect(movrFees > 0).to.be.true;582583 const qtzRandomAccountAsset = await helper.assets.account(assetId, randomAccountMoonriver.address);584585 expect(qtzRandomAccountAsset).to.be.null;586587 balanceForeignQtzTokenFinal = 0n;588589 const qtzOutcomeTransfer = balanceForeignQtzTokenMiddle - balanceForeignQtzTokenFinal;590 console.log('[Quartz -> Moonriver] outcome %s QTZ', bigIntToDecimals(qtzOutcomeTransfer));591 expect(qtzOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;592 });593594 await helper.wait.newBlocks(3);595596 balanceQuartzTokenFinal = await helper.balance.getSubstrate(randomAccountQuartz.address);597 const actuallyDelivered = balanceQuartzTokenFinal - balanceQuartzTokenMiddle;598 expect(actuallyDelivered > 0).to.be.true;599600 console.log('[Moonriver -> Quartz] actually delivered %s QTZ', bigIntToDecimals(actuallyDelivered));601602 const qtzFees = TRANSFER_AMOUNT - actuallyDelivered;603 console.log('[Moonriver -> Quartz] transaction fees on Quartz: %s QTZ', bigIntToDecimals(qtzFees));604 expect(qtzFees == 0n).to.be.true;605 });606});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 {XcmV2TraitsOutcome, XcmV2TraitsError} from '../interfaces';22import {itSub, expect, describeXcm, usingPlaygrounds, usingKaruraPlaygrounds, usingRelayPlaygrounds, usingMoonriverPlaygrounds} from '../util/playgrounds';2324const QUARTZ_CHAIN = 2095;25const KARURA_CHAIN = 2000;26const MOONRIVER_CHAIN = 2023;2728const RELAY_PORT = 9844;29const KARURA_PORT = 9946;30const MOONRIVER_PORT = 9947;3132const relayUrl = 'ws://127.0.0.1:' + RELAY_PORT;33const karuraUrl = 'ws://127.0.0.1:' + KARURA_PORT;34const moonriverUrl = 'ws://127.0.0.1:' + MOONRIVER_PORT;3536const KARURA_DECIMALS = 12;3738const TRANSFER_AMOUNT = 2000000000000000000000000n;3940describeXcm('[XCM] Integration test: Exchanging tokens with Karura', () => {41 let alice: IKeyringPair;42 let randomAccount: IKeyringPair;4344 let balanceQuartzTokenInit: bigint;45 let balanceQuartzTokenMiddle: bigint;46 let balanceQuartzTokenFinal: bigint;47 let balanceKaruraTokenInit: bigint;48 let balanceKaruraTokenMiddle: bigint;49 let balanceKaruraTokenFinal: bigint;50 let balanceQuartzForeignTokenInit: bigint;51 let balanceQuartzForeignTokenMiddle: bigint;52 let balanceQuartzForeignTokenFinal: 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 usingKaruraPlaygrounds(karuraUrl, async (helper) => {63 const destination = {64 V0: {65 X2: [66 'Parent',67 {68 Parachain: QUARTZ_CHAIN,69 },70 ],71 },72 };7374 const metadata = {75 name: 'QTZ',76 symbol: 'QTZ',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 balanceKaruraTokenInit = await helper.balance.getSubstrate(randomAccount.address);84 balanceQuartzForeignTokenInit = 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 balanceQuartzTokenInit = await helper.balance.getSubstrate(randomAccount.address);90 });91 });9293 itSub('Should connect and send QTZ to Karura', async ({helper}) => {94 const destination = {95 V0: {96 X2: [97 'Parent',98 {99 Parachain: KARURA_CHAIN,100 },101 ],102 },103 };104105 const beneficiary = {106 V0: {107 X1: {108 AccountId32: {109 network: 'Any',110 id: randomAccount.addressRaw,111 },112 },113 },114 };115116 const assets = {117 V1: [118 {119 id: {120 Concrete: {121 parents: 0,122 interior: 'Here',123 },124 },125 fun: {126 Fungible: TRANSFER_AMOUNT,127 },128 },129 ],130 };131132 const feeAssetItem = 0;133 const weightLimit = 5000000000;134135 await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, weightLimit);136 balanceQuartzTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);137138 const qtzFees = balanceQuartzTokenInit - balanceQuartzTokenMiddle - TRANSFER_AMOUNT;139 console.log('[Quartz -> Karura] transaction fees on Quartz: %s QTZ', bigIntToDecimals(qtzFees));140 expect(qtzFees > 0n).to.be.true;141142 await usingKaruraPlaygrounds(karuraUrl, async (helper) => {143 await helper.wait.newBlocks(3);144 balanceQuartzForeignTokenMiddle = await helper.tokens.accounts(randomAccount.address, {ForeignAsset: 0});145 balanceKaruraTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);146147 const karFees = balanceKaruraTokenInit - balanceKaruraTokenMiddle;148 const qtzIncomeTransfer = balanceQuartzForeignTokenMiddle - balanceQuartzForeignTokenInit;149150 console.log(151 '[Quartz -> Karura] transaction fees on Karura: %s KAR',152 bigIntToDecimals(karFees, KARURA_DECIMALS),153 );154 console.log('[Quartz -> Karura] income %s QTZ', bigIntToDecimals(qtzIncomeTransfer));155 expect(karFees == 0n).to.be.true;156 expect(qtzIncomeTransfer == TRANSFER_AMOUNT).to.be.true;157 });158 });159160 itSub('Should connect to Karura and send QTZ back', async ({helper}) => {161 await usingKaruraPlaygrounds(karuraUrl, async (helper) => {162 const destination = {163 V1: {164 parents: 1,165 interior: {166 X2: [167 {Parachain: QUARTZ_CHAIN},168 {169 AccountId32: {170 network: 'Any',171 id: randomAccount.addressRaw,172 },173 },174 ],175 },176 },177 };178179 const id = {180 ForeignAsset: 0,181 };182183 const destWeight = 50000000;184185 await helper.xTokens.transfer(randomAccount, id, TRANSFER_AMOUNT, destination, destWeight);186 balanceKaruraTokenFinal = await helper.balance.getSubstrate(randomAccount.address);187 balanceQuartzForeignTokenFinal = await helper.tokens.accounts(randomAccount.address, id);188189 const karFees = balanceKaruraTokenMiddle - balanceKaruraTokenFinal;190 const qtzOutcomeTransfer = balanceQuartzForeignTokenMiddle - balanceQuartzForeignTokenFinal;191192 console.log(193 '[Karura -> Quartz] transaction fees on Karura: %s KAR',194 bigIntToDecimals(karFees, KARURA_DECIMALS),195 );196 console.log('[Karura -> Quartz] outcome %s QTZ', bigIntToDecimals(qtzOutcomeTransfer));197198 expect(karFees > 0).to.be.true;199 expect(qtzOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;200 });201202 await helper.wait.newBlocks(3);203204 balanceQuartzTokenFinal = await helper.balance.getSubstrate(randomAccount.address);205 const actuallyDelivered = balanceQuartzTokenFinal - balanceQuartzTokenMiddle;206 expect(actuallyDelivered > 0).to.be.true;207208 console.log('[Karura -> Quartz] actually delivered %s QTZ', bigIntToDecimals(actuallyDelivered));209210 const qtzFees = TRANSFER_AMOUNT - actuallyDelivered;211 console.log('[Karura -> Quartz] transaction fees on Quartz: %s QTZ', bigIntToDecimals(qtzFees));212 expect(qtzFees == 0n).to.be.true;213 });214});215216// These tests are relevant only when the foreign asset pallet is disabled217describeXcm('[XCM] Integration test: Quartz rejects non-native tokens', () => {218 let alice: IKeyringPair;219220 before(async () => {221 await usingPlaygrounds(async (_helper, privateKey) => {222 alice = privateKey('//Alice');223 });224 });225226 itSub('Quartz rejects tokens from the Relay', async ({helper}) => {227 await usingRelayPlaygrounds(relayUrl, async (helper) => {228 const destination = {229 V1: {230 parents: 0,231 interior: {X1: {232 Parachain: QUARTZ_CHAIN,233 },234 },235 }};236237 const beneficiary = {238 V1: {239 parents: 0,240 interior: {X1: {241 AccountId32: {242 network: 'Any',243 id: alice.addressRaw,244 },245 }},246 },247 };248249 const assets = {250 V1: [251 {252 id: {253 Concrete: {254 parents: 0,255 interior: 'Here',256 },257 },258 fun: {259 Fungible: 50_000_000_000_000_000n,260 },261 },262 ],263 };264265 const feeAssetItem = 0;266 const weightLimit = 5_000_000_000;267268 await helper.xcm.limitedReserveTransferAssets(alice, destination, beneficiary, assets, feeAssetItem, weightLimit);269 });270271 const maxWaitBlocks = 3;272273 const dmpQueueExecutedDownward = await helper.wait.event(maxWaitBlocks, 'dmpQueue', 'ExecutedDownward');274275 expect(276 dmpQueueExecutedDownward != null,277 '[Relay] dmpQueue.ExecutedDownward event is expected',278 ).to.be.true;279280 const event = dmpQueueExecutedDownward!.event;281 const outcome = event.data[1] as XcmV2TraitsOutcome;282283 expect(284 outcome.isIncomplete,285 '[Relay] The outcome of the XCM should be `Incomplete`',286 ).to.be.true;287288 const incomplete = outcome.asIncomplete;289 expect(290 incomplete[1].toString() == 'AssetNotFound',291 '[Relay] The XCM error should be `AssetNotFound`',292 ).to.be.true;293 });294295 itSub('Quartz rejects KAR tokens from Karura', async ({helper}) => {296 await usingKaruraPlaygrounds(karuraUrl, async (helper) => {297 const destination = {298 V1: {299 parents: 1,300 interior: {301 X2: [302 {Parachain: QUARTZ_CHAIN},303 {304 AccountId32: {305 network: 'Any',306 id: alice.addressRaw,307 },308 },309 ],310 },311 },312 };313314 const id = {315 Token: 'KAR',316 };317318 const destWeight = 50000000;319320 await helper.xTokens.transfer(alice, id, 100_000_000_000n, destination, destWeight);321 });322323 const maxWaitBlocks = 3;324325 const xcmpQueueFailEvent = await helper.wait.event(maxWaitBlocks, 'xcmpQueue', 'Fail');326327 expect(328 xcmpQueueFailEvent != null,329 '[Karura] xcmpQueue.FailEvent event is expected',330 ).to.be.true;331332 const event = xcmpQueueFailEvent!.event;333 const outcome = event.data[1] as XcmV2TraitsError;334335 expect(336 outcome.isUntrustedReserveLocation,337 '[Karura] The XCM error should be `UntrustedReserveLocation`',338 ).to.be.true;339 });340});341342describeXcm('[XCM] Integration test: Exchanging QTZ with Moonriver', () => {343344 // Quartz constants345 let quartzAlice: IKeyringPair;346 let quartzAssetLocation;347348 let randomAccountQuartz: IKeyringPair;349 let randomAccountMoonriver: IKeyringPair;350351 // Moonriver constants352 let assetId: string;353354 const councilVotingThreshold = 2;355 const technicalCommitteeThreshold = 2;356 const votingPeriod = 3;357 const delayPeriod = 0;358359 const quartzAssetMetadata = {360 name: 'xcQuartz',361 symbol: 'xcQTZ',362 decimals: 18,363 isFrozen: false,364 minimalBalance: 1n,365 };366367 let balanceQuartzTokenInit: bigint;368 let balanceQuartzTokenMiddle: bigint;369 let balanceQuartzTokenFinal: bigint;370 let balanceForeignQtzTokenInit: bigint;371 let balanceForeignQtzTokenMiddle: bigint;372 let balanceForeignQtzTokenFinal: bigint;373 let balanceMovrTokenInit: bigint;374 let balanceMovrTokenMiddle: bigint;375 let balanceMovrTokenFinal: bigint;376377 before(async () => {378 await usingPlaygrounds(async (_helper, privateKey) => {379 const keyringEth = new Keyring({type: 'ethereum'});380 const keyringSr25519 = new Keyring({type: 'sr25519'});381382 quartzAlice = privateKey('//Alice');383 randomAccountQuartz = generateKeyringPair(keyringSr25519);384 randomAccountMoonriver = generateKeyringPair(keyringEth);385386 balanceForeignQtzTokenInit = 0n;387 });388389 await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => {390 const alithAccount = helper.account.alithAccount();391 const baltatharAccount = helper.account.baltatharAccount();392 const dorothyAccount = helper.account.dorothyAccount();393394 // >>> Sponsoring Dorothy >>>395 console.log('Sponsoring Dorothy.......');396 await helper.balance.transferToEthereum(alithAccount, dorothyAccount.address, 11_000_000_000_000_000_000n);397 console.log('Sponsoring Dorothy.......DONE');398 // <<< Sponsoring Dorothy <<<399400 quartzAssetLocation = {401 XCM: {402 parents: 1,403 interior: {X1: {Parachain: QUARTZ_CHAIN}},404 },405 };406 const existentialDeposit = 1n;407 const isSufficient = true;408 const unitsPerSecond = 1n;409 const numAssetsWeightHint = 0;410411 const encodedProposal = helper.assetManager.makeRegisterForeignAssetProposal({412 location: quartzAssetLocation,413 metadata: quartzAssetMetadata,414 existentialDeposit,415 isSufficient,416 unitsPerSecond,417 numAssetsWeightHint,418 });419 const proposalHash = blake2AsHex(encodedProposal);420421 console.log('Encoded proposal for registerForeignAsset & setAssetUnitsPerSecond is %s', encodedProposal);422 console.log('Encoded length %d', encodedProposal.length);423 console.log('Encoded proposal hash for batch utility after schedule is %s', proposalHash);424425 // >>> Note motion preimage >>>426 console.log('Note motion preimage.......');427 await helper.democracy.notePreimage(baltatharAccount, encodedProposal);428 console.log('Note motion preimage.......DONE');429 // <<< Note motion preimage <<<430431 // >>> Propose external motion through council >>>432 console.log('Propose external motion through council.......');433 const externalMotion = helper.democracy.externalProposeMajority(proposalHash);434 const encodedMotion = externalMotion?.method.toHex() || '';435 const motionHash = blake2AsHex(encodedMotion);436 console.log('Motion hash is %s', motionHash);437438 await helper.collective.council.propose(baltatharAccount, councilVotingThreshold, externalMotion, externalMotion.encodedLength);439440 const councilProposalIdx = await helper.collective.council.proposalCount() - 1;441 await helper.collective.council.vote(dorothyAccount, motionHash, councilProposalIdx, true);442 await helper.collective.council.vote(baltatharAccount, motionHash, councilProposalIdx, true);443444 await helper.collective.council.close(dorothyAccount, motionHash, councilProposalIdx, 1_000_000_000, externalMotion.encodedLength);445 console.log('Propose external motion through council.......DONE');446 // <<< Propose external motion through council <<<447448 // >>> Fast track proposal through technical committee >>>449 console.log('Fast track proposal through technical committee.......');450 const fastTrack = helper.democracy.fastTrack(proposalHash, votingPeriod, delayPeriod);451 const encodedFastTrack = fastTrack?.method.toHex() || '';452 const fastTrackHash = blake2AsHex(encodedFastTrack);453 console.log('FastTrack hash is %s', fastTrackHash);454455 await helper.collective.techCommittee.propose(alithAccount, technicalCommitteeThreshold, fastTrack, fastTrack.encodedLength);456457 const techProposalIdx = await helper.collective.techCommittee.proposalCount() - 1;458 await helper.collective.techCommittee.vote(baltatharAccount, fastTrackHash, techProposalIdx, true);459 await helper.collective.techCommittee.vote(alithAccount, fastTrackHash, techProposalIdx, true);460461 await helper.collective.techCommittee.close(baltatharAccount, fastTrackHash, techProposalIdx, 1_000_000_000, fastTrack.encodedLength);462 console.log('Fast track proposal through technical committee.......DONE');463 // <<< Fast track proposal through technical committee <<<464465 // >>> Referendum voting >>>466 console.log('Referendum voting.......');467 await helper.democracy.referendumVote(dorothyAccount, 0, {468 balance: 10_000_000_000_000_000_000n,469 vote: {aye: true, conviction: 1},470 });471 console.log('Referendum voting.......DONE');472 // <<< Referendum voting <<<473474 // >>> Acquire Quartz AssetId Info on Moonriver >>>475 console.log('Acquire Quartz AssetId Info on Moonriver.......');476477 // Wait for the democracy execute478 await helper.wait.newBlocks(5);479480 assetId = (await helper.assetManager.assetTypeId(quartzAssetLocation)).toString();481482 console.log('QTZ asset ID is %s', assetId);483 console.log('Acquire Quartz AssetId Info on Moonriver.......DONE');484 // >>> Acquire Quartz AssetId Info on Moonriver >>>485486 // >>> Sponsoring random Account >>>487 console.log('Sponsoring random Account.......');488 await helper.balance.transferToEthereum(baltatharAccount, randomAccountMoonriver.address, 11_000_000_000_000_000_000n);489 console.log('Sponsoring random Account.......DONE');490 // <<< Sponsoring random Account <<<491492 balanceMovrTokenInit = await helper.balance.getEthereum(randomAccountMoonriver.address);493 });494495 await usingPlaygrounds(async (helper) => {496 await helper.balance.transferToSubstrate(quartzAlice, randomAccountQuartz.address, 10n * TRANSFER_AMOUNT);497 balanceQuartzTokenInit = await helper.balance.getSubstrate(randomAccountQuartz.address);498 });499 });500501 itSub('Should connect and send QTZ to Moonriver', async ({helper}) => {502 const currencyId = {503 NativeAssetId: 'Here',504 };505 const dest = {506 V1: {507 parents: 1,508 interior: {509 X2: [510 {Parachain: MOONRIVER_CHAIN},511 {AccountKey20: {network: 'Any', key: randomAccountMoonriver.address}},512 ],513 },514 },515 };516 const amount = TRANSFER_AMOUNT;517 const destWeight = 850000000;518519 await helper.xTokens.transfer(randomAccountQuartz, currencyId, amount, dest, destWeight);520521 balanceQuartzTokenMiddle = await helper.balance.getSubstrate(randomAccountQuartz.address);522 expect(balanceQuartzTokenMiddle < balanceQuartzTokenInit).to.be.true;523524 const transactionFees = balanceQuartzTokenInit - balanceQuartzTokenMiddle - TRANSFER_AMOUNT;525 console.log('[Quartz -> Moonriver] transaction fees on Quartz: %s QTZ', bigIntToDecimals(transactionFees));526 expect(transactionFees > 0).to.be.true;527528 await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => {529 await helper.wait.newBlocks(3);530531 balanceMovrTokenMiddle = await helper.balance.getEthereum(randomAccountMoonriver.address);532533 const movrFees = balanceMovrTokenInit - balanceMovrTokenMiddle;534 console.log('[Quartz -> Moonriver] transaction fees on Moonriver: %s MOVR',bigIntToDecimals(movrFees));535 expect(movrFees == 0n).to.be.true;536537 balanceForeignQtzTokenMiddle = (await helper.assets.account(assetId, randomAccountMoonriver.address))!; // BigInt(qtzRandomAccountAsset['balance']);538 const qtzIncomeTransfer = balanceForeignQtzTokenMiddle - balanceForeignQtzTokenInit;539 console.log('[Quartz -> Moonriver] income %s QTZ', bigIntToDecimals(qtzIncomeTransfer));540 expect(qtzIncomeTransfer == TRANSFER_AMOUNT).to.be.true;541 });542 });543544 itSub('Should connect to Moonriver and send QTZ back', async ({helper}) => {545 await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => {546 const asset = {547 V1: {548 id: {549 Concrete: {550 parents: 1,551 interior: {552 X1: {Parachain: QUARTZ_CHAIN},553 },554 },555 },556 fun: {557 Fungible: TRANSFER_AMOUNT,558 },559 },560 };561 const destination = {562 V1: {563 parents: 1,564 interior: {565 X2: [566 {Parachain: QUARTZ_CHAIN},567 {AccountId32: {network: 'Any', id: randomAccountQuartz.addressRaw}},568 ],569 },570 },571 };572 const destWeight = 50000000;573574 await helper.xTokens.transferMultiasset(randomAccountMoonriver, asset, destination, destWeight);575576 balanceMovrTokenFinal = await helper.balance.getEthereum(randomAccountMoonriver.address);577578 const movrFees = balanceMovrTokenMiddle - balanceMovrTokenFinal;579 console.log('[Moonriver -> Quartz] transaction fees on Moonriver: %s MOVR', bigIntToDecimals(movrFees));580 expect(movrFees > 0).to.be.true;581582 const qtzRandomAccountAsset = await helper.assets.account(assetId, randomAccountMoonriver.address);583584 expect(qtzRandomAccountAsset).to.be.null;585586 balanceForeignQtzTokenFinal = 0n;587588 const qtzOutcomeTransfer = balanceForeignQtzTokenMiddle - balanceForeignQtzTokenFinal;589 console.log('[Quartz -> Moonriver] outcome %s QTZ', bigIntToDecimals(qtzOutcomeTransfer));590 expect(qtzOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;591 });592593 await helper.wait.newBlocks(3);594595 balanceQuartzTokenFinal = await helper.balance.getSubstrate(randomAccountQuartz.address);596 const actuallyDelivered = balanceQuartzTokenFinal - balanceQuartzTokenMiddle;597 expect(actuallyDelivered > 0).to.be.true;598599 console.log('[Moonriver -> Quartz] actually delivered %s QTZ', bigIntToDecimals(actuallyDelivered));600601 const qtzFees = TRANSFER_AMOUNT - actuallyDelivered;602 console.log('[Moonriver -> Quartz] transaction fees on Quartz: %s QTZ', bigIntToDecimals(qtzFees));603 expect(qtzFees == 0n).to.be.true;604 });605});