git.delta.rocks / unique-network / refs/commits / 06b395f7b928

difftreelog

source

tests/src/xcm/lowLevelXcmQuartz.test.ts15.9 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 {itSub, expect, describeXCM, usingPlaygrounds, usingKaruraPlaygrounds,  usingMoonriverPlaygrounds, usingShidenPlaygrounds, usingRelayPlaygrounds} from '../util';19import {DevUniqueHelper, Event} from '../util/playgrounds/unique.dev';20import {STATEMINE_CHAIN, QUARTZ_CHAIN, KARURA_CHAIN, MOONRIVER_CHAIN, SHIDEN_CHAIN, STATEMINE_DECIMALS, KARURA_DECIMALS, QTZ_DECIMALS, RELAY_DECIMALS, SHIDEN_DECIMALS, karuraUrl, moonriverUrl, relayUrl, shidenUrl, statemineUrl, SAFE_XCM_VERSION, XcmTestHelper, TRANSFER_AMOUNT} from './xcm.types';212223const testHelper = new XcmTestHelper('quartz');2425describeXCM('[XCMLL] Integration test: Exchanging tokens with Karura', () => {26  let alice: IKeyringPair;27  let randomAccount: IKeyringPair;2829  let balanceQuartzTokenInit: bigint;30  let balanceQuartzTokenMiddle: bigint;31  let balanceQuartzTokenFinal: bigint;32  let balanceKaruraTokenInit: bigint;33  let balanceKaruraTokenMiddle: bigint;34  let balanceKaruraTokenFinal: bigint;35  let balanceQuartzForeignTokenInit: bigint;36  let balanceQuartzForeignTokenMiddle: bigint;37  let balanceQuartzForeignTokenFinal: bigint;3839  // computed by a test transfer from prod Quartz to prod Karura.40  // 2 QTZ sent https://quartz.subscan.io/xcm_message/kusama-f60d821b049f8835a3005ce7102285006f5b61e941  // 1.919176000000000000 QTZ received (you can check Karura's chain state in the corresponding block)42  const expectedKaruraIncomeFee = 2000000000000000000n - 1919176000000000000n;43  const karuraEps = 8n * 10n ** 16n;4445  let karuraBackwardTransferAmount: bigint;4647  before(async () => {48    await usingPlaygrounds(async (helper, privateKey) => {49      alice = await privateKey('//Alice');50      [randomAccount] = await helper.arrange.createAccounts([0n], alice);5152      // Set the default version to wrap the first message to other chains.53      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);54    });5556    await usingKaruraPlaygrounds(karuraUrl, async (helper) => {57      const destination = {58        V2: {59          parents: 1,60          interior: {61            X1: {62              Parachain: QUARTZ_CHAIN,63            },64          },65        },66      };6768      const metadata = {69        name: 'Quartz',70        symbol: 'QTZ',71        decimals: 18,72        minimalBalance: 1000000000000000000n,73      };7475      await helper.getSudo().assetRegistry.registerForeignAsset(alice, destination, metadata);76      await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n);77      balanceKaruraTokenInit = await helper.balance.getSubstrate(randomAccount.address);78      balanceQuartzForeignTokenInit = await helper.tokens.accounts(randomAccount.address, {ForeignAsset: 0});79    });8081    await usingPlaygrounds(async (helper) => {82      await helper.balance.transferToSubstrate(alice, randomAccount.address, 10n * TRANSFER_AMOUNT);83      balanceQuartzTokenInit = await helper.balance.getSubstrate(randomAccount.address);84    });85  });8687  itSub('Should connect and send QTZ to Karura', async () => {88    await testHelper.sendUnqTo('karura', randomAccount);89  });9091  itSub('Should connect to Karura and send QTZ back', async () => {92    await testHelper.sendUnqBack('karura', alice, randomAccount);93  });9495  itSub('Karura can send only up to its balance', async () => {96    await testHelper.sendOnlyOwnedBalance('karura', alice);97  });98});99// These tests are relevant only when100// the the corresponding foreign assets are not registered101describeXCM('[XCMLL] Integration test: Quartz rejects non-native tokens', () => {102  let alice: IKeyringPair;103  let alith: IKeyringPair;104105  const testAmount = 100_000_000_000n;106  let quartzParachainJunction;107  let quartzAccountJunction;108109  let quartzParachainMultilocation: any;110  let quartzAccountMultilocation: any;111  let quartzCombinedMultilocation: any;112113  let messageSent: any;114115  const maxWaitBlocks = 3;116117  before(async () => {118    await usingPlaygrounds(async (helper, privateKey) => {119      alice = await privateKey('//Alice');120121      quartzParachainJunction = {Parachain: QUARTZ_CHAIN};122      quartzAccountJunction = {123        AccountId32: {124          network: 'Any',125          id: alice.addressRaw,126        },127      };128129      quartzParachainMultilocation = {130        V2: {131          parents: 1,132          interior: {133            X1: quartzParachainJunction,134          },135        },136      };137138      quartzAccountMultilocation = {139        V2: {140          parents: 0,141          interior: {142            X1: quartzAccountJunction,143          },144        },145      };146147      quartzCombinedMultilocation = {148        V2: {149          parents: 1,150          interior: {151            X2: [quartzParachainJunction, quartzAccountJunction],152          },153        },154      };155156      // Set the default version to wrap the first message to other chains.157      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);158    });159160    // eslint-disable-next-line require-await161    await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => {162      alith = helper.account.alithAccount();163    });164  });165166  itSub('Quartz rejects KAR tokens from Karura', async () => {167    await testHelper.rejectNativeTokensFrom('karura', alice);168  });169170  itSub('Quartz rejects MOVR tokens from Moonriver', async () => {171    await testHelper.rejectNativeTokensFrom('moonriver', alice);172  });173174  itSub('Quartz rejects SDN tokens from Shiden', async () => {175    await testHelper.rejectNativeTokensFrom('shiden', alice);176  });177});178179describeXCM('[XCMLL] Integration test: Exchanging QTZ with Moonriver', () => {180  // Quartz constants181  let alice: IKeyringPair;182  let quartzAssetLocation;183184  let randomAccountQuartz: IKeyringPair;185  let randomAccountMoonriver: IKeyringPair;186187  // Moonriver constants188  let assetId: string;189190  const quartzAssetMetadata = {191    name: 'xcQuartz',192    symbol: 'xcQTZ',193    decimals: 18,194    isFrozen: false,195    minimalBalance: 1n,196  };197198  let balanceQuartzTokenInit: bigint;199  let balanceQuartzTokenMiddle: bigint;200  let balanceQuartzTokenFinal: bigint;201  let balanceForeignQtzTokenInit: bigint;202  let balanceForeignQtzTokenMiddle: bigint;203  let balanceForeignQtzTokenFinal: bigint;204  let balanceMovrTokenInit: bigint;205  let balanceMovrTokenMiddle: bigint;206  let balanceMovrTokenFinal: bigint;207208  before(async () => {209    await usingPlaygrounds(async (helper, privateKey) => {210      alice = await privateKey('//Alice');211      [randomAccountQuartz] = await helper.arrange.createAccounts([0n], alice);212213      balanceForeignQtzTokenInit = 0n;214215      // Set the default version to wrap the first message to other chains.216      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);217    });218219    await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => {220      const alithAccount = helper.account.alithAccount();221      const baltatharAccount = helper.account.baltatharAccount();222      const dorothyAccount = helper.account.dorothyAccount();223224      randomAccountMoonriver = helper.account.create();225226      // >>> Sponsoring Dorothy >>>227      console.log('Sponsoring Dorothy.......');228      await helper.balance.transferToEthereum(alithAccount, dorothyAccount.address, 11_000_000_000_000_000_000n);229      console.log('Sponsoring Dorothy.......DONE');230      // <<< Sponsoring Dorothy <<<231232      quartzAssetLocation = {233        XCM: {234          parents: 1,235          interior: {X1: {Parachain: QUARTZ_CHAIN}},236        },237      };238      const existentialDeposit = 1n;239      const isSufficient = true;240      const unitsPerSecond = 1n;241      const numAssetsWeightHint = 0;242243      const encodedProposal = helper.assetManager.makeRegisterForeignAssetProposal({244        location: quartzAssetLocation,245        metadata: quartzAssetMetadata,246        existentialDeposit,247        isSufficient,248        unitsPerSecond,249        numAssetsWeightHint,250      });251252      console.log('Encoded proposal for registerForeignAsset & setAssetUnitsPerSecond is %s', encodedProposal);253254      await helper.fastDemocracy.executeProposal('register QTZ foreign asset', encodedProposal);255256      // >>> Acquire Quartz AssetId Info on Moonriver >>>257      console.log('Acquire Quartz AssetId Info on Moonriver.......');258259      assetId = (await helper.assetManager.assetTypeId(quartzAssetLocation)).toString();260261      console.log('QTZ asset ID is %s', assetId);262      console.log('Acquire Quartz AssetId Info on Moonriver.......DONE');263      // >>> Acquire Quartz AssetId Info on Moonriver >>>264265      // >>> Sponsoring random Account >>>266      console.log('Sponsoring random Account.......');267      await helper.balance.transferToEthereum(baltatharAccount, randomAccountMoonriver.address, 11_000_000_000_000_000_000n);268      console.log('Sponsoring random Account.......DONE');269      // <<< Sponsoring random Account <<<270271      balanceMovrTokenInit = await helper.balance.getEthereum(randomAccountMoonriver.address);272    });273274    await usingPlaygrounds(async (helper) => {275      await helper.balance.transferToSubstrate(alice, randomAccountQuartz.address, 10n * TRANSFER_AMOUNT);276      balanceQuartzTokenInit = await helper.balance.getSubstrate(randomAccountQuartz.address);277    });278  });279280  itSub('Should connect and send QTZ to Moonriver', async () => {281    await testHelper.sendUnqTo('moonriver', randomAccountQuartz, randomAccountMoonriver);282  });283284  itSub('Should connect to Moonriver and send QTZ back', async () => {285    await testHelper.sendUnqBack('moonriver', alice, randomAccountQuartz);286  });287288  itSub('Moonriver can send only up to its balance', async () => {289    await testHelper.sendOnlyOwnedBalance('moonriver', alice);290  });291292  itSub('Should not accept reserve transfer of QTZ from Moonriver', async () => {293    await testHelper.reserveTransferUNQfrom('moonriver', alice);294  });295});296297describeXCM('[XCMLL] Integration test: Exchanging tokens with Shiden', () => {298  let alice: IKeyringPair;299  let sender: IKeyringPair;300301  const QTZ_ASSET_ID_ON_SHIDEN = 1;302  const QTZ_MINIMAL_BALANCE_ON_SHIDEN = 1n;303304  // Quartz -> Shiden305  const shidenInitialBalance = 1n * (10n ** SHIDEN_DECIMALS); // 1 SHD, existential deposit required to actually create the account on Shiden306  const unitsPerSecond = 228_000_000_000n; // This is Phala's value. What will be ours?307  const qtzToShidenTransferred = 10n * (10n ** QTZ_DECIMALS); // 10 QTZ308  const qtzToShidenArrived = 9_999_999_999_088_000_000n; // 9.999 ... QTZ, Shiden takes a commision in foreign tokens309310  // Shiden -> Quartz311  const qtzFromShidenTransfered = 5n * (10n ** QTZ_DECIMALS); // 5 QTZ312  const qtzOnShidenLeft = qtzToShidenArrived - qtzFromShidenTransfered; // 4.999_999_999_088_000_000n QTZ313314  let balanceAfterQuartzToShidenXCM: bigint;315316  before(async () => {317    await usingPlaygrounds(async (helper, privateKey) => {318      alice = await privateKey('//Alice');319      [sender] = await helper.arrange.createAccounts([100n], alice);320      console.log('sender', sender.address);321322      // Set the default version to wrap the first message to other chains.323      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);324    });325326    await usingShidenPlaygrounds(shidenUrl, async (helper) => {327      console.log('1. Create foreign asset and metadata');328      // TODO update metadata with values from production329      await helper.assets.create(330        alice,331        QTZ_ASSET_ID_ON_SHIDEN,332        alice.address,333        QTZ_MINIMAL_BALANCE_ON_SHIDEN,334      );335336      await helper.assets.setMetadata(337        alice,338        QTZ_ASSET_ID_ON_SHIDEN,339        'Cross chain QTZ',340        'xcQTZ',341        Number(QTZ_DECIMALS),342      );343344      console.log('2. Register asset location on Shiden');345      const assetLocation = {346        V2: {347          parents: 1,348          interior: {349            X1: {350              Parachain: QUARTZ_CHAIN,351            },352          },353        },354      };355356      await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.registerAssetLocation', [assetLocation, QTZ_ASSET_ID_ON_SHIDEN]);357358      console.log('3. Set QTZ payment for XCM execution on Shiden');359      await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.setAssetUnitsPerSecond', [assetLocation, unitsPerSecond]);360361      console.log('4. Transfer 1 SDN to recipient to create the account (needed due to existential balance)');362      await helper.balance.transferToSubstrate(alice, sender.address, shidenInitialBalance);363    });364  });365366  itSub('Should connect and send QTZ to Shiden', async () => {367    await testHelper.sendUnqTo('shiden', sender);368  });369370  itSub('Should connect to Shiden and send QTZ back', async () => {371    await testHelper.sendUnqBack('shiden', alice, sender);372  });373374  itSub('Shiden can send only up to its balance', async () => {375    await testHelper.sendOnlyOwnedBalance('shiden', alice);376  });377378  itSub('Should not accept reserve transfer of QTZ from Shiden', async () => {379    await testHelper.reserveTransferUNQfrom('shiden', alice);380  });381});382383describeXCM('[XCMLL] Integration test: The relay can do some root ops', () => {384  let sudoer: IKeyringPair;385386  before(async function () {387    await usingRelayPlaygrounds(relayUrl, async (_, privateKey) => {388      sudoer = await privateKey('//Alice');389    });390  });391392  // At the moment there is no reliable way393  // to establish the correspondence between the `ExecutedDownward` event394  // and the relay's sent message due to `SetTopic` instruction395  // containing an unpredictable topic silently added by the relay on the router level.396  // This changes the message hash on arrival to our chain.397  //398  // See:399  // * The relay's router: https://github.com/paritytech/polkadot-sdk/blob/f60318f68687e601c47de5ad5ca88e2c3f8139a7/polkadot/runtime/westend/src/xcm_config.rs#L83400  // * The `WithUniqueTopic` helper: https://github.com/paritytech/polkadot-sdk/blob/945ebbbcf66646be13d5b1d1bc26c8b0d3296d9e/polkadot/xcm/xcm-builder/src/routing.rs#L36401  //402  // Because of this, we insert time gaps between tests so403  // different `ExecutedDownward` events won't interfere with each other.404  afterEach(async () => {405    await usingPlaygrounds(async (helper) => {406      await helper.wait.newBlocks(3);407    });408  });409410  itSub('The relay can set storage', async () => {411    await testHelper.relayIsPermittedToSetStorage(sudoer, 'plain');412  });413414  itSub('The relay can batch set storage', async () => {415    await testHelper.relayIsPermittedToSetStorage(sudoer, 'batch');416  });417418  itSub('The relay can batchAll set storage', async () => {419    await testHelper.relayIsPermittedToSetStorage(sudoer, 'batchAll');420  });421422  itSub('The relay can forceBatch set storage', async () => {423    await testHelper.relayIsPermittedToSetStorage(sudoer, 'forceBatch');424  });425426  itSub('[negative] The relay cannot set balance', async () => {427    await testHelper.relayIsNotPermittedToSetBalance(sudoer, 'plain');428  });429430  itSub('[negative] The relay cannot set balance via batch', async () => {431    await testHelper.relayIsNotPermittedToSetBalance(sudoer, 'batch');432  });433434  itSub('[negative] The relay cannot set balance via batchAll', async () => {435    await testHelper.relayIsNotPermittedToSetBalance(sudoer, 'batchAll');436  });437438  itSub('[negative] The relay cannot set balance via forceBatch', async () => {439    await testHelper.relayIsNotPermittedToSetBalance(sudoer, 'forceBatch');440  });441442  itSub('[negative] The relay cannot set balance via dispatchAs', async () => {443    await testHelper.relayIsNotPermittedToSetBalance(sudoer, 'dispatchAs');444  });445});