diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e7ad55df9..022330d21 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -68,16 +68,20 @@ integration-tests: image: paritytech/ci-unified:bullseye-1.70.0 stage: test variables: - CI: "true" + SPIRITNET_BLOCK_NUMBER: 6390611 + HYDRADX_BLOCK_NUMBER: 5235787 + POLKADOT_BLOCK_NUMBER: 21010819 + SPIRITNET_WASM_OVERRIDE: "../../target/debug/wbuild/spiritnet-runtime/spiritnet_runtime.compact.compressed.wasm" script: - cd ./integration-tests/chopsticks - curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash - export NVM_DIR="$HOME/.nvm" && . "$NVM_DIR/nvm.sh" --no-use - eval "[ -f .nvmrc ] && nvm install" && nvm use - yarn --immutable + - cargo build -p spiritnet-runtime - yarn ts-check - yarn lint - - yarn test:CI + - yarn test # TODO: The try-runtime-cli executable could be built as part of the Docker image directly, saving some time. test-try-runtime: diff --git a/integration-tests/chopsticks/.env-example b/integration-tests/chopsticks/.env-example index 964511506..5c0f67747 100644 --- a/integration-tests/chopsticks/.env-example +++ b/integration-tests/chopsticks/.env-example @@ -1,6 +1,10 @@ -HYDRADX_WS= -HYDRADX_PORT= -POLKADOT_WS= -POLKADOT_PORT= -SPIRITNET_WS= -SPIRITNET_PORT= +# Those env variables are mandatory for the tests to work. + +# The Spiritnet block number to be used in the tests +SPIRITNET_BLOCK_NUMBER= +# The HydraDx block number to be used in the tests +HYDRADX_BLOCK_NUMBER= +# The Polkadot block number to be used in the tests +POLKADOT_BLOCK_NUMBER= +# Directory path to the new WASM blob +SPIRITNET_WASM_OVERRIDE= diff --git a/integration-tests/chopsticks/README.md b/integration-tests/chopsticks/README.md new file mode 100644 index 000000000..44791ce12 --- /dev/null +++ b/integration-tests/chopsticks/README.md @@ -0,0 +1,74 @@ +# e2e Chopsticks tests + +This project is a set of end-to-end tests for the KILT protocol against other parachains. +Other functionalities such as a creation of DID can be easy inserted. + +## Getting Started + +These instructions will allow you to run the tests on your local machine for development and testing purposes. + +### Prerequisites + +- Node.js v20.11.0 (as specified in the [`.nvmrc`](https://github.com/nvm-sh/nvm)). With `nvm use` the right node version will be installed. +- npm (comes with Node.js) + + +### Installing + +To install the node modules call: + +```sh +yarn +``` + +### Running the tests + +In the package.json a script is provided. + +By calling the command below, the test will be executed: + +```sh +yarn test +``` + +Please make sure an appropriate env is set. + +### Env + +The project uses environment variables for configuration. You can find an example in the `.env-example` file. Copy this file to +.env and fill in the appropriate values. Explanation for the values are in the `.env-example` file provided. + + +## Built With + +[TypeScript](https://www.typescriptlang.org/) +[Chopsticks](https://github.com/AcalaNetwork/chopsticks) +[Polkadot API](https://github.com/polkadot-js/api) + +## Code Style + +This project uses Prettier and ESLint for code formatting and linting. The configuration for these tools can be found in the `.prettierrc` and `.eslintrc.json` files respectively. + +To check your code for style issues, run: + +```sh +yarn lint +``` + +To automatically fix style issues, run: + +```sh +yarn lint:fix +``` + +## Adding a new test case + +To add a new test case, you need to insert a new object into the list of test cases. For example, if you want to add a new instance of `LimitedReserveTestConfiguration`, you would insert it into the `testPairsLimitedReserveTransfers` list. Here's a step-by-step guide: + + +- Create a new instance of LimitedReserveTestConfiguration. Make sure to fill in all the necessary parameters for the test. +- Insert this new instance into the testPairsLimitedReserveTransfers list. + +The tests are configuration-driven, meaning they can be easily customized for different scenarios. The test framework doesn't make assumptions about which parachain is sending which coin to which destination over which relay chain. Fundamental events such as the moving of coins or the creation of a new account should be emitted during the test. + +The test cases live in the tests folder and use mocks from the network directory. The network directory contains helper functions to set the blockchain state and provides an abstraction over the chopsticks functionalities, such as creating a network. diff --git a/integration-tests/chopsticks/package.json b/integration-tests/chopsticks/package.json index f5bb6e0c3..c5e33b6de 100644 --- a/integration-tests/chopsticks/package.json +++ b/integration-tests/chopsticks/package.json @@ -8,8 +8,8 @@ "author": "[\"KILT \"]", "license": "MIT", "devDependencies": { - "@acala-network/chopsticks": "0.10.0", - "@acala-network/chopsticks-testing": "0.10.1", + "@acala-network/chopsticks": "0.11.0", + "@acala-network/chopsticks-testing": "0.11.0", "@polkadot/api": "^10.11.2", "@types/node": "^20.11.30", "@typescript-eslint/eslint-plugin": "^7.7.0", @@ -19,6 +19,7 @@ "eslint-config-prettier": "^9.1.0", "eslint-config-standard-with-typescript": "^43.0.1", "eslint-plugin-import": "^2.25.2", + "eslint-plugin-jsx-a11y": "^6.8.0", "eslint-plugin-n": "^15.0.0 || ^16.0.0 ", "eslint-plugin-prettier": "^5.1.3", "eslint-plugin-promise": "^6.0.0", @@ -26,15 +27,13 @@ "ts-node": "^10.9.2", "tsx": "^4.7.1", "typescript": "*", - "vitest": "^1.4.0", - "eslint-plugin-jsx-a11y": "^6.8.0" + "vitest": "^1.4.0" }, "scripts": { "ts-check": "tsc --noEmit", "lint": "eslint src && prettier --check src", "lint:fix": "eslint --fix src && prettier --write src", "clean": "rm -rf ./db", - "test": "LOG_LEVEL=error vitest", - "test:CI": "vitest --bail 0 --no-file-parallelism" + "test": "LOG_LEVEL=error vitest" } } diff --git a/integration-tests/chopsticks/src/helper/api.ts b/integration-tests/chopsticks/src/helper/api.ts new file mode 100644 index 000000000..18855a262 --- /dev/null +++ b/integration-tests/chopsticks/src/helper/api.ts @@ -0,0 +1,176 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { ApiPromise } from '@polkadot/api' + +/** + * All possible ways to submit an XCM message for the xtokens pallet. + * different structs for the xcm versions are provided + */ +export const xtokens = { + parachainV2: (paraId: number) => (acc: any) => ({ + V1: { + parents: 1, + interior: { + X2: [ + { Parachain: paraId }, + { + AccountId32: { + network: 'Any', + id: acc, + }, + }, + ], + }, + }, + }), + parachainV3: (paraId: number) => (acc: any) => ({ + V3: { + parents: 1, + interior: { + X2: [ + { Parachain: paraId }, + { + AccountId32: { + id: acc, + }, + }, + ], + }, + }, + }), + transfer: + (token: any, dest: (dest: any) => any, weight: any = 'Unlimited') => + ({ api }: { api: ApiPromise }, acc: any, amount: any) => + api.tx.xTokens.transfer(token, amount, dest(acc), weight), +} + +/** + * All possible ways to submit an XCM message for the xcmPallet. + * different structs for the xcm versions are provided + */ +export const xcmPallet = { + parachainV2: (parents: number, paraId: number) => ({ + V2: { + parents, + interior: { + X1: { Parachain: paraId }, + }, + }, + }), + parachainV3: (parents: number, paraId: any) => ({ + V3: { + parents, + interior: { + X1: { Parachain: paraId }, + }, + }, + }), + limitedTeleportAssets: + (token: any, amount: any, dest: any) => + ({ api }: { api: ApiPromise }, acc: any) => + (api.tx.xcmPallet || api.tx.polkadotXcm).limitedTeleportAssets( + dest, + { + V3: { + parents: 0, + interior: { + X1: { + AccountId32: { + // network: 'Any', + id: acc, + }, + }, + }, + }, + }, + { + V3: [ + { + id: token, + fun: { Fungible: amount }, + }, + ], + }, + 0, + 'Unlimited' + ), + limitedReserveTransferAssetsV2: + (token: any, dest: any) => + ({ api }: { api: ApiPromise }, acc: any, amount: any) => + (api.tx.xcmPallet || api.tx.polkadotXcm).limitedReserveTransferAssets( + dest, + { + V2: { + parents: 0, + interior: { + X1: { + AccountId32: { + network: 'Any', + id: acc, + }, + }, + }, + }, + }, + { + V2: [ + { + id: token, + fun: { Fungible: amount }, + }, + ], + }, + 0, + 'Unlimited' + ), + limitedReserveTransferAssetsV3: + (token: any, dest: any) => + ({ api }: { api: ApiPromise }, acc: any, amount: any) => + (api.tx.xcmPallet || api.tx.polkadotXcm).limitedReserveTransferAssets( + dest, + { + V3: { + parents: 0, + interior: { + X1: { + AccountId32: { + id: acc, + }, + }, + }, + }, + }, + { + V3: [ + { + id: token, + fun: { Fungible: amount }, + }, + ], + }, + 0, + 'Unlimited' + ), +} + +/** + * Different pallets to submit xcm messages. + */ +export const tx = { + xtokens, + xcmPallet, +} + +/** + * Query functions for different chains. + * Native tokens are fetched via the system pallet, while other tokens are fetched via the tokens pallet. + * + */ + +export const query = { + balances: async ({ api }: { api: ApiPromise }, address: string) => + BigInt(((await api.query.system.account(address)) as any).data.free), + tokens: + (token: any) => + async ({ api }: { api: ApiPromise }, address: string) => + BigInt(((await api.query.tokens.accounts(address, token)) as any).free), +} diff --git a/integration-tests/chopsticks/src/helper/utils.ts b/integration-tests/chopsticks/src/helper/utils.ts new file mode 100644 index 000000000..3732a344a --- /dev/null +++ b/integration-tests/chopsticks/src/helper/utils.ts @@ -0,0 +1,82 @@ +import { Keyring } from '@polkadot/keyring' +import { u8aToHex } from '@polkadot/util' +import { decodeAddress } from '@polkadot/util-crypto' +import { ExpectStatic } from 'vitest' + +/** + * Keyring instance to generate key pairs with 38 ss58Format encoding. + */ +const keyring = new Keyring({ type: 'ed25519', ss58Format: 38 }) + +/** + * Key pairs for Alice, Bob, and Charlie used for testing. + */ +export const keysAlice = keyring.addFromUri('//alice', undefined, 'ed25519') +export const keysBob = keyring.addFromUri('//bob', undefined, 'ed25519') +export const keysCharlie = keyring.addFromUri('//charlie', undefined, 'ed25519') + +/** + * Converts a given value to a Number + */ +export function toNumber(value: string | undefined): number | undefined { + if (value === undefined) { + return undefined + } + + return Number(value) +} + +/** + * Converts a given address to its hexadecimal representation. + * + * @param addr - The address to be converted. It should be a string representation of an address. + */ +export function hexAddress(addr: string) { + return u8aToHex(decodeAddress(addr)) +} + +/** + * Validates if the received balance is within the expected range considering a certain precision. + * + * @param previousBalance - The balance before the operation. + * @param receivedBalance - The balance after the operation. + * @param deltaBalance - The expected change in balance. + * @param expect - The assertion function from the testing library. + * @param precision - The precision of the balance validation. It must be a value between 0 and 100. + * + * The function calculates the expected balance by adding the deltaBalance to the previousBalance. + * It then calculates the lower and upper bounds of the expected balance considering the precision. + * The received balance is then checked if it falls within the range of the lower and upper bounds. + * + * If the precision is not between 0 and 100, the function throws an error. + * + * @throws {Error} If the precision is not between 0 and 100. + */ +export function validateBalanceWithPrecision( + previousBalance: bigint, + receivedBalance: bigint, + deltaBalance: bigint, + expect: ExpectStatic, + precision: bigint +) { + if (precision < BigInt(0) || precision > BigInt(100)) { + throw new Error('Precision must be between 0 and 100') + } + + const allowedError = BigInt(100) - precision + const expectedBalance = previousBalance + deltaBalance + + const lowerBound = expectedBalance - (expectedBalance * allowedError) / BigInt(100) + const upperBound = expectedBalance + (expectedBalance * allowedError) / BigInt(100) + + expect(receivedBalance).toBeGreaterThanOrEqual(lowerBound) + expect(receivedBalance).toBeLessThanOrEqual(upperBound) +} + +export const KILT = BigInt(1e15) +export const DOT = BigInt(1e10) +export const HDX = BigInt(1e12) + +export const initialBalanceKILT = BigInt(100) * KILT +export const initialBalanceDOT = BigInt(100) * DOT +export const initialBalanceHDX = BigInt(100) * HDX diff --git a/integration-tests/chopsticks/src/network/hydraDx.ts b/integration-tests/chopsticks/src/network/hydraDx.ts index 97e39d801..3277bead7 100644 --- a/integration-tests/chopsticks/src/network/hydraDx.ts +++ b/integration-tests/chopsticks/src/network/hydraDx.ts @@ -1,15 +1,22 @@ -import { setupContext, SetupOption } from '@acala-network/chopsticks-testing' -import type { Config } from './types.js' -import { initialBalanceHDX, initialBalanceKILT, toNumber } from '../utils.js' +import { SetupOption } from '@acala-network/chopsticks-testing' -/// Options used to create the HydraDx context -export const options: SetupOption = { - endpoint: process.env.HYDRADX_WS || ['wss://hydradx-rpc.dwellir.com', 'wss://rpc.hydradx.cloud'], - db: './db/hydradx.db.sqlite', - port: toNumber(process.env.HYDRADX_PORT) || 9001, -} +import { initialBalanceHDX, initialBalanceKILT, toNumber } from '../helper/utils.js' -export const kiltTokenId = 28 +/// Options used to create the HydraDx context +export const getSetupOptions = ({ + blockNumber, + wasmOverride, +}: { + blockNumber?: number + wasmOverride?: string +} = {}) => + ({ + endpoint: process.env.HYDRADX_WS || ['wss://hydradx-rpc.dwellir.com', 'wss://rpc.hydradx.cloud'], + db: './db/hydradx.db.sqlite', + port: toNumber(process.env.HYDRADX_PORT) || 9001, + blockNumber, + wasmOverride, + }) as SetupOption /// Sets the [TechnicalCommittee] and [Council] governance to the given accounts export function setGovernance(addr: string[]) { @@ -28,7 +35,7 @@ export function assignNativeTokensToAccounts(addr: string[], balance: bigint = i } } -/// Assigns KILT tokens to an accounts +/// Assigns KILT tokens to accounts export function assignKiltTokensToAccounts(addr: string[], balance: bigint = initialBalanceKILT) { return { Tokens: { @@ -37,12 +44,13 @@ export function assignKiltTokensToAccounts(addr: string[], balance: bigint = ini } } +// Sibling Sovereign Account +export const siblingSovereignAccount = '5Eg2fntQqFi3EvFWAf71G66Ecjjah26bmFzoANAeHFgj9Lia' + +export const kiltTokenId = 28 + /// HydraDX ParaId export const paraId = 2034 /// Omnipool account export const omnipoolAccount = '7L53bUTBbfuj14UpdCNPwmgzzHSsrsTWBHX5pys32mVWM3C1' - -export async function getContext(): Promise { - return setupContext(options) -} diff --git a/integration-tests/chopsticks/src/network/index.ts b/integration-tests/chopsticks/src/network/index.ts new file mode 100644 index 000000000..18851c0df --- /dev/null +++ b/integration-tests/chopsticks/src/network/index.ts @@ -0,0 +1,40 @@ +import * as SpiritnetConfig from './spiritnet.js' +import * as PolkadotConfig from './relay.js' +import * as HydraDxConfig from './hydraDx.js' +import { ChainConfigs } from './types.js' + +/* + * Get an environment variable and throw an error if it is not set. + */ +function getEnvVariable(name: string): string { + const value = process.env[name] + if (value === undefined) { + throw new Error(`Environment variable ${name} is not set.`) + } + return value +} + +/* + * Object with all the chain configurations. + */ +export const all: ChainConfigs = { + spiritnet: { + getConfig: SpiritnetConfig.getSetupOptions, + parameters: { + blockNumber: Number(getEnvVariable('SPIRITNET_BLOCK_NUMBER')), + wasmOverride: getEnvVariable('SPIRITNET_WASM_OVERRIDE'), + }, + }, + hydraDx: { + getConfig: HydraDxConfig.getSetupOptions, + parameters: { + blockNumber: Number(getEnvVariable('HYDRADX_BLOCK_NUMBER')), + }, + }, + polkadot: { + getConfig: PolkadotConfig.getSetupOptions, + parameters: { + blockNumber: Number(getEnvVariable('POLKADOT_BLOCK_NUMBER')), + }, + }, +} diff --git a/integration-tests/chopsticks/src/network/polkadot.ts b/integration-tests/chopsticks/src/network/relay.ts similarity index 51% rename from integration-tests/chopsticks/src/network/polkadot.ts rename to integration-tests/chopsticks/src/network/relay.ts index ebefd9be3..a0bb2e7aa 100644 --- a/integration-tests/chopsticks/src/network/polkadot.ts +++ b/integration-tests/chopsticks/src/network/relay.ts @@ -1,17 +1,26 @@ -import { setupContext, SetupOption } from '@acala-network/chopsticks-testing' -import type { Config } from './types.js' -import { initialBalanceDOT, toNumber } from '../utils.js' +import type { SetupOption } from '@acala-network/chopsticks-testing' + +import { initialBalanceDOT, toNumber } from '../helper/utils.js' /// Options used to create the HydraDx context -export const options: SetupOption = { - endpoint: process.env.POLKADOT_WS || [ - 'wss://rpc.polkadot.io', - 'wss://polkadot-rpc.dwellir.com', - 'wss://rpc.ibp.network/polkadot', - ], - db: './db/polkadot.db.sqlite', - port: toNumber(process.env.POLKADOT_PORT) || 9000, -} +export const getSetupOptions = ({ + blockNumber = undefined, + wasmOverride = undefined, +}: { + blockNumber?: number + wasmOverride?: string +}) => + ({ + endpoint: process.env.POLKADOT_WS || [ + 'wss://rpc.polkadot.io', + 'wss://polkadot-rpc.dwellir.com', + 'wss://rpc.ibp.network/polkadot', + ], + db: './db/polkadot.db.sqlite', + port: toNumber(process.env.POLKADOT_PORT) || 9000, + blockNumber, + wasmOverride, + }) as SetupOption /// Assigns the native tokens to an accounts export function assignNativeTokensToAccounts(addr: string[], balance: bigint = initialBalanceDOT) { @@ -34,7 +43,3 @@ export function removeDisputesAndMessageQueues() { }, } } - -export async function getContext(): Promise { - return setupContext(options) -} diff --git a/integration-tests/chopsticks/src/network/spiritnet.ts b/integration-tests/chopsticks/src/network/spiritnet.ts index 67c4bd9fc..c21b558be 100644 --- a/integration-tests/chopsticks/src/network/spiritnet.ts +++ b/integration-tests/chopsticks/src/network/spiritnet.ts @@ -1,13 +1,22 @@ -import { setupContext, SetupOption } from '@acala-network/chopsticks-testing' -import type { Config } from './types.js' -import { initialBalanceKILT, toNumber } from '../utils.js' +import { SetupOption } from '@acala-network/chopsticks-testing' + +import { initialBalanceKILT, toNumber } from '../helper/utils.js' /// Options used to create the Spiritnet context -const options: SetupOption = { - endpoint: process.env.SPIRITNET_WS || 'wss://kilt-rpc.dwellir.com', - db: './db/spiritnet.db.sqlite', - port: toNumber(process.env.SPIRITNET_PORT) || 9002, -} +export const getSetupOptions = ({ + blockNumber = undefined, + wasmOverride = undefined, +}: { + blockNumber?: number + wasmOverride?: string +}) => + ({ + endpoint: process.env.SPIRITNET_WS || 'wss://kilt-rpc.dwellir.com', + db: './db/spiritnet.db.sqlite', + port: toNumber(process.env.SPIRITNET_PORT) || 9002, + wasmOverride, + blockNumber, + }) as SetupOption /// Assigns the native tokens to an accounts export function assignNativeTokensToAccounts(addr: string[], balance: bigint = initialBalanceKILT) { @@ -28,11 +37,7 @@ export function setGovernance(addr: string[]) { /// Spiritnet ParaId export const paraId = 2086 +export const KILT = { Concrete: { parents: 0, interior: 'Here' } } -/// The sovereign account of HydraDx in Spiritnet -export const hydraDxSovereignAccount = '4qXPdpioJ6D8cgdeYXaukV2Y2oAQUHaX1VnGhdbSRqJn2CBt' - -/// Returns the Spiritnet context for the given options -export async function getContext(): Promise { - return setupContext(options) -} +/// Sibling sovereign account +export const siblingSovereignAccount = '5Eg2fnshxV9kofpcNEFE7azHLAjcCtpNkbsH3kkWZasYUVKs' diff --git a/integration-tests/chopsticks/src/network/types.ts b/integration-tests/chopsticks/src/network/types.ts index 0beaed1c3..25f395f5b 100644 --- a/integration-tests/chopsticks/src/network/types.ts +++ b/integration-tests/chopsticks/src/network/types.ts @@ -1,3 +1,28 @@ -import type { setupContext } from '@acala-network/chopsticks-testing' +import type { SetupOption, setupContext } from '@acala-network/chopsticks-testing' export type Config = Awaited> + +/** + * `ChainConfig` is an interface that represents the configuration for a blockchain. + * + * @interface ChainConfig + * + * @property {Function} getConfig - A function that takes an object as an argument. + * The object can have two optional properties: `blockNumber` and `wasmOverride`. + * `blockNumber` is a number that represents the block number in the blockchain, which can be set as env variable. + * `wasmOverride` is a string that can be used to override the WASM code for the blockchain, which can also be set as env variable. + * The function returns a `SetupOption` object. + * + * @property {Object} parameters - An object that contains the parameters for the getConfig function. + * It has two optional properties: `blockNumber` and `wasmOverride`. + */ +export interface ChainConfig { + getConfig: ({ blockNumber, wasmOverride }: { blockNumber?: number; wasmOverride?: string }) => SetupOption + parameters: { + blockNumber?: number + wasmOverride?: string + } +} + +/// A Record of all possible chain configurations. +export type ChainConfigs = Record diff --git a/integration-tests/chopsticks/src/network/utils.ts b/integration-tests/chopsticks/src/network/utils.ts index 00ef612bf..39ab59a4c 100644 --- a/integration-tests/chopsticks/src/network/utils.ts +++ b/integration-tests/chopsticks/src/network/utils.ts @@ -1,53 +1,73 @@ -export function getSiblingLocation(paraId: number) { - return { - parents: 1, - interior: { - X1: { Parachain: paraId }, - }, - } -} +import { connectParachains, connectVertical } from '@acala-network/chopsticks' +import { setTimeout } from 'timers/promises' +import { setupContext, SetupOption } from '@acala-network/chopsticks-testing' + +import type { Config } from './types.js' -export function getParentLocation() { - return { - parents: 1, - interior: 'Here', +/** + * This function is used to shut down a network composed of multiple chains. + * + * @param {Config[]} chains - An array of chain configurations that make up the network. + * + * @returns {Promise} + * Returns a Promise that resolves when all chains in the network have been successfully shut down. + * + */ +export async function shutDownNetwork(chains: Config[]): Promise { + await setTimeout(50) + const tearDown = chains.map((chain) => chain.teardown()) + await Promise.all(tearDown) +} +const newBlock = async (newBlockConfig: { count: number }, contexts: Config[]) => { + await Promise.all(contexts.map((context) => context.dev.newBlock(newBlockConfig))) +} +async function connectNetworks(relayChain: Config, parachains: Config[]) { + for (const parachain of parachains) { + await connectVertical(relayChain.chain, parachain.chain) } + + await connectParachains(parachains.map((parachain) => parachain.chain)) + + const newBlockConfig = { count: 2 } + // fixes api runtime disconnect warning + await setTimeout(50) + // Perform runtime upgrade and establish xcm connections. + await newBlock(newBlockConfig, [relayChain, ...parachains]) } -export function getAccountLocationV2(addr: string) { - return { - V2: { - parents: 0, - interior: { - X1: { - AccountId32: { - network: 'Any', - id: addr, - }, - }, - }, - }, - } +/** + * This function is used to set up a network with a relay chain, a sender, and a receiver. + * + * @param {SetupOption} relayChain - The relay chain option for the network setup. + * @param {SetupOption} sender - The sender option for the network setup. + * @param {SetupOption} receiver - The receiver option for the network setup. + * + * @returns {Promise<{relayChainContext: Config, senderChainContext: Config, receiverChainContext: Config}>} + * An object containing the contexts of the relay chain, sender, and receiver. + */ +export async function setupNetwork( + relayChain: SetupOption, + sender: SetupOption, + receiver: SetupOption +): Promise<{ relayChainContext: Config; senderChainContext: Config; receiverChainContext: Config }> { + await setTimeout(50) + const relayChainContext = await setupContext(relayChain) + const senderChainContext = await setupContext(sender) + const receiverChainContext = await setupContext(receiver) + + await connectNetworks(relayChainContext, [senderChainContext, receiverChainContext]) + return { relayChainContext, senderChainContext, receiverChainContext } } -export function getAccountLocationV3(addr: string) { - return { - V3: { - parents: 0, - interior: { - X1: { - AccountId32: { - id: addr, - }, - }, - }, - }, - } +/// Creates a new block for the given context +export async function createBlock(context: Config) { + // fixes api runtime disconnect warning + await setTimeout(50) + await context.dev.newBlock() } -export function getNativeAssetIdLocation(amount: bigint) { - return { - id: { Concrete: { parents: 0, interior: 'Here' } }, - fun: { Fungible: amount }, - } +/// sets the storage for the given context. +export async function setStorage(context: Config, storage: Record>) { + await context.dev.setStorage(storage) + await createBlock(context) } diff --git a/integration-tests/chopsticks/src/tests/index.ts b/integration-tests/chopsticks/src/tests/index.ts deleted file mode 100644 index 413b22c08..000000000 --- a/integration-tests/chopsticks/src/tests/index.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { beforeAll, afterAll } from 'vitest' -import { connectParachains, connectVertical } from '@acala-network/chopsticks' -import { setTimeout } from 'timers/promises' - -import * as SpiritnetConfig from '../network/spiritnet.js' -import * as PolkadotConfig from '../network/polkadot.js' -import * as HydraDxConfig from '../network/hydraDx.js' -import type { Config } from '../network/types.js' - -export let spiritnetContext: Config -export let hydradxContext: Config -export let polkadotContext: Config - -beforeAll(async () => { - spiritnetContext = await SpiritnetConfig.getContext() - hydradxContext = await HydraDxConfig.getContext() - polkadotContext = await PolkadotConfig.getContext() - - // Setup network - //@ts-expect-error Something weird in the exported types - await connectVertical(polkadotContext.chain, spiritnetContext.chain) - //@ts-expect-error Something weird in the exported types - await connectVertical(polkadotContext.chain, hydradxContext.chain) - //@ts-expect-error Something weird in the exported types - await connectParachains([spiritnetContext.chain, hydradxContext.chain]) - - const newBlockConfig = { count: 2 } - // fixes api runtime disconnect warning - await setTimeout(50) - // Perform runtime upgrade and establish xcm connections. - await Promise.all([ - polkadotContext.dev.newBlock(newBlockConfig), - spiritnetContext.dev.newBlock(newBlockConfig), - hydradxContext.dev.newBlock(newBlockConfig), - ]) -}, 300_000) - -afterAll(async () => { - // fixes api runtime disconnect warning - await setTimeout(50) - await Promise.all([spiritnetContext.teardown(), hydradxContext.teardown(), polkadotContext.teardown()]) -}) - -export async function getFreeBalanceSpiritnet(account: string): Promise { - const accountInfo = await spiritnetContext.api.query.system.account(account) - return accountInfo.data.free.toBigInt() -} - -export async function getFreeBalanceHydraDxKilt(account: string): Promise { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const accountInfo: any = await hydradxContext.api.query.tokens.accounts(account, HydraDxConfig.kiltTokenId) - return accountInfo.free.toBigInt() -} diff --git a/integration-tests/chopsticks/src/tests/utils.ts b/integration-tests/chopsticks/src/tests/utils.ts deleted file mode 100644 index 60cff25e9..000000000 --- a/integration-tests/chopsticks/src/tests/utils.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { ExpectStatic } from 'vitest' -import { setTimeout } from 'timers/promises' -import { u8aToHex } from '@polkadot/util' -import { decodeAddress } from '@polkadot/util-crypto' - -import { Config } from '../network/types.js' - -/// Creates a new block for the given context -export async function createBlock(context: Config) { - // fixes api runtime disconnect warning - await setTimeout(50) - await context.dev.newBlock() -} - -/// sets the storage for the given context. -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export async function setStorage(context: Config, storage: { [key: string]: any }) { - await context.dev.setStorage(storage) - await createBlock(context) -} - -/// checks the balance of an account and expects it to be the given amount -export async function checkBalance( - getFreeBalanceFunction: (account: string) => Promise, - account: string, - expect: ExpectStatic, - expectedAmount = BigInt(0) -) { - const balance = await getFreeBalanceFunction(account) - expect(balance).eq(BigInt(expectedAmount)) -} - -/// checks the balance of an account and expects it to be in the given range -export async function checkBalanceInRange( - getFreeBalanceFunction: (account: string) => Promise, - account: string, - expect: ExpectStatic, - expectedRange: [bigint, bigint] -) { - const balance = await getFreeBalanceFunction(account) - expect(balance >= expectedRange[0]) - expect(balance <= expectedRange[1]) -} - -export function hexAddress(addr: string) { - return u8aToHex(decodeAddress(addr)) -} diff --git a/integration-tests/chopsticks/src/tests/xcm/__snapshots__/limitedReserveTransferSpiritnetHydraDxV2.test.ts.snap b/integration-tests/chopsticks/src/tests/xcm/__snapshots__/limitedReserveTransferSpiritnetHydraDxV2.test.ts.snap deleted file mode 100644 index 5a77b13be..000000000 --- a/integration-tests/chopsticks/src/tests/xcm/__snapshots__/limitedReserveTransferSpiritnetHydraDxV2.test.ts.snap +++ /dev/null @@ -1,83 +0,0 @@ -// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html - -exports[`Limited Reserve V2 Transfers from Spiritnet Account Alice -> HydraDx Account Alice > receiver events currencies 1`] = ` -[ - { - "data": { - "amount": "(rounded 1000000000000000)", - "currencyId": 28, - "who": "7NL1GYCJu8cFSnWBLwET2X3fMdodw8T75zuxi59hA2bhdjQq", - }, - "method": "Deposited", - "section": "currencies", - }, - { - "data": { - "amount": "(rounded 4200000000000)", - "currencyId": 28, - "who": "7L53bUTBopuwFt3mKUfmkzgGLayYa1Yvn1hAg9v5UMrQzTfh", - }, - "method": "Deposited", - "section": "currencies", - }, -] -`; - -exports[`Limited Reserve V2 Transfers from Spiritnet Account Alice -> HydraDx Account Alice > receiver events xcmpQueue 1`] = ` -[ - { - "data": { - "messageHash": "(hash)", - "messageId": "(hash)", - "weight": { - "proofSize": 0, - "refTime": 400000000, - }, - }, - "method": "Success", - "section": "xcmpQueue", - }, -] -`; - -exports[`Limited Reserve V2 Transfers from Spiritnet Account Alice -> HydraDx Account Alice > sender events Balances 1`] = ` -[ - { - "data": { - "amount": "(rounded 170000000000)", - "who": "4seWojfEHrk5YKPahdErazQ3CWEHZYi6NV4gKz5AaejWbRPJ", - }, - "method": "Withdraw", - "section": "balances", - }, -] -`; - -exports[`Limited Reserve V2 Transfers from Spiritnet Account Alice -> HydraDx Account Alice > sender events xcm pallet 1`] = ` -[ - { - "data": { - "outcome": { - "Complete": { - "proofSize": 0, - "refTime": 400000000, - }, - }, - }, - "method": "Attempted", - "section": "polkadotXcm", - }, -] -`; - -exports[`Limited Reserve V2 Transfers from Spiritnet Account Alice -> HydraDx Account Alice > sender events xcm queue pallet 1`] = ` -[ - { - "data": { - "messageHash": "(hash)", - }, - "method": "XcmpMessageSent", - "section": "xcmpQueue", - }, -] -`; diff --git a/integration-tests/chopsticks/src/tests/xcm/__snapshots__/limitedReserveTransferSpiritnetHydraDxV3.test.ts.snap b/integration-tests/chopsticks/src/tests/xcm/__snapshots__/limitedReserveTransferSpiritnetHydraDxV3.test.ts.snap deleted file mode 100644 index 645c74e47..000000000 --- a/integration-tests/chopsticks/src/tests/xcm/__snapshots__/limitedReserveTransferSpiritnetHydraDxV3.test.ts.snap +++ /dev/null @@ -1,83 +0,0 @@ -// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html - -exports[`Limited Reserve V3 Transfers from Spiritnet Account Alice -> HydraDx Account Alice > receiver events currencies 1`] = ` -[ - { - "data": { - "amount": "(rounded 1000000000000000)", - "currencyId": 28, - "who": "7NL1GYCJu8cFSnWBLwET2X3fMdodw8T75zuxi59hA2bhdjQq", - }, - "method": "Deposited", - "section": "currencies", - }, - { - "data": { - "amount": "(rounded 4200000000000)", - "currencyId": 28, - "who": "7L53bUTBopuwFt3mKUfmkzgGLayYa1Yvn1hAg9v5UMrQzTfh", - }, - "method": "Deposited", - "section": "currencies", - }, -] -`; - -exports[`Limited Reserve V3 Transfers from Spiritnet Account Alice -> HydraDx Account Alice > receiver events xcmpQueue 1`] = ` -[ - { - "data": { - "messageHash": "(hash)", - "messageId": "(hash)", - "weight": { - "proofSize": 0, - "refTime": 400000000, - }, - }, - "method": "Success", - "section": "xcmpQueue", - }, -] -`; - -exports[`Limited Reserve V3 Transfers from Spiritnet Account Alice -> HydraDx Account Alice > sender events Balances 1`] = ` -[ - { - "data": { - "amount": "(rounded 170000000000)", - "who": "4seWojfEHrk5YKPahdErazQ3CWEHZYi6NV4gKz5AaejWbRPJ", - }, - "method": "Withdraw", - "section": "balances", - }, -] -`; - -exports[`Limited Reserve V3 Transfers from Spiritnet Account Alice -> HydraDx Account Alice > sender events xcm pallet 1`] = ` -[ - { - "data": { - "outcome": { - "Complete": { - "proofSize": 0, - "refTime": 400000000, - }, - }, - }, - "method": "Attempted", - "section": "polkadotXcm", - }, -] -`; - -exports[`Limited Reserve V3 Transfers from Spiritnet Account Alice -> HydraDx Account Alice > sender events xcm queue pallet 1`] = ` -[ - { - "data": { - "messageHash": "(hash)", - }, - "method": "XcmpMessageSent", - "section": "xcmpQueue", - }, -] -`; diff --git a/integration-tests/chopsticks/src/tests/xcm/__snapshots__/limitedReseveTransferHydraDxSpiritnet.test.ts.snap b/integration-tests/chopsticks/src/tests/xcm/__snapshots__/limitedReseveTransferHydraDxSpiritnet.test.ts.snap deleted file mode 100644 index 2ec640010..000000000 --- a/integration-tests/chopsticks/src/tests/xcm/__snapshots__/limitedReseveTransferHydraDxSpiritnet.test.ts.snap +++ /dev/null @@ -1,139 +0,0 @@ -// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html - -exports[`Limited Reserve Transfers from HydraDx Account Alice -> Spiritnet Account Alice > receiver events Balances 1`] = ` -[ - { - "data": { - "amount": 1000000000000000, - "who": "4qXPdpioJ6D8cgdeYXaukV2Y2oAQUHaX1VnGhdbSRqJn2CBt", - }, - "method": "Withdraw", - "section": "balances", - }, -] -`; - -exports[`Limited Reserve Transfers from HydraDx Account Alice -> Spiritnet Account Alice > receiver events Balances 2`] = ` -[ - { - "data": { - "account": "4seWojfEHrk5YKPahdErazQ3CWEHZYi6NV4gKz5AaejWbRPJ", - "freeBalance": "(rounded 1000000000000000)", - }, - "method": "Endowed", - "section": "balances", - }, -] -`; - -exports[`Limited Reserve Transfers from HydraDx Account Alice -> Spiritnet Account Alice > receiver events xcmpQueue 1`] = ` -[ - { - "data": { - "messageHash": "(hash)", - "messageId": "(hash)", - "weight": { - "proofSize": 0, - "refTime": 800000000, - }, - }, - "method": "Success", - "section": "xcmpQueue", - }, -] -`; - -exports[`Limited Reserve Transfers from HydraDx Account Alice -> Spiritnet Account Alice > sender events currencies 1`] = ` -[ - { - "data": { - "amount": "(rounded 440000000000)", - "currencyId": 0, - "who": "7NL1GYCJu8cFSnWBLwET2X3fMdodw8T75zuxi59hA2bhdjQq", - }, - "method": "Withdrawn", - "section": "currencies", - }, - { - "data": { - "amount": 1000000000000000, - "currencyId": 28, - "who": "7NL1GYCJu8cFSnWBLwET2X3fMdodw8T75zuxi59hA2bhdjQq", - }, - "method": "Withdrawn", - "section": "currencies", - }, -] -`; - -exports[`Limited Reserve Transfers from HydraDx Account Alice -> Spiritnet Account Alice > sender events currencies 2`] = ` -[ - { - "data": { - "assets": [ - { - "fun": { - "Fungible": 1000000000000000, - }, - "id": { - "Concrete": { - "interior": { - "X1": { - "Parachain": "(rounded 2100)", - }, - }, - "parents": 1, - }, - }, - }, - ], - "dest": { - "interior": { - "X2": [ - { - "Parachain": "(rounded 2100)", - }, - { - "AccountId32": { - "id": "(hash)", - "network": null, - }, - }, - ], - }, - "parents": 1, - }, - "fee": { - "fun": { - "Fungible": 1000000000000000, - }, - "id": { - "Concrete": { - "interior": { - "X1": { - "Parachain": "(rounded 2100)", - }, - }, - "parents": 1, - }, - }, - }, - "sender": "7NL1GYCJu8cFSnWBLwET2X3fMdodw8T75zuxi59hA2bhdjQq", - }, - "method": "TransferredMultiAssets", - "section": "xTokens", - }, -] -`; - -exports[`Limited Reserve Transfers from HydraDx Account Alice -> Spiritnet Account Alice > sender events xcm queue pallet 1`] = ` -[ - { - "data": { - "messageHash": "(hash)", - }, - "method": "XcmpMessageSent", - "section": "xcmpQueue", - }, -] -`; diff --git a/integration-tests/chopsticks/src/tests/xcm/initiateWithdrawAsset/__snapshots__/index.test.ts.snap b/integration-tests/chopsticks/src/tests/xcm/initiateWithdrawAsset/__snapshots__/index.test.ts.snap new file mode 100644 index 000000000..f2f0b7c27 --- /dev/null +++ b/integration-tests/chopsticks/src/tests/xcm/initiateWithdrawAsset/__snapshots__/index.test.ts.snap @@ -0,0 +1,160 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`Withdraw Asset > HydraDx -> KILT DEV > receiver events "xcmpQueue" 1`] = ` +[ + { + "data": { + "messageHash": "(hash)", + "messageId": "(hash)", + "weight": { + "proofSize": 0, + "refTime": 800000000, + }, + }, + "method": "Success", + "section": "xcmpQueue", + }, +] +`; + +exports[`Withdraw Asset > HydraDx -> KILT DEV > receiver events {"section":"system","method":"NewAccount"} 1`] = ` +[ + { + "data": { + "account": "4rsmbFBYpVmWE2LHRsSZKxf22a8cbJLxMZqvVGkGkSDmDBcr", + }, + "method": "NewAccount", + "section": "system", + }, +] +`; + +exports[`Withdraw Asset > HydraDx -> KILT DEV > sender events "xcmpQueue" 1`] = ` +[ + { + "data": { + "messageHash": "(hash)", + }, + "method": "XcmpMessageSent", + "section": "xcmpQueue", + }, +] +`; + +exports[`Withdraw Asset > HydraDx -> KILT at Block > receiver events "xcmpQueue" 1`] = ` +[ + { + "data": { + "messageHash": "(hash)", + "messageId": "(hash)", + "weight": { + "proofSize": 0, + "refTime": 800000000, + }, + }, + "method": "Success", + "section": "xcmpQueue", + }, +] +`; + +exports[`Withdraw Asset > HydraDx -> KILT at Block > receiver events {"section":"balances","method":"Withdraw"} 1`] = ` +[ + { + "data": { + "amount": 1000000000000000, + "who": "4qXPdpioJ6D8cgdeYXaukV2Y2oAQUHaX1VnGhdbSRqJn2CBt", + }, + "method": "Withdraw", + "section": "balances", + }, +] +`; + +exports[`Withdraw Asset > HydraDx -> KILT at Block > receiver events {"section":"system","method":"NewAccount"} 1`] = ` +[ + { + "data": { + "account": "4rsmbFBYpVmWE2LHRsSZKxf22a8cbJLxMZqvVGkGkSDmDBcr", + }, + "method": "NewAccount", + "section": "system", + }, +] +`; + +exports[`Withdraw Asset > HydraDx -> KILT at Block > sender events "xcmpQueue" 1`] = ` +[ + { + "data": { + "messageHash": "(hash)", + }, + "method": "XcmpMessageSent", + "section": "xcmpQueue", + }, +] +`; + +exports[`Withdraw Asset > HydraDx -> KILT at Block > sender events {"section":"currencies","method":"Withdrawn"} 1`] = ` +[ + { + "data": { + "amount": "(rounded 440000000000)", + "currencyId": 0, + "who": "7NL1GYCJu8cFSnWBLwET2X3fMdodw8T75zuxi59hA2bhdjQq", + }, + "method": "Withdrawn", + "section": "currencies", + }, + { + "data": { + "amount": 1000000000000000, + "currencyId": 28, + "who": "7NL1GYCJu8cFSnWBLwET2X3fMdodw8T75zuxi59hA2bhdjQq", + }, + "method": "Withdrawn", + "section": "currencies", + }, +] +`; + +exports[`Withdraw Asset > HydraDx -> KILT live > receiver events "xcmpQueue" 1`] = ` +[ + { + "data": { + "messageHash": "(hash)", + "messageId": "(hash)", + "weight": { + "proofSize": 0, + "refTime": 800000000, + }, + }, + "method": "Success", + "section": "xcmpQueue", + }, +] +`; + +exports[`Withdraw Asset > HydraDx -> KILT live > receiver events {"section":"system","method":"NewAccount"} 1`] = ` +[ + { + "data": { + "account": "4rsmbFBYpVmWE2LHRsSZKxf22a8cbJLxMZqvVGkGkSDmDBcr", + }, + "method": "NewAccount", + "section": "system", + }, +] +`; + +exports[`Withdraw Asset > HydraDx -> KILT live > sender events "xcmpQueue" 1`] = ` +[ + { + "data": { + "messageHash": "(hash)", + }, + "method": "XcmpMessageSent", + "section": "xcmpQueue", + }, +] +`; diff --git a/integration-tests/chopsticks/src/tests/xcm/initiateWithdrawAsset/config.ts b/integration-tests/chopsticks/src/tests/xcm/initiateWithdrawAsset/config.ts new file mode 100644 index 000000000..268d6a148 --- /dev/null +++ b/integration-tests/chopsticks/src/tests/xcm/initiateWithdrawAsset/config.ts @@ -0,0 +1,167 @@ +import * as PolkadotChainConfigs from '../../../network/index.js' +import { initialBalanceHDX, initialBalanceKILT, keysAlice, keysBob } from '../../../helper/utils.js' +import * as SpiritnetConfig from '../../../network/spiritnet.js' +import * as HydraDxConfig from '../../../network/hydraDx.js' +import { tx, query } from '../../../helper/api.js' + +import type { ApiPromise } from '@polkadot/api' +import type { BasicConfig, BasicXcmTestConfiguration, BasisTxContext } from '../types.js' +import type { SubmittableExtrinsic } from '@polkadot/api/types' + +interface Config extends BasicConfig { + precision: bigint +} + +interface Query { + sender: ({ api }: { api: ApiPromise }, address: string) => Promise + receiver: ({ api }: { api: ApiPromise }, address: string) => Promise +} + +interface TxContext extends BasisTxContext { + balanceToTransfer: bigint + tx: ({ api }: { api: ApiPromise }, submitter: string, amount: string | number) => SubmittableExtrinsic<'promise'> +} + +/* + * Configuration for the WithdrawAssets test extends the BasicXcmTestConfiguration + **/ +interface WithdrawAssetTestConfiguration extends BasicXcmTestConfiguration { + config: Config + query: Query + txContext: TxContext +} +// Test pairs for WithdrawAssets +export const testPairsWithdrawAssets: WithdrawAssetTestConfiguration[] = [ + { + config: { + desc: 'HydraDx -> KILT live', + precision: BigInt(96), + }, + + network: { + sender: PolkadotChainConfigs.all.hydraDx.getConfig({}), + receiver: PolkadotChainConfigs.all.spiritnet.getConfig({}), + relay: PolkadotChainConfigs.all.polkadot.getConfig({}), + }, + accounts: { + senderAccount: keysAlice, + receiverAccount: keysBob, + }, + query: { + sender: query.tokens(HydraDxConfig.kiltTokenId), + receiver: query.balances, + }, + txContext: { + tx: tx.xtokens.transfer(HydraDxConfig.kiltTokenId, tx.xtokens.parachainV3(SpiritnetConfig.paraId)), + pallets: { + sender: ['xcmpQueue'], + receiver: ['xcmpQueue', { section: 'system', method: 'NewAccount' }], + }, + balanceToTransfer: BigInt(1e15), + }, + storage: { + senderStorage: { + ...HydraDxConfig.assignKiltTokensToAccounts([keysAlice.address], initialBalanceKILT), + ...HydraDxConfig.assignNativeTokensToAccounts([keysAlice.address], initialBalanceHDX), + }, + receiverStorage: {}, + relayStorage: {}, + }, + sovereignAccount: { + sender: SpiritnetConfig.siblingSovereignAccount, + receiver: HydraDxConfig.siblingSovereignAccount, + }, + }, + + { + config: { + desc: 'HydraDx -> KILT DEV', + precision: BigInt(96), + }, + + network: { + sender: PolkadotChainConfigs.all.hydraDx.getConfig({}), + receiver: PolkadotChainConfigs.all.spiritnet.getConfig(PolkadotChainConfigs.all.spiritnet.parameters), + relay: PolkadotChainConfigs.all.polkadot.getConfig({}), + }, + accounts: { + senderAccount: keysAlice, + receiverAccount: keysBob, + }, + query: { + sender: query.tokens(HydraDxConfig.kiltTokenId), + receiver: query.balances, + }, + txContext: { + tx: tx.xtokens.transfer(HydraDxConfig.kiltTokenId, tx.xtokens.parachainV3(SpiritnetConfig.paraId)), + pallets: { + sender: ['xcmpQueue'], + receiver: ['xcmpQueue', { section: 'system', method: 'NewAccount' }], + }, + balanceToTransfer: BigInt(1e15), + }, + storage: { + senderStorage: { + ...HydraDxConfig.assignKiltTokensToAccounts([keysAlice.address], initialBalanceKILT), + ...HydraDxConfig.assignNativeTokensToAccounts([keysAlice.address], initialBalanceHDX), + }, + receiverStorage: {}, + relayStorage: {}, + }, + sovereignAccount: { + sender: SpiritnetConfig.siblingSovereignAccount, + receiver: HydraDxConfig.siblingSovereignAccount, + }, + }, + + { + config: { + desc: 'HydraDx -> KILT at Block', + precision: BigInt(99), + }, + + network: { + sender: PolkadotChainConfigs.all.hydraDx.getConfig({ + blockNumber: PolkadotChainConfigs.all.hydraDx.parameters.blockNumber, + }), + receiver: PolkadotChainConfigs.all.spiritnet.getConfig({ + blockNumber: PolkadotChainConfigs.all.spiritnet.parameters.blockNumber, + }), + relay: PolkadotChainConfigs.all.polkadot.getConfig({ + blockNumber: PolkadotChainConfigs.all.polkadot.parameters.blockNumber, + }), + }, + accounts: { + senderAccount: keysAlice, + receiverAccount: keysBob, + }, + query: { + sender: query.tokens(HydraDxConfig.kiltTokenId), + receiver: query.balances, + }, + txContext: { + tx: tx.xtokens.transfer(HydraDxConfig.kiltTokenId, tx.xtokens.parachainV3(SpiritnetConfig.paraId)), + pallets: { + sender: ['xcmpQueue', { section: 'currencies', method: 'Withdrawn' }], + receiver: [ + 'xcmpQueue', + { section: 'balances', method: 'Withdraw' }, + { section: 'system', method: 'NewAccount' }, + ], + }, + balanceToTransfer: BigInt(1e15), + }, + storage: { + senderStorage: { + ...HydraDxConfig.assignKiltTokensToAccounts([keysAlice.address], initialBalanceKILT), + ...HydraDxConfig.assignNativeTokensToAccounts([keysAlice.address], initialBalanceHDX), + }, + receiverStorage: {}, + relayStorage: {}, + }, + sovereignAccount: { + sender: SpiritnetConfig.siblingSovereignAccount, + receiver: HydraDxConfig.siblingSovereignAccount, + }, + }, +] as const diff --git a/integration-tests/chopsticks/src/tests/xcm/initiateWithdrawAsset/index.test.ts b/integration-tests/chopsticks/src/tests/xcm/initiateWithdrawAsset/index.test.ts new file mode 100644 index 000000000..d7dacbb1f --- /dev/null +++ b/integration-tests/chopsticks/src/tests/xcm/initiateWithdrawAsset/index.test.ts @@ -0,0 +1,128 @@ +import { describe, beforeEach, it, afterEach } from 'vitest' +import { sendTransaction, withExpect } from '@acala-network/chopsticks-testing' +import type { KeyringPair } from '@polkadot/keyring/types' + +import { createBlock, setStorage } from '../../../network/utils.js' +import { hexAddress, validateBalanceWithPrecision } from '../../../helper/utils.js' +import { testPairsWithdrawAssets } from './config.js' +import { Config } from '../../../network/types.js' +import { setupNetwork, shutDownNetwork } from '../../../network/utils.js' + +describe.each(testPairsWithdrawAssets)( + 'Withdraw Asset', + { timeout: 30_000 }, + async ({ network, storage, accounts, query, sovereignAccount, txContext, config }) => { + let senderContext: Config + let receiverContext: Config + let relayContext: Config + let senderAccount: KeyringPair + let receiverAccount: KeyringPair + const { desc, precision } = config + + // Create the network context + beforeEach(async () => { + const { receiver, sender, relay } = network + + const { receiverChainContext, senderChainContext, relayChainContext } = await setupNetwork( + relay, + sender, + receiver + ) + + relayContext = relayChainContext + senderContext = senderChainContext + receiverContext = receiverChainContext + + const { receiverStorage, senderStorage, relayStorage } = storage + await setStorage(senderContext, senderStorage) + await setStorage(receiverContext, receiverStorage) + await setStorage(relayContext, relayStorage) + + const { senderAccount: a, receiverAccount: b } = accounts + senderAccount = a + receiverAccount = b + }, 20_000) + + // Shut down the network + afterEach(async () => { + try { + await shutDownNetwork([senderContext, receiverContext, relayContext]) + } catch (error) { + if (!(error instanceof TypeError)) { + console.error(error) + } + } + }) + + it(desc, { timeout: 10_000, retry: 3 }, async ({ expect }) => { + const { checkEvents, checkSystemEvents } = withExpect(expect) + + const { pallets, tx, balanceToTransfer } = txContext + + // Balance of the sovereign account before the transfer + const senderSovereignAccountBalanceBeforeTransfer = await query.receiver( + receiverContext, + sovereignAccount.receiver + ) + + const balanceSenderBeforeTransfer = await query.sender(senderContext, senderAccount.address) + const initialBalanceReceiver = await query.receiver(receiverContext, receiverAccount.address) + + // Check initial balance receiver should be zero + expect(initialBalanceReceiver).toBe(BigInt(0)) + + // Fire tx + const signedTx = tx( + senderContext, + hexAddress(receiverAccount.address), + balanceToTransfer.toString() + ).signAsync(senderAccount) + + const events = await sendTransaction(signedTx) + + // check sender state + await createBlock(senderContext) + + pallets.sender.map((pallet) => + checkEvents(events, pallet).toMatchSnapshot(`sender events ${JSON.stringify(pallet)}`) + ) + + const balanceSenderAfterTransfer = await query.sender(senderContext, senderAccount.address) + const removedBalance = balanceToTransfer * BigInt(-1) + + validateBalanceWithPrecision( + balanceSenderBeforeTransfer, + balanceSenderAfterTransfer, + removedBalance, + expect, + precision + ) + + // check receiver state + await createBlock(receiverContext) + + const senderSovereignAccountBalanceAfterTransfer = await query.receiver( + receiverContext, + sovereignAccount.receiver + ) + + expect(senderSovereignAccountBalanceAfterTransfer).toBe( + senderSovereignAccountBalanceBeforeTransfer - BigInt(balanceToTransfer) + ) + + pallets.receiver.map((pallet) => + checkSystemEvents(receiverContext, pallet).toMatchSnapshot(`receiver events ${JSON.stringify(pallet)}`) + ) + + const balanceReceiverAfterTransfer = await query.receiver(receiverContext, receiverAccount.address) + + validateBalanceWithPrecision( + initialBalanceReceiver, + balanceReceiverAfterTransfer, + balanceToTransfer, + expect, + precision + ) + }) + } +) diff --git a/integration-tests/chopsticks/src/tests/xcm/limitedReserveTransfer/__snapshots__/index.test.ts.snap b/integration-tests/chopsticks/src/tests/xcm/limitedReserveTransfer/__snapshots__/index.test.ts.snap new file mode 100644 index 000000000..e773cd719 --- /dev/null +++ b/integration-tests/chopsticks/src/tests/xcm/limitedReserveTransfer/__snapshots__/index.test.ts.snap @@ -0,0 +1,413 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`Limited Reserve Transfers > Kilt -> HydraDx at block V2 > receiver events "currencies" 1`] = ` +[ + { + "data": { + "amount": "(rounded 1000000000000000)", + "currencyId": 28, + "who": "7MZG43idRmdg8VSt5BS9mVJeBhhxxt5y55hCsMpoKp5xFQX2", + }, + "method": "Deposited", + "section": "currencies", + }, + { + "data": { + "amount": "(rounded 4100000000000)", + "currencyId": 28, + "who": "7L53bUTBopuwFt3mKUfmkzgGLayYa1Yvn1hAg9v5UMrQzTfh", + }, + "method": "Deposited", + "section": "currencies", + }, +] +`; + +exports[`Limited Reserve Transfers > Kilt -> HydraDx at block V2 > receiver events "tokens" 1`] = ` +[ + { + "data": { + "amount": "(rounded 1000000000000000)", + "currencyId": 28, + "who": "7MZG43idRmdg8VSt5BS9mVJeBhhxxt5y55hCsMpoKp5xFQX2", + }, + "method": "Endowed", + "section": "tokens", + }, + { + "data": { + "amount": "(rounded 1000000000000000)", + "currencyId": 28, + "who": "7MZG43idRmdg8VSt5BS9mVJeBhhxxt5y55hCsMpoKp5xFQX2", + }, + "method": "Deposited", + "section": "tokens", + }, + { + "data": { + "amount": "(rounded 4100000000000)", + "currencyId": 28, + "who": "7L53bUTBopuwFt3mKUfmkzgGLayYa1Yvn1hAg9v5UMrQzTfh", + }, + "method": "Deposited", + "section": "tokens", + }, +] +`; + +exports[`Limited Reserve Transfers > Kilt -> HydraDx at block V2 > receiver events "xcmpQueue" 1`] = ` +[ + { + "data": { + "messageHash": "(hash)", + "messageId": "(hash)", + "weight": { + "proofSize": 0, + "refTime": 400000000, + }, + }, + "method": "Success", + "section": "xcmpQueue", + }, +] +`; + +exports[`Limited Reserve Transfers > Kilt -> HydraDx at block V2 > sender events "polkadotXcm" 1`] = ` +[ + { + "data": { + "outcome": { + "Complete": { + "proofSize": 0, + "refTime": 400000000, + }, + }, + }, + "method": "Attempted", + "section": "polkadotXcm", + }, +] +`; + +exports[`Limited Reserve Transfers > Kilt -> HydraDx at block V2 > sender events "xcmpQueue" 1`] = ` +[ + { + "data": { + "messageHash": "(hash)", + }, + "method": "XcmpMessageSent", + "section": "xcmpQueue", + }, +] +`; + +exports[`Limited Reserve Transfers > Kilt -> HydraDx at block V2 > sender events {"section":"balances","method":"Withdraw"} 1`] = ` +[ + { + "data": { + "amount": "(rounded 170000000000)", + "who": "4seWojfEHrk5YKPahdErazQ3CWEHZYi6NV4gKz5AaejWbRPJ", + }, + "method": "Withdraw", + "section": "balances", + }, +] +`; + +exports[`Limited Reserve Transfers > Kilt -> HydraDx at block V3 > receiver events "currencies" 1`] = ` +[ + { + "data": { + "amount": "(rounded 1000000000000000)", + "currencyId": 28, + "who": "7MZG43idRmdg8VSt5BS9mVJeBhhxxt5y55hCsMpoKp5xFQX2", + }, + "method": "Deposited", + "section": "currencies", + }, + { + "data": { + "amount": "(rounded 4100000000000)", + "currencyId": 28, + "who": "7L53bUTBopuwFt3mKUfmkzgGLayYa1Yvn1hAg9v5UMrQzTfh", + }, + "method": "Deposited", + "section": "currencies", + }, +] +`; + +exports[`Limited Reserve Transfers > Kilt -> HydraDx at block V3 > receiver events "tokens" 1`] = ` +[ + { + "data": { + "amount": "(rounded 1000000000000000)", + "currencyId": 28, + "who": "7MZG43idRmdg8VSt5BS9mVJeBhhxxt5y55hCsMpoKp5xFQX2", + }, + "method": "Endowed", + "section": "tokens", + }, + { + "data": { + "amount": "(rounded 1000000000000000)", + "currencyId": 28, + "who": "7MZG43idRmdg8VSt5BS9mVJeBhhxxt5y55hCsMpoKp5xFQX2", + }, + "method": "Deposited", + "section": "tokens", + }, + { + "data": { + "amount": "(rounded 4100000000000)", + "currencyId": 28, + "who": "7L53bUTBopuwFt3mKUfmkzgGLayYa1Yvn1hAg9v5UMrQzTfh", + }, + "method": "Deposited", + "section": "tokens", + }, +] +`; + +exports[`Limited Reserve Transfers > Kilt -> HydraDx at block V3 > receiver events "xcmpQueue" 1`] = ` +[ + { + "data": { + "messageHash": "(hash)", + "messageId": "(hash)", + "weight": { + "proofSize": 0, + "refTime": 400000000, + }, + }, + "method": "Success", + "section": "xcmpQueue", + }, +] +`; + +exports[`Limited Reserve Transfers > Kilt -> HydraDx at block V3 > sender events "polkadotXcm" 1`] = ` +[ + { + "data": { + "outcome": { + "Complete": { + "proofSize": 0, + "refTime": 400000000, + }, + }, + }, + "method": "Attempted", + "section": "polkadotXcm", + }, +] +`; + +exports[`Limited Reserve Transfers > Kilt -> HydraDx at block V3 > sender events "xcmpQueue" 1`] = ` +[ + { + "data": { + "messageHash": "(hash)", + }, + "method": "XcmpMessageSent", + "section": "xcmpQueue", + }, +] +`; + +exports[`Limited Reserve Transfers > Kilt -> HydraDx at block V3 > sender events {"section":"balances","method":"Withdraw"} 1`] = ` +[ + { + "data": { + "amount": "(rounded 170000000000)", + "who": "4seWojfEHrk5YKPahdErazQ3CWEHZYi6NV4gKz5AaejWbRPJ", + }, + "method": "Withdraw", + "section": "balances", + }, +] +`; + +exports[`Limited Reserve Transfers > Kilt -> HydraDx live V2 > receiver events "xcmpQueue" 1`] = ` +[ + { + "data": { + "messageHash": "(hash)", + "messageId": "(hash)", + "weight": { + "proofSize": 0, + "refTime": 400000000, + }, + }, + "method": "Success", + "section": "xcmpQueue", + }, +] +`; + +exports[`Limited Reserve Transfers > Kilt -> HydraDx live V2 > sender events "polkadotXcm" 1`] = ` +[ + { + "data": { + "outcome": { + "Complete": { + "proofSize": 0, + "refTime": 400000000, + }, + }, + }, + "method": "Attempted", + "section": "polkadotXcm", + }, +] +`; + +exports[`Limited Reserve Transfers > Kilt -> HydraDx live V2 > sender events "xcmpQueue" 1`] = ` +[ + { + "data": { + "messageHash": "(hash)", + }, + "method": "XcmpMessageSent", + "section": "xcmpQueue", + }, +] +`; + +exports[`Limited Reserve Transfers > Kilt -> HydraDx live V3 > receiver events "xcmpQueue" 1`] = ` +[ + { + "data": { + "messageHash": "(hash)", + "messageId": "(hash)", + "weight": { + "proofSize": 0, + "refTime": 400000000, + }, + }, + "method": "Success", + "section": "xcmpQueue", + }, +] +`; + +exports[`Limited Reserve Transfers > Kilt -> HydraDx live V3 > sender events "polkadotXcm" 1`] = ` +[ + { + "data": { + "outcome": { + "Complete": { + "proofSize": 0, + "refTime": 400000000, + }, + }, + }, + "method": "Attempted", + "section": "polkadotXcm", + }, +] +`; + +exports[`Limited Reserve Transfers > Kilt -> HydraDx live V3 > sender events "xcmpQueue" 1`] = ` +[ + { + "data": { + "messageHash": "(hash)", + }, + "method": "XcmpMessageSent", + "section": "xcmpQueue", + }, +] +`; + +exports[`Limited Reserve Transfers > Kilt DEV -> HydraDx live v2 > receiver events "xcmpQueue" 1`] = ` +[ + { + "data": { + "messageHash": "(hash)", + "messageId": "(hash)", + "weight": { + "proofSize": 0, + "refTime": 400000000, + }, + }, + "method": "Success", + "section": "xcmpQueue", + }, +] +`; + +exports[`Limited Reserve Transfers > Kilt DEV -> HydraDx live v2 > sender events "polkadotXcm" 1`] = ` +[ + { + "data": { + "outcome": { + "Complete": { + "proofSize": 0, + "refTime": 400000000, + }, + }, + }, + "method": "Attempted", + "section": "polkadotXcm", + }, +] +`; + +exports[`Limited Reserve Transfers > Kilt DEV -> HydraDx live v2 > sender events "xcmpQueue" 1`] = ` +[ + { + "data": { + "messageHash": "(hash)", + }, + "method": "XcmpMessageSent", + "section": "xcmpQueue", + }, +] +`; + +exports[`Limited Reserve Transfers > Kilt DEV -> HydraDx live v3 > receiver events "xcmpQueue" 1`] = ` +[ + { + "data": { + "messageHash": "(hash)", + "messageId": "(hash)", + "weight": { + "proofSize": 0, + "refTime": 400000000, + }, + }, + "method": "Success", + "section": "xcmpQueue", + }, +] +`; + +exports[`Limited Reserve Transfers > Kilt DEV -> HydraDx live v3 > sender events "polkadotXcm" 1`] = ` +[ + { + "data": { + "outcome": { + "Complete": { + "proofSize": 0, + "refTime": 400000000, + }, + }, + }, + "method": "Attempted", + "section": "polkadotXcm", + }, +] +`; + +exports[`Limited Reserve Transfers > Kilt DEV -> HydraDx live v3 > sender events "xcmpQueue" 1`] = ` +[ + { + "data": { + "messageHash": "(hash)", + }, + "method": "XcmpMessageSent", + "section": "xcmpQueue", + }, +] +`; diff --git a/integration-tests/chopsticks/src/tests/xcm/limitedReserveTransfer/config.ts b/integration-tests/chopsticks/src/tests/xcm/limitedReserveTransfer/config.ts new file mode 100644 index 000000000..2a7978ca8 --- /dev/null +++ b/integration-tests/chopsticks/src/tests/xcm/limitedReserveTransfer/config.ts @@ -0,0 +1,293 @@ +import * as PolkadotChainConfigs from '../../../network/index.js' +import { initialBalanceKILT, keysAlice, keysBob } from '../../../helper/utils.js' +import * as SpiritnetConfig from '../../../network/spiritnet.js' +import * as HydraDxConfig from '../../../network/hydraDx.js' +import { tx, query } from '../../../helper/api.js' + +import type { ApiPromise } from '@polkadot/api' +import type { BasicConfig, BasicXcmTestConfiguration, BasisTxContext } from '../types.js' +import type { SubmittableExtrinsic } from '@polkadot/api/types' + +interface Config extends BasicConfig { + precision: bigint +} + +interface Query { + sender: ({ api }: { api: ApiPromise }, address: string) => Promise + receiver: ({ api }: { api: ApiPromise }, address: string) => Promise +} + +interface TxContext extends BasisTxContext { + balanceToTransfer: bigint + tx: ({ api }: { api: ApiPromise }, submitter: string, amount: string | number) => SubmittableExtrinsic<'promise'> +} + +/* + * Configuration for the LimitedReserveTransfer test extends the BasicXcmTestConfiguration + **/ +interface LimitedReserveTestConfiguration extends BasicXcmTestConfiguration { + config: Config + query: Query + txContext: TxContext +} + +// Test pairs for limited reserve transfers +export const testPairsLimitedReserveTransfers: LimitedReserveTestConfiguration[] = [ + { + config: { + desc: 'Kilt -> HydraDx live V2', + precision: BigInt(96), + }, + + network: { + sender: PolkadotChainConfigs.all.spiritnet.getConfig({}), + receiver: PolkadotChainConfigs.all.hydraDx.getConfig({}), + relay: PolkadotChainConfigs.all.polkadot.getConfig({}), + }, + accounts: { + senderAccount: keysAlice, + receiverAccount: keysBob, + }, + query: { + sender: query.balances, + receiver: query.tokens(HydraDxConfig.kiltTokenId), + }, + txContext: { + tx: tx.xcmPallet.limitedReserveTransferAssetsV2( + SpiritnetConfig.KILT, + tx.xcmPallet.parachainV2(1, HydraDxConfig.paraId) + ), + pallets: { + sender: ['xcmpQueue', 'polkadotXcm'], + receiver: ['xcmpQueue'], + }, + balanceToTransfer: BigInt(1e15), + }, + storage: { + senderStorage: SpiritnetConfig.assignNativeTokensToAccounts([keysAlice.address], initialBalanceKILT), + receiverStorage: {}, + relayStorage: {}, + }, + sovereignAccount: { + sender: HydraDxConfig.siblingSovereignAccount, + receiver: SpiritnetConfig.siblingSovereignAccount, + }, + }, + + { + config: { + desc: 'Kilt -> HydraDx at block V2', + precision: BigInt(99), + }, + + network: { + sender: PolkadotChainConfigs.all.spiritnet.getConfig({ + blockNumber: PolkadotChainConfigs.all.spiritnet.parameters.blockNumber, + }), + receiver: PolkadotChainConfigs.all.hydraDx.getConfig({ + blockNumber: PolkadotChainConfigs.all.hydraDx.parameters.blockNumber, + }), + relay: PolkadotChainConfigs.all.polkadot.getConfig({ + blockNumber: PolkadotChainConfigs.all.polkadot.parameters.blockNumber, + }), + }, + accounts: { + senderAccount: keysAlice, + receiverAccount: keysBob, + }, + query: { + sender: query.balances, + receiver: query.tokens(HydraDxConfig.kiltTokenId), + }, + txContext: { + tx: tx.xcmPallet.limitedReserveTransferAssetsV2( + SpiritnetConfig.KILT, + tx.xcmPallet.parachainV2(1, HydraDxConfig.paraId) + ), + pallets: { + sender: ['xcmpQueue', 'polkadotXcm', { section: 'balances', method: 'Withdraw' }], + receiver: ['xcmpQueue', 'tokens', 'currencies'], + }, + balanceToTransfer: BigInt(1e15), + }, + storage: { + senderStorage: SpiritnetConfig.assignNativeTokensToAccounts([keysAlice.address], initialBalanceKILT), + receiverStorage: {}, + relayStorage: {}, + }, + sovereignAccount: { + sender: HydraDxConfig.siblingSovereignAccount, + receiver: SpiritnetConfig.siblingSovereignAccount, + }, + }, + + { + config: { + desc: 'Kilt -> HydraDx live V3', + precision: BigInt(96), + }, + + network: { + sender: PolkadotChainConfigs.all.spiritnet.getConfig({}), + receiver: PolkadotChainConfigs.all.hydraDx.getConfig({}), + relay: PolkadotChainConfigs.all.polkadot.getConfig({}), + }, + accounts: { + senderAccount: keysAlice, + receiverAccount: keysBob, + }, + query: { + sender: query.balances, + receiver: query.tokens(HydraDxConfig.kiltTokenId), + }, + txContext: { + tx: tx.xcmPallet.limitedReserveTransferAssetsV3( + SpiritnetConfig.KILT, + tx.xcmPallet.parachainV3(1, HydraDxConfig.paraId) + ), + pallets: { + sender: ['xcmpQueue', 'polkadotXcm'], + receiver: ['xcmpQueue'], + }, + balanceToTransfer: BigInt(1e15), + }, + storage: { + senderStorage: SpiritnetConfig.assignNativeTokensToAccounts([keysAlice.address], initialBalanceKILT), + receiverStorage: {}, + relayStorage: {}, + }, + sovereignAccount: { + sender: HydraDxConfig.siblingSovereignAccount, + receiver: SpiritnetConfig.siblingSovereignAccount, + }, + }, + + { + config: { + desc: 'Kilt -> HydraDx at block V3', + precision: BigInt(99), + }, + + network: { + sender: PolkadotChainConfigs.all.spiritnet.getConfig({ + blockNumber: PolkadotChainConfigs.all.spiritnet.parameters.blockNumber, + }), + receiver: PolkadotChainConfigs.all.hydraDx.getConfig({ + blockNumber: PolkadotChainConfigs.all.hydraDx.parameters.blockNumber, + }), + relay: PolkadotChainConfigs.all.polkadot.getConfig({ + blockNumber: PolkadotChainConfigs.all.polkadot.parameters.blockNumber, + }), + }, + accounts: { + senderAccount: keysAlice, + receiverAccount: keysBob, + }, + query: { + sender: query.balances, + receiver: query.tokens(HydraDxConfig.kiltTokenId), + }, + txContext: { + tx: tx.xcmPallet.limitedReserveTransferAssetsV3( + SpiritnetConfig.KILT, + tx.xcmPallet.parachainV3(1, HydraDxConfig.paraId) + ), + pallets: { + sender: ['xcmpQueue', 'polkadotXcm', { section: 'balances', method: 'Withdraw' }], + receiver: ['xcmpQueue', 'tokens', 'currencies'], + }, + balanceToTransfer: BigInt(1e15), + }, + storage: { + senderStorage: SpiritnetConfig.assignNativeTokensToAccounts([keysAlice.address], initialBalanceKILT), + receiverStorage: {}, + relayStorage: {}, + }, + sovereignAccount: { + sender: HydraDxConfig.siblingSovereignAccount, + receiver: SpiritnetConfig.siblingSovereignAccount, + }, + }, + + { + config: { + desc: 'Kilt DEV -> HydraDx live v3', + precision: BigInt(96), + }, + + network: { + sender: PolkadotChainConfigs.all.spiritnet.getConfig(PolkadotChainConfigs.all.spiritnet.parameters), + receiver: PolkadotChainConfigs.all.hydraDx.getConfig({}), + relay: PolkadotChainConfigs.all.polkadot.getConfig({}), + }, + accounts: { + senderAccount: keysAlice, + receiverAccount: keysBob, + }, + query: { + sender: query.balances, + receiver: query.tokens(HydraDxConfig.kiltTokenId), + }, + txContext: { + tx: tx.xcmPallet.limitedReserveTransferAssetsV3( + SpiritnetConfig.KILT, + tx.xcmPallet.parachainV3(1, HydraDxConfig.paraId) + ), + pallets: { + sender: ['xcmpQueue', 'polkadotXcm'], + receiver: ['xcmpQueue'], + }, + balanceToTransfer: BigInt(1e15), + }, + storage: { + senderStorage: SpiritnetConfig.assignNativeTokensToAccounts([keysAlice.address], initialBalanceKILT), + receiverStorage: {}, + relayStorage: {}, + }, + sovereignAccount: { + sender: HydraDxConfig.siblingSovereignAccount, + receiver: SpiritnetConfig.siblingSovereignAccount, + }, + }, + + { + config: { + desc: 'Kilt DEV -> HydraDx live v2', + precision: BigInt(96), + }, + + network: { + sender: PolkadotChainConfigs.all.spiritnet.getConfig(PolkadotChainConfigs.all.spiritnet.parameters), + receiver: PolkadotChainConfigs.all.hydraDx.getConfig({}), + relay: PolkadotChainConfigs.all.polkadot.getConfig({}), + }, + accounts: { + senderAccount: keysAlice, + receiverAccount: keysBob, + }, + query: { + sender: query.balances, + receiver: query.tokens(HydraDxConfig.kiltTokenId), + }, + txContext: { + tx: tx.xcmPallet.limitedReserveTransferAssetsV2( + SpiritnetConfig.KILT, + tx.xcmPallet.parachainV2(1, HydraDxConfig.paraId) + ), + pallets: { + sender: ['xcmpQueue', 'polkadotXcm'], + receiver: ['xcmpQueue'], + }, + balanceToTransfer: BigInt(1e15), + }, + storage: { + senderStorage: SpiritnetConfig.assignNativeTokensToAccounts([keysAlice.address], initialBalanceKILT), + receiverStorage: {}, + relayStorage: {}, + }, + sovereignAccount: { + sender: HydraDxConfig.siblingSovereignAccount, + receiver: SpiritnetConfig.siblingSovereignAccount, + }, + }, +] as const diff --git a/integration-tests/chopsticks/src/tests/xcm/limitedReserveTransfer/index.test.ts b/integration-tests/chopsticks/src/tests/xcm/limitedReserveTransfer/index.test.ts new file mode 100644 index 000000000..0c8276ded --- /dev/null +++ b/integration-tests/chopsticks/src/tests/xcm/limitedReserveTransfer/index.test.ts @@ -0,0 +1,124 @@ +import { describe, beforeEach, it, afterEach } from 'vitest' +import type { KeyringPair } from '@polkadot/keyring/types' +import { sendTransaction, withExpect } from '@acala-network/chopsticks-testing' + +import { createBlock, setStorage } from '../../../network/utils.js' +import { validateBalanceWithPrecision, hexAddress } from '../../../helper/utils.js' +import { testPairsLimitedReserveTransfers } from './config.js' +import { Config } from '../../../network/types.js' +import { setupNetwork, shutDownNetwork } from '../../../network/utils.js' + +describe.each(testPairsLimitedReserveTransfers)( + 'Limited Reserve Transfers', + { timeout: 30_000 }, + async ({ network, storage, accounts, query, sovereignAccount, txContext, config }) => { + let senderContext: Config + let receiverContext: Config + let relayContext: Config + let senderAccount: KeyringPair + let receiverAccount: KeyringPair + const { desc, precision } = config + + beforeEach(async () => { + const { receiver, sender, relay } = network + + const { receiverChainContext, senderChainContext, relayChainContext } = await setupNetwork( + relay, + sender, + receiver + ) + + relayContext = relayChainContext + senderContext = senderChainContext + receiverContext = receiverChainContext + + const { receiverStorage, senderStorage, relayStorage } = storage + await setStorage(senderContext, senderStorage) + await setStorage(receiverContext, receiverStorage) + await setStorage(relayContext, relayStorage) + + const { senderAccount: a, receiverAccount: b } = accounts + senderAccount = a + receiverAccount = b + }, 20_000) + + afterEach(async () => { + try { + await shutDownNetwork([senderContext, receiverContext, relayContext]) + } catch (error) { + if (!(error instanceof TypeError)) { + console.error(error) + } + } + }) + + it(desc, { timeout: 10_000, retry: 3 }, async ({ expect }) => { + const { checkEvents, checkSystemEvents } = withExpect(expect) + const { pallets, tx, balanceToTransfer } = txContext + + // Balance of the sovereign account before the transfer + const receiverSovereignAccountBalanceBeforeTransfer = await query.sender( + senderContext, + sovereignAccount.sender + ) + + const initialBalanceSender = await query.sender(senderContext, senderAccount.address) + + const initialBalanceReceiver = await query.receiver(receiverContext, receiverAccount.address) + + // Check initial balance receiver should be zero + expect(initialBalanceReceiver).toBe(BigInt(0)) + + const signedTx = tx( + senderContext, + hexAddress(receiverAccount.address), + balanceToTransfer.toString() + ).signAsync(senderAccount) + + const events = await sendTransaction(signedTx) + + // check sender state + await createBlock(senderContext) + + pallets.sender.map((pallet) => + checkEvents(events, pallet).toMatchSnapshot(`sender events ${JSON.stringify(pallet)}`) + ) + + const balanceSenderAfterTransfer = await query.sender(senderContext, senderAccount.address) + const receiverSovereignAccountBalanceAfterTransfer = await query.sender( + senderContext, + sovereignAccount.sender + ) + expect(receiverSovereignAccountBalanceAfterTransfer).toBe( + receiverSovereignAccountBalanceBeforeTransfer + BigInt(balanceToTransfer) + ) + + const removedBalance = balanceToTransfer * BigInt(-1) + + validateBalanceWithPrecision( + initialBalanceSender, + balanceSenderAfterTransfer, + removedBalance, + expect, + precision + ) + + // check receiver state + await createBlock(receiverContext) + + pallets.receiver.map((pallet) => + checkSystemEvents(receiverContext, pallet).toMatchSnapshot(`receiver events ${JSON.stringify(pallet)}`) + ) + + const balanceReceiverAfterTransfer = await query.receiver(receiverContext, receiverAccount.address) + + validateBalanceWithPrecision( + initialBalanceReceiver, + balanceReceiverAfterTransfer, + balanceToTransfer, + expect, + precision + ) + }) + } +) diff --git a/integration-tests/chopsticks/src/tests/xcm/limitedReserveTransferSpiritnetHydraDxV2.test.ts b/integration-tests/chopsticks/src/tests/xcm/limitedReserveTransferSpiritnetHydraDxV2.test.ts deleted file mode 100644 index f2f0a40e6..000000000 --- a/integration-tests/chopsticks/src/tests/xcm/limitedReserveTransferSpiritnetHydraDxV2.test.ts +++ /dev/null @@ -1,81 +0,0 @@ -import { test } from 'vitest' -import { sendTransaction, withExpect } from '@acala-network/chopsticks-testing' - -import * as SpiritnetConfig from '../../network/spiritnet.js' -import * as HydraDxConfig from '../../network/hydraDx.js' -import { KILT, initialBalanceKILT, keysAlice } from '../../utils.js' -import { spiritnetContext, hydradxContext, getFreeBalanceSpiritnet, getFreeBalanceHydraDxKilt } from '../index.js' -import { getAccountLocationV2, getNativeAssetIdLocation, getSiblingLocation } from '../../network/utils.js' -import { checkBalance, checkBalanceInRange, createBlock, hexAddress, setStorage } from '../utils.js' - -const KILT_ASSET_V2 = { V2: [getNativeAssetIdLocation(KILT)] } - -test('Limited Reserve V2 Transfers from Spiritnet Account Alice -> HydraDx Account Alice', async ({ expect }) => { - const { checkEvents, checkSystemEvents } = withExpect(expect) - - // Assign alice some KILT tokens - await setStorage( - spiritnetContext, - SpiritnetConfig.assignNativeTokensToAccounts([keysAlice.address], initialBalanceKILT) - ) - - // Balance of the hydraDx sovereign account before the transfer - const hydraDxSovereignAccountBalanceBeforeTransfer = await getFreeBalanceSpiritnet( - SpiritnetConfig.hydraDxSovereignAccount - ) - - // check initial balance of Alice on Spiritnet - await checkBalance(getFreeBalanceSpiritnet, keysAlice.address, expect, initialBalanceKILT) - - // Alice should have NO KILT on HydraDx - await checkBalance(getFreeBalanceHydraDxKilt, keysAlice.address, expect, BigInt(0)) - - const aliceAddress = hexAddress(keysAlice.address) - const hydraDxDestination = { V2: getSiblingLocation(HydraDxConfig.paraId) } - const beneficiary = getAccountLocationV2(aliceAddress) - - const signedTx = spiritnetContext.api.tx.polkadotXcm - .limitedReserveTransferAssets(hydraDxDestination, beneficiary, KILT_ASSET_V2, 0, 'Unlimited') - .signAsync(keysAlice) - - const events = await sendTransaction(signedTx) - - // Check sender state - await createBlock(spiritnetContext) - - // Check events sender - checkEvents(events, 'xcmpQueue').toMatchSnapshot('sender events xcm queue pallet') - checkEvents(events, 'polkadotXcm').toMatchSnapshot('sender events xcm pallet') - checkEvents(events, { section: 'balances', method: 'Withdraw' }).toMatchSnapshot('sender events Balances') - - // check balance. The sovereign account should hold one additional KILT. - await checkBalance( - getFreeBalanceSpiritnet, - SpiritnetConfig.hydraDxSovereignAccount, - expect, - hydraDxSovereignAccountBalanceBeforeTransfer + KILT - ) - - // check balance sender - // Equal to `initialBalanceKILT - KILT` - tx fees - await checkBalanceInRange(getFreeBalanceSpiritnet, keysAlice.address, expect, [ - BigInt('98999830999996320'), - BigInt('98999830999996321'), - ]) - - // Check receiver state - await createBlock(hydradxContext) - - // Check events receiver - checkSystemEvents(hydradxContext, { section: 'currencies', method: 'Deposited' }).toMatchSnapshot( - 'receiver events currencies' - ) - checkSystemEvents(hydradxContext, 'xcmpQueue').toMatchSnapshot('receiver events xcmpQueue') - - // check balance receiver - // check balance. Equal to `KILT` - tx fees - await checkBalanceInRange(getFreeBalanceHydraDxKilt, aliceAddress, expect, [ - BigInt(996349465529793), - BigInt(996349465529796), - ]) -}, 20_000) diff --git a/integration-tests/chopsticks/src/tests/xcm/limitedReserveTransferSpiritnetHydraDxV3.test.ts b/integration-tests/chopsticks/src/tests/xcm/limitedReserveTransferSpiritnetHydraDxV3.test.ts deleted file mode 100644 index 1f0a68245..000000000 --- a/integration-tests/chopsticks/src/tests/xcm/limitedReserveTransferSpiritnetHydraDxV3.test.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { test } from 'vitest' -import { sendTransaction, withExpect } from '@acala-network/chopsticks-testing' - -import * as SpiritnetConfig from '../../network/spiritnet.js' -import * as HydraDxConfig from '../../network/hydraDx.js' -import { KILT, initialBalanceKILT, keysAlice } from '../../utils.js' -import { spiritnetContext, hydradxContext, getFreeBalanceSpiritnet, getFreeBalanceHydraDxKilt } from '../index.js' -import { getAccountLocationV3, getNativeAssetIdLocation, getSiblingLocation } from '../../network/utils.js' -import { checkBalance, checkBalanceInRange, createBlock, hexAddress, setStorage } from '../utils.js' - -const KILT_ASSET_V3 = { V3: [getNativeAssetIdLocation(KILT)] } - -test('Limited Reserve V3 Transfers from Spiritnet Account Alice -> HydraDx Account Alice', async ({ expect }) => { - const { checkEvents, checkSystemEvents } = withExpect(expect) - - // Assign alice some KILT tokens - await setStorage( - spiritnetContext, - SpiritnetConfig.assignNativeTokensToAccounts([keysAlice.address], initialBalanceKILT) - ) - - // Balance of the hydraDx sovereign account before the transfer - const hydraDxSovereignAccountBalanceBeforeTransfer = await getFreeBalanceSpiritnet( - SpiritnetConfig.hydraDxSovereignAccount - ) - - // check initial balance of Alice on Spiritnet - await checkBalance(getFreeBalanceSpiritnet, keysAlice.address, expect, initialBalanceKILT) - // Alice should have NO KILT on HydraDx - await checkBalance(getFreeBalanceHydraDxKilt, keysAlice.address, expect, BigInt(0)) - - const aliceAddress = hexAddress(keysAlice.address) - const hydraDxDestination = { V3: getSiblingLocation(HydraDxConfig.paraId) } - const beneficiary = getAccountLocationV3(aliceAddress) - - const signedTx = spiritnetContext.api.tx.polkadotXcm - .limitedReserveTransferAssets(hydraDxDestination, beneficiary, KILT_ASSET_V3, 0, 'Unlimited') - .signAsync(keysAlice) - - const events = await sendTransaction(signedTx) - - // Check sender state - await createBlock(spiritnetContext) - - // Check events sender - checkEvents(events, 'xcmpQueue').toMatchSnapshot('sender events xcm queue pallet') - checkEvents(events, 'polkadotXcm').toMatchSnapshot('sender events xcm pallet') - checkEvents(events, { section: 'balances', method: 'Withdraw' }).toMatchSnapshot('sender events Balances') - - // check balance. The sovereign account should hold one additional KILT. - await checkBalance( - getFreeBalanceSpiritnet, - SpiritnetConfig.hydraDxSovereignAccount, - expect, - hydraDxSovereignAccountBalanceBeforeTransfer + KILT - ) - - // check balance sender - // Equal to `initialBalanceKILT - KILT` - tx fees - await checkBalanceInRange(getFreeBalanceSpiritnet, keysAlice.address, expect, [ - BigInt('98999830999996320'), - BigInt('98999830999996321'), - ]) - - // Check receiver state - await createBlock(hydradxContext) - - // Check events receiver - checkSystemEvents(hydradxContext, { section: 'currencies', method: 'Deposited' }).toMatchSnapshot( - 'receiver events currencies' - ) - checkSystemEvents(hydradxContext, 'xcmpQueue').toMatchSnapshot('receiver events xcmpQueue') - - // check balance receiver - // check balance. Equal to `KILT` - tx fees - await checkBalanceInRange(getFreeBalanceHydraDxKilt, aliceAddress, expect, [ - BigInt(996349465529793), - BigInt(996349465529796), - ]) -}, 20_000) diff --git a/integration-tests/chopsticks/src/tests/xcm/limitedReseveTransferHydraDxSpiritnet.test.ts b/integration-tests/chopsticks/src/tests/xcm/limitedReseveTransferHydraDxSpiritnet.test.ts deleted file mode 100644 index 704c93ee3..000000000 --- a/integration-tests/chopsticks/src/tests/xcm/limitedReseveTransferHydraDxSpiritnet.test.ts +++ /dev/null @@ -1,87 +0,0 @@ -import { test } from 'vitest' -import { sendTransaction, withExpect } from '@acala-network/chopsticks-testing' - -import * as HydraDxConfig from '../../network/hydraDx.js' -import * as SpiritnetConfig from '../../network/spiritnet.js' -import { KILT, initialBalanceHDX, initialBalanceKILT, keysAlice } from '../../utils.js' -import { getFreeBalanceHydraDxKilt, getFreeBalanceSpiritnet, hydradxContext, spiritnetContext } from '../index.js' -import { checkBalance, createBlock, hexAddress, setStorage, checkBalanceInRange } from '../utils.js' - -const aliceLocation = { - V3: { - parents: 1, - interior: { - X2: [ - { Parachain: SpiritnetConfig.paraId }, - { - AccountId32: { - id: hexAddress(keysAlice.address), - }, - }, - ], - }, - }, -} - -test('Limited Reserve Transfers from HydraDx Account Alice -> Spiritnet Account Alice', async ({ expect }) => { - const { checkEvents, checkSystemEvents } = withExpect(expect) - - // assign initial balance to Alice. Alice also needs to have some HDX, otherwise the account gets dusted - const config = { - ...HydraDxConfig.assignKiltTokensToAccounts([keysAlice.address], initialBalanceKILT), - ...HydraDxConfig.assignNativeTokensToAccounts([keysAlice.address], initialBalanceHDX), - } - - // Set storage - await setStorage(hydradxContext, config) - - const hydraDxSovereignAccountBalanceBeforeTransfer = await getFreeBalanceSpiritnet( - SpiritnetConfig.hydraDxSovereignAccount - ) - - // check initial balance of alice - await checkBalance(getFreeBalanceHydraDxKilt, keysAlice.address, expect, initialBalanceKILT) - - const signedTx = hydradxContext.api.tx.xTokens - .transfer(HydraDxConfig.kiltTokenId, KILT, aliceLocation, 'Unlimited') - .signAsync(keysAlice) - - const events = await sendTransaction(signedTx) - - // Check sender state - await createBlock(hydradxContext) - - // Check events sender - checkEvents(events, 'xcmpQueue').toMatchSnapshot('sender events xcm queue pallet') - checkEvents(events, { section: 'currencies', method: 'Withdrawn' }).toMatchSnapshot('sender events currencies') - checkEvents(events, 'xTokens').toMatchSnapshot('sender events currencies') - - // Check balance - await checkBalance(getFreeBalanceHydraDxKilt, keysAlice.address, expect, initialBalanceKILT - KILT) - - // Check receiver state - await createBlock(spiritnetContext) - - // check events receiver - checkSystemEvents(spiritnetContext, 'xcmpQueue').toMatchSnapshot('receiver events xcmpQueue') - checkSystemEvents(spiritnetContext, { section: 'balances', method: 'Withdraw' }).toMatchSnapshot( - 'receiver events Balances' - ) - checkSystemEvents(spiritnetContext, { section: 'balances', method: 'Endowed' }).toMatchSnapshot( - 'receiver events Balances' - ) - - // Check balance receiver - await checkBalance( - getFreeBalanceSpiritnet, - SpiritnetConfig.hydraDxSovereignAccount, - expect, - hydraDxSovereignAccountBalanceBeforeTransfer - KILT - ) - - // Alice receives a bit less since the tx fees has to be paid. - await checkBalanceInRange(getFreeBalanceSpiritnet, keysAlice.address, expect, [ - BigInt('999999999971174'), - BigInt('999999999976345'), - ]) -}, 20_000) diff --git a/integration-tests/chopsticks/src/tests/xcm/types.ts b/integration-tests/chopsticks/src/tests/xcm/types.ts new file mode 100644 index 000000000..96f993457 --- /dev/null +++ b/integration-tests/chopsticks/src/tests/xcm/types.ts @@ -0,0 +1,48 @@ +import type { ApiPromise } from '@polkadot/api' +import type { SubmittableExtrinsic } from '@polkadot/api/types' +import type { KeyringPair } from '@polkadot/keyring/types' +import type { SetupOption, EventFilter } from '@acala-network/chopsticks-testing' + +export interface Storage { + senderStorage: Record> + receiverStorage: Record> + relayStorage: Record> +} + +export interface Accounts { + senderAccount: KeyringPair + receiverAccount: KeyringPair +} + +export interface NetworkSetupOption { + sender: SetupOption + receiver: SetupOption + relay: SetupOption +} + +export interface BasisTxContext { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + tx: ({ api }: { api: ApiPromise }, submitter: string, ...args: any[]) => SubmittableExtrinsic<'promise'> + pallets: { + sender: EventFilter[] + receiver: EventFilter[] + } +} + +export interface BasicConfig { + desc: string +} + +interface SovereignAccount { + sender: string + receiver: string +} + +export interface BasicXcmTestConfiguration { + config: BasicConfig + storage: Storage + accounts: Accounts + network: NetworkSetupOption + sovereignAccount: SovereignAccount + txContext: BasisTxContext +} diff --git a/integration-tests/chopsticks/src/utils.ts b/integration-tests/chopsticks/src/utils.ts deleted file mode 100644 index 272d444f3..000000000 --- a/integration-tests/chopsticks/src/utils.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Keyring } from '@polkadot/keyring' - -const keyring = new Keyring({ type: 'ed25519', ss58Format: 38 }) - -export const keysAlice = keyring.addFromUri('//alice', undefined, 'ed25519') -export const keysBob = keyring.addFromUri('//bob', undefined, 'ed25519') -export const keysCharlie = keyring.addFromUri('//charlie', undefined, 'ed25519') - -export function toNumber(value: string | undefined): number | undefined { - if (value === undefined) { - return undefined - } - - return Number(value) -} - -export const KILT = BigInt(1e15) -export const DOT = BigInt(1e10) -export const HDX = BigInt(1e12) - -export const initialBalanceKILT = BigInt(100) * KILT -export const initialBalanceDOT = BigInt(100) * DOT -export const initialBalanceHDX = BigInt(100) * HDX diff --git a/integration-tests/chopsticks/yarn.lock b/integration-tests/chopsticks/yarn.lock index d835f62c5..bca06ac54 100644 --- a/integration-tests/chopsticks/yarn.lock +++ b/integration-tests/chopsticks/yarn.lock @@ -7,33 +7,12 @@ resolved "https://registry.yarnpkg.com/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz#bd9154aec9983f77b3a034ecaa015c2e4201f6cf" integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA== -"@acala-network/chopsticks-core@0.10.0": - version "0.10.0" - resolved "https://registry.yarnpkg.com/@acala-network/chopsticks-core/-/chopsticks-core-0.10.0.tgz#0191a4ca9739b84f07e4267cb5e7d0ca7eccb7b9" - integrity sha512-JoxlHLLpKgs/STdG4xh8KkMIWM0Ssjo1aeMEoW8G0zgKcgGqUdrsMGtJoHsxxTFOYvsZCavV37ukGLZFmq1zMg== - dependencies: - "@acala-network/chopsticks-executor" "0.10.0" - "@polkadot/rpc-provider" "^10.11.2" - "@polkadot/types" "^10.11.2" - "@polkadot/types-codec" "^10.11.2" - "@polkadot/types-known" "^10.11.2" - "@polkadot/util" "^12.6.2" - "@polkadot/util-crypto" "^12.6.2" - comlink "^4.4.1" - eventemitter3 "^5.0.1" - lodash "^4.17.21" - lru-cache "^10.2.0" - pino "^8.19.0" - pino-pretty "^11.0.0" - rxjs "^7.8.1" - zod "^3.22.4" - -"@acala-network/chopsticks-core@0.10.1": - version "0.10.1" - resolved "https://registry.yarnpkg.com/@acala-network/chopsticks-core/-/chopsticks-core-0.10.1.tgz#11920c95db7f609d26b93d57ea1d30732b8467f0" - integrity sha512-AmGRoUzbmVHy8rZL6nA+CtiP4GZw/4DGTN+srqPcaWPq/hGKYKjhfw6yOUS/Ln7wukbTGfoy0pP9FUNwFAOt4g== +"@acala-network/chopsticks-core@0.11.0": + version "0.11.0" + resolved "https://registry.yarnpkg.com/@acala-network/chopsticks-core/-/chopsticks-core-0.11.0.tgz#b3eb9377658c000c516db5a090f7f9a69ba910ae" + integrity sha512-3+ensuinkesviDh2EI97XUv/M2gOAovUdOGQEeB/xC3ynNBP0t9zVi39C+L9fuW0PGjr2bqZlM30JXI0VQVXqA== dependencies: - "@acala-network/chopsticks-executor" "0.10.1" + "@acala-network/chopsticks-executor" "0.11.0" "@polkadot/rpc-provider" "^10.11.2" "@polkadot/types" "^10.11.2" "@polkadot/types-codec" "^10.11.2" @@ -49,91 +28,50 @@ rxjs "^7.8.1" zod "^3.22.4" -"@acala-network/chopsticks-db@0.10.0": - version "0.10.0" - resolved "https://registry.yarnpkg.com/@acala-network/chopsticks-db/-/chopsticks-db-0.10.0.tgz#4088c255fe7409d8f098e2582bc68b628a58dd3f" - integrity sha512-wVqSjOQGMMdQVfsGhLqw7PzVYL8OIgtxZYRDT1fqwd844DeEV4RzhQvIoyzzmSpMR91RHr0qTg58zoBdv7UKbQ== - dependencies: - "@acala-network/chopsticks-core" "0.10.0" - "@polkadot/util" "^12.6.2" - idb "^8.0.0" - sqlite3 "^5.1.7" - typeorm "^0.3.20" - -"@acala-network/chopsticks-db@0.10.1": - version "0.10.1" - resolved "https://registry.yarnpkg.com/@acala-network/chopsticks-db/-/chopsticks-db-0.10.1.tgz#30e630e23d54f97b0fe06812e242ce0ae95541ac" - integrity sha512-5zkQ0pZHEKjqYb2aQvrzPNzju0iQhe+C1QflW21m8pC0BzCXPRQGC3edyPn6Lmthq/8nLN3CJyb4LccY2ENhFQ== +"@acala-network/chopsticks-db@0.11.0": + version "0.11.0" + resolved "https://registry.yarnpkg.com/@acala-network/chopsticks-db/-/chopsticks-db-0.11.0.tgz#6710f4285b46672c724ecc52a7d2f48028712367" + integrity sha512-egKySQ29Az4CCTwStZBgs6Lpg1c7IlE3e/dVMA2wIG65u7TqAdRxirjPLFRdayTVrvAgCPYbH3pQVpmUbh2yfg== dependencies: - "@acala-network/chopsticks-core" "0.10.1" + "@acala-network/chopsticks-core" "0.11.0" "@polkadot/util" "^12.6.2" idb "^8.0.0" sqlite3 "^5.1.7" typeorm "^0.3.20" -"@acala-network/chopsticks-executor@0.10.0": - version "0.10.0" - resolved "https://registry.yarnpkg.com/@acala-network/chopsticks-executor/-/chopsticks-executor-0.10.0.tgz#2d5e0fd179d6fa2679aaf9fc31312fa4c58a77e8" - integrity sha512-hBjlVjdCZ+d8Oo50X9U+wHqf0jQ9AucFj2dX9rLV+AQrNDOAeWob+kSqMMRcEUembg3Fyv7zIPv8OGVFrOreiQ== - dependencies: - "@polkadot/util" "^12.6.2" - "@polkadot/wasm-util" "^7.3.2" - -"@acala-network/chopsticks-executor@0.10.1": - version "0.10.1" - resolved "https://registry.yarnpkg.com/@acala-network/chopsticks-executor/-/chopsticks-executor-0.10.1.tgz#017eba0618c7e379810c4f20e90eb583df3b15c2" - integrity sha512-NUFD2GaGWIpPtE3C7i5T1BJJwMLZgQZdHgGSREQV8KndzwiuWqkxDu33N/cmDkrFmZG84S6xVnf6qPHIsw5UNQ== +"@acala-network/chopsticks-executor@0.11.0": + version "0.11.0" + resolved "https://registry.yarnpkg.com/@acala-network/chopsticks-executor/-/chopsticks-executor-0.11.0.tgz#1904280cd3ed04eb6b9b1b105789dc0f8b559013" + integrity sha512-1rRf5NghiaAexM9RvJITB1P92wPwaQunLp9svggBacRA6EYlgEgjJ7Sp2lo1QXmr2kjTQQs2mmGsMeQksrbkcg== dependencies: "@polkadot/util" "^12.6.2" "@polkadot/wasm-util" "^7.3.2" -"@acala-network/chopsticks-testing@0.10.1": - version "0.10.1" - resolved "https://registry.yarnpkg.com/@acala-network/chopsticks-testing/-/chopsticks-testing-0.10.1.tgz#dc33561df5e76bc9580b4fc851a6df9f831fedb0" - integrity sha512-GaTZVckSZVLZJC7V8TmiDt3fWsSzWtg9az3/GBz1jB2wx7EJDKd9Siu2lVMyJW6jZlEGXijtuexzkOXtttAWCw== +"@acala-network/chopsticks-testing@0.11.0": + version "0.11.0" + resolved "https://registry.yarnpkg.com/@acala-network/chopsticks-testing/-/chopsticks-testing-0.11.0.tgz#e43d1c0e9d6a8090ba96f32add3d96c659cb62c3" + integrity sha512-Z2zRutmn5ccHxnBxLQSldCcUBQfEgT5h6mZnQ+QVYbaO06SYpN1xpGjJw7Vj+ZLZW44Oi6Lc0whEgmN0ZLwgDA== dependencies: - "@acala-network/chopsticks-utils" "0.10.1" + "@acala-network/chopsticks-utils" "0.11.0" "@polkadot/api" "^10.11.2" "@polkadot/types" "^10.11.2" -"@acala-network/chopsticks-utils@0.10.1": - version "0.10.1" - resolved "https://registry.yarnpkg.com/@acala-network/chopsticks-utils/-/chopsticks-utils-0.10.1.tgz#5839afc2c5f14b7db029116432f1b870370803d7" - integrity sha512-P7V5B5SbWqgplqdpExjsxbFW2S/+fA2hQ0Ow9o57SsM0qg3sH3YaVd2jBJ6kEHXGoFxUGqUIoiKm/+3cYUUImA== +"@acala-network/chopsticks-utils@0.11.0": + version "0.11.0" + resolved "https://registry.yarnpkg.com/@acala-network/chopsticks-utils/-/chopsticks-utils-0.11.0.tgz#95eb7415972f3e1b8ba95faa6ed32d2ff045c368" + integrity sha512-fUA0/wJnPkyxPAT7W85Ufn5bQtPQpLBdPVIVHVRaWwF8E3gaFSm4E1aLnquv+PRT66yYf6aYPAXpnsDhBjwQIg== dependencies: - "@acala-network/chopsticks" "0.10.1" + "@acala-network/chopsticks" "0.11.0" "@polkadot/api" "^10.11.2" "@polkadot/types" "^10.11.2" -"@acala-network/chopsticks@0.10.0": - version "0.10.0" - resolved "https://registry.yarnpkg.com/@acala-network/chopsticks/-/chopsticks-0.10.0.tgz#d4af5b220b8b14b79254ebd0622c2f500828c25b" - integrity sha512-mx5hIGOHll0tz1+jRA4jpC+GaUhLeTWULSFyf9DgbHHR0rfxtzhNgML8EOIrIA5Otg3v0/0WlBwFqvMRQCvFwA== - dependencies: - "@acala-network/chopsticks-core" "0.10.0" - "@acala-network/chopsticks-db" "0.10.0" - "@pnpm/npm-conf" "^2.2.2" - "@polkadot/api-augment" "^10.11.2" - "@polkadot/types" "^10.11.2" - "@polkadot/util" "^12.6.2" - "@polkadot/util-crypto" "^12.6.2" - axios "^1.6.8" - dotenv "^16.4.5" - global-agent "^3.0.0" - js-yaml "^4.1.0" - jsondiffpatch "^0.5.0" - lodash "^4.17.21" - ws "^8.16.0" - yargs "^17.7.2" - zod "^3.22.4" - -"@acala-network/chopsticks@0.10.1": - version "0.10.1" - resolved "https://registry.yarnpkg.com/@acala-network/chopsticks/-/chopsticks-0.10.1.tgz#466a04328a580c077ecd8544a2d2939c5a5f2817" - integrity sha512-zSMK8G3wbrygKQJUjftotXBqlDHpTAZHNgZdyso8wGX+mKjEvtzVPt5ADX/h9pDXgvg5yehyJHTjQBL6QbwDdw== +"@acala-network/chopsticks@0.11.0": + version "0.11.0" + resolved "https://registry.yarnpkg.com/@acala-network/chopsticks/-/chopsticks-0.11.0.tgz#499192430c11379f129f1a25d17375a471fe75e1" + integrity sha512-D40P8mgZDGWa/QePiiSzT371v7cPln2jpmfiLJ1k8SmpU9wQndophDBxGccDWjU4N7JtCCZEE5NpoAqs+3UO/A== dependencies: - "@acala-network/chopsticks-core" "0.10.1" - "@acala-network/chopsticks-db" "0.10.1" + "@acala-network/chopsticks-core" "0.11.0" + "@acala-network/chopsticks-db" "0.11.0" "@pnpm/npm-conf" "^2.2.2" "@polkadot/api-augment" "^10.11.2" "@polkadot/types" "^10.11.2"