diff --git a/.gitignore b/.gitignore index 9184f2eb..45d0833f 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,7 @@ zombienet-macos zombienet-linux-arm64 zombienet-linux-x64 zombienet.log +zombienet/bin/* # Binaries /bin/* diff --git a/README.md b/README.md index 5ccef86b..5ff8579a 100644 --- a/README.md +++ b/README.md @@ -323,3 +323,7 @@ From the root directory run `./ -p native spawn ./zombien ### Create an asset From the root directory run `yarn start:zombienet-post-script`. You can run this right after running your zombienet network. + +## E2E Testing + +You can access the E2E tests and its documentation [here](./e2e-tests/). diff --git a/e2e-tests/README.md b/e2e-tests/README.md new file mode 100644 index 00000000..b4ccded8 --- /dev/null +++ b/e2e-tests/README.md @@ -0,0 +1,36 @@ +## E2E Tests + +End-to-end tests that run on a zombienet testnet. + +**NOTE: tested using polkadot v1.4.0** + +### Setup + +To setup the testing environment you need to first download the `polkadot`, `polkadot-execute-worker`, `polkadot-prepare-worker` and `polkadot-parachain` from the `polkadot-sdk` [release page](https://github.com/paritytech/polkadot-sdk/releases/latest), as well as the `trappist-node` from its [release page](https://github.com/paritytech/trappist/releases/latest), and place them in the `../zombienet/bin/` folder. + +You also need to have the latest `zombienet` executable in the `../zombienet/` folder, which you can download from [here](https://github.com/paritytech/zombienet/releases/latest). + +### Launching zombienet + +To launch the zombienet run the following commands: +```bash +$ yarn build && yarn e2e:build +``` +Then run: +```bash +$ yarn e2e:zombienet +``` +And this will launch the zombienet using the config file located in the `../zombienet/` directory. Once it finished its setup, you can proceed to the following step. + +### Launching the tests + +For testing, we provide 4 options: + +* Testing liquidity tokens transfers with the command `yarn e2e:liquidity-assets`. +* Testing foreign assets transfers with the command `yarn e2e:foreign-assets`. +* Testing local transferss with the command `yarn e2e:local`. +* Testing assets transfers with the command `yarn e2e:assets`. + +Each of these commands will run the appropiate script to setup the basics, located in `../scripts/`. Wait for it to finish setting up the testing environment, and then go through the tests indicated in the `./tests/index.ts` file for the chosen option. + +After each testing suite has been completed, it's recommended to restart the zombienet before running another test suite. diff --git a/e2e-tests/balance.ts b/e2e-tests/balance.ts new file mode 100644 index 00000000..9edee8f1 --- /dev/null +++ b/e2e-tests/balance.ts @@ -0,0 +1,138 @@ +// Copyright 2023 Parity Technologies (UK) Ltd. + +import type { ApiPromise } from '@polkadot/api'; +import type { FrameSystemAccountInfo, PalletAssetsAssetAccount } from '@polkadot/types/lookup'; +import type { Option } from '@polkadot/types-codec'; + +/** + * Initial and final balances for a specific assetId, used by `balanceTracker` + * to validate the evolution of a particular account's balance + * + * @interface IBalance + * + * @property {[string, number][]} IBalance.initial Account's initial balance + * for the given assetIds + * @property {[string, number][]} IBalance.final Account's final balance for + * the given assetIds + */ +export interface IBalance { + initial: [string, number][]; + final: [string, number][]; +} + +/** + * Function to keep track of the evolution of the balances for specific assetIds + * for a given account, used to validate the correct processing of the tests' + * transactions. + * It queries the node for the appropiate pallet's balance for the given test + * suite, and stores it as either `initial`, if no previous value is stored, + * or `final`. Both properties consist of an array of tuples containing an assetId + * and its balance for the account for the moment the value was queried, either + * before or after running the test. + * + * @param api api instance + * @param test name of the test currently being ran + * @param address address of the account whose balance is being queried + * @param assetIds Ids of the assets being queried + * @param balance initial balance for the account queried + * @returns instance of IBalance containing the initial and optionally the final + * balance of the account queried + */ +export const balanceTracker = async ( + api: ApiPromise, + test: string, + address: string, + assetIds: string[], + balance?: IBalance, +): Promise => { + let balances: IBalance = { initial: [], final: [] }; + let accountInfo: FrameSystemAccountInfo | Option; + switch (test) { + case '--foreign-assets': + if (!balance) { + for (const assetId of assetIds) { + accountInfo = (await api.query.foreignAssets.account(assetId, address)) as Option; + if (accountInfo.valueOf() === null) { + balances.initial.push([assetId, 0]); + } else { + balances.initial.push([assetId, accountInfo.unwrap().balance.toBn().toNumber()]); + } + } + } else { + balances = balance; + for (const assetId of assetIds) { + accountInfo = (await api.query.foreignAssets.account(assetId, address)) as Option; + balances.final.push([assetId, accountInfo.unwrap().balance.toBn().toNumber()]); + } + } + return balances; + case '--liquidity-assets': + if (!balance) { + for (const assetId of assetIds) { + accountInfo = await api.query.poolAssets.account(assetId, address); + if (accountInfo.isNone) { + balances.initial.push([assetId, 0]); + } else { + balances.initial.push([assetId, accountInfo.unwrap().balance.toBn().toNumber()]); + } + } + } else { + balances = balance; + for (const assetId of assetIds) { + accountInfo = await api.query.poolAssets.account(assetId, address); + balances.final.push([assetId, accountInfo.unwrap().balance.toBn().toNumber()]); + } + } + return balances; + case '--local': + if (!balance) { + accountInfo = await api.query.system.account(address); + if (accountInfo === null) { + balances.initial.push(['0', 0]); + } else { + balances.initial.push(['0', Number(accountInfo.data.free)]); + } + } else { + balances = balance; + accountInfo = await api.query.system.account(address); + balances.final.push(['0', Number(accountInfo.data.free)]); + } + return balances; + case '--assets': + if (!balance) { + for (const assetId of assetIds) { + accountInfo = await api.query.assets.account(assetId, address); + if (accountInfo.isNone) { + balances.initial.push([assetId, 0]); + } else { + balances.initial.push([assetId, accountInfo.unwrap().balance.toBn().toNumber()]); + } + } + } else { + balances = balance; + for (const assetId of assetIds) { + accountInfo = await api.query.assets.account(assetId, address); + balances.final.push([assetId, accountInfo.unwrap().balance.toBn().toNumber()]); + } + } + return balances; + default: + if (!balance) { + for (const assetId of assetIds) { + accountInfo = await api.query.system.account(address); + if (accountInfo) { + balances.initial.push([assetId, accountInfo.data.free.toBn().toNumber()]); + } else { + balances.initial.push([assetId, 0]); + } + } + } else { + balances = balance; + for (const assetId of assetIds) { + accountInfo = await api.query.system.account(address); + balances.final.push([assetId, accountInfo.data.free.toBn().toNumber()]); + } + } + return balances; + } +}; diff --git a/e2e-tests/consts.ts b/e2e-tests/consts.ts new file mode 100644 index 00000000..615f1672 --- /dev/null +++ b/e2e-tests/consts.ts @@ -0,0 +1,10 @@ +// Copyright 2023 Parity Technologies (UK) Ltd. + +export const KUSAMA_ASSET_HUB_WS_URL = 'ws://127.0.0.1:9911'; +export const ROCOCO_ALICE_WS_URL = 'ws://127.0.0.1:9900'; +export const TRAPPIST_WS_URL = 'ws://127.0.0.1:9921'; + +export const BOB_ADDR = '5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty'; +export const FERDE_ADDR = '5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL'; + +export const EXTRINSIC_VERSION = 4; diff --git a/e2e-tests/executor.ts b/e2e-tests/executor.ts new file mode 100644 index 00000000..4932963a --- /dev/null +++ b/e2e-tests/executor.ts @@ -0,0 +1,152 @@ +// Copyright 2023 Parity Technologies (UK) Ltd. +import { ApiPromise, WsProvider } from '@polkadot/api'; +import { Keyring } from '@polkadot/keyring'; +import { cryptoWaitReady } from '@polkadot/util-crypto'; + +import { delay } from '../scripts/util'; +import { constructApiPromise } from '../src'; +import { balanceTracker, IBalance } from './balance'; +import { KUSAMA_ASSET_HUB_WS_URL, ROCOCO_ALICE_WS_URL, TRAPPIST_WS_URL } from './consts'; +import { startProgressBar, startTestLogger, terminateProgressBar, testResultLogger, updateProgressBar } from './logger'; +import { assetTests, foreignAssetsTests, IndividualTest, liquidPoolsTests, localTests, tests } from './tests'; +import { verification } from './verification'; + +const executor = async (testCase: string) => { + let originWsUrl = ''; + let destWsUrl = ''; + let testData: IndividualTest[] = []; + + await cryptoWaitReady(); + + const keyring = new Keyring({ type: 'sr25519' }); + let n: { [K: string]: Function } = {}; + + switch (testCase) { + case '--foreign-assets': + testData = tests.foreignAssets; + n = foreignAssetsTests; + break; + case '--liquidity-assets': + testData = tests.liquidPools; + n = liquidPoolsTests; + break; + case '--local': + testData = tests.local; + n = localTests; + break; + case '--assets': + testData = tests.assets; + n = assetTests; + break; + } + + let counter: number = 0; + + startTestLogger(testCase); + + const progressBar = startProgressBar(testData, testCase); + + const results: [string, string, string, boolean][] = []; + + for (const t of testData) { + const originChainId: string = t.args[0]; + const destChainId: string = t.args[1]; + const originAddr: string = t.args[2]; + const destAddr: string = t.args[3]; + const assetIds: string[] = t.args[4].slice(1, -1).split(','); + const amounts: string[] = t.args[5].slice(1, -1).split(','); + const opts: object = JSON.parse(t.args[6], (key: string, value: string) => { + return key === 'paysWithFeeOrigin' ? JSON.stringify(value) : value; + }) as object; + let chainName: string = ''; + + switch (originChainId) { + case '0': + originWsUrl = ROCOCO_ALICE_WS_URL; + chainName = 'Rococo'; + break; + case '1000': + originWsUrl = KUSAMA_ASSET_HUB_WS_URL; + chainName = 'Kusama Asset Hub'; + break; + case '1836': + originWsUrl = TRAPPIST_WS_URL; + chainName = 'Trappist'; + break; + } + if (originChainId == destChainId) { + destWsUrl = originWsUrl; + } else { + switch (destChainId) { + case '0': + destWsUrl = ROCOCO_ALICE_WS_URL; + chainName = 'Rococo'; + break; + case '1000': + destWsUrl = KUSAMA_ASSET_HUB_WS_URL; + chainName = 'Kusama Asset Hub'; + break; + case '1836': + destWsUrl = TRAPPIST_WS_URL; + chainName = 'Trappist'; + break; + } + } + + const { api, specName, safeXcmVersion } = await constructApiPromise(originWsUrl); + + await api.isReady; + + const originApi = api; + const destinationApi = + originChainId == destChainId + ? originApi + : await ApiPromise.create({ + provider: new WsProvider(destWsUrl), + noInitWarn: true, + }); + + await destinationApi.isReady; + const destInitialBalance: IBalance = await balanceTracker(destinationApi, testCase, destAddr, assetIds); + const originKeyring = keyring.addFromUri(originAddr); + + //eslint-disable-next-line @typescript-eslint/no-unsafe-call + await n[t.test](originKeyring, destChainId, destAddr, assetIds, amounts, opts, api, specName, safeXcmVersion); + + await delay(24000); + + const destFinalBalance: IBalance = await balanceTracker( + destinationApi, + testCase, + destAddr, + assetIds, + destInitialBalance, + ); + + const verificationAssetIds: string[] = t.verification[0].slice(1, -1).split(','); + const verificationAmounts: string[] = t.verification[1].slice(1, -1).split(','); + + const correctlyReceived = verification(verificationAssetIds, verificationAmounts, destFinalBalance); + + await originApi.disconnect(); + await destinationApi.disconnect(); + + counter += 1; + + updateProgressBar(counter, progressBar); + + for (let i = 0; i < assetIds.length; i++) { + results.push([t.test, assetIds[i], chainName, correctlyReceived[i][1]]); + } + } + + for (let i = 0; i < results.length; i++) { + testResultLogger(results[i][0], results[i][1], results[i][2], results[i][3]); + } + + terminateProgressBar(progressBar, testCase); +}; + +executor(process.argv[2]) + .catch((err) => console.error(err)) + .finally(() => process.exit()); diff --git a/e2e-tests/logger.ts b/e2e-tests/logger.ts new file mode 100644 index 00000000..4076d53d --- /dev/null +++ b/e2e-tests/logger.ts @@ -0,0 +1,110 @@ +// Copyright 2023 Parity Technologies (UK) Ltd. +import colors from 'ansi-colors'; +import chalk from 'chalk'; +import * as cliProgress from 'cli-progress'; +import { IndividualTest } from 'tests'; + +/** + * + * @param testCase a string containing the test option selected + * @returns a prettified version of the testCase + */ +const defineTest = (testCase: string): string => { + let test: string = ''; + + switch (testCase) { + case '--foreign-assets': + test = 'Foreign Assets Transfers'; + break; + case '--liquidity-assets': + test = 'Liqudity Tokens Transfers'; + break; + case '--local': + test = 'Native Token Transfers'; + break; + case '--assets': + test = 'Local Assets Transfers'; + break; + } + return test; +}; + +/** + * Calls defineTest and prints a message signaling the begining of the tests + * @param testCase a string containing the test option selected + */ +export const startTestLogger = (testCase: string) => { + const test = defineTest(testCase); + + console.log(chalk.yellow(`Initializing tests for ${test}\n`)); +}; + +/** + * This creates and starts an instance of cliProgress containing a SingleBar to + * display the test suite's progress + * + * @param testData an array containing the individual tests for the test suite + * selected + * @param testCase the test suite selected + * @returns an instance of the cliProgress that will be used to track the progress + * of the tests + */ +export const startProgressBar = (testData: IndividualTest[], testCase: string): cliProgress.SingleBar => { + const test = defineTest(testCase); + + const coverage: number = testData.length; + + const progressBar = new cliProgress.SingleBar({ + format: + `\n${test} Test Suite Progress |` + colors.cyan('{bar}') + '| {percentage}% || {value}/{total} tests covered \n', + barCompleteChar: '\u2588', + barIncompleteChar: '\u2591', + hideCursor: true, + }); + + progressBar.start(coverage, 0); + + return progressBar; +}; + +/** + * Clears the progress bar in place and prints an updated version + * @param counter current test number + * @param progressBar instance of the cliProgress + */ +export const updateProgressBar = (counter: number, progressBar: cliProgress.SingleBar) => { + process.stdout.moveCursor(0, -2); + + process.stdout.clearLine(0); + + progressBar.increment(counter); +}; + +/** + * Terminates the cliProgess instance. It's called after the test suite is over + * @param progressBar instance of cliProgress to be terminated + * @param testCase + */ +export const terminateProgressBar = (progressBar: cliProgress.SingleBar, testCase: string) => { + const test = defineTest(testCase); + console.log(chalk.yellow(`Test Suite for ${test} completed.\n`)); + + progressBar.stop(); + console.log('\n'); +}; + +/** + * + * @param testName current test's name + * @param assetId Id of the asset tested against + * @param chainName Name of the chain against the test ran + * @param passed whether the test passed or failed + */ +export const testResultLogger = (testName: string, assetId: string, chainName: string, passed: boolean) => { + const tokenId = assetId === '' ? 'native asset' : `asset ${assetId}`; + if (passed) { + console.log(chalk.green(`Test ${testName} passed for ${chainName}'s ${tokenId} \u2705\n`)); + } else { + console.log(chalk.red(`Test ${testName} failed for ${chainName}'s ${tokenId} \u274E\n`)); + } +}; diff --git a/e2e-tests/tests.sh b/e2e-tests/tests.sh new file mode 100755 index 00000000..d53cd824 --- /dev/null +++ b/e2e-tests/tests.sh @@ -0,0 +1,18 @@ +#!/bin/sh +test=$1 +yarn run build:e2e +if [ $test = '--assets' ]; then + script=post +elif [ $test = '--local' ]; then + script=post +elif [ $test = '--liquidity-assets' ]; then + script=liquidity-assets +elif [ $test = '--foreign-assets' ]; then + script=foreign-assets +fi + +yarn run start:zombienet-$script-script + +sleep 24 + +node ./e2e-tests/build/e2e-tests/executor.js $test diff --git a/e2e-tests/tests/assets.ts b/e2e-tests/tests/assets.ts new file mode 100644 index 00000000..7a2f3b6d --- /dev/null +++ b/e2e-tests/tests/assets.ts @@ -0,0 +1,90 @@ +// Copyright 2023 Parity Technologies (UK) Ltd. +import { ApiPromise } from '@polkadot/api'; +import { KeyringPair } from '@polkadot/keyring/types'; +import { EXTRINSIC_VERSION } from 'consts'; + +import { AssetTransferApi } from '../../src'; +import { TxResult } from '../../src/types'; + +const createAssetApi = (api: ApiPromise, specName: string, safeXcmVersion: number): AssetTransferApi => { + const injectedRegistry = { + rococo: { + '1836': { + tokens: ['HOP'], + assetsInfo: {}, + foreignAssetsInfo: {}, + specName: 'trappist-rococo', + poolPairsInfo: {}, + }, + }, + }; + + const assetApi = new AssetTransferApi(api, specName, safeXcmVersion, { injectedRegistry }); + + return assetApi; +}; + +const createLocalSystemAssetsTransferTransaction = async ( + origin: KeyringPair, + destChainId: string, + destAddr: string, + assetIds: string[], + amounts: string[], + opts: object, + api: ApiPromise, + specName: string, + safeXcmVersion: number, +) => { + const assetApi = createAssetApi(api, specName, safeXcmVersion); + + let localTransferInfo: TxResult<'payload'>; + try { + localTransferInfo = await assetApi.createTransferTransaction(destChainId, destAddr, assetIds, amounts, opts); + + const payload = api.createType('ExtrinsicPayload', localTransferInfo.tx, { + version: EXTRINSIC_VERSION, + }); + + const extrinsic = api.registry.createType('Extrinsic', { method: payload.method }, { version: 4 }); + + await api.tx(extrinsic).signAndSend(origin); + } catch (e) { + console.error(e); + throw Error(e as string); + } +}; + +const createPayFeesTransaction = async ( + origin: KeyringPair, + destChainId: string, + destAddr: string, + assetIds: string[], + amounts: string[], + opts: object, + api: ApiPromise, + specName: string, + safeXcmVersion: number, +) => { + const assetApi = createAssetApi(api, specName, safeXcmVersion); + + let localTransferInfo: TxResult<'payload'>; + try { + localTransferInfo = await assetApi.createTransferTransaction(destChainId, destAddr, assetIds, amounts, opts); + + const payload = api.createType('ExtrinsicPayload', localTransferInfo.tx, { + version: EXTRINSIC_VERSION, + }); + + const extrinsic = api.registry.createType('Extrinsic', { method: payload.method }, { version: 4 }); + + await api.tx(extrinsic).signAndSend(origin); + } catch (e) { + console.error(e); + throw Error(e as string); + } +}; + +export const assetTests: { [K: string]: Function } = { + createLocalSystemAssetsTransferTransaction, + createPayFeesTransaction, +}; diff --git a/e2e-tests/tests/foreignAssets.ts b/e2e-tests/tests/foreignAssets.ts new file mode 100644 index 00000000..0efdc03e --- /dev/null +++ b/e2e-tests/tests/foreignAssets.ts @@ -0,0 +1,90 @@ +// Copyright 2023 Parity Technologies (UK) Ltd. +import { ApiPromise } from '@polkadot/api'; +import { KeyringPair } from '@polkadot/keyring/types'; +import { EXTRINSIC_VERSION } from 'consts'; + +import { AssetTransferApi } from '../../src'; +import { TxResult } from '../../src/types'; + +const createAssetApi = (api: ApiPromise, specName: string, safeXcmVersion: number): AssetTransferApi => { + const injectedRegistry = { + rococo: { + '1836': { + tokens: ['HOP'], + assetsInfo: {}, + foreignAssetsInfo: {}, + specName: 'trappist-rococo', + poolPairsInfo: {}, + }, + }, + }; + + const assetApi = new AssetTransferApi(api, specName, safeXcmVersion, { injectedRegistry }); + + return assetApi; +}; + +const createForeignTransferTransaction = async ( + origin: KeyringPair, + destChainId: string, + destAddr: string, + assetIds: string[], + amounts: string[], + opts: object, + api: ApiPromise, + specName: string, + safeXcmVersion: number, +) => { + const assetApi = createAssetApi(api, specName, safeXcmVersion); + + let localTransferInfo: TxResult<'payload'>; + try { + localTransferInfo = await assetApi.createTransferTransaction(destChainId, destAddr, assetIds, amounts, opts); + + const payload = api.createType('ExtrinsicPayload', localTransferInfo.tx, { + version: EXTRINSIC_VERSION, + }); + + const extrinsic = api.registry.createType('Extrinsic', { method: payload.method }, { version: 4 }); + + await api.tx(extrinsic).signAndSend(origin); + } catch (e) { + console.error(e); + throw Error(e as string); + } +}; + +const createLocalForeignTransferTransaction = async ( + origin: KeyringPair, + destChainId: string, + destAddr: string, + assetIds: string[], + amounts: string[], + opts: object, + api: ApiPromise, + specName: string, + safeXcmVersion: number, +) => { + const assetApi = createAssetApi(api, specName, safeXcmVersion); + + let localTransferInfo: TxResult<'payload'>; + try { + localTransferInfo = await assetApi.createTransferTransaction(destChainId, destAddr, assetIds, amounts, opts); + + const payload = api.createType('ExtrinsicPayload', localTransferInfo.tx, { + version: EXTRINSIC_VERSION, + }); + + const extrinsic = api.registry.createType('Extrinsic', { method: payload.method }, { version: 4 }); + + await api.tx(extrinsic).signAndSend(origin); + } catch (e) { + console.error(e); + throw Error(e as string); + } +}; + +export const foreignAssetsTests: { [K: string]: Function } = { + createForeignTransferTransaction, + createLocalForeignTransferTransaction, +}; diff --git a/e2e-tests/tests/index.ts b/e2e-tests/tests/index.ts new file mode 100644 index 00000000..b1c8cc82 --- /dev/null +++ b/e2e-tests/tests/index.ts @@ -0,0 +1,167 @@ +// Copyright 2023 Parity Technologies (UK) Ltd. + +export { assetTests } from './assets'; +export { foreignAssetsTests } from './foreignAssets'; +export { liquidPoolsTests } from './liquidPools'; +export { localTests } from './local'; +import { BOB_ADDR, FERDE_ADDR } from '../consts'; + +export interface IndividualTest { + test: string; + args: string[]; + verification: string[]; +} + +export interface TestGroups { + foreignAssets: IndividualTest[]; + liquidPools: IndividualTest[]; + assets: IndividualTest[]; + local: IndividualTest[]; +} + +export const tests: TestGroups = { + foreignAssets: [ + { + test: 'createForeignTransferTransaction', + args: [ + '1000', + '1836', + '//Alice', + BOB_ADDR, + '[{ "parents": "1", "interior": { "X2": [{ "Parachain": "1836" }, { "GeneralIndex": "0" }]}}]', + '[200000000]', + '{ "format": "payload", "xcmVersion": 3, "sendersAddr": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY" }', + ], + verification: [ + '[{ "parents": "1", "interior": { "X2": [{ "Parachain": "1836" }, { "GeneralIndex": "0" }]}}]', + '[200000000]', + ], + }, + { + test: 'createLocalForeignTransferTransaction', + args: [ + '1000', + '1000', + '//Alice', + FERDE_ADDR, + '[{ "parents": "1", "interior": { "X2": [{ "Parachain": "1836" }, { "GeneralIndex": "0" }]}}]', + '[200000000000]', + '{ "format": "payload", "xcmVersion": 3, "sendersAddr": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY" }', + ], + verification: [ + '[{ "parents": "1", "interior": { "X2": [{ "Parachain": "1836" }, { "GeneralIndex": "0" }]}}]', + '[200000000000]', + ], + }, + ], + liquidPools: [ + { + test: 'createLocalTransferTransaction', + args: [ + '1000', + '1000', + '//Alice', + BOB_ADDR, + '[0]', + '[20000]', + '{ "format": "payload", "keepAlive": true, "transferLiquidToken": true, "sendersAddr": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY" }', + ], + verification: ['[0]', '[20000]'], + }, + { + test: 'createPayFeesTransaction', + args: [ + '1000', + '1000', + '//Bob', + FERDE_ADDR, + '[0]', + '[3000000]', + '{ "format": "payload", "keepAlive": true, "transferLiquidToken": true, "paysWithFeeOrigin": { "parents": "0", "interior": { "X2": [{"PalletInstance": "50"}, { "GeneralIndex": "1" }]}}, "sendersAddr": "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty" }', + ], + verification: ['[0]', '[3000000]'], + }, + ], + local: [ + { + test: 'createLocalTransferTransaction', + args: [ + '1000', + '1000', + '//Alice', + BOB_ADDR, + '[]', + '[100000000000]', + '{ "format": "payload", "keepAlive": true, "sendersAddr": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY" }', + ], + verification: ['[]', '[100000000000]'], + }, + { + test: 'createLocalTransferTransactionWithFees', + args: [ + '0', + '0', + '//Alice', + BOB_ADDR, + '[]', + '[100000000000000000]', + '{ "format": "payload", "keepAlive": true, "sendersAddr": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", "paysWithFeeOrigin": { "parents": "0", "interior": { "X2": [{"PalletInstance": "50"}, { "GeneralIndex": "1" }]}} }', + ], + verification: ['[]', '[10000000000000]'], + }, + { + test: 'createLimitedNativeTransferToRelay', + args: [ + '1000', + '0', + '//Alice', + BOB_ADDR, + '[]', + '[1000000000000000]', + '{ "format": "payload", "keepAlive": true, "xcmVersion": 3, "isLimited": true, "sendersAddr": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", "paysWithFeeOrigin": { "parents": "0", "interior": { "X2": [{"PalletInstance": "50"}, { "GeneralIndex": "1" }]}} }', + ], + verification: ['[]', '[1000000000000000]'], + }, + { + test: 'createLimitedNativeTransferToSystem', + args: [ + '0', + '1000', + '//Bob', + FERDE_ADDR, + '[]', + '[210000000000000]', + '{ "format": "payload", "keepAlive": true, "xcmVersion": 3, "isLimited": true, "weightLimit": {"refTime": "10000" , "proofSize": "3000"}, "sendersAddr": "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty", "paysWithFeeOrigin": { "parents": "0", "interior": { "X2": [{"PalletInstance": "50"}, { "GeneralIndex": "1" }]}} }', + ], + verification: ['[]', '[210000000000000]'], + }, + ], + assets: [ + { + test: 'createLocalSystemAssetsTransferTransaction', + args: [ + '1000', + '1000', + '//Alice', + BOB_ADDR, + '[1]', + '[3000000000000]', + '{ "format": "payload", "keepAlive": true, "sendersAddr": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY" }', + ], + verification: ['[1]', '[3000000000000]'], + }, + { + test: 'createPayFeesTransaction', + args: [ + '1000', + '1000', + '//Bob', + FERDE_ADDR, + '[1]', + '[200000000000]', + '{ "format": "payload", "keepAlive": false, "sendersAddr": "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty", "paysWithFeeOrigin": { "parents": "0", "interior": { "X2": [{"PalletInstance": "50"}, { "GeneralIndex": "1" }]}} }', + ], + verification: ['[1]', '[200000000000]'], + }, + ], +}; diff --git a/e2e-tests/tests/liquidPools.ts b/e2e-tests/tests/liquidPools.ts new file mode 100644 index 00000000..49683064 --- /dev/null +++ b/e2e-tests/tests/liquidPools.ts @@ -0,0 +1,87 @@ +// Copyright 2023 Parity Technologies (UK) Ltd. +import { ApiPromise } from '@polkadot/api'; +import { KeyringPair } from '@polkadot/keyring/types'; + +import { AssetTransferApi } from '../../src'; +import { TxResult } from '../../src/types'; +import { EXTRINSIC_VERSION } from '../consts'; + +const createAssetApi = (api: ApiPromise, specName: string, safeXcmVersion: number): AssetTransferApi => { + const injectedRegistry = { + rococo: { + '1836': { + tokens: ['HOP'], + assetsInfo: {}, + foreignAssetsInfo: {}, + specName: 'trappist-rococo', + poolPairsInfo: {}, + }, + }, + }; + + const assetApi = new AssetTransferApi(api, specName, safeXcmVersion, { injectedRegistry }); + + return assetApi; +}; + +const createLocalTransferTransaction = async ( + origin: KeyringPair, + destChainId: string, + destAddr: string, + assetIds: string[], + amounts: string[], + opts: object, + api: ApiPromise, + specName: string, + safeXcmVersion: number, +) => { + const assetApi = createAssetApi(api, specName, safeXcmVersion); + + let localTransferInfo: TxResult<'payload'>; + try { + localTransferInfo = await assetApi.createTransferTransaction(destChainId, destAddr, assetIds, amounts, opts); + + const payload = api.createType('ExtrinsicPayload', localTransferInfo.tx, { + version: EXTRINSIC_VERSION, + }); + + const extrinsic = api.registry.createType('Extrinsic', { method: payload.method }, { version: 4 }); + + await api.tx(extrinsic).signAndSend(origin); + } catch (e) { + console.error(e); + throw Error(e as string); + } +}; + +const createPayFeesTransaction = async ( + origin: KeyringPair, + destChainId: string, + destAddr: string, + assetIds: string[], + amounts: string[], + opts: {}, + api: ApiPromise, + specName: string, + safeXcmVersion: number, +) => { + const assetApi = createAssetApi(api, specName, safeXcmVersion); + + let transferWithFeesInfo: TxResult<'payload'>; + try { + transferWithFeesInfo = await assetApi.createTransferTransaction(destChainId, destAddr, assetIds, amounts, opts); + + const payload = api.createType('ExtrinsicPayload', transferWithFeesInfo.tx, { + version: EXTRINSIC_VERSION, + }); + + const extrinsic = api.registry.createType('Extrinsic', { method: payload.method }, { version: 4 }); + + await api.tx(extrinsic).signAndSend(origin); + } catch (e) { + console.error(e); + throw Error(e as string); + } +}; + +export const liquidPoolsTests: { [K: string]: Function } = { createLocalTransferTransaction, createPayFeesTransaction }; diff --git a/e2e-tests/tests/local.ts b/e2e-tests/tests/local.ts new file mode 100644 index 00000000..5cc3aec4 --- /dev/null +++ b/e2e-tests/tests/local.ts @@ -0,0 +1,153 @@ +// Copyright 2023 Parity Technologies (UK) Ltd. + +import { ApiPromise } from '@polkadot/api'; +import { KeyringPair } from '@polkadot/keyring/types'; +import { EXTRINSIC_VERSION } from 'consts'; + +import { AssetTransferApi } from '../../src'; +import { TxResult } from '../../src/types'; + +const createAssetApi = (api: ApiPromise, specName: string, safeXcmVersion: number): AssetTransferApi => { + const injectedRegistry = { + rococo: { + '1836': { + tokens: ['HOP'], + assetsInfo: {}, + foreignAssetsInfo: {}, + specName: 'trappist-rococo', + poolPairsInfo: {}, + }, + }, + }; + + const assetApi = new AssetTransferApi(api, specName, safeXcmVersion, { injectedRegistry }); + + return assetApi; +}; + +const createLocalTransferTransaction = async ( + origin: KeyringPair, + destChainId: string, + destAddr: string, + assetIds: string[], + amounts: string[], + opts: {}, + api: ApiPromise, + specName: string, + safeXcmVersion: number, +) => { + const assetApi = createAssetApi(api, specName, safeXcmVersion); + + let localTransferInfo: TxResult<'payload'>; + try { + localTransferInfo = await assetApi.createTransferTransaction(destChainId, destAddr, assetIds, amounts, opts); + + const payload = api.createType('ExtrinsicPayload', localTransferInfo.tx, { + version: EXTRINSIC_VERSION, + }); + + const extrinsic = api.registry.createType('Extrinsic', { method: payload.method }, { version: 4 }); + + await api.tx(extrinsic).signAndSend(origin); + } catch (e) { + console.error(e); + throw Error(e as string); + } +}; + +const createLocalTransferTransactionWithFees = async ( + origin: KeyringPair, + destChainId: string, + destAddr: string, + assetIds: string[], + amounts: string[], + opts: {}, + api: ApiPromise, + specName: string, + safeXcmVersion: number, +) => { + const assetApi = createAssetApi(api, specName, safeXcmVersion); + + let localTransferInfo: TxResult<'payload'>; + try { + localTransferInfo = await assetApi.createTransferTransaction(destChainId, destAddr, assetIds, amounts, opts); + + const payload = api.createType('ExtrinsicPayload', localTransferInfo.tx, { + version: EXTRINSIC_VERSION, + }); + + const extrinsic = api.registry.createType('Extrinsic', { method: payload.method }, { version: 4 }); + + await api.tx(extrinsic).signAndSend(origin); + } catch (e) { + console.error(e); + throw Error(e as string); + } +}; + +const createLimitedNativeTransferToRelay = async ( + origin: KeyringPair, + destChainId: string, + destAddr: string, + assetIds: string[], + amounts: string[], + opts: {}, + api: ApiPromise, + specName: string, + safeXcmVersion: number, +) => { + const assetApi = createAssetApi(api, specName, safeXcmVersion); + + let localTransferInfo: TxResult<'payload'>; + try { + localTransferInfo = await assetApi.createTransferTransaction(destChainId, destAddr, assetIds, amounts, opts); + + const payload = api.createType('ExtrinsicPayload', localTransferInfo.tx, { + version: EXTRINSIC_VERSION, + }); + + const extrinsic = api.registry.createType('Extrinsic', { method: payload.method }, { version: 4 }); + + await api.tx(extrinsic).signAndSend(origin); + } catch (e) { + console.error(e); + throw Error(e as string); + } +}; + +const createLimitedNativeTransferToSystem = async ( + origin: KeyringPair, + destChainId: string, + destAddr: string, + assetIds: string[], + amounts: string[], + opts: {}, + api: ApiPromise, + specName: string, + safeXcmVersion: number, +) => { + const assetApi = createAssetApi(api, specName, safeXcmVersion); + + let localTransferInfo: TxResult<'payload'>; + try { + localTransferInfo = await assetApi.createTransferTransaction(destChainId, destAddr, assetIds, amounts, opts); + + const payload = api.createType('ExtrinsicPayload', localTransferInfo.tx, { + version: EXTRINSIC_VERSION, + }); + + const extrinsic = api.registry.createType('Extrinsic', { method: payload.method }, { version: 4 }); + + await api.tx(extrinsic).signAndSend(origin); + } catch (e) { + console.error(e); + throw Error(e as string); + } +}; + +export const localTests: { [K: string]: Function } = { + createLocalTransferTransaction, + createLocalTransferTransactionWithFees, + createLimitedNativeTransferToRelay, + createLimitedNativeTransferToSystem, +}; diff --git a/e2e-tests/tsconfig.json b/e2e-tests/tsconfig.json new file mode 100644 index 00000000..928e51d6 --- /dev/null +++ b/e2e-tests/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "@substrate/dev/config/tsconfig.json", + "compilerOptions": { + "baseUrl": ".", + "outDir": "build", + "suppressImplicitAnyIndexErrors": true, + "ignoreDeprecations": "5.0", + "resolveJsonModule": true, + }, +} diff --git a/e2e-tests/verification.ts b/e2e-tests/verification.ts new file mode 100644 index 00000000..99a88a4e --- /dev/null +++ b/e2e-tests/verification.ts @@ -0,0 +1,24 @@ +// Copyright 2023 Parity Technologies (UK) Ltd. +import { IBalance } from './balance'; + +/** + * This function verifies whether the test transaction impacted the destination's + * account balance + * @param assetIds assets to be queried + * @param amounts expected final balance + * @param destBalance stored queried balance + * @returns whetere the balance was modified as expected or not + */ +export const verification = (assetIds: string[], amounts: string[], destBalance: IBalance) => { + const destInitialBalance: [string, number][] = destBalance.initial; + const destFinalBalance: [string, number][] = destBalance.final; + const correctlyReceived: [string, boolean][] = []; + + let check = true; + for (let i = 0; i < assetIds.length; i++) { + check = destInitialBalance[i][1] + Number(amounts[i]) == destFinalBalance[i][1]; + correctlyReceived.push([destInitialBalance[i][0], check]); + } + + return correctlyReceived; +}; diff --git a/e2e-tests/zombienet.sh b/e2e-tests/zombienet.sh new file mode 100755 index 00000000..2f7831df --- /dev/null +++ b/e2e-tests/zombienet.sh @@ -0,0 +1,14 @@ +#!/bin/sh +if [ $(uname) = 'Darwin' ]; then + os='zombienet-macOS' +elif [ $(uname) = 'Linux' ]; then + if [ $(uname -m) = 'x86_64' ]; then + os='zombienet-linux-x64' + elif [ $(uname -m) = 'arm64' ]; then + os='zombienet-linux-arm64' + fi +fi + +sed -i 's="./zombienet/zombienet": .*"="./zombienet/zombienet": "'$os' -p native spawn ./zombienet/medium-network.toml"=' package.json + +yarn run zombienet diff --git a/package.json b/package.json index e63de041..11cb43aa 100644 --- a/package.json +++ b/package.json @@ -7,10 +7,17 @@ "build": "substrate-exec-rimraf ./lib && substrate-exec-tsc", "build:scripts": "substrate-exec-rimraf scripts/build/ && substrate-exec-tsc --project scripts/tsconfig.json", "build:examples": "substrate-exec-rimraf examples/build/ && substrate-exec-tsc --project examples/tsconfig.json", + "build:e2e": "substrate-exec-rimraf e2e-tests/build/ && substrate-exec-tsc --project e2e-tests/tsconfig.json", "start": "node ./lib/index.js", "start:zombienet-post-script": "yarn build:scripts && node ./scripts/build/testNetworkSetup.js", "start:zombienet-foreign-assets-script": "yarn build:scripts && node ./scripts/build/testNetworkForeignAssets.js", "start:zombienet-liquidity-assets-script": "yarn build:scripts && node ./scripts/build/testNetworkLiquidAssets.js", + "zombienet": "./zombienet/zombienet-linux-x64 -p native spawn ./zombienet/medium-network.toml", + "e2e:zombienet": "./e2e-tests/zombienet.sh", + "e2e:assets": "./e2e-tests/tests.sh --assets", + "e2e:local": "./e2e-tests/tests.sh --local", + "e2e:liquidity-assets": "./e2e-tests/tests.sh --liquidity-assets", + "e2e:foreign-assets": "./e2e-tests/tests.sh --foreign-assets", "lint": "substrate-dev-run-lint", "lint:fix": "substrate-dev-run-lint --fix", "test": "NODE_ENV=test substrate-exec-jest --detectOpenHandles", @@ -34,10 +41,13 @@ "packageManager": "yarn@4.0.2", "devDependencies": { "@substrate/dev": "^0.7.1", + "@types/cli-progress": "^3", "chalk": "4.1.2", + "cli-progress": "^3.12.0", "typedoc": "^0.25.4", "typedoc-plugin-missing-exports": "^1.0.0", - "typedoc-theme-hierarchy": "^4.0.0" + "typedoc-theme-hierarchy": "^4.0.0", + "ansi-colors": "^4.1.3" }, "dependencies": { "@polkadot/api": "^10.11.2", diff --git a/scripts/testNetworkForeignAssets.ts b/scripts/testNetworkForeignAssets.ts index 5f60a009..a8ff4d34 100644 --- a/scripts/testNetworkForeignAssets.ts +++ b/scripts/testNetworkForeignAssets.ts @@ -9,7 +9,7 @@ import { KUSAMA_ASSET_HUB_WS_URL, ROCOCO_ALICE_WS_URL, TRAPPIST_WS_URL } from '. import { awaitBlockProduction, awaitEpochChange, delay, logWithDate } from './util'; const fAssetSetMetadataCall = (assetHubApi: ApiPromise): `0x${string}` => { - const trappistMultiLocation = { + const trappistMultilocation = { parents: 1, interior: { X1: { @@ -18,7 +18,12 @@ const fAssetSetMetadataCall = (assetHubApi: ApiPromise): `0x${string}` => { }, }; - const setMetadataTx = assetHubApi.tx.foreignAssets.setMetadata(trappistMultiLocation, 'Trappist Hop', 'Hop', 12); + const setMetadataTx = assetHubApi.tx.foreignAssets.setMetadata( + trappistMultilocation, + 'Asset Hub Rococo Hop', + 'Hop', + 12, + ); const hexCall = assetHubApi.registry .createType('Call', { @@ -31,7 +36,7 @@ const fAssetSetMetadataCall = (assetHubApi: ApiPromise): `0x${string}` => { }; const fAssetCreateCall = (assetHubApi: ApiPromise): `0x${string}` => { - const trappistMultiLocation = { + const trappistMultilocation = { parents: 1, interior: { X1: { @@ -41,7 +46,7 @@ const fAssetCreateCall = (assetHubApi: ApiPromise): `0x${string}` => { }; const createTx = assetHubApi.tx.foreignAssets.create( - trappistMultiLocation, + trappistMultilocation, '5Eg2fnsjAAr8RGZfa8Sy5mYFPabA9ZLNGYECCKXPD6xnK6D2', // Sibling 1836 -> ParaId '100000000000', ); @@ -58,7 +63,7 @@ const fAssetCreateCall = (assetHubApi: ApiPromise): `0x${string}` => { const sudoCallWrapper = (trappistApi: ApiPromise, call: `0x${string}`) => { // Double encode the call - const xcmDoubleEncoded = trappistApi.createType('StagingXcmDoubleEncoded', { + const xcmDoubleEncoded = trappistApi.createType('XcmDoubleEncoded', { encoded: call, }); @@ -233,7 +238,7 @@ const main = async () => { }); await trappistApi.disconnect().then(() => { - logWithDate(chalk.blue('Polkadot-js successfully disconnected from trappist')); + logWithDate(chalk.blue('Polkadot-js successfully disconnected from Trappist')); }); }; diff --git a/scripts/testNetworkSetup.ts b/scripts/testNetworkSetup.ts index 4cc80727..c4b32ac4 100644 --- a/scripts/testNetworkSetup.ts +++ b/scripts/testNetworkSetup.ts @@ -123,7 +123,7 @@ const main = async () => { assetInfo.assetSymbol, assetInfo.assetDecimals, ), - kusamaAssetHubApi.tx.assets.mint(assetInfo.assetId, alice.address, 1000 * 120000000), + kusamaAssetHubApi.tx.assets.mint(assetInfo.assetId, alice.address, 100000 * 120000000), ]; const batch = kusamaAssetHubApi.tx.utility.batchAll(txs); diff --git a/src/AssetTransferApi.ts b/src/AssetTransferApi.ts index 8a12a135..674e5b73 100644 --- a/src/AssetTransferApi.ts +++ b/src/AssetTransferApi.ts @@ -736,7 +736,7 @@ export class AssetTransferApi { 'CheckMortality', 'CheckNonce', 'CheckWeight', - 'ChargeTransactionPayment', + 'ChargeAssetTxPayment', ], tip: this.api.registry.createType('Compact', 0).toHex(), version: tx.version, @@ -835,16 +835,15 @@ export class AssetTransferApi { if (this.api.query.assetConversion !== undefined) { try { for (const poolPairsData of await this.api.query.assetConversion.pools.entries()) { - const poolStorageKeyData = poolPairsData[0]; + const tokenPairs = poolPairsData[0]; // remove any commas from multilocation key values e.g. Parachain: 2,125 -> Parachain: 2125 - const poolAssetDataStr = JSON.stringify(poolStorageKeyData).replace(/(\d),/g, '$1'); - const palletAssetConversionNativeOrAssetIdData = sanitizeKeys( - JSON.parse(poolAssetDataStr), - ) as UnionXcmMultiLocation[]; + const sanitizedTokenPairs = JSON.stringify(tokenPairs).replace(/(\d),/g, '$1'); + const firstLpTokenSlice = sanitizedTokenPairs.slice(1, -1).slice(0, sanitizedTokenPairs.indexOf('},{"p')); + const secondLpTokenSlice = sanitizedTokenPairs.slice(1, -1).slice(sanitizedTokenPairs.indexOf(',{"p')); - const firstLpToken = palletAssetConversionNativeOrAssetIdData[0]; - const secondLpToken = palletAssetConversionNativeOrAssetIdData[1]; + const firstLpToken = sanitizeKeys(JSON.parse(firstLpTokenSlice)) as UnionXcmMultiLocation; + const secondLpToken = sanitizeKeys(JSON.parse(secondLpTokenSlice)) as UnionXcmMultiLocation; if ( JSON.stringify(firstLpToken) == JSON.stringify(feeAsset) || diff --git a/src/registry/Registry.ts b/src/registry/Registry.ts index 1f55f44b..98a66516 100644 --- a/src/registry/Registry.ts +++ b/src/registry/Registry.ts @@ -6,6 +6,7 @@ import { ASSET_HUB_CHAIN_ID, KUSAMA_ASSET_HUB_SPEC_NAMES, POLKADOT_ASSET_HUB_SPEC_NAMES, + ROCOCO_ASSET_HUB_SPEC_NAME, WESTEND_ASSET_HUB_SPEC_NAMES, } from '../consts'; import type { AssetTransferApiOpts } from '../types'; @@ -259,7 +260,8 @@ export class Registry { if ( POLKADOT_ASSET_HUB_SPEC_NAMES.includes(specName.toLowerCase()) || KUSAMA_ASSET_HUB_SPEC_NAMES.includes(specName.toLowerCase()) || - WESTEND_ASSET_HUB_SPEC_NAMES.includes(specName.toLowerCase()) + WESTEND_ASSET_HUB_SPEC_NAMES.includes(specName.toLowerCase()) || + ROCOCO_ASSET_HUB_SPEC_NAME.includes(specName.toLowerCase()) ) { this.specNameToIdCache.set(specName, '1000'); return '1000'; diff --git a/src/registry/findRelayChain.ts b/src/registry/findRelayChain.ts index d683ebab..b6a9df83 100644 --- a/src/registry/findRelayChain.ts +++ b/src/registry/findRelayChain.ts @@ -29,7 +29,7 @@ export const findRelayChain = (specName: string, registry: ChainInfoRegistry): R return 'westend'; const rococoChains = Object.keys(registry.rococo).map((val) => registry.rococo[val].specName); - if (rococoChains.includes(specName.toLowerCase()) || ROCOCO_ASSET_HUB_SPEC_NAME) { + if (rococoChains.includes(specName.toLowerCase()) || ROCOCO_ASSET_HUB_SPEC_NAME.includes(specName.toLowerCase())) { return 'rococo'; } diff --git a/yarn.lock b/yarn.lock index d38d6b96..4be2317c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1350,7 +1350,10 @@ __metadata: "@polkadot/api": "npm:^10.11.2" "@substrate/asset-transfer-api-registry": "npm:^0.2.13" "@substrate/dev": "npm:^0.7.1" + "@types/cli-progress": "npm:^3" + ansi-colors: "npm:^4.1.3" chalk: "npm:4.1.2" + cli-progress: "npm:^3.12.0" typedoc: "npm:^0.25.4" typedoc-plugin-missing-exports: "npm:^1.0.0" typedoc-theme-hierarchy: "npm:^4.0.0" @@ -1467,6 +1470,15 @@ __metadata: languageName: node linkType: hard +"@types/cli-progress@npm:^3": + version: 3.11.5 + resolution: "@types/cli-progress@npm:3.11.5" + dependencies: + "@types/node": "npm:*" + checksum: cb19187637b0a9b92219eab8d3d42250f1773328c24cb265d1bc677e3017f512e95e834e4846bcf0964efc232a13f86f7ef01843be804daa5433cc655c375bb3 + languageName: node + linkType: hard + "@types/graceful-fs@npm:^4.1.3": version: 4.1.6 resolution: "@types/graceful-fs@npm:4.1.6" @@ -1751,6 +1763,13 @@ __metadata: languageName: node linkType: hard +"ansi-colors@npm:^4.1.3": + version: 4.1.3 + resolution: "ansi-colors@npm:4.1.3" + checksum: 43d6e2fc7b1c6e4dc373de708ee76311ec2e0433e7e8bd3194e7ff123ea6a747428fc61afdcf5969da5be3a5f0fd054602bec56fc0ebe249ce2fcde6e649e3c2 + languageName: node + linkType: hard + "ansi-escapes@npm:^4.2.1": version: 4.3.2 resolution: "ansi-escapes@npm:4.3.2" @@ -2155,6 +2174,15 @@ __metadata: languageName: node linkType: hard +"cli-progress@npm:^3.12.0": + version: 3.12.0 + resolution: "cli-progress@npm:3.12.0" + dependencies: + string-width: "npm:^4.2.3" + checksum: a6a549919a7461f5e798b18a4a19f83154bab145d3ec73d7f3463a8db8e311388c545ace1105557760a058cc4999b7f28c9d8d24d9783ee2912befb32544d4b8 + languageName: node + linkType: hard + "cliui@npm:^8.0.1": version: 8.0.1 resolution: "cliui@npm:8.0.1" diff --git a/zombienet/medium-network.toml b/zombienet/medium-network.toml index 45459d8b..6bec642d 100644 --- a/zombienet/medium-network.toml +++ b/zombienet/medium-network.toml @@ -1,5 +1,5 @@ [relaychain] -default_command = "./bin/polkadot" +default_command = "./zombienet/bin/polkadot" default_args = [ "-lparachain=debug" ] chain = "rococo-local" @@ -32,13 +32,13 @@ cumulus_based = true [[parachains.collators]] name = "asset-hub-rococo-collator01" - command = "./bin/polkadot-parachain" + command = "./zombienet/bin/polkadot-parachain" args = ["--log=xcm=trace,pallet-assets=trace"] ws_port = 9910 [[parachains.collators]] name = "asset-hub-rococo-collator02" - command = "./bin/polkadot-parachain" + command = "./zombienet/bin/polkadot-parachain" ws_port = 9911 args = ["--log=xcm=trace,pallet-assets=trace"] @@ -50,13 +50,13 @@ chain = "trappist-local" [[parachains.collators]] name = "trappist-collator01" - command = "./bin/trappist-node" + command = "./zombienet/bin/trappist-node" ws_port = 9920 args = ["--log=xcm=trace,pallet-assets=trace"] [[parachains.collators]] name = "trappist-collator02" - command = "./bin/trappist-node" + command = "./zombienet/bin/trappist-node" ws_port = 9921 args = ["--log=xcm=trace,pallet-assets=trace"]