Skip to content

feat(cast): bring remaining --json outputs into JsonEnvelope#14918

Open
mablr wants to merge 17 commits into
masterfrom
mablr/cast-json-envelope-parity
Open

feat(cast): bring remaining --json outputs into JsonEnvelope#14918
mablr wants to merge 17 commits into
masterfrom
mablr/cast-json-envelope-parity

Conversation

@mablr
Copy link
Copy Markdown
Collaborator

@mablr mablr commented May 26, 2026

Description

Towards OSS-239 (almost there after wallet follow-up)

Move print_scalar, print_list, print_tokens, and print_json_object from local helpers in args.rs into foundry_cli::json so every cmd module can import them directly instead of inlining the if/else pattern.

Replace all remaining inline if shell::is_json() { sh_println!(...) } patterns across cmd modules:

  • estimate, find_block, call, mktxprint_scalar
  • txpool (all variants), rpc, vaddr/createprint_json_object
  • keychain list/inspect/doctorprint_json_object / print_json_success
  • erc20 name/symbol/decimalsprint_scalar
  • erc20 balance/allowance/total_supplyprint_json_success (keep format_uint_exp for plain-mode display)
  • Block and Tx in args.rs → parse pre-formatted JSON string and wrap
  • Selectors in args.rs → new SelectorInfo struct, envelope-wraps array
  • AbiEncodeEvent in args.rs → new EncodedEvent{topics,data} struct
  • interface → wrap ABI array in envelope on stdout path

Documented explicitly unsupported commands with inline comments: send (N::ReceiptResponse lacks Serialize), logs --subscribe (streaming), wallet (follow-up), upload-signature (external API), four-byte-calldata (interactive).

PR Checklist

  • Added Tests
  • Added Documentation
  • Breaking changes (for commands that already output JSON by default)

Move `print_scalar`, `print_list`, `print_tokens`, and
`print_json_object` from
local helpers in `args.rs` into `foundry_cli::json` so every cmd module
can import them directly instead of inlining the if/else pattern.

Replace all remaining inline `if shell::is_json() { sh_println!(...) }`
patterns across cmd modules:
- `estimate`, `find_block`, `call`, `mktx` → `print_scalar`
- `txpool` (all variants), `rpc`, `vaddr/create` → `print_json_object`
- `keychain list/inspect/doctor` → `print_json_object` /
  `print_json_success`
- `erc20 name/symbol/decimals` → `print_scalar`
- `erc20 balance/allowance/total_supply` → `print_json_success` (keep
  `format_uint_exp` for plain-mode display)
- `Block` and `Tx` in `args.rs` → parse pre-formatted JSON string and
  wrap
- `Selectors` in `args.rs` → new `SelectorInfo` struct, envelope-wraps
  array
- `AbiEncodeEvent` in `args.rs` → new `EncodedEvent{topics,data}` struct
- `interface` → wrap ABI array in envelope on stdout path

Document explicitly unsupported commands with inline comments: `send`
(N::ReceiptResponse lacks Serialize), `logs --subscribe` (streaming),
`wallet` (follow-up), `upload-signature` (external API),
`four-byte-calldata` (interactive).

Add CLI tests: `selectors_json_envelope`,
`abi_encode_event_json_envelope`.
Comment thread crates/cast/src/args.rs Outdated
Comment thread crates/cast/src/cmd/call.rs Outdated
Comment thread crates/cast/src/cmd/interface.rs
Copy link
Copy Markdown
Member

@mattsse mattsse left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requesting changes: these paths can fail or emit invalid JSON for valid --json usages.

P1: cast call --json reparses response as JSON, but Cast::call returns raw hex when no output is decoded. Valid calls can therefore error on serde_json::from_str. Please avoid reparsing arbitrary display output; return structured data from Cast::call, or wrap raw hex as a JSON string/envelope in the command layer.

P1: cast block --json and cast tx --json also assume output is a JSON object. --raw or scalar field paths such as block hash / tx from can return scalar strings, which either fail JSON parsing or are not objects. Please only call print_json_object for object output, and use a scalar JSON envelope/string path for field selections.

P2: cast mktx prints the expiration status line to stdout before later JSON/scalar output, which corrupts --json output. Please move this to stderr/warning output, suppress it under shell::is_json(), or include it inside the JSON payload.

Please add regressions for cast call --json with undecoded/raw output, cast block/tx --json scalar field paths, and cast mktx --json --expires-at ... producing a single valid JSON document.

Copy link
Copy Markdown
Collaborator Author

@mablr mablr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Outscoped cast call as it was overlapping with #14774.

Fixed the rest.

@mablr mablr force-pushed the mablr/cast-json-envelope-parity branch from 959137a to 60cfa08 Compare May 28, 2026 08:39
@mablr mablr requested review from figtracer and mattsse May 28, 2026 08:40
Copy link
Copy Markdown
Collaborator

@figtracer figtracer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Three focused JSON-output regressions from the local review pass.

Comment thread crates/cast/src/cmd/vaddr/create.rs
Comment thread crates/cast/src/cmd/keychain.rs
Comment thread crates/cast/src/cmd/interface.rs Outdated
- args.rs: use get(pos).cloned() instead of indexing resolve_results to
  avoid panic in cast selectors --json when --resolve is not set
- interface.rs: use sh_status! for "Saved interface at" so it goes to
  stderr in JSON mode, keeping stdout clean
- keychain.rs: move is_json() branch before the is_empty() guard so
  cast --json keychain list emits [] instead of plain text when empty
- vaddr/create.rs: defer JSON/text output until after register()
  succeeds
  to avoid emitting two stdout documents when registration fails
Copy link
Copy Markdown
Collaborator

@stevencartavia stevencartavia left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

crates/cast/src/cmd/vaddr/create.rs — mined salt is lost on register failure

The non---no-register path now calls register(...).await? before printing the salt/registration_hash/master_id/virtual_addresses block. On master those values were printed first.

Reproduced with no signer (so register errors):

master

STDOUT:
Salt:              0x9bf4…155f
Registration hash: 0x0000…e5fd
Master ID:         0x3a96b610
Virtual addresses:
  tag=0x000000000000  0x3A96B610…0000
STDERR:
Error: cast vaddr create requires a signer (...)

PR

STDOUT:  (only the "Mining…/Found salt in…" lines)
STDERR:
Error: cast vaddr create requires a signer (...)

The mined salt is now lost on any registration failure — the user has to mine again (which can take minutes). Suggest restoring the prior order: compute the payload once, print it, then call register. That also lets you collapse the duplicated JSON/plain-text blocks between the no_register branch and the register branch (both currently emit the same payload; print_json_success and print_json_object are equivalent inside if shell::is_json()).


crates/cast/src/cmd/keychain.rsrun_list empty case becomes an error

run_list now routes the empty-keys case through sh_err!, which writes to stderr and prefixes Error:. The same situation in run_show still uses sh_println!, so the two siblings disagree.

Reproduced with TEMPO_HOME pointing at a keys.toml containing keys = []:

master:  EXIT 0  STDOUT: "No keys found in keys.toml."  STDERR: (empty)
PR:      EXIT 0  STDOUT: (empty)                        STDERR: "Error: No keys found in keys.toml."

An empty list is a successful result, not an error. Suggest keeping sh_println! here for parity with run_show (or change both).


crates/cast/src/cmd/interface.rs--json stdout and --json -o file shapes diverge

cast interface --json to stdout now wraps the parsed ABIs in a JsonEnvelope, but cast interface --json -o <file> still writes the raw concatenated json_abi strings to the file. Same data, two shapes depending on whether -o is set.

Reproduced with a single-ABI input:

stdout (--json) file (--json -o)
master [ { ...abi... } ] [ { ...abi... } ]
PR {"schema_version":1,"success":true,"data":[[{...}]],…} [ { ...abi... } ]

If file output is intentionally a raw artifact (not wrapped), please call that out (doc/comment + test); otherwise both destinations should share the envelope shape. Note that for multiple interfaces the file content is also several JSON objects newline-joined, which isn't one valid JSON document.

@mablr mablr requested review from figtracer and stevencartavia May 28, 2026 18:34
Comment thread crates/cast/src/args.rs
Comment thread crates/cast/src/cmd/vaddr/create.rs Outdated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

4 participants