diff --git a/README.md b/README.md index 1844a98..0008f17 100644 --- a/README.md +++ b/README.md @@ -258,6 +258,14 @@ EXAMPLES: genlayer appeal 0x1234... --bond 500gen ``` +#### Transaction Trace + +Inspect execution traces for debugging: + +```bash +genlayer transactions trace [--round N] [--rpc URL] +``` + #### Account Management View and manage your account. diff --git a/src/commands/transactions/index.ts b/src/commands/transactions/index.ts index b41f332..8130999 100644 --- a/src/commands/transactions/index.ts +++ b/src/commands/transactions/index.ts @@ -2,6 +2,7 @@ import {Command} from "commander"; import {TransactionStatus, TransactionHash} from "genlayer-js/types"; import {ReceiptAction, ReceiptOptions} from "./receipt"; import {AppealAction, AppealOptions, AppealBondOptions} from "./appeal"; +import {TraceAction, TraceOptions} from "./trace"; function parseIntOption(value: string, fallback: number): number { const parsed = parseInt(value, 10); @@ -45,5 +46,15 @@ export function initializeTransactionsCommands(program: Command) { await appealAction.appealBond({txId, ...options}); }); + program + .command("trace ") + .description("Get execution trace for a transaction (return data, stdout, stderr, GenVM logs)") + .option("--round ", "Consensus round number (default: 0)", (value) => parseIntOption(value, 0), 0) + .option("--rpc ", "RPC URL for the network") + .action(async (txId: TransactionHash, options: TraceOptions) => { + const traceAction = new TraceAction(); + await traceAction.trace({txId, ...options}); + }); + return program; } diff --git a/src/commands/transactions/trace.ts b/src/commands/transactions/trace.ts new file mode 100644 index 0000000..13db39e --- /dev/null +++ b/src/commands/transactions/trace.ts @@ -0,0 +1,42 @@ +import {BaseAction} from "../../lib/actions/BaseAction"; +import {TransactionHash} from "genlayer-js/types"; + +export interface TraceParams { + txId: TransactionHash; + round?: number; + rpc?: string; +} + +export interface TraceOptions extends Omit {} + +export class TraceAction extends BaseAction { + constructor() { + super(); + } + + async trace({ + txId, + round = 0, + rpc, + }: TraceParams): Promise { + const client = await this.getClient(rpc, true); + this.startSpinner(`Fetching execution trace for ${txId} (round: ${round})...`); + + try { + const result = await client.request({ + method: "gen_dbg_traceTransaction" as any, + params: [{txID: txId, round}], + }); + + const trace = (result as any); + if (!trace) { + this.failSpinner("No trace found", `No execution trace found for transaction ${txId}`); + return; + } + + this.succeedSpinner("Execution trace retrieved", trace); + } catch (error) { + this.failSpinner("Error retrieving execution trace", error); + } + } +}