diff --git a/.github/workflows/verify-compilation.yml b/.github/workflows/verify-compilation.yml
index 1c33242..221e495 100644
--- a/.github/workflows/verify-compilation.yml
+++ b/.github/workflows/verify-compilation.yml
@@ -13,10 +13,16 @@ jobs:
- name: Set up Node.js
uses: actions/setup-node@v2
with:
- node-version: "14"
+ node-version: "20"
- name: Install dependencies
run: npm install
+ - name: Setup bun
+ uses: oven-sh/setup-bun@v2
+
- name: Verify TypeScript compilation
- run: npm run build
+ run: bun run build
+
+ - name: Run tests
+ run: bun run test
diff --git a/README.md b/README.md
index e4c2512..6ba0deb 100644
--- a/README.md
+++ b/README.md
@@ -1,25 +1,29 @@
# moonbeam-tools
-Tools related to Moonbeam blockchains
+Tools related to Moonbeam blockchains.
-# Requirements
+## Requirements
-* NodeJS v14+
+* bun v1+
-# Tools
+## Actions
+__Installation__: `npm install`
+__Test__: `bun run test`
+__Build__: `bun run build`
+__Publish__: `bun publish`
-## Installation
+## Debug
-```
-sudo npm install -g moonbeam-tools@latest
-```
+You can use `DEBUG=helper:*` for logs on the state manipation
+
+# Tools
-## Running moonbeam-monitor
+## Monitoring chains
Allows to monitor a Moonbeam network. To do so, run the following command:
```
-moonbeam-monitor --networks moonriver
+bunx moonbeam-tools --networks moonbeam moonriver
```
```
@@ -40,7 +44,7 @@ Options:
Monitoring
-
+
+
+
+
+
+
+`,
+);
+// editorconfig-checker-enable
+
+const openCmd = (() => {
+ switch (process.platform) {
+ case "darwin":
+ return "open";
+ case "win32":
+ return "start";
+ default:
+ return "xdg-open";
+ }
+})();
+
+exec(`${openCmd} ${outputFile}`);
diff --git a/src/tools/fees.ts b/src/tools/fees.ts
new file mode 100644
index 0000000..b2daeb6
--- /dev/null
+++ b/src/tools/fees.ts
@@ -0,0 +1,1120 @@
+import { exec as execProcess } from "child_process";
+import fs from "fs";
+import assert from "node:assert/strict";
+import util from "node:util";
+import { setTimeout } from "timers/promises";
+
+import "@moonbeam-network/api-augment";
+import { ApiPromise, WsProvider } from "@polkadot/api";
+import "@polkadot/api-augment";
+import { SubmittableExtrinsic as SubmittableExtrinsicPromise } from "@polkadot/api/promise/types";
+import { ApiTypes, SubmittableExtrinsic } from "@polkadot/api/types";
+import Keyring from "@polkadot/keyring";
+import { DispatchError, EventRecord } from "@polkadot/types/interfaces";
+import { EvmCoreErrorExitReason } from "@polkadot/types/lookup";
+import { BN, u8aToHex } from "@polkadot/util";
+import { ethers } from "ethers";
+import { AccessListish } from "ethers/lib/utils.js";
+import * as RLP from "rlp";
+import solc from "solc";
+import { JsonRpcResponseWithResult, Web3 } from "web3";
+import { Contract } from "web3-eth-contract";
+import yargs from "yargs";
+import {
+ ALITH_PRIVATE_KEY,
+ BALTATHAR_ADDRESS,
+ BALTATHAR_PRIVATE_KEY,
+ CHARLETH_ADDRESS,
+ CHARLETH_PRIVATE_KEY,
+ DOROTHY_PRIVATE_KEY,
+} from "../utils/constants.ts";
+
+const httpUrl = "http://127.0.0.1:9933";
+const wssUrl = "ws://127.0.0.1:9944";
+
+const exec = util.promisify(execProcess);
+const ethersApi = new ethers.providers.JsonRpcProvider(httpUrl);
+const keyringEth = new Keyring({ type: "ethereum" });
+export const alith = keyringEth.addFromUri(ALITH_PRIVATE_KEY);
+export const baltathar = keyringEth.addFromUri(BALTATHAR_PRIVATE_KEY);
+export const charleth = keyringEth.addFromUri(CHARLETH_PRIVATE_KEY);
+export const dorothy = keyringEth.addFromUri(DOROTHY_PRIVATE_KEY);
+const web3 = new Web3(wssUrl);
+web3.eth.accounts.wallet.add(ALITH_PRIVATE_KEY);
+
+/**
+ * This test assumes the following:
+ * + moonbeam
+ * - EVM calls are unfiltered.
+ * NormalFilter - Call::EVM(_) => true
+ * - EVM origin is allowed for all.
+ * type CallOrigin = EnsureAddressAlways;
+ *
+ * pub struct EnsureAddressAlways;
+ * impl EnsureAddressOrigin for EnsureAddressAlways {
+ * type Success = ();
+ *
+ * fn try_address_origin(
+ * _address: &H160,
+ * _origin: OuterOrigin,
+ * ) -> Result {
+ * Ok(())
+ * }
+ *
+ * fn ensure_address_origin(
+ * _address: &H160,
+ * _origin: OuterOrigin,
+ * ) -> Result {
+ * Ok(())
+ * }
+ * }
+ * + frontier
+ * - Baltathar pays no EVM fees and full substrate fees, while Charleth pays the opposite.
+ * let baltathar_addr = H160::from_str("0x3cd0a705a2dc65e5b1e1205896baa2be8a07c6e0").unwrap();
+ * let (validate, payable) = if source == baltathar_addr {
+ * (false, Pays::Yes)
+ * } else {
+ * (true, Pays::No)
+ * };
+ *
+ * Then start the node with the following command
+ * ./target/release/moonbeam \
+ * --execution=Native \
+ * --wasm-execution=interpreted-i-know-what-i-do \
+ * --ethapi=txpool \
+ * --no-hardware-benchmarks \
+ * --no-telemetry \
+ * --no-prometheus \
+ * --force-authoring \
+ * --rpc-cors=all \
+ * --alice \
+ * --chain=moonbase-dev \
+ * --sealing=manual \
+ * --in-peers=0 \
+ * --out-peers=0 -linfo \
+ * --tmp
+ *
+ * Examples:
+ * ts-node ./src/tools/fees.ts --name fees --type compute
+ * ts-node ./src/tools/fees.ts --name fees --type compute --multiplier 300000000
+ * ts-node ./src/tools/fees.ts --name fees --type length-small
+ * ts-node ./src/tools/fees.ts --name fees --type length-big
+ *
+ * The result will open in the browser once done
+ */
+
+/**
+ * Observations
+ * - The first EVM call causes the SmartContract storage to be initialized and costs around 20,000 gas (we avoid this by pre-initializing the storage)
+ * - The fees sometime jump abruptly and stay at that level, this is due to nonce going from 1 byte to 2 bytes and so on
+ * - The block fill ratio is computed differently by transaction-payment; the multiplier is updated only due to `Normal` weight class (actual / max).
+ * The actual weight also contains some extra weight that doesn't belong to extrinsics (maybe coming from on_initialize/on_finalize)
+ */
+
+/// === test methods === ///
+
+const TESTER_CONTRACT = `// SPDX-License-Identifier: GPL-3.0-only
+ pragma solidity >=0.8.3;
+
+ contract Tester {
+
+ uint256 public count;
+
+ function infinite() public pure {
+ while (true) {}
+ }
+
+ function incrementalLoop(uint256 n) public {
+ uint256 i = 0;
+ while (i < n) {
+ count = count + 1;
+ i += 1;
+ }
+ }
+
+ function bigData(bytes memory b) public {
+ // do nothing
+ }
+ }`;
+const TESTER_JSON = compileSolidity(TESTER_CONTRACT);
+const TESTER_INTERFACE = new ethers.utils.Interface(TESTER_JSON.contract.abi);
+
+async function runTest(
+ api: ApiPromise,
+ options: { callType: "compute" | "length-small" | "length-big"; multiplier: BN | null },
+) {
+ const result = [];
+ console.log(`options: ${JSON.stringify(options)}`);
+ let contractAddr = "0xc01Ee7f10EA4aF4673cFff62710E1D7792aBa8f3";
+
+ // deploy contract
+ const maxBlockWeight = api.consts.system.blockWeights.maxBlock.refTime.toBn();
+ const blockNumber = (await api.rpc.chain.getBlock()).block.header.number.toNumber();
+ const nextFeeMultiplierOriginal = await api.query.transactionPayment.nextFeeMultiplier();
+ if (blockNumber === 0) {
+ const { contract, rawTx } = await createContract(TESTER_JSON, {
+ ...ALITH_TRANSACTION_TEMPLATE,
+ gas: 900_000,
+ gasPrice: 1_250_000_000,
+ });
+
+ const results = await createBlock(api, rawTx);
+ assert.equal(results, true, "failure during block creation");
+ await expectEVMSuccess(api);
+ console.log(`contractAddress: ${contract.options.address.toString()}`);
+ contractAddr = contract.options.address;
+ }
+
+ // use the specified call type
+ const contractCall = (() => {
+ switch (options.callType) {
+ case "compute":
+ return TESTER_INTERFACE.encodeFunctionData("incrementalLoop", [10]);
+ case "length-small":
+ return TESTER_INTERFACE.encodeFunctionData("bigData", [new Array(100).fill(0x01)]);
+ case "length-big":
+ return TESTER_INTERFACE.encodeFunctionData("bigData", [new Array(50 * 1024).fill(0x01)]);
+ default:
+ throw new Error(`invalid options.callType ${options.callType}`);
+ }
+ })();
+
+ // init the smart contract storage, if not done then the first tx has a storage initialization cost of around 20,000
+ await createBlock(api, [
+ await api.tx.evm
+ .call(
+ dorothy.address,
+ contractAddr,
+ contractCall,
+ 0,
+ 900_000n,
+ 2_000_000_000n,
+ null,
+ null,
+ [],
+ )
+ .signAsync(dorothy),
+ ]);
+
+ // override nextFeeMultiplier if needed, note that its value will change immediately after block creation
+ const nextFeeMultiplierOverride = options.multiplier || nextFeeMultiplierOriginal;
+ if (nextFeeMultiplierOverride) {
+ console.log(`overriding nextFeeMultiplier to ${nextFeeMultiplierOverride}`);
+ await createBlock(api, [
+ await api.tx.balances.transfer(contractAddr, 0).signAsync(alith),
+ await api.tx.sudo
+ .sudo(
+ await api.tx.system
+ .setStorage([
+ [
+ "0x3f1467a096bcd71a5b6a0c8155e208103f2edf3bdf381debe331ab7446addfdc",
+ u8aToHex(api.createType("u128", nextFeeMultiplierOverride).toU8a()),
+ ],
+ ])
+ .signAsync(alith),
+ )
+ .signAsync(alith),
+ ]);
+ }
+
+ // start load test
+ // const loadFactors = [...generateLoad(60, 1), ...Array(10).fill(0)];
+ // const loadFactors = [...Array(183).fill(19)];
+ const loadFactors = [...Array(183).fill(55)];
+ // const loadFactors = [...Array(1).fill(0)];
+ const repsPerLoad = 30;
+ for await (const [loadFactorIndex, loadFactor] of loadFactors.entries()) {
+ console.log(
+ `load: ${loadFactor} (${repsPerLoad} reps) ${loadFactorIndex + 1}/${loadFactors.length}`,
+ );
+ for await (const rep of new Array(repsPerLoad).keys()) {
+ // uncomment the following code to reduce feeMultiplier by 10 each 100 blocks
+ // if (blockN % 100 === 0) {
+ // console.log(`feeMultiplier ${feeMultiplier.toString()}`);
+ // await createBlock(api, [
+ // await api.tx.sudo
+ // .sudo(
+ // await api.tx.system
+ // .setStorage([
+ // [
+ // "0x3f1467a096bcd71a5b6a0c8155e208103f2edf3bdf381debe331ab7446addfdc",
+ // u8aToHex(api.createType("u128", feeMultiplier).toU8a()),
+ // ],
+ // ])
+ // .signAsync(alith)
+ // )
+ // .signAsync(alith),
+ // ]);
+ // if (feeMultiplier.eqn(1)) {
+ // feeMultiplier = BN_ZERO;
+ // break;
+ // }
+ // feeMultiplier = feeMultiplier.divn(10);
+ // }
+ // blockN++;
+
+ const multiplierBefore = await api.query.transactionPayment.nextFeeMultiplier();
+ const fees = await txObserveFeeDiff(api, async () => {
+ const txs = [
+ // fill block
+ await api.tx.sudo
+ .sudo(await api.tx.system.fillBlock(loadFactor * 10_000_000).signAsync(alith))
+ .signAsync(alith),
+
+ // charge substrate fees
+ await api.tx.evm
+ .call(baltathar.address, contractAddr, contractCall, 0, 11_000_000n, 0n, null, null, [])
+ .signAsync(baltathar, { tip: 1n * 10n ** 15n }),
+
+ // charge EVM fees
+ await api.tx.evm
+ .call(
+ charleth.address,
+ contractAddr,
+ contractCall,
+ 0,
+ 11_000_000n,
+ 2_000_000_000_000_000n,
+ null,
+ null,
+ [],
+ )
+ .signAsync(charleth, { tip: 1n * 10n ** 15n }),
+ ];
+
+ txs.forEach((t) => {
+ console.log(t.hash.toString());
+ });
+
+ return txs;
+ });
+
+ // get block details
+ const transactions = {
+ substrate: null,
+ evm: null,
+ };
+ const block = await api.rpc.chain.getBlock();
+ for (const [i, ext] of block.block.extrinsics.entries()) {
+ if (ext.signer.eq(BALTATHAR_ADDRESS)) {
+ transactions.substrate = {
+ index: i,
+ extrinsicLength: ext.encodedLength,
+ extrinsic: ext,
+ };
+ } else if (ext.signer.eq(CHARLETH_ADDRESS)) {
+ transactions.evm = {
+ index: i,
+ extrinsicLength: ext.encodedLength,
+ extrinsic: ext,
+ };
+ }
+ }
+
+ // compute block weight, from events
+ const weights = {};
+ const events = await api.query.system.events();
+ let totalBlockWeight = new BN(0);
+ for (const { phase, event } of events) {
+ if (phase.isApplyExtrinsic) {
+ if (
+ api.events.system.ExtrinsicSuccess.is(event) ||
+ api.events.system.ExtrinsicFailed.is(event)
+ ) {
+ weights[phase.asApplyExtrinsic.toNumber()] =
+ event.data.dispatchInfo.weight.refTime.toBn();
+ }
+ }
+ }
+ if (!transactions.substrate || transactions.evm) {
+ }
+ for (const i of Object.keys(weights)) {
+ const key = parseInt(i);
+ if (transactions.substrate && transactions.substrate.index === key) {
+ transactions.substrate.weight = weights[i].toString();
+ } else if (transactions.evm && transactions.evm.index === key) {
+ transactions.evm.weight = weights[i].toString();
+ }
+ switch (parseInt(i)) {
+ case transactions.substrate.index:
+ transactions.substrate.weight = weights[i].toString();
+ break;
+ case transactions.evm.index:
+ transactions.evm.weight = weights[i].toString();
+ break;
+ }
+ totalBlockWeight = totalBlockWeight.add(weights[i]);
+ }
+
+ // get feeDetails
+
+ const feeDetails = (
+ await api.rpc.payment.queryFeeDetails(transactions.substrate.extrinsic.toHex())
+ ).inclusionFee.unwrap();
+ const supplyFactor = 1; // 100 for moonbeam, 1 otherwise
+ const substrateFeeDetails = {
+ baseFee: feeDetails.baseFee.toString(),
+ lengthFee: feeDetails.lenFee.toString(),
+ adjustedWeightFee: multiplierBefore
+ .mul(new BN(transactions.substrate.weight).muln(50_000 * supplyFactor))
+ .div(new BN("1000000000000000000"))
+ .toString(),
+ total: null,
+ };
+ substrateFeeDetails.total = Object.values(substrateFeeDetails)
+ .reduce((acc, v) => acc.add(new BN(v)), new BN(0))
+ .toString();
+
+ const multiplierAfter = await api.query.transactionPayment.nextFeeMultiplier();
+
+ delete transactions.substrate.extrinsic;
+ delete transactions.evm.extrinsic;
+ const data = {
+ fullPercent: totalBlockWeight.muln(100).div(maxBlockWeight).toNumber(),
+ ...fees,
+ transactions,
+ substrateFeeDetails,
+ multiplier: {
+ before: multiplierBefore.toString(),
+ after: multiplierAfter.toString(),
+ },
+ block: (await api.rpc.chain.getBlock()).block.header.number.toNumber(),
+ };
+ result.push(data);
+ if (data.block === 4) {
+ throw Error("FOUR!");
+ }
+ }
+ }
+
+ return {
+ multiplier: nextFeeMultiplierOverride.toString(),
+ callType: options.callType,
+ result,
+ };
+}
+
+function generateLoad(middle: number, inc: number = 1): number[] {
+ const load = [];
+ for (let i = 0; i <= middle; i += inc) {
+ load.push(i);
+ }
+ for (let i = 0; i <= 50; i++) {
+ load.push(middle);
+ }
+ for (let i = middle; i >= 0; i -= inc) {
+ load.push(i);
+ }
+
+ return load;
+}
+
+async function txObserveFeeDiff(
+ api: ApiPromise,
+ txFunc: () => Promise,
+) {
+ const txs = await txFunc();
+ const balanceBeforeBaltathar = await api.query.system.account(BALTATHAR_ADDRESS);
+ const balanceBeforeCharleth = await api.query.system.account(CHARLETH_ADDRESS);
+ await createBlock(api, txs);
+ const balanceAfterBaltathar = await api.query.system.account(BALTATHAR_ADDRESS);
+ const balanceAfterCharleth = await api.query.system.account(CHARLETH_ADDRESS);
+
+ return {
+ substrate: balanceBeforeBaltathar.data.free.sub(balanceAfterBaltathar.data.free).toString(),
+ evm: balanceBeforeCharleth.data.free.sub(balanceAfterCharleth.data.free).toString(),
+ };
+}
+
+/// === block creation methods === ///
+
+async function expectEVMSuccess(api: ApiPromise) {
+ const events = await api.query.system.events();
+ const ethereumResult = events.find(
+ ({ event: { section, method } }) => section == "ethereum" && method == "Executed",
+ ).event.data[3] as EvmCoreErrorExitReason;
+ assert.equal(ethereumResult.isSucceed, true, "EVM operation failed");
+}
+
+function extractError(events: EventRecord[] = []): DispatchError | undefined {
+ return events
+ .filter(({ event }) => "system" === event.section && ["ExtrinsicFailed"].includes(event.method))
+ .map(
+ ({
+ event: {
+ data: [dispatchError],
+ },
+ }) => dispatchError as DispatchError,
+ )[0];
+}
+
+async function customWeb3Request(web3: Web3, method: string, params: any[]) {
+ return new Promise((resolve, reject) => {
+ (web3.currentProvider as any).send(
+ {
+ jsonrpc: "2.0",
+ id: 1,
+ method,
+ params,
+ },
+ (error: Error | null, result?: JsonRpcResponseWithResult) => {
+ if (error) {
+ reject(
+ `Failed to send custom request (${method} (${params
+ .map((p) => {
+ const str = p.toString();
+ return str.length > 128 ? `${str.slice(0, 96)}...${str.slice(-28)}` : str;
+ })
+ .join(",")})): ${error.message || error.toString()}`,
+ );
+ }
+ resolve(result);
+ },
+ );
+ });
+}
+
+interface BlockCreation {
+ parentHash?: string;
+ finalize?: boolean;
+}
+type ExtrinsicCreation = boolean;
+async function createBlock<
+ ApiType extends ApiTypes,
+ Call extends
+ | SubmittableExtrinsic
+ | Promise>
+ | string
+ | Promise,
+ Calls extends Call | Call[],
+>(api: ApiPromise, transactions?: Calls, options: BlockCreation = {}) {
+ const results: ({ type: "eth"; hash: string } | { type: "sub"; hash: string })[] = [];
+ const txs =
+ transactions == undefined ? [] : Array.isArray(transactions) ? transactions : [transactions];
+ for await (const call of txs) {
+ if (typeof call == "string") {
+ // Ethereum
+ results.push({
+ type: "eth",
+ hash: (await customWeb3Request(web3, "eth_sendRawTransaction", [call])).result.toString(),
+ });
+ } else if (call.isSigned) {
+ results.push({
+ type: "sub",
+ hash: (await call.send()).toString(),
+ });
+ } else {
+ results.push({
+ type: "sub",
+ hash: (await call.signAndSend(alith)).toString(),
+ });
+ }
+ }
+
+ const { parentHash, finalize } = options;
+ const block = parentHash
+ ? await api.rpc.engine.createBlock(true, finalize, parentHash)
+ : await api.rpc.engine.createBlock(true, finalize);
+ const blockHash = block.get("hash").toString();
+
+ // No need to extract events if no transactions
+ if (results.length == 0) {
+ return {
+ block,
+ result: null,
+ };
+ }
+
+ // We retrieve the events for that block
+ const allRecords: EventRecord[] = (await (await api.at(blockHash)).query.system.events()) as any;
+ // We retrieve the block (including the extrinsics)
+ const blockData = await api.rpc.chain.getBlock(blockHash);
+
+ const result: ExtrinsicCreation[] = results.map((result) => {
+ const extrinsicIndex =
+ result.type == "eth"
+ ? allRecords
+ .find(
+ ({ phase, event: { section, method, data } }) =>
+ phase.isApplyExtrinsic &&
+ section == "ethereum" &&
+ method == "Executed" &&
+ data[2].toString() == result.hash,
+ )
+ ?.phase?.asApplyExtrinsic?.toNumber()
+ : blockData.block.extrinsics.findIndex((ext) => ext.hash.toHex() == result.hash);
+ // We retrieve the events associated with the extrinsic
+ const events = allRecords.filter(
+ ({ phase }) => phase.isApplyExtrinsic && phase.asApplyExtrinsic.toNumber() === extrinsicIndex,
+ );
+ const failure = extractError(events);
+ const successful = extrinsicIndex !== undefined && !failure;
+ return successful;
+ });
+
+ // Adds extra time to avoid empty transaction when querying it
+ if (results.find((r) => r.type == "eth")) {
+ await setTimeout(2);
+ }
+
+ return Array.isArray(transactions) ? result : (result[0] as boolean);
+}
+
+const ALITH_TRANSACTION_TEMPLATE: TransactionOptions = {
+ from: alith.address,
+ privateKey: ALITH_PRIVATE_KEY,
+ nonce: null,
+ gas: 500_000,
+ gasPrice: 1_000_000_000,
+ value: "0x00",
+};
+interface TransactionOptions {
+ from?: string;
+ to?: string;
+ privateKey?: string;
+ nonce?: number;
+ gas?: string | number;
+ gasPrice?: string | number;
+ maxFeePerGas?: string | number;
+ maxPriorityFeePerGas?: string | number;
+ value?: string | number;
+ data?: string;
+ accessList?: AccessListish; // AccessList | Array<[string, Array]>
+}
+async function createTransaction(
+ options: TransactionOptions,
+ ethTransactionType = "Legacy",
+): Promise {
+ const isLegacy = ethTransactionType === "Legacy";
+ const isEip2930 = ethTransactionType === "EIP2930";
+ const isEip1559 = ethTransactionType === "EIP1559";
+
+ const gasPrice = options.gasPrice !== undefined ? options.gasPrice : 1_000_000_000;
+ const maxPriorityFeePerGas =
+ options.maxPriorityFeePerGas !== undefined ? options.maxPriorityFeePerGas : 0;
+ const value = options.value !== undefined ? options.value : "0x00";
+ const from = options.from || alith.address;
+ const privateKey = options.privateKey !== undefined ? options.privateKey : ALITH_PRIVATE_KEY;
+
+ // Instead of hardcoding the gas limit, we estimate the gas
+ const gas =
+ options.gas ||
+ (await web3.eth.estimateGas({
+ from: from,
+ to: options.to,
+ data: options.data,
+ }));
+
+ const maxFeePerGas = options.maxFeePerGas || 1_000_000_000;
+ const accessList = options.accessList || [];
+ const nonce =
+ options.nonce != null ? options.nonce : await web3.eth.getTransactionCount(from, "pending");
+
+ let data, rawTransaction;
+ if (isLegacy) {
+ data = {
+ from,
+ to: options.to,
+ value: value && value.toString(),
+ gasPrice,
+ gas,
+ nonce: nonce,
+ data: options.data,
+ };
+ const tx = await web3.eth.accounts.signTransaction(data, privateKey);
+ rawTransaction = tx.rawTransaction;
+ } else {
+ const signer = new ethers.Wallet(privateKey, ethersApi);
+ const chainId = await web3.eth.getChainId();
+ if (isEip2930) {
+ data = {
+ from,
+ to: options.to,
+ value: value && value.toString(),
+ gasPrice,
+ gasLimit: gas,
+ nonce: nonce,
+ data: options.data,
+ accessList,
+ chainId,
+ type: 1,
+ };
+ } else if (isEip1559) {
+ data = {
+ from,
+ to: options.to,
+ value: value && value.toString(),
+ maxFeePerGas,
+ maxPriorityFeePerGas,
+ gasLimit: gas,
+ nonce: nonce,
+ data: options.data,
+ accessList,
+ chainId,
+ type: 2,
+ };
+ }
+ rawTransaction = await signer.signTransaction(data);
+ }
+
+ return rawTransaction;
+}
+
+async function createContract(
+ contractCompiled: Compiled,
+ options: TransactionOptions = ALITH_TRANSACTION_TEMPLATE,
+ contractArguments: any[] = [],
+): Promise<{ rawTx: string; contract: Contract; contractAddress: string }> {
+ const from = options.from !== undefined ? options.from : alith.address;
+ const nonce = options.nonce || Number(await web3.eth.getTransactionCount(from));
+
+ const contractAddress =
+ "0x" +
+ web3.utils
+ .sha3(RLP.encode([from, nonce]) as any)
+ .slice(12)
+ .substring(14);
+
+ const contract = new web3.eth.Contract(contractCompiled.contract.abi, contractAddress);
+ const data = contract
+ .deploy({
+ data: contractCompiled.byteCode,
+ arguments: contractArguments,
+ })
+ .encodeABI();
+
+ const rawTx = await createTransaction({ ...options, from, nonce, data });
+
+ return {
+ rawTx,
+ contract,
+ contractAddress,
+ };
+}
+
+/// === solidity compile methods === ///
+
+export interface Compiled {
+ byteCode: string;
+ contract: any;
+ sourceCode: string;
+}
+function compileSolidity(fileContents: string): Compiled {
+ // const fileContents = fs.readFileSync(filepath).toString();
+ const result = JSON.parse(
+ solc.compile(
+ JSON.stringify({
+ language: "Solidity",
+ sources: {
+ "main.sol": {
+ content: fileContents,
+ },
+ },
+ settings: {
+ outputSelection: {
+ "*": {
+ "*": ["*"],
+ },
+ },
+ },
+ }),
+ {
+ import: (_: string) => {
+ return { error: "imports not supported" };
+ },
+ },
+ ),
+ );
+ if (!result.contracts) {
+ throw result;
+ }
+ const allContractNames = Object.keys(result.contracts["main.sol"]);
+ const reduced = allContractNames.reduce((p, contractName) => {
+ p[contractName] = {
+ byteCode: "0x" + result.contracts["main.sol"][contractName].evm.bytecode.object,
+ contract: result.contracts["main.sol"][contractName],
+ sourceCode: fileContents,
+ };
+ return p;
+ }, {});
+ return reduced[allContractNames[0]];
+}
+
+/// === main === ///
+
+async function view(input: string, output: string, open: boolean) {
+ const data = JSON.parse(fs.readFileSync(input).toString("utf-8"));
+ const labels = data.result.map((x: any) => x["block"]);
+ const fullPercent = data.result.map((x: any) => x["fullPercent"]);
+ const substrateFees = data.result.map((x: any) => new BN(x["substrate"]).toString());
+ const evmFees = data.result.map((x: any) => new BN(x["evm"]).toString());
+ const multiplier = data.result.map((x: any) => new BN(x["multiplier"]["before"]).toString());
+ const diff = data.result.map((x: any) => {
+ const a = new BN(x["substrate"]);
+ const b = new BN(x["evm"]);
+ return a.sub(b).abs().muln(100).div(a.add(b).divn(2)).toString();
+ });
+ const diffSubstrate = data.result.map((x: any) => {
+ const a = new BN(x["substrate"]);
+ const b = new BN(x["evm"]);
+ return a.sub(b).toString();
+ });
+
+ // editorconfig-checker-disable
+ fs.writeFileSync(
+ output,
+ `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `,
+ );
+ // editorconfig-checker-enable
+
+ const openCmd = (() => {
+ switch (process.platform) {
+ case "darwin":
+ return "open";
+ case "win32":
+ return "start";
+ default:
+ return "xdg-open";
+ }
+ })();
+
+ if (open) {
+ await exec(`${openCmd} ${output}`);
+ }
+}
+
+const argv = yargs(process.argv.slice(2))
+ .usage("Usage: $0")
+ .version("1.0.0")
+ .options({
+ name: {
+ type: "string",
+ description: "The output file name",
+ demandOption: true,
+ },
+ multiplier: {
+ type: "string",
+ description: "The multiplier override",
+ default: "",
+ },
+ view: {
+ type: "boolean",
+ description: "View existing file",
+ },
+ type: {
+ type: "string",
+ description: "View existing file",
+ choices: ["compute", "length-small", "length-big"],
+ demandOption: false,
+ default: "compute",
+ },
+ }).argv;
+
+async function main() {
+ const name = `${argv.name}-${argv.type}`;
+ if (argv.view) {
+ await view(`${name}.json`, `${name}.html`, true);
+ return;
+ }
+
+ const api = await ApiPromise.create({
+ initWasm: false,
+ provider: new WsProvider(wssUrl),
+ });
+
+ try {
+ const results = await runTest(api, {
+ callType: argv.type as any,
+ multiplier: argv.multiplier.length === 0 ? null : new BN(argv.multiplier),
+ });
+ fs.writeFileSync(`${name}.json`, JSON.stringify(results, null, 2));
+ await view(`${name}.json`, `${name}.html`, true);
+ } finally {
+ await api.disconnect();
+ }
+}
+
+main()
+ .catch((err) => console.error("ERR!", err))
+ .finally(() => process.exit(0));
diff --git a/src/tools/flood-evm-compute.ts b/src/tools/flood-evm-compute.ts
index e797e05..daacb5c 100644
--- a/src/tools/flood-evm-compute.ts
+++ b/src/tools/flood-evm-compute.ts
@@ -1,14 +1,13 @@
// This script is expected to run against a parachain network (using launch.ts script)
-
-import { ALITH_PRIVATE_KEY } from "../utils/constants";
-import { compileSolidity } from "../utils/web3/solidity";
import { Keyring } from "@polkadot/api";
-
import * as rlp from "rlp";
+import { Web3 } from "web3";
import yargs from "yargs";
-import { getMonitoredApiFor, NETWORK_YARGS_OPTIONS } from "../utils/networks";
-import Web3 from "web3";
-import { callContract, deployContract } from "../utils/web3/contracts";
+
+import { ALITH_PRIVATE_KEY } from "../utils/constants.ts";
+import { getMonitoredApiFor, NETWORK_YARGS_OPTIONS } from "../utils/networks.ts";
+import { callContract, deployContract } from "../utils/web3/contracts.ts";
+import { compileSolidity } from "../utils/web3/solidity.ts";
const argv = yargs(process.argv.slice(2))
.usage("Usage: $0")
diff --git a/src/tools/flood-evm-transfers.ts b/src/tools/flood-evm-transfers.ts
index 0ee6f31..320a90f 100644
--- a/src/tools/flood-evm-transfers.ts
+++ b/src/tools/flood-evm-transfers.ts
@@ -1,13 +1,11 @@
// This script is expected to run against a parachain network (using launch.ts script)
-
-import { ALITH_PRIVATE_KEY } from "../utils/constants";
import { Keyring } from "@polkadot/api";
-import { TransactionReceipt } from "web3-core";
-
+import { TransactionReceipt, Web3 } from "web3";
import yargs from "yargs";
-import { getMonitoredApiFor, NETWORK_YARGS_OPTIONS } from "../utils/networks";
-import Web3 from "web3";
-import { customWeb3Request } from "../utils/web3/transactions";
+
+import { ALITH_PRIVATE_KEY } from "../utils/constants.ts";
+import { getMonitoredApiFor, NETWORK_YARGS_OPTIONS } from "../utils/networks.ts";
+import { customWeb3Request } from "../utils/web3/transactions.ts";
const argv = yargs(process.argv.slice(2))
.usage("Usage: $0")
diff --git a/src/tools/flood-remarks.ts b/src/tools/flood-remarks.ts
index 6c617a6..7d4511d 100644
--- a/src/tools/flood-remarks.ts
+++ b/src/tools/flood-remarks.ts
@@ -1,13 +1,11 @@
// This script is expected to run against a parachain network (using launch.ts script)
-
-import { ALITH_PRIVATE_KEY } from "../utils/constants";
import { Keyring } from "@polkadot/api";
-import { TransactionReceipt } from "web3-core";
-
+import { TransactionReceipt, Web3 } from "web3";
import yargs from "yargs";
-import { getMonitoredApiFor, NETWORK_YARGS_OPTIONS } from "../utils/networks";
-import Web3 from "web3";
-import { customWeb3Request } from "../utils/web3/transactions";
+
+import { ALITH_PRIVATE_KEY } from "../utils/constants.ts";
+import { getMonitoredApiFor, NETWORK_YARGS_OPTIONS } from "../utils/networks.ts";
+import { customWeb3Request } from "../utils/web3/transactions.ts";
const argv = yargs(process.argv.slice(2))
.usage("Usage: $0")
diff --git a/src/tools/fork-exported-state.ts b/src/tools/fork-exported-state.ts
index 64979fa..f39950b 100644
--- a/src/tools/fork-exported-state.ts
+++ b/src/tools/fork-exported-state.ts
@@ -1,12 +1,11 @@
// This script is expected to run against a parachain network (using launch.ts script)
-
-import fs from "node:fs/promises";
import yargs from "yargs";
+
import {
downloadExportedState,
NetworkName,
neutralizeExportedState,
-} from "../libs/helpers/state-manipulator";
+} from "../libs/helpers/state-manipulator/index.ts";
const argv = yargs(process.argv.slice(2))
.usage("Usage: $0")
diff --git a/src/tools/get-parachains-xcm-versions.ts b/src/tools/get-parachains-xcm-versions.ts
index c4e8ea8..f1a1eea 100644
--- a/src/tools/get-parachains-xcm-versions.ts
+++ b/src/tools/get-parachains-xcm-versions.ts
@@ -1,4 +1,5 @@
import "@polkadot/api-augment/kusama";
+
import { ApiPromise, WsProvider } from "@polkadot/api";
import {
prodParasKusama,
@@ -7,7 +8,6 @@ import {
prodParasPolkadotCommon,
} from "@polkadot/apps-config";
import yargs from "yargs";
-import fs from "fs";
export const NETWORK_WS_URLS: { [name: string]: string } = {
rococo: "wss://rococo-rpc.polkadot.io",
diff --git a/src/tools/get-relay-runtime.ts b/src/tools/get-relay-runtime.ts
index b1fe9c9..dfc11c5 100644
--- a/src/tools/get-relay-runtime.ts
+++ b/src/tools/get-relay-runtime.ts
@@ -1,6 +1,6 @@
import { ApiPromise, WsProvider } from "@polkadot/api";
-import yargs from "yargs";
import fs from "fs";
+import yargs from "yargs";
export const NETWORK_WS_URLS: { [name: string]: string } = {
rococo: "wss://rococo-rpc.polkadot.io",
diff --git a/src/tools/get-transaction-data.ts b/src/tools/get-transaction-data.ts
new file mode 100644
index 0000000..a49579a
--- /dev/null
+++ b/src/tools/get-transaction-data.ts
@@ -0,0 +1,39 @@
+import { ApiPromise, WsProvider } from "@polkadot/api";
+import fs from "fs";
+import { getApiFor, NETWORK_YARGS_OPTIONS } from "src/utils/networks.ts";
+import yargs from "yargs";
+
+export const NETWORK_WS_URLS: { [name: string]: string } = {
+ rococo: "wss://rococo-rpc.polkadot.io",
+ westend: "wss://westend.api.onfinality.io/public-ws",
+ kusama: "wss://kusama.api.onfinality.io/public-ws",
+ polkadot: "wss://polkadot.api.onfinality.io/public-ws",
+};
+
+const argv = yargs(process.argv.slice(2))
+ .usage("Usage: $0")
+ .version("1.0.0")
+ .options({
+ ...NETWORK_YARGS_OPTIONS,
+ at: {
+ type: "number",
+ description: "Block number",
+ }
+ }).argv;
+
+const main = async () => {
+ const api = await getApiFor(argv);
+
+ const blockHash = argv.at
+ ? await api.rpc.chain.getBlockHash(argv.at)
+ : await api.rpc.chain.getBlockHash();
+ const block = await api.rpc.chain.getBlock(blockHash);
+
+ block.block.extrinsics.forEach((ex, index) => {
+ console.log(index, `${ex.method.section.toString()}.${ex.method.method.toString()} [${ex.hash.toHex()}]\n${ex.method.method.toString() == "setValidationData" ? "..." : ex.toHex()}`);
+ });
+
+ await api.disconnect();
+};
+
+main();
diff --git a/src/tools/kill-prefix-delegators.ts b/src/tools/kill-prefix-delegators.ts
index 0d37e05..722a173 100644
--- a/src/tools/kill-prefix-delegators.ts
+++ b/src/tools/kill-prefix-delegators.ts
@@ -1,11 +1,10 @@
-import { xxhashAsU8a, blake2AsU8a, blake2AsHex } from "@polkadot/util-crypto";
-import { u8aConcat } from "@polkadot/util";
-import yargs from "yargs";
-import { PalletDemocracyReferendumInfo } from "@polkadot/types/lookup";
-import { getApiFor, NETWORK_YARGS_OPTIONS } from "../utils/networks";
-import chalk from "chalk";
import { Keyring } from "@polkadot/api";
-import { ALITH_PRIVATE_KEY } from "../utils/constants";
+import { PalletDemocracyReferendumInfo } from "@polkadot/types/lookup";
+import { blake2AsHex } from "@polkadot/util-crypto";
+import yargs from "yargs";
+
+import { ALITH_PRIVATE_KEY } from "../utils/constants.ts";
+import { getApiFor, NETWORK_YARGS_OPTIONS } from "../utils/networks.ts";
const debug = require("debug")("main");
diff --git a/src/tools/list-active-evm-accounts.ts b/src/tools/list-active-evm-accounts.ts
index 0479a43..edc42b5 100644
--- a/src/tools/list-active-evm-accounts.ts
+++ b/src/tools/list-active-evm-accounts.ts
@@ -1,8 +1,8 @@
+import { ApiPromise } from "@polkadot/api";
import yargs from "yargs";
-import fs from "fs";
-import { exploreBlockRange, getApiFor, NETWORK_YARGS_OPTIONS, reverseBlocks } from "..";
-import { ApiPromise } from "@polkadot/api";
+import { exploreBlockRange, getApiFor, NETWORK_YARGS_OPTIONS } from "../index.ts";
+
const XEN_ADDRESS = "0xb564A5767A00Ee9075cAC561c427643286F8F4E1".toLowerCase();
const argv = yargs(process.argv.slice(2))
diff --git a/src/tools/list-collator-candidates.ts b/src/tools/list-collator-candidates.ts
index 7d1bc13..5aeb0c5 100644
--- a/src/tools/list-collator-candidates.ts
+++ b/src/tools/list-collator-candidates.ts
@@ -1,8 +1,9 @@
// This script is expected to run against a parachain network (using launch.ts script)
import "@moonbeam-network/api-augment";
+
import chalk from "chalk";
-import yargs from "yargs";
import { table } from "table";
+import yargs from "yargs";
import {
combineRequestsPerDelegators,
@@ -10,7 +11,7 @@ import {
getApiFor,
NETWORK_YARGS_OPTIONS,
numberWithCommas,
-} from "..";
+} from "../index.ts";
const argv = yargs(process.argv.slice(2))
.usage("Usage: $0")
diff --git a/src/tools/list-collator-rewards.ts b/src/tools/list-collator-rewards.ts
index 1e41307..f0852ad 100644
--- a/src/tools/list-collator-rewards.ts
+++ b/src/tools/list-collator-rewards.ts
@@ -1,10 +1,9 @@
// This script is expected to run against a parachain network (using launch.ts script)
-import yargs from "yargs";
+import { ParachainInherentData } from "@polkadot/types/interfaces";
import fs from "fs";
+import yargs from "yargs";
-import { exploreBlockRange, getApiFor, NETWORK_YARGS_OPTIONS } from "..";
-
-import { ParachainInherentData } from "@polkadot/types/interfaces";
+import { exploreBlockRange, getApiFor, NETWORK_YARGS_OPTIONS } from "../index.ts";
const INITIAL_ROUND_BLOCK = {
moonbeam: 1200,
diff --git a/src/tools/list-delegations.ts b/src/tools/list-delegations.ts
index feaf59d..4eb61b0 100644
--- a/src/tools/list-delegations.ts
+++ b/src/tools/list-delegations.ts
@@ -1,13 +1,13 @@
// This script is expected to run against a parachain network (using launch.ts script)
-import yargs from "yargs";
import { table } from "table";
+import yargs from "yargs";
import {
+ combineRequestsPerDelegators,
getAccountIdentity,
getApiFor,
NETWORK_YARGS_OPTIONS,
- combineRequestsPerDelegators,
-} from "..";
+} from "../index.ts";
const argv = yargs(process.argv.slice(2))
.usage("Usage: $0")
diff --git a/src/tools/list-delegators.ts b/src/tools/list-delegators.ts
index f571413..88bc5e6 100644
--- a/src/tools/list-delegators.ts
+++ b/src/tools/list-delegators.ts
@@ -1,13 +1,13 @@
// This script is expected to run against a parachain network (using launch.ts script)
-import yargs from "yargs";
import { table } from "table";
+import yargs from "yargs";
import {
+ combineRequestsPerDelegators,
getAccountIdentity,
getApiFor,
NETWORK_YARGS_OPTIONS,
- combineRequestsPerDelegators,
-} from "..";
+} from "../index.ts";
const argv = yargs(process.argv.slice(2))
.usage("Usage: $0")
diff --git a/src/tools/list-limbo-balance-accounts.ts b/src/tools/list-limbo-balance-accounts.ts
index 53d18c5..e294ad9 100644
--- a/src/tools/list-limbo-balance-accounts.ts
+++ b/src/tools/list-limbo-balance-accounts.ts
@@ -1,11 +1,11 @@
// This script is expected to run against a parachain network (using launch.ts script)
-import yargs from "yargs";
-import { table } from "table";
-
-import { getAccountIdentity } from "../utils/monitoring";
-import { getApiFor, NETWORK_YARGS_OPTIONS } from "..";
import { bnMax } from "@polkadot/util";
+import { table } from "table";
import Web3 from "web3";
+import yargs from "yargs";
+
+import { getApiFor, NETWORK_YARGS_OPTIONS } from "../index.ts";
+import { getAccountIdentity } from "../utils/monitoring.ts";
const argv = yargs(process.argv.slice(2))
.usage("Usage: $0")
@@ -41,10 +41,10 @@ const main = async () => {
affectedAccounts.push([
addressesToCheck[idx],
identities[idx],
- Web3.utils.fromWei(free.toString()),
- Web3.utils.fromWei(reserved.toString()),
- Web3.utils.fromWei(frozen.toString()),
- Web3.utils.fromWei(frozen.sub(free).toString()),
+ Web3.utils.fromWei(free.toString(), "ether"),
+ Web3.utils.fromWei(reserved.toString(), "ether"),
+ Web3.utils.fromWei(frozen.toString(), "ether"),
+ Web3.utils.fromWei(frozen.sub(free).toString(), "ether"),
]);
}
});
diff --git a/src/tools/list-methods.ts b/src/tools/list-methods.ts
index 1635d27..77cd0dc 100644
--- a/src/tools/list-methods.ts
+++ b/src/tools/list-methods.ts
@@ -1,6 +1,6 @@
import yargs from "yargs";
-import { getApiFor, NETWORK_YARGS_OPTIONS } from "../utils/networks";
+import { getApiFor, NETWORK_YARGS_OPTIONS } from "../utils/networks.ts";
const argv = yargs(process.argv.slice(2))
.usage("Usage: $0")
diff --git a/src/tools/list-precompiles.ts b/src/tools/list-precompiles.ts
index f5c94d8..ac365d9 100644
--- a/src/tools/list-precompiles.ts
+++ b/src/tools/list-precompiles.ts
@@ -1,10 +1,11 @@
-import { xxhashAsU8a, blake2AsU8a } from "@polkadot/util-crypto";
import { u8aConcat } from "@polkadot/util";
-import yargs from "yargs";
-import { getApiFor, getViemAccountFor, getViemFor, NETWORK_YARGS_OPTIONS } from "../utils/networks";
-import { createPublicClient, createWalletClient, encodeFunctionData, http } from "viem";
-import { privateKeyToAccount } from "viem/accounts";
+import { blake2AsU8a, xxhashAsU8a } from "@polkadot/util-crypto";
import chalk from "chalk";
+import { encodeFunctionData } from "viem";
+import { privateKeyToAccount } from "viem/accounts";
+import yargs from "yargs";
+
+import { getViemAccountFor, getViemFor, NETWORK_YARGS_OPTIONS } from "../utils/networks.ts";
const debug = require("debug")("main");
diff --git a/src/tools/list-referendum.ts b/src/tools/list-referendum.ts
index c25d953..4402754 100644
--- a/src/tools/list-referendum.ts
+++ b/src/tools/list-referendum.ts
@@ -1,14 +1,14 @@
+import { ApiPromise } from "@polkadot/api";
+import { BN, BN_TEN } from "@polkadot/util";
import humanizeNumber from "humanize-number";
import { moment } from "moment-parseplus";
import yargs from "yargs";
-import { ApiPromise } from "@polkadot/api";
-import { BN, BN_TEN } from "@polkadot/util";
-import { getBlockDate } from "../utils/block-time";
-import { getApiFor, NETWORK_YARGS_OPTIONS } from "../utils/networks";
-import { getReferendumByGroups } from "../utils/referenda";
-import { callInterpreter, renderCallInterpretation } from "../utils/transactions";
-import { promiseConcurrent } from "../utils/functions";
+import { getBlockDate } from "../utils/block-time.ts";
+import { promiseConcurrent } from "../utils/functions.ts";
+import { getApiFor, NETWORK_YARGS_OPTIONS } from "../utils/networks.ts";
+import { getReferendumByGroups } from "../utils/referenda.ts";
+import { callInterpreter, renderCallInterpretation } from "../utils/transactions.ts";
const argv = yargs(process.argv.slice(2))
.usage("Usage: $0")
diff --git a/src/tools/list-storages.ts b/src/tools/list-storages.ts
index 174872d..e80c977 100644
--- a/src/tools/list-storages.ts
+++ b/src/tools/list-storages.ts
@@ -1,6 +1,8 @@
import yargs from "yargs";
+import chalk from "chalk";
-import { getApiFor, NETWORK_YARGS_OPTIONS } from "../utils/networks";
+import { getApiFor, NETWORK_YARGS_OPTIONS } from "../utils/networks.ts";
+import { xxhashAsHex } from "@polkadot/util-crypto";
const argv = yargs(process.argv.slice(2))
.usage("Usage: $0")
@@ -9,15 +11,26 @@ const argv = yargs(process.argv.slice(2))
...NETWORK_YARGS_OPTIONS,
}).argv;
+const capitalize = (s) => {
+ return String(s[0]).toUpperCase() + String(s).slice(1);
+}
const main = async () => {
const api = await getApiFor(argv);
for (const section of Object.keys(api.query)) {
- console.log(`${section}`);
+ const palletName = section == "evm" ? "EVM" : capitalize(section);
+ const sectionKey = xxhashAsHex(palletName, 128);
+ console.log(`${chalk.yellow(palletName)}`);
for (const method of Object.keys(api.query[section])) {
- console.log(
- ` ${`${section}.${method}`.padStart(50, " ")}: ${api.query[section][method].keyPrefix()}`,
- );
+ if (api.query[section][method].keyPrefix().includes(sectionKey.slice(2))) {
+ console.log(
+ ` ${`${section}.${method}`.padStart(50, " ")}: ${chalk.yellow(sectionKey)}${api.query[section][method].keyPrefix().slice(34)}`,
+ );
+ } else {
+ console.log(
+ ` ${`${section}.${method}`.padStart(50, " ")}: ${api.query[section][method].keyPrefix()}`,
+ );
+ }
}
}
await api.disconnect();
diff --git a/src/tools/monitor.ts b/src/tools/monitor.ts
index f8a62ef..a3af324 100644
--- a/src/tools/monitor.ts
+++ b/src/tools/monitor.ts
@@ -1,10 +1,9 @@
#!/usr/bin/env node
-
-// This script is expected to run against a moonbeam blockchain
import yargs from "yargs";
-import { getMonitoredApiFor, NETWORK_NAMES } from "..";
+import { getMonitoredApiFor, NETWORK_NAMES } from "../index.ts";
+// This script is expected to run against a moonbeam blockchain
const argv = yargs(process.argv.slice(2))
.usage("Usage: $0")
.version("1.0.0")
diff --git a/src/tools/propose-motion.ts b/src/tools/propose-motion.ts
index 131bf7d..fe6cd03 100644
--- a/src/tools/propose-motion.ts
+++ b/src/tools/propose-motion.ts
@@ -1,23 +1,22 @@
-/*
- Performs a runtime upgrade through sudo or council (requires polkadot v0.9.32+)
-
-Ex: ./node_modules/.bin/ts-node-transpile-only src/tools/upgrade-network.ts \
- --url ws://localhost:9944 \
- --send-proposal-as council-external \
- --collective-threshold 3 \
- --proxy \
- --account-priv-key \
-*/
-import yargs from "yargs";
-import fs from "fs";
-import "@polkadot/api-augment";
+// Performs a runtime upgrade through sudo or council (requires polkadot v0.9.32+)
+//
+// Ex: bun src/tools/upgrade-network.ts \
+// --url ws://localhost:9944 \
+// --send-proposal-as council-external \
+// --collective-threshold 3 \
+// --proxy \
+// --account-priv-key
import "@moonbeam-network/api-augment";
+import "@polkadot/api-augment";
+
import { Keyring } from "@polkadot/api";
import { KeyringPair } from "@polkadot/keyring/types";
-import { getApiFor, NETWORK_YARGS_OPTIONS } from "../utils/networks";
-import { monitorSubmittedExtrinsic, waitForAllMonitoredExtrinsics } from "../utils/monitoring";
-import { maybeProxyCall } from "../utils/transactions";
-import { ALITH_PRIVATE_KEY } from "../utils/constants";
+import yargs from "yargs";
+
+import { ALITH_PRIVATE_KEY } from "../utils/constants.ts";
+import { monitorSubmittedExtrinsic, waitForAllMonitoredExtrinsics } from "../utils/monitoring.ts";
+import { getApiFor, NETWORK_YARGS_OPTIONS } from "../utils/networks.ts";
+import { maybeProxyCall } from "../utils/transactions.ts";
const argv = yargs(process.argv.slice(2))
.usage("Usage: $0")
diff --git a/src/tools/proxy-announcements.ts b/src/tools/proxy-announcements.ts
index b0ab2a1..d278090 100644
--- a/src/tools/proxy-announcements.ts
+++ b/src/tools/proxy-announcements.ts
@@ -1,12 +1,11 @@
// This script is specific to the moonbeam foundation
// It allows to verify proxy announcement against transfers from a csv file
-
import chalk from "chalk";
-import yargs from "yargs";
import fs from "fs";
import { table } from "table";
+import yargs from "yargs";
-import { getApiFor, NETWORK_YARGS_OPTIONS, numberWithCommas } from "..";
+import { getApiFor, NETWORK_YARGS_OPTIONS, numberWithCommas } from "../index.ts";
const argv = yargs(process.argv.slice(2))
.usage("Usage: $0")
diff --git a/src/tools/relay-hrmp.ts b/src/tools/relay-hrmp.ts
index 9e43106..86271c1 100644
--- a/src/tools/relay-hrmp.ts
+++ b/src/tools/relay-hrmp.ts
@@ -1,10 +1,11 @@
// This script is expected to run against a parachain network (using launch.ts script)
-import yargs from "yargs";
+import "@polkadot/api-augment";
+
import chalk from "chalk";
import { table } from "table";
-import "@polkadot/api-augment";
+import yargs from "yargs";
-import { getApiFor, NETWORK_YARGS_OPTIONS } from "..";
+import { getApiFor, NETWORK_YARGS_OPTIONS } from "../index.ts";
const argv = yargs(process.argv.slice(2))
.usage("Usage: $0")
diff --git a/src/tools/run-moonbeam-fork.ts b/src/tools/run-moonbeam-fork.ts
index 70d18ac..97b9620 100644
--- a/src/tools/run-moonbeam-fork.ts
+++ b/src/tools/run-moonbeam-fork.ts
@@ -1,23 +1,23 @@
// This script is expected to run against a parachain network (using launch.ts script)
-
-import moment from "moment";
-import prettyBytes from "pretty-bytes";
-import { SingleBar } from "cli-progress";
-import { runTask, spawnTask } from "../utils/runner";
-import { ChildProcessWithoutNullStreams } from "node:child_process";
-import semver from "semver";
-import yargs from "yargs";
import chalk from "chalk";
+import { SingleBar } from "cli-progress";
import fs from "fs/promises";
+import inquirer from "inquirer";
+import moment from "moment";
import fetch from "node-fetch";
+import { ChildProcessWithoutNullStreams } from "node:child_process";
import path from "path";
+import prettyBytes from "pretty-bytes";
+import semver from "semver";
+import yargs from "yargs";
+
import {
downloadExportedState,
NetworkName,
neutralizeExportedState,
-} from "../libs/helpers/state-manipulator";
-import { ALITH_PRIVATE_KEY } from "../utils/constants";
-import inquirer from "inquirer";
+} from "../libs/helpers/state-manipulator/index.ts";
+import { ALITH_PRIVATE_KEY } from "../utils/constants.ts";
+import { runTask, spawnTask } from "../utils/runner.ts";
const argv = yargs(process.argv.slice(2))
.usage("Usage: $0")
diff --git a/src/tools/scale.ts b/src/tools/scale.ts
index 620751e..25b58d7 100755
--- a/src/tools/scale.ts
+++ b/src/tools/scale.ts
@@ -1,5 +1,4 @@
#!/usr/bin/env ts-node
-
/**
* Usage:
*
@@ -24,13 +23,12 @@
* ...
* }'
*/
-
import { ApiPromise, WsProvider } from "@polkadot/api";
-import { u8aToHex, hexToU8a, hexToNumber, u8aToNumber, isArray } from "@polkadot/util";
-import { xxhashAsU8a, blake2AsU8a } from "@polkadot/util-crypto";
-import yargs from "yargs";
-import mergeWith from "lodash.mergewith";
+import { hexToU8a, isArray, u8aToHex } from "@polkadot/util";
+import { blake2AsU8a, xxhashAsU8a } from "@polkadot/util-crypto";
import isObject from "lodash.isobject";
+import mergeWith from "lodash.mergewith";
+import yargs from "yargs";
const args = yargs
.showHelpOnFail(true)
diff --git a/src/tools/search-account-transactions.ts b/src/tools/search-account-transactions.ts
index 743440d..e8dc3c6 100644
--- a/src/tools/search-account-transactions.ts
+++ b/src/tools/search-account-transactions.ts
@@ -1,7 +1,6 @@
import yargs from "yargs";
-import fs from "fs";
-import { exploreBlockRange, getApiFor, NETWORK_YARGS_OPTIONS, reverseBlocks } from "..";
+import { getApiFor, NETWORK_YARGS_OPTIONS, reverseBlocks } from "../index.ts";
const argv = yargs(process.argv.slice(2))
.usage("Usage: $0")
diff --git a/src/tools/send-clear-origin-messages-from-relay.ts b/src/tools/send-clear-origin-messages-from-relay.ts
index 304f574..5ebc5bd 100644
--- a/src/tools/send-clear-origin-messages-from-relay.ts
+++ b/src/tools/send-clear-origin-messages-from-relay.ts
@@ -1,21 +1,10 @@
-import yargs from "yargs";
import "@polkadot/api-augment";
-import { Keyring } from "@polkadot/api";
-import { getApiFor } from "../utils/networks";
-import { getAccountIdentity } from "../utils/monitoring";
+import { Keyring } from "@polkadot/api";
import { BN } from "@polkadot/util";
-import {
- isBigInt,
- isBn,
- isHex,
- isNumber,
- isU8a,
- u8aConcat,
- u8aToBn,
- u8aToHex,
- u8aToU8a,
-} from "@polkadot/util";
+import yargs from "yargs";
+
+import { getApiFor } from "../utils/networks.ts";
const argv = yargs(process.argv.slice(2))
.usage("Usage: $0")
diff --git a/src/tools/storage-module-key.ts b/src/tools/storage-module-key.ts
index 733e3b6..0ec62c5 100644
--- a/src/tools/storage-module-key.ts
+++ b/src/tools/storage-module-key.ts
@@ -1,5 +1,5 @@
-import { xxhashAsU8a } from "@polkadot/util-crypto";
import { u8aConcat, u8aToHex } from "@polkadot/util";
+import { xxhashAsU8a } from "@polkadot/util-crypto";
import yargs from "yargs";
const debug = require("debug")("main");
diff --git a/src/tools/upgrade-network.ts b/src/tools/upgrade-network.ts
index 8229067..73c17ed 100644
--- a/src/tools/upgrade-network.ts
+++ b/src/tools/upgrade-network.ts
@@ -1,29 +1,28 @@
-/*
- Performs a runtime upgrade through sudo or council (requires polkadot v0.9.32+)
-
-Ex: ./node_modules/.bin/ts-node-transpile-only src/tools/upgrade-network.ts \
- --url ws://localhost:9944 \
- --send-proposal-as council-external \
- --collective-threshold 3 \
- --proxy \
- --account-priv-key \
-*/
-import yargs from "yargs";
-import fs from "fs";
-import "@polkadot/api-augment";
+// Performs a runtime upgrade through sudo or council (requires polkadot v0.9.32+)
+//
+// Ex: bun src/tools/upgrade-network.ts \
+// --url ws://localhost:9944 \
+// --send-proposal-as council-external \
+// --collective-threshold 3 \
+// --proxy \
+// --account-priv-key
import "@moonbeam-network/api-augment";
+import "@polkadot/api-augment";
+
import { Keyring } from "@polkadot/api";
import { KeyringPair } from "@polkadot/keyring/types";
-import { getApiFor, NETWORK_YARGS_OPTIONS } from "../utils/networks";
import { blake2AsHex } from "@polkadot/util-crypto";
-import { hexToU8a } from "@polkadot/util";
+import fs from "fs";
+import yargs from "yargs";
+
+import { ALITH_PRIVATE_KEY } from "../utils/constants.ts";
import {
monitorSubmittedExtrinsic,
waitBlocks,
waitForAllMonitoredExtrinsics,
-} from "../utils/monitoring";
-import { maybeProxyCall } from "../utils/transactions";
-import { ALITH_PRIVATE_KEY } from "../utils/constants";
+} from "../utils/monitoring.ts";
+import { getApiFor, NETWORK_YARGS_OPTIONS } from "../utils/networks.ts";
+import { maybeProxyCall } from "../utils/transactions.ts";
const argv = yargs(process.argv.slice(2))
.usage("Usage: $0")
@@ -51,6 +50,11 @@ const argv = yargs(process.argv.slice(2))
demandOption: false,
conflicts: ["send-proposal-as", "collective-threshold"],
},
+ enact: {
+ type: "boolean",
+ demandOption: false,
+ conflicts: ["sudo", "send-proposal-as", "collective-threshold"],
+ },
alith: {
type: "boolean",
demandOption: false,
@@ -90,7 +94,7 @@ async function main() {
const collectiveThreshold =
argv["collective-threshold"] ||
Math.ceil(((await api.query.openTechCommitteeCollective.members()).length * 3) / 5);
- const proposalAmount = api.consts.democracy.minimumDeposit;
+ const proposalAmount = api.consts?.democracy?.minimumDeposit || 0n;
let account: KeyringPair;
let nonce;
@@ -110,13 +114,19 @@ async function main() {
console.log(`Unexpected runtime ${codeHash} size: ${code.length}`);
process.exit(1);
}
- console.log(`Using runtime wasm with size: ${code.length}`);
+ console.log(`Using runtime wasm with size: ${code.length} [hash: ${codeHash}]`);
const tryProxy = (call) => {
return maybeProxyCall(api, call, argv["proxy"], argv["proxy-type"]);
};
- if (argv["sudo"]) {
+ if (argv["enact"]) {
+ await tryProxy(api.tx.system.applyAuthorizedUpgrade(codeHex)).signAndSend(
+ account,
+ { nonce: nonce++ },
+ monitorSubmittedExtrinsic(api, { id: "sudo" }),
+ );
+ } else if (argv["sudo"]) {
const proposal = api.tx.system.setCode(codeHex);
await tryProxy(api.tx.sudo.sudo(proposal)).signAndSend(
account,
diff --git a/src/tools/vote-motion.ts b/src/tools/vote-motion.ts
index 131bf7d..f0e3597 100644
--- a/src/tools/vote-motion.ts
+++ b/src/tools/vote-motion.ts
@@ -1,23 +1,22 @@
-/*
- Performs a runtime upgrade through sudo or council (requires polkadot v0.9.32+)
-
-Ex: ./node_modules/.bin/ts-node-transpile-only src/tools/upgrade-network.ts \
- --url ws://localhost:9944 \
- --send-proposal-as council-external \
- --collective-threshold 3 \
- --proxy \
- --account-priv-key \
-*/
-import yargs from "yargs";
-import fs from "fs";
-import "@polkadot/api-augment";
+// Performs a runtime upgrade through sudo or council (requires polkadot v0.9.32+)
+//
+// Ex: bun src/tools/upgrade-network.ts \
+// --url ws://localhost:9944 \
+// --send-proposal-as council-external \
+// --collective-threshold 3 \
+// --proxy \
+// --account-priv-key \
import "@moonbeam-network/api-augment";
+import "@polkadot/api-augment";
+
import { Keyring } from "@polkadot/api";
import { KeyringPair } from "@polkadot/keyring/types";
-import { getApiFor, NETWORK_YARGS_OPTIONS } from "../utils/networks";
-import { monitorSubmittedExtrinsic, waitForAllMonitoredExtrinsics } from "../utils/monitoring";
-import { maybeProxyCall } from "../utils/transactions";
-import { ALITH_PRIVATE_KEY } from "../utils/constants";
+import yargs from "yargs";
+
+import { ALITH_PRIVATE_KEY } from "../utils/constants.ts";
+import { monitorSubmittedExtrinsic, waitForAllMonitoredExtrinsics } from "../utils/monitoring.ts";
+import { getApiFor, NETWORK_YARGS_OPTIONS } from "../utils/networks.ts";
+import { maybeProxyCall } from "../utils/transactions.ts";
const argv = yargs(process.argv.slice(2))
.usage("Usage: $0")
diff --git a/src/utils/monitoring.ts b/src/utils/monitoring.ts
index 5d2990e..5337b96 100644
--- a/src/utils/monitoring.ts
+++ b/src/utils/monitoring.ts
@@ -1,23 +1,23 @@
-import type { ApiPromise } from "@polkadot/api";
-import type { Extrinsic, BlockHash, EventRecord } from "@polkadot/types/interfaces";
-import type { Block } from "@polkadot/types/interfaces/runtime/types";
-import { Data, GenericEthereumAccountId, Option, u128, u8, bool } from "@polkadot/types";
+import "@moonbeam-network/api-augment";
+import "@polkadot/api-augment";
+
+import { ApiDecoration } from "@polkadot/api/types";
+import { Data, GenericEthereumAccountId, Option, u128 } from "@polkadot/types";
+import { Codec, ITuple } from "@polkadot/types-codec/types";
+import { PalletIdentityRegistration } from "@polkadot/types/lookup";
import { ISubmittableResult } from "@polkadot/types/types";
-import type { EthereumTransactionTransactionV2 } from "@polkadot/types/lookup";
-import type { LegacyTransaction } from "@polkadot/types/interfaces/eth";
import { u8aToString } from "@polkadot/util";
import { ethereumEncode } from "@polkadot/util-crypto";
-import { mapExtrinsics, TxWithEventAndFee } from "./types";
-
-import "@polkadot/api-augment";
-import "@moonbeam-network/api-augment";
-
import chalk from "chalk";
import Debug from "debug";
-import { PalletIdentityRegistration } from "@polkadot/types/lookup";
-import { Codec, ITuple } from "@polkadot/types-codec/types";
-import { promiseConcurrent } from "./functions";
-import { ApiDecoration } from "@polkadot/api/types";
+
+import { promiseConcurrent } from "./functions.ts";
+import { mapExtrinsics, TxWithEventAndFee } from "./types.ts";
+
+import type { ApiPromise } from "@polkadot/api";
+import type { Extrinsic, BlockHash, EventRecord } from "@polkadot/types/interfaces";
+import type { Block } from "@polkadot/types/interfaces/runtime/types";
+import type { LegacyTransaction } from "@polkadot/types/interfaces/eth";
const debug = Debug("monitoring");
export const printTokens = (api: ApiPromise, tokens: bigint, decimals = 2, pad = 9) => {
@@ -37,6 +37,7 @@ export interface BlockDetails {
records: EventRecord[];
txWithEvents: TxWithEventAndFee[];
weightPercentage: number;
+ storageUsed: number;
}
// TODO: Improve with cache and eviction
@@ -108,11 +109,11 @@ export const getAccountIdentities = async (
const superIdentityOpts =
validSuperOfs.length > 0
? await api.rpc.state.queryStorageAt