Skip to content

Commit a84d7d1

Browse files
authored
Merge pull request #1362 from 0xVida/main
feat: add compact JSON mode, remote actions JSON support, error envelopes and replay diff schema validation
2 parents 7d1847c + 0c9b8ef commit a84d7d1

19 files changed

Lines changed: 844 additions & 105 deletions

docs/jq-examples.md

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
# Querying JSON Reports with jq
2+
3+
This document provides practical recipes for parsing, filtering, and automating tasks using `soroban-debug` JSON output combined with the `jq` utility.
4+
5+
By default, all JSON outputs generated by the debugger are compact for efficient piping and automation. To pretty-print JSON for local readability, you can pass the global `--pretty` flag:
6+
```bash
7+
# Compact JSON (ideal for piping)
8+
soroban-debug run --contract contract.wasm --function test --output json
9+
10+
# Pretty-printed JSON (ideal for humans)
11+
soroban-debug run --contract contract.wasm --function test --output json --pretty
12+
```
13+
14+
---
15+
16+
## 1. Inspecting Execution Status & Result
17+
18+
To verify whether a run succeeded and extract the return value:
19+
```bash
20+
soroban-debug run --contract contract.wasm --function my_func --output json | jq '.status'
21+
# Outputs: "success" or "error"
22+
23+
soroban-debug run --contract contract.wasm --function my_func --output json | jq '.result.result'
24+
# Outputs the raw return value (e.g. "42")
25+
```
26+
27+
---
28+
29+
## 2. Resource Budget Analysis
30+
31+
Extract CPU instruction and memory usage from a debugger run:
32+
```bash
33+
soroban-debug run --contract contract.wasm --function my_func --output json \
34+
| jq '.result.budget | {cpu: .cpu_instructions, mem: .memory_bytes}'
35+
```
36+
37+
Output:
38+
```json
39+
{
40+
"cpu": 12450,
41+
"mem": 2048
42+
}
43+
```
44+
45+
---
46+
47+
## 3. Retrieving Contract Events
48+
49+
Extract specific topics or data from emitted execution events:
50+
```bash
51+
soroban-debug run --contract contract.wasm --function my_func --output json \
52+
| jq '.result.events[] | {topics: .topics, data: .data}'
53+
```
54+
55+
---
56+
57+
## 4. Querying Replay Diff Reports
58+
59+
Replay an execution and inspect whether the storage state remains identical:
60+
```bash
61+
soroban-debug replay trace.json --contract contract.wasm --format json \
62+
| jq '.result.storage_diff | {modified: .modified, unchanged: .unchanged_count}'
63+
```
64+
65+
Check if execution flow (call sequence) remains identical:
66+
```bash
67+
soroban-debug replay trace.json --contract contract.wasm --format json \
68+
| jq '.result.flow_diff.identical'
69+
```
70+
71+
---
72+
73+
## 5. Structured Error Handling
74+
75+
On execution failure, you can gracefully query the error code, category, and suggestions:
76+
```bash
77+
soroban-debug run --contract contract.wasm --function my_func --output json \
78+
| jq '.error | {code: .code, category: .category, suggestion: .suggestion}'
79+
```
80+
81+
Output:
82+
```json
83+
{
84+
"code": "debugger::invalid_function",
85+
"category": "parser_failure",
86+
"suggestion": "Action: Ensure the function name is spelled exactly as exported by the contract..."
87+
}
88+
```

man/man1/soroban-debug-remote.1

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
.SH NAME
55
remote \- Connect to remote debug server
66
.SH SYNOPSIS
7-
\fBremote\fR <\fB\-r\fR|\fB\-\-remote\fR> [\fB\-t\fR|\fB\-\-token\fR] [\fB\-c\fR|\fB\-\-contract\fR] [\fB\-f\fR|\fB\-\-function\fR] [\fB\-\-tls\-cert\fR] [\fB\-\-tls\-key\fR] [\fB\-\-tls\-ca\fR] [\fB\-\-session\-label\fR] [\fB\-a\fR|\fB\-\-args\fR] [\fB\-\-connect\-timeout\-ms\fR] [\fB\-\-timeout\-ms\fR] [\fB\-\-inspect\-timeout\-ms\fR] [\fB\-\-storage\-timeout\-ms\fR] [\fB\-\-retry\-attempts\fR] [\fB\-\-retry\-base\-delay\-ms\fR] [\fB\-\-retry\-max\-delay\-ms\fR] [\fB\-h\fR|\fB\-\-help\fR] [\fIremote and server\fR]
7+
\fBremote\fR <\fB\-r\fR|\fB\-\-remote\fR> [\fB\-t\fR|\fB\-\-token\fR] [\fB\-c\fR|\fB\-\-contract\fR] [\fB\-f\fR|\fB\-\-function\fR] [\fB\-\-tls\-cert\fR] [\fB\-\-tls\-key\fR] [\fB\-\-tls\-ca\fR] [\fB\-\-session\-label\fR] [\fB\-a\fR|\fB\-\-args\fR] [\fB\-\-connect\-timeout\-ms\fR] [\fB\-\-timeout\-ms\fR] [\fB\-\-inspect\-timeout\-ms\fR] [\fB\-\-storage\-timeout\-ms\fR] [\fB\-\-retry\-attempts\fR] [\fB\-\-retry\-base\-delay\-ms\fR] [\fB\-\-retry\-max\-delay\-ms\fR] [\fB\-\-format\fR] [\fB\-h\fR|\fB\-\-help\fR] [\fIremote and server\fR]
88
.SH DESCRIPTION
99
Connect to remote debug server
1010
.SH OPTIONS
@@ -85,6 +85,19 @@ Maximum delay in milliseconds between retry attempts.
8585

8686
Default: 2 000 ms.
8787
.TP
88+
\fB\-\-format\fR \fI<FORMAT>\fR [default: pretty]
89+
Output format for remote command (pretty, json)
90+
.br
91+
92+
.br
93+
\fIPossible values:\fR
94+
.RS 14
95+
.IP \(bu 2
96+
pretty
97+
.IP \(bu 2
98+
json
99+
.RE
100+
.TP
88101
\fB\-h\fR, \fB\-\-help\fR
89102
Print help (see a summary with \*(Aq\-h\*(Aq)
90103
.SH "Remote and Server"

man/man1/soroban-debug-replay.1

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
.SH NAME
55
replay \- Replay execution from a previously exported trace file
66
.SH SYNOPSIS
7-
\fBreplay\fR [\fB\-c\fR|\fB\-\-contract\fR] [\fB\-\-replay\-until\fR] [\fB\-o\fR|\fB\-\-output\fR] [\fB\-v\fR|\fB\-\-verbose\fR] [\fB\-h\fR|\fB\-\-help\fR] <\fITRACE_FILE\fR>
7+
\fBreplay\fR [\fB\-c\fR|\fB\-\-contract\fR] [\fB\-\-replay\-until\fR] [\fB\-o\fR|\fB\-\-output\fR] [\fB\-\-format\fR] [\fB\-v\fR|\fB\-\-verbose\fR] [\fB\-h\fR|\fB\-\-help\fR] <\fITRACE_FILE\fR>
88
.SH DESCRIPTION
99
Replay execution from a previously exported trace file
1010
.SH OPTIONS
@@ -18,6 +18,19 @@ Stop replay at step N (0\-based index into call sequence)
1818
\fB\-o\fR, \fB\-\-output\fR \fI<OUTPUT>\fR
1919
Output file for the diff report (default: stdout)
2020
.TP
21+
\fB\-\-format\fR \fI<FORMAT>\fR [default: pretty]
22+
Output format for replay command (pretty, json)
23+
.br
24+
25+
.br
26+
\fIPossible values:\fR
27+
.RS 14
28+
.IP \(bu 2
29+
pretty
30+
.IP \(bu 2
31+
json
32+
.RE
33+
.TP
2134
\fB\-v\fR, \fB\-\-verbose\fR
2235
Show verbose output during replay
2336
.TP

man/man1/soroban-debug.1

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
.SH NAME
55
soroban\-debug \- A debugger for Soroban smart contracts
66
.SH SYNOPSIS
7-
\fBsoroban\-debug\fR [\fB\-q\fR|\fB\-\-quiet\fR] [\fB\-v\fR|\fB\-\-verbose\fR] [\fB\-\-no\-banner\fR] [\fB\-\-history\-file\fR] [\fB\-\-budget\-trend\fR] [\fB\-\-trend\-contract\fR] [\fB\-\-trend\-function\fR] [\fB\-\-trend\-regression\-threshold\-pct\fR] [\fB\-\-trend\-regression\-lookback\fR] [\fB\-\-trend\-regression\-smoothing\fR] [\fB\-\-version\-verbose\fR] [\fB\-\-list\-functions\fR] [\fB\-h\fR|\fB\-\-help\fR] [\fB\-V\fR|\fB\-\-version\fR] [\fIsubcommands\fR]
7+
\fBsoroban\-debug\fR [\fB\-q\fR|\fB\-\-quiet\fR] [\fB\-v\fR|\fB\-\-verbose\fR] [\fB\-\-no\-banner\fR] [\fB\-\-history\-file\fR] [\fB\-\-budget\-trend\fR] [\fB\-\-trend\-contract\fR] [\fB\-\-trend\-function\fR] [\fB\-\-trend\-regression\-threshold\-pct\fR] [\fB\-\-trend\-regression\-lookback\fR] [\fB\-\-trend\-regression\-smoothing\fR] [\fB\-\-pretty\fR] [\fB\-\-version\-verbose\fR] [\fB\-\-list\-functions\fR] [\fB\-h\fR|\fB\-\-help\fR] [\fB\-V\fR|\fB\-\-version\fR] [\fIsubcommands\fR]
88
.SH DESCRIPTION
99
A debugger for Soroban smart contracts
1010
.SH OPTIONS
@@ -43,6 +43,9 @@ Filter budget trend by function name
4343
.TP
4444
\fB\-\-trend\-regression\-smoothing\fR \fI<N>\fR [default: 1]
4545

46+
.TP
47+
\fB\-\-pretty\fR
48+
Pretty\-print JSON outputs instead of compact JSON
4649
.TP
4750
\fB\-\-version\-verbose\fR
4851
Show detailed version information

scratch/test_trace.json

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
{
2+
"label": "Execution of increment on tests/fixtures/wasm/counter.wasm",
3+
"contract": "tests/fixtures/wasm/counter.wasm",
4+
"function": "increment",
5+
"args": null,
6+
"storage": {
7+
"contract_code": "ContractCode(ContractCodeEntry { ext: V1(ContractCodeEntryV1 { ext: V0, cost_inputs: ContractCodeCostInputs { ext: V0, n_instructions: 135, n_functions: 8, n_globals: 3, n_table_entries: 0, n_types: 6, n_data_segments: 0, n_elem_segments: 0, n_imports: 5, n_exports: 6, n_data_segment_bytes: 0 } }), hash: Hash(5be3612b8625f8a0bd7cdd12736995be4bba6726aa03ce55f33aa87f4bfd4eef), code: BytesM(0061736d01000000011e0660027e7e017e60017e017e60037e7e7e017e60017f006000017e600000021f05016c01300000016c01310000016901320001016901310001016c015f0002030908030401040405050505030100100619037f01418080c0000b7f00418080c0000b7f00418080c0000b073b06066d656d6f7279020003676574000809696e6372656d656e740009015f000c0a5f5f646174615f656e6403010b5f5f686561705f6261736503020aef02087102027e017f420021010240108680808000220242021080808080004201520d0002400240200242021081808080002201a741ff0171220341c100460d00024020034107470d00200142088721010c020b000b200110828080800021010b20002001370308420121010b200020013703000b0600428ed0000b3000024020004280808080808080c0007c42ffffffffffffffff00560d0020004208864207840f0b20001083808080000b4102017f017e23808080800041106b220024808080800020001085808080002000290308420020002802001b1087808080002101200041106a24808080800020010b7002017f027e23808080800041106b2200248080808000200010858080800002402000290308420020002802001b220142017c22022001590d00108a80808000000b108680808000200210878080800042021084808080001a20021087808080002102200041106a24808080800020020b0900108b80808000000b0300000b02000b004f0e636f6e747261637473706563763000000000000000000000000367657400000000000000000100000007000000000000000000000009696e6372656d656e74000000000000000000000100000007001e11636f6e7472616374656e766d6574617630000000000000001600000000006f0e636f6e74726163746d65746176300000000000000005727376657200000000000006312e39322e3000000000000000000008727373646b7665720000003032322e302e31312333346637663533616533316530666430326161623433366139383732653739666136373163613032) }) (ttl=4095)",
8+
"contract_data:Persistent:LedgerKeyContractInstance": "ContractInstance(ScContractInstance { executable: Wasm(Hash(5be3612b8625f8a0bd7cdd12736995be4bba6726aa03ce55f33aa87f4bfd4eef)), storage: Some(ScMap(VecM([ScMapEntry { key: Symbol(ScSymbol(StringM(c))), val: I64(1) }]))) }) (ttl=4095)",
9+
"contract_data:Temporary:LedgerKeyNonce(ScNonceKey { nonce: 801925984706572462 })": "Void (ttl=6311999)"
10+
},
11+
"budget": {
12+
"cpu_instructions": 485710,
13+
"memory_bytes": 1164922,
14+
"cpu_limit": null,
15+
"memory_limit": null
16+
},
17+
"return_value": "I64(1)",
18+
"call_sequence": [
19+
{
20+
"function": "increment",
21+
"args": null,
22+
"depth": 0
23+
}
24+
],
25+
"events": [
26+
{
27+
"contract_id": null,
28+
"topics": [
29+
"Symbol(ScSymbol(StringM(fn_call)))",
30+
"Bytes(ScBytes(BytesM(54ca65a58200bfffb708756d19fd134e1677063f85bb87e0c7a54abb480d375c)))",
31+
"Symbol(ScSymbol(StringM(increment)))"
32+
],
33+
"data": "Void"
34+
},
35+
{
36+
"contract_id": "Hash(54ca65a58200bfffb708756d19fd134e1677063f85bb87e0c7a54abb480d375c)",
37+
"topics": [
38+
"Symbol(ScSymbol(StringM(fn_return)))",
39+
"Symbol(ScSymbol(StringM(increment)))"
40+
],
41+
"data": "I64(1)"
42+
}
43+
]
44+
}

scratch/test_trace.manifest.json

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"schema_version": "1.0.0",
3+
"artifact_group": "replay_artifacts",
4+
"created_at": "2026-05-28T15:13:20.272312+00:00",
5+
"label": "Execution of increment on tests/fixtures/wasm/counter.wasm",
6+
"contract": "tests/fixtures/wasm/counter.wasm",
7+
"function": "increment",
8+
"files": [
9+
{
10+
"kind": "trace",
11+
"path": "scratch/test_trace.json",
12+
"description": "Primary execution trace used for replay"
13+
},
14+
{
15+
"kind": "manifest",
16+
"path": "scratch/test_trace.manifest.json",
17+
"description": "Replay artifact manifest"
18+
},
19+
{
20+
"kind": "contract_wasm",
21+
"path": "tests/fixtures/wasm/counter.wasm",
22+
"description": "Contract WASM used to generate the trace"
23+
}
24+
]
25+
}

src/cli/args.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,10 @@ pub struct Cli {
150150
#[command(subcommand)]
151151
pub command: Option<Commands>,
152152

153+
/// Pretty-print JSON outputs instead of compact JSON
154+
#[arg(long, global = true)]
155+
pub pretty: bool,
156+
153157
/// Show detailed version information
154158
#[arg(long)]
155159
pub version_verbose: bool,
@@ -1037,7 +1041,6 @@ mod tests {
10371041
assert_eq!(args.network_snapshot.unwrap().to_str().unwrap(), "state.json");
10381042
}
10391043
}
1040-
}
10411044

10421045
#[derive(Parser)]
10431046
pub struct CompareArgs {
@@ -1238,6 +1241,10 @@ pub struct ReplayArgs {
12381241
#[arg(short, long)]
12391242
pub output: Option<PathBuf>,
12401243

1244+
/// Output format for replay command (pretty, json)
1245+
#[arg(long, value_enum, default_value_t = OutputFormat::Pretty)]
1246+
pub format: OutputFormat,
1247+
12411248
/// Show verbose output during replay
12421249
#[arg(short, long)]
12431250
pub verbose: bool,
@@ -1383,6 +1390,10 @@ pub struct RemoteArgs {
13831390
#[arg(long, value_name = "MS", default_value = "2000")]
13841391
pub retry_max_delay_ms: u64,
13851392

1393+
/// Output format for remote command (pretty, json)
1394+
#[arg(long, value_enum, default_value_t = OutputFormat::Pretty)]
1395+
pub format: OutputFormat,
1396+
13861397
/// Remote operation to perform (default: execute or ping)
13871398
#[command(subcommand)]
13881399
pub action: Option<RemoteAction>,

0 commit comments

Comments
 (0)