diff --git a/src/base.ts b/src/base.ts index 3dc1767..a6600cd 100644 --- a/src/base.ts +++ b/src/base.ts @@ -14,6 +14,8 @@ export abstract class BlockchainCommand extends Command { default: "testnet", }), }; + + static enableJsonFlag = true; } export abstract class TransactionCommand extends BlockchainCommand { diff --git a/src/commands/node/list.ts b/src/commands/node/list.ts index 67f6a87..dc981d3 100644 --- a/src/commands/node/list.ts +++ b/src/commands/node/list.ts @@ -15,8 +15,9 @@ export default class NodeList extends BlockchainCommand { page: Flags.integer({ description: "The contract call paging size.", helpValue: "N", default: 100 }), }; - public async run(): Promise { + public async run(): Promise[]> { const { flags } = await this.parse(NodeList); + const provider = await getProvider(flags.network); const nodes = await getContract(flags.network, "nodes", provider); const operatorId = normalizeHex(flags.operator); @@ -24,6 +25,10 @@ export default class NodeList extends BlockchainCommand { 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))); + const records = results.slice(flags.skip, flags.skip + flags.size); + + const output = normalizeRecords(records); + if (!flags.json) console.log(output); + return output; } } diff --git a/src/commands/node/show.ts b/src/commands/node/show.ts index 781aea2..1aeb74d 100644 --- a/src/commands/node/show.ts +++ b/src/commands/node/show.ts @@ -8,12 +8,16 @@ export default class NodeShow extends BlockchainCommand { static usage = "<%= command.id %> ID"; static args: Arg[] = [{ name: "ID", description: "The ID of the node to show.", required: true }]; - public async run(): Promise { + public async run(): Promise> { const { args, flags } = await this.parse(NodeShow); + const provider = await getProvider(flags.network); const nodes = await getContract(flags.network, "nodes", provider); const nodeId = normalizeHex(args.ID); const record = await nodes.getNode(nodeId); - console.log(normalizeRecord(record)); + + const output = normalizeRecord(record); + if (!flags.json) console.log(output); + return output; } } diff --git a/src/commands/project/content.ts b/src/commands/project/content.ts index ada0dbf..645686d 100644 --- a/src/commands/project/content.ts +++ b/src/commands/project/content.ts @@ -17,7 +17,7 @@ export default class ProjectContent extends TransactionCommand { { name: "SHA", description: "The SHA-256 checksum of the bundle." }, ]; - public async run(): Promise { + public async run(): Promise> { const { args, flags } = await this.parse(ProjectContent); if (args.URL === undefined || args.SHA === undefined) { this.error("URL and SHA must be specified"); @@ -30,11 +30,14 @@ export default class ProjectContent extends TransactionCommand { CliUx.ux.action.start("- Submitting transaction"); const tx = await projects.setProjectContent(projectId, args.URL, bundleSha); CliUx.ux.action.stop("done"); - console.log(`> ${getTxUrl(tx)}`); + this.log(`> ${getTxUrl(tx)}`); CliUx.ux.action.start("- Processing transaction"); const receipt = await tx.wait(); CliUx.ux.action.stop("done"); const event = await decodeEvent(receipt, projects, "ProjectContentChanged"); - console.log(normalizeRecord(event)); + + const output = normalizeRecord(event); + if (!flags.json) console.log(output); + return output; } } diff --git a/src/commands/project/create.ts b/src/commands/project/create.ts index cf1d4e5..97d8768 100644 --- a/src/commands/project/create.ts +++ b/src/commands/project/create.ts @@ -17,7 +17,7 @@ export default class ProjectCreate extends TransactionCommand { owner: Flags.string({ description: "[default: caller] The owner for the new project.", helpValue: "ADDR" }), }; - public async run(): Promise { + public async run(): Promise> { const { args, flags } = await this.parse(ProjectCreate); if (!!args.URL !== !!args.SHA) { this.error("URL and SHA must be specified together"); @@ -30,11 +30,14 @@ export default class ProjectCreate extends TransactionCommand { CliUx.ux.action.start("- Submitting transaction"); const tx = await projects.createProject([owner, args.NAME, args.EMAIL, args.URL, bundleSha]); CliUx.ux.action.stop("done"); - console.log(`> ${getTxUrl(tx)}`); + this.log(`> ${getTxUrl(tx)}`); CliUx.ux.action.start("- Processing transaction"); const receipt = await tx.wait(); CliUx.ux.action.stop("done"); const event = await decodeEvent(receipt, projects, "ProjectCreated"); - console.log(normalizeRecord(event)); + + const output = normalizeRecord(event); + if (!flags.json) console.log(output); + return output; } } diff --git a/src/commands/project/delete.ts b/src/commands/project/delete.ts index 087eb03..d58916e 100644 --- a/src/commands/project/delete.ts +++ b/src/commands/project/delete.ts @@ -9,19 +9,23 @@ export default class ProjectDelete extends TransactionCommand { static usage = "<%= command.id %> ID"; static args: Arg[] = [{ name: "ID", description: "The ID of the project to delete.", required: true }]; - public async run(): Promise { + public async run(): Promise> { const { args, flags } = await this.parse(ProjectDelete); + 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"); const tx = await projects.deleteProject(projectId); CliUx.ux.action.stop("done"); - console.log(`> ${getTxUrl(tx)}`); + this.log(`> ${getTxUrl(tx)}`); CliUx.ux.action.start("- Processing transaction"); const receipt = await tx.wait(); CliUx.ux.action.stop("done"); const event = await decodeEvent(receipt, projects, "ProjectDeleted"); - console.log(normalizeRecord(event)); + + const output = normalizeRecord(event); + if (!flags.json) console.log(output); + return output; } } diff --git a/src/commands/project/list.ts b/src/commands/project/list.ts index 1e6b527..67fd40d 100644 --- a/src/commands/project/list.ts +++ b/src/commands/project/list.ts @@ -14,8 +14,9 @@ export default class ProjectList extends BlockchainCommand { page: Flags.integer({ description: "The contract call paging size.", helpValue: "N", default: 100 }), }; - public async run(): Promise { + public async run(): Promise[]> { const { flags } = await this.parse(ProjectList); + const provider = await getProvider(flags.network); const projects = await getContract(flags.network, "projects", provider); const owner = normalizeHex(flags.owner); @@ -26,6 +27,9 @@ export default class ProjectList extends BlockchainCommand { if (flags.owner) { results = results.filter((v) => v.owner.toLowerCase() === owner.toLowerCase()); } - console.log(normalizeRecords(results.slice(flags.skip, flags.skip + flags.size))); + + const output = normalizeRecords(results.slice(flags.skip, flags.skip + flags.size)); + if (!flags.json) console.log(output); + return output; } } diff --git a/src/commands/project/owner.ts b/src/commands/project/owner.ts index 8ce01e0..912d912 100644 --- a/src/commands/project/owner.ts +++ b/src/commands/project/owner.ts @@ -13,19 +13,23 @@ export default class ProjectOwner extends TransactionCommand { { name: "ADDR", description: "The address of the new project owner.", required: true }, ]; - public async run(): Promise { + public async run(): Promise> { const { args, flags } = await this.parse(ProjectOwner); + 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"); const tx = await projects.setProjectOwner(projectId, args.ADDR); CliUx.ux.action.stop("done"); - console.log(`> ${getTxUrl(tx)}`); + this.log(`> ${getTxUrl(tx)}`); CliUx.ux.action.start("- Processing transaction"); const receipt = await tx.wait(); CliUx.ux.action.stop("done"); const event = await decodeEvent(receipt, projects, "ProjectOwnerChanged"); - console.log(normalizeRecord(event)); + + const output = normalizeRecord(event); + if (!flags.json) console.log(output); + return output; } } diff --git a/src/commands/project/props.ts b/src/commands/project/props.ts index 32249b1..762a490 100644 --- a/src/commands/project/props.ts +++ b/src/commands/project/props.ts @@ -13,8 +13,9 @@ export default class ProjectProps extends TransactionCommand { { name: "EMAIL", description: "The new email for admin notifications.", required: true }, ]; - public async run(): Promise { + public async run(): Promise> { const { args, flags } = await this.parse(ProjectProps); + const signer = await getSigner(flags.network, flags.address, flags.signer); const projects = await getContract(flags.network, "projects", signer); const projectId = normalizeHex(args.ID); @@ -26,6 +27,9 @@ export default class ProjectProps extends TransactionCommand { const receipt = await tx.wait(); CliUx.ux.action.stop("done"); const event = await decodeEvent(receipt, projects, "ProjectPropsChanged"); - console.log(normalizeRecord(event)); + + const output = normalizeRecord(event); + if (!flags.json) console.log(output); + return output; } } diff --git a/src/commands/project/show.ts b/src/commands/project/show.ts index d9e51e6..23ae663 100644 --- a/src/commands/project/show.ts +++ b/src/commands/project/show.ts @@ -8,12 +8,16 @@ export default class ProjectShow extends BlockchainCommand { static usage = "<%= command.id %> ID"; static args: Arg[] = [{ name: "ID", description: "The ID of the project to show.", required: true }]; - public async run(): Promise { + public async run(): Promise> { const { args, flags } = await this.parse(ProjectShow); + const provider = await getProvider(flags.network); const projects = await getContract(flags.network, "projects", provider); const projectId = normalizeHex(args.ID); const record = await projects.getProject(projectId); - console.log(normalizeRecord(record)); + + const output = normalizeRecord(record); + if (!flags.json) console.log(output); + return output; } } diff --git a/src/commands/reservation/create.ts b/src/commands/reservation/create.ts index 1f79b40..707a1e3 100644 --- a/src/commands/reservation/create.ts +++ b/src/commands/reservation/create.ts @@ -19,7 +19,7 @@ export default class ReservationCreate extends TransactionCommand { norenew: Flags.boolean({ description: "Don't renew these reservations in next epoch." }), }; - public async run(): Promise { + public async run(): Promise[]> { const { args, flags, raw } = await this.parse(ReservationCreate); if (!flags.spot && !!flags.norenew) { this.error("--norenew requires --spot"); @@ -37,14 +37,16 @@ export default class ReservationCreate extends TransactionCommand { const prices = nodeIds.map(() => parseUnits("1", 18)); CliUx.ux.action.start("- Submitting transaction"); const slot = { last: !!flags.spot, next: !flags.norenew }; - console.log([projectId, nodeIds, prices, slot]); const tx = await reservations.createReservations(projectId, nodeIds, prices, slot); CliUx.ux.action.stop("done"); - console.log(`> ${getTxUrl(tx)}`); + this.log(`> ${getTxUrl(tx)}`); CliUx.ux.action.start("- Processing transaction"); const receipt = await tx.wait(); CliUx.ux.action.stop("done"); const events = await decodeEvents(receipt, reservations, "ReservationCreated"); - console.log(normalizeRecords(events)); + + const output = normalizeRecords(events); + if (!flags.json) console.log(output); + return output; } } diff --git a/src/commands/reservation/delete.ts b/src/commands/reservation/delete.ts index 4a11875..ebfd373 100644 --- a/src/commands/reservation/delete.ts +++ b/src/commands/reservation/delete.ts @@ -14,7 +14,7 @@ export default class ReservationDelete extends TransactionCommand { { name: "IDS", description: "The IDs of the nodes to release from the project.", required: true }, ]; - public async run(): Promise { + public async run(): Promise[]> { const { args, flags, raw } = await this.parse(ReservationDelete); // Parse vararg IDS const nodeIds = raw @@ -30,11 +30,14 @@ export default class ReservationDelete extends TransactionCommand { const slot = { last: false, next: true }; const tx = await reservations.deleteReservations(projectId, nodeIds, slot); CliUx.ux.action.stop("done"); - console.log(`> ${getTxUrl(tx)}`); + this.log(`> ${getTxUrl(tx)}`); CliUx.ux.action.start("- Processing transaction"); const receipt = await tx.wait(); CliUx.ux.action.stop("done"); const events = await decodeEvents(receipt, reservations, "ReservationDeleted"); - console.log(normalizeRecords(events)); + + const output = normalizeRecords(events); + if (!flags.json) console.log(output); + return output; } } diff --git a/src/commands/reservation/list.ts b/src/commands/reservation/list.ts index 9ac72ff..4d36765 100644 --- a/src/commands/reservation/list.ts +++ b/src/commands/reservation/list.ts @@ -15,8 +15,9 @@ export default class ReservationList extends BlockchainCommand { page: Flags.integer({ description: "The contract call paging size.", helpValue: "N", default: 100 }), }; - public async run(): Promise { + public async run(): Promise[]> { const { args, flags } = await this.parse(ReservationList); + const provider = await getProvider(flags.network); const reservations = await getContract(flags.network, "reservations", provider); const projectId = normalizeHex(args.ID); @@ -24,6 +25,9 @@ export default class ReservationList extends BlockchainCommand { const results: Result[] = await getAll(flags.page, async (i, n) => { return await reservations.getReservations(projectId, i, n, { blockTag }); }); - console.log(normalizeRecords(results.slice(flags.skip, flags.skip + flags.size))); + + const output = normalizeRecords(results.slice(flags.skip, flags.skip + flags.size)); + if (!flags.json) console.log(output); + return output; } } diff --git a/src/helpers.ts b/src/helpers.ts index 6f8b3c3..17d0b4e 100644 --- a/src/helpers.ts +++ b/src/helpers.ts @@ -27,10 +27,7 @@ export function normalizeRecord(r: Record | Result): Record isNaN(Number(k))) .map((k) => { - if (r[k] instanceof BigNumber) { - return [k, normalizeBigNumber(r[k])]; - } - return [k, r[k]]; + return [k, normalizeRecordValue(r[k])]; }) ); } @@ -39,6 +36,16 @@ export function normalizeRecords(rs: (Record | Result)[]): Reco return rs.map((r) => normalizeRecord(r)); } +function normalizeRecordValue(val: unknown): unknown { + if (Array.isArray(val)) { + return val.map(normalizeRecordValue); + } + if (val instanceof BigNumber) { + return normalizeBigNumber(val); + } + return val; +} + // Prepends 0x, and replaces undefined and empty with 256-bit zero hash. export function normalizeHex(s: string | undefined): string { if (!s?.length) {