git.delta.rocks / unique-network / refs/commits / 59c98d647e2d

difftreelog

fix xcm quartz test

Daniel Shiposha2022-12-21parent: #b756528.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 {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 USDT_ASSET_ID = 100;41const USDT_ASSET_METADATA_DECIMALS = 18;42const USDT_ASSET_METADATA_NAME = 'USDT';43const USDT_ASSET_METADATA_DESCRIPTION = 'USDT';44const USDT_ASSET_METADATA_MINIMAL_BALANCE = 1n;4546describeXCM('[XCM] Integration test: Exchanging USDT with Statemine', () => {47  let alice: IKeyringPair;48  let bob: IKeyringPair;49  50  let balanceStmnBefore: bigint;51  let balanceStmnAfter: bigint;5253  let balanceQuartzBefore: bigint;54  let balanceQuartzAfter: bigint;55  let balanceQuartzFinal: bigint;5657  let balanceBobBefore: bigint;58  let balanceBobAfter: bigint;59  let balanceBobFinal: bigint;6061  let balanceBobRelayTokenBefore: bigint;62  let balanceBobRelayTokenAfter: bigint;636465  before(async () => {66    await usingPlaygrounds(async (_helper, privateKey) => {67      alice = await privateKey('//Alice');68      bob = await privateKey('//Bob'); // funds donor69    });7071    await usingStateminePlaygrounds(statemineUrl, async (helper) => {72      // 350.00 (three hundred fifty) DOT73      const fundingAmount = 3_500_000_000_000n; 7475      await helper.assets.create(76        alice,77        USDT_ASSET_ID,78        alice.address,79        USDT_ASSET_METADATA_MINIMAL_BALANCE,80      );81      await helper.assets.setMetadata(82        alice,83        USDT_ASSET_ID,84        USDT_ASSET_METADATA_NAME,85        USDT_ASSET_METADATA_DESCRIPTION,86        USDT_ASSET_METADATA_DECIMALS,87      );88      await helper.assets.mint(89        alice,90        USDT_ASSET_ID,91        alice.address,92        TRANSFER_AMOUNT,93      );9495      // funding parachain sovereing account (Parachain: 2095)96      const parachainSovereingAccount = helper.address.paraSiblingSovereignAccount(QUARTZ_CHAIN);97      await helper.balance.transferToSubstrate(bob, parachainSovereingAccount, fundingAmount);98    });99100101    await usingPlaygrounds(async (helper) => {102      const location = {103        V1: {104          parents: 1,105          interior: {X3: [106            {107              Parachain: STATEMINE_CHAIN,108            },109            {110              PalletInstance: STATEMINE_PALLET_INSTANCE,111            },112            {113              GeneralIndex: USDT_ASSET_ID,114            },115          ]},116        },117      };118119      const metadata =120      {121        name: USDT_ASSET_ID,122        symbol: USDT_ASSET_METADATA_NAME,123        decimals: USDT_ASSET_METADATA_DECIMALS,124        minimalBalance: USDT_ASSET_METADATA_MINIMAL_BALANCE,125      };126      await helper.getSudo().foreignAssets.register(alice, alice.address, location, metadata);127      balanceQuartzBefore = await helper.balance.getSubstrate(alice.address);128    });129130131    // Providing the relay currency to the unique sender account132    await usingRelayPlaygrounds(relayUrl, async (helper) => {133      const destination = {134        V1: {135          parents: 0,136          interior: {X1: {137            Parachain: QUARTZ_CHAIN,138          },139          },140        }};141142      const beneficiary = {143        V1: {144          parents: 0,145          interior: {X1: {146            AccountId32: {147              network: 'Any',148              id: alice.addressRaw,149            },150          }},151        },152      };153154      const assets = {155        V1: [156          {157            id: {158              Concrete: {159                parents: 0,160                interior: 'Here',161              },162            },163            fun: {164              Fungible: TRANSFER_AMOUNT,165            },166          },167        ],168      };169170      const feeAssetItem = 0;171      const weightLimit = 5_000_000_000;172173      await helper.xcm.limitedReserveTransferAssets(alice, destination, beneficiary, assets, feeAssetItem, weightLimit);174    });175  176  });177178  itSub('Should connect and send USDT from Statemine to Quartz', async ({helper}) => {179    await usingStateminePlaygrounds(statemineUrl, async (helper) => {180      const dest = {181        V1: {182          parents: 1,183          interior: {X1: {184            Parachain: QUARTZ_CHAIN,185          },186          },187        }};188189      const beneficiary = {190        V1: {191          parents: 0,192          interior: {X1: {193            AccountId32: {194              network: 'Any',195              id: alice.addressRaw,196            },197          }},198        },199      };200201      const assets = {202        V1: [203          {204            id: {205              Concrete: {206                parents: 0,207                interior: {208                  X2: [209                    {210                      PalletInstance: STATEMINE_PALLET_INSTANCE,211                    },212                    {213                      GeneralIndex: USDT_ASSET_ID,214                    }, 215                  ]},216              },217            },218            fun: {219              Fungible: TRANSFER_AMOUNT,220            },221          },222        ],223      };224225      const feeAssetItem = 0;226      const weightLimit = 5000000000;227228      balanceStmnBefore = await helper.balance.getSubstrate(alice.address);229      await helper.xcm.limitedReserveTransferAssets(alice, dest, beneficiary, assets, feeAssetItem, weightLimit);230231      balanceStmnAfter = await helper.balance.getSubstrate(alice.address);232233      // common good parachain take commission in it native token234      console.log(235        '[Quartz -> Statemine] transaction fees on Statemine: %s WND',236        helper.util.bigIntToDecimals(balanceStmnBefore - balanceStmnAfter, STATEMINE_DECIMALS),237      );238      expect(balanceStmnBefore > balanceStmnAfter).to.be.true;239240    });241242243    // ensure that asset has been delivered244    await helper.wait.newBlocks(3);245246    // expext collection id will be with id 1247    const free = await helper.ft.getBalance(1, {Substrate: alice.address});248249    balanceQuartzAfter = await helper.balance.getSubstrate(alice.address);250251    // commission has not paid in USDT token252    expect(free == TRANSFER_AMOUNT).to.be.true;253    console.log(254      '[Quartz -> Statemine] transaction fees on Quartz: %s USDT',255      helper.util.bigIntToDecimals(TRANSFER_AMOUNT - free),256    );257    // ... and parachain native token258    expect(balanceQuartzAfter == balanceQuartzBefore).to.be.true;259    console.log(260      '[Quartz -> Statemine] transaction fees on Quartz: %s WND',261      helper.util.bigIntToDecimals(balanceQuartzAfter - balanceQuartzBefore, STATEMINE_DECIMALS),262    );    263  });264265  itSub('Should connect and send USDT from Quartz to Statemine back', async ({helper}) => {266    const destination = {267      V1: {268        parents: 1,269        interior: {X2: [270          {271            Parachain: STATEMINE_CHAIN,272          },273          {274            AccountId32: {275              network: 'Any',276              id: alice.addressRaw,277            },278          },279        ]},280      },281    };282283    const currencies: [any, bigint][] = [284      [285        {286          ForeignAssetId: 0,287        },288        //10_000_000_000_000_000n,289        TRANSFER_AMOUNT,290      ], 291      [292        {293          NativeAssetId: 'Parent',294        },295        400_000_000_000_000n,296      ],297    ];298299    const feeItem = 1;300    const destWeight = 500000000000;301302    await helper.xTokens.transferMulticurrencies(alice, currencies, feeItem, destination, destWeight);303    304    // the commission has been paid in parachain native token305    balanceQuartzFinal = await helper.balance.getSubstrate(alice.address);306    expect(balanceQuartzAfter > balanceQuartzFinal).to.be.true;307308    await usingStateminePlaygrounds(statemineUrl, async (helper) => {309      await helper.wait.newBlocks(3);310      311      // The USDT token never paid fees. Its amount not changed from begin value.312      // Also check that xcm transfer has been succeeded 313      expect((await helper.assets.account(USDT_ASSET_ID, alice.address))! == TRANSFER_AMOUNT).to.be.true;314    });315  });316317  itSub('Should connect and send Relay token to Quartz', async ({helper}) => {318    balanceBobBefore = await helper.balance.getSubstrate(bob.address);319    balanceBobRelayTokenBefore = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'});320321    // Providing the relay currency to the unique sender account322    await usingRelayPlaygrounds(relayUrl, async (helper) => {323      const destination = {324        V1: {325          parents: 0,326          interior: {X1: {327            Parachain: QUARTZ_CHAIN,328          },329          },330        }};331332      const beneficiary = {333        V1: {334          parents: 0,335          interior: {X1: {336            AccountId32: {337              network: 'Any',338              id: bob.addressRaw,339            },340          }},341        },342      };343344      const assets = {345        V1: [346          {347            id: {348              Concrete: {349                parents: 0,350                interior: 'Here',351              },352            },353            fun: {354              Fungible: TRANSFER_AMOUNT,355            },356          },357        ],358      };359360      const feeAssetItem = 0;361      const weightLimit = 5_000_000_000;362363      await helper.xcm.limitedReserveTransferAssets(bob, destination, beneficiary, assets, feeAssetItem, weightLimit);364    });365  366    await helper.wait.newBlocks(3);367368    balanceBobAfter = await helper.balance.getSubstrate(bob.address);  369    balanceBobRelayTokenAfter = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'});370371    const wndFee = balanceBobRelayTokenAfter - TRANSFER_AMOUNT - balanceBobRelayTokenBefore; 372    console.log(373      '[Relay (Westend) -> Quartz] transaction fees: %s QTZ',374      helper.util.bigIntToDecimals(balanceBobAfter - balanceBobBefore),375    );376    console.log(377      '[Relay (Westend) -> Quartz] transaction fees: %s WND',378      helper.util.bigIntToDecimals(wndFee, STATEMINE_DECIMALS),379    );380    expect(balanceBobBefore == balanceBobAfter).to.be.true;381    expect(balanceBobRelayTokenBefore < balanceBobRelayTokenAfter).to.be.true;382  });383384  itSub('Should connect and send Relay token back', async ({helper}) => {385    const destination = {386      V1: {387        parents: 1,388        interior: {X2: [389          {390            Parachain: STATEMINE_CHAIN,391          },392          {393            AccountId32: {394              network: 'Any',395              id: bob.addressRaw,396            },397          },398        ]},399      },400    };401402    const currencies: any = [403      [404        {405          NativeAssetId: 'Parent',406        },407        TRANSFER_AMOUNT,408      ],409    ];410411    const feeItem = 0;412    const destWeight = 500000000000;413414    await helper.xTokens.transferMulticurrencies(bob, currencies, feeItem, destination, destWeight);415416    balanceBobFinal = await helper.balance.getSubstrate(bob.address);417    console.log('[Relay (Westend) to Quartz] transaction fees: %s QTZ', balanceBobAfter - balanceBobFinal);418  });419});420421describeXCM('[XCM] Integration test: Exchanging tokens with Karura', () => {422  let alice: IKeyringPair;423  let randomAccount: IKeyringPair;424425  let balanceQuartzTokenInit: bigint;426  let balanceQuartzTokenMiddle: bigint;427  let balanceQuartzTokenFinal: bigint;428  let balanceKaruraTokenInit: bigint;429  let balanceKaruraTokenMiddle: bigint;430  let balanceKaruraTokenFinal: bigint;431  let balanceQuartzForeignTokenInit: bigint;432  let balanceQuartzForeignTokenMiddle: bigint;433  let balanceQuartzForeignTokenFinal: bigint;434435  before(async () => {436    await usingPlaygrounds(async (helper, privateKey) => {437      alice = await privateKey('//Alice');438      [randomAccount] = await helper.arrange.createAccounts([0n], alice);439    });440441    await usingKaruraPlaygrounds(karuraUrl, async (helper) => {442      const destination = {443        V0: {444          X2: [445            'Parent',446            {447              Parachain: QUARTZ_CHAIN,448            },449          ],450        },451      };452453      const metadata = {454        name: 'QTZ',455        symbol: 'QTZ',456        decimals: 18,457        minimalBalance: 1n,458      };459460      await helper.getSudo().assetRegistry.registerForeignAsset(alice, destination, metadata);461      await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n);462      balanceKaruraTokenInit = await helper.balance.getSubstrate(randomAccount.address);463      balanceQuartzForeignTokenInit = await helper.tokens.accounts(randomAccount.address, {ForeignAsset: 0});464    });465466    await usingPlaygrounds(async (helper) => {467      await helper.balance.transferToSubstrate(alice, randomAccount.address, 10n * TRANSFER_AMOUNT);468      balanceQuartzTokenInit = await helper.balance.getSubstrate(randomAccount.address);469    });470  });471472  itSub('Should connect and send QTZ to Karura', async ({helper}) => {473    const destination = {474      V0: {475        X2: [476          'Parent',477          {478            Parachain: KARURA_CHAIN,479          },480        ],481      },482    };483484    const beneficiary = {485      V0: {486        X1: {487          AccountId32: {488            network: 'Any',489            id: randomAccount.addressRaw,490          },491        },492      },493    };494495    const assets = {496      V1: [497        {498          id: {499            Concrete: {500              parents: 0,501              interior: 'Here',502            },503          },504          fun: {505            Fungible: TRANSFER_AMOUNT,506          },507        },508      ],509    };510511    const feeAssetItem = 0;512    const weightLimit = 5000000000;513514    await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, weightLimit);515    balanceQuartzTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);516517    const qtzFees = balanceQuartzTokenInit - balanceQuartzTokenMiddle - TRANSFER_AMOUNT;518    console.log('[Quartz -> Karura] transaction fees on Quartz: %s QTZ', helper.util.bigIntToDecimals(qtzFees));519    expect(qtzFees > 0n).to.be.true;520521    await usingKaruraPlaygrounds(karuraUrl, async (helper) => {522      await helper.wait.newBlocks(3);523      balanceQuartzForeignTokenMiddle = await helper.tokens.accounts(randomAccount.address, {ForeignAsset: 0});524      balanceKaruraTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);525526      const karFees = balanceKaruraTokenInit - balanceKaruraTokenMiddle;527      const qtzIncomeTransfer = balanceQuartzForeignTokenMiddle - balanceQuartzForeignTokenInit;528529      console.log(530        '[Quartz -> Karura] transaction fees on Karura: %s KAR',531        helper.util.bigIntToDecimals(karFees, KARURA_DECIMALS),532      );533      console.log('[Quartz -> Karura] income %s QTZ', helper.util.bigIntToDecimals(qtzIncomeTransfer));534      expect(karFees == 0n).to.be.true;535      expect(qtzIncomeTransfer == TRANSFER_AMOUNT).to.be.true;536    });537  });538539  itSub('Should connect to Karura and send QTZ back', async ({helper}) => {540    await usingKaruraPlaygrounds(karuraUrl, async (helper) => {541      const destination = {542        V1: {543          parents: 1,544          interior: {545            X2: [546              {Parachain: QUARTZ_CHAIN},547              {548                AccountId32: {549                  network: 'Any',550                  id: randomAccount.addressRaw,551                },552              },553            ],554          },555        },556      };557558      const id = {559        ForeignAsset: 0,560      };561562      const destWeight = 50000000;563564      await helper.xTokens.transfer(randomAccount, id, TRANSFER_AMOUNT, destination, destWeight);565      balanceKaruraTokenFinal = await helper.balance.getSubstrate(randomAccount.address);566      balanceQuartzForeignTokenFinal = await helper.tokens.accounts(randomAccount.address, id);567568      const karFees = balanceKaruraTokenMiddle - balanceKaruraTokenFinal;569      const qtzOutcomeTransfer = balanceQuartzForeignTokenMiddle - balanceQuartzForeignTokenFinal;570571      console.log(572        '[Karura -> Quartz] transaction fees on Karura: %s KAR',573        helper.util.bigIntToDecimals(karFees, KARURA_DECIMALS),574      );575      console.log('[Karura -> Quartz] outcome %s QTZ', helper.util.bigIntToDecimals(qtzOutcomeTransfer));576577      expect(karFees > 0).to.be.true;578      expect(qtzOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;579    });580581    await helper.wait.newBlocks(3);582583    balanceQuartzTokenFinal = await helper.balance.getSubstrate(randomAccount.address);584    const actuallyDelivered = balanceQuartzTokenFinal - balanceQuartzTokenMiddle;585    expect(actuallyDelivered > 0).to.be.true;586587    console.log('[Karura -> Quartz] actually delivered %s QTZ', helper.util.bigIntToDecimals(actuallyDelivered));588589    const qtzFees = TRANSFER_AMOUNT - actuallyDelivered;590    console.log('[Karura -> Quartz] transaction fees on Quartz: %s QTZ', helper.util.bigIntToDecimals(qtzFees));591    expect(qtzFees == 0n).to.be.true;592  });593});594595// These tests are relevant only when the foreign asset pallet is disabled596describeXCM('[XCM] Integration test: Quartz rejects non-native tokens', () => {597  let alice: IKeyringPair;598599  before(async () => {600    await usingPlaygrounds(async (_helper, privateKey) => {601      alice = await privateKey('//Alice');602    });603  });604605  itSub('Quartz rejects tokens from the Relay', async ({helper}) => {606    await usingRelayPlaygrounds(relayUrl, async (helper) => {607      const destination = {608        V1: {609          parents: 0,610          interior: {X1: {611            Parachain: QUARTZ_CHAIN,612          },613          },614        }};615616      const beneficiary = {617        V1: {618          parents: 0,619          interior: {X1: {620            AccountId32: {621              network: 'Any',622              id: alice.addressRaw,623            },624          }},625        },626      };627628      const assets = {629        V1: [630          {631            id: {632              Concrete: {633                parents: 0,634                interior: 'Here',635              },636            },637            fun: {638              Fungible: TRANSFER_AMOUNT,639            },640          },641        ],642      };643644      const feeAssetItem = 0;645      const weightLimit = 5_000_000_000;646647      await helper.xcm.limitedReserveTransferAssets(alice, destination, beneficiary, assets, feeAssetItem, weightLimit);648    });649650    const maxWaitBlocks = 3;651652    const dmpQueueExecutedDownward = await helper.wait.event(maxWaitBlocks, 'dmpQueue', 'ExecutedDownward');653654    expect(655      dmpQueueExecutedDownward != null,656      '[Relay] dmpQueue.ExecutedDownward event is expected',657    ).to.be.true;658659    const event = dmpQueueExecutedDownward!.event;660    const outcome = event.data[1] as XcmV2TraitsOutcome;661662    expect(663      outcome.isIncomplete,664      '[Relay] The outcome of the XCM should be `Incomplete`',665    ).to.be.true;666667    const incomplete = outcome.asIncomplete;668    expect(669      incomplete[1].toString() == 'AssetNotFound',670      '[Relay] The XCM error should be `AssetNotFound`',671    ).to.be.true;672  });673674  itSub('Quartz rejects KAR tokens from Karura', async ({helper}) => {675    await usingKaruraPlaygrounds(karuraUrl, async (helper) => {676      const destination = {677        V1: {678          parents: 1,679          interior: {680            X2: [681              {Parachain: QUARTZ_CHAIN},682              {683                AccountId32: {684                  network: 'Any',685                  id: alice.addressRaw,686                },687              },688            ],689          },690        },691      };692693      const id = {694        Token: 'KAR',695      };696697      const destWeight = 50000000;698699      await helper.xTokens.transfer(alice, id, 100_000_000_000n, destination, destWeight);700    });701702    const maxWaitBlocks = 3;703704    const xcmpQueueFailEvent = await helper.wait.event(maxWaitBlocks, 'xcmpQueue', 'Fail');705706    expect(707      xcmpQueueFailEvent != null,708      '[Karura] xcmpQueue.FailEvent event is expected',709    ).to.be.true;710711    const event = xcmpQueueFailEvent!.event;712    const outcome = event.data[1] as XcmV2TraitsError;713714    expect(715      outcome.isUntrustedReserveLocation,716      '[Karura] The XCM error should be `UntrustedReserveLocation`',717    ).to.be.true;718  });719});720721describeXCM('[XCM] Integration test: Exchanging QTZ with Moonriver', () => {722  // Quartz constants723  let quartzDonor: IKeyringPair;724  let quartzAssetLocation;725726  let randomAccountQuartz: IKeyringPair;727  let randomAccountMoonriver: IKeyringPair;728729  // Moonriver constants730  let assetId: string;731732  const councilVotingThreshold = 2;733  const technicalCommitteeThreshold = 2;734  const votingPeriod = 3;735  const delayPeriod = 0;736737  const quartzAssetMetadata = {738    name: 'xcQuartz',739    symbol: 'xcQTZ',740    decimals: 18,741    isFrozen: false,742    minimalBalance: 1n,743  };744745  let balanceQuartzTokenInit: bigint;746  let balanceQuartzTokenMiddle: bigint;747  let balanceQuartzTokenFinal: bigint;748  let balanceForeignQtzTokenInit: bigint;749  let balanceForeignQtzTokenMiddle: bigint;750  let balanceForeignQtzTokenFinal: bigint;751  let balanceMovrTokenInit: bigint;752  let balanceMovrTokenMiddle: bigint;753  let balanceMovrTokenFinal: bigint;754755  before(async () => {756    await usingPlaygrounds(async (helper, privateKey) => {757      quartzDonor = await privateKey('//Alice');758      [randomAccountQuartz] = await helper.arrange.createAccounts([0n], quartzDonor);759760      balanceForeignQtzTokenInit = 0n;761    });762763    await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => {764      const alithAccount = helper.account.alithAccount();765      const baltatharAccount = helper.account.baltatharAccount();766      const dorothyAccount = helper.account.dorothyAccount();767768      randomAccountMoonriver = helper.account.create();769770      // >>> Sponsoring Dorothy >>>771      console.log('Sponsoring Dorothy.......');772      await helper.balance.transferToEthereum(alithAccount, dorothyAccount.address, 11_000_000_000_000_000_000n);773      console.log('Sponsoring Dorothy.......DONE');774      // <<< Sponsoring Dorothy <<<775776      quartzAssetLocation = {777        XCM: {778          parents: 1,779          interior: {X1: {Parachain: QUARTZ_CHAIN}},780        },781      };782      const existentialDeposit = 1n;783      const isSufficient = true;784      const unitsPerSecond = 1n;785      const numAssetsWeightHint = 0;786787      const encodedProposal = helper.assetManager.makeRegisterForeignAssetProposal({788        location: quartzAssetLocation,789        metadata: quartzAssetMetadata,790        existentialDeposit,791        isSufficient,792        unitsPerSecond,793        numAssetsWeightHint,794      });795      const proposalHash = blake2AsHex(encodedProposal);796797      console.log('Encoded proposal for registerForeignAsset & setAssetUnitsPerSecond is %s', encodedProposal);798      console.log('Encoded length %d', encodedProposal.length);799      console.log('Encoded proposal hash for batch utility after schedule is %s', proposalHash);800801      // >>> Note motion preimage >>>802      console.log('Note motion preimage.......');803      await helper.democracy.notePreimage(baltatharAccount, encodedProposal);804      console.log('Note motion preimage.......DONE');805      // <<< Note motion preimage <<<806807      // >>> Propose external motion through council >>>808      console.log('Propose external motion through council.......');809      const externalMotion = helper.democracy.externalProposeMajority(proposalHash);810      const encodedMotion = externalMotion?.method.toHex() || '';811      const motionHash = blake2AsHex(encodedMotion);812      console.log('Motion hash is %s', motionHash);813814      await helper.collective.council.propose(baltatharAccount, councilVotingThreshold, externalMotion, externalMotion.encodedLength);815816      const councilProposalIdx = await helper.collective.council.proposalCount() - 1;817      await helper.collective.council.vote(dorothyAccount, motionHash, councilProposalIdx, true);818      await helper.collective.council.vote(baltatharAccount, motionHash, councilProposalIdx, true);819820      await helper.collective.council.close(dorothyAccount, motionHash, councilProposalIdx, 1_000_000_000, externalMotion.encodedLength);821      console.log('Propose external motion through council.......DONE');822      // <<< Propose external motion through council <<<823824      // >>> Fast track proposal through technical committee >>>825      console.log('Fast track proposal through technical committee.......');826      const fastTrack = helper.democracy.fastTrack(proposalHash, votingPeriod, delayPeriod);827      const encodedFastTrack = fastTrack?.method.toHex() || '';828      const fastTrackHash = blake2AsHex(encodedFastTrack);829      console.log('FastTrack hash is %s', fastTrackHash);830831      await helper.collective.techCommittee.propose(alithAccount, technicalCommitteeThreshold, fastTrack, fastTrack.encodedLength);832833      const techProposalIdx = await helper.collective.techCommittee.proposalCount() - 1;834      await helper.collective.techCommittee.vote(baltatharAccount, fastTrackHash, techProposalIdx, true);835      await helper.collective.techCommittee.vote(alithAccount, fastTrackHash, techProposalIdx, true);836837      await helper.collective.techCommittee.close(baltatharAccount, fastTrackHash, techProposalIdx, 1_000_000_000, fastTrack.encodedLength);838      console.log('Fast track proposal through technical committee.......DONE');839      // <<< Fast track proposal through technical committee <<<840841      // >>> Referendum voting >>>842      console.log('Referendum voting.......');843      await helper.democracy.referendumVote(dorothyAccount, 0, {844        balance: 10_000_000_000_000_000_000n,845        vote: {aye: true, conviction: 1},846      });847      console.log('Referendum voting.......DONE');848      // <<< Referendum voting <<<849850      // >>> Acquire Quartz AssetId Info on Moonriver >>>851      console.log('Acquire Quartz AssetId Info on Moonriver.......');852853      // Wait for the democracy execute854      await helper.wait.newBlocks(5);855856      assetId = (await helper.assetManager.assetTypeId(quartzAssetLocation)).toString();857858      console.log('QTZ asset ID is %s', assetId);859      console.log('Acquire Quartz AssetId Info on Moonriver.......DONE');860      // >>> Acquire Quartz AssetId Info on Moonriver >>>861862      // >>> Sponsoring random Account >>>863      console.log('Sponsoring random Account.......');864      await helper.balance.transferToEthereum(baltatharAccount, randomAccountMoonriver.address, 11_000_000_000_000_000_000n);865      console.log('Sponsoring random Account.......DONE');866      // <<< Sponsoring random Account <<<867868      balanceMovrTokenInit = await helper.balance.getEthereum(randomAccountMoonriver.address);869    });870871    await usingPlaygrounds(async (helper) => {872      await helper.balance.transferToSubstrate(quartzDonor, randomAccountQuartz.address, 10n * TRANSFER_AMOUNT);873      balanceQuartzTokenInit = await helper.balance.getSubstrate(randomAccountQuartz.address);874    });875  });876877  itSub('Should connect and send QTZ to Moonriver', async ({helper}) => {878    const currencyId = {879      NativeAssetId: 'Here',880    };881    const dest = {882      V1: {883        parents: 1,884        interior: {885          X2: [886            {Parachain: MOONRIVER_CHAIN},887            {AccountKey20: {network: 'Any', key: randomAccountMoonriver.address}},888          ],889        },890      },891    };892    const amount = TRANSFER_AMOUNT;893    const destWeight = 850000000;894895    await helper.xTokens.transfer(randomAccountQuartz, currencyId, amount, dest, destWeight);896897    balanceQuartzTokenMiddle = await helper.balance.getSubstrate(randomAccountQuartz.address);898    expect(balanceQuartzTokenMiddle < balanceQuartzTokenInit).to.be.true;899900    const transactionFees = balanceQuartzTokenInit - balanceQuartzTokenMiddle - TRANSFER_AMOUNT;901    console.log('[Quartz -> Moonriver] transaction fees on Quartz: %s QTZ', helper.util.bigIntToDecimals(transactionFees));902    expect(transactionFees > 0).to.be.true;903904    await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => {905      await helper.wait.newBlocks(3);906907      balanceMovrTokenMiddle = await helper.balance.getEthereum(randomAccountMoonriver.address);908909      const movrFees = balanceMovrTokenInit - balanceMovrTokenMiddle;910      console.log('[Quartz -> Moonriver] transaction fees on Moonriver: %s MOVR',helper.util.bigIntToDecimals(movrFees));911      expect(movrFees == 0n).to.be.true;912913      balanceForeignQtzTokenMiddle = (await helper.assets.account(assetId, randomAccountMoonriver.address))!; // BigInt(qtzRandomAccountAsset['balance']);914      const qtzIncomeTransfer = balanceForeignQtzTokenMiddle - balanceForeignQtzTokenInit;915      console.log('[Quartz -> Moonriver] income %s QTZ', helper.util.bigIntToDecimals(qtzIncomeTransfer));916      expect(qtzIncomeTransfer == TRANSFER_AMOUNT).to.be.true;917    });918  });919920  itSub('Should connect to Moonriver and send QTZ back', async ({helper}) => {921    await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => {922      const asset = {923        V1: {924          id: {925            Concrete: {926              parents: 1,927              interior: {928                X1: {Parachain: QUARTZ_CHAIN},929              },930            },931          },932          fun: {933            Fungible: TRANSFER_AMOUNT,934          },935        },936      };937      const destination = {938        V1: {939          parents: 1,940          interior: {941            X2: [942              {Parachain: QUARTZ_CHAIN},943              {AccountId32: {network: 'Any', id: randomAccountQuartz.addressRaw}},944            ],945          },946        },947      };948      const destWeight = 50000000;949950      await helper.xTokens.transferMultiasset(randomAccountMoonriver, asset, destination, destWeight);951952      balanceMovrTokenFinal = await helper.balance.getEthereum(randomAccountMoonriver.address);953954      const movrFees = balanceMovrTokenMiddle - balanceMovrTokenFinal;955      console.log('[Moonriver -> Quartz] transaction fees on Moonriver: %s MOVR', helper.util.bigIntToDecimals(movrFees));956      expect(movrFees > 0).to.be.true;957958      const qtzRandomAccountAsset = await helper.assets.account(assetId, randomAccountMoonriver.address);959960      expect(qtzRandomAccountAsset).to.be.null;961962      balanceForeignQtzTokenFinal = 0n;963964      const qtzOutcomeTransfer = balanceForeignQtzTokenMiddle - balanceForeignQtzTokenFinal;965      console.log('[Quartz -> Moonriver] outcome %s QTZ', helper.util.bigIntToDecimals(qtzOutcomeTransfer));966      expect(qtzOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;967    });968969    await helper.wait.newBlocks(3);970971    balanceQuartzTokenFinal = await helper.balance.getSubstrate(randomAccountQuartz.address);972    const actuallyDelivered = balanceQuartzTokenFinal - balanceQuartzTokenMiddle;973    expect(actuallyDelivered > 0).to.be.true;974975    console.log('[Moonriver -> Quartz] actually delivered %s QTZ', helper.util.bigIntToDecimals(actuallyDelivered));976977    const qtzFees = TRANSFER_AMOUNT - actuallyDelivered;978    console.log('[Moonriver -> Quartz] transaction fees on Quartz: %s QTZ', helper.util.bigIntToDecimals(qtzFees));979    expect(qtzFees == 0n).to.be.true;980  });981});
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 {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 wndFee = balanceBobRelayTokenAfter - TRANSFER_AMOUNT_RELAY - balanceBobRelayTokenBefore;380    expect(wndFee > 0).to.be.true('westend fees should be greater than 0');381    console.log(382      '[Relay (Westend) -> Quartz] transaction fees: %s QTZ',383      helper.util.bigIntToDecimals(balanceBobAfter - balanceBobBefore),384    );385    console.log(386      '[Relay (Westend) -> Quartz] transaction fees: %s WND',387      helper.util.bigIntToDecimals(wndFee, STATEMINE_DECIMALS),388    );389    expect(balanceBobBefore == balanceBobAfter).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).to.be.true('qtz Fees should be greater than 0');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).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).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).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});