git.delta.rocks / unique-network / refs/commits / 9532bdeed19c

difftreelog

source

tests/src/xcm/xcmQuartz.test.ts30.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 {XcmV2TraitsOutcome, 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 STATEMINE_DECIMALS = 12;36const KARURA_DECIMALS = 12;3738const TRANSFER_AMOUNT = 2000000000000000000000000n;3940const FUNDING_AMOUNT = 3_500_000_0000_000_000n; 4142const TRANSFER_AMOUNT_RELAY = 50_000_000_000_000_000n;4344const USDT_ASSET_ID = 100;45const USDT_ASSET_METADATA_DECIMALS = 18;46const USDT_ASSET_METADATA_NAME = 'USDT';47const USDT_ASSET_METADATA_DESCRIPTION = 'USDT';48const USDT_ASSET_METADATA_MINIMAL_BALANCE = 1n;49const USDT_ASSET_AMOUNT = 10_000_000_000_000_000_000_000_000n;5051describeXCM('[XCM] Integration test: Exchanging USDT with Statemine', () => {52  let alice: IKeyringPair;53  let bob: IKeyringPair;54  55  let balanceStmnBefore: bigint;56  let balanceStmnAfter: bigint;5758  let balanceQuartzBefore: bigint;59  let balanceQuartzAfter: bigint;60  let balanceQuartzFinal: bigint;6162  let balanceBobBefore: bigint;63  let balanceBobAfter: bigint;64  let balanceBobFinal: bigint;6566  let balanceBobRelayTokenBefore: bigint;67  let balanceBobRelayTokenAfter: bigint;686970  before(async () => {71    await usingPlaygrounds(async (_helper, privateKey) => {72      alice = await privateKey('//Alice');73      bob = await privateKey('//Bob'); // sovereign account on Statemine(t) funds donor74    });7576    await usingRelayPlaygrounds(relayUrl, async (helper) => {77      // Fund accounts on Statemine(t)78      await helper.xcm.teleportNativeAsset(alice, STATEMINE_CHAIN, alice.addressRaw, FUNDING_AMOUNT);79      await helper.xcm.teleportNativeAsset(alice, STATEMINE_CHAIN, bob.addressRaw, FUNDING_AMOUNT);80    });8182    await usingStateminePlaygrounds(statemineUrl, async (helper) => {83      const sovereignFundingAmount = 3_500_000_000n; 8485      await helper.assets.create(86        alice,87        USDT_ASSET_ID,88        alice.address,89        USDT_ASSET_METADATA_MINIMAL_BALANCE,90      );91      await helper.assets.setMetadata(92        alice,93        USDT_ASSET_ID,94        USDT_ASSET_METADATA_NAME,95        USDT_ASSET_METADATA_DESCRIPTION,96        USDT_ASSET_METADATA_DECIMALS,97      );98      await helper.assets.mint(99        alice,100        USDT_ASSET_ID,101        alice.address,102        USDT_ASSET_AMOUNT,103      );104105      // funding parachain sovereing account on Statemine(t).106      // The sovereign account should be created before any action107      // (the assets pallet on Statemine(t) check if the sovereign account exists)108      const parachainSovereingAccount = helper.address.paraSiblingSovereignAccount(QUARTZ_CHAIN);109      await helper.balance.transferToSubstrate(bob, parachainSovereingAccount, sovereignFundingAmount);110    });111112113    await usingPlaygrounds(async (helper) => {114      const location = {115        V1: {116          parents: 1,117          interior: {X3: [118            {119              Parachain: STATEMINE_CHAIN,120            },121            {122              PalletInstance: STATEMINE_PALLET_INSTANCE,123            },124            {125              GeneralIndex: USDT_ASSET_ID,126            },127          ]},128        },129      };130131      const metadata =132      {133        name: USDT_ASSET_ID,134        symbol: USDT_ASSET_METADATA_NAME,135        decimals: USDT_ASSET_METADATA_DECIMALS,136        minimalBalance: USDT_ASSET_METADATA_MINIMAL_BALANCE,137      };138      await helper.getSudo().foreignAssets.register(alice, alice.address, location, metadata);139      balanceQuartzBefore = await helper.balance.getSubstrate(alice.address);140    });141142143    // Providing the relay currency to the quartz sender account144    // (fee for USDT XCM are paid in relay tokens)145    await usingRelayPlaygrounds(relayUrl, async (helper) => {146      const destination = {147        V1: {148          parents: 0,149          interior: {X1: {150            Parachain: QUARTZ_CHAIN,151          },152          },153        }};154155      const beneficiary = {156        V1: {157          parents: 0,158          interior: {X1: {159            AccountId32: {160              network: 'Any',161              id: alice.addressRaw,162            },163          }},164        },165      };166167      const assets = {168        V1: [169          {170            id: {171              Concrete: {172                parents: 0,173                interior: 'Here',174              },175            },176            fun: {177              Fungible: TRANSFER_AMOUNT_RELAY,178            },179          },180        ],181      };182183      const feeAssetItem = 0;184185      await helper.xcm.limitedReserveTransferAssets(alice, destination, beneficiary, assets, feeAssetItem, {Unlimited: null});186    });187  188  });189190  itSub('Should connect and send USDT from Statemine to Quartz', async ({helper}) => {191    await usingStateminePlaygrounds(statemineUrl, async (helper) => {192      const dest = {193        V1: {194          parents: 1,195          interior: {X1: {196            Parachain: QUARTZ_CHAIN,197          },198          },199        }};200201      const beneficiary = {202        V1: {203          parents: 0,204          interior: {X1: {205            AccountId32: {206              network: 'Any',207              id: alice.addressRaw,208            },209          }},210        },211      };212213      const assets = {214        V1: [215          {216            id: {217              Concrete: {218                parents: 0,219                interior: {220                  X2: [221                    {222                      PalletInstance: STATEMINE_PALLET_INSTANCE,223                    },224                    {225                      GeneralIndex: USDT_ASSET_ID,226                    }, 227                  ]},228              },229            },230            fun: {231              Fungible: TRANSFER_AMOUNT,232            },233          },234        ],235      };236237      const feeAssetItem = 0;238239      balanceStmnBefore = await helper.balance.getSubstrate(alice.address);240      await helper.xcm.limitedReserveTransferAssets(alice, dest, beneficiary, assets, feeAssetItem, {Unlimited: null});241242      balanceStmnAfter = await helper.balance.getSubstrate(alice.address);243244      // common good parachain take commission in it native token245      console.log(246        '[Quartz -> Statemine] transaction fees on Statemine: %s WND',247        helper.util.bigIntToDecimals(balanceStmnBefore - balanceStmnAfter, STATEMINE_DECIMALS),248      );249      expect(balanceStmnBefore > balanceStmnAfter).to.be.true;250251    });252253254    // ensure that asset has been delivered255    await helper.wait.newBlocks(3);256257    // expext collection id will be with id 1258    const free = await helper.ft.getBalance(1, {Substrate: alice.address});259260    balanceQuartzAfter = await helper.balance.getSubstrate(alice.address);261262    console.log(263      '[Quartz -> Statemine] transaction fees on Quartz: %s USDT',264      helper.util.bigIntToDecimals(TRANSFER_AMOUNT - free, USDT_ASSET_METADATA_DECIMALS),265    );266    console.log(267      '[Quartz -> Statemine] transaction fees on Quartz: %s QTZ',268      helper.util.bigIntToDecimals(balanceQuartzAfter - balanceQuartzBefore),269    );    270    // commission has not paid in USDT token271    expect(free).to.be.equal(TRANSFER_AMOUNT);272    // ... and parachain native token273    expect(balanceQuartzAfter == balanceQuartzBefore).to.be.true;274  });275276  itSub('Should connect and send USDT from Quartz to Statemine back', async ({helper}) => {277    const destination = {278      V1: {279        parents: 1,280        interior: {X2: [281          {282            Parachain: STATEMINE_CHAIN,283          },284          {285            AccountId32: {286              network: 'Any',287              id: alice.addressRaw,288            },289          },290        ]},291      },292    };293294    const relayFee = 400_000_000_000_000n;295    const currencies: [any, bigint][] = [296      [297        {298          ForeignAssetId: 0,299        },300        TRANSFER_AMOUNT,301      ], 302      [303        {304          NativeAssetId: 'Parent',305        },306        relayFee,307      ],308    ];309310    const feeItem = 1;311312    await helper.xTokens.transferMulticurrencies(alice, currencies, feeItem, destination, {Unlimited: null});313    314    // the commission has been paid in parachain native token315    balanceQuartzFinal = await helper.balance.getSubstrate(alice.address);316    expect(balanceQuartzAfter > balanceQuartzFinal).to.be.true;317318    await usingStateminePlaygrounds(statemineUrl, async (helper) => {319      await helper.wait.newBlocks(3);320      321      // The USDT token never paid fees. Its amount not changed from begin value.322      // Also check that xcm transfer has been succeeded 323      expect((await helper.assets.account(USDT_ASSET_ID, alice.address))! == USDT_ASSET_AMOUNT).to.be.true;324    });325  });326327  itSub('Should connect and send Relay token to Quartz', async ({helper}) => {328    balanceBobBefore = await helper.balance.getSubstrate(bob.address);329    balanceBobRelayTokenBefore = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'});330331    await usingRelayPlaygrounds(relayUrl, async (helper) => {332      const destination = {333        V1: {334          parents: 0,335          interior: {X1: {336            Parachain: QUARTZ_CHAIN,337          },338          },339        }};340341      const beneficiary = {342        V1: {343          parents: 0,344          interior: {X1: {345            AccountId32: {346              network: 'Any',347              id: bob.addressRaw,348            },349          }},350        },351      };352353      const assets = {354        V1: [355          {356            id: {357              Concrete: {358                parents: 0,359                interior: 'Here',360              },361            },362            fun: {363              Fungible: TRANSFER_AMOUNT_RELAY,364            },365          },366        ],367      };368369      const feeAssetItem = 0;370371      await helper.xcm.limitedReserveTransferAssets(bob, destination, beneficiary, assets, feeAssetItem, {Unlimited: null});372    });373  374    await helper.wait.newBlocks(3);375376    balanceBobAfter = await helper.balance.getSubstrate(bob.address);  377    balanceBobRelayTokenAfter = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'});378379    const wndFeeOnQuartz = balanceBobRelayTokenAfter - TRANSFER_AMOUNT_RELAY - balanceBobRelayTokenBefore;380    console.log(381      '[Relay (Westend) -> Quartz] transaction fees: %s QTZ',382      helper.util.bigIntToDecimals(balanceBobAfter - balanceBobBefore),383    );384    console.log(385      '[Relay (Westend) -> Quartz] transaction fees: %s WND',386      helper.util.bigIntToDecimals(wndFeeOnQuartz, STATEMINE_DECIMALS),387    );388    expect(wndFeeOnQuartz == 0n, 'No incoming WND fees should be taken').to.be.true;389    expect(balanceBobBefore == balanceBobAfter, 'No incoming QTZ fees should be taken').to.be.true;390    expect(balanceBobRelayTokenBefore < balanceBobRelayTokenAfter).to.be.true;391  });392393  itSub('Should connect and send Relay token back', async ({helper}) => {394    const destination = {395      V1: {396        parents: 1,397        interior: {X2: [398          {399            Parachain: STATEMINE_CHAIN,400          },401          {402            AccountId32: {403              network: 'Any',404              id: bob.addressRaw,405            },406          },407        ]},408      },409    };410411    const currencies: any = [412      [413        {414          NativeAssetId: 'Parent',415        },416        TRANSFER_AMOUNT_RELAY,417      ],418    ];419420    const feeItem = 0;421422    await helper.xTokens.transferMulticurrencies(bob, currencies, feeItem, destination, {Unlimited: null});423424    balanceBobFinal = await helper.balance.getSubstrate(bob.address);425    console.log('[Relay (Westend) to Quartz] transaction fees: %s QTZ', balanceBobAfter - balanceBobFinal);426  });427});428429describeXCM('[XCM] Integration test: Exchanging tokens with Karura', () => {430  let alice: IKeyringPair;431  let randomAccount: IKeyringPair;432433  let balanceQuartzTokenInit: bigint;434  let balanceQuartzTokenMiddle: bigint;435  let balanceQuartzTokenFinal: bigint;436  let balanceKaruraTokenInit: bigint;437  let balanceKaruraTokenMiddle: bigint;438  let balanceKaruraTokenFinal: bigint;439  let balanceQuartzForeignTokenInit: bigint;440  let balanceQuartzForeignTokenMiddle: bigint;441  let balanceQuartzForeignTokenFinal: bigint;442443  before(async () => {444    await usingPlaygrounds(async (helper, privateKey) => {445      alice = await privateKey('//Alice');446      [randomAccount] = await helper.arrange.createAccounts([0n], alice);447    });448449    await usingKaruraPlaygrounds(karuraUrl, async (helper) => {450      const destination = {451        V0: {452          X2: [453            'Parent',454            {455              Parachain: QUARTZ_CHAIN,456            },457          ],458        },459      };460461      const metadata = {462        name: 'QTZ',463        symbol: 'QTZ',464        decimals: 18,465        minimalBalance: 1n,466      };467468      await helper.getSudo().assetRegistry.registerForeignAsset(alice, destination, metadata);469      await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n);470      balanceKaruraTokenInit = await helper.balance.getSubstrate(randomAccount.address);471      balanceQuartzForeignTokenInit = await helper.tokens.accounts(randomAccount.address, {ForeignAsset: 0});472    });473474    await usingPlaygrounds(async (helper) => {475      await helper.balance.transferToSubstrate(alice, randomAccount.address, 10n * TRANSFER_AMOUNT);476      balanceQuartzTokenInit = await helper.balance.getSubstrate(randomAccount.address);477    });478  });479480  itSub('Should connect and send QTZ to Karura', async ({helper}) => {481    const destination = {482      V0: {483        X2: [484          'Parent',485          {486            Parachain: KARURA_CHAIN,487          },488        ],489      },490    };491492    const beneficiary = {493      V0: {494        X1: {495          AccountId32: {496            network: 'Any',497            id: randomAccount.addressRaw,498          },499        },500      },501    };502503    const assets = {504      V1: [505        {506          id: {507            Concrete: {508              parents: 0,509              interior: 'Here',510            },511          },512          fun: {513            Fungible: TRANSFER_AMOUNT,514          },515        },516      ],517    };518519    const feeAssetItem = 0;520521    await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, {Unlimited: null});522    balanceQuartzTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);523524    const qtzFees = balanceQuartzTokenInit - balanceQuartzTokenMiddle - TRANSFER_AMOUNT;525    expect(qtzFees > 0n, 'Negative fees QTZ, looks like nothing was transferred').to.be.true;526    console.log('[Quartz -> Karura] transaction fees on Quartz: %s QTZ', helper.util.bigIntToDecimals(qtzFees));527528    await usingKaruraPlaygrounds(karuraUrl, async (helper) => {529      await helper.wait.newBlocks(3);530      balanceQuartzForeignTokenMiddle = await helper.tokens.accounts(randomAccount.address, {ForeignAsset: 0});531      balanceKaruraTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);532533      const karFees = balanceKaruraTokenInit - balanceKaruraTokenMiddle;534      const qtzIncomeTransfer = balanceQuartzForeignTokenMiddle - balanceQuartzForeignTokenInit;535536      console.log(537        '[Quartz -> Karura] transaction fees on Karura: %s KAR',538        helper.util.bigIntToDecimals(karFees, KARURA_DECIMALS),539      );540      console.log('[Quartz -> Karura] income %s QTZ', helper.util.bigIntToDecimals(qtzIncomeTransfer));541      expect(karFees == 0n).to.be.true;542      expect(qtzIncomeTransfer == TRANSFER_AMOUNT).to.be.true;543    });544  });545546  itSub('Should connect to Karura and send QTZ back', async ({helper}) => {547    await usingKaruraPlaygrounds(karuraUrl, async (helper) => {548      const destination = {549        V1: {550          parents: 1,551          interior: {552            X2: [553              {Parachain: QUARTZ_CHAIN},554              {555                AccountId32: {556                  network: 'Any',557                  id: randomAccount.addressRaw,558                },559              },560            ],561          },562        },563      };564565      const id = {566        ForeignAsset: 0,567      };568569      await helper.xTokens.transfer(randomAccount, id, TRANSFER_AMOUNT, destination, {Unlimited: null});570      balanceKaruraTokenFinal = await helper.balance.getSubstrate(randomAccount.address);571      balanceQuartzForeignTokenFinal = await helper.tokens.accounts(randomAccount.address, id);572573      const karFees = balanceKaruraTokenMiddle - balanceKaruraTokenFinal;574      const qtzOutcomeTransfer = balanceQuartzForeignTokenMiddle - balanceQuartzForeignTokenFinal;575576      console.log(577        '[Karura -> Quartz] transaction fees on Karura: %s KAR',578        helper.util.bigIntToDecimals(karFees, KARURA_DECIMALS),579      );580      console.log('[Karura -> Quartz] outcome %s QTZ', helper.util.bigIntToDecimals(qtzOutcomeTransfer));581582      expect(karFees > 0, 'Negative fees KAR, looks like nothing was transferred').to.be.true;583      expect(qtzOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;584    });585586    await helper.wait.newBlocks(3);587588    balanceQuartzTokenFinal = await helper.balance.getSubstrate(randomAccount.address);589    const actuallyDelivered = balanceQuartzTokenFinal - balanceQuartzTokenMiddle;590    expect(actuallyDelivered > 0).to.be.true;591592    console.log('[Karura -> Quartz] actually delivered %s QTZ', helper.util.bigIntToDecimals(actuallyDelivered));593594    const qtzFees = TRANSFER_AMOUNT - actuallyDelivered;595    console.log('[Karura -> Quartz] transaction fees on Quartz: %s QTZ', helper.util.bigIntToDecimals(qtzFees));596    expect(qtzFees == 0n).to.be.true;597  });598});599600// These tests are relevant only when the foreign asset pallet is disabled601describeXCM('[XCM] Integration test: Quartz rejects non-native tokens', () => {602  let alice: IKeyringPair;603604  before(async () => {605    await usingPlaygrounds(async (_helper, privateKey) => {606      alice = await privateKey('//Alice');607    });608  });609610  itSub('Quartz rejects KAR tokens from Karura', async ({helper}) => {611    await usingKaruraPlaygrounds(karuraUrl, async (helper) => {612      const destination = {613        V1: {614          parents: 1,615          interior: {616            X2: [617              {Parachain: QUARTZ_CHAIN},618              {619                AccountId32: {620                  network: 'Any',621                  id: alice.addressRaw,622                },623              },624            ],625          },626        },627      };628629      const id = {630        Token: 'KAR',631      };632633      await helper.xTokens.transfer(alice, id, 100_000_000_000n, destination, {Unlimited: null});634    });635636    const maxWaitBlocks = 3;637638    const xcmpQueueFailEvent = await helper.wait.event(maxWaitBlocks, 'xcmpQueue', 'Fail');639640    expect(641      xcmpQueueFailEvent != null,642      '[Karura] xcmpQueue.FailEvent event is expected',643    ).to.be.true;644645    const event = xcmpQueueFailEvent!.event;646    const outcome = event.data[1] as XcmV2TraitsError;647648    console.log('>>> Karura reject outcome: ', outcome.toHuman());649    // expect(650    //   outcome.isUntrustedReserveLocation,651    //   '[Karura] The XCM error should be `UntrustedReserveLocation`',652    // ).to.be.true;653  });654});655656describeXCM('[XCM] Integration test: Exchanging QTZ with Moonriver', () => {657  // Quartz constants658  let quartzDonor: IKeyringPair;659  let quartzAssetLocation;660661  let randomAccountQuartz: IKeyringPair;662  let randomAccountMoonriver: IKeyringPair;663664  // Moonriver constants665  let assetId: string;666667  const councilVotingThreshold = 2;668  const technicalCommitteeThreshold = 2;669  const votingPeriod = 3;670  const delayPeriod = 0;671672  const quartzAssetMetadata = {673    name: 'xcQuartz',674    symbol: 'xcQTZ',675    decimals: 18,676    isFrozen: false,677    minimalBalance: 1n,678  };679680  let balanceQuartzTokenInit: bigint;681  let balanceQuartzTokenMiddle: bigint;682  let balanceQuartzTokenFinal: bigint;683  let balanceForeignQtzTokenInit: bigint;684  let balanceForeignQtzTokenMiddle: bigint;685  let balanceForeignQtzTokenFinal: bigint;686  let balanceMovrTokenInit: bigint;687  let balanceMovrTokenMiddle: bigint;688  let balanceMovrTokenFinal: bigint;689690  before(async () => {691    await usingPlaygrounds(async (helper, privateKey) => {692      quartzDonor = await privateKey('//Alice');693      [randomAccountQuartz] = await helper.arrange.createAccounts([0n], quartzDonor);694695      balanceForeignQtzTokenInit = 0n;696    });697698    await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => {699      const alithAccount = helper.account.alithAccount();700      const baltatharAccount = helper.account.baltatharAccount();701      const dorothyAccount = helper.account.dorothyAccount();702703      randomAccountMoonriver = helper.account.create();704705      // >>> Sponsoring Dorothy >>>706      console.log('Sponsoring Dorothy.......');707      await helper.balance.transferToEthereum(alithAccount, dorothyAccount.address, 11_000_000_000_000_000_000n);708      console.log('Sponsoring Dorothy.......DONE');709      // <<< Sponsoring Dorothy <<<710711      quartzAssetLocation = {712        XCM: {713          parents: 1,714          interior: {X1: {Parachain: QUARTZ_CHAIN}},715        },716      };717      const existentialDeposit = 1n;718      const isSufficient = true;719      const unitsPerSecond = 1n;720      const numAssetsWeightHint = 0;721722      const encodedProposal = helper.assetManager.makeRegisterForeignAssetProposal({723        location: quartzAssetLocation,724        metadata: quartzAssetMetadata,725        existentialDeposit,726        isSufficient,727        unitsPerSecond,728        numAssetsWeightHint,729      });730      const proposalHash = blake2AsHex(encodedProposal);731732      console.log('Encoded proposal for registerForeignAsset & setAssetUnitsPerSecond is %s', encodedProposal);733      console.log('Encoded length %d', encodedProposal.length);734      console.log('Encoded proposal hash for batch utility after schedule is %s', proposalHash);735736      // >>> Note motion preimage >>>737      console.log('Note motion preimage.......');738      await helper.democracy.notePreimage(baltatharAccount, encodedProposal);739      console.log('Note motion preimage.......DONE');740      // <<< Note motion preimage <<<741742      // >>> Propose external motion through council >>>743      console.log('Propose external motion through council.......');744      const externalMotion = helper.democracy.externalProposeMajority(proposalHash);745      const encodedMotion = externalMotion?.method.toHex() || '';746      const motionHash = blake2AsHex(encodedMotion);747      console.log('Motion hash is %s', motionHash);748749      await helper.collective.council.propose(baltatharAccount, councilVotingThreshold, externalMotion, externalMotion.encodedLength);750751      const councilProposalIdx = await helper.collective.council.proposalCount() - 1;752      await helper.collective.council.vote(dorothyAccount, motionHash, councilProposalIdx, true);753      await helper.collective.council.vote(baltatharAccount, motionHash, councilProposalIdx, true);754755      await helper.collective.council.close(dorothyAccount, motionHash, councilProposalIdx, 1_000_000_000, externalMotion.encodedLength);756      console.log('Propose external motion through council.......DONE');757      // <<< Propose external motion through council <<<758759      // >>> Fast track proposal through technical committee >>>760      console.log('Fast track proposal through technical committee.......');761      const fastTrack = helper.democracy.fastTrack(proposalHash, votingPeriod, delayPeriod);762      const encodedFastTrack = fastTrack?.method.toHex() || '';763      const fastTrackHash = blake2AsHex(encodedFastTrack);764      console.log('FastTrack hash is %s', fastTrackHash);765766      await helper.collective.techCommittee.propose(alithAccount, technicalCommitteeThreshold, fastTrack, fastTrack.encodedLength);767768      const techProposalIdx = await helper.collective.techCommittee.proposalCount() - 1;769      await helper.collective.techCommittee.vote(baltatharAccount, fastTrackHash, techProposalIdx, true);770      await helper.collective.techCommittee.vote(alithAccount, fastTrackHash, techProposalIdx, true);771772      await helper.collective.techCommittee.close(baltatharAccount, fastTrackHash, techProposalIdx, 1_000_000_000, fastTrack.encodedLength);773      console.log('Fast track proposal through technical committee.......DONE');774      // <<< Fast track proposal through technical committee <<<775776      // >>> Referendum voting >>>777      console.log('Referendum voting.......');778      await helper.democracy.referendumVote(dorothyAccount, 0, {779        balance: 10_000_000_000_000_000_000n,780        vote: {aye: true, conviction: 1},781      });782      console.log('Referendum voting.......DONE');783      // <<< Referendum voting <<<784785      // >>> Acquire Quartz AssetId Info on Moonriver >>>786      console.log('Acquire Quartz AssetId Info on Moonriver.......');787788      // Wait for the democracy execute789      await helper.wait.newBlocks(5);790791      assetId = (await helper.assetManager.assetTypeId(quartzAssetLocation)).toString();792793      console.log('QTZ asset ID is %s', assetId);794      console.log('Acquire Quartz AssetId Info on Moonriver.......DONE');795      // >>> Acquire Quartz AssetId Info on Moonriver >>>796797      // >>> Sponsoring random Account >>>798      console.log('Sponsoring random Account.......');799      await helper.balance.transferToEthereum(baltatharAccount, randomAccountMoonriver.address, 11_000_000_000_000_000_000n);800      console.log('Sponsoring random Account.......DONE');801      // <<< Sponsoring random Account <<<802803      balanceMovrTokenInit = await helper.balance.getEthereum(randomAccountMoonriver.address);804    });805806    await usingPlaygrounds(async (helper) => {807      await helper.balance.transferToSubstrate(quartzDonor, randomAccountQuartz.address, 10n * TRANSFER_AMOUNT);808      balanceQuartzTokenInit = await helper.balance.getSubstrate(randomAccountQuartz.address);809    });810  });811812  itSub('Should connect and send QTZ to Moonriver', async ({helper}) => {813    const currencyId = {814      NativeAssetId: 'Here',815    };816    const dest = {817      V1: {818        parents: 1,819        interior: {820          X2: [821            {Parachain: MOONRIVER_CHAIN},822            {AccountKey20: {network: 'Any', key: randomAccountMoonriver.address}},823          ],824        },825      },826    };827    const amount = TRANSFER_AMOUNT;828829    await helper.xTokens.transfer(randomAccountQuartz, currencyId, amount, dest, {Unlimited: null});830831    balanceQuartzTokenMiddle = await helper.balance.getSubstrate(randomAccountQuartz.address);832    expect(balanceQuartzTokenMiddle < balanceQuartzTokenInit).to.be.true;833834    const transactionFees = balanceQuartzTokenInit - balanceQuartzTokenMiddle - TRANSFER_AMOUNT;835    console.log('[Quartz -> Moonriver] transaction fees on Quartz: %s QTZ', helper.util.bigIntToDecimals(transactionFees));836    expect(transactionFees > 0, 'Negative fees QTZ, looks like nothing was transferred').to.be.true;837838    await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => {839      await helper.wait.newBlocks(3);840841      balanceMovrTokenMiddle = await helper.balance.getEthereum(randomAccountMoonriver.address);842843      const movrFees = balanceMovrTokenInit - balanceMovrTokenMiddle;844      console.log('[Quartz -> Moonriver] transaction fees on Moonriver: %s MOVR',helper.util.bigIntToDecimals(movrFees));845      expect(movrFees == 0n).to.be.true;846847      balanceForeignQtzTokenMiddle = (await helper.assets.account(assetId, randomAccountMoonriver.address))!; // BigInt(qtzRandomAccountAsset['balance']);848      const qtzIncomeTransfer = balanceForeignQtzTokenMiddle - balanceForeignQtzTokenInit;849      console.log('[Quartz -> Moonriver] income %s QTZ', helper.util.bigIntToDecimals(qtzIncomeTransfer));850      expect(qtzIncomeTransfer == TRANSFER_AMOUNT).to.be.true;851    });852  });853854  itSub('Should connect to Moonriver and send QTZ back', async ({helper}) => {855    await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => {856      const asset = {857        V1: {858          id: {859            Concrete: {860              parents: 1,861              interior: {862                X1: {Parachain: QUARTZ_CHAIN},863              },864            },865          },866          fun: {867            Fungible: TRANSFER_AMOUNT,868          },869        },870      };871      const destination = {872        V1: {873          parents: 1,874          interior: {875            X2: [876              {Parachain: QUARTZ_CHAIN},877              {AccountId32: {network: 'Any', id: randomAccountQuartz.addressRaw}},878            ],879          },880        },881      };882883      await helper.xTokens.transferMultiasset(randomAccountMoonriver, asset, destination, {Unlimited: null});884885      balanceMovrTokenFinal = await helper.balance.getEthereum(randomAccountMoonriver.address);886887      const movrFees = balanceMovrTokenMiddle - balanceMovrTokenFinal;888      console.log('[Moonriver -> Quartz] transaction fees on Moonriver: %s MOVR', helper.util.bigIntToDecimals(movrFees));889      expect(movrFees > 0, 'Negative fees MOVR, looks like nothing was transferred').to.be.true;890891      const qtzRandomAccountAsset = await helper.assets.account(assetId, randomAccountMoonriver.address);892893      expect(qtzRandomAccountAsset).to.be.null;894895      balanceForeignQtzTokenFinal = 0n;896897      const qtzOutcomeTransfer = balanceForeignQtzTokenMiddle - balanceForeignQtzTokenFinal;898      console.log('[Quartz -> Moonriver] outcome %s QTZ', helper.util.bigIntToDecimals(qtzOutcomeTransfer));899      expect(qtzOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;900    });901902    await helper.wait.newBlocks(3);903904    balanceQuartzTokenFinal = await helper.balance.getSubstrate(randomAccountQuartz.address);905    const actuallyDelivered = balanceQuartzTokenFinal - balanceQuartzTokenMiddle;906    expect(actuallyDelivered > 0).to.be.true;907908    console.log('[Moonriver -> Quartz] actually delivered %s QTZ', helper.util.bigIntToDecimals(actuallyDelivered));909910    const qtzFees = TRANSFER_AMOUNT - actuallyDelivered;911    console.log('[Moonriver -> Quartz] transaction fees on Quartz: %s QTZ', helper.util.bigIntToDecimals(qtzFees));912    expect(qtzFees == 0n).to.be.true;913  });914});