git.delta.rocks / unique-network / refs/commits / 7b023557183b

difftreelog

source

tests/src/xcm/xcmQuartz.test.ts20.8 KiBsourcehistory
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 {IKeyringPair} from '@polkadot/types/types';18import {blake2AsHex} from '@polkadot/util-crypto';19import {XcmV2TraitsOutcome, XcmV2TraitsError} from '../interfaces';20import {itSub, expect, describeXcm, usingPlaygrounds, usingKaruraPlaygrounds, usingRelayPlaygrounds, usingMoonriverPlaygrounds} from '../util/playgrounds';2122const QUARTZ_CHAIN = 2095;23const KARURA_CHAIN = 2000;24const MOONRIVER_CHAIN = 2023;2526const RELAY_PORT = 9844;27const KARURA_PORT = 9946;28const MOONRIVER_PORT = 9947;2930const relayUrl = 'ws://127.0.0.1:' + RELAY_PORT;31const karuraUrl = 'ws://127.0.0.1:' + KARURA_PORT;32const moonriverUrl = 'ws://127.0.0.1:' + MOONRIVER_PORT;3334const KARURA_DECIMALS = 12;3536const TRANSFER_AMOUNT = 2000000000000000000000000n;3738describeXcm('[XCM] Integration test: Exchanging tokens with Karura', () => {39  let alice: IKeyringPair;40  let randomAccount: IKeyringPair;4142  let balanceQuartzTokenInit: bigint;43  let balanceQuartzTokenMiddle: bigint;44  let balanceQuartzTokenFinal: bigint;45  let balanceKaruraTokenInit: bigint;46  let balanceKaruraTokenMiddle: bigint;47  let balanceKaruraTokenFinal: bigint;48  let balanceQuartzForeignTokenInit: bigint;49  let balanceQuartzForeignTokenMiddle: bigint;50  let balanceQuartzForeignTokenFinal: bigint;5152  before(async () => {53    await usingPlaygrounds(async (helper, privateKey) => {54      alice = privateKey('//Alice');55      [randomAccount] = await helper.arrange.createAccounts([0n], alice);56    });5758    await usingKaruraPlaygrounds(karuraUrl, async (helper) => {59      const destination = {60        V0: {61          X2: [62            'Parent',63            {64              Parachain: QUARTZ_CHAIN,65            },66          ],67        },68      };6970      const metadata = {71        name: 'QTZ',72        symbol: 'QTZ',73        decimals: 18,74        minimalBalance: 1n,75      };7677      await helper.getSudo().assetRegistry.registerForeignAsset(alice, destination, metadata);78      await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n);79      balanceKaruraTokenInit = await helper.balance.getSubstrate(randomAccount.address);80      balanceQuartzForeignTokenInit = await helper.tokens.accounts(randomAccount.address, {ForeignAsset: 0});81    });8283    await usingPlaygrounds(async (helper) => {84      await helper.balance.transferToSubstrate(alice, randomAccount.address, 10n * TRANSFER_AMOUNT);85      balanceQuartzTokenInit = await helper.balance.getSubstrate(randomAccount.address);86    });87  });8889  itSub('Should connect and send QTZ to Karura', async ({helper}) => {90    const destination = {91      V0: {92        X2: [93          'Parent',94          {95            Parachain: KARURA_CHAIN,96          },97        ],98      },99    };100101    const beneficiary = {102      V0: {103        X1: {104          AccountId32: {105            network: 'Any',106            id: randomAccount.addressRaw,107          },108        },109      },110    };111112    const assets = {113      V1: [114        {115          id: {116            Concrete: {117              parents: 0,118              interior: 'Here',119            },120          },121          fun: {122            Fungible: TRANSFER_AMOUNT,123          },124        },125      ],126    };127128    const feeAssetItem = 0;129    const weightLimit = 5000000000;130131    await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, weightLimit);132    balanceQuartzTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);133134    const qtzFees = balanceQuartzTokenInit - balanceQuartzTokenMiddle - TRANSFER_AMOUNT;135    console.log('[Quartz -> Karura] transaction fees on Quartz: %s QTZ', helper.util.bigIntToDecimals(qtzFees));136    expect(qtzFees > 0n).to.be.true;137138    await usingKaruraPlaygrounds(karuraUrl, async (helper) => {139      await helper.wait.newBlocks(3);140      balanceQuartzForeignTokenMiddle = await helper.tokens.accounts(randomAccount.address, {ForeignAsset: 0});141      balanceKaruraTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);142143      const karFees = balanceKaruraTokenInit - balanceKaruraTokenMiddle;144      const qtzIncomeTransfer = balanceQuartzForeignTokenMiddle - balanceQuartzForeignTokenInit;145146      console.log(147        '[Quartz -> Karura] transaction fees on Karura: %s KAR',148        helper.util.bigIntToDecimals(karFees, KARURA_DECIMALS),149      );150      console.log('[Quartz -> Karura] income %s QTZ', helper.util.bigIntToDecimals(qtzIncomeTransfer));151      expect(karFees == 0n).to.be.true;152      expect(qtzIncomeTransfer == TRANSFER_AMOUNT).to.be.true;153    });154  });155156  itSub('Should connect to Karura and send QTZ back', async ({helper}) => {157    await usingKaruraPlaygrounds(karuraUrl, async (helper) => {158      const destination = {159        V1: {160          parents: 1,161          interior: {162            X2: [163              {Parachain: QUARTZ_CHAIN},164              {165                AccountId32: {166                  network: 'Any',167                  id: randomAccount.addressRaw,168                },169              },170            ],171          },172        },173      };174175      const id = {176        ForeignAsset: 0,177      };178179      const destWeight = 50000000;180181      await helper.xTokens.transfer(randomAccount, id, TRANSFER_AMOUNT, destination, destWeight);182      balanceKaruraTokenFinal = await helper.balance.getSubstrate(randomAccount.address);183      balanceQuartzForeignTokenFinal = await helper.tokens.accounts(randomAccount.address, id);184185      const karFees = balanceKaruraTokenMiddle - balanceKaruraTokenFinal;186      const qtzOutcomeTransfer = balanceQuartzForeignTokenMiddle - balanceQuartzForeignTokenFinal;187188      console.log(189        '[Karura -> Quartz] transaction fees on Karura: %s KAR',190        helper.util.bigIntToDecimals(karFees, KARURA_DECIMALS),191      );192      console.log('[Karura -> Quartz] outcome %s QTZ', helper.util.bigIntToDecimals(qtzOutcomeTransfer));193194      expect(karFees > 0).to.be.true;195      expect(qtzOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;196    });197198    await helper.wait.newBlocks(3);199200    balanceQuartzTokenFinal = await helper.balance.getSubstrate(randomAccount.address);201    const actuallyDelivered = balanceQuartzTokenFinal - balanceQuartzTokenMiddle;202    expect(actuallyDelivered > 0).to.be.true;203204    console.log('[Karura -> Quartz] actually delivered %s QTZ', helper.util.bigIntToDecimals(actuallyDelivered));205206    const qtzFees = TRANSFER_AMOUNT - actuallyDelivered;207    console.log('[Karura -> Quartz] transaction fees on Quartz: %s QTZ', helper.util.bigIntToDecimals(qtzFees));208    expect(qtzFees == 0n).to.be.true;209  });210});211212// These tests are relevant only when the foreign asset pallet is disabled213describeXcm('[XCM] Integration test: Quartz rejects non-native tokens', () => {214  let alice: IKeyringPair;215216  before(async () => {217    await usingPlaygrounds(async (_helper, privateKey) => {218      alice = privateKey('//Alice');219    });220  });221222  itSub('Quartz rejects tokens from the Relay', async ({helper}) => {223    await usingRelayPlaygrounds(relayUrl, async (helper) => {224      const destination = {225        V1: {226          parents: 0,227          interior: {X1: {228            Parachain: QUARTZ_CHAIN,229          },230          },231        }};232233      const beneficiary = {234        V1: {235          parents: 0,236          interior: {X1: {237            AccountId32: {238              network: 'Any',239              id: alice.addressRaw,240            },241          }},242        },243      };244245      const assets = {246        V1: [247          {248            id: {249              Concrete: {250                parents: 0,251                interior: 'Here',252              },253            },254            fun: {255              Fungible: 50_000_000_000_000_000n,256            },257          },258        ],259      };260261      const feeAssetItem = 0;262      const weightLimit = 5_000_000_000;263264      await helper.xcm.limitedReserveTransferAssets(alice, destination, beneficiary, assets, feeAssetItem, weightLimit);265    });266267    const maxWaitBlocks = 3;268269    const dmpQueueExecutedDownward = await helper.wait.event(maxWaitBlocks, 'dmpQueue', 'ExecutedDownward');270271    expect(272      dmpQueueExecutedDownward != null,273      '[Relay] dmpQueue.ExecutedDownward event is expected',274    ).to.be.true;275276    const event = dmpQueueExecutedDownward!.event;277    const outcome = event.data[1] as XcmV2TraitsOutcome;278279    expect(280      outcome.isIncomplete,281      '[Relay] The outcome of the XCM should be `Incomplete`',282    ).to.be.true;283284    const incomplete = outcome.asIncomplete;285    expect(286      incomplete[1].toString() == 'AssetNotFound',287      '[Relay] The XCM error should be `AssetNotFound`',288    ).to.be.true;289  });290291  itSub('Quartz rejects KAR tokens from Karura', async ({helper}) => {292    await usingKaruraPlaygrounds(karuraUrl, async (helper) => {293      const destination = {294        V1: {295          parents: 1,296          interior: {297            X2: [298              {Parachain: QUARTZ_CHAIN},299              {300                AccountId32: {301                  network: 'Any',302                  id: alice.addressRaw,303                },304              },305            ],306          },307        },308      };309310      const id = {311        Token: 'KAR',312      };313314      const destWeight = 50000000;315316      await helper.xTokens.transfer(alice, id, 100_000_000_000n, destination, destWeight);317    });318319    const maxWaitBlocks = 3;320321    const xcmpQueueFailEvent = await helper.wait.event(maxWaitBlocks, 'xcmpQueue', 'Fail');322323    expect(324      xcmpQueueFailEvent != null,325      '[Karura] xcmpQueue.FailEvent event is expected',326    ).to.be.true;327328    const event = xcmpQueueFailEvent!.event;329    const outcome = event.data[1] as XcmV2TraitsError;330331    expect(332      outcome.isUntrustedReserveLocation,333      '[Karura] The XCM error should be `UntrustedReserveLocation`',334    ).to.be.true;335  });336});337338describeXcm('[XCM] Integration test: Exchanging QTZ with Moonriver', () => {339340  // Quartz constants341  let quartzDonor: IKeyringPair;342  let quartzAssetLocation;343344  let randomAccountQuartz: IKeyringPair;345  let randomAccountMoonriver: IKeyringPair;346347  // Moonriver constants348  let assetId: string;349350  const councilVotingThreshold = 2;351  const technicalCommitteeThreshold = 2;352  const votingPeriod = 3;353  const delayPeriod = 0;354355  const quartzAssetMetadata = {356    name: 'xcQuartz',357    symbol: 'xcQTZ',358    decimals: 18,359    isFrozen: false,360    minimalBalance: 1n,361  };362363  let balanceQuartzTokenInit: bigint;364  let balanceQuartzTokenMiddle: bigint;365  let balanceQuartzTokenFinal: bigint;366  let balanceForeignQtzTokenInit: bigint;367  let balanceForeignQtzTokenMiddle: bigint;368  let balanceForeignQtzTokenFinal: bigint;369  let balanceMovrTokenInit: bigint;370  let balanceMovrTokenMiddle: bigint;371  let balanceMovrTokenFinal: bigint;372373  before(async () => {374    await usingPlaygrounds(async (helper, privateKey) => {375      quartzDonor = privateKey('//Alice');376      [randomAccountQuartz] = await helper.arrange.createAccounts([0n], quartzDonor);377378      balanceForeignQtzTokenInit = 0n;379    });380381    await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => {382      const alithAccount = helper.account.alithAccount();383      const baltatharAccount = helper.account.baltatharAccount();384      const dorothyAccount = helper.account.dorothyAccount();385386      randomAccountMoonriver = helper.account.create();387388      // >>> Sponsoring Dorothy >>>389      console.log('Sponsoring Dorothy.......');390      await helper.balance.transferToEthereum(alithAccount, dorothyAccount.address, 11_000_000_000_000_000_000n);391      console.log('Sponsoring Dorothy.......DONE');392      // <<< Sponsoring Dorothy <<<393394      quartzAssetLocation = {395        XCM: {396          parents: 1,397          interior: {X1: {Parachain: QUARTZ_CHAIN}},398        },399      };400      const existentialDeposit = 1n;401      const isSufficient = true;402      const unitsPerSecond = 1n;403      const numAssetsWeightHint = 0;404405      const encodedProposal = helper.assetManager.makeRegisterForeignAssetProposal({406        location: quartzAssetLocation,407        metadata: quartzAssetMetadata,408        existentialDeposit,409        isSufficient,410        unitsPerSecond,411        numAssetsWeightHint,412      });413      const proposalHash = blake2AsHex(encodedProposal);414415      console.log('Encoded proposal for registerForeignAsset & setAssetUnitsPerSecond is %s', encodedProposal);416      console.log('Encoded length %d', encodedProposal.length);417      console.log('Encoded proposal hash for batch utility after schedule is %s', proposalHash);418419      // >>> Note motion preimage >>>420      console.log('Note motion preimage.......');421      await helper.democracy.notePreimage(baltatharAccount, encodedProposal);422      console.log('Note motion preimage.......DONE');423      // <<< Note motion preimage <<<424425      // >>> Propose external motion through council >>>426      console.log('Propose external motion through council.......');427      const externalMotion = helper.democracy.externalProposeMajority(proposalHash);428      const encodedMotion = externalMotion?.method.toHex() || '';429      const motionHash = blake2AsHex(encodedMotion);430      console.log('Motion hash is %s', motionHash);431432      await helper.collective.council.propose(baltatharAccount, councilVotingThreshold, externalMotion, externalMotion.encodedLength);433434      const councilProposalIdx = await helper.collective.council.proposalCount() - 1;435      await helper.collective.council.vote(dorothyAccount, motionHash, councilProposalIdx, true);436      await helper.collective.council.vote(baltatharAccount, motionHash, councilProposalIdx, true);437438      await helper.collective.council.close(dorothyAccount, motionHash, councilProposalIdx, 1_000_000_000, externalMotion.encodedLength);439      console.log('Propose external motion through council.......DONE');440      // <<< Propose external motion through council <<<441442      // >>> Fast track proposal through technical committee >>>443      console.log('Fast track proposal through technical committee.......');444      const fastTrack = helper.democracy.fastTrack(proposalHash, votingPeriod, delayPeriod);445      const encodedFastTrack = fastTrack?.method.toHex() || '';446      const fastTrackHash = blake2AsHex(encodedFastTrack);447      console.log('FastTrack hash is %s', fastTrackHash);448449      await helper.collective.techCommittee.propose(alithAccount, technicalCommitteeThreshold, fastTrack, fastTrack.encodedLength);450451      const techProposalIdx = await helper.collective.techCommittee.proposalCount() - 1;452      await helper.collective.techCommittee.vote(baltatharAccount, fastTrackHash, techProposalIdx, true);453      await helper.collective.techCommittee.vote(alithAccount, fastTrackHash, techProposalIdx, true);454455      await helper.collective.techCommittee.close(baltatharAccount, fastTrackHash, techProposalIdx, 1_000_000_000, fastTrack.encodedLength);456      console.log('Fast track proposal through technical committee.......DONE');457      // <<< Fast track proposal through technical committee <<<458459      // >>> Referendum voting >>>460      console.log('Referendum voting.......');461      await helper.democracy.referendumVote(dorothyAccount, 0, {462        balance: 10_000_000_000_000_000_000n,463        vote: {aye: true, conviction: 1},464      });465      console.log('Referendum voting.......DONE');466      // <<< Referendum voting <<<467468      // >>> Acquire Quartz AssetId Info on Moonriver >>>469      console.log('Acquire Quartz AssetId Info on Moonriver.......');470471      // Wait for the democracy execute472      await helper.wait.newBlocks(5);473474      assetId = (await helper.assetManager.assetTypeId(quartzAssetLocation)).toString();475476      console.log('QTZ asset ID is %s', assetId);477      console.log('Acquire Quartz AssetId Info on Moonriver.......DONE');478      // >>> Acquire Quartz AssetId Info on Moonriver >>>479480      // >>> Sponsoring random Account >>>481      console.log('Sponsoring random Account.......');482      await helper.balance.transferToEthereum(baltatharAccount, randomAccountMoonriver.address, 11_000_000_000_000_000_000n);483      console.log('Sponsoring random Account.......DONE');484      // <<< Sponsoring random Account <<<485486      balanceMovrTokenInit = await helper.balance.getEthereum(randomAccountMoonriver.address);487    });488489    await usingPlaygrounds(async (helper) => {490      await helper.balance.transferToSubstrate(quartzDonor, randomAccountQuartz.address, 10n * TRANSFER_AMOUNT);491      balanceQuartzTokenInit = await helper.balance.getSubstrate(randomAccountQuartz.address);492    });493  });494495  itSub('Should connect and send QTZ to Moonriver', async ({helper}) => {496    const currencyId = {497      NativeAssetId: 'Here',498    };499    const dest = {500      V1: {501        parents: 1,502        interior: {503          X2: [504            {Parachain: MOONRIVER_CHAIN},505            {AccountKey20: {network: 'Any', key: randomAccountMoonriver.address}},506          ],507        },508      },509    };510    const amount = TRANSFER_AMOUNT;511    const destWeight = 850000000;512513    await helper.xTokens.transfer(randomAccountQuartz, currencyId, amount, dest, destWeight);514515    balanceQuartzTokenMiddle = await helper.balance.getSubstrate(randomAccountQuartz.address);516    expect(balanceQuartzTokenMiddle < balanceQuartzTokenInit).to.be.true;517518    const transactionFees = balanceQuartzTokenInit - balanceQuartzTokenMiddle - TRANSFER_AMOUNT;519    console.log('[Quartz -> Moonriver] transaction fees on Quartz: %s QTZ', helper.util.bigIntToDecimals(transactionFees));520    expect(transactionFees > 0).to.be.true;521522    await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => {523      await helper.wait.newBlocks(3);524525      balanceMovrTokenMiddle = await helper.balance.getEthereum(randomAccountMoonriver.address);526527      const movrFees = balanceMovrTokenInit - balanceMovrTokenMiddle;528      console.log('[Quartz -> Moonriver] transaction fees on Moonriver: %s MOVR',helper.util.bigIntToDecimals(movrFees));529      expect(movrFees == 0n).to.be.true;530531      balanceForeignQtzTokenMiddle = (await helper.assets.account(assetId, randomAccountMoonriver.address))!; // BigInt(qtzRandomAccountAsset['balance']);532      const qtzIncomeTransfer = balanceForeignQtzTokenMiddle - balanceForeignQtzTokenInit;533      console.log('[Quartz -> Moonriver] income %s QTZ', helper.util.bigIntToDecimals(qtzIncomeTransfer));534      expect(qtzIncomeTransfer == TRANSFER_AMOUNT).to.be.true;535    });536  });537538  itSub('Should connect to Moonriver and send QTZ back', async ({helper}) => {539    await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => {540      const asset = {541        V1: {542          id: {543            Concrete: {544              parents: 1,545              interior: {546                X1: {Parachain: QUARTZ_CHAIN},547              },548            },549          },550          fun: {551            Fungible: TRANSFER_AMOUNT,552          },553        },554      };555      const destination = {556        V1: {557          parents: 1,558          interior: {559            X2: [560              {Parachain: QUARTZ_CHAIN},561              {AccountId32: {network: 'Any', id: randomAccountQuartz.addressRaw}},562            ],563          },564        },565      };566      const destWeight = 50000000;567568      await helper.xTokens.transferMultiasset(randomAccountMoonriver, asset, destination, destWeight);569570      balanceMovrTokenFinal = await helper.balance.getEthereum(randomAccountMoonriver.address);571572      const movrFees = balanceMovrTokenMiddle - balanceMovrTokenFinal;573      console.log('[Moonriver -> Quartz] transaction fees on Moonriver: %s MOVR', helper.util.bigIntToDecimals(movrFees));574      expect(movrFees > 0).to.be.true;575576      const qtzRandomAccountAsset = await helper.assets.account(assetId, randomAccountMoonriver.address);577578      expect(qtzRandomAccountAsset).to.be.null;579580      balanceForeignQtzTokenFinal = 0n;581582      const qtzOutcomeTransfer = balanceForeignQtzTokenMiddle - balanceForeignQtzTokenFinal;583      console.log('[Quartz -> Moonriver] outcome %s QTZ', helper.util.bigIntToDecimals(qtzOutcomeTransfer));584      expect(qtzOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;585    });586587    await helper.wait.newBlocks(3);588589    balanceQuartzTokenFinal = await helper.balance.getSubstrate(randomAccountQuartz.address);590    const actuallyDelivered = balanceQuartzTokenFinal - balanceQuartzTokenMiddle;591    expect(actuallyDelivered > 0).to.be.true;592593    console.log('[Moonriver -> Quartz] actually delivered %s QTZ', helper.util.bigIntToDecimals(actuallyDelivered));594595    const qtzFees = TRANSFER_AMOUNT - actuallyDelivered;596    console.log('[Moonriver -> Quartz] transaction fees on Quartz: %s QTZ', helper.util.bigIntToDecimals(qtzFees));597    expect(qtzFees == 0n).to.be.true;598  });599});