git.delta.rocks / unique-network / refs/commits / 159d4fc7deeb

difftreelog

source

tests/src/xcm/xcmUnique.test.ts32.0 KiBsourcehistory
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617import {IKeyringPair} from '@polkadot/types/types';18import {blake2AsHex} from '@polkadot/util-crypto';19import config from '../config';20import {XcmV2TraitsError} from '../interfaces';21import {itSub, expect, describeXCM, usingPlaygrounds, usingAcalaPlaygrounds, usingRelayPlaygrounds, usingMoonbeamPlaygrounds, usingStatemintPlaygrounds} from '../util';2223const UNIQUE_CHAIN = 2037;24const STATEMINT_CHAIN = 1000;25const ACALA_CHAIN = 2000;26const MOONBEAM_CHAIN = 2004;2728const STATEMINT_PALLET_INSTANCE = 50;2930const relayUrl = config.relayUrl;31const statemintUrl = config.statemintUrl;32const acalaUrl = config.acalaUrl;33const moonbeamUrl = config.moonbeamUrl;3435const RELAY_DECIMALS = 12;36const STATEMINT_DECIMALS = 12;37const ACALA_DECIMALS = 12;3839const TRANSFER_AMOUNT = 2000000000000000000000000n;4041const FUNDING_AMOUNT = 3_500_000_0000_000_000n;4243const TRANSFER_AMOUNT_RELAY = 50_000_000_000_000_000n;4445const USDT_ASSET_ID = 100;46const USDT_ASSET_METADATA_DECIMALS = 18;47const USDT_ASSET_METADATA_NAME = 'USDT';48const USDT_ASSET_METADATA_DESCRIPTION = 'USDT';49const USDT_ASSET_METADATA_MINIMAL_BALANCE = 1n;50const USDT_ASSET_AMOUNT = 10_000_000_000_000_000_000_000_000n;5152describeXCM('[XCM] Integration test: Exchanging USDT with Statemint', () => {53  let alice: IKeyringPair;54  let bob: IKeyringPair;5556  let balanceStmnBefore: bigint;57  let balanceStmnAfter: bigint;5859  let balanceUniqueBefore: bigint;60  let balanceUniqueAfter: bigint;61  let balanceUniqueFinal: bigint;6263  let balanceBobBefore: bigint;64  let balanceBobAfter: bigint;65  let balanceBobFinal: bigint;6667  let balanceBobRelayTokenBefore: bigint;68  let balanceBobRelayTokenAfter: bigint;697071  before(async () => {72    await usingPlaygrounds(async (_helper, privateKey) => {73      alice = await privateKey('//Alice');74      bob = await privateKey('//Bob'); // sovereign account on Statemint funds donor75    });7677    await usingRelayPlaygrounds(relayUrl, async (helper) => {78      // Fund accounts on Statemint79      await helper.xcm.teleportNativeAsset(alice, STATEMINT_CHAIN, alice.addressRaw, FUNDING_AMOUNT);80      await helper.xcm.teleportNativeAsset(alice, STATEMINT_CHAIN, bob.addressRaw, FUNDING_AMOUNT);81    });8283    await usingStatemintPlaygrounds(statemintUrl, async (helper) => {84      const sovereignFundingAmount = 3_500_000_000n;8586      await helper.assets.create(87        alice,88        USDT_ASSET_ID,89        alice.address,90        USDT_ASSET_METADATA_MINIMAL_BALANCE,91      );92      await helper.assets.setMetadata(93        alice,94        USDT_ASSET_ID,95        USDT_ASSET_METADATA_NAME,96        USDT_ASSET_METADATA_DESCRIPTION,97        USDT_ASSET_METADATA_DECIMALS,98      );99      await helper.assets.mint(100        alice,101        USDT_ASSET_ID,102        alice.address,103        USDT_ASSET_AMOUNT,104      );105106      // funding parachain sovereing account on Statemint.107      // The sovereign account should be created before any action108      // (the assets pallet on Statemint check if the sovereign account exists)109      const parachainSovereingAccount = helper.address.paraSiblingSovereignAccount(UNIQUE_CHAIN);110      await helper.balance.transferToSubstrate(bob, parachainSovereingAccount, sovereignFundingAmount);111    });112113114    await usingPlaygrounds(async (helper) => {115      const location = {116        V1: {117          parents: 1,118          interior: {X3: [119            {120              Parachain: STATEMINT_CHAIN,121            },122            {123              PalletInstance: STATEMINT_PALLET_INSTANCE,124            },125            {126              GeneralIndex: USDT_ASSET_ID,127            },128          ]},129        },130      };131132      const metadata =133      {134        name: USDT_ASSET_ID,135        symbol: USDT_ASSET_METADATA_NAME,136        decimals: USDT_ASSET_METADATA_DECIMALS,137        minimalBalance: USDT_ASSET_METADATA_MINIMAL_BALANCE,138      };139      await helper.getSudo().foreignAssets.register(alice, alice.address, location, metadata);140      balanceUniqueBefore = await helper.balance.getSubstrate(alice.address);141    });142143144    // Providing the relay currency to the unique sender account145    // (fee for USDT XCM are paid in relay tokens)146    await usingRelayPlaygrounds(relayUrl, async (helper) => {147      const destination = {148        V1: {149          parents: 0,150          interior: {X1: {151            Parachain: UNIQUE_CHAIN,152          },153          },154        }};155156      const beneficiary = {157        V1: {158          parents: 0,159          interior: {X1: {160            AccountId32: {161              network: 'Any',162              id: alice.addressRaw,163            },164          }},165        },166      };167168      const assets = {169        V1: [170          {171            id: {172              Concrete: {173                parents: 0,174                interior: 'Here',175              },176            },177            fun: {178              Fungible: TRANSFER_AMOUNT_RELAY,179            },180          },181        ],182      };183184      const feeAssetItem = 0;185186      await helper.xcm.limitedReserveTransferAssets(alice, destination, beneficiary, assets, feeAssetItem, 'Unlimited');187    });188189  });190191  itSub('Should connect and send USDT from Statemint to Unique', async ({helper}) => {192    await usingStatemintPlaygrounds(statemintUrl, async (helper) => {193      const dest = {194        V1: {195          parents: 1,196          interior: {X1: {197            Parachain: UNIQUE_CHAIN,198          },199          },200        }};201202      const beneficiary = {203        V1: {204          parents: 0,205          interior: {X1: {206            AccountId32: {207              network: 'Any',208              id: alice.addressRaw,209            },210          }},211        },212      };213214      const assets = {215        V1: [216          {217            id: {218              Concrete: {219                parents: 0,220                interior: {221                  X2: [222                    {223                      PalletInstance: STATEMINT_PALLET_INSTANCE,224                    },225                    {226                      GeneralIndex: USDT_ASSET_ID,227                    },228                  ]},229              },230            },231            fun: {232              Fungible: TRANSFER_AMOUNT,233            },234          },235        ],236      };237238      const feeAssetItem = 0;239240      balanceStmnBefore = await helper.balance.getSubstrate(alice.address);241      await helper.xcm.limitedReserveTransferAssets(alice, dest, beneficiary, assets, feeAssetItem, 'Unlimited');242243      balanceStmnAfter = await helper.balance.getSubstrate(alice.address);244245      // common good parachain take commission in it native token246      console.log(247        '[Statemint -> Unique] transaction fees on Statemint: %s WND',248        helper.util.bigIntToDecimals(balanceStmnBefore - balanceStmnAfter, STATEMINT_DECIMALS),249      );250      expect(balanceStmnBefore > balanceStmnAfter).to.be.true;251252    });253254255    // ensure that asset has been delivered256    await helper.wait.newBlocks(3);257258    // expext collection id will be with id 1259    const free = await helper.ft.getBalance(1, {Substrate: alice.address});260261    balanceUniqueAfter = await helper.balance.getSubstrate(alice.address);262263    console.log(264      '[Statemint -> Unique] transaction fees on Unique: %s USDT',265      helper.util.bigIntToDecimals(TRANSFER_AMOUNT - free, USDT_ASSET_METADATA_DECIMALS),266    );267    console.log(268      '[Statemint -> Unique] transaction fees on Unique: %s UNQ',269      helper.util.bigIntToDecimals(balanceUniqueAfter - balanceUniqueBefore),270    );271    // commission has not paid in USDT token272    expect(free).to.be.equal(TRANSFER_AMOUNT);273    // ... and parachain native token274    expect(balanceUniqueAfter == balanceUniqueBefore).to.be.true;275  });276277  itSub('Should connect and send USDT from Unique to Statemint back', async ({helper}) => {278    const destination = {279      V1: {280        parents: 1,281        interior: {X2: [282          {283            Parachain: STATEMINT_CHAIN,284          },285          {286            AccountId32: {287              network: 'Any',288              id: alice.addressRaw,289            },290          },291        ]},292      },293    };294295    const relayFee = 400_000_000_000_000n;296    const currencies: [any, bigint][] = [297      [298        {299          ForeignAssetId: 0,300        },301        TRANSFER_AMOUNT,302      ],303      [304        {305          NativeAssetId: 'Parent',306        },307        relayFee,308      ],309    ];310311    const feeItem = 1;312313    await helper.xTokens.transferMulticurrencies(alice, currencies, feeItem, destination, 'Unlimited');314315    // the commission has been paid in parachain native token316    balanceUniqueFinal = await helper.balance.getSubstrate(alice.address);317    console.log('[Unique -> Statemint] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(balanceUniqueFinal - balanceUniqueAfter));318    expect(balanceUniqueAfter > balanceUniqueFinal).to.be.true;319320    await usingStatemintPlaygrounds(statemintUrl, async (helper) => {321      await helper.wait.newBlocks(3);322323      // The USDT token never paid fees. Its amount not changed from begin value.324      // Also check that xcm transfer has been succeeded325      expect((await helper.assets.account(USDT_ASSET_ID, alice.address))! == USDT_ASSET_AMOUNT).to.be.true;326    });327  });328329  itSub('Should connect and send Relay token to Unique', async ({helper}) => {330    balanceBobBefore = await helper.balance.getSubstrate(bob.address);331    balanceBobRelayTokenBefore = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'});332333    await usingRelayPlaygrounds(relayUrl, async (helper) => {334      const destination = {335        V1: {336          parents: 0,337          interior: {X1: {338            Parachain: UNIQUE_CHAIN,339          },340          },341        }};342343      const beneficiary = {344        V1: {345          parents: 0,346          interior: {X1: {347            AccountId32: {348              network: 'Any',349              id: bob.addressRaw,350            },351          }},352        },353      };354355      const assets = {356        V1: [357          {358            id: {359              Concrete: {360                parents: 0,361                interior: 'Here',362              },363            },364            fun: {365              Fungible: TRANSFER_AMOUNT_RELAY,366            },367          },368        ],369      };370371      const feeAssetItem = 0;372373      await helper.xcm.limitedReserveTransferAssets(bob, destination, beneficiary, assets, feeAssetItem, 'Unlimited');374    });375376    await helper.wait.newBlocks(3);377378    balanceBobAfter = await helper.balance.getSubstrate(bob.address);379    balanceBobRelayTokenAfter = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'});380381    const wndFeeOnUnique = balanceBobRelayTokenAfter - TRANSFER_AMOUNT_RELAY - balanceBobRelayTokenBefore;382    const wndDiffOnUnique = balanceBobRelayTokenAfter - balanceBobRelayTokenBefore;383    console.log(384      '[Relay (Westend) -> Unique] transaction fees: %s UNQ',385      helper.util.bigIntToDecimals(balanceBobAfter - balanceBobBefore),386    );387    console.log(388      '[Relay (Westend) -> Unique] transaction fees: %s WND',389      helper.util.bigIntToDecimals(wndFeeOnUnique, STATEMINT_DECIMALS),390    );391    console.log('[Relay (Westend) -> Unique] actually delivered: %s WND', wndDiffOnUnique);392    expect(wndFeeOnUnique == 0n, 'No incoming WND fees should be taken').to.be.true;393    expect(balanceBobBefore == balanceBobAfter, 'No incoming UNQ fees should be taken').to.be.true;394  });395396  itSub('Should connect and send Relay token back', async ({helper}) => {397    let relayTokenBalanceBefore: bigint;398    let relayTokenBalanceAfter: bigint;399    await usingRelayPlaygrounds(relayUrl, async (helper) => {400      relayTokenBalanceBefore = await helper.balance.getSubstrate(bob.address);401    });402403    const destination = {404      V1: {405        parents: 1,406        interior: {407          X1:{408            AccountId32: {409              network: 'Any',410              id: bob.addressRaw,411            },412          },413        },414      },415    };416417    const currencies: any = [418      [419        {420          NativeAssetId: 'Parent',421        },422        TRANSFER_AMOUNT_RELAY,423      ],424    ];425426    const feeItem = 0;427428    await helper.xTokens.transferMulticurrencies(bob, currencies, feeItem, destination, 'Unlimited');429430    balanceBobFinal = await helper.balance.getSubstrate(bob.address);431    console.log('[Unique -> Relay (Westend)] transaction fees: %s UNQ',  helper.util.bigIntToDecimals(balanceBobAfter - balanceBobFinal));432433    await usingRelayPlaygrounds(relayUrl, async (helper) => {434      await helper.wait.newBlocks(10);435      relayTokenBalanceAfter = await helper.balance.getSubstrate(bob.address);436437      const diff = relayTokenBalanceAfter - relayTokenBalanceBefore;438      console.log('[Unique -> Relay (Westend)] actually delivered: %s WND', helper.util.bigIntToDecimals(diff, RELAY_DECIMALS));439      expect(diff > 0, 'Relay tokens was not delivered back').to.be.true;440    });441  });442});443444describeXCM('[XCM] Integration test: Exchanging tokens with Acala', () => {445  let alice: IKeyringPair;446  let randomAccount: IKeyringPair;447448  let balanceUniqueTokenInit: bigint;449  let balanceUniqueTokenMiddle: bigint;450  let balanceUniqueTokenFinal: bigint;451  let balanceAcalaTokenInit: bigint;452  let balanceAcalaTokenMiddle: bigint;453  let balanceAcalaTokenFinal: bigint;454  let balanceUniqueForeignTokenInit: bigint;455  let balanceUniqueForeignTokenMiddle: bigint;456  let balanceUniqueForeignTokenFinal: bigint;457458  // computed by a test transfer from prod Unique to prod Acala.459  // 2 UNQ sent https://unique.subscan.io/xcm_message/polkadot-bad0b68847e2398af25d482e9ee6f9c1f9ec2a48460  // 1.898970000000000000 UNQ received (you can check Acala's chain state in the corresponding block)461  const expectedAcalaIncomeFee = 2000000000000000000n - 1898970000000000000n;462463  const ACALA_BACKWARD_TRANSFER_AMOUNT = TRANSFER_AMOUNT - expectedAcalaIncomeFee;464465  before(async () => {466    await usingPlaygrounds(async (helper, privateKey) => {467      alice = await privateKey('//Alice');468      [randomAccount] = await helper.arrange.createAccounts([0n], alice);469    });470471    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {472      const destination = {473        V1: {474          parents: 1,475          interior: {476            X1: {477              Parachain: UNIQUE_CHAIN,478            },479          },480        },481      };482483      const metadata = {484        name: 'Unique Network',485        symbol: 'UNQ',486        decimals: 18,487        minimalBalance: 1250000000000000000n,488      };489490      await helper.getSudo().assetRegistry.registerForeignAsset(alice, destination, metadata);491      await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n);492      balanceAcalaTokenInit = await helper.balance.getSubstrate(randomAccount.address);493      balanceUniqueForeignTokenInit = await helper.tokens.accounts(randomAccount.address, {ForeignAsset: 0});494    });495496    await usingPlaygrounds(async (helper) => {497      await helper.balance.transferToSubstrate(alice, randomAccount.address, 10n * TRANSFER_AMOUNT);498      balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccount.address);499    });500  });501502  itSub('Should connect and send UNQ to Acala', async ({helper}) => {503504    const destination = {505      V1: {506        parents: 1,507        interior: {508          X1: {509            Parachain: ACALA_CHAIN,510          },511        },512      },513    };514515    const beneficiary = {516      V1: {517        parents: 0,518        interior: {519          X1: {520            AccountId32: {521              network: 'Any',522              id: randomAccount.addressRaw,523            },524          },525        },526      },527    };528529    const assets = {530      V1: [531        {532          id: {533            Concrete: {534              parents: 0,535              interior: 'Here',536            },537          },538          fun: {539            Fungible: TRANSFER_AMOUNT,540          },541        },542      ],543    };544545    const feeAssetItem = 0;546547    await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited');548    balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);549550    const unqFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;551    console.log('[Unique -> Acala] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));552    expect(unqFees > 0n, 'Negative fees UNQ, looks like nothing was transferred').to.be.true;553554    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {555      await helper.wait.newBlocks(3);556557      balanceUniqueForeignTokenMiddle = await helper.tokens.accounts(randomAccount.address, {ForeignAsset: 0});558      balanceAcalaTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);559560      const acaFees = balanceAcalaTokenInit - balanceAcalaTokenMiddle;561      const unqIncomeTransfer = balanceUniqueForeignTokenMiddle - balanceUniqueForeignTokenInit;562      const acaUnqFees = TRANSFER_AMOUNT - unqIncomeTransfer;563564      console.log(565        '[Unique -> Acala] transaction fees on Acala: %s ACA',566        helper.util.bigIntToDecimals(acaFees, ACALA_DECIMALS),567      );568      console.log(569        '[Unique -> Acala] transaction fees on Acala: %s UNQ',570        helper.util.bigIntToDecimals(acaUnqFees),571      );572      console.log('[Unique -> Acala] income %s UNQ', helper.util.bigIntToDecimals(unqIncomeTransfer));573      expect(acaFees == 0n).to.be.true;574      expect(575        acaUnqFees == expectedAcalaIncomeFee,576        'Acala took different income fee, check the Acala foreign asset config',577      ).to.be.true;578    });579  });580581  itSub('Should connect to Acala and send UNQ back', async ({helper}) => {582    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {583      const destination = {584        V1: {585          parents: 1,586          interior: {587            X2: [588              {Parachain: UNIQUE_CHAIN},589              {590                AccountId32: {591                  network: 'Any',592                  id: randomAccount.addressRaw,593                },594              },595            ],596          },597        },598      };599600      const id = {601        ForeignAsset: 0,602      };603604      await helper.xTokens.transfer(randomAccount, id, ACALA_BACKWARD_TRANSFER_AMOUNT, destination, 'Unlimited');605      balanceAcalaTokenFinal = await helper.balance.getSubstrate(randomAccount.address);606      balanceUniqueForeignTokenFinal = await helper.tokens.accounts(randomAccount.address, id);607608      const acaFees = balanceAcalaTokenMiddle - balanceAcalaTokenFinal;609      const unqOutcomeTransfer = balanceUniqueForeignTokenMiddle - balanceUniqueForeignTokenFinal;610611      console.log(612        '[Acala -> Unique] transaction fees on Acala: %s ACA',613        helper.util.bigIntToDecimals(acaFees, ACALA_DECIMALS),614      );615      console.log('[Acala -> Unique] outcome %s UNQ', helper.util.bigIntToDecimals(unqOutcomeTransfer));616617      expect(acaFees > 0, 'Negative fees ACA, looks like nothing was transferred').to.be.true;618      expect(unqOutcomeTransfer == ACALA_BACKWARD_TRANSFER_AMOUNT).to.be.true;619    });620621    await helper.wait.newBlocks(3);622623    balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccount.address);624    const actuallyDelivered = balanceUniqueTokenFinal - balanceUniqueTokenMiddle;625    expect(actuallyDelivered > 0).to.be.true;626627    console.log('[Acala -> Unique] actually delivered %s UNQ', helper.util.bigIntToDecimals(actuallyDelivered));628629    const unqFees = ACALA_BACKWARD_TRANSFER_AMOUNT - actuallyDelivered;630    console.log('[Acala -> Unique] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));631    expect(unqFees == 0n).to.be.true;632  });633});634635// These tests are relevant only when the foreign asset pallet is disabled636describeXCM('[XCM] Integration test: Unique rejects non-native tokens', () => {637  let alice: IKeyringPair;638639  before(async () => {640    await usingPlaygrounds(async (_helper, privateKey) => {641      alice = await privateKey('//Alice');642    });643  });644645  itSub('Unique rejects ACA tokens from Acala', async ({helper}) => {646    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {647      const destination = {648        V1: {649          parents: 1,650          interior: {651            X2: [652              {Parachain: UNIQUE_CHAIN},653              {654                AccountId32: {655                  network: 'Any',656                  id: alice.addressRaw,657                },658              },659            ],660          },661        },662      };663664      const id = {665        Token: 'ACA',666      };667668      await helper.xTokens.transfer(alice, id, 100_000_000_000n, destination, 'Unlimited');669    });670671    const maxWaitBlocks = 3;672673    const xcmpQueueFailEvent = await helper.wait.event(maxWaitBlocks, 'xcmpQueue', 'Fail');674675    expect(676      xcmpQueueFailEvent != null,677      '[Acala] xcmpQueue.FailEvent event is expected',678    ).to.be.true;679680    const event = xcmpQueueFailEvent!.event;681    const outcome = event.data[1] as XcmV2TraitsError;682683    expect(684      outcome.isFailedToTransactAsset,685      '[Acala] The XCM error should be `FailedToTransactAsset`',686    ).to.be.true;687  });688});689690describeXCM('[XCM] Integration test: Exchanging UNQ with Moonbeam', () => {691  // Unique constants692  let uniqueDonor: IKeyringPair;693  let uniqueAssetLocation;694695  let randomAccountUnique: IKeyringPair;696  let randomAccountMoonbeam: IKeyringPair;697698  // Moonbeam constants699  let assetId: string;700701  const councilVotingThreshold = 2;702  const technicalCommitteeThreshold = 2;703  const votingPeriod = 3;704  const delayPeriod = 0;705706  const uniqueAssetMetadata = {707    name: 'xcUnique',708    symbol: 'xcUNQ',709    decimals: 18,710    isFrozen: false,711    minimalBalance: 1n,712  };713714  let balanceUniqueTokenInit: bigint;715  let balanceUniqueTokenMiddle: bigint;716  let balanceUniqueTokenFinal: bigint;717  let balanceForeignUnqTokenInit: bigint;718  let balanceForeignUnqTokenMiddle: bigint;719  let balanceForeignUnqTokenFinal: bigint;720  let balanceGlmrTokenInit: bigint;721  let balanceGlmrTokenMiddle: bigint;722  let balanceGlmrTokenFinal: bigint;723724  before(async () => {725    await usingPlaygrounds(async (helper, privateKey) => {726      uniqueDonor = await privateKey('//Alice');727      [randomAccountUnique] = await helper.arrange.createAccounts([0n], uniqueDonor);728729      balanceForeignUnqTokenInit = 0n;730    });731732    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {733      const alithAccount = helper.account.alithAccount();734      const baltatharAccount = helper.account.baltatharAccount();735      const dorothyAccount = helper.account.dorothyAccount();736737      randomAccountMoonbeam = helper.account.create();738739      // >>> Sponsoring Dorothy >>>740      console.log('Sponsoring Dorothy.......');741      await helper.balance.transferToEthereum(alithAccount, dorothyAccount.address, 11_000_000_000_000_000_000n);742      console.log('Sponsoring Dorothy.......DONE');743      // <<< Sponsoring Dorothy <<<744745      uniqueAssetLocation = {746        XCM: {747          parents: 1,748          interior: {X1: {Parachain: UNIQUE_CHAIN}},749        },750      };751      const existentialDeposit = 1n;752      const isSufficient = true;753      const unitsPerSecond = 1n;754      const numAssetsWeightHint = 0;755756      const encodedProposal = helper.assetManager.makeRegisterForeignAssetProposal({757        location: uniqueAssetLocation,758        metadata: uniqueAssetMetadata,759        existentialDeposit,760        isSufficient,761        unitsPerSecond,762        numAssetsWeightHint,763      });764      const proposalHash = blake2AsHex(encodedProposal);765766      console.log('Encoded proposal for registerForeignAsset & setAssetUnitsPerSecond is %s', encodedProposal);767      console.log('Encoded length %d', encodedProposal.length);768      console.log('Encoded proposal hash for batch utility after schedule is %s', proposalHash);769770      // >>> Note motion preimage >>>771      console.log('Note motion preimage.......');772      await helper.democracy.notePreimage(baltatharAccount, encodedProposal);773      console.log('Note motion preimage.......DONE');774      // <<< Note motion preimage <<<775776      // >>> Propose external motion through council >>>777      console.log('Propose external motion through council.......');778      const externalMotion = helper.democracy.externalProposeMajority({Legacy: proposalHash});779      const encodedMotion = externalMotion?.method.toHex() || '';780      const motionHash = blake2AsHex(encodedMotion);781      console.log('Motion hash is %s', motionHash);782783      await helper.collective.council.propose(baltatharAccount, councilVotingThreshold, externalMotion, externalMotion.encodedLength);784785      const councilProposalIdx = await helper.collective.council.proposalCount() - 1;786      await helper.collective.council.vote(dorothyAccount, motionHash, councilProposalIdx, true);787      await helper.collective.council.vote(baltatharAccount, motionHash, councilProposalIdx, true);788789      await helper.collective.council.close(790        dorothyAccount,791        motionHash,792        councilProposalIdx,793        {794          refTime: 1_000_000_000,795          proofSize: 1_000_000,796        },797        externalMotion.encodedLength,798      );799      console.log('Propose external motion through council.......DONE');800      // <<< Propose external motion through council <<<801802      // >>> Fast track proposal through technical committee >>>803      console.log('Fast track proposal through technical committee.......');804      const fastTrack = helper.democracy.fastTrack(proposalHash, votingPeriod, delayPeriod);805      const encodedFastTrack = fastTrack?.method.toHex() || '';806      const fastTrackHash = blake2AsHex(encodedFastTrack);807      console.log('FastTrack hash is %s', fastTrackHash);808809      await helper.collective.techCommittee.propose(alithAccount, technicalCommitteeThreshold, fastTrack, fastTrack.encodedLength);810811      const techProposalIdx = await helper.collective.techCommittee.proposalCount() - 1;812      await helper.collective.techCommittee.vote(baltatharAccount, fastTrackHash, techProposalIdx, true);813      await helper.collective.techCommittee.vote(alithAccount, fastTrackHash, techProposalIdx, true);814815      await helper.collective.techCommittee.close(816        baltatharAccount,817        fastTrackHash,818        techProposalIdx,819        {820          refTime: 1_000_000_000,821          proofSize: 1_000_000,822        },823        fastTrack.encodedLength,824      );825      console.log('Fast track proposal through technical committee.......DONE');826      // <<< Fast track proposal through technical committee <<<827828      // >>> Referendum voting >>>829      console.log('Referendum voting.......');830      await helper.democracy.referendumVote(dorothyAccount, 0, {831        balance: 10_000_000_000_000_000_000n,832        vote: {aye: true, conviction: 1},833      });834      console.log('Referendum voting.......DONE');835      // <<< Referendum voting <<<836837      // >>> Acquire Unique AssetId Info on Moonbeam >>>838      console.log('Acquire Unique AssetId Info on Moonbeam.......');839840      // Wait for the democracy execute841      await helper.wait.newBlocks(5);842843      assetId = (await helper.assetManager.assetTypeId(uniqueAssetLocation)).toString();844845      console.log('UNQ asset ID is %s', assetId);846      console.log('Acquire Unique AssetId Info on Moonbeam.......DONE');847      // >>> Acquire Unique AssetId Info on Moonbeam >>>848849      // >>> Sponsoring random Account >>>850      console.log('Sponsoring random Account.......');851      await helper.balance.transferToEthereum(baltatharAccount, randomAccountMoonbeam.address, 11_000_000_000_000_000_000n);852      console.log('Sponsoring random Account.......DONE');853      // <<< Sponsoring random Account <<<854855      balanceGlmrTokenInit = await helper.balance.getEthereum(randomAccountMoonbeam.address);856    });857858    await usingPlaygrounds(async (helper) => {859      await helper.balance.transferToSubstrate(uniqueDonor, randomAccountUnique.address, 10n * TRANSFER_AMOUNT);860      balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccountUnique.address);861    });862  });863864  itSub('Should connect and send UNQ to Moonbeam', async ({helper}) => {865    const currencyId = {866      NativeAssetId: 'Here',867    };868    const dest = {869      V1: {870        parents: 1,871        interior: {872          X2: [873            {Parachain: MOONBEAM_CHAIN},874            {AccountKey20: {network: 'Any', key: randomAccountMoonbeam.address}},875          ],876        },877      },878    };879    const amount = TRANSFER_AMOUNT;880881    await helper.xTokens.transfer(randomAccountUnique, currencyId, amount, dest, 'Unlimited');882883    balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccountUnique.address);884    expect(balanceUniqueTokenMiddle < balanceUniqueTokenInit).to.be.true;885886    const transactionFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;887    console.log('[Unique -> Moonbeam] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(transactionFees));888    expect(transactionFees > 0, 'Negative fees UNQ, looks like nothing was transferred').to.be.true;889890    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {891      await helper.wait.newBlocks(3);892893      balanceGlmrTokenMiddle = await helper.balance.getEthereum(randomAccountMoonbeam.address);894895      const glmrFees = balanceGlmrTokenInit - balanceGlmrTokenMiddle;896      console.log('[Unique -> Moonbeam] transaction fees on Moonbeam: %s GLMR', helper.util.bigIntToDecimals(glmrFees));897      expect(glmrFees == 0n).to.be.true;898899      balanceForeignUnqTokenMiddle = (await helper.assets.account(assetId, randomAccountMoonbeam.address))!;900901      const unqIncomeTransfer = balanceForeignUnqTokenMiddle - balanceForeignUnqTokenInit;902      console.log('[Unique -> Moonbeam] income %s UNQ', helper.util.bigIntToDecimals(unqIncomeTransfer));903      expect(unqIncomeTransfer == TRANSFER_AMOUNT).to.be.true;904    });905  });906907  itSub('Should connect to Moonbeam and send UNQ back', async ({helper}) => {908    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {909      const asset = {910        V1: {911          id: {912            Concrete: {913              parents: 1,914              interior: {915                X1: {Parachain: UNIQUE_CHAIN},916              },917            },918          },919          fun: {920            Fungible: TRANSFER_AMOUNT,921          },922        },923      };924      const destination = {925        V1: {926          parents: 1,927          interior: {928            X2: [929              {Parachain: UNIQUE_CHAIN},930              {AccountId32: {network: 'Any', id: randomAccountUnique.addressRaw}},931            ],932          },933        },934      };935936      await helper.xTokens.transferMultiasset(randomAccountMoonbeam, asset, destination, 'Unlimited');937938      balanceGlmrTokenFinal = await helper.balance.getEthereum(randomAccountMoonbeam.address);939940      const glmrFees = balanceGlmrTokenMiddle - balanceGlmrTokenFinal;941      console.log('[Moonbeam -> Unique] transaction fees on Moonbeam: %s GLMR', helper.util.bigIntToDecimals(glmrFees));942      expect(glmrFees > 0, 'Negative fees GLMR, looks like nothing was transferred').to.be.true;943944      const unqRandomAccountAsset = await helper.assets.account(assetId, randomAccountMoonbeam.address);945946      expect(unqRandomAccountAsset).to.be.null;947948      balanceForeignUnqTokenFinal = 0n;949950      const unqOutcomeTransfer = balanceForeignUnqTokenMiddle - balanceForeignUnqTokenFinal;951      console.log('[Unique -> Moonbeam] outcome %s UNQ', helper.util.bigIntToDecimals(unqOutcomeTransfer));952      expect(unqOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;953    });954955    await helper.wait.newBlocks(3);956957    balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccountUnique.address);958    const actuallyDelivered = balanceUniqueTokenFinal - balanceUniqueTokenMiddle;959    expect(actuallyDelivered > 0).to.be.true;960961    console.log('[Moonbeam -> Unique] actually delivered %s UNQ', helper.util.bigIntToDecimals(actuallyDelivered));962963    const unqFees = TRANSFER_AMOUNT - actuallyDelivered;964    console.log('[Moonbeam -> Unique] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));965    expect(unqFees == 0n).to.be.true;966  });967});