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

difftreelog

test(xcm) unique <-> moonbeam

Daniel Shiposha2022-09-02parent: #2286f73.patch.diff
in: master

1 file changed

modifiedtests/src/xcm/xcmUnique.test.tsdiffbeforeafterboth
before · tests/src/xcm/xcmUnique.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 chai from 'chai';18import chaiAsPromised from 'chai-as-promised';1920import {WsProvider} from '@polkadot/api';21import {ApiOptions} from '@polkadot/api/types';22import {IKeyringPair} from '@polkadot/types/types';23import usingApi, {submitTransactionAsync} from '../substrate/substrate-api';24import {getGenericResult, generateKeyringPair} from '../util/helpers';25import waitNewBlocks from '../substrate/wait-new-blocks';26import getBalance from '../substrate/get-balance';2728chai.use(chaiAsPromised);29const expect = chai.expect;3031const UNIQUE_CHAIN = 2037;32const ACALA_CHAIN = 2000;3334const ACALA_PORT = '9946';3536const TRANSFER_AMOUNT = 2000000000000000000000000n;3738describe('Integration test: Exchanging UNQ with Acala', () => {39  let alice: IKeyringPair;40  let randomAccount: IKeyringPair;41  42  let balanceUniqueTokenBefore: bigint;43  let balanceUniqueTokenAfter: bigint;44  let balanceUniqueTokenFinal: bigint;45  let balanceAcalaTokenBefore: bigint;46  let balanceAcalaTokenAfter: bigint;47  let balanceAcalaTokenFinal: bigint;48  let balanceUniqueForeignTokenAfter: bigint;49  let balanceUniqueForeignTokenBefore: bigint;50  let balanceUniqueForeignTokenFinal: bigint;51  52  before(async () => {53    await usingApi(async (api, privateKeyWrapper) => {54      alice = privateKeyWrapper('//Alice');55      randomAccount = generateKeyringPair();56    });57  58    const acalaApiOptions: ApiOptions = {59      provider: new WsProvider('ws://127.0.0.1:' + ACALA_PORT),60    };61  62    // Acala side63    await usingApi(64      async (api) => {65        const destination = {66          V0: {67            X2: [68              'Parent',69              {70                Parachain: UNIQUE_CHAIN,71              },72            ],73          },74        };75  76        const metadata = {77          name: 'UNQ',78          symbol: 'UNQ',79          decimals: 18,80          minimalBalance: 1,81        };82  83        const tx = api.tx.assetRegistry.registerForeignAsset(destination, metadata);84        const sudoTx = api.tx.sudo.sudo(tx as any);85        const events = await submitTransactionAsync(alice, sudoTx);86        const result = getGenericResult(events);87        expect(result.success).to.be.true;88  89        const tx1 = api.tx.balances.transfer(randomAccount.address, 10000000000000n);90        const events1 = await submitTransactionAsync(alice, tx1);91        const result1 = getGenericResult(events1);92        expect(result1.success).to.be.true;93  94        [balanceAcalaTokenBefore] = await getBalance(api, [randomAccount.address]);95        {96          const {free} = (await api.query.tokens.accounts(randomAccount.addressRaw, {ForeignAsset: 0})).toJSON() as any;97          balanceUniqueForeignTokenBefore = BigInt(free);98        }99      },100      acalaApiOptions,101    );102  103    // Unique side104    await usingApi(async (api) => {105      const tx0 = api.tx.balances.transfer(randomAccount.address, 10n * TRANSFER_AMOUNT);106      const events0 = await submitTransactionAsync(alice, tx0);107      const result0 = getGenericResult(events0);108      expect(result0.success).to.be.true;109  110      [balanceUniqueTokenBefore] = await getBalance(api, [randomAccount.address]);111    });112  });113  114  it('Should connect and send UNQ to Acala', async () => {115  116    // Unique side117    await usingApi(async (api) => {118  119      const destination = {120        V0: {121          X2: [122            'Parent',123            {124              Parachain: ACALA_CHAIN,125            },126          ],127        },128      };129  130      const beneficiary = {131        V0: {132          X1: {133            AccountId32: {134              network: 'Any',135              id: randomAccount.addressRaw,136            },137          },138        },139      };140  141      const assets = {142        V1: [143          {144            id: {145              Concrete: {146                parents: 0,147                interior: 'Here',148              },149            },150            fun: {151              Fungible: TRANSFER_AMOUNT,152            },153          },154        ],155      };156  157      const feeAssetItem = 0;158  159      const weightLimit = {160        Limited: 5000000000,161      };162  163      const tx = api.tx.polkadotXcm.limitedReserveTransferAssets(destination, beneficiary, assets, feeAssetItem, weightLimit);164      const events = await submitTransactionAsync(randomAccount, tx);165      const result = getGenericResult(events);166      expect(result.success).to.be.true;167  168      [balanceUniqueTokenAfter] = await getBalance(api, [randomAccount.address]);169  170      const unqFees = balanceUniqueTokenBefore - balanceUniqueTokenAfter;171      console.log('[Unique -> Acala] transaction fees on Unique: %s UNQ', unqFees);172      expect(unqFees > 0n).to.be.true;173    });174  175    // Acala side176    await usingApi(177      async (api) => {178        // todo do something about instant sealing, where there might not be any new blocks179        await waitNewBlocks(api, 3);180        const {free} = (await api.query.tokens.accounts(randomAccount.addressRaw, {ForeignAsset: 0})).toJSON() as any;181        balanceUniqueForeignTokenAfter = BigInt(free);182  183        [balanceAcalaTokenAfter] = await getBalance(api, [randomAccount.address]);184185        const acaFees = balanceAcalaTokenBefore - balanceAcalaTokenAfter;186        const unqDiffAfterIncomeTransfer = balanceUniqueForeignTokenAfter - balanceUniqueForeignTokenBefore;187188        const unqFees = unqDiffAfterIncomeTransfer - TRANSFER_AMOUNT;189190        console.log('[Unique -> Acala] transaction fees on Acala: %s ACA', acaFees);191        console.log('[Unique -> Acala] transaction fees on Acala: %s UNQ', unqFees);192        expect(acaFees == 0n).to.be.true;193        expect(unqFees == 0n).to.be.true;194      },195      {provider: new WsProvider('ws://127.0.0.1:' + ACALA_PORT)},196    );197  });198  199  it('Should connect to Acala and send UNQ back', async () => {200  201    // Acala side202    await usingApi(203      async (api) => {204        const destination = {205          V1: {206            parents: 1,207            interior: {208              X2: [209                {Parachain: UNIQUE_CHAIN},210                {211                  AccountId32: {212                    network: 'Any',213                    id: randomAccount.addressRaw,214                  },215                },216              ],217            },218          },219        };220  221        const id = {222          ForeignAsset: 0,223        };224  225        const amount = TRANSFER_AMOUNT;226        const destWeight = 50000000;227  228        const tx = api.tx.xTokens.transfer(id, amount, destination, destWeight);229        const events = await submitTransactionAsync(randomAccount, tx);230        const result = getGenericResult(events);231        expect(result.success).to.be.true;232  233        [balanceAcalaTokenFinal] = await getBalance(api, [randomAccount.address]);234        {235          const {free} = (await api.query.tokens.accounts(randomAccount.addressRaw, {ForeignAsset: 0})).toJSON() as any;236          balanceUniqueForeignTokenFinal = BigInt(free);237        }238  239        const acaFees = balanceAcalaTokenAfter - balanceAcalaTokenFinal;240        const unqDiffAfterOutcomeTransfer = balanceUniqueForeignTokenAfter - TRANSFER_AMOUNT;241242        const unqFees = unqDiffAfterOutcomeTransfer - balanceUniqueForeignTokenFinal;243244        console.log('[Acala -> Unique] transaction fees on Acala: %s ACA', acaFees);245        console.log('[Acala -> Unique] transaction fees on Acala: %s UNQ', unqFees);246247        expect(acaFees > 0).to.be.true;248        expect(unqFees == 0n).to.be.true;249      },250      {provider: new WsProvider('ws://127.0.0.1:' + ACALA_PORT)},251    );252253    // Unique side254    await usingApi(async (api) => {255      // todo do something about instant sealing, where there might not be any new blocks256      await waitNewBlocks(api, 3);257  258      [balanceUniqueTokenFinal] = await getBalance(api, [randomAccount.address]);259      const actuallyDelivered = balanceUniqueTokenFinal - balanceUniqueTokenAfter;260      expect(actuallyDelivered > 0).to.be.true;261  262      const unqFees = TRANSFER_AMOUNT - actuallyDelivered;263      console.log('[Acala -> Unique] transaction fees on Unique: %s UNQ', unqFees);264      expect(unqFees == 0n).to.be.true;265    });266  });267});
after · tests/src/xcm/xcmUnique.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 chai from 'chai';18import chaiAsPromised from 'chai-as-promised';1920import {WsProvider, Keyring} from '@polkadot/api';21import {ApiOptions} from '@polkadot/api/types';22import {IKeyringPair} from '@polkadot/types/types';23import usingApi, {submitTransactionAsync} from '../substrate/substrate-api';24import {getGenericResult, generateKeyringPair} from '../util/helpers';25import {MultiLocation} from '@polkadot/types/interfaces';26import {blake2AsHex} from '@polkadot/util-crypto';27import waitNewBlocks from '../substrate/wait-new-blocks';28import getBalance from '../substrate/get-balance';2930chai.use(chaiAsPromised);31const expect = chai.expect;3233const UNIQUE_CHAIN = 2037;34const ACALA_CHAIN = 2000;35const MOONBEAM_CHAIN = 2004;3637const ACALA_PORT = 9946;38const MOONBEAM_PORT = 9947;3940const TRANSFER_AMOUNT = 2000000000000000000000000n;4142function parachainApiOptions(port: number): ApiOptions {43  return {44    provider: new WsProvider('ws://127.0.0.1:' + port.toString()),45  };46}4748function acalaOptions(): ApiOptions {49  return parachainApiOptions(ACALA_PORT);50}5152function moonbeamOptions(): ApiOptions {53  return parachainApiOptions(MOONBEAM_PORT);54}5556describe('Integration test: Exchanging UNQ with Acala', () => {57  let alice: IKeyringPair;58  let randomAccount: IKeyringPair;59  60  let balanceUniqueTokenInit: bigint;61  let balanceUniqueTokenMiddle: bigint;62  let balanceUniqueTokenFinal: bigint;63  let balanceAcalaTokenInit: bigint;64  let balanceAcalaTokenMiddle: bigint;65  let balanceAcalaTokenFinal: bigint;66  let balanceUniqueForeignTokenInit: bigint;67  let balanceUniqueForeignTokenMiddle: bigint;68  let balanceUniqueForeignTokenFinal: bigint;69  70  before(async () => {71    await usingApi(async (api, privateKeyWrapper) => {72      alice = privateKeyWrapper('//Alice');73      randomAccount = generateKeyringPair();74    });7576    // Acala side77    await usingApi(78      async (api) => {79        const destination = {80          V0: {81            X2: [82              'Parent',83              {84                Parachain: UNIQUE_CHAIN,85              },86            ],87          },88        };89  90        const metadata = {91          name: 'UNQ',92          symbol: 'UNQ',93          decimals: 18,94          minimalBalance: 1,95        };96  97        const tx = api.tx.assetRegistry.registerForeignAsset(destination, metadata);98        const sudoTx = api.tx.sudo.sudo(tx as any);99        const events = await submitTransactionAsync(alice, sudoTx);100        const result = getGenericResult(events);101        expect(result.success).to.be.true;102  103        const tx1 = api.tx.balances.transfer(randomAccount.address, 10000000000000n);104        const events1 = await submitTransactionAsync(alice, tx1);105        const result1 = getGenericResult(events1);106        expect(result1.success).to.be.true;107  108        [balanceAcalaTokenInit] = await getBalance(api, [randomAccount.address]);109        {110          const {free} = (await api.query.tokens.accounts(randomAccount.addressRaw, {ForeignAsset: 0})).toJSON() as any;111          balanceUniqueForeignTokenInit = BigInt(free);112        }113      },114      acalaOptions(),115    );116  117    // Unique side118    await usingApi(async (api) => {119      const tx0 = api.tx.balances.transfer(randomAccount.address, 10n * TRANSFER_AMOUNT);120      const events0 = await submitTransactionAsync(alice, tx0);121      const result0 = getGenericResult(events0);122      expect(result0.success).to.be.true;123  124      [balanceUniqueTokenInit] = await getBalance(api, [randomAccount.address]);125    });126  });127  128  it('Should connect and send UNQ to Acala', async () => {129  130    // Unique side131    await usingApi(async (api) => {132  133      const destination = {134        V0: {135          X2: [136            'Parent',137            {138              Parachain: ACALA_CHAIN,139            },140          ],141        },142      };143  144      const beneficiary = {145        V0: {146          X1: {147            AccountId32: {148              network: 'Any',149              id: randomAccount.addressRaw,150            },151          },152        },153      };154  155      const assets = {156        V1: [157          {158            id: {159              Concrete: {160                parents: 0,161                interior: 'Here',162              },163            },164            fun: {165              Fungible: TRANSFER_AMOUNT,166            },167          },168        ],169      };170  171      const feeAssetItem = 0;172  173      const weightLimit = {174        Limited: 5000000000,175      };176  177      const tx = api.tx.polkadotXcm.limitedReserveTransferAssets(destination, beneficiary, assets, feeAssetItem, weightLimit);178      const events = await submitTransactionAsync(randomAccount, tx);179      const result = getGenericResult(events);180      expect(result.success).to.be.true;181  182      [balanceUniqueTokenMiddle] = await getBalance(api, [randomAccount.address]);183  184      const unqFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle + TRANSFER_AMOUNT;185      console.log('[Unique -> Acala] transaction fees on Unique: %s UNQ', unqFees);186      expect(unqFees > 0n).to.be.true;187    });188  189    // Acala side190    await usingApi(191      async (api) => {192        await waitNewBlocks(api, 3);193        const {free} = (await api.query.tokens.accounts(randomAccount.addressRaw, {ForeignAsset: 0})).toJSON() as any;194        balanceUniqueForeignTokenMiddle = BigInt(free);195  196        [balanceAcalaTokenMiddle] = await getBalance(api, [randomAccount.address]);197198        const acaFees = balanceAcalaTokenInit - balanceAcalaTokenMiddle;199        const unqIncomeTransfer = balanceUniqueForeignTokenMiddle - balanceUniqueForeignTokenInit;200201        console.log('[Unique -> Acala] transaction fees on Acala: %s ACA', acaFees);202        console.log('[Unique -> Acala] income %s UNQ', unqIncomeTransfer);203        expect(acaFees == 0n).to.be.true;204        expect(unqIncomeTransfer == TRANSFER_AMOUNT).to.be.true;205      },206      acalaOptions(),207    );208  });209  210  it('Should connect to Acala and send UNQ back', async () => {211  212    // Acala side213    await usingApi(214      async (api) => {215        const destination = {216          V1: {217            parents: 1,218            interior: {219              X2: [220                {Parachain: UNIQUE_CHAIN},221                {222                  AccountId32: {223                    network: 'Any',224                    id: randomAccount.addressRaw,225                  },226                },227              ],228            },229          },230        };231  232        const id = {233          ForeignAsset: 0,234        };235236        const destWeight = 50000000;237  238        const tx = api.tx.xTokens.transfer(id, TRANSFER_AMOUNT, destination, destWeight);239        const events = await submitTransactionAsync(randomAccount, tx);240        const result = getGenericResult(events);241        expect(result.success).to.be.true;242  243        [balanceAcalaTokenFinal] = await getBalance(api, [randomAccount.address]);244        {245          const {free} = (await api.query.tokens.accounts(randomAccount.addressRaw, {ForeignAsset: 0})).toJSON() as any;246          balanceUniqueForeignTokenFinal = BigInt(free);247        }248  249        const acaFees = balanceAcalaTokenMiddle - balanceAcalaTokenFinal;250        const unqOutcomeTransfer = balanceUniqueForeignTokenMiddle - balanceUniqueForeignTokenFinal;251252        console.log('[Acala -> Unique] transaction fees on Acala: %s ACA', acaFees);253        console.log('[Acala -> Unique] outcome %s UNQ', unqOutcomeTransfer);254255        expect(acaFees > 0).to.be.true;256        expect(unqOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;257      },258      acalaOptions(),259    );260261    // Unique side262    await usingApi(async (api) => {263      await waitNewBlocks(api, 3);264  265      [balanceUniqueTokenFinal] = await getBalance(api, [randomAccount.address]);266      const actuallyDelivered = balanceUniqueTokenFinal - balanceUniqueTokenMiddle;267      expect(actuallyDelivered > 0).to.be.true;268269      console.log('[Acala -> Unique] actually delivered %s UNQ', actuallyDelivered);270  271      const unqFees = TRANSFER_AMOUNT - actuallyDelivered;272      console.log('[Acala -> Unique] transaction fees on Unique: %s UNQ', unqFees);273      expect(unqFees == 0n).to.be.true;274    });275  });276});277278describe('Integration test: Exchanging UNQ with Moonbeam', () => {279280  // Unique constants281  let uniqueAlice: IKeyringPair;282  let uniqueAssetLocation;283284  let randomAccountUnique: IKeyringPair;285  let randomAccountMoonbeam: IKeyringPair;286287  // Moonbeam constants288  let assetId: string;289290  const moonbeamKeyring = new Keyring({type: 'ethereum'});291  const alithPrivateKey = '0x5fb92d6e98884f76de468fa3f6278f8807c48bebc13595d45af5bdc4da702133';292  const baltatharPrivateKey = '0x8075991ce870b93a8870eca0c0f91913d12f47948ca0fd25b49c6fa7cdbeee8b';293  const dorothyPrivateKey = '0x39539ab1876910bbf3a223d84a29e28f1cb4e2e456503e7e91ed39b2e7223d68';294295  const alithAccount = moonbeamKeyring.addFromUri(alithPrivateKey, undefined, 'ethereum');296  const baltatharAccount = moonbeamKeyring.addFromUri(baltatharPrivateKey, undefined, 'ethereum');297  const dorothyAccount = moonbeamKeyring.addFromUri(dorothyPrivateKey, undefined, 'ethereum');298299  const councilVotingThreshold = 2;300  const technicalCommitteeThreshold = 2;301  const votingPeriod = 3;302  const delayPeriod = 0;303304  const uniqueAssetMetadata = {305    name: 'xcUnique',306    symbol: 'xcUNQ',307    decimals: 18,308    isFrozen: false,309    minimalBalance: 1,310  };311312  let balanceUniqueTokenInit: bigint;313  let balanceUniqueTokenMiddle: bigint;314  let balanceUniqueTokenFinal: bigint;315  let balanceForeignUnqTokenInit: bigint;316  let balanceForeignUnqTokenMiddle: bigint;317  let balanceForeignUnqTokenFinal: bigint;318  let balanceGlmrTokenInit: bigint;319  let balanceGlmrTokenMiddle: bigint;320  let balanceGlmrTokenFinal: bigint;321322  before(async () => {323    await usingApi(async (api, privateKeyWrapper) => {324      uniqueAlice = privateKeyWrapper('//Alice');325      randomAccountUnique = generateKeyringPair();326      randomAccountMoonbeam = generateKeyringPair('ethereum');327328      balanceForeignUnqTokenInit = 0n;329    });330331    await usingApi(332      async (api) => {333334        // >>> Sponsoring Dorothy >>>335        console.log('Sponsoring Dorothy.......');336        const tx0 = api.tx.balances.transfer(dorothyAccount.address, 11_000_000_000_000_000_000n);337        const events0 = await submitTransactionAsync(alithAccount, tx0);338        const result0 = getGenericResult(events0);339        expect(result0.success).to.be.true;340        console.log('Sponsoring Dorothy.......DONE');341        // <<< Sponsoring Dorothy <<<342343        const sourceLocation: MultiLocation = api.createType(344          'MultiLocation',345          {346            parents: 1,347            interior: {X1: {Parachain: UNIQUE_CHAIN}},348          },349        );350351        uniqueAssetLocation = {XCM: sourceLocation};352        const existentialDeposit = 1;353        const isSufficient = true;354        const unitsPerSecond = '1';355        const numAssetsWeightHint = 0;356357        const registerTx = api.tx.assetManager.registerForeignAsset(358          uniqueAssetLocation,359          uniqueAssetMetadata,360          existentialDeposit,361          isSufficient,362        );363        console.log('Encoded proposal for registerAsset is %s', registerTx.method.toHex() || '');364365        const setUnitsTx = api.tx.assetManager.setAssetUnitsPerSecond(366          uniqueAssetLocation,367          unitsPerSecond,368          numAssetsWeightHint,369        );370        console.log('Encoded proposal for setAssetUnitsPerSecond is %s', setUnitsTx.method.toHex() || '');371372        const batchCall = api.tx.utility.batchAll([registerTx, setUnitsTx]);373        console.log('Encoded proposal for batchCall is %s', batchCall.method.toHex() || '');374375        // >>> Note motion preimage >>>376        console.log('Note motion preimage.......');377        const encodedProposal = batchCall?.method.toHex() || '';378        const proposalHash = blake2AsHex(encodedProposal);379        console.log('Encoded proposal for batch utility after schedule is %s', encodedProposal);380        console.log('Encoded proposal hash for batch utility after schedule is %s', proposalHash);381        console.log('Encoded length %d', encodedProposal.length);382383        const tx1 = api.tx.democracy.notePreimage(encodedProposal);384        const events1 = await submitTransactionAsync(baltatharAccount, tx1);385        const result1 = getGenericResult(events1);386        expect(result1.success).to.be.true;387        console.log('Note motion preimage.......DONE');388        // <<< Note motion preimage <<<389390        // >>> Propose external motion through council >>>391        console.log('Propose external motion through council.......');392        const externalMotion = api.tx.democracy.externalProposeMajority(proposalHash);393        const tx2 = api.tx.councilCollective.propose(394          councilVotingThreshold,395          externalMotion,396          externalMotion.encodedLength,397        );398        const events2 = await submitTransactionAsync(baltatharAccount, tx2);399        const result2 = getGenericResult(events2);400        expect(result2.success).to.be.true;401402        const encodedMotion = externalMotion?.method.toHex() || '';403        const motionHash = blake2AsHex(encodedMotion);404        console.log('Motion hash is %s', motionHash);405406        const tx3 = api.tx.councilCollective.vote(motionHash, 0, true);407        {408          const events3 = await submitTransactionAsync(dorothyAccount, tx3);409          const result3 = getGenericResult(events3);410          expect(result3.success).to.be.true;411        }412        {413          const events3 = await submitTransactionAsync(baltatharAccount, tx3);414          const result3 = getGenericResult(events3);415          expect(result3.success).to.be.true;416        }417418        const tx4 = api.tx.councilCollective.close(motionHash, 0, 1_000_000_000, externalMotion.encodedLength);419        const events4 = await submitTransactionAsync(dorothyAccount, tx4);420        const result4 = getGenericResult(events4);421        expect(result4.success).to.be.true;422        console.log('Propose external motion through council.......DONE');423        // <<< Propose external motion through council <<<424425        // >>> Fast track proposal through technical committee >>>426        console.log('Fast track proposal through technical committee.......');427        const fastTrack = api.tx.democracy.fastTrack(proposalHash, votingPeriod, delayPeriod);428        const tx5 = api.tx.techCommitteeCollective.propose(429          technicalCommitteeThreshold,430          fastTrack,431          fastTrack.encodedLength,432        );433        const events5 = await submitTransactionAsync(alithAccount, tx5);434        const result5 = getGenericResult(events5);435        expect(result5.success).to.be.true;436437        const encodedFastTrack = fastTrack?.method.toHex() || '';438        const fastTrackHash = blake2AsHex(encodedFastTrack);439        console.log('FastTrack hash is %s', fastTrackHash);440441        const proposalIdx = Number(await api.query.techCommitteeCollective.proposalCount()) - 1;442        const tx6 = api.tx.techCommitteeCollective.vote(fastTrackHash, proposalIdx, true);443        {444          const events6 = await submitTransactionAsync(baltatharAccount, tx6);445          const result6 = getGenericResult(events6);446          expect(result6.success).to.be.true;447        }448        {449          const events6 = await submitTransactionAsync(alithAccount, tx6);450          const result6 = getGenericResult(events6);451          expect(result6.success).to.be.true;452        }453454        const tx7 = api.tx.techCommitteeCollective455          .close(fastTrackHash, proposalIdx, 1_000_000_000, fastTrack.encodedLength);456        const events7 = await submitTransactionAsync(baltatharAccount, tx7);457        const result7 = getGenericResult(events7);458        expect(result7.success).to.be.true;459        console.log('Fast track proposal through technical committee.......DONE');460        // <<< Fast track proposal through technical committee <<<461462        // >>> Referendum voting >>>463        console.log('Referendum voting.......');464        const tx8 = api.tx.democracy.vote(465          0,466          {Standard: {balance: 10_000_000_000_000_000_000n, vote: {aye: true, conviction: 1}}},467        );468        const events8 = await submitTransactionAsync(dorothyAccount, tx8);469        const result8 = getGenericResult(events8);470        expect(result8.success).to.be.true;471        console.log('Referendum voting.......DONE');472        // <<< Referendum voting <<<473474        // >>> Acquire Unique AssetId Info on Moonbeam >>>475        console.log('Acquire Unique AssetId Info on Moonbeam.......');476477        // Wait for the democracy execute478        await waitNewBlocks(api, 5);479480        assetId = (await api.query.assetManager.assetTypeId({481          XCM: sourceLocation,482        })).toString();483484        console.log('UNQ asset ID is %s', assetId);485        console.log('Acquire Unique AssetId Info on Moonbeam.......DONE');486        // >>> Acquire Unique AssetId Info on Moonbeam >>>487488        // >>> Sponsoring random Account >>>489        console.log('Sponsoring random Account.......');490        const tx10 = api.tx.balances.transfer(randomAccountMoonbeam.address, 11_000_000_000_000_000_000n);491        const events10 = await submitTransactionAsync(baltatharAccount, tx10);492        const result10 = getGenericResult(events10);493        expect(result10.success).to.be.true;494        console.log('Sponsoring random Account.......DONE');495        // <<< Sponsoring random Account <<<496497        [balanceGlmrTokenInit] = await getBalance(api, [randomAccountMoonbeam.address]);498      },499      moonbeamOptions(),500    );501502    await usingApi(async (api) => {503      const tx0 = api.tx.balances.transfer(randomAccountUnique.address, 10n * TRANSFER_AMOUNT);504      const events0 = await submitTransactionAsync(uniqueAlice, tx0);505      const result0 = getGenericResult(events0);506      expect(result0.success).to.be.true;507508      [balanceUniqueTokenInit] = await getBalance(api, [randomAccountUnique.address]);509    });510  });511512  it('Should connect and send UNQ to Moonbeam', async () => {513    await usingApi(async (api) => {514      const currencyId = {515        NativeAssetId: 'Here',516      };517      const dest = {518        V1: {519          parents: 1,520          interior: {521            X2: [522              {Parachain: MOONBEAM_CHAIN},523              {AccountKey20: {network: 'Any', key: randomAccountMoonbeam.address}},524            ],525          },526        },527      };528      const amount = TRANSFER_AMOUNT;529      const destWeight = 850000000;530531      const tx = api.tx.xTokens.transfer(currencyId, amount, dest, destWeight);532      const events = await submitTransactionAsync(randomAccountUnique, tx);533      const result = getGenericResult(events);534      expect(result.success).to.be.true;535536      [balanceUniqueTokenMiddle] = await getBalance(api, [randomAccountUnique.address]);537      expect(balanceUniqueTokenMiddle < balanceUniqueTokenInit).to.be.true;538539      const transactionFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle + TRANSFER_AMOUNT;540      console.log('[Unique -> Moonbeam] transaction fees on Unique: %s UNQ', transactionFees);541      expect(transactionFees > 0).to.be.true;542    });543544    await usingApi(545      async (api) => {546        await waitNewBlocks(api, 3);547548        [balanceGlmrTokenMiddle] = await getBalance(api, [randomAccountMoonbeam.address]);549550        const glmrFees = balanceGlmrTokenInit - balanceGlmrTokenMiddle;551        console.log('[Unique -> Moonbeam] transaction fees on Moonbeam: %s GLMR', glmrFees);552        expect(glmrFees == 0n).to.be.true;553554        const unqRandomAccountAsset = (555          await api.query.assets.account(assetId, randomAccountMoonbeam.address)556        ).toJSON()! as any;557558        balanceForeignUnqTokenMiddle = BigInt(unqRandomAccountAsset['balance']);559        const unqIncomeTransfer = balanceForeignUnqTokenMiddle - balanceForeignUnqTokenInit;560        console.log('[Unique -> Moonbeam] income %s UNQ', unqIncomeTransfer);561        expect(unqIncomeTransfer == TRANSFER_AMOUNT).to.be.true;562      },563      moonbeamOptions(),564    );565  });566567  it('Should connect to Moonbeam and send UNQ back', async () => {568    await usingApi(569      async (api) => {570        const asset = {571          V1: {572            id: {573              Concrete: {574                parents: 1,575                interior: {576                  X1: {Parachain: UNIQUE_CHAIN},577                },578              },579            },580            fun: {581              Fungible: TRANSFER_AMOUNT,582            },583          },584        };585        const destination = {586          V1: {587            parents: 1,588            interior: {589              X2: [590                {Parachain: UNIQUE_CHAIN},591                {AccountId32: {network: 'Any', id: randomAccountUnique.addressRaw}},592              ],593            },594          },595        };596        const destWeight = 50000000;597598        const tx = api.tx.xTokens.transferMultiasset(asset, destination, destWeight);599        const events = await submitTransactionAsync(randomAccountMoonbeam, tx);600        const result = getGenericResult(events);601        expect(result.success).to.be.true;602603        [balanceGlmrTokenFinal] = await getBalance(api, [randomAccountMoonbeam.address]);604605        const glmrFees = balanceGlmrTokenMiddle - balanceGlmrTokenFinal;606        console.log('[Moonbeam -> Unique] transaction fees on Moonbeam: %s GLMR', glmrFees);607        expect(glmrFees > 0).to.be.true;608609        const unqRandomAccountAsset = (610          await api.query.assets.account(assetId, randomAccountMoonbeam.address)611        ).toJSON()! as any;612613        expect(unqRandomAccountAsset).to.be.null;614615        balanceForeignUnqTokenFinal = 0n;616617        const unqOutcomeTransfer = balanceForeignUnqTokenMiddle - balanceForeignUnqTokenFinal;618        console.log('[Unique -> Moonbeam] outcome %s UNQ', unqOutcomeTransfer);619        expect(unqOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;620      },621      moonbeamOptions(),622    );623624    await usingApi(async (api) => {625      await waitNewBlocks(api, 3);626627      [balanceUniqueTokenFinal] = await getBalance(api, [randomAccountUnique.address]);628      const actuallyDelivered = balanceUniqueTokenFinal - balanceUniqueTokenMiddle;629      expect(actuallyDelivered > 0).to.be.true;630631      console.log('[Moonbeam -> Unique] actually delivered %s UNQ', actuallyDelivered);632633      const unqFees = TRANSFER_AMOUNT - actuallyDelivered;634      console.log('[Moonbeam -> Unique] transaction fees on Unique: %s UNQ', unqFees);635      expect(unqFees == 0n).to.be.true;636    });637  });638});