From 8f1c71a530a9a783721818efb908cab187248e10 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 10 Mar 2026 14:16:28 +0000 Subject: [PATCH 1/3] Initial plan From 60b5561407d0ca846e26ef0868f259f2376296a4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 10 Mar 2026 14:25:26 +0000 Subject: [PATCH 2/3] Add nova transactions command Co-authored-by: SynthLuvr <131367121+SynthLuvr@users.noreply.github.com> --- api.ts | 11 +++++++++++ commands/transactions.ts | 37 +++++++++++++++++++++++++++++++++++++ entrypoint.ts | 1 + responses/index.ts | 1 + responses/transactions.ts | 17 +++++++++++++++++ tests/transactions.test.ts | 34 ++++++++++++++++++++++++++++++++++ 6 files changed, 101 insertions(+) create mode 100644 commands/transactions.ts create mode 100644 responses/transactions.ts create mode 100644 tests/transactions.test.ts diff --git a/api.ts b/api.ts index 2da0002..ffda677 100644 --- a/api.ts +++ b/api.ts @@ -17,6 +17,7 @@ import { LinkCreatedResponse, RateLimited, TokenCreatedResponse, + TransactionsResponse, ValidationErrorResponse, } from "./responses/index.js"; @@ -51,6 +52,16 @@ class NovaApiClient { return Ok(validatedResponse); } + async getTransactions(address: string) { + const endpoint = AccountsEndpoints[this.network]; + const response = await this.http + .get(`${endpoint}/transactions`, { searchParams: { address } }) + .json(); + const validatedResponse = TransactionsResponse(response); + if (validatedResponse instanceof type.errors) return parseError(response); + return Ok(validatedResponse); + } + async login(email: string, publicKey: string) { const endpoint = AuthEndpoints[this.network]; const response = await this.http.post(`${endpoint}/login`, { diff --git a/commands/transactions.ts b/commands/transactions.ts new file mode 100644 index 0000000..3c18957 --- /dev/null +++ b/commands/transactions.ts @@ -0,0 +1,37 @@ +import { Ok } from "ts-handling"; +import { api } from "../api.js"; +import program, { logExit, printOk } from "../cli.js"; +import { getAddressFromTokenOrKey } from "./address.js"; + +const getTransactions = async (address?: string) => { + const resolvedAddress = address + ? Ok(address) + : await getAddressFromTokenOrKey(); + if (!resolvedAddress.ok) return resolvedAddress; + + const response = await api.getTransactions(resolvedAddress.data); + if (!response.ok) return response; + return Ok(response.data.contents.transactions); +}; + +program + .command("transactions") + .description("Shows transactions for an account") + .option("-j, --json", "Output results as JSON") + .option("-t, --toon", "Output results as TOON") + .argument("[address]", "The account address to get transactions for") + .action(async (address: string | undefined) => { + const transactions = await getTransactions(address); + if (!transactions.ok) return logExit(transactions.error); + + const humanReadable = + transactions.data.length === 0 + ? "No transactions" + : transactions.data + .map((tx) => `${tx.type} ${tx.amount} (${tx.txId})`) + .join("\n"); + + printOk({ transactions: transactions.data }, humanReadable); + }); + +export { getTransactions }; diff --git a/entrypoint.ts b/entrypoint.ts index 68b92a3..2207ab5 100644 --- a/entrypoint.ts +++ b/entrypoint.ts @@ -8,6 +8,7 @@ import "./commands/import-key.js"; import "./commands/login.js"; import "./commands/send.js"; import "./commands/token.js"; +import "./commands/transactions.js"; import "./commands/withdraw.js"; try { diff --git a/responses/index.ts b/responses/index.ts index 1299342..15fb66e 100644 --- a/responses/index.ts +++ b/responses/index.ts @@ -4,4 +4,5 @@ export * from "./generate.js"; export * from "./link-created.js"; export * from "./rate-limited.js"; export * from "./token-created.js"; +export * from "./transactions.js"; export * from "./validation-error.js"; diff --git a/responses/transactions.ts b/responses/transactions.ts new file mode 100644 index 0000000..38255ba --- /dev/null +++ b/responses/transactions.ts @@ -0,0 +1,17 @@ +import { type } from "arktype"; + +const TransactionsResponse = type({ + code: "200", + contents: { + transactions: type({ + amount: "string", + time: "number", + txId: "string", + type: "'deposit' | 'transfer' | 'withdrawal'", + "from?": "string", + "to?": "string", + }).array(), + }, +}); + +export { TransactionsResponse }; diff --git a/tests/transactions.test.ts b/tests/transactions.test.ts new file mode 100644 index 0000000..8ca940f --- /dev/null +++ b/tests/transactions.test.ts @@ -0,0 +1,34 @@ +import { describe, expect } from "vitest"; +import { it } from "./base.js"; + +describe("nova transactions", () => { + it("prints no transactions for a fresh account", async ({ nova }) => { + const stdout = await nova(["transactions"]); + expect(stdout).toBe("No transactions"); + }); + + it("prints no transactions for a fresh account in JSON format", async ({ + nova, + }) => { + const stdout = await nova(["-j", "transactions"]); + const result = JSON.parse(stdout); + expect(result.status).toBe("ok"); + expect(result.result.transactions).toEqual([]); + }); + + it("prints no transactions for a given address", async ({ nova }) => { + const address = await nova(["address"]); + const stdout = await nova(["transactions", address]); + expect(stdout).toBe("No transactions"); + }); + + it("prints no transactions for a given address in JSON format", async ({ + nova, + }) => { + const address = await nova(["address"]); + const stdout = await nova(["-j", "transactions", address]); + const result = JSON.parse(stdout); + expect(result.status).toBe("ok"); + expect(result.result.transactions).toEqual([]); + }); +}); From 82d322dfde13e6cf388ce1138098e5566bc9d3ad Mon Sep 17 00:00:00 2001 From: SynthLuvr <131367121+SynthLuvr@users.noreply.github.com> Date: Tue, 10 Mar 2026 15:29:17 +0100 Subject: [PATCH 3/3] lint --- package.json | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/package.json b/package.json index 7fb21c2..ce9218f 100644 --- a/package.json +++ b/package.json @@ -19,9 +19,7 @@ "bin": { "nova": "bin/nova.mjs" }, - "files": [ - "dist" - ], + "files": ["dist"], "scripts": { "prebuild": "pnpm del -rf dist", "build": "pnpm tsc",