Skip to content

Add event-log decoding to receipt (--event <cast-style signature>) #11

Description

@erikzrekz

Summary

Add event-log decoding to radius-cli receipt so a receipt's logs can be decoded against a cast-style event signature. Today receipt returns raw logs (topics + data); extracting a value from an emitted event means hand-parsing topics[n] and converting hex. A generic decoder — not tied to any specific contract — would make event-driven flows a clean composition.

This is a protocol-agnostic primitive, deliberately not an application command. (Context: a discussion about ERC-8004 agent registration concluded that registration semantics belong in a skill, but the one non-trivial step — reading an event value out of a receipt — is a generic gap worth closing in the CLI.)

Proposed interface

# Decode logs in a receipt that match the given event signature
radius-cli receipt 0x<hash> --event "Transfer(address indexed from, address indexed to, uint256 indexed tokenId)"

# Repeatable for multiple events; --json emits structured decoded logs
radius-cli receipt 0x<hash> \
  --event "Transfer(address indexed,address indexed,uint256 indexed)" \
  --event "Registered(uint256 indexed,string,address indexed)" \
  --json
  • --event <sig> (repeatable): a cast-style event signature. The indexed keyword matters — it's how the decoder knows which params are in topics vs data, so accept and honor it (mirrors cast).
  • Matching is by topics[0] == the event's keccak256 selector. Non-matching logs are passed through (raw) so nothing is silently dropped.
  • Human output: event name + named args per matching log. --json: an array of { address, eventName, args: { name: value }, logIndex } (bigints as decimal strings, consistent with the existing --json convention).

Why this fits radius-cli

  • The CLI already parses cast-style signatures (parseCastSignature / coerceArg in src/lib/signature.ts) and uses viem (encodeFunctionData / decodeFunctionResult). Event decoding is the symmetric primitive: viem already provides parseAbiItem("event Transfer(...)") + decodeEventLog.
  • Keeps the tool cast-shaped — a generic primitive over receipts, no per-protocol or per-contract knowledge, no hardcoded addresses.

Motivating example (one of many)

Extracting the minted agentId from an ERC-8004 register(string) transaction is currently:

cast to-dec $(radius-cli --json receipt $TX | jq -r '.logs[0].topics[3]')   # fragile: assumes log/topic position

With this feature it becomes self-describing and position-independent:

radius-cli --json receipt $TX --event "Transfer(address indexed,address indexed,uint256 indexed tokenId)" \
  | jq -r '.[0].args.tokenId'

The same primitive serves any event (ERC-20 Transfer/Approval, escrow JobCreated, payment PaymentIntentSettled, etc.).

Acceptance criteria

  • radius-cli receipt <hash> --event <sig> decodes logs whose topics[0] matches the event selector and prints event name + named args.
  • --event is repeatable; all provided signatures are tried against every log.
  • indexed is honored so indexed vs non-indexed params decode correctly; a signature missing indexed either errors clearly or documents the limitation.
  • --json emits a structured array (address, eventName, args, logIndex); bigints serialized as decimal strings.
  • Logs that match no provided signature are passed through raw, not dropped.
  • Malformed event signatures fail with a clear message.
  • Tests cover: a single indexed-arg event, a mixed indexed/non-indexed event, multiple --event filters, and a no-match passthrough.

Out of scope

  • A 4-byte/event-signature lookup database (caller supplies the signature, same as call/tx).
  • Full-ABI receipt decoding from an --abi <file> (could be a follow-up; this issue is the signature-based path).
  • Any ERC-8004 / application-specific command.

References

  • Existing signature parsing: src/lib/signature.ts (parseCastSignature, coerceArg)
  • Current receipt command (raw logs only): src/commands/receipt.ts
  • viem helpers: parseAbiItem, decodeEventLog

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions