From b352c240a8a783940716f7d8e0e4633605d91bd5 Mon Sep 17 00:00:00 2001 From: Crystalin Date: Mon, 1 Aug 2022 16:50:14 -0400 Subject: [PATCH] Adds support for --proxied-account --- README.md | 8 +++ src/commands/commonArgs.ts | 39 ++++++++++ src/commands/createAndSendCommand.ts | 36 ++++------ src/commands/getTransactionDataCommand.ts | 14 ++-- src/commands/signCommand.ts | 2 +- src/commands/submitTxCommand.ts | 2 +- src/commands/types.ts | 59 +++++++++++++++ src/commands/verifyCommand.ts | 4 +- src/commands/voteCouncilCommand.ts | 32 ++++----- src/commands/voteTechCommitteeCommand.ts | 20 ++++-- src/methods/createAndSendTx.ts | 33 +++++---- src/methods/getTransactionData.ts | 9 +-- src/methods/types.ts | 32 +++++---- src/methods/voteCouncil.ts | 87 +++++++++++------------ src/methods/voteTechCommittee.ts | 85 +++++++++++----------- test/dev-node.ts | 34 ++------- 16 files changed, 294 insertions(+), 202 deletions(-) create mode 100644 src/commands/commonArgs.ts create mode 100644 src/commands/types.ts diff --git a/README.md b/README.md index 50f1642..2481273 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,14 @@ Then, in the two other windows run : You will see the updated parachainBond in the apps (chain state) if connecting to ws://localhost:34102 +### Proxy + +It is possible to proxy transaction by using: +`npm run cli voteCouncil -- --network --ws
--proxied-account [--proxy-type ]` + +* proxied-account: The account that is **proxied** +* proxy-type: The type of proxy (default: null, will use the first proxy account matching) + ### Specific Actions Some specific actions are provided as a preconfigured feature: diff --git a/src/commands/commonArgs.ts b/src/commands/commonArgs.ts new file mode 100644 index 0000000..329eccb --- /dev/null +++ b/src/commands/commonArgs.ts @@ -0,0 +1,39 @@ + +import { authorizedChains } from "../methods/utils"; +import { TxWrapperArgs } from "./types"; + +export const commonWrapperArgs: {[Property in keyof TxWrapperArgs]: any} = { + sudo: { + describe: "activates sudo mode", + type: "boolean" as "boolean", + default: false, + demandOption: false, + }, + "proxied-account": { + describe: "address of the proxied account", + type: "string" as "string", + }, + "proxy-type": { + describe: "Type of proxy", + type: "string" as "string", + }, +}; + +export const commonNetworkArgs = { + network: { + describe: "the network on which you want to send the tx", + type: "string" as "string", + choices: authorizedChains, + demandOption: true, + }, + ws: { + describe: "websocket address of the endpoint on which to connect", + type: "string" as "string", + demandOption: true, + } +} + +export const commonArgs = { + ...commonWrapperArgs, + ...commonNetworkArgs +} \ No newline at end of file diff --git a/src/commands/createAndSendCommand.ts b/src/commands/createAndSendCommand.ts index 7051c39..2ec8575 100644 --- a/src/commands/createAndSendCommand.ts +++ b/src/commands/createAndSendCommand.ts @@ -1,21 +1,10 @@ import { Argv } from "yargs"; -import { CreateAndSendArgs } from "../methods/types"; import { createAndSendTxPrompt } from "../methods/createAndSendTx"; import { exit } from "../methods/utils"; -import { ALITH, authorizedChains, BALTATHAR } from "../methods/utils"; +import { commonArgs } from "./commonArgs"; +import { CreateAndSendArgs, NetworkArgs, TxWrapperArgs } from "./types"; export const createTxOptions = { - network: { - describe: "the network on which you want to send the tx", - type: "string" as "string", - choices: authorizedChains, - demandOption: true, - }, - ws: { - describe: "websocket address of the endpoint on which to connect", - type: "string" as "string", - demandOption: true, - }, address: { describe: "address of the sender", type: "string" as "string", @@ -31,12 +20,6 @@ export const createTxOptions = { type: "string" as "string", demandOption: true, }, - sudo: { - describe: "activates sudo mode", - type: "boolean" as "boolean", - default: false, - demandOption: false, - }, nonce: { describe: "nonce to use", type: "number" as "number", @@ -54,9 +37,12 @@ export const createAndSendTxCommand = { command: "createAndSendTx", describe: "creates a transaction payload, prompts for signature and sends it", builder: (yargs: Argv) => { - return yargs.options(createTxOptions); + return yargs.options({ + ...commonArgs, + ...createTxOptions + }); }, - handler: async (argv: CreateAndSendArgs) => { + handler: async (argv: CreateAndSendArgs & NetworkArgs & TxWrapperArgs) => { if (!argv["params"]) { console.log(`Missing params`); return; @@ -90,9 +76,15 @@ export const createAndSendTxCommand = { tx: argv.tx, params, address: argv.address, - sudo: argv.sudo, immortality: argv.immortality, }, + { + sudo: argv.sudo, + proxy: argv["proxied-account"] ? { + account: argv["proxied-account"], + type: argv["proxy-type"] + } : undefined + }, { ws: argv.ws, network: argv.network } ); exit(); diff --git a/src/commands/getTransactionDataCommand.ts b/src/commands/getTransactionDataCommand.ts index b692619..2be4791 100644 --- a/src/commands/getTransactionDataCommand.ts +++ b/src/commands/getTransactionDataCommand.ts @@ -1,16 +1,19 @@ import { Argv } from "yargs"; -import { CreateAndSendArgs } from "../methods/types"; import { getTransactionData } from "../methods/getTransactionData"; import { createTxOptions } from "./createAndSendCommand"; -import { checkArgvList } from "../methods/utils"; +import { commonNetworkArgs } from "./commonArgs"; +import { CreateAndSendArgs, NetworkArgs, TxWrapperArgs } from "./types"; export const getTransactionDataCommand = { command: "getTransactionData", description: "creates a transaction payload and resolves", builder: (yargs: Argv) => { - return yargs.options(createTxOptions); + return yargs.options({ + ...commonNetworkArgs, + ...createTxOptions + }); }, - handler: async (argv: CreateAndSendArgs) => { + handler: async (argv: CreateAndSendArgs & NetworkArgs & TxWrapperArgs) => { if (!argv["params"]) { console.log(`Missing params`); return; @@ -32,7 +35,8 @@ export const getTransactionDataCommand = { return; } return await getTransactionData( - { tx: argv.tx, params: JSON.parse(argv.params), address: argv.address, sudo: argv.sudo }, + { tx: argv.tx, params: JSON.parse(argv.params), address: argv.address }, + { sudo: argv.sudo, proxy: argv["proxied-account"] ? { account: argv["proxied-account"], type: argv["proxy-type"] } : undefined }, { ws: argv.ws, network: argv.network } ); }, diff --git a/src/commands/signCommand.ts b/src/commands/signCommand.ts index 6280f39..df7b6bf 100644 --- a/src/commands/signCommand.ts +++ b/src/commands/signCommand.ts @@ -1,5 +1,5 @@ import { Argv } from "yargs"; -import { SignArgs, SignPromptArgs } from "../methods/types"; +import { SignArgs, SignPromptArgs } from "./types"; import { sign } from "../methods/sign"; import { isNetworkType } from "../methods/utils"; diff --git a/src/commands/submitTxCommand.ts b/src/commands/submitTxCommand.ts index 61135ec..ee02609 100644 --- a/src/commands/submitTxCommand.ts +++ b/src/commands/submitTxCommand.ts @@ -1,5 +1,5 @@ import { Argv } from "yargs"; -import { SendTxArgs } from "../methods/types"; +import { SendTxArgs } from "./types"; import { submitPreSignedTx } from "../methods/submitPreSignedTx"; export const submitTxCommand = { diff --git a/src/commands/types.ts b/src/commands/types.ts new file mode 100644 index 0000000..5daf7f3 --- /dev/null +++ b/src/commands/types.ts @@ -0,0 +1,59 @@ +// Command Args +export interface SignArgs extends SignPromptArgs { + message?: string; +} +export interface SignPromptArgs { + type?: string; + "private-key"?: string; + derivePath: string; +} +export interface VerifyArgs { + message?: string; + signature?: string; + "public-key"?: string; + type?: string; +} +export interface SendTxArgs { + ws?: string; + "tx-data"?: string; +} + +export interface CreateAndSendArgs { + address?: string; + tx?: string; + params?: string; + nonce?: number; + immortality: boolean; +} +export interface VoteCouncilArgs { + address?: string; +} + +export type TxParam = boolean | string | number | { [key: string]: any }; + + +// Methods args +export interface TxArgs { + nonce?: number; + tx: string; + params: TxParam[]; + address: string; + immortality?: boolean; +} + +export interface TxWrapperArgs { + sudo?: boolean; + "proxied-account"?: string; + "proxy-type"?: "Any" | string; +} + +export interface NetworkArgs { + ws?: string; + network?: string; +} + +export type NetworkType = "ethereum" | "sr25519"; + +export interface Vote { + yes: boolean; +} diff --git a/src/commands/verifyCommand.ts b/src/commands/verifyCommand.ts index f1d7195..343c293 100644 --- a/src/commands/verifyCommand.ts +++ b/src/commands/verifyCommand.ts @@ -1,7 +1,7 @@ import { Argv } from "yargs"; -import { VerifyArgs } from "../methods/types"; +import { VerifyArgs } from "./types"; import { verify } from "../methods/verify"; -import { ALITH, isNetworkType } from "../methods/utils"; +import { isNetworkType } from "../methods/utils"; export const verifyOptions = { type: { diff --git a/src/commands/voteCouncilCommand.ts b/src/commands/voteCouncilCommand.ts index af8d1fb..d2421e3 100644 --- a/src/commands/voteCouncilCommand.ts +++ b/src/commands/voteCouncilCommand.ts @@ -1,21 +1,10 @@ import { Argv } from "yargs"; -import { VoteCouncilArgs } from "../methods/types"; import { exit } from "../methods/utils"; -import { ALITH, authorizedChains } from "../methods/utils"; import { voteCouncilPrompt } from "../methods/voteCouncil"; +import { commonArgs } from "./commonArgs"; +import { NetworkArgs, TxWrapperArgs, VoteCouncilArgs } from "./types"; -export const specificTxOptions = { - network: { - describe: "the network on which you want to send the tx", - type: "string" as "string", - choices: authorizedChains, - demandOption: true, - }, - ws: { - describe: "websocket address of the endpoint on which to connect", - type: "string" as "string", - demandOption: true, - }, +export const specificTxArgs = { address: { describe: "address of the sender", type: "string" as "string", @@ -27,9 +16,12 @@ export const voteCouncilCommand = { command: "voteCouncil", describe: "creates a vote council payload, prompts for signature and sends it", builder: (yargs: Argv) => { - return yargs.options(specificTxOptions); + return yargs.options({ + ...commonArgs, + ...specificTxArgs + }); }, - handler: async (argv: VoteCouncilArgs) => { + handler: async (argv: VoteCouncilArgs & NetworkArgs & TxWrapperArgs) => { if (!argv["address"]) { console.log(`Missing address`); return; @@ -42,7 +34,13 @@ export const voteCouncilCommand = { console.log(`Missing network`); return; } - await voteCouncilPrompt(argv.address, { ws: argv.ws, network: argv.network }); + await voteCouncilPrompt(argv.address, { + sudo: argv.sudo, + proxy: argv["proxied-account"] ? { + account: argv["proxied-account"], + type: argv["proxy-type"] + } : undefined + }, { ws: argv.ws, network: argv.network }); exit(); }, }; diff --git a/src/commands/voteTechCommitteeCommand.ts b/src/commands/voteTechCommitteeCommand.ts index daaa4e8..d618816 100644 --- a/src/commands/voteTechCommitteeCommand.ts +++ b/src/commands/voteTechCommitteeCommand.ts @@ -1,16 +1,20 @@ import { Argv } from "yargs"; -import { VoteCouncilArgs } from "../methods/types"; import { voteTechCommitteePrompt } from "../methods/voteTechCommittee"; -import { specificTxOptions } from "./voteCouncilCommand"; +import { specificTxArgs } from "./voteCouncilCommand"; import { exit } from "../methods/utils"; +import { commonArgs } from "./commonArgs"; +import { NetworkArgs, TxWrapperArgs, VoteCouncilArgs } from "./types"; export const voteTechCommitteeCommand = { command: "voteTechCommittee", describe: "creates a tech committee vote payload, prompts for signature and sends it", builder: (yargs: Argv) => { - return yargs.options(specificTxOptions); + return yargs.options({ + ...commonArgs, + ...specificTxArgs + }); }, - handler: async (argv: VoteCouncilArgs) => { + handler: async (argv: VoteCouncilArgs & NetworkArgs & TxWrapperArgs) => { if (!argv["address"]) { console.log(`Missing address`); return; @@ -23,7 +27,13 @@ export const voteTechCommitteeCommand = { console.log(`Missing network`); return; } - await voteTechCommitteePrompt(argv.address, { ws: argv.ws, network: argv.network }); + await voteTechCommitteePrompt(argv.address, { + sudo: argv.sudo, + proxy: argv["proxied-account"] ? { + account: argv["proxied-account"], + type: argv["proxy-type"] + } : undefined + }, { ws: argv.ws, network: argv.network }); exit(); }, }; diff --git a/src/methods/createAndSendTx.ts b/src/methods/createAndSendTx.ts index 80af684..fbb36a1 100644 --- a/src/methods/createAndSendTx.ts +++ b/src/methods/createAndSendTx.ts @@ -9,15 +9,17 @@ import chalk from "chalk" import { moonbeamChains } from "./utils"; import { SignerResult, SubmittableExtrinsic } from "@polkadot/api/types"; -import { NetworkArgs, TxArgs, TxParam } from "./types"; +import { NetworkOpt, TxOpt, TxWrapperOpt } from "./types"; export async function createAndSendTx( - txArgs: TxArgs, - networkArgs: NetworkArgs, + txOpt: TxOpt, + txWrapperOpt: TxWrapperOpt, + networkOpt: NetworkOpt, signatureFunction: (payload: string) => Promise<`0x${string}`> ) { - const { tx, params, address, sudo, nonce } = txArgs; - const { ws, network } = networkArgs; + const { tx, params, address, nonce } = txOpt; + const { sudo, proxy } = txWrapperOpt + const { ws, network } = networkOpt; const [sectionName, methodName] = tx.split("."); let api: ApiPromise; @@ -31,12 +33,17 @@ export async function createAndSendTx( provider: new WsProvider(ws), }); } - let txExtrinsic: SubmittableExtrinsic<"promise", ISubmittableResult>; + let txExtrinsic: SubmittableExtrinsic<"promise", ISubmittableResult> = api.tx[sectionName][methodName](...params); if (sudo) { - txExtrinsic = await api.tx.sudo.sudo(api.tx[sectionName][methodName](...params)); - } else { - txExtrinsic = await api.tx[sectionName][methodName](...params); + txExtrinsic = api.tx.sudo.sudo(txExtrinsic); + } + if (proxy && proxy.account) { + txExtrinsic = api.tx.proxy.proxy( + proxy.account, + proxy.type || null, + txExtrinsic); } + txExtrinsic = await txExtrinsic; // explicit display of name, args const { method: { args, method, section } } = txExtrinsic; @@ -58,7 +65,7 @@ export async function createAndSendTx( }); }, }; - let options = txArgs.immortality ? { signer, era: 0, nonce } : { signer, nonce }; + let options = txOpt.immortality ? { signer, era: 0, nonce } : { signer, nonce }; const keyring = new Keyring({ type: "ethereum" }); const genesisAccount = await keyring.addFromUri( @@ -80,7 +87,7 @@ export async function createAndSendTx( if (error?.isModule) { const { docs, name, section } = api.registry.findMetaError(error.asModule); console.log('\t', `${chalk.red(`${section}.${name}`)}`, `${docs}`); - } else if (section=="system" && method == "ExtrinsicSuccess") { + } else if (section == "system" && method == "ExtrinsicSuccess") { console.log('\t', chalk.green(`${section}.${method}`), data.toString()); } else { console.log('\t', `${section}.${method}`, data.toString()); @@ -98,8 +105,8 @@ export async function createAndSendTx( }); } -export async function createAndSendTxPrompt(txArgs: TxArgs, networkArgs: NetworkArgs) { - return createAndSendTx(txArgs, networkArgs, async (payload: string) => { +export async function createAndSendTxPrompt(txOpt: TxOpt, txWrapperOpt: TxWrapperOpt, networkOpt: NetworkOpt) { + return createAndSendTx(txOpt, txWrapperOpt, networkOpt, async (payload: string) => { const response = await prompts({ type: "text", name: "signature", diff --git a/src/methods/getTransactionData.ts b/src/methods/getTransactionData.ts index 31b61e8..9804c9b 100644 --- a/src/methods/getTransactionData.ts +++ b/src/methods/getTransactionData.ts @@ -1,10 +1,11 @@ import { createAndSendTx } from "./createAndSendTx"; -import { NetworkArgs, TxArgs } from "./types"; +import { NetworkOpt, TxOpt, TxWrapperOpt } from "./types"; -export async function getTransactionData(txArgs: TxArgs, networkArgs: NetworkArgs) { +export async function getTransactionData(txOpt: TxOpt, txWrapperOpt: TxWrapperOpt, networkOpt: NetworkOpt) { return createAndSendTx( - txArgs, - networkArgs, + txOpt, + txWrapperOpt, + networkOpt, // Here we don't want to send the signature, // just see the payload so we return empty signature async (_: string) => { diff --git a/src/methods/types.ts b/src/methods/types.ts index 8134595..77eb97e 100644 --- a/src/methods/types.ts +++ b/src/methods/types.ts @@ -1,50 +1,54 @@ // Command Args -export interface SignArgs extends SignPromptArgs { +export interface SignOpt extends SignPromptOpt { message?: string; } -export interface SignPromptArgs { +export interface SignPromptOpt { type?: string; "private-key"?: string; derivePath: string; } -export interface VerifyArgs { +export interface VerifyOpt { message?: string; signature?: string; "public-key"?: string; type?: string; } -export interface SendTxArgs { +export interface SendTxOpt { ws?: string; "tx-data"?: string; } -export interface CreateAndSendArgs { - network?: string; - ws?: string; + +export interface CreateAndSendOpt { address?: string; tx?: string; params?: string; - sudo: boolean; nonce?: number; immortality: boolean; } -export interface VoteCouncilArgs { - network?: string; - ws?: string; +export interface VoteCouncilOpt { address?: string; } export type TxParam = boolean | string | number | { [key: string]: any }; +export interface ProxyOpt { + account: string; + type?: "Any" | string; +} // Methods args -export interface TxArgs { +export interface TxOpt { nonce?: number; tx: string; params: TxParam[]; address: string; - sudo?: boolean; immortality?: boolean; } -export interface NetworkArgs { + +export interface TxWrapperOpt { + sudo?: boolean; + proxy?: ProxyOpt; +} +export interface NetworkOpt { ws: string; network: string; } diff --git a/src/methods/voteCouncil.ts b/src/methods/voteCouncil.ts index 839972a..e2890b8 100644 --- a/src/methods/voteCouncil.ts +++ b/src/methods/voteCouncil.ts @@ -1,7 +1,7 @@ import { ApiPromise } from "@polkadot/api"; import prompts from "prompts"; -import { moonbeamChains, retrieveApi } from "./utils"; -import { NetworkArgs, Vote } from "./types"; +import { retrieveApi } from "./utils"; +import { NetworkOpt, ProxyOpt, TxWrapperOpt, Vote } from "./types"; import { createAndSendTx } from "./createAndSendTx"; export async function retrieveMotions(api: ApiPromise): Promise< @@ -37,24 +37,22 @@ export async function retrieveMotions(api: ApiPromise): Promise< const proposal = preimageData.toHuman() && preimageData.unwrap().isAvailable ? api.registry.createType( - "Proposal", - preimageData.unwrap().asAvailable.data.toU8a(true) - ) + "Proposal", + preimageData.unwrap().asAvailable.data.toU8a(true) + ) : null; if (proposal) { - data.text = `[${vote.index}] ${motion.method} - ${proposal.toHuman().section}.${ - proposal.toHuman().method - }: ${Object.keys((proposal.toHuman() as any).args) - .map((argKey: any) => { - const text = `${argKey}:${(proposal.toHuman() as any).args[argKey]}`; - return `${ - text.length > 100 - ? `${text.substring(0, 7)}..${text.substring(text.length - 4)}` - : text - }`; - }) - .join(`, `)}`; + data.text = `[${vote.index}] ${motion.method} - ${proposal.toHuman().section}.${proposal.toHuman().method + }: ${Object.keys((proposal.toHuman() as any).args) + .map((argKey: any) => { + const text = `${argKey}:${(proposal.toHuman() as any).args[argKey]}`; + return `${text.length > 100 + ? `${text.substring(0, 7)}..${text.substring(text.length - 4)}` + : text + }`; + }) + .join(`, `)}`; } } else { data.text = `[${vote.index}] ${motion.section}.${motion.method}`; @@ -64,8 +62,8 @@ export async function retrieveMotions(api: ApiPromise): Promise< ); } -export async function voteCouncilPrompt(address: string, networkArgs: NetworkArgs) { - const api = await retrieveApi(networkArgs.network, networkArgs.ws); +export async function voteCouncilPrompt(address: string, txWrapperOpt: TxWrapperOpt, networkOpt: NetworkOpt) { + const api = await retrieveApi(networkOpt.network, networkOpt.ws); // Retrieve list of motions const motions = await retrieveMotions(api); @@ -100,9 +98,8 @@ export async function voteCouncilPrompt(address: string, networkArgs: NetworkArg let vote: Vote = await prompts({ type: "select", name: "yes", - message: `Pick a vote for [Motion #${selectedMotion.index}] ${ - selectedMotion.text || `Not available - hash ${selectedMotion.hash}` - }`, + message: `Pick a vote for [Motion #${selectedMotion.index}] ${selectedMotion.text || `Not available - hash ${selectedMotion.hash}` + }`, choices: [ { title: "Yes", value: true }, { title: "No", value: false }, @@ -116,32 +113,32 @@ export async function voteCouncilPrompt(address: string, networkArgs: NetworkArg } // If more than one motion, use batch utility - const txArgs = + const txOpt = votes.length === 1 ? { - address, - tx: `councilCollective.vote`, - params: [ - motions[motionSelection.index[0]].hash, - motions[motionSelection.index[0]].index, - votes[0].yes, - ], - } + address, + tx: `councilCollective.vote`, + params: [ + motions[motionSelection.index[0]].hash, + motions[motionSelection.index[0]].index, + votes[0].yes, + ], + } : { - address, - tx: `utility.batch`, - params: [ - votes.map((vote: Vote, i: number) => { - let selectedMotion = motions[motionSelection.index[i]]; - return api.tx.councilCollective.vote( - selectedMotion.hash, - selectedMotion.index, - vote.yes - ); - }), - ], - }; - return createAndSendTx(txArgs, networkArgs, async (payload: string) => { + address, + tx: `utility.batch`, + params: [ + votes.map((vote: Vote, i: number) => { + let selectedMotion = motions[motionSelection.index[i]]; + return api.tx.councilCollective.vote( + selectedMotion.hash, + selectedMotion.index, + vote.yes + ); + }), + ], + }; + return createAndSendTx(txOpt, txWrapperOpt, networkOpt, async (payload: string) => { const response = await prompts({ type: "text", name: "signature", diff --git a/src/methods/voteTechCommittee.ts b/src/methods/voteTechCommittee.ts index 1a093bf..62c8939 100644 --- a/src/methods/voteTechCommittee.ts +++ b/src/methods/voteTechCommittee.ts @@ -1,7 +1,7 @@ import { ApiPromise } from "@polkadot/api"; import prompts from "prompts"; import { retrieveApi } from "./utils"; -import { NetworkArgs, Vote } from "./types"; +import { NetworkOpt, TxWrapperOpt, Vote } from "./types"; import { createAndSendTx } from "./createAndSendTx"; export async function retrieveMotions(api: ApiPromise): Promise< @@ -41,24 +41,22 @@ export async function retrieveMotions(api: ApiPromise): Promise< const proposal = preimageData.toHuman() && preimageData.unwrap().isAvailable ? api.registry.createType( - "Proposal", - preimageData.unwrap().asAvailable.data.toU8a(true) - ) + "Proposal", + preimageData.unwrap().asAvailable.data.toU8a(true) + ) : null; if (proposal) { - data.text = `[${vote.index}] ${motion.method} - ${proposal.section}.${ - proposal.method - }: ${Object.keys((proposal.toHuman() as any).args) - .map((argKey) => { - const text = `${argKey}:${(proposal.toHuman() as any).args[argKey]}`; - return `${ - text.length > 100 - ? `${text.substring(0, 7)}..${text.substring(text.length - 4)}` - : text - }`; - }) - .join(`, `)}`; + data.text = `[${vote.index}] ${motion.method} - ${proposal.section}.${proposal.method + }: ${Object.keys((proposal.toHuman() as any).args) + .map((argKey) => { + const text = `${argKey}:${(proposal.toHuman() as any).args[argKey]}`; + return `${text.length > 100 + ? `${text.substring(0, 7)}..${text.substring(text.length - 4)}` + : text + }`; + }) + .join(`, `)}`; } } else { data.text = `[${vote.index}] ${motion.section}.${motion.method}`; @@ -68,8 +66,8 @@ export async function retrieveMotions(api: ApiPromise): Promise< ); } -export async function voteTechCommitteePrompt(address: string, networkArgs: NetworkArgs) { - const api = await retrieveApi(networkArgs.network, networkArgs.ws); +export async function voteTechCommitteePrompt(address: string, txWrapperOpt: TxWrapperOpt, networkOpt: NetworkOpt) { + const api = await retrieveApi(networkOpt.network, networkOpt.ws); // Retrieve list of motions const motions = await retrieveMotions(api); @@ -104,9 +102,8 @@ export async function voteTechCommitteePrompt(address: string, networkArgs: Netw let vote: Vote = await prompts({ type: "select", name: "yes", - message: `Pick a vote for [Motion #${selectedMotion.index}] ${ - selectedMotion.text || `Not available - hash ${selectedMotion.hash}` - }`, + message: `Pick a vote for [Motion #${selectedMotion.index}] ${selectedMotion.text || `Not available - hash ${selectedMotion.hash}` + }`, choices: [ { title: "Yes", value: true }, { title: "No", value: false }, @@ -120,32 +117,32 @@ export async function voteTechCommitteePrompt(address: string, networkArgs: Netw } // If more than one motion, use batch utility - const txArgs = + const txOpt = votes.length === 1 ? { - address, - tx: `techCommitteeCollective.vote`, - params: [ - motions[motionSelection.index[0]].hash, - motions[motionSelection.index[0]].index, - votes[0].yes, - ], - } + address, + tx: `techCommitteeCollective.vote`, + params: [ + motions[motionSelection.index[0]].hash, + motions[motionSelection.index[0]].index, + votes[0].yes, + ], + } : { - address, - tx: `utility.batch`, - params: [ - votes.map((vote: Vote, i: number) => { - let selectedMotion = motions[motionSelection.index[i]]; - return api.tx.techCommitteeCollective.vote( - selectedMotion.hash, - selectedMotion.index, - vote.yes - ); - }), - ], - }; - return createAndSendTx(txArgs, networkArgs, async (payload: string) => { + address, + tx: `utility.batch`, + params: [ + votes.map((vote: Vote, i: number) => { + let selectedMotion = motions[motionSelection.index[i]]; + return api.tx.techCommitteeCollective.vote( + selectedMotion.hash, + selectedMotion.index, + vote.yes + ); + }), + ], + }; + return createAndSendTx(txOpt, txWrapperOpt, networkOpt, async (payload: string) => { const response = await prompts({ type: "text", name: "signature", diff --git a/test/dev-node.ts b/test/dev-node.ts index 249473c..aeadc64 100644 --- a/test/dev-node.ts +++ b/test/dev-node.ts @@ -18,33 +18,6 @@ const debug = require("debug")("test:dev-node"); const paraName = "moonbase-0.17.0"; const paraDocker = "purestake/moonbeam:v0.17.0"; -export async function findAvailablePorts() { - const availablePorts = await Promise.all( - [null, null, null].map(async (_, index) => { - let selectedPort = 0; - let port = 1024 + index * 20000 + (process.pid % 20000); - let endingPort = 65535; - while (!selectedPort && port < endingPort) { - const inUse = await tcpPortUsed.check(port, "127.0.0.1"); - if (!inUse) { - selectedPort = port; - } - port++; - } - if (!selectedPort) { - throw new Error(`No available port`); - } - return selectedPort; - }) - ); - - return { - p2pPort: availablePorts[0], - rpcPort: availablePorts[1], - wsPort: availablePorts[2], - }; -} - // Stores if the node has already started. // It is used when a test file contains multiple describeDevMoonbeam. Those are // executed within the same PID and so would generate a race condition if started @@ -52,7 +25,6 @@ export async function findAvailablePorts() { let nodeStarted = false; // This will start a moonbeam dev node, only 1 at a time (check every 100ms). -// This will prevent race condition on the findAvailablePorts which uses the PID of the process export async function startMoonbeamDevNode(withWasm?: boolean): Promise<{ p2pPort: number; rpcPort: number; @@ -66,7 +38,11 @@ export async function startMoonbeamDevNode(withWasm?: boolean): Promise<{ }); } nodeStarted = true; - const { p2pPort, rpcPort, wsPort } = await findAvailablePorts(); + const { p2pPort, rpcPort, wsPort } = { + p2pPort: 33333, + rpcPort: 9933, + wsPort: 9944, + } let cmd: string; if (process.env.LOCAL_BUILD) {