git.delta.rocks / unique-network / refs/commits / 6f5be65656d0

difftreelog

source

tests/src/xcm/xcmQuartz.test.ts31.3 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 config from '../config';20import {XcmV2TraitsError} from '../interfaces';21import {itSub, expect, describeXCM, usingPlaygrounds, usingKaruraPlaygrounds, usingRelayPlaygrounds, usingMoonriverPlaygrounds, usingStateminePlaygrounds} from '../util';2223const QUARTZ_CHAIN = 2095;24const STATEMINE_CHAIN = 1000;25const KARURA_CHAIN = 2000;26const MOONRIVER_CHAIN = 2023;2728const STATEMINE_PALLET_INSTANCE = 50;2930const relayUrl = config.relayUrl;31const statemineUrl = config.statemineUrl;32const karuraUrl = config.karuraUrl;33const moonriverUrl = config.moonriverUrl;3435const RELAY_DECIMALS = 12;36const STATEMINE_DECIMALS = 12;37const KARURA_DECIMALS = 12;3839const TRANSFER_AMOUNT = 2000000000000000000000000n;4041const FUNDING_AMOUNT = 3_500_000_0000_000_000n;4243const TRANSFER_AMOUNT_RELAY = 50_000_000_000_000_000n;4445const USDT_ASSET_ID = 100;46const USDT_ASSET_METADATA_DECIMALS = 18;47const USDT_ASSET_METADATA_NAME = 'USDT';48const USDT_ASSET_METADATA_DESCRIPTION = 'USDT';49const USDT_ASSET_METADATA_MINIMAL_BALANCE = 1n;50const USDT_ASSET_AMOUNT = 10_000_000_000_000_000_000_000_000n;5152describeXCM('[XCM] Integration test: Exchanging USDT with Statemine', () => {53  let alice: IKeyringPair;54  let bob: IKeyringPair;5556  let balanceStmnBefore: bigint;57  let balanceStmnAfter: bigint;5859  let balanceQuartzBefore: bigint;60  let balanceQuartzAfter: bigint;61  let balanceQuartzFinal: bigint;6263  let balanceBobBefore: bigint;64  let balanceBobAfter: bigint;65  let balanceBobFinal: bigint;6667  let balanceBobRelayTokenBefore: bigint;68  let balanceBobRelayTokenAfter: bigint;697071  before(async () => {72    await usingPlaygrounds(async (_helper, privateKey) => {73      alice = await privateKey('//Alice');74      bob = await privateKey('//Bob'); // sovereign account on Statemine(t) funds donor75    });7677    await usingRelayPlaygrounds(relayUrl, async (helper) => {78      // Fund accounts on Statemine(t)79      await helper.xcm.teleportNativeAsset(alice, STATEMINE_CHAIN, alice.addressRaw, FUNDING_AMOUNT);80      await helper.xcm.teleportNativeAsset(alice, STATEMINE_CHAIN, bob.addressRaw, FUNDING_AMOUNT);81    });8283    await usingStateminePlaygrounds(statemineUrl, async (helper) => {84      const sovereignFundingAmount = 3_500_000_000n;8586      await helper.assets.create(87        alice,88        USDT_ASSET_ID,89        alice.address,90        USDT_ASSET_METADATA_MINIMAL_BALANCE,91      );92      await helper.assets.setMetadata(93        alice,94        USDT_ASSET_ID,95        USDT_ASSET_METADATA_NAME,96        USDT_ASSET_METADATA_DESCRIPTION,97        USDT_ASSET_METADATA_DECIMALS,98      );99      await helper.assets.mint(100        alice,101        USDT_ASSET_ID,102        alice.address,103        USDT_ASSET_AMOUNT,104      );105106      // funding parachain sovereing account on Statemine(t).107      // The sovereign account should be created before any action108      // (the assets pallet on Statemine(t) check if the sovereign account exists)109      const parachainSovereingAccount = helper.address.paraSiblingSovereignAccount(QUARTZ_CHAIN);110      await helper.balance.transferToSubstrate(bob, parachainSovereingAccount, sovereignFundingAmount);111    });112113114    await usingPlaygrounds(async (helper) => {115      const location = {116        V1: {117          parents: 1,118          interior: {X3: [119            {120              Parachain: STATEMINE_CHAIN,121            },122            {123              PalletInstance: STATEMINE_PALLET_INSTANCE,124            },125            {126              GeneralIndex: USDT_ASSET_ID,127            },128          ]},129        },130      };131132      const metadata =133      {134        name: USDT_ASSET_ID,135        symbol: USDT_ASSET_METADATA_NAME,136        decimals: USDT_ASSET_METADATA_DECIMALS,137        minimalBalance: USDT_ASSET_METADATA_MINIMAL_BALANCE,138      };139      await helper.getSudo().foreignAssets.register(alice, alice.address, location, metadata);140      balanceQuartzBefore = await helper.balance.getSubstrate(alice.address);141    });142143144    // Providing the relay currency to the quartz sender account145    // (fee for USDT XCM are paid in relay tokens)146    await usingRelayPlaygrounds(relayUrl, async (helper) => {147      const destination = {148        V1: {149          parents: 0,150          interior: {X1: {151            Parachain: QUARTZ_CHAIN,152          },153          },154        }};155156      const beneficiary = {157        V1: {158          parents: 0,159          interior: {X1: {160            AccountId32: {161              network: 'Any',162              id: alice.addressRaw,163            },164          }},165        },166      };167168      const assets = {169        V1: [170          {171            id: {172              Concrete: {173                parents: 0,174                interior: 'Here',175              },176            },177            fun: {178              Fungible: TRANSFER_AMOUNT_RELAY,179            },180          },181        ],182      };183184      const feeAssetItem = 0;185186      await helper.xcm.limitedReserveTransferAssets(alice, destination, beneficiary, assets, feeAssetItem, 'Unlimited');187    });188189  });190191  itSub('Should connect and send USDT from Statemine to Quartz', async ({helper}) => {192    await usingStateminePlaygrounds(statemineUrl, async (helper) => {193      const dest = {194        V1: {195          parents: 1,196          interior: {X1: {197            Parachain: QUARTZ_CHAIN,198          },199          },200        }};201202      const beneficiary = {203        V1: {204          parents: 0,205          interior: {X1: {206            AccountId32: {207              network: 'Any',208              id: alice.addressRaw,209            },210          }},211        },212      };213214      const assets = {215        V1: [216          {217            id: {218              Concrete: {219                parents: 0,220                interior: {221                  X2: [222                    {223                      PalletInstance: STATEMINE_PALLET_INSTANCE,224                    },225                    {226                      GeneralIndex: USDT_ASSET_ID,227                    },228                  ]},229              },230            },231            fun: {232              Fungible: TRANSFER_AMOUNT,233            },234          },235        ],236      };237238      const feeAssetItem = 0;239240      balanceStmnBefore = await helper.balance.getSubstrate(alice.address);241      await helper.xcm.limitedReserveTransferAssets(alice, dest, beneficiary, assets, feeAssetItem, 'Unlimited');242243      balanceStmnAfter = await helper.balance.getSubstrate(alice.address);244245      // common good parachain take commission in it native token246      console.log(247        '[Statemine -> Quartz] transaction fees on Statemine: %s WND',248        helper.util.bigIntToDecimals(balanceStmnBefore - balanceStmnAfter, STATEMINE_DECIMALS),249      );250      expect(balanceStmnBefore > balanceStmnAfter).to.be.true;251252    });253254255    // ensure that asset has been delivered256    await helper.wait.newBlocks(3);257258    // expext collection id will be with id 1259    const free = await helper.ft.getBalance(1, {Substrate: alice.address});260261    balanceQuartzAfter = await helper.balance.getSubstrate(alice.address);262263    console.log(264      '[Statemine -> Quartz] transaction fees on Quartz: %s USDT',265      helper.util.bigIntToDecimals(TRANSFER_AMOUNT - free, USDT_ASSET_METADATA_DECIMALS),266    );267    console.log(268      '[Statemine -> Quartz] transaction fees on Quartz: %s QTZ',269      helper.util.bigIntToDecimals(balanceQuartzAfter - balanceQuartzBefore),270    );271    // commission has not paid in USDT token272    expect(free).to.be.equal(TRANSFER_AMOUNT);273    // ... and parachain native token274    expect(balanceQuartzAfter == balanceQuartzBefore).to.be.true;275  });276277  itSub('Should connect and send USDT from Quartz to Statemine back', async ({helper}) => {278    const destination = {279      V1: {280        parents: 1,281        interior: {X2: [282          {283            Parachain: STATEMINE_CHAIN,284          },285          {286            AccountId32: {287              network: 'Any',288              id: alice.addressRaw,289            },290          },291        ]},292      },293    };294295    const relayFee = 400_000_000_000_000n;296    const currencies: [any, bigint][] = [297      [298        {299          ForeignAssetId: 0,300        },301        TRANSFER_AMOUNT,302      ],303      [304        {305          NativeAssetId: 'Parent',306        },307        relayFee,308      ],309    ];310311    const feeItem = 1;312313    await helper.xTokens.transferMulticurrencies(alice, currencies, feeItem, destination, 'Unlimited');314315    // the commission has been paid in parachain native token316    balanceQuartzFinal = await helper.balance.getSubstrate(alice.address);317    console.log('[Quartz -> Statemine] transaction fees on Quartz: %s QTZ', helper.util.bigIntToDecimals(balanceQuartzFinal - balanceQuartzAfter));318    expect(balanceQuartzAfter > balanceQuartzFinal).to.be.true;319320    await usingStateminePlaygrounds(statemineUrl, async (helper) => {321      await helper.wait.newBlocks(3);322323      // The USDT token never paid fees. Its amount not changed from begin value.324      // Also check that xcm transfer has been succeeded325      expect((await helper.assets.account(USDT_ASSET_ID, alice.address))! == USDT_ASSET_AMOUNT).to.be.true;326    });327  });328329  itSub('Should connect and send Relay token to Quartz', async ({helper}) => {330    balanceBobBefore = await helper.balance.getSubstrate(bob.address);331    balanceBobRelayTokenBefore = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'});332333    await usingRelayPlaygrounds(relayUrl, async (helper) => {334      const destination = {335        V1: {336          parents: 0,337          interior: {X1: {338            Parachain: QUARTZ_CHAIN,339          },340          },341        }};342343      const beneficiary = {344        V1: {345          parents: 0,346          interior: {X1: {347            AccountId32: {348              network: 'Any',349              id: bob.addressRaw,350            },351          }},352        },353      };354355      const assets = {356        V1: [357          {358            id: {359              Concrete: {360                parents: 0,361                interior: 'Here',362              },363            },364            fun: {365              Fungible: TRANSFER_AMOUNT_RELAY,366            },367          },368        ],369      };370371      const feeAssetItem = 0;372373      await helper.xcm.limitedReserveTransferAssets(bob, destination, beneficiary, assets, feeAssetItem, 'Unlimited');374    });375376    await helper.wait.newBlocks(3);377378    balanceBobAfter = await helper.balance.getSubstrate(bob.address);379    balanceBobRelayTokenAfter = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'});380381    const wndFeeOnQuartz = balanceBobRelayTokenAfter - TRANSFER_AMOUNT_RELAY - balanceBobRelayTokenBefore;382    const wndDiffOnQuartz = balanceBobRelayTokenAfter - balanceBobRelayTokenBefore;383    console.log(384      '[Relay (Westend) -> Quartz] transaction fees: %s QTZ',385      helper.util.bigIntToDecimals(balanceBobAfter - balanceBobBefore),386    );387    console.log(388      '[Relay (Westend) -> Quartz] transaction fees: %s WND',389      helper.util.bigIntToDecimals(wndFeeOnQuartz, STATEMINE_DECIMALS),390    );391    console.log('[Relay (Westend) -> Quartz] actually delivered: %s WND', wndDiffOnQuartz);392    expect(wndFeeOnQuartz == 0n, 'No incoming WND fees should be taken').to.be.true;393    expect(balanceBobBefore == balanceBobAfter, 'No incoming QTZ fees should be taken').to.be.true;394  });395396  itSub('Should connect and send Relay token back', async ({helper}) => {397    let relayTokenBalanceBefore: bigint;398    let relayTokenBalanceAfter: bigint;399    await usingRelayPlaygrounds(relayUrl, async (helper) => {400      relayTokenBalanceBefore = await helper.balance.getSubstrate(bob.address);401    });402403    const destination = {404      V1: {405        parents: 1,406        interior: {407          X1:{408            AccountId32: {409              network: 'Any',410              id: bob.addressRaw,411            },412          },413        },414      },415    };416417    const currencies: any = [418      [419        {420          NativeAssetId: 'Parent',421        },422        TRANSFER_AMOUNT_RELAY,423      ],424    ];425426    const feeItem = 0;427428    await helper.xTokens.transferMulticurrencies(bob, currencies, feeItem, destination, 'Unlimited');429430    balanceBobFinal = await helper.balance.getSubstrate(bob.address);431    console.log('[Quartz -> Relay (Westend)] transaction fees: %s QTZ',  helper.util.bigIntToDecimals(balanceBobAfter - balanceBobFinal));432433    await usingRelayPlaygrounds(relayUrl, async (helper) => {434      await helper.wait.newBlocks(10);435      relayTokenBalanceAfter = await helper.balance.getSubstrate(bob.address);436437      const diff = relayTokenBalanceAfter - relayTokenBalanceBefore;438      console.log('[Quartz -> Relay (Westend)] actually delivered: %s WND', helper.util.bigIntToDecimals(diff, RELAY_DECIMALS));439      expect(diff > 0, 'Relay tokens was not delivered back').to.be.true;440    });441  });442});443444describeXCM('[XCM] Integration test: Exchanging tokens with Karura', () => {445  let alice: IKeyringPair;446  let randomAccount: IKeyringPair;447448  let balanceQuartzTokenInit: bigint;449  let balanceQuartzTokenMiddle: bigint;450  let balanceQuartzTokenFinal: bigint;451  let balanceKaruraTokenInit: bigint;452  let balanceKaruraTokenMiddle: bigint;453  let balanceKaruraTokenFinal: bigint;454  let balanceQuartzForeignTokenInit: bigint;455  let balanceQuartzForeignTokenMiddle: bigint;456  let balanceQuartzForeignTokenFinal: bigint;457458  before(async () => {459    await usingPlaygrounds(async (helper, privateKey) => {460      alice = await privateKey('//Alice');461      [randomAccount] = await helper.arrange.createAccounts([0n], alice);462    });463464    await usingKaruraPlaygrounds(karuraUrl, async (helper) => {465      const destination = {466        V1: {467          parents: 1,468          interior: {469            X1: {470              Parachain: QUARTZ_CHAIN,471            },472          },473        },474      };475476      const metadata = {477        name: 'QTZ',478        symbol: 'QTZ',479        decimals: 18,480        minimalBalance: 1n,481      };482483      await helper.getSudo().assetRegistry.registerForeignAsset(alice, destination, metadata);484      await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n);485      balanceKaruraTokenInit = await helper.balance.getSubstrate(randomAccount.address);486      balanceQuartzForeignTokenInit = await helper.tokens.accounts(randomAccount.address, {ForeignAsset: 0});487    });488489    await usingPlaygrounds(async (helper) => {490      await helper.balance.transferToSubstrate(alice, randomAccount.address, 10n * TRANSFER_AMOUNT);491      balanceQuartzTokenInit = await helper.balance.getSubstrate(randomAccount.address);492    });493  });494495  itSub('Should connect and send QTZ to Karura', async ({helper}) => {496    const destination = {497      V1: {498        parents: 1,499        interior: {500          X1: {501            Parachain: KARURA_CHAIN,502          },503        },504      },505    };506507    const beneficiary = {508      V1: {509        parents: 0,510        interior: {511          X1: {512            AccountId32: {513              network: 'Any',514              id: randomAccount.addressRaw,515            },516          },517        },518      },519    };520521    const assets = {522      V1: [523        {524          id: {525            Concrete: {526              parents: 0,527              interior: 'Here',528            },529          },530          fun: {531            Fungible: TRANSFER_AMOUNT,532          },533        },534      ],535    };536537    const feeAssetItem = 0;538539    await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited');540    balanceQuartzTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);541542    const qtzFees = balanceQuartzTokenInit - balanceQuartzTokenMiddle - TRANSFER_AMOUNT;543    expect(qtzFees > 0n, 'Negative fees QTZ, looks like nothing was transferred').to.be.true;544    console.log('[Quartz -> Karura] transaction fees on Quartz: %s QTZ', helper.util.bigIntToDecimals(qtzFees));545546    await usingKaruraPlaygrounds(karuraUrl, async (helper) => {547      await helper.wait.newBlocks(3);548      balanceQuartzForeignTokenMiddle = await helper.tokens.accounts(randomAccount.address, {ForeignAsset: 0});549      balanceKaruraTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);550551      const karFees = balanceKaruraTokenInit - balanceKaruraTokenMiddle;552      const qtzIncomeTransfer = balanceQuartzForeignTokenMiddle - balanceQuartzForeignTokenInit;553554      console.log(555        '[Quartz -> Karura] transaction fees on Karura: %s KAR',556        helper.util.bigIntToDecimals(karFees, KARURA_DECIMALS),557      );558      console.log('[Quartz -> Karura] income %s QTZ', helper.util.bigIntToDecimals(qtzIncomeTransfer));559      expect(karFees == 0n).to.be.true;560      expect(qtzIncomeTransfer == TRANSFER_AMOUNT).to.be.true;561    });562  });563564  itSub('Should connect to Karura and send QTZ back', async ({helper}) => {565    await usingKaruraPlaygrounds(karuraUrl, async (helper) => {566      const destination = {567        V1: {568          parents: 1,569          interior: {570            X2: [571              {Parachain: QUARTZ_CHAIN},572              {573                AccountId32: {574                  network: 'Any',575                  id: randomAccount.addressRaw,576                },577              },578            ],579          },580        },581      };582583      const id = {584        ForeignAsset: 0,585      };586587      await helper.xTokens.transfer(randomAccount, id, TRANSFER_AMOUNT, destination, 'Unlimited');588      balanceKaruraTokenFinal = await helper.balance.getSubstrate(randomAccount.address);589      balanceQuartzForeignTokenFinal = await helper.tokens.accounts(randomAccount.address, id);590591      const karFees = balanceKaruraTokenMiddle - balanceKaruraTokenFinal;592      const qtzOutcomeTransfer = balanceQuartzForeignTokenMiddle - balanceQuartzForeignTokenFinal;593594      console.log(595        '[Karura -> Quartz] transaction fees on Karura: %s KAR',596        helper.util.bigIntToDecimals(karFees, KARURA_DECIMALS),597      );598      console.log('[Karura -> Quartz] outcome %s QTZ', helper.util.bigIntToDecimals(qtzOutcomeTransfer));599600      expect(karFees > 0, 'Negative fees KAR, looks like nothing was transferred').to.be.true;601      expect(qtzOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;602    });603604    await helper.wait.newBlocks(3);605606    balanceQuartzTokenFinal = await helper.balance.getSubstrate(randomAccount.address);607    const actuallyDelivered = balanceQuartzTokenFinal - balanceQuartzTokenMiddle;608    expect(actuallyDelivered > 0).to.be.true;609610    console.log('[Karura -> Quartz] actually delivered %s QTZ', helper.util.bigIntToDecimals(actuallyDelivered));611612    const qtzFees = TRANSFER_AMOUNT - actuallyDelivered;613    console.log('[Karura -> Quartz] transaction fees on Quartz: %s QTZ', helper.util.bigIntToDecimals(qtzFees));614    expect(qtzFees == 0n).to.be.true;615  });616});617618// These tests are relevant only when the foreign asset pallet is disabled619describeXCM('[XCM] Integration test: Quartz rejects non-native tokens', () => {620  let alice: IKeyringPair;621622  before(async () => {623    await usingPlaygrounds(async (_helper, privateKey) => {624      alice = await privateKey('//Alice');625    });626  });627628  itSub('Quartz rejects KAR tokens from Karura', async ({helper}) => {629    await usingKaruraPlaygrounds(karuraUrl, async (helper) => {630      const destination = {631        V1: {632          parents: 1,633          interior: {634            X2: [635              {Parachain: QUARTZ_CHAIN},636              {637                AccountId32: {638                  network: 'Any',639                  id: alice.addressRaw,640                },641              },642            ],643          },644        },645      };646647      const id = {648        Token: 'KAR',649      };650651      await helper.xTokens.transfer(alice, id, 100_000_000_000n, destination, 'Unlimited');652    });653654    const maxWaitBlocks = 3;655656    const xcmpQueueFailEvent = await helper.wait.event(maxWaitBlocks, 'xcmpQueue', 'Fail');657658    expect(659      xcmpQueueFailEvent != null,660      '[Karura] xcmpQueue.FailEvent event is expected',661    ).to.be.true;662663    const event = xcmpQueueFailEvent!.event;664    const outcome = event.data[1] as XcmV2TraitsError;665666    expect(667      outcome.isFailedToTransactAsset,668      '[Karura] The XCM error should be `FailedToTransactAsset`',669    ).to.be.true;670  });671});672673describeXCM('[XCM] Integration test: Exchanging QTZ with Moonriver', () => {674  // Quartz constants675  let quartzDonor: IKeyringPair;676  let quartzAssetLocation;677678  let randomAccountQuartz: IKeyringPair;679  let randomAccountMoonriver: IKeyringPair;680681  // Moonriver constants682  let assetId: string;683684  const councilVotingThreshold = 2;685  const technicalCommitteeThreshold = 2;686  const votingPeriod = 3;687  const delayPeriod = 0;688689  const quartzAssetMetadata = {690    name: 'xcQuartz',691    symbol: 'xcQTZ',692    decimals: 18,693    isFrozen: false,694    minimalBalance: 1n,695  };696697  let balanceQuartzTokenInit: bigint;698  let balanceQuartzTokenMiddle: bigint;699  let balanceQuartzTokenFinal: bigint;700  let balanceForeignQtzTokenInit: bigint;701  let balanceForeignQtzTokenMiddle: bigint;702  let balanceForeignQtzTokenFinal: bigint;703  let balanceMovrTokenInit: bigint;704  let balanceMovrTokenMiddle: bigint;705  let balanceMovrTokenFinal: bigint;706707  before(async () => {708    await usingPlaygrounds(async (helper, privateKey) => {709      quartzDonor = await privateKey('//Alice');710      [randomAccountQuartz] = await helper.arrange.createAccounts([0n], quartzDonor);711712      balanceForeignQtzTokenInit = 0n;713    });714715    await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => {716      const alithAccount = helper.account.alithAccount();717      const baltatharAccount = helper.account.baltatharAccount();718      const dorothyAccount = helper.account.dorothyAccount();719720      randomAccountMoonriver = helper.account.create();721722      // >>> Sponsoring Dorothy >>>723      console.log('Sponsoring Dorothy.......');724      await helper.balance.transferToEthereum(alithAccount, dorothyAccount.address, 11_000_000_000_000_000_000n);725      console.log('Sponsoring Dorothy.......DONE');726      // <<< Sponsoring Dorothy <<<727728      quartzAssetLocation = {729        XCM: {730          parents: 1,731          interior: {X1: {Parachain: QUARTZ_CHAIN}},732        },733      };734      const existentialDeposit = 1n;735      const isSufficient = true;736      const unitsPerSecond = 1n;737      const numAssetsWeightHint = 0;738739      const encodedProposal = helper.assetManager.makeRegisterForeignAssetProposal({740        location: quartzAssetLocation,741        metadata: quartzAssetMetadata,742        existentialDeposit,743        isSufficient,744        unitsPerSecond,745        numAssetsWeightHint,746      });747      const proposalHash = blake2AsHex(encodedProposal);748749      console.log('Encoded proposal for registerForeignAsset & setAssetUnitsPerSecond is %s', encodedProposal);750      console.log('Encoded length %d', encodedProposal.length);751      console.log('Encoded proposal hash for batch utility after schedule is %s', proposalHash);752753      // >>> Note motion preimage >>>754      console.log('Note motion preimage.......');755      await helper.democracy.notePreimage(baltatharAccount, encodedProposal);756      console.log('Note motion preimage.......DONE');757      // <<< Note motion preimage <<<758759      // >>> Propose external motion through council >>>760      console.log('Propose external motion through council.......');761      const externalMotion = helper.democracy.externalProposeMajority({Legacy: proposalHash});762      const encodedMotion = externalMotion?.method.toHex() || '';763      const motionHash = blake2AsHex(encodedMotion);764      console.log('Motion hash is %s', motionHash);765766      await helper.collective.council.propose(baltatharAccount, councilVotingThreshold, externalMotion, externalMotion.encodedLength);767768      const councilProposalIdx = await helper.collective.council.proposalCount() - 1;769      await helper.collective.council.vote(dorothyAccount, motionHash, councilProposalIdx, true);770      await helper.collective.council.vote(baltatharAccount, motionHash, councilProposalIdx, true);771772      await helper.collective.council.close(773        dorothyAccount,774        motionHash,775        councilProposalIdx,776        {777          refTime: 1_000_000_000,778          proofSize: 1_000_000,779        },780        externalMotion.encodedLength,781      );782      console.log('Propose external motion through council.......DONE');783      // <<< Propose external motion through council <<<784785      // >>> Fast track proposal through technical committee >>>786      console.log('Fast track proposal through technical committee.......');787      const fastTrack = helper.democracy.fastTrack(proposalHash, votingPeriod, delayPeriod);788      const encodedFastTrack = fastTrack?.method.toHex() || '';789      const fastTrackHash = blake2AsHex(encodedFastTrack);790      console.log('FastTrack hash is %s', fastTrackHash);791792      await helper.collective.techCommittee.propose(alithAccount, technicalCommitteeThreshold, fastTrack, fastTrack.encodedLength);793794      const techProposalIdx = await helper.collective.techCommittee.proposalCount() - 1;795      await helper.collective.techCommittee.vote(baltatharAccount, fastTrackHash, techProposalIdx, true);796      await helper.collective.techCommittee.vote(alithAccount, fastTrackHash, techProposalIdx, true);797798      await helper.collective.techCommittee.close(799        baltatharAccount,800        fastTrackHash,801        techProposalIdx,802        {803          refTime: 1_000_000_000,804          proofSize: 1_000_000,805        },806        fastTrack.encodedLength,807      );808      console.log('Fast track proposal through technical committee.......DONE');809      // <<< Fast track proposal through technical committee <<<810811      // >>> Referendum voting >>>812      console.log('Referendum voting.......');813      await helper.democracy.referendumVote(dorothyAccount, 0, {814        balance: 10_000_000_000_000_000_000n,815        vote: {aye: true, conviction: 1},816      });817      console.log('Referendum voting.......DONE');818      // <<< Referendum voting <<<819820      // >>> Acquire Quartz AssetId Info on Moonriver >>>821      console.log('Acquire Quartz AssetId Info on Moonriver.......');822823      // Wait for the democracy execute824      await helper.wait.newBlocks(5);825826      assetId = (await helper.assetManager.assetTypeId(quartzAssetLocation)).toString();827828      console.log('QTZ asset ID is %s', assetId);829      console.log('Acquire Quartz AssetId Info on Moonriver.......DONE');830      // >>> Acquire Quartz AssetId Info on Moonriver >>>831832      // >>> Sponsoring random Account >>>833      console.log('Sponsoring random Account.......');834      await helper.balance.transferToEthereum(baltatharAccount, randomAccountMoonriver.address, 11_000_000_000_000_000_000n);835      console.log('Sponsoring random Account.......DONE');836      // <<< Sponsoring random Account <<<837838      balanceMovrTokenInit = await helper.balance.getEthereum(randomAccountMoonriver.address);839    });840841    await usingPlaygrounds(async (helper) => {842      await helper.balance.transferToSubstrate(quartzDonor, randomAccountQuartz.address, 10n * TRANSFER_AMOUNT);843      balanceQuartzTokenInit = await helper.balance.getSubstrate(randomAccountQuartz.address);844    });845  });846847  itSub('Should connect and send QTZ to Moonriver', async ({helper}) => {848    const currencyId = {849      NativeAssetId: 'Here',850    };851    const dest = {852      V1: {853        parents: 1,854        interior: {855          X2: [856            {Parachain: MOONRIVER_CHAIN},857            {AccountKey20: {network: 'Any', key: randomAccountMoonriver.address}},858          ],859        },860      },861    };862    const amount = TRANSFER_AMOUNT;863864    await helper.xTokens.transfer(randomAccountQuartz, currencyId, amount, dest, 'Unlimited');865866    balanceQuartzTokenMiddle = await helper.balance.getSubstrate(randomAccountQuartz.address);867    expect(balanceQuartzTokenMiddle < balanceQuartzTokenInit).to.be.true;868869    const transactionFees = balanceQuartzTokenInit - balanceQuartzTokenMiddle - TRANSFER_AMOUNT;870    console.log('[Quartz -> Moonriver] transaction fees on Quartz: %s QTZ', helper.util.bigIntToDecimals(transactionFees));871    expect(transactionFees > 0, 'Negative fees QTZ, looks like nothing was transferred').to.be.true;872873    await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => {874      await helper.wait.newBlocks(3);875876      balanceMovrTokenMiddle = await helper.balance.getEthereum(randomAccountMoonriver.address);877878      const movrFees = balanceMovrTokenInit - balanceMovrTokenMiddle;879      console.log('[Quartz -> Moonriver] transaction fees on Moonriver: %s MOVR',helper.util.bigIntToDecimals(movrFees));880      expect(movrFees == 0n).to.be.true;881882      balanceForeignQtzTokenMiddle = (await helper.assets.account(assetId, randomAccountMoonriver.address))!; // BigInt(qtzRandomAccountAsset['balance']);883      const qtzIncomeTransfer = balanceForeignQtzTokenMiddle - balanceForeignQtzTokenInit;884      console.log('[Quartz -> Moonriver] income %s QTZ', helper.util.bigIntToDecimals(qtzIncomeTransfer));885      expect(qtzIncomeTransfer == TRANSFER_AMOUNT).to.be.true;886    });887  });888889  itSub('Should connect to Moonriver and send QTZ back', async ({helper}) => {890    await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => {891      const asset = {892        V1: {893          id: {894            Concrete: {895              parents: 1,896              interior: {897                X1: {Parachain: QUARTZ_CHAIN},898              },899            },900          },901          fun: {902            Fungible: TRANSFER_AMOUNT,903          },904        },905      };906      const destination = {907        V1: {908          parents: 1,909          interior: {910            X2: [911              {Parachain: QUARTZ_CHAIN},912              {AccountId32: {network: 'Any', id: randomAccountQuartz.addressRaw}},913            ],914          },915        },916      };917918      await helper.xTokens.transferMultiasset(randomAccountMoonriver, asset, destination, 'Unlimited');919920      balanceMovrTokenFinal = await helper.balance.getEthereum(randomAccountMoonriver.address);921922      const movrFees = balanceMovrTokenMiddle - balanceMovrTokenFinal;923      console.log('[Moonriver -> Quartz] transaction fees on Moonriver: %s MOVR', helper.util.bigIntToDecimals(movrFees));924      expect(movrFees > 0, 'Negative fees MOVR, looks like nothing was transferred').to.be.true;925926      const qtzRandomAccountAsset = await helper.assets.account(assetId, randomAccountMoonriver.address);927928      expect(qtzRandomAccountAsset).to.be.null;929930      balanceForeignQtzTokenFinal = 0n;931932      const qtzOutcomeTransfer = balanceForeignQtzTokenMiddle - balanceForeignQtzTokenFinal;933      console.log('[Quartz -> Moonriver] outcome %s QTZ', helper.util.bigIntToDecimals(qtzOutcomeTransfer));934      expect(qtzOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;935    });936937    await helper.wait.newBlocks(3);938939    balanceQuartzTokenFinal = await helper.balance.getSubstrate(randomAccountQuartz.address);940    const actuallyDelivered = balanceQuartzTokenFinal - balanceQuartzTokenMiddle;941    expect(actuallyDelivered > 0).to.be.true;942943    console.log('[Moonriver -> Quartz] actually delivered %s QTZ', helper.util.bigIntToDecimals(actuallyDelivered));944945    const qtzFees = TRANSFER_AMOUNT - actuallyDelivered;946    console.log('[Moonriver -> Quartz] transaction fees on Quartz: %s QTZ', helper.util.bigIntToDecimals(qtzFees));947    expect(qtzFees == 0n).to.be.true;948  });949});