Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: add foreign assets script to test network #272

Merged
merged 16 commits into from
Nov 2, 2023
Merged
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"build:examples": "substrate-exec-rimraf examples/build/ && substrate-exec-tsc --project examples/tsconfig.json",
"start": "node ./lib/index.js",
"start:zombienet-post-script": "yarn build:scripts && node ./scripts/build/testNetworkSetup.js",
"start:zombienet-foreign-assets-script": "yarn build:scripts && node ./scripts/build/testNetworkForeignAssets.js",
"lint": "substrate-dev-run-lint",
"lint:fix": "substrate-dev-run-lint --fix",
"test": "NODE_ENV=test substrate-exec-jest --detectOpenHandles",
Expand Down
5 changes: 5 additions & 0 deletions scripts/consts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// Copyright 2023 Parity Technologies (UK) Ltd.

export const KUSAMA_ASSET_HUB_WS_URL = 'ws://127.0.0.1:9911';
export const ROCOCO_ALICE_WS_URL = 'ws://127.0.0.1:9900';
export const TRAPPIST_WS_URL = 'ws://127.0.0.1:9921';
220 changes: 220 additions & 0 deletions scripts/testNetworkForeignAssets.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
// Copyright 2023 Parity Technologies (UK) Ltd.

import { ApiPromise, WsProvider } from '@polkadot/api';
import { Keyring } from '@polkadot/keyring';
import { cryptoWaitReady } from '@polkadot/util-crypto';
import chalk from 'chalk';

import { KUSAMA_ASSET_HUB_WS_URL, TRAPPIST_WS_URL } from './consts';
import { awaitBlockProduction, delay, logWithDate } from './util';

const fAssetSetMetadataCall = (assetHubApi: ApiPromise): `0x${string}` => {
const trappistMultiLocation = {
parents: 1,
interior: {
X1: {
parachain: 1836,
},
},
};

const setMetadataTx = assetHubApi.tx.foreignAssets.setMetadata(trappistMultiLocation, 'Trappist Hop', 'Hop', 12);

const hexCall = assetHubApi.registry
.createType('Call', {
callIndex: setMetadataTx.callIndex,
args: setMetadataTx.args,
})
.toHex();

return hexCall;
};

const fAssetCreateCall = (assetHubApi: ApiPromise): `0x${string}` => {
const trappistMultiLocation = {
parents: 1,
interior: {
X1: {
parachain: 1836,
},
},
};

const createTx = assetHubApi.tx.foreignAssets.create(
trappistMultiLocation,
'5Eg2fnsjAAr8RGZfa8Sy5mYFPabA9ZLNGYECCKXPD6xnK6D2', // Sibling 1836 -> ParaId
'100000000000'
);

const hexCall = assetHubApi.registry
.createType('Call', {
callIndex: createTx.callIndex,
args: createTx.args,
})
.toHex();

return hexCall;
};

const sudoCallWrapper = (trappistApi: ApiPromise, call: `0x${string}`) => {
// Double encode the call
const xcmDoubleEncoded = trappistApi.createType('XcmDoubleEncoded', {
encoded: call,
});

const xcmOriginType = trappistApi.createType('XcmOriginKind', 'Xcm');
const xcmDestMultiLocation = {
V3: {
parents: 1,
interior: {
X1: {
parachain: 1000,
},
},
},
};
const xcmMessage = {
V3: [
{
withdrawAsset: [
{
id: {
concrete: {
parents: 1,
interior: { Here: '' },
},
},
fun: {
fungible: 100000000000,
},
},
],
},
{
buyExecution: {
fees: {
id: {
concrete: {
parents: 1,
interior: { Here: '' },
},
},
fun: {
fungible: 100000000000,
},
},
weightLimit: { Unlimited: '' },
},
},
{
transact: {
originKind: xcmOriginType,
requireWeightAtMost: {
refTime: 8000000000,
proofSize: 65536,
},
call: xcmDoubleEncoded,
},
},
{
refundSurplus: true,
},
{
depositAsset: {
assets: {
wild: {
All: '',
},
},
beneficiary: {
parents: 0,
interior: {
x1: {
AccountId32: {
id: '0x7369626c2c070000000000000000000000000000000000000000000000000000',
},
},
},
},
},
},
],
};
const xcmMsg = trappistApi.tx.polkadotXcm.send(xcmDestMultiLocation, xcmMessage);
const xcmCall = trappistApi.createType('Call', {
callIndex: xcmMsg.callIndex,
args: xcmMsg.args,
});

return xcmCall;
};

const createForeignAssetViaSudo = (assetHubApi: ApiPromise, trappistApi: ApiPromise) => {
const foreignAssetCreateCall = fAssetCreateCall(assetHubApi);
return sudoCallWrapper(trappistApi, foreignAssetCreateCall);
};

const setMetadataForeignAssetViaSudo = (assetHubApi: ApiPromise, trappistApi: ApiPromise) => {
const setMetadataCall = fAssetSetMetadataCall(assetHubApi);
return sudoCallWrapper(trappistApi, setMetadataCall);
};

const main = async () => {
logWithDate(chalk.yellow('Initializing script to create foreignAssets on chain'));
await cryptoWaitReady();

const keyring = new Keyring({ type: 'sr25519' });
const alice = keyring.addFromUri('//Alice');
const bob = keyring.addFromUri('//Bob');

const kusamaAssetHubApi = await ApiPromise.create({
provider: new WsProvider(KUSAMA_ASSET_HUB_WS_URL),
noInitWarn: true,
});

await kusamaAssetHubApi.isReady;
logWithDate(chalk.green('Created a connection to Kusama AssetHub'));

const trappistApi = await ApiPromise.create({
provider: new WsProvider(TRAPPIST_WS_URL),
noInitWarn: true,
});

await trappistApi.isReady;
logWithDate(chalk.green('Created a connection to Trappist'));

logWithDate(chalk.magenta('Sending funds to Trappist Sibling on Kusama AssetHub'));

await kusamaAssetHubApi.tx.balances
.transferKeepAlive('5Eg2fnsjAAr8RGZfa8Sy5mYFPabA9ZLNGYECCKXPD6xnK6D2', 10000000000000)
.signAndSend(bob);

const foreignAssetsCreateSudoXcmCall = createForeignAssetViaSudo(kusamaAssetHubApi, trappistApi);

logWithDate('Sending Sudo XCM message from relay chain to execute create foreign asset call on Kusama AssetHub');
await trappistApi.tx.sudo.sudo(foreignAssetsCreateSudoXcmCall).signAndSend(alice);

await delay(24000);

const foreignAssetsSetMetadataSudoXcmCall = setMetadataForeignAssetViaSudo(kusamaAssetHubApi, trappistApi);

logWithDate('Sending Sudo XCM message from relay chain to execute setMetadata call on Kusama AssetHub');
await trappistApi.tx.sudo.sudo(foreignAssetsSetMetadataSudoXcmCall).signAndSend(alice);

await delay(24000);

await kusamaAssetHubApi.disconnect().then(() => {
logWithDate(chalk.blue('Polkadot-js successfully disconnected from asset-hub'));
});

await trappistApi.disconnect().then(() => {
logWithDate(chalk.blue('Polkadot-js successfully disconnected from trappist'));
});
};

// eslint-disable-next-line @typescript-eslint/no-floating-promises
awaitBlockProduction(TRAPPIST_WS_URL).then(async () => {
await main()
.catch(console.error)
.finally(() => process.exit());
});
63 changes: 4 additions & 59 deletions scripts/testNetworkSetup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,72 +5,17 @@ import '@polkadot/api-augment';
import { ApiPromise, WsProvider } from '@polkadot/api';
import { Keyring } from '@polkadot/keyring';
import { DispatchError } from '@polkadot/types/interfaces';
import { formatDate } from '@polkadot/util';
import { cryptoWaitReady } from '@polkadot/util-crypto';
import chalk from 'chalk';

import { KUSAMA_ASSET_HUB_WS_URL, ROCOCO_ALICE_WS_URL } from './consts';
import { awaitBlockProduction, delay, logWithDate } from './util';

/**
* This script is intended to be run after zombienet is running.
* It uses the hard coded values given in `zombienet.toml`.
*/

const KUSAMA_ASSET_HUB_WS_URL = 'ws://127.0.0.1:9911';
const ROCOCO_ALICE_WS_URL = 'ws://127.0.0.1:9900';

/**
* Set a delay (sleep)
*
* @param ms Milliseconds
*/
const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));

/**
* Formats a string to match the output of polkadot-js logging.
*
* @param log String to be logged
* @param remove Remove lines before that were cleared by std
*/
const logWithDate = (log: string, remove?: boolean) => {
remove
? console.log(`\r${formatDate(new Date())} ${log}`)
: console.log(`${formatDate(new Date())} ${log}`);
};

/**
* Will block the main script from running until there is blocks in Polkadot AssetHub being produced.
*/
const awaitBlockProduction = async () => {
logWithDate(chalk.yellow(`Initializing polkadot-js: Polling until ${KUSAMA_ASSET_HUB_WS_URL} is available`));
const kusamaAssetHubApi = await ApiPromise.create({
provider: new WsProvider(KUSAMA_ASSET_HUB_WS_URL),
noInitWarn: true,
});
logWithDate(chalk.yellow('Polkadot-js is connected'));

await kusamaAssetHubApi.isReady;

let counter = 3;
let blocksProducing = false;
while (!blocksProducing) {
const { number } = await kusamaAssetHubApi.rpc.chain.getHeader();

if (number.toNumber() > 0) {
blocksProducing = true;
}
await delay(1000);

counter += 1;
process.stdout.clearLine(0);
process.stdout.write(`\rWaiting for Block production on Kusama AssetHub${'.'.repeat((counter % 3) + 1)}`);
}

process.stdout.clearLine(0);
logWithDate(chalk.magenta('Blocks are producing'), true);
await kusamaAssetHubApi.disconnect().then(() => {
logWithDate(chalk.blue('Polkadot-js successfully disconnected'));
});
};

const main = async () => {
logWithDate(chalk.yellow('Initializing script to run transaction on chain'));
await cryptoWaitReady();
Expand Down Expand Up @@ -214,7 +159,7 @@ const main = async () => {
};

// eslint-disable-next-line @typescript-eslint/no-floating-promises
awaitBlockProduction().then(async () => {
awaitBlockProduction(KUSAMA_ASSET_HUB_WS_URL).then(async () => {
await main()
.catch(console.error)
.finally(() => process.exit());
Expand Down
59 changes: 59 additions & 0 deletions scripts/util.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Copyright 2023 Parity Technologies (UK) Ltd.

import { ApiPromise, WsProvider } from '@polkadot/api';
import { formatDate } from '@polkadot/util';
import chalk from 'chalk';

/**
* Set a delay (sleep)
*
* @param ms Milliseconds
*/
export const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));

/**
* Formats a string to match the output of polkadot-js logging.
*
* @param log String to be logged
* @param remove Remove lines before that were cleared by std
*/
export const logWithDate = (log: string, remove?: boolean) => {
remove
? console.log(`\r${formatDate(new Date())} ${log}`)
: console.log(`${formatDate(new Date())} ${log}`);
};

/**
* Will block the main script from running until there is blocks in Polkadot AssetHub being produced.
*/
export const awaitBlockProduction = async (wsUrl: string) => {
logWithDate(chalk.yellow(`Initializing polkadot-js: Polling until ${wsUrl} is available`));
const api = await ApiPromise.create({
provider: new WsProvider(wsUrl),
noInitWarn: true,
});
logWithDate(chalk.yellow('Polkadot-js is connected'));

await api.isReady;

let counter = 3;
let blocksProducing = false;
while (!blocksProducing) {
const { number } = await api.rpc.chain.getHeader();

if (number.toNumber() > 0) {
blocksProducing = true;
}
await delay(1000);

counter += 1;
process.stdout.clearLine(0);
process.stdout.write(`\rWaiting for Block production on Kusama AssetHub${'.'.repeat((counter % 3) + 1)}`);
}

process.stdout.clearLine(0);
logWithDate(chalk.magenta('Blocks are producing'), true);
await api.disconnect().then(() => {
logWithDate(chalk.blue('Polkadot-js successfully disconnected'));
});
};
Loading