diff --git a/src/base.ts b/src/base.ts index b4f526d..00ab9e5 100644 --- a/src/base.ts +++ b/src/base.ts @@ -1,5 +1,6 @@ import { Command, Flags } from "@oclif/core"; import { CommandError } from "@oclif/core/lib/interfaces"; +import { SignerType, SignerTypes } from "./helpers"; import { NetworkName, NetworkNames } from "./networks"; type TxError = { error: { reason: string } }; @@ -7,7 +8,7 @@ type TxError = { error: { reason: string } }; export abstract class BlockchainCommand extends Command { static globalFlags = { network: Flags.enum({ - description: "The network to use", + description: "The network to use.", options: NetworkNames, default: "testnet", }), @@ -19,11 +20,11 @@ export abstract class TransactionCommand extends BlockchainCommand { ...super.globalFlags, address: Flags.string({ description: "The account address to use.", - exclusive: ["ledger"], }), - ledger: Flags.boolean({ - description: "Use Ledger wallet to sign transactions", - exclusive: ["address"], + signer: Flags.enum({ + description: "The method for signing transactions.", + options: SignerTypes, + default: "keystore", }), }; diff --git a/src/commands/node/list.ts b/src/commands/node/list.ts index b366bff..a090667 100644 --- a/src/commands/node/list.ts +++ b/src/commands/node/list.ts @@ -1,6 +1,7 @@ import { Flags } from "@oclif/core"; +import { Result } from "ethers/lib/utils"; import { BlockchainCommand } from "../../base"; -import { getContract, getProvider, normalizeHex, normalizeRecords } from "../../helpers"; +import { getAll, getContract, getProvider, normalizeHex, normalizeRecords } from "../../helpers"; export default class NodeList extends BlockchainCommand { static description = "Lists content nodes on the Armada Network."; @@ -11,6 +12,7 @@ export default class NodeList extends BlockchainCommand { operator: Flags.string({ description: "Filter by operator ID.", helpValue: "ID" }), skip: Flags.integer({ description: "The number of results to skip.", helpValue: "N", default: 0 }), size: Flags.integer({ description: "The number of results to list.", helpValue: "N", default: 100 }), + page: Flags.integer({ description: "The contract call paging size.", helpValue: "N", default: 100 }), }; public async run(): Promise { @@ -18,7 +20,10 @@ export default class NodeList extends BlockchainCommand { const provider = await getProvider(flags.network); const nodes = await getContract(flags.network, "nodes", provider); const operatorId = normalizeHex(flags.operator); - const records = await nodes.getNodes(operatorId, flags.topology, flags.skip, flags.size); - console.log(normalizeRecords(records)); + const blockTag = await provider.getBlockNumber(); + const results: Result[] = await getAll(flags.page, async (i, n) => { + return await nodes.getNodes(operatorId, flags.topology, i, n, { blockTag }); + }); + console.log(normalizeRecords(results.slice(flags.skip, flags.skip + flags.size))); } } diff --git a/src/commands/project/content.ts b/src/commands/project/content.ts index 73f41a3..ada0dbf 100644 --- a/src/commands/project/content.ts +++ b/src/commands/project/content.ts @@ -23,7 +23,7 @@ export default class ProjectContent extends TransactionCommand { this.error("URL and SHA must be specified"); } - const signer = await getSigner(flags.network, flags.address, flags.ledger); + const signer = await getSigner(flags.network, flags.address, flags.signer); const projects = await getContract(flags.network, "projects", signer); const projectId = normalizeHex(args.ID); const bundleSha = normalizeHex(args.SHA); diff --git a/src/commands/project/create.ts b/src/commands/project/create.ts index b631fdd..edc483b 100644 --- a/src/commands/project/create.ts +++ b/src/commands/project/create.ts @@ -20,7 +20,7 @@ export default class ProjectCreate extends TransactionCommand { this.error("URL and SHA must be specified together"); } - const signer = await getSigner(flags.network, flags.address, flags.ledger); + const signer = await getSigner(flags.network, flags.address, flags.signer); const projects = await getContract(flags.network, "projects", signer); const address = await signer.getAddress(); const bundleSha = normalizeHex(args.SHA); diff --git a/src/commands/project/delete.ts b/src/commands/project/delete.ts index 503c96d..087eb03 100644 --- a/src/commands/project/delete.ts +++ b/src/commands/project/delete.ts @@ -11,7 +11,7 @@ export default class ProjectDelete extends TransactionCommand { public async run(): Promise { const { args, flags } = await this.parse(ProjectDelete); - const signer = await getSigner(flags.network, flags.address, flags.ledger); + const signer = await getSigner(flags.network, flags.address, flags.signer); const projects = await getContract(flags.network, "projects", signer); const projectId = normalizeHex(args.ID); CliUx.ux.action.start("- Submitting transaction"); diff --git a/src/commands/project/list.ts b/src/commands/project/list.ts index 5e80cd9..18bc9d5 100644 --- a/src/commands/project/list.ts +++ b/src/commands/project/list.ts @@ -1,6 +1,7 @@ import { Flags } from "@oclif/core"; +import { Result } from "ethers/lib/utils"; import { BlockchainCommand } from "../../base"; -import { getContract, getProvider, normalizeHex, normalizeRecords } from "../../helpers"; +import { getAll, getContract, getProvider, normalizeHex, normalizeRecords } from "../../helpers"; export default class ProjectList extends BlockchainCommand { static description = "Lists projects on the Armada Network."; @@ -10,17 +11,21 @@ export default class ProjectList extends BlockchainCommand { owner: Flags.string({ description: "Filter by owner address.", helpValue: "ADDR" }), skip: Flags.integer({ description: "The number of results to skip.", helpValue: "N", default: 0 }), size: Flags.integer({ description: "The number of results to list.", helpValue: "N", default: 100 }), + page: Flags.integer({ description: "The contract call paging size.", helpValue: "N", default: 100 }), }; public async run(): Promise { const { flags } = await this.parse(ProjectList); const provider = await getProvider(flags.network); const projects = await getContract(flags.network, "projects", provider); - let records = await projects.getProjects(flags.skip, flags.size); + const owner = normalizeHex(flags.owner); + const blockTag = await provider.getBlockNumber(); + let results: Result[] = await getAll(flags.page, async (i, n) => { + return await projects.getProjects(i, n, { blockTag }); + }); if (flags.owner) { - const owner = normalizeHex(flags.owner); - records = records.filter((value: { owner: string }) => value.owner.toLowerCase() === owner.toLowerCase()); + results = results.filter((v) => v.owner.toLowerCase() === owner.toLowerCase()); } - console.log(normalizeRecords(records)); + console.log(normalizeRecords(results.slice(flags.skip, flags.skip + flags.size))); } } diff --git a/src/helpers.ts b/src/helpers.ts index b77917a..ad4237f 100644 --- a/src/helpers.ts +++ b/src/helpers.ts @@ -7,6 +7,9 @@ import { listWallets, loadWallet } from "./keystore"; import { LedgerSigner } from "./ledger"; import { ContractName, Contracts, NetworkName, Networks } from "./networks"; +export type SignerType = "keystore" | "ledger"; +export const SignerTypes: SignerType[] = ["keystore", "ledger"]; + const Chains: Record = { 0: "mainnet", 5: "goerli", @@ -46,15 +49,19 @@ export async function getProvider(network: NetworkName): Promise { return provider; } -export async function getSigner(network: NetworkName, address: string | undefined, ledger: boolean): Promise { +export async function getSigner( + network: NetworkName, + address: string | undefined, + signer: SignerType +): Promise { const url = Networks[network].url; const provider = new ethers.providers.JsonRpcProvider(url); - let signer: Signer; - if (ledger) { + let wallet: Signer; + if (signer === "ledger") { console.log("Make sure the Ledger wallet is unlocked and the Ethereum application is open"); - signer = new LedgerSigner(provider); - const address = await signer.getAddress(); + wallet = new LedgerSigner(provider); + const address = await wallet.getAddress(); console.log("Using Ledger wallet. Wallet address: ", address); } else { if (!address) { @@ -88,11 +95,11 @@ export async function getSigner(network: NetworkName, address: string | undefine password = res.password as string; } - signer = await loadWallet(address, password); - signer = signer.connect(provider); + wallet = await loadWallet(address, password); + wallet = wallet.connect(provider); } - return signer; + return wallet; } export async function getContract( @@ -132,3 +139,16 @@ export async function decodeEvents(receipt: TransactionReceipt, contract: Contra export async function decodeEvent(receipt: TransactionReceipt, contract: Contract, event: string): Promise { return (await decodeEvents(receipt, contract, event))[0]; } + +// Returns all results of a paged function call (a function that accepts skip and size parameters). +export async function getAll(page: number, call: (skip: number, size: number) => Promise): Promise { + const results: Result[] = []; + while (page > 0) { + const records = await call(results.length, page); + results.push(...records); + if (records.length !== page) { + break; + } + } + return results; +}