git.delta.rocks / unique-network / refs/commits / 0f81eccfefee

difftreelog

source

tests/src/xcm/xcmUnique.test.ts20.6 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, XcmV2TraitsOutcome} from '../interfaces';21import {itSub, expect, describeXCM, usingPlaygrounds, usingAcalaPlaygrounds, usingRelayPlaygrounds, usingMoonbeamPlaygrounds} from '../util';2223const UNIQUE_CHAIN = 2037;24const ACALA_CHAIN = 2000;25const MOONBEAM_CHAIN = 2004;2627const relayUrl = config.relayUrl;28const acalaUrl = config.acalaUrl;29const moonbeamUrl = config.moonbeamUrl;3031const ACALA_DECIMALS = 12;3233const TRANSFER_AMOUNT = 2000000000000000000000000n;3435describeXCM('[XCM] Integration test: Exchanging tokens with Acala', () => {36  let alice: IKeyringPair;37  let randomAccount: IKeyringPair;3839  let balanceUniqueTokenInit: bigint;40  let balanceUniqueTokenMiddle: bigint;41  let balanceUniqueTokenFinal: bigint;42  let balanceAcalaTokenInit: bigint;43  let balanceAcalaTokenMiddle: bigint;44  let balanceAcalaTokenFinal: bigint;45  let balanceUniqueForeignTokenInit: bigint;46  let balanceUniqueForeignTokenMiddle: bigint;47  let balanceUniqueForeignTokenFinal: bigint;4849  before(async () => {50    await usingPlaygrounds(async (helper, privateKey) => {51      alice = await privateKey('//Alice');52      [randomAccount] = await helper.arrange.createAccounts([0n], alice);53    });5455    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {56      const destination = {57        V0: {58          X2: [59            'Parent',60            {61              Parachain: UNIQUE_CHAIN,62            },63          ],64        },65      };6667      const metadata = {68        name: 'UNQ',69        symbol: 'UNQ',70        decimals: 18,71        minimalBalance: 1n,72      };7374      await helper.getSudo().assetRegistry.registerForeignAsset(alice, destination, metadata);75      await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n);76      balanceAcalaTokenInit = await helper.balance.getSubstrate(randomAccount.address);77      balanceUniqueForeignTokenInit = await helper.tokens.accounts(randomAccount.address, {ForeignAsset: 0});78    });7980    await usingPlaygrounds(async (helper) => {81      await helper.balance.transferToSubstrate(alice, randomAccount.address, 10n * TRANSFER_AMOUNT);82      balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccount.address);83    });84  });8586  itSub('Should connect and send UNQ to Acala', async ({helper}) => {8788    const destination = {89      V0: {90        X2: [91          'Parent',92          {93            Parachain: ACALA_CHAIN,94          },95        ],96      },97    };9899    const beneficiary = {100      V0: {101        X1: {102          AccountId32: {103            network: 'Any',104            id: randomAccount.addressRaw,105          },106        },107      },108    };109110    const assets = {111      V1: [112        {113          id: {114            Concrete: {115              parents: 0,116              interior: 'Here',117            },118          },119          fun: {120            Fungible: TRANSFER_AMOUNT,121          },122        },123      ],124    };125126    const feeAssetItem = 0;127    const weightLimit = 5000000000;128129    await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, weightLimit);130131    balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);132133    const unqFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;134    console.log('[Unique -> Acala] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));135    expect(unqFees > 0n).to.be.true;136137    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {138      await helper.wait.newBlocks(3);139140      balanceUniqueForeignTokenMiddle = await helper.tokens.accounts(randomAccount.address, {ForeignAsset: 0});141      balanceAcalaTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);142143      const acaFees = balanceAcalaTokenInit - balanceAcalaTokenMiddle;144      const unqIncomeTransfer = balanceUniqueForeignTokenMiddle - balanceUniqueForeignTokenInit;145146      console.log(147        '[Unique -> Acala] transaction fees on Acala: %s ACA',148        helper.util.bigIntToDecimals(acaFees, ACALA_DECIMALS),149      );150      console.log('[Unique -> Acala] income %s UNQ', helper.util.bigIntToDecimals(unqIncomeTransfer));151      expect(acaFees == 0n).to.be.true;152      expect(unqIncomeTransfer == TRANSFER_AMOUNT).to.be.true;153    });154  });155156  itSub('Should connect to Acala and send UNQ back', async ({helper}) => {157    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {158      const destination = {159        V1: {160          parents: 1,161          interior: {162            X2: [163              {Parachain: UNIQUE_CHAIN},164              {165                AccountId32: {166                  network: 'Any',167                  id: randomAccount.addressRaw,168                },169              },170            ],171          },172        },173      };174175      const id = {176        ForeignAsset: 0,177      };178179      const destWeight = 50000000;180181      await helper.xTokens.transfer(randomAccount, id, TRANSFER_AMOUNT, destination, destWeight);182183      balanceAcalaTokenFinal = await helper.balance.getSubstrate(randomAccount.address);184      balanceUniqueForeignTokenFinal = await helper.tokens.accounts(randomAccount.address, id);185186      const acaFees = balanceAcalaTokenMiddle - balanceAcalaTokenFinal;187      const unqOutcomeTransfer = balanceUniqueForeignTokenMiddle - balanceUniqueForeignTokenFinal;188189      console.log(190        '[Acala -> Unique] transaction fees on Acala: %s ACA',191        helper.util.bigIntToDecimals(acaFees, ACALA_DECIMALS),192      );193      console.log('[Acala -> Unique] outcome %s UNQ', helper.util.bigIntToDecimals(unqOutcomeTransfer));194195      expect(acaFees > 0).to.be.true;196      expect(unqOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;197    });198199    await helper.wait.newBlocks(3);200201    balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccount.address);202    const actuallyDelivered = balanceUniqueTokenFinal - balanceUniqueTokenMiddle;203    expect(actuallyDelivered > 0).to.be.true;204205    console.log('[Acala -> Unique] actually delivered %s UNQ', helper.util.bigIntToDecimals(actuallyDelivered));206207    const unqFees = TRANSFER_AMOUNT - actuallyDelivered;208    console.log('[Acala -> Unique] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));209    expect(unqFees == 0n).to.be.true;210  });211});212213// These tests are relevant only when the foreign asset pallet is disabled214describeXCM('[XCM] Integration test: Unique rejects non-native tokens', () => {215  let alice: IKeyringPair;216217  before(async () => {218    await usingPlaygrounds(async (_helper, privateKey) => {219      alice = await privateKey('//Alice');220    });221  });222223  itSub('Unique rejects tokens from the Relay', async ({helper}) => {224    await usingRelayPlaygrounds(relayUrl, async (helper) => {225      const destination = {226        V1: {227          parents: 0,228          interior: {X1: {229            Parachain: UNIQUE_CHAIN,230          },231          },232        }};233234      const beneficiary = {235        V1: {236          parents: 0,237          interior: {X1: {238            AccountId32: {239              network: 'Any',240              id: alice.addressRaw,241            },242          }},243        },244      };245246      const assets = {247        V1: [248          {249            id: {250              Concrete: {251                parents: 0,252                interior: 'Here',253              },254            },255            fun: {256              Fungible: 50_000_000_000_000_000n,257            },258          },259        ],260      };261262      const feeAssetItem = 0;263      const weightLimit = 5_000_000_000;264265      await helper.xcm.limitedReserveTransferAssets(alice, destination, beneficiary, assets, feeAssetItem, weightLimit);266    });267268    const maxWaitBlocks = 3;269270    const dmpQueueExecutedDownward = await helper.wait.event(maxWaitBlocks, 'dmpQueue', 'ExecutedDownward');271272    expect(273      dmpQueueExecutedDownward != null,274      '[Relay] dmpQueue.ExecutedDownward event is expected',275    ).to.be.true;276277    const event = dmpQueueExecutedDownward!.event;278    const outcome = event.data[1] as XcmV2TraitsOutcome;279280    expect(281      outcome.isIncomplete,282      '[Relay] The outcome of the XCM should be `Incomplete`',283    ).to.be.true;284285    const incomplete = outcome.asIncomplete;286    expect(287      incomplete[1].toString() == 'AssetNotFound',288      '[Relay] The XCM error should be `AssetNotFound`',289    ).to.be.true;290  });291292  itSub('Unique rejects ACA tokens from Acala', async ({helper}) => {293    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {294      const destination = {295        V1: {296          parents: 1,297          interior: {298            X2: [299              {Parachain: UNIQUE_CHAIN},300              {301                AccountId32: {302                  network: 'Any',303                  id: alice.addressRaw,304                },305              },306            ],307          },308        },309      };310311      const id = {312        Token: 'ACA',313      };314315      const destWeight = 50000000;316317      await helper.xTokens.transfer(alice, id, 100_000_000_000n, destination, destWeight);318    });319320    const maxWaitBlocks = 3;321322    const xcmpQueueFailEvent = await helper.wait.event(maxWaitBlocks, 'xcmpQueue', 'Fail');323324    expect(325      xcmpQueueFailEvent != null,326      '[Acala] xcmpQueue.FailEvent event is expected',327    ).to.be.true;328329    const event = xcmpQueueFailEvent!.event;330    const outcome = event.data[1] as XcmV2TraitsError;331332    expect(333      outcome.isUntrustedReserveLocation,334      '[Acala] The XCM error should be `UntrustedReserveLocation`',335    ).to.be.true;336  });337});338339describeXCM('[XCM] Integration test: Exchanging UNQ with Moonbeam', () => {340  // Unique constants341  let uniqueDonor: IKeyringPair;342  let uniqueAssetLocation;343344  let randomAccountUnique: IKeyringPair;345  let randomAccountMoonbeam: IKeyringPair;346347  // Moonbeam constants348  let assetId: string;349350  const councilVotingThreshold = 2;351  const technicalCommitteeThreshold = 2;352  const votingPeriod = 3;353  const delayPeriod = 0;354355  const uniqueAssetMetadata = {356    name: 'xcUnique',357    symbol: 'xcUNQ',358    decimals: 18,359    isFrozen: false,360    minimalBalance: 1n,361  };362363  let balanceUniqueTokenInit: bigint;364  let balanceUniqueTokenMiddle: bigint;365  let balanceUniqueTokenFinal: bigint;366  let balanceForeignUnqTokenInit: bigint;367  let balanceForeignUnqTokenMiddle: bigint;368  let balanceForeignUnqTokenFinal: bigint;369  let balanceGlmrTokenInit: bigint;370  let balanceGlmrTokenMiddle: bigint;371  let balanceGlmrTokenFinal: bigint;372373  before(async () => {374    await usingPlaygrounds(async (helper, privateKey) => {375      uniqueDonor = await privateKey('//Alice');376      [randomAccountUnique] = await helper.arrange.createAccounts([0n], uniqueDonor);377378      balanceForeignUnqTokenInit = 0n;379    });380381    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {382      const alithAccount = helper.account.alithAccount();383      const baltatharAccount = helper.account.baltatharAccount();384      const dorothyAccount = helper.account.dorothyAccount();385386      randomAccountMoonbeam = helper.account.create();387388      // >>> Sponsoring Dorothy >>>389      console.log('Sponsoring Dorothy.......');390      await helper.balance.transferToEthereum(alithAccount, dorothyAccount.address, 11_000_000_000_000_000_000n);391      console.log('Sponsoring Dorothy.......DONE');392      // <<< Sponsoring Dorothy <<<393394      uniqueAssetLocation = {395        XCM: {396          parents: 1,397          interior: {X1: {Parachain: UNIQUE_CHAIN}},398        },399      };400      const existentialDeposit = 1n;401      const isSufficient = true;402      const unitsPerSecond = 1n;403      const numAssetsWeightHint = 0;404405      const encodedProposal = helper.assetManager.makeRegisterForeignAssetProposal({406        location: uniqueAssetLocation,407        metadata: uniqueAssetMetadata,408        existentialDeposit,409        isSufficient,410        unitsPerSecond,411        numAssetsWeightHint,412      });413      const proposalHash = blake2AsHex(encodedProposal);414415      console.log('Encoded proposal for registerForeignAsset & setAssetUnitsPerSecond is %s', encodedProposal);416      console.log('Encoded length %d', encodedProposal.length);417      console.log('Encoded proposal hash for batch utility after schedule is %s', proposalHash);418419      // >>> Note motion preimage >>>420      console.log('Note motion preimage.......');421      await helper.democracy.notePreimage(baltatharAccount, encodedProposal);422      console.log('Note motion preimage.......DONE');423      // <<< Note motion preimage <<<424425      // >>> Propose external motion through council >>>426      console.log('Propose external motion through council.......');427      const externalMotion = helper.democracy.externalProposeMajority(proposalHash);428      const encodedMotion = externalMotion?.method.toHex() || '';429      const motionHash = blake2AsHex(encodedMotion);430      console.log('Motion hash is %s', motionHash);431432      await helper.collective.council.propose(baltatharAccount, councilVotingThreshold, externalMotion, externalMotion.encodedLength);433434      const councilProposalIdx = await helper.collective.council.proposalCount() - 1;435      await helper.collective.council.vote(dorothyAccount, motionHash, councilProposalIdx, true);436      await helper.collective.council.vote(baltatharAccount, motionHash, councilProposalIdx, true);437438      await helper.collective.council.close(dorothyAccount, motionHash, councilProposalIdx, 1_000_000_000, externalMotion.encodedLength);439      console.log('Propose external motion through council.......DONE');440      // <<< Propose external motion through council <<<441442      // >>> Fast track proposal through technical committee >>>443      console.log('Fast track proposal through technical committee.......');444      const fastTrack = helper.democracy.fastTrack(proposalHash, votingPeriod, delayPeriod);445      const encodedFastTrack = fastTrack?.method.toHex() || '';446      const fastTrackHash = blake2AsHex(encodedFastTrack);447      console.log('FastTrack hash is %s', fastTrackHash);448449      await helper.collective.techCommittee.propose(alithAccount, technicalCommitteeThreshold, fastTrack, fastTrack.encodedLength);450451      const techProposalIdx = await helper.collective.techCommittee.proposalCount() - 1;452      await helper.collective.techCommittee.vote(baltatharAccount, fastTrackHash, techProposalIdx, true);453      await helper.collective.techCommittee.vote(alithAccount, fastTrackHash, techProposalIdx, true);454455      await helper.collective.techCommittee.close(baltatharAccount, fastTrackHash, techProposalIdx, 1_000_000_000, fastTrack.encodedLength);456      console.log('Fast track proposal through technical committee.......DONE');457      // <<< Fast track proposal through technical committee <<<458459      // >>> Referendum voting >>>460      console.log('Referendum voting.......');461      await helper.democracy.referendumVote(dorothyAccount, 0, {462        balance: 10_000_000_000_000_000_000n,463        vote: {aye: true, conviction: 1},464      });465      console.log('Referendum voting.......DONE');466      // <<< Referendum voting <<<467468      // >>> Acquire Unique AssetId Info on Moonbeam >>>469      console.log('Acquire Unique AssetId Info on Moonbeam.......');470471      // Wait for the democracy execute472      await helper.wait.newBlocks(5);473474      assetId = (await helper.assetManager.assetTypeId(uniqueAssetLocation)).toString();475476      console.log('UNQ asset ID is %s', assetId);477      console.log('Acquire Unique AssetId Info on Moonbeam.......DONE');478      // >>> Acquire Unique AssetId Info on Moonbeam >>>479480      // >>> Sponsoring random Account >>>481      console.log('Sponsoring random Account.......');482      await helper.balance.transferToEthereum(baltatharAccount, randomAccountMoonbeam.address, 11_000_000_000_000_000_000n);483      console.log('Sponsoring random Account.......DONE');484      // <<< Sponsoring random Account <<<485486      balanceGlmrTokenInit = await helper.balance.getEthereum(randomAccountMoonbeam.address);487    });488489    await usingPlaygrounds(async (helper) => {490      await helper.balance.transferToSubstrate(uniqueDonor, randomAccountUnique.address, 10n * TRANSFER_AMOUNT);491      balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccountUnique.address);492    });493  });494495  itSub('Should connect and send UNQ to Moonbeam', async ({helper}) => {496    const currencyId = {497      NativeAssetId: 'Here',498    };499    const dest = {500      V1: {501        parents: 1,502        interior: {503          X2: [504            {Parachain: MOONBEAM_CHAIN},505            {AccountKey20: {network: 'Any', key: randomAccountMoonbeam.address}},506          ],507        },508      },509    };510    const amount = TRANSFER_AMOUNT;511    const destWeight = 850000000;512513    await helper.xTokens.transfer(randomAccountUnique, currencyId, amount, dest, destWeight);514515    balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccountUnique.address);516    expect(balanceUniqueTokenMiddle < balanceUniqueTokenInit).to.be.true;517518    const transactionFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;519    console.log('[Unique -> Moonbeam] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(transactionFees));520    expect(transactionFees > 0).to.be.true;521522    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {523      await helper.wait.newBlocks(3);524525      balanceGlmrTokenMiddle = await helper.balance.getEthereum(randomAccountMoonbeam.address);526527      const glmrFees = balanceGlmrTokenInit - balanceGlmrTokenMiddle;528      console.log('[Unique -> Moonbeam] transaction fees on Moonbeam: %s GLMR', helper.util.bigIntToDecimals(glmrFees));529      expect(glmrFees == 0n).to.be.true;530531      balanceForeignUnqTokenMiddle = (await helper.assets.account(assetId, randomAccountMoonbeam.address))!;532533      const unqIncomeTransfer = balanceForeignUnqTokenMiddle - balanceForeignUnqTokenInit;534      console.log('[Unique -> Moonbeam] income %s UNQ', helper.util.bigIntToDecimals(unqIncomeTransfer));535      expect(unqIncomeTransfer == TRANSFER_AMOUNT).to.be.true;536    });537  });538539  itSub('Should connect to Moonbeam and send UNQ back', async ({helper}) => {540    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {541      const asset = {542        V1: {543          id: {544            Concrete: {545              parents: 1,546              interior: {547                X1: {Parachain: UNIQUE_CHAIN},548              },549            },550          },551          fun: {552            Fungible: TRANSFER_AMOUNT,553          },554        },555      };556      const destination = {557        V1: {558          parents: 1,559          interior: {560            X2: [561              {Parachain: UNIQUE_CHAIN},562              {AccountId32: {network: 'Any', id: randomAccountUnique.addressRaw}},563            ],564          },565        },566      };567      const destWeight = 50000000;568569      await helper.xTokens.transferMultiasset(randomAccountMoonbeam, asset, destination, destWeight);570571      balanceGlmrTokenFinal = await helper.balance.getEthereum(randomAccountMoonbeam.address);572573      const glmrFees = balanceGlmrTokenMiddle - balanceGlmrTokenFinal;574      console.log('[Moonbeam -> Unique] transaction fees on Moonbeam: %s GLMR', helper.util.bigIntToDecimals(glmrFees));575      expect(glmrFees > 0).to.be.true;576577      const unqRandomAccountAsset = await helper.assets.account(assetId, randomAccountMoonbeam.address);578579      expect(unqRandomAccountAsset).to.be.null;580      581      balanceForeignUnqTokenFinal = 0n;582583      const unqOutcomeTransfer = balanceForeignUnqTokenMiddle - balanceForeignUnqTokenFinal;584      console.log('[Unique -> Moonbeam] outcome %s UNQ', helper.util.bigIntToDecimals(unqOutcomeTransfer));585      expect(unqOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;586    });587588    await helper.wait.newBlocks(3);589590    balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccountUnique.address);591    const actuallyDelivered = balanceUniqueTokenFinal - balanceUniqueTokenMiddle;592    expect(actuallyDelivered > 0).to.be.true;593594    console.log('[Moonbeam -> Unique] actually delivered %s UNQ', helper.util.bigIntToDecimals(actuallyDelivered));595596    const unqFees = TRANSFER_AMOUNT - actuallyDelivered;597    console.log('[Moonbeam -> Unique] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));598    expect(unqFees == 0n).to.be.true;599  });600});