git.delta.rocks / unique-network / refs/commits / 3b6e667c53c2

difftreelog

fix remove redundant comment

Daniel Shiposha2022-10-10parent: #c18879f.patch.diff
in: master

1 file changed

modifiedtests/src/xcm/xcmQuartz.test.tsdiffbeforeafterboth
before · tests/src/xcm/xcmQuartz.test.ts
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    // 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});
after · tests/src/xcm/xcmQuartz.test.ts
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});