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/e2e-tests/balance.ts b/e2e-tests/balance.ts new file mode 100644 index 00000000..f61824c5 --- /dev/null +++ b/e2e-tests/balance.ts @@ -0,0 +1,98 @@ +// Copyright 2023 Parity Technologies (UK) Ltd. + +import { ApiPromise } from '@polkadot/api'; +import type { FrameSystemAccountInfo, PalletAssetsAssetAccount } from '@polkadot/types/lookup'; +import type { Option } from '@polkadot/types-codec'; +export interface IBalance { + initial: [string, number][]; + final: [string, number][]; +} + +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; + 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); + 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); + balances.initial.push(['0', accountInfo.data.free.toBn().toNumber()]); + } else { + balances = balance; + accountInfo = await api.query.system.account(address); + balances.final.push(['0', accountInfo.data.free.toBn().toNumber()]); + } + return balances; + case '--assets': + if (!balance) { + for (const assetId of assetIds) { + accountInfo = await api.query.assets.account(assetId, address); + 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.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..4424070e --- /dev/null +++ b/e2e-tests/consts.ts @@ -0,0 +1,17 @@ +// 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 MOONRIVER_WS_URL = 'ws://127.0.0.1:9931'; + +export const BOB_ROC_ADDR = '5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty'; +export const FERDE_ROC_ADDR = '5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL'; +export const BOB_KAH_ADDR = '5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty'; +export const FERDE_KAH_ADDR = '5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL'; + +export const ASSET_ID = 1; + +export const MOONRIVER_ADDR = '5Eg2fnt8QeGtg4mcc5ELxPx6qxP2FSEWHbbYXENZ17ormkGP'; +export const TRAPPIST_ADDR = '5Eg2fntDDP4P8TjqRg3Jq89y5boE26JUMM3D7VU3bCAp76nc'; +export const ASSET_HUB_ADDR = '5Eg2fntNprdN3FgH4sfEaaZhYtddZQSQUqvYJ1f2mLtinVhV'; diff --git a/e2e-tests/executor.ts b/e2e-tests/executor.ts new file mode 100644 index 00000000..0d3545fb --- /dev/null +++ b/e2e-tests/executor.ts @@ -0,0 +1,156 @@ +// Copyright 2023 Parity Technologies (UK) Ltd. +import { ApiPromise, WsProvider } from '@polkadot/api'; +import { Keyring } from '@polkadot/keyring'; +// import { KeyringPair } from '@polkadot/keyring/types'; +import { cryptoWaitReady } from '@polkadot/util-crypto'; + +import { constructApiPromise } from '../src'; +import { balanceTracker, IBalance } from './balance'; +import { KUSAMA_ASSET_HUB_WS_URL, MOONRIVER_WS_URL, ROCOCO_ALICE_WS_URL, TRAPPIST_WS_URL } from './consts'; +import { assetTests, foreignAssetsTests, IndividualTest, liquidPoolsTests, localTests, tests } from './tests'; +import { verification } from './verification'; +import { delay } from '../scripts/util' + +const executor = async (testCase: string) => { + let originWsUrl = ''; + let destWsUrl = ''; + + let testData: IndividualTest[] = []; + + await cryptoWaitReady(); + + const keyring = new Keyring({ type: 'sr25519' }); + + switch (testCase) { + case '--foreign-assets': + testData = tests.foreignAssets; + break; + case '--liquidity-assets': + testData = tests.liquidPools; + break; + case '--local': + testData = tests.local; + break; + case '--assets': + testData = tests.assets; + break; + } + + let n: { [K: string]: Function } = {}; + + switch (testCase) { + case '--foreign-assets': + n = foreignAssetsTests; + break; + case '--liquidity-assets': + n = liquidPoolsTests; + break; + case '--local': + n = localTests; + break; + case '--assets': + n = assetTests; + break; + } + console.log(n) + + let originChainId = ''; + let destChainId = ''; + let originAddr = ''; + let destAddr = ''; + let assetIds: string[] = []; + let amounts: string[] = []; + let opts: object = {}; + + for (const t of testData) { + + originChainId = t.args[0]; + destChainId = t.args[1]; + originAddr = t.args[2]; + destAddr = t.args[3]; + assetIds = t.args[4].slice(1, -1).split(','); + amounts = t.args[5].slice(1, -1).split(','); + opts = JSON.parse(t.args[6]); + + switch (originChainId) { + case '0': + originWsUrl = ROCOCO_ALICE_WS_URL; + break; + case '1000': + originWsUrl = KUSAMA_ASSET_HUB_WS_URL; + break; + case '2500': + originWsUrl = TRAPPIST_WS_URL; + break; + case '2600': + originWsUrl = MOONRIVER_WS_URL; + break; + } + + if (originChainId == destChainId) { + destWsUrl = originWsUrl; + } else { + switch (destChainId) { + case '0': + destWsUrl = ROCOCO_ALICE_WS_URL; + break; + case '1000': + destWsUrl = KUSAMA_ASSET_HUB_WS_URL; + break; + case '2500': + destWsUrl = TRAPPIST_WS_URL; + break; + case '2600': + destWsUrl = MOONRIVER_WS_URL; + 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; + let 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); + + let destFinalBalance: IBalance = await balanceTracker( + destinationApi, + testCase, + destAddr, + assetIds, + destInitialBalance + ); + + const correctlyReceived = verification(assetIds, amounts, destFinalBalance); + + for (let i = 0; i < assetIds.length; i++) { + if (correctlyReceived[i][1]) { + console.log('all good'); + } else { + console.log('badd'); + } + } + + await delay(12000); + + originApi.disconnect(); + destinationApi.disconnect(); + } + +}; + +executor(process.argv[2]).finally(() => process.exit()); diff --git a/e2e-tests/logger.ts b/e2e-tests/logger.ts new file mode 100644 index 00000000..c9193b86 --- /dev/null +++ b/e2e-tests/logger.ts @@ -0,0 +1 @@ +// Copyright 2023 Parity Technologies (UK) Ltd. diff --git a/e2e-tests/tests.sh b/e2e-tests/tests.sh new file mode 100755 index 00000000..489aee7b --- /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..8759e9fc --- /dev/null +++ b/e2e-tests/tests/assets.ts @@ -0,0 +1,72 @@ +// 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'; + +const createAssetApi = (api: ApiPromise, specName: string, safeXcmVersion: number): AssetTransferApi => { + const injectedRegistry = { + rococo: { + '2500': { + 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 localTransfer: TxResult<'submittable'>; + try { + localTransfer = await assetApi.createTransferTransaction(destChainId, destAddr, assetIds, amounts, opts); + await localTransfer.tx.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 localTransfer: TxResult<'submittable'>; + try { + localTransfer = await assetApi.createTransferTransaction(destChainId, destAddr, assetIds, amounts, opts); + await localTransfer.tx.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..133aeb75 --- /dev/null +++ b/e2e-tests/tests/foreignAssets.ts @@ -0,0 +1,49 @@ +// 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'; + +const createAssetApi = (api: ApiPromise, specName: string, safeXcmVersion: number): AssetTransferApi => { + const injectedRegistry = { + kusama: { + '3000': { + tokens: ['HOP'], + assetsInfo: {}, + foreignAssetsInfo: {}, + specName: 'trappist', + poolPairsInfo: {}, + }, + }, + }; + const assetApi = new AssetTransferApi(api, specName, safeXcmVersion, { + injectedRegistry, + }); + + return assetApi; +}; + +const createTransferTransaction = 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 localTransfer: TxResult<'submittable'>; + try { + localTransfer = await assetApi.createTransferTransaction(destChainId, destAddr, assetIds, amounts, opts); + await localTransfer.tx.signAndSend(origin); + } catch (e) { + console.error(e); + throw Error(e as string); + } +}; +export const foreignAssetsTests: { [K: string]: Function } = { createTransferTransaction }; diff --git a/e2e-tests/tests/index.ts b/e2e-tests/tests/index.ts new file mode 100644 index 00000000..8a010038 --- /dev/null +++ b/e2e-tests/tests/index.ts @@ -0,0 +1,133 @@ +// Copyright 2023 Parity Technologies (UK) Ltd. + +export { assetTests } from './assets'; +export { foreignAssetsTests } from './foreignAssets'; +export { liquidPoolsTests } from './liquidPools'; +export { localTests } from './local'; +import { BOB_KAH_ADDR, BOB_ROC_ADDR, FERDE_KAH_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: [ + { + // This will declare the call to use + test: 'createTransferTransactionCall', + // This will be all the args for the above call + args: [ + '2500', + '1000', + '//Alice', + BOB_KAH_ADDR, + '[{ "parents": "1", "interior": { "X2": [{ "Parachain": "2500" }, { "GeneralIndex": "0" }]}}]', + '[200000000000]', + '{ "format": "submittable", "xcmVersion": 3 }', + ], + // This will be a tuple that will allow us to verify if the xcm message + // succesfully went through on the other end + verification: ['', ''], + }, + ], + liquidPools: [ + { + // This will declare the call to use + test: 'createLocalTransferTransaction', + // This will be all the args for the above call + args: [ + '1000', + '1000', + '//Alice', + BOB_KAH_ADDR, + '[0]', + '[2000]', + '{ "format": "submittable", "transferLiquidToken": true }', + ], + // This will be a tuple that will allow us to verify if the xcm message + // succesfully went through on the other end + verification: ['[0]', '[2000]'], + }, + { + // This will declare the call to use + test: 'createPayFeesTransaction', + // This will be all the args for the above call + args: [ + '1000', + '1000', + '//Alice', + BOB_KAH_ADDR, + '[0]', + '[30000]', + '{ "format": "submittable", "transferLiquidToken": true }', + ], + // This will be a tuple that will allow us to verify if the xcm message + // succesfully went through on the other end + verification: ['[0]', '[30000]'], + }, + ], + local: [ + { + // This will declare the call to use + test: 'createSystemLocalTransferTransaction', + // This will be all the args for the above call + args: [ + '1000', + '1000', + '//Alice', + BOB_KAH_ADDR, + '[]', + '[10000000000000]', + '{ "format": "submittable", "keepAlive": true }', + ], + // This will be a tuple that will allow us to verify if the xcm message + // succesfully went through on the other end + verification: ['[0]', '[10000000000000]'], + } + ], + assets: [ + { + // This will declare the call to use + test: 'createLocalSystemAssetsTransferTransaction', + // This will be all the args for the above call + args: [ + '1000', + '1000', + '//Alice', + BOB_ROC_ADDR, + '[1]', + '[3000000000000]', + '{ "format": "submittable", "keepAlive": true }', + ], + // This will be a tuple that will allow us to verify if the xcm message + // succesfully went through on the other end + verification: ['[1]', '[30000]'], + }, + { + // This will declare the call to use + test: 'createPayFeesTransaction', + // This will be all the args for the above call + args: [ + '1000', + '1000', + '//Bob', + FERDE_KAH_ADDR, + '[1]', + '[200000000000]', + '{ "format": "submittable", "keepAlive": true }', + ], + // This will be a tuple that will allow us to verify if the xcm message + // succesfully went through on the other end + verification: ['[1]', '[2000]'], + }, + ], +}; diff --git a/e2e-tests/tests/liquidPools.ts b/e2e-tests/tests/liquidPools.ts new file mode 100644 index 00000000..74b69e23 --- /dev/null +++ b/e2e-tests/tests/liquidPools.ts @@ -0,0 +1,73 @@ +// 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'; + +const createAssetApi = (api: ApiPromise, specName: string, safeXcmVersion: number): AssetTransferApi => { + const injectedRegistry = { + kusama: { + '3000': { + tokens: ['HOP'], + assetsInfo: {}, + foreignAssetsInfo: {}, + specName: 'trappist', + 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<'submittable'>; + try { + localTransferInfo = await assetApi.createTransferTransaction(destChainId, destAddr, assetIds, amounts, opts); + await localTransferInfo.tx.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<'submittable'>; + try { + localTransferInfo = await assetApi.createTransferTransaction(destChainId, destAddr, assetIds, amounts, opts); + await localTransferInfo.tx.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..78689811 --- /dev/null +++ b/e2e-tests/tests/local.ts @@ -0,0 +1,101 @@ +// 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'; + +const createAssetApi = (api: ApiPromise, specName: string, safeXcmVersion: number): AssetTransferApi => { + const injectedRegistry = { + kusama: { + '3000': { + tokens: ['HOP'], + assetsInfo: {}, + foreignAssetsInfo: {}, + specName: 'trappist', + poolPairsInfo: {}, + }, + }, + }; + const assetApi = new AssetTransferApi(api, specName, safeXcmVersion, { + injectedRegistry, + }); + + return assetApi; +}; + +const createSystemLocalTransferTransaction = 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<'submittable'>; + try { + localTransferInfo = await assetApi.createTransferTransaction(destChainId, destAddr, assetIds, amounts, opts); + await localTransferInfo.tx.signAndSend(origin); + } catch (e) { + console.error(e); + throw Error(e as string); + } +}; + +const createRelayLocalTransferTransaction = 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<'submittable'>; + try { + localTransferInfo = await assetApi.createTransferTransaction(destChainId, destAddr, assetIds, amounts, opts); + await localTransferInfo.tx.signAndSend(origin); + } catch (e) { + console.error(e); + throw Error(e as string); + } +}; + +const createRelayToSystemTransferTransaction = 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<'submittable'>; + try { + localTransferInfo = await assetApi.createTransferTransaction(destChainId, destAddr, assetIds, amounts, opts); + await localTransferInfo.tx.signAndSend(origin); + } catch (e) { + console.error(e); + throw Error(e as string); + } +}; + +export const localTests: { [K: string]: Function } = { + createSystemLocalTransferTransaction, + createRelayLocalTransferTransaction, + createRelayToSystemTransferTransaction, +}; diff --git a/e2e-tests/tsconfig.json b/e2e-tests/tsconfig.json new file mode 100644 index 00000000..b3d794f0 --- /dev/null +++ b/e2e-tests/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "@substrate/dev/config/tsconfig.json", + "compilerOptions": { + "baseUrl": ".", + "outDir": "build", + "suppressImplicitAnyIndexErrors": true, + "resolveJsonModule": true, + }, +} diff --git a/e2e-tests/verification.ts b/e2e-tests/verification.ts new file mode 100644 index 00000000..c58a47c8 --- /dev/null +++ b/e2e-tests/verification.ts @@ -0,0 +1,20 @@ +// Copyright 2023 Parity Technologies (UK) Ltd. +import { IBalance } from 'balance'; + +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][] = []; + console.log(destInitialBalance) + console.log(destFinalBalance) + + let check: boolean = 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..0bcb857c --- /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 \ No newline at end of file diff --git a/package.json b/package.json index 5ddd00e7..349a0e17 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", diff --git a/scripts/consts.ts b/scripts/consts.ts index fac167a5..1a3e85ab 100644 --- a/scripts/consts.ts +++ b/scripts/consts.ts @@ -3,3 +3,4 @@ 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 MOONRIVER_WS_URL = 'ws://127.0.0.1:9931'; diff --git a/scripts/testNetworkForeignAssets.ts b/scripts/testNetworkForeignAssets.ts index 79667e1e..c1be1d4a 100644 --- a/scripts/testNetworkForeignAssets.ts +++ b/scripts/testNetworkForeignAssets.ts @@ -13,7 +13,7 @@ const fAssetSetMetadataCall = (assetHubApi: ApiPromise): `0x${string}` => { parents: 1, interior: { X1: { - parachain: 1836, + parachain: 3000, }, }, }; @@ -35,14 +35,14 @@ const fAssetCreateCall = (assetHubApi: ApiPromise): `0x${string}` => { parents: 1, interior: { X1: { - parachain: 1836, + parachain: 3000, }, }, }; const createTx = assetHubApi.tx.foreignAssets.create( trappistMultiLocation, - '5Eg2fnsjAAr8RGZfa8Sy5mYFPabA9ZLNGYECCKXPD6xnK6D2', // Sibling 1836 -> ParaId + '5Eg2fntDDP4P8TjqRg3Jq89y5boE26JUMM3D7VU3bCAp76nc', // Sibling 1836 -> ParaId '100000000000' ); @@ -58,7 +58,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, }); @@ -210,8 +210,8 @@ const main = async () => { logWithDate(chalk.magenta('Sending funds to Trappist Sibling on Kusama AssetHub')); - await kusamaAssetHubApi.tx.balances - .transferKeepAlive('5Eg2fnsjAAr8RGZfa8Sy5mYFPabA9ZLNGYECCKXPD6xnK6D2', 10000000000000) + await kusamaAssetHubApi.tx.balances // ParaID 3000 + .transferKeepAlive('5Eg2fntDDP4P8TjqRg3Jq89y5boE26JUMM3D7VU3bCAp76nc', 10000000000000) .signAndSend(bob); const foreignAssetsCreateSudoXcmCall = createForeignAssetViaSudo(kusamaAssetHubApi, trappistApi); diff --git a/scripts/testNetworkLiquidAssets.ts b/scripts/testNetworkLiquidAssets.ts index bab1f2ff..8d87374c 100644 --- a/scripts/testNetworkLiquidAssets.ts +++ b/scripts/testNetworkLiquidAssets.ts @@ -80,8 +80,12 @@ const main = async () => { const hrmpChannelCalls = []; - hrmpChannelCalls.push(openHrmpChannels(relayApi, Number(1000), Number(1836))); - hrmpChannelCalls.push(openHrmpChannels(relayApi, Number(1836), Number(1000))); + hrmpChannelCalls.push(openHrmpChannels(relayApi, Number(1000), Number(3000))); + hrmpChannelCalls.push(openHrmpChannels(relayApi, Number(1000), Number(4000))); + hrmpChannelCalls.push(openHrmpChannels(relayApi, Number(3000), Number(1000))); + hrmpChannelCalls.push(openHrmpChannels(relayApi, Number(3000), Number(4000))); + hrmpChannelCalls.push(openHrmpChannels(relayApi, Number(4000), Number(1000))); + hrmpChannelCalls.push(openHrmpChannels(relayApi, Number(4000), Number(3000))); await relayApi.tx.sudo.sudo(relayApi.tx.utility.batchAll(hrmpChannelCalls)).signAndSend(alice); diff --git a/scripts/testNetworkSetup.ts b/scripts/testNetworkSetup.ts index 06d49cf6..d6789aac 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/zombienet/medium-network.toml b/zombienet/medium-network.toml index c5cdc83f..492c3614 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,34 +32,52 @@ cumulus_based = true [[parachains.collators]] name = "statemine-collator01" - command = "./bin/polkadot-parachain" + command = "./zombienet/bin/polkadot-parachain" args = ["--log=xcm=trace,pallet-assets=trace"] ws_port = 9910 [[parachains.collators]] name = "statemine-collator02" - command = "./bin/polkadot-parachain" + command = "./zombienet/bin/polkadot-parachain" ws_port = 9911 args = ["--log=xcm=trace,pallet-assets=trace"] [[parachains]] -id = 1836 +id = 2500 add_to_genesis = true cumulus_based = true 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"] +[[parachains]] +id = 2600 +add_to_genesis = true +cumulus_based = true +chain = "moonriver-local" + + [[parachains.collators]] + name = "moonriver-collator01" + command = "./zombienet/bin/moonbeam" + ws_port = 9930 + args = ["--log=xcm=trace,pallet-assets=trace"] + + [[parachains.collators]] + name = "moonriver-collator02" + command = "./zombienet/bin/moonbeam" + ws_port = 9931 + args = ["--log=xcm=trace,pallet-assets=trace"] + [types.Header] number = "u64" parent_hash = "Hash"