diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 00000000..16d8d68f --- /dev/null +++ b/.eslintignore @@ -0,0 +1,2 @@ +build/ +node_modules/ \ No newline at end of file diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 00000000..29a09851 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,28 @@ +{ + "env": { + "node": true, + "es2021": true + }, + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended" + ], + "parser": "@typescript-eslint/parser", + "parserOptions": { + "ecmaVersion": 12, + "sourceType": "script" + }, + "plugins": [ + "@typescript-eslint" + ], + "rules": { + "indent": ["error", 2], + "linebreak-style": ["error", "unix"], + "quotes": ["error", "single"], + "semi": ["error", "always"], + "@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }], + "@typescript-eslint/explicit-module-boundary-types": "off", + "@typescript-eslint/no-explicit-any": "off", + "no-undef": "off" + } +} diff --git a/.github/workflows/lint-e2e_tests.yml b/.github/workflows/lint-e2e_tests.yml new file mode 100644 index 00000000..6c9562d0 --- /dev/null +++ b/.github/workflows/lint-e2e_tests.yml @@ -0,0 +1,30 @@ +name: Lint e2e_tests + +on: + pull_request: + branches: + - main + push: + branches: + - main + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +# A workflow run is made up of one or more jobs that can run sequentially or in parallel +jobs: + lint: + runs-on: ubuntu-22.04 + defaults: + run: + shell: bash + working-directory: ./e2e_tests + steps: + - name: Checkout + uses: actions/checkout@v3.1.0 + - run: npm i + - run: npm run format + - run: npm run lint + - run: npm run build diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 00000000..3e2e84b0 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,2 @@ +build/ +node_modules/ diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 00000000..f138104f --- /dev/null +++ b/.prettierrc @@ -0,0 +1,8 @@ +{ + "singleQuote": true, + "tabWidth": 2, + "semi": true, + "useTabs": false, + "trailingComma": "es5", + "arrowParens": "always" +} diff --git a/e2e_tests/common.ts b/e2e_tests/common.ts index dcadfc0c..f6d7991b 100644 --- a/e2e_tests/common.ts +++ b/e2e_tests/common.ts @@ -1,66 +1,67 @@ -import { ApiPromise, Keyring } from "@polkadot/api"; -import { SubmittableExtrinsic, SignerOptions } from "@polkadot/api/types"; -import { KeyringPair } from "@polkadot/keyring/types"; +import { ApiPromise, Keyring } from '@polkadot/api'; +import { SignerOptions, SubmittableExtrinsic } from '@polkadot/api/types'; +import { KeyringPair } from '@polkadot/keyring/types'; const RELAY_ASSET_ID = 1; async function submitExtrinsic( - signer: KeyringPair, - call: SubmittableExtrinsic<"promise">, - options: Partial + signer: KeyringPair, + call: SubmittableExtrinsic<'promise'>, + options: Partial ): Promise { - return new Promise(async (resolve, reject) => { - const unsub = await call.signAndSend(signer, options, (result) => { - console.log(`Current status is ${result.status}`); - if (result.status.isInBlock) { - console.log(`Transaction included at blockHash ${result.status.asInBlock}`); - } else if (result.status.isFinalized) { - console.log(`Transaction finalized at blockHash ${result.status.asFinalized}`); - unsub(); - return resolve(); - } else if (result.isError) { - console.log(`Transaction error`); - unsub(); - return reject(); - } - }); - }); + return new Promise((resolve, reject) => { + const unsub = call.signAndSend(signer, options, (result) => { + console.log(`Current status is ${result.status}`); + if (result.status.isInBlock) { + console.log(`Transaction included at blockHash ${result.status.asInBlock}`); + } else if (result.status.isFinalized) { + console.log(`Transaction finalized at blockHash ${result.status.asFinalized}`); + unsub.then(); + return resolve(); + } else if (result.isError) { + console.log('Transaction error'); + unsub.then(); + return reject(); + } + }); + }); } -async function setupRelayAsset( - api: ApiPromise, - signer: KeyringPair, - initialBalance: bigint = 0n -) { - const assetMetadata = { - decimals: 12, - name: "ROC", - symbol: "ROC", - existentialDeposit: 10n ** 3n, - location: null, - additional: null, - }; +async function setupRelayAsset(api: ApiPromise, signer: KeyringPair, initialBalance = 0n) { + const assetMetadata = { + decimals: 12, + name: 'ROC', + symbol: 'ROC', + existentialDeposit: 10n ** 3n, + location: null, + additional: null, + }; - const assetSetupCalls = [ - api.tx.assetRegistry.registerAsset(assetMetadata, RELAY_ASSET_ID), - api.tx.assetRate.create(RELAY_ASSET_ID, 1_000_000_000_000_000_000n), // 1 on 1 - ]; + const assetSetupCalls = [ + api.tx.assetRegistry.registerAsset(assetMetadata, RELAY_ASSET_ID), + api.tx.assetRate.create(RELAY_ASSET_ID, 1_000_000_000_000_000_000n), // 1 on 1 + ]; - if (initialBalance > BigInt(0)) { - assetSetupCalls.push( - api.tx.tokens.setBalance(signer.address, RELAY_ASSET_ID, initialBalance, 0) - ); - } + if (initialBalance > BigInt(0)) { + assetSetupCalls.push( + api.tx.tokens.setBalance(signer.address, RELAY_ASSET_ID, initialBalance, 0) + ); + } - const batchCall = api.tx.utility.batch(assetSetupCalls); - const sudoCall = api.tx.sudo.sudo(batchCall); + const batchCall = api.tx.utility.batch(assetSetupCalls); + const sudoCall = api.tx.sudo.sudo(batchCall); - await submitExtrinsic(signer, sudoCall, {}); + await submitExtrinsic(signer, sudoCall, {}); } // Transfer the relay chain asset to the parachain specified by paraId. // Receiver address is same as the sender's. -async function transferRelayAssetToPara(amount: bigint, paraId: number, relayApi: ApiPromise, signer: KeyringPair) { +async function transferRelayAssetToPara( + amount: bigint, + paraId: number, + relayApi: ApiPromise, + signer: KeyringPair +) { const receiverKeypair = new Keyring(); receiverKeypair.addFromAddress(signer.address); @@ -68,7 +69,7 @@ async function transferRelayAssetToPara(amount: bigint, paraId: number, relayApi const transferKind = paraId < 2000 ? 'limitedTeleportAssets' : 'limitedReserveTransferAssets'; const feeAssetItem = 0; - const weightLimit = "Unlimited"; + const weightLimit = 'Unlimited'; const reserveTransfer = relayApi.tx.xcmPallet[transferKind]( { V3: { parents: 0, interior: { X1: { Parachain: paraId } } } }, //dest { @@ -77,7 +78,7 @@ async function transferRelayAssetToPara(amount: bigint, paraId: number, relayApi interior: { X1: { AccountId32: { - chain: "Any", + chain: 'Any', id: receiverKeypair.pairs[0].publicKey, }, }, @@ -88,7 +89,7 @@ async function transferRelayAssetToPara(amount: bigint, paraId: number, relayApi V3: [ { id: { - Concrete: { parents: 0, interior: "Here" }, + Concrete: { parents: 0, interior: 'Here' }, }, fun: { Fungible: amount, @@ -97,13 +98,13 @@ async function transferRelayAssetToPara(amount: bigint, paraId: number, relayApi ], }, //asset feeAssetItem, - weightLimit, + weightLimit ); await submitExtrinsic(signer, reserveTransfer, {}); } async function sleep(milliseconds: number) { - return new Promise(resolve => setTimeout(resolve, milliseconds)); + return new Promise((resolve) => setTimeout(resolve, milliseconds)); } -export { submitExtrinsic, setupRelayAsset, transferRelayAssetToPara, sleep, RELAY_ASSET_ID } +export { RELAY_ASSET_ID, setupRelayAsset, sleep, submitExtrinsic, transferRelayAssetToPara }; diff --git a/e2e_tests/consts.ts b/e2e_tests/consts.ts index 3b2e0dff..697e1b23 100644 --- a/e2e_tests/consts.ts +++ b/e2e_tests/consts.ts @@ -16,4 +16,4 @@ const CONFIG = { contribution_timeout: 5, }; -export {UNIT, INITIAL_PRICE, CORE_COUNT, TIMESLICE_PERIOD, IDEAL_CORES_SOLD, CONFIG}; +export { CONFIG, CORE_COUNT, IDEAL_CORES_SOLD, INITIAL_PRICE, TIMESLICE_PERIOD, UNIT }; diff --git a/e2e_tests/fee-payment/custom.ts b/e2e_tests/fee-payment/custom.ts index 3883fdd2..0f5fea44 100644 --- a/e2e_tests/fee-payment/custom.ts +++ b/e2e_tests/fee-payment/custom.ts @@ -1,39 +1,39 @@ -import { ApiPromise, WsProvider, Keyring } from "@polkadot/api"; -import { submitExtrinsic, setupRelayAsset, RELAY_ASSET_ID } from "../common"; +import { ApiPromise, Keyring, WsProvider } from '@polkadot/api'; +import { RELAY_ASSET_ID, setupRelayAsset, submitExtrinsic } from '../common'; async function run(nodeName: string, networkInfo: any, _jsArgs: any) { - const { wsUri: regionXUri } = networkInfo.nodesByName[nodeName]; - const { wsUri: rococoUri } = networkInfo.nodesByName["rococo-validator01"]; - - const rococoApi = await ApiPromise.create({ provider: new WsProvider(rococoUri) }); - const regionXApi = await ApiPromise.create({ - provider: new WsProvider(regionXUri), - signedExtensions: { - ChargeAssetTxPayment: { - extrinsic: { - tip: "Compact", - assetId: "Option", - }, - payload: {}, - }, - }, - }); - - // account to submit tx - const keyring = new Keyring({ type: "sr25519" }); - const alice = keyring.addFromUri("//Alice"); - - const setXcmVersion = rococoApi.tx.xcmPallet.forceDefaultXcmVersion([3]); - await submitExtrinsic(alice, rococoApi.tx.sudo.sudo(setXcmVersion), {}); - - await setupRelayAsset(regionXApi, alice, 10n**12n); - - const receiverKeypair = new Keyring(); - receiverKeypair.addFromAddress(alice.address); - - // Try to pay for fees with relay chain asset. - const remarkCall = regionXApi.tx.system.remark("0x44"); - await submitExtrinsic(alice, remarkCall, { assetId: RELAY_ASSET_ID }); + const { wsUri: regionXUri } = networkInfo.nodesByName[nodeName]; + const { wsUri: rococoUri } = networkInfo.nodesByName['rococo-validator01']; + + const rococoApi = await ApiPromise.create({ provider: new WsProvider(rococoUri) }); + const regionXApi = await ApiPromise.create({ + provider: new WsProvider(regionXUri), + signedExtensions: { + ChargeAssetTxPayment: { + extrinsic: { + tip: 'Compact', + assetId: 'Option', + }, + payload: {}, + }, + }, + }); + + // account to submit tx + const keyring = new Keyring({ type: 'sr25519' }); + const alice = keyring.addFromUri('//Alice'); + + const setXcmVersion = rococoApi.tx.xcmPallet.forceDefaultXcmVersion([3]); + await submitExtrinsic(alice, rococoApi.tx.sudo.sudo(setXcmVersion), {}); + + await setupRelayAsset(regionXApi, alice, 10n ** 12n); + + const receiverKeypair = new Keyring(); + receiverKeypair.addFromAddress(alice.address); + + // Try to pay for fees with relay chain asset. + const remarkCall = regionXApi.tx.system.remark('0x44'); + await submitExtrinsic(alice, remarkCall, { assetId: RELAY_ASSET_ID }); } export { run }; diff --git a/e2e_tests/fee-payment/native.ts b/e2e_tests/fee-payment/native.ts index b4fef856..13a3d4e1 100644 --- a/e2e_tests/fee-payment/native.ts +++ b/e2e_tests/fee-payment/native.ts @@ -1,18 +1,18 @@ -import { ApiPromise, Keyring, WsProvider } from "@polkadot/api"; -import { submitExtrinsic } from "../common"; +import { ApiPromise, Keyring, WsProvider } from '@polkadot/api'; +import { submitExtrinsic } from '../common'; async function run(nodeName: string, networkInfo: any, _jsArgs: any) { - const { wsUri } = networkInfo.nodesByName[nodeName]; - const api = await ApiPromise.create({ provider: new WsProvider(wsUri) }); + const { wsUri } = networkInfo.nodesByName[nodeName]; + const api = await ApiPromise.create({ provider: new WsProvider(wsUri) }); - // account to submit tx - const keyring = new Keyring({ type: "sr25519" }); - const alice = keyring.addFromUri("//Alice"); - const bob = keyring.addFromUri("//Bob"); + // account to submit tx + const keyring = new Keyring({ type: 'sr25519' }); + const alice = keyring.addFromUri('//Alice'); + const bob = keyring.addFromUri('//Bob'); - const call = api.tx.balances.transferKeepAlive(bob.address, 10n ** 6n); - const sudo = api.tx.sudo.sudo(call); - await submitExtrinsic(alice, sudo, {}); + const call = api.tx.balances.transferKeepAlive(bob.address, 10n ** 6n); + const sudo = api.tx.sudo.sudo(call); + await submitExtrinsic(alice, sudo, {}); } export { run }; diff --git a/e2e_tests/governance/delegated.ts b/e2e_tests/governance/delegated.ts index 960428f6..14f0f2b9 100644 --- a/e2e_tests/governance/delegated.ts +++ b/e2e_tests/governance/delegated.ts @@ -1,52 +1,52 @@ -import { ApiPromise, WsProvider, Keyring } from "@polkadot/api"; -import { submitExtrinsic, setupRelayAsset, RELAY_ASSET_ID } from "../common"; +import { ApiPromise, Keyring, WsProvider } from '@polkadot/api'; +import { RELAY_ASSET_ID, setupRelayAsset, submitExtrinsic } from '../common'; -const PREIMAGE_HASH = "0xb8375f7ca0c64a384f2dd643a0d520977f3aae06e64afb8c960891eee5147bd1"; +const PREIMAGE_HASH = '0xb8375f7ca0c64a384f2dd643a0d520977f3aae06e64afb8c960891eee5147bd1'; async function run(nodeName: string, networkInfo: any, _jsArgs: any) { - const { wsUri } = networkInfo.nodesByName[nodeName]; - const api = await ApiPromise.create({ - provider: new WsProvider(wsUri), - signedExtensions: { - ChargeAssetTxPayment: { - extrinsic: { - tip: "Compact", - assetId: "Option", - }, - payload: {}, - }, - }, - }); - - // account to submit tx - const keyring = new Keyring({ type: "sr25519" }); - const alice = keyring.addFromUri("//Alice"); - const anna = keyring.addFromUri("//Anna"); - - await setupRelayAsset(api, alice); - - const giveBalanceCall = api.tx.tokens.setBalance(anna.address, RELAY_ASSET_ID, 10n ** 18n, 0); - await submitExtrinsic(alice, api.tx.sudo.sudo(giveBalanceCall), {}); - - const remarkCallBytes = api.tx.system.remark("hey").toU8a(); - await submitExtrinsic(alice, api.tx.preimage.notePreimage(remarkCallBytes), {}); - - const submitProposal = api.tx.delegatedReferenda.submit( - { system: "Root" }, - { Lookup: { hash: PREIMAGE_HASH, len: remarkCallBytes.length } }, - { After: 5 } - ); - await submitExtrinsic(anna, submitProposal, { assetId: RELAY_ASSET_ID }); - - const placeDeposit = api.tx.delegatedReferenda.placeDecisionDeposit(0); - await submitExtrinsic(anna, placeDeposit, { assetId: RELAY_ASSET_ID }); - - const voteCall = api.tx.delegatedConvictionVoting.vote(0, { - // Voting with relay chain tokens. We know this is true; otherwise, this call - // would fail, given that Anna doesn't have 10^16 RegionX tokens. - Standard: { vote: { aye: true, conviction: "None" }, balance: 10n ** 16n }, - }); - await submitExtrinsic(anna, voteCall, { assetId: RELAY_ASSET_ID }); + const { wsUri } = networkInfo.nodesByName[nodeName]; + const api = await ApiPromise.create({ + provider: new WsProvider(wsUri), + signedExtensions: { + ChargeAssetTxPayment: { + extrinsic: { + tip: 'Compact', + assetId: 'Option', + }, + payload: {}, + }, + }, + }); + + // account to submit tx + const keyring = new Keyring({ type: 'sr25519' }); + const alice = keyring.addFromUri('//Alice'); + const anna = keyring.addFromUri('//Anna'); + + await setupRelayAsset(api, alice); + + const giveBalanceCall = api.tx.tokens.setBalance(anna.address, RELAY_ASSET_ID, 10n ** 18n, 0); + await submitExtrinsic(alice, api.tx.sudo.sudo(giveBalanceCall), {}); + + const remarkCallBytes = api.tx.system.remark('hey').toU8a(); + await submitExtrinsic(alice, api.tx.preimage.notePreimage(remarkCallBytes), {}); + + const submitProposal = api.tx.delegatedReferenda.submit( + { system: 'Root' }, + { Lookup: { hash: PREIMAGE_HASH, len: remarkCallBytes.length } }, + { After: 5 } + ); + await submitExtrinsic(anna, submitProposal, { assetId: RELAY_ASSET_ID }); + + const placeDeposit = api.tx.delegatedReferenda.placeDecisionDeposit(0); + await submitExtrinsic(anna, placeDeposit, { assetId: RELAY_ASSET_ID }); + + const voteCall = api.tx.delegatedConvictionVoting.vote(0, { + // Voting with relay chain tokens. We know this is true; otherwise, this call + // would fail, given that Anna doesn't have 10^16 RegionX tokens. + Standard: { vote: { aye: true, conviction: 'None' }, balance: 10n ** 16n }, + }); + await submitExtrinsic(anna, voteCall, { assetId: RELAY_ASSET_ID }); } export { run }; diff --git a/e2e_tests/governance/native.ts b/e2e_tests/governance/native.ts index 3fb59b93..2e0a7053 100644 --- a/e2e_tests/governance/native.ts +++ b/e2e_tests/governance/native.ts @@ -1,36 +1,36 @@ -import { ApiPromise, WsProvider, Keyring } from "@polkadot/api"; -import { submitExtrinsic, setupRelayAsset } from "../common"; +import { ApiPromise, Keyring, WsProvider } from '@polkadot/api'; +import { setupRelayAsset, submitExtrinsic } from '../common'; -const PREIMAGE_HASH = "0x0ccf4369e9a9f88f035828ba0dd5da645d5c0fa7baa86bdc8d7a80c183ab84c9"; +const PREIMAGE_HASH = '0x0ccf4369e9a9f88f035828ba0dd5da645d5c0fa7baa86bdc8d7a80c183ab84c9'; async function run(nodeName: string, networkInfo: any, _jsArgs: any) { - const { wsUri } = networkInfo.nodesByName[nodeName]; - const api = await ApiPromise.create({ provider: new WsProvider(wsUri) }); + const { wsUri } = networkInfo.nodesByName[nodeName]; + const api = await ApiPromise.create({ provider: new WsProvider(wsUri) }); - // account to submit tx - const keyring = new Keyring({ type: "sr25519" }); - const alice = keyring.addFromUri("//Alice"); + // account to submit tx + const keyring = new Keyring({ type: 'sr25519' }); + const alice = keyring.addFromUri('//Alice'); - // relay asset is needed for storing the preimage. - await setupRelayAsset(api, alice); + // relay asset is needed for storing the preimage. + await setupRelayAsset(api, alice); - const spendCallBytes = api.tx.treasury.spendLocal(10n ** 6n, alice.address).toU8a(); - await submitExtrinsic(alice, api.tx.preimage.notePreimage(spendCallBytes), {}); + const spendCallBytes = api.tx.treasury.spendLocal(10n ** 6n, alice.address).toU8a(); + await submitExtrinsic(alice, api.tx.preimage.notePreimage(spendCallBytes), {}); - const submitProposal = api.tx.nativeReferenda.submit( - { Origins: "SmallTipper" }, - { Lookup: { hash: PREIMAGE_HASH, len: spendCallBytes.length } }, - { After: 5 } - ); - await submitExtrinsic(alice, submitProposal, {}); + const submitProposal = api.tx.nativeReferenda.submit( + { Origins: 'SmallTipper' }, + { Lookup: { hash: PREIMAGE_HASH, len: spendCallBytes.length } }, + { After: 5 } + ); + await submitExtrinsic(alice, submitProposal, {}); - const placeDeposit = api.tx.nativeReferenda.placeDecisionDeposit(0); - await submitExtrinsic(alice, placeDeposit, {}); + const placeDeposit = api.tx.nativeReferenda.placeDecisionDeposit(0); + await submitExtrinsic(alice, placeDeposit, {}); - const voteCall = api.tx.nativeConvictionVoting.vote(0, { - Standard: { vote: { aye: true, conviction: "None" }, balance: 10n ** 16n }, - }); - await submitExtrinsic(alice, voteCall, {}); + const voteCall = api.tx.nativeConvictionVoting.vote(0, { + Standard: { vote: { aye: true, conviction: 'None' }, balance: 10n ** 16n }, + }); + await submitExtrinsic(alice, voteCall, {}); } export { run }; diff --git a/e2e_tests/package.json b/e2e_tests/package.json deleted file mode 100644 index d8878058..00000000 --- a/e2e_tests/package.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "name": "e2e-tests", - "version": "1.0.0", - "description": "", - "main": "index.js", - "scripts": { - "build": "npx tsc --outDir build > /dev/null" - }, - "keywords": [], - "author": "", - "license": "ISC", - "dependencies": { - "@polkadot/api": "^11.0.2", - "@polkadot/keyring": "^12.6.2", - "coretime-utils": "^0.3.2", - "typescript": "^5.4.5" - } -} diff --git a/e2e_tests/tsconfig.json b/e2e_tests/tsconfig.json deleted file mode 100644 index c9901f8a..00000000 --- a/e2e_tests/tsconfig.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "compilerOptions": { - "module": "commonjs", - "outDir": "build", - "target": "ES2020", - "strict": true, - "esModuleInterop": true - }, - "include": [ - "**/*.ts" - ], - "exclude": [ - "node_modules" - ] -} diff --git a/e2e_tests/xc-transfer/asset-transfer.ts b/e2e_tests/xc-transfer/asset-transfer.ts index 097e0098..7cf438c3 100644 --- a/e2e_tests/xc-transfer/asset-transfer.ts +++ b/e2e_tests/xc-transfer/asset-transfer.ts @@ -1,93 +1,99 @@ -import { ApiPromise, WsProvider, Keyring } from "@polkadot/api"; -import { RELAY_ASSET_ID, setupRelayAsset, sleep, submitExtrinsic, transferRelayAssetToPara } from "../common"; +import { ApiPromise, Keyring, WsProvider } from '@polkadot/api'; +import { + RELAY_ASSET_ID, + setupRelayAsset, + sleep, + submitExtrinsic, + transferRelayAssetToPara, +} from '../common'; -import assert from "node:assert"; +import assert from 'node:assert'; -const TOLERANCE = 10n**10n; +const TOLERANCE = 10n ** 10n; async function run(nodeName: string, networkInfo: any, _jsArgs: any) { - const { wsUri: regionXUri } = networkInfo.nodesByName[nodeName]; - const { wsUri: rococoUri } = networkInfo.nodesByName["rococo-validator01"]; - - const rococoApi = await ApiPromise.create({ provider: new WsProvider(rococoUri) }); - const regionXApi = await ApiPromise.create({ provider: new WsProvider(regionXUri) }); - - // account to submit tx - const keyring = new Keyring({ type: "sr25519" }); - const alice = keyring.addFromUri("//Alice"); - - const setXcmVersion = rococoApi.tx.xcmPallet.forceDefaultXcmVersion([3]); - await submitExtrinsic(alice, rococoApi.tx.sudo.sudo(setXcmVersion), {}); - - await setupRelayAsset(regionXApi, alice); - - const receiverKeypair = new Keyring(); - receiverKeypair.addFromAddress(alice.address); - - const assertRegionXBalance = async (address: string, balance: bigint) => { - const { free } = ( - await regionXApi.query.tokens.accounts(address, RELAY_ASSET_ID) - ).toJSON() as any; - - console.log(`RegionX: ${BigInt(free).toString()} | Expected: ${balance}`); - assert(balance - BigInt(free) < TOLERANCE); - }; - - const assertRococoBalance = async (address: string, balance: bigint) => { - const { - data: { free }, - } = (await rococoApi.query.system.account(address)).toJSON() as any; - - console.log(`Rococo: ${BigInt(free).toString()} | Expected: ${balance}`); - assert(balance - BigInt(free) < TOLERANCE); - }; - - await assertRegionXBalance(alice.address, 10n ** 12n); - await assertRococoBalance(alice.address, 10n ** 18n); - - await transferRelayAssetToPara(3n * 10n ** 12n, 2000, rococoApi, alice); - await sleep(5 * 1000); - - await assertRegionXBalance(alice.address, 4n * 10n ** 12n); - await assertRococoBalance(alice.address, 10n ** 18n - 3n * 10n ** 12n); - - const regionXReserveTransfer = regionXApi.tx.polkadotXcm.limitedReserveTransferAssets( - { V3: { parents: 1, interior: "Here" } }, //dest - { - V3: { - parents: 0, - interior: { - X1: { - AccountId32: { - chain: "Any", - id: receiverKeypair.pairs[0].publicKey, - }, - }, - }, - }, - }, //beneficiary - { - V3: [ - { - id: { - Concrete: { parents: 1, interior: "Here" }, - }, - fun: { - Fungible: 10n ** 12n, - }, - }, - ], - }, //asset - 0, - "Unlimited" - ); - - await submitExtrinsic(alice, regionXReserveTransfer, {}); - - await sleep(5 * 1000); - - await assertRegionXBalance(alice.address, 3n * 10n ** 12n); - await assertRococoBalance(alice.address, 10n ** 18n - 3n * 10n ** 12n); + const { wsUri: regionXUri } = networkInfo.nodesByName[nodeName]; + const { wsUri: rococoUri } = networkInfo.nodesByName['rococo-validator01']; + + const rococoApi = await ApiPromise.create({ provider: new WsProvider(rococoUri) }); + const regionXApi = await ApiPromise.create({ provider: new WsProvider(regionXUri) }); + + // account to submit tx + const keyring = new Keyring({ type: 'sr25519' }); + const alice = keyring.addFromUri('//Alice'); + + const setXcmVersion = rococoApi.tx.xcmPallet.forceDefaultXcmVersion([3]); + await submitExtrinsic(alice, rococoApi.tx.sudo.sudo(setXcmVersion), {}); + + await setupRelayAsset(regionXApi, alice); + + const receiverKeypair = new Keyring(); + receiverKeypair.addFromAddress(alice.address); + + const assertRegionXBalance = async (address: string, balance: bigint) => { + const { free } = ( + await regionXApi.query.tokens.accounts(address, RELAY_ASSET_ID) + ).toJSON() as any; + + console.log(`RegionX: ${BigInt(free).toString()} | Expected: ${balance}`); + assert(balance - BigInt(free) < TOLERANCE); + }; + + const assertRococoBalance = async (address: string, balance: bigint) => { + const { + data: { free }, + } = (await rococoApi.query.system.account(address)).toJSON() as any; + + console.log(`Rococo: ${BigInt(free).toString()} | Expected: ${balance}`); + assert(balance - BigInt(free) < TOLERANCE); + }; + + await assertRegionXBalance(alice.address, 10n ** 12n); + await assertRococoBalance(alice.address, 10n ** 18n); + + await transferRelayAssetToPara(3n * 10n ** 12n, 2000, rococoApi, alice); + await sleep(5 * 1000); + + await assertRegionXBalance(alice.address, 4n * 10n ** 12n); + await assertRococoBalance(alice.address, 10n ** 18n - 3n * 10n ** 12n); + + const regionXReserveTransfer = regionXApi.tx.polkadotXcm.limitedReserveTransferAssets( + { V3: { parents: 1, interior: 'Here' } }, //dest + { + V3: { + parents: 0, + interior: { + X1: { + AccountId32: { + chain: 'Any', + id: receiverKeypair.pairs[0].publicKey, + }, + }, + }, + }, + }, //beneficiary + { + V3: [ + { + id: { + Concrete: { parents: 1, interior: 'Here' }, + }, + fun: { + Fungible: 10n ** 12n, + }, + }, + ], + }, //asset + 0, + 'Unlimited' + ); + + await submitExtrinsic(alice, regionXReserveTransfer, {}); + + await sleep(5 * 1000); + + await assertRegionXBalance(alice.address, 3n * 10n ** 12n); + await assertRococoBalance(alice.address, 10n ** 18n - 3n * 10n ** 12n); } export { run }; diff --git a/e2e_tests/xc-transfer/region-transfer.ts b/e2e_tests/xc-transfer/region-transfer.ts index 9d635984..691b7d73 100644 --- a/e2e_tests/xc-transfer/region-transfer.ts +++ b/e2e_tests/xc-transfer/region-transfer.ts @@ -1,237 +1,237 @@ -import { ApiPromise, WsProvider, Keyring } from "@polkadot/api"; -import { CONFIG, INITIAL_PRICE, UNIT, CORE_COUNT } from "../consts"; -import { submitExtrinsic, sleep, setupRelayAsset, transferRelayAssetToPara } from "../common"; -import { KeyringPair } from "@polkadot/keyring/types"; -import { getEncodedRegionId, Id, RegionId } from "coretime-utils"; -import assert from "node:assert"; +import { ApiPromise, Keyring, WsProvider } from '@polkadot/api'; +import { KeyringPair } from '@polkadot/keyring/types'; +import { getEncodedRegionId, Id, RegionId } from 'coretime-utils'; +import assert from 'node:assert'; +import { setupRelayAsset, sleep, submitExtrinsic, transferRelayAssetToPara } from '../common'; +import { CONFIG, CORE_COUNT, INITIAL_PRICE, UNIT } from '../consts'; -const REGIONX_SOVEREIGN_ACCOUNT = "5Eg2fntJ27qsari4FGrGhrMqKFDRnkNSR6UshkZYBGXmSuC8"; +const REGIONX_SOVEREIGN_ACCOUNT = '5Eg2fntJ27qsari4FGrGhrMqKFDRnkNSR6UshkZYBGXmSuC8'; async function run(_nodeName: any, networkInfo: any, _jsArgs: any) { - const { wsUri: regionXUri } = networkInfo.nodesByName["regionx-collator01"]; - const { wsUri: coretimeUri } = networkInfo.nodesByName["coretime-collator01"]; - const { wsUri: rococoUri } = networkInfo.nodesByName["rococo-validator01"]; - - const regionXApi = await ApiPromise.create({ provider: new WsProvider(regionXUri) }); - const rococoApi = await ApiPromise.create({ - provider: new WsProvider(rococoUri), - types: { Id }, - }); - const coretimeApi = await ApiPromise.create({ - provider: new WsProvider(coretimeUri), - types: { Id }, - }); - - // account to submit tx - const keyring = new Keyring({ type: "sr25519" }); - const alice = keyring.addFromUri("//Alice"); - - const txSetCoretimeXcmVersion = coretimeApi.tx.polkadotXcm.forceDefaultXcmVersion([3]); - const txSetRelayXcmVersion = rococoApi.tx.xcmPallet.forceDefaultXcmVersion([3]); - await submitExtrinsic(alice, coretimeApi.tx.sudo.sudo(txSetCoretimeXcmVersion), {}); - await submitExtrinsic(alice, rococoApi.tx.sudo.sudo(txSetRelayXcmVersion), {}); - - await setupRelayAsset(regionXApi, alice); - - await openHrmpChannel(alice, rococoApi, 1005, 2000); - await openHrmpChannel(alice, rococoApi, 2000, 1005); - - // Needed for fee payment - // The Coretime chain account by default has tokens for fee payment. - await transferRelayAssetToPara(10n ** 12n, 2000, rococoApi, alice); - - await configureBroker(coretimeApi, alice); - await startSales(coretimeApi, alice); - - const txSetBalance = coretimeApi.tx.balances.forceSetBalance(alice.address, 1000 * UNIT); - await submitExtrinsic(alice, coretimeApi.tx.sudo.sudo(txSetBalance), {}); - - const regionId = await purchaseRegion(coretimeApi, alice); - if (!regionId) throw new Error("RegionId not found"); - - const receiverKeypair = new Keyring(); - receiverKeypair.addFromAddress(alice.address); - - const feeAssetItem = 0; - const weightLimit = "Unlimited"; - const reserveTransferToRegionX = coretimeApi.tx.polkadotXcm.limitedReserveTransferAssets( - { V3: { parents: 1, interior: { X1: { Parachain: 2000 } } } }, //dest - { - V3: { - parents: 0, - interior: { - X1: { - AccountId32: { - chain: "Any", - id: receiverKeypair.pairs[0].publicKey, - }, - }, - }, - }, - }, //beneficiary - { - V3: [ - { - id: { - Concrete: { - parents: 1, - interior: "Here", - }, - }, - fun: { - Fungible: 10n ** 10n, - }, - }, // ^^ fee payment asset - { - id: { - Concrete: { - parents: 0, - interior: { X1: { PalletInstance: 50 } }, - }, - }, - fun: { - NonFungible: { - Index: getEncodedRegionId(regionId, coretimeApi).toString(), - }, - }, - }, - ], - }, //asset - feeAssetItem, - weightLimit - ); - await submitExtrinsic(alice, reserveTransferToRegionX, {}); - - await sleep(5000); - - var regions = await regionXApi.query.regions.regions.entries(); - assert.equal(regions.length, 1); - assert.deepStrictEqual(regions[0][0].toHuman(), [regionId]); - assert.deepStrictEqual(regions[0][1].toHuman(), { owner: alice.address, record: "Pending" }); - - var regions = await coretimeApi.query.broker.regions.entries(); - assert.equal(regions.length, 1); - assert.deepStrictEqual(regions[0][0].toHuman(), [regionId]); - assert.equal((regions[0][1].toHuman() as any).owner, REGIONX_SOVEREIGN_ACCOUNT); - - const reserveTransferToCoretime = regionXApi.tx.polkadotXcm.limitedReserveTransferAssets( - { V3: { parents: 1, interior: { X1: { Parachain: 1005 } } } }, // dest - { - V3: { - parents: 0, - interior: { - X1: { - AccountId32: { - chain: "Any", - id: receiverKeypair.pairs[0].publicKey, - }, - }, - }, - }, - }, // ^^ beneficiary - { - V3: [ - { - id: { - Concrete: { - parents: 1, - interior: "Here", - }, - }, - fun: { - Fungible: 10n ** 10n, - }, - }, // ^^ fee payment asset - { - id: { - Concrete: { - parents: 1, - // chain: Rococo-Coretime, pallet: pallet_broker - interior: { X2: [{ Parachain: 1005 }, { PalletInstance: 50 }] }, - }, - }, - fun: { - NonFungible: { - Index: getEncodedRegionId(regionId, regionXApi).toString(), - }, - }, - }, - ], - }, // ^^ asset - feeAssetItem, - weightLimit - ); - await submitExtrinsic(alice, reserveTransferToCoretime, {}); - await sleep(5000); - - var regions = await regionXApi.query.regions.regions.entries(); - assert.equal(regions.length, 0); - - var regions = await coretimeApi.query.broker.regions.entries(); - assert.equal(regions.length, 1); - assert.deepStrictEqual(regions[0][0].toHuman(), [regionId]); - assert.equal((regions[0][1].toHuman() as any).owner, alice.address); + const { wsUri: regionXUri } = networkInfo.nodesByName['regionx-collator01']; + const { wsUri: coretimeUri } = networkInfo.nodesByName['coretime-collator01']; + const { wsUri: rococoUri } = networkInfo.nodesByName['rococo-validator01']; + + const regionXApi = await ApiPromise.create({ provider: new WsProvider(regionXUri) }); + const rococoApi = await ApiPromise.create({ + provider: new WsProvider(rococoUri), + types: { Id }, + }); + const coretimeApi = await ApiPromise.create({ + provider: new WsProvider(coretimeUri), + types: { Id }, + }); + + // account to submit tx + const keyring = new Keyring({ type: 'sr25519' }); + const alice = keyring.addFromUri('//Alice'); + + const txSetCoretimeXcmVersion = coretimeApi.tx.polkadotXcm.forceDefaultXcmVersion([3]); + const txSetRelayXcmVersion = rococoApi.tx.xcmPallet.forceDefaultXcmVersion([3]); + await submitExtrinsic(alice, coretimeApi.tx.sudo.sudo(txSetCoretimeXcmVersion), {}); + await submitExtrinsic(alice, rococoApi.tx.sudo.sudo(txSetRelayXcmVersion), {}); + + await setupRelayAsset(regionXApi, alice); + + await openHrmpChannel(alice, rococoApi, 1005, 2000); + await openHrmpChannel(alice, rococoApi, 2000, 1005); + + // Needed for fee payment + // The Coretime chain account by default has tokens for fee payment. + await transferRelayAssetToPara(10n ** 12n, 2000, rococoApi, alice); + + await configureBroker(coretimeApi, alice); + await startSales(coretimeApi, alice); + + const txSetBalance = coretimeApi.tx.balances.forceSetBalance(alice.address, 1000 * UNIT); + await submitExtrinsic(alice, coretimeApi.tx.sudo.sudo(txSetBalance), {}); + + const regionId = await purchaseRegion(coretimeApi, alice); + if (!regionId) throw new Error('RegionId not found'); + + const receiverKeypair = new Keyring(); + receiverKeypair.addFromAddress(alice.address); + + const feeAssetItem = 0; + const weightLimit = 'Unlimited'; + const reserveTransferToRegionX = coretimeApi.tx.polkadotXcm.limitedReserveTransferAssets( + { V3: { parents: 1, interior: { X1: { Parachain: 2000 } } } }, //dest + { + V3: { + parents: 0, + interior: { + X1: { + AccountId32: { + chain: 'Any', + id: receiverKeypair.pairs[0].publicKey, + }, + }, + }, + }, + }, //beneficiary + { + V3: [ + { + id: { + Concrete: { + parents: 1, + interior: 'Here', + }, + }, + fun: { + Fungible: 10n ** 10n, + }, + }, // ^^ fee payment asset + { + id: { + Concrete: { + parents: 0, + interior: { X1: { PalletInstance: 50 } }, + }, + }, + fun: { + NonFungible: { + Index: getEncodedRegionId(regionId, coretimeApi).toString(), + }, + }, + }, + ], + }, //asset + feeAssetItem, + weightLimit + ); + await submitExtrinsic(alice, reserveTransferToRegionX, {}); + + await sleep(5000); + + let regions = await regionXApi.query.regions.regions.entries(); + assert.equal(regions.length, 1); + assert.deepStrictEqual(regions[0][0].toHuman(), [regionId]); + assert.deepStrictEqual(regions[0][1].toHuman(), { owner: alice.address, record: 'Pending' }); + + regions = await coretimeApi.query.broker.regions.entries(); + assert.equal(regions.length, 1); + assert.deepStrictEqual(regions[0][0].toHuman(), [regionId]); + assert.equal((regions[0][1].toHuman() as any).owner, REGIONX_SOVEREIGN_ACCOUNT); + + const reserveTransferToCoretime = regionXApi.tx.polkadotXcm.limitedReserveTransferAssets( + { V3: { parents: 1, interior: { X1: { Parachain: 1005 } } } }, // dest + { + V3: { + parents: 0, + interior: { + X1: { + AccountId32: { + chain: 'Any', + id: receiverKeypair.pairs[0].publicKey, + }, + }, + }, + }, + }, // ^^ beneficiary + { + V3: [ + { + id: { + Concrete: { + parents: 1, + interior: 'Here', + }, + }, + fun: { + Fungible: 10n ** 10n, + }, + }, // ^^ fee payment asset + { + id: { + Concrete: { + parents: 1, + // chain: Rococo-Coretime, pallet: pallet_broker + interior: { X2: [{ Parachain: 1005 }, { PalletInstance: 50 }] }, + }, + }, + fun: { + NonFungible: { + Index: getEncodedRegionId(regionId, regionXApi).toString(), + }, + }, + }, + ], + }, // ^^ asset + feeAssetItem, + weightLimit + ); + await submitExtrinsic(alice, reserveTransferToCoretime, {}); + await sleep(5000); + + regions = await regionXApi.query.regions.regions.entries(); + assert.equal(regions.length, 0); + + regions = await coretimeApi.query.broker.regions.entries(); + assert.equal(regions.length, 1); + assert.deepStrictEqual(regions[0][0].toHuman(), [regionId]); + assert.equal((regions[0][1].toHuman() as any).owner, alice.address); } async function openHrmpChannel( - signer: KeyringPair, - relayApi: ApiPromise, - senderParaId: number, - recipientParaId: number + signer: KeyringPair, + relayApi: ApiPromise, + senderParaId: number, + recipientParaId: number ) { - const openHrmp = relayApi.tx.parasSudoWrapper.sudoEstablishHrmpChannel( - senderParaId, // sender - recipientParaId, // recipient - 8, // Max capacity - 102400 // Max message size - ); - const sudoCall = relayApi.tx.sudo.sudo(openHrmp); - - return submitExtrinsic(signer, sudoCall, {}); + const openHrmp = relayApi.tx.parasSudoWrapper.sudoEstablishHrmpChannel( + senderParaId, // sender + recipientParaId, // recipient + 8, // Max capacity + 102400 // Max message size + ); + const sudoCall = relayApi.tx.sudo.sudo(openHrmp); + + return submitExtrinsic(signer, sudoCall, {}); } async function configureBroker(coretimeApi: ApiPromise, signer: KeyringPair): Promise { - const configCall = coretimeApi.tx.broker.configure(CONFIG); - const sudo = coretimeApi.tx.sudo.sudo(configCall); - return submitExtrinsic(signer, sudo, {}); + const configCall = coretimeApi.tx.broker.configure(CONFIG); + const sudo = coretimeApi.tx.sudo.sudo(configCall); + return submitExtrinsic(signer, sudo, {}); } async function startSales(coretimeApi: ApiPromise, signer: KeyringPair): Promise { - const startSaleCall = coretimeApi.tx.broker.startSales(INITIAL_PRICE, CORE_COUNT); - const sudo = coretimeApi.tx.sudo.sudo(startSaleCall); - return submitExtrinsic(signer, sudo, {}); + const startSaleCall = coretimeApi.tx.broker.startSales(INITIAL_PRICE, CORE_COUNT); + const sudo = coretimeApi.tx.sudo.sudo(startSaleCall); + return submitExtrinsic(signer, sudo, {}); } async function purchaseRegion( - coretimeApi: ApiPromise, - buyer: KeyringPair + coretimeApi: ApiPromise, + buyer: KeyringPair ): Promise { - const callTx = async (resolve: (regionId: RegionId | null) => void) => { - const purchase = coretimeApi.tx.broker.purchase(INITIAL_PRICE * 2); - const unsub = await purchase.signAndSend(buyer, async (result: any) => { - if (result.status.isInBlock) { - const regionId = await getRegionId(coretimeApi); - unsub(); - resolve(regionId); - } - }); - }; - - return new Promise(callTx); + const callTx = async (resolve: (regionId: RegionId | null) => void) => { + const purchase = coretimeApi.tx.broker.purchase(INITIAL_PRICE * 2); + const unsub = await purchase.signAndSend(buyer, async (result: any) => { + if (result.status.isInBlock) { + const regionId = await getRegionId(coretimeApi); + unsub(); + resolve(regionId); + } + }); + }; + + return new Promise(callTx); } async function getRegionId(coretimeApi: ApiPromise): Promise { - const events: any = await coretimeApi.query.system.events(); + const events: any = await coretimeApi.query.system.events(); - for (const record of events) { - const { event } = record; - if (event.section === "broker" && event.method === "Purchased") { - const data = event.data[1].toHuman(); - return data; - } - } + for (const record of events) { + const { event } = record; + if (event.section === 'broker' && event.method === 'Purchased') { + const data = event.data[1].toHuman(); + return data; + } + } - return null; + return null; } export { run }; diff --git a/package.json b/package.json new file mode 100644 index 00000000..8e89e0a5 --- /dev/null +++ b/package.json @@ -0,0 +1,33 @@ +{ + "name": "regionx-e2e-tests", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "build": "npx tsc > /dev/null", + "lint": "eslint e2e_tests --fix", + "format": "npx prettier --write './e2e_tests/**/*.ts'" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "@polkadot/api": "^11.0.2", + "@polkadot/keyring": "^12.6.2", + "coretime-utils": "^0.3.2", + "typescript": "^4.1.6" + }, + "devDependencies": { + "eslint": "8.31.0", + "eslint-config-airbnb": "^19.0.4", + "eslint-config-airbnb-typescript": "^17.0.0", + "eslint-config-prettier": "^8.6.0", + "eslint-import-resolver-typescript": "^3.5.3", + "eslint-plugin-import": "^2.27.4", + "eslint-plugin-prettier": "^4.0.0", + "prettier": "^2.8.1", + "prettier-plugin-organize-imports": "^3.2.0", + "@typescript-eslint/eslint-plugin": "^5.48.1", + "@typescript-eslint/parser": "^5.48.1" + } +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000..eafcd671 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "module": "commonjs", + "outDir": "e2e_tests/build", + "target": "ES2020", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true + }, + "include": ["e2e_tests/**/*.ts"], + "exclude": ["node_modules"] +}