Skip to content

cs-test: registry-compatible runner with nested owner threading - #2

Merged
llbartekll merged 9 commits into
mainfrom
cs-test-registry-output
May 27, 2026
Merged

llbartekll merged 9 commits into
mainfrom
cs-test-registry-output

Conversation

@llbartekll

Copy link
Copy Markdown
Owner

Summary

  • Lands @manuelwedler's cs-test extension that emits a registry-compatible results.json so the ERC-7730 registry CI can drive our runner alongside other implementations (originally proposed in Add registry-compatible results.json output to cs-test #1).
  • Replaces the third-party harness identifier with @llbartekll/cs-test and splits the stub's ENS vs local name resolution into separate maps so descriptor sources: [...] arrays are honored correctly.
  • Adds owner: Option<String> to DisplayEntry::Nested so nested calldata frames carry the inner descriptor's metadata.owner. Propagated through engine.rs, eip712.rs, the cs-test renderer, the registry result comparator, the Swift / React Native UniFFI bindings, the iOS wallet, and the integration docs.
  • Reverts the legacy-tx decoder addition from Add registry-compatible results.json output to cs-test #1 — no current registry fixture uses legacy txs and the EIP-155 disambiguation was untested.

Commits

  • c6c6000 Add registry-compatible results.json output to cs-test (Manuel)
  • c19a9e6 Support legacy transactions in cs-test rawTx decoder (Manuel, reverted below)
  • 42298b6 Emit interpolatedIntent in cs-test results.json (Manuel)
  • bfc2d44 Revert legacy-tx commit (out of scope, no fixture exercises it)
  • 02b75cc cs-test: emit own runner ID and split ENS vs local stub maps
  • fe76472 Track owner on nested calldata DisplayEntry frames
  • 1879f39 Fix nested owner comparison and bindings

Manuel's original commits keep their SHAs intact. New work is Co-Authored-By: Manuel Wedler.

Test plan

  • cargo test — 352/352 passing
  • cargo clippy --all-targets -- -D warnings — clean
  • cargo fmt --check — clean
  • cargo test -p clear-signing --features uniffi,github-registry — passing
  • cargo clippy -p clear-signing --all-targets --features uniffi,github-registry -- -D warnings — clean
  • ./scripts/generate_uniffi_bindings.sh + ./scripts/build-xcframework.sh — clean
  • xcodebuild -project wallet/Wallet.xcodeproj -scheme Wallet -destination "generic/platform=iOS Simulator" -configuration Debug buildBUILD SUCCEEDED
  • E2E against the registry's aave fixture: runner == "@llbartekll/cs-test", implementation == "llbartekll/clear-signing@0.1.0", 3/3 cases pass with rendered nested owners populated

Out of scope (follow-ups on registry PR #2586)

  • Add ensNames alongside addressNames in the v2 schema; migrate the aave example fixture so sosalkin.eth moves to ensNames.
  • Permit per-implementation runner identifiers in the spec (currently doc says "Always @ethereum-sourcify/...").
  • Once this PR merges, switch .github/actions/run-rust-tests/action.yml from cloning Manuel's fork to cargo install --git https://github.com/llbartekll/clear-signing cs-test against main.

Closes #1 (superseded — see the per-fix breakdown in #1 review thread).

🤖 Generated with Claude Code

manuelwedler and others added 7 commits May 20, 2026 19:21
Adapts cs-test to the unified clear-signing test runner contract used by
the manuelwedler/clear-signing-erc7730-registry CI:

- New --output/-o <path> flag emits results.json with the runner literal,
  implementation id (llbartekll/clear-signing@<version>), and per-case
  status (pass/fail/error). Markdown path is unchanged when the flag is
  absent.
- Per-case decode/format errors are now captured as error status instead
  of aborting the run. Group entries flatten to the parent fields map;
  nested calldata emits the recursive {intent, owner, fields} shape.
- DataProviderStub gains nftCollectionNames and blockTimestamps to match
  the v2 input format; StubDataProvider resolves both, plus ENS names
  from the same address-names map.
- Adds clear_signing::VERSION so the runner can report the library
  version it is bound to. Exit code stays 0 in --output mode regardless
  of case outcomes per the registry contract.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds decoding for the three legacy forms registry fixtures may carry:
6-field pre-EIP-155 unsigned, 9-field EIP-155 unsigned signing payloads
([..., chainId, 0, 0]), and 9-field signed ([..., v, r, s] with chainId
recovered from v). EIP-1559 type-0x02 handling is factored into its own
function but unchanged.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds optional `interpolatedIntent` to the registry-compatible rendered
output, populated from `DisplayModel.interpolated_intent`. Omitted (not
null) when the descriptor has no template placeholders.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This reverts commit c19a9e6.

No registry fixture currently uses legacy transactions (the v2 testsv2/
corpus is EIP-1559 only). The EIP-155 disambiguation between unsigned
9-tuples and signed 9-tuples is untested, and the pre-EIP-155 mainnet
default is a footgun. Reintroduce when a real fixture motivates it.

The decode_eip1559 internal extraction is reverted along with this; it
was bundled into the same commit. Restore as a separate refactor if
wanted.
Two related changes to the cs-test test runner so it represents our
implementation honestly to the registry and serves the right data to
the engine's source-driven address-name resolution.

Runner identity. RUNNER_ID is changed from a third-party vendor literal
to "@llbartekll/cs-test" so the registry's results.json correctly
attributes the harness emitting each row. The docstring referencing a
specific upstream fork is dropped.

ENS vs local split. The DataProvider trait defines resolve_ens_name and
resolve_local_name as semantically distinct lookups, and the engine
branches on the field's `sources` array. The stub previously fed both
methods from a single addressNames map, so any contact label spuriously
satisfied an ENS-sourced field. DataProviderStub now carries a separate
ensNames map, and StubDataProvider routes each method to its own map.
Adds three unit tests covering ens-only, local-only, and both-populated
states.

Co-Authored-By: Manuel Wedler <34456797+manuelwedler@users.noreply.github.com>
Nested calldata frames now carry the inner descriptor's metadata.owner.
This closes the parity gap where wallets rendering a nested call had no
way to know whose descriptor produced the inner intent.

Engine. DisplayEntry::Nested gains owner: Option<String>. Calldata
nesting populates it from inner_descriptor.metadata.owner.clone() at the
successful-render site in render_calldata_field; raw/fallback frames
(unresolved callee, unknown selector, depth limit, undecodable inner,
non-bytes calldata) leave it as None. EIP-712 nesting threads it the
same way through the recursive format_calldata call.

cs-test. The registry-compatible results.json now emits the nested
owner via NestedRendered.owner; the empty-string workaround comment is
gone. Pattern matches in compare.rs and the unit test in results.rs are
updated.

Tests. The existing Safe execTransaction nested test asserts the inner
ERC-20 descriptor's owner ("Circle") propagates; the no-inner-descriptor
test asserts raw fallback frames carry None. The three-level userops
nested test asserts owner propagation through EIP-712 → calldata →
calldata.

Downstream. Swift UniFFI bindings are regenerated. Wallet pattern
matches in DisplayModelView, CalldataDiagnostics, and TypedDataDiagnostics
are updated; DisplayModelView renders the inner owner as a small caption
when present. React Native, CLAUDE, and AGENTS docs are updated to
reflect the new field on the nested shape.

Co-Authored-By: Manuel Wedler <34456797+manuelwedler@users.noreply.github.com>
Compare nested expected owners in cs-test, sync React Native bindings with the updated DisplayEntry::Nested layout, and clean generated Swift whitespace.

Co-Authored-By: Manuel Wedler <34456797+manuelwedler@users.noreply.github.com>
Rust 1.95 (the version CI now picks up via dtolnay/rust-toolchain@stable)
stabilized the manual_is_multiple_of clippy lint. Pre-existing parity
checks at five call sites used `% 2 != 0` / `% 2 == 0` patterns, which
the new lint rejects under `-D warnings`. Replace each with the explicit
`.is_multiple_of(2)` form (stabilized in 1.87, available on our floor
toolchain).

Sites:
- crates/clear-signing/src/uniffi_compat/mod.rs:429
- crates/clear-signing/src/engine.rs:1795
- crates/clear-signing/tests/quickswap_integration.rs:35
- crates/clear-signing/tests/celo_accounts_integration.rs:32
- crates/clear-signing/tests/lifi_integration.rs:35

Pure mechanical fix — no behavior change.
A stray `rust_out` Mach-O binary slipped into the previous commit — it
was the output of an offhand `rustc -` probe against the local
toolchain. Drop it.
@llbartekll
llbartekll merged commit 536b945 into main May 27, 2026
2 of 3 checks passed
@manuelwedler

Copy link
Copy Markdown
Contributor

thanks, will update the registry PR

@manuelwedler

Copy link
Copy Markdown
Contributor

One question: why did you revert c19a9e6? I think this made a few tests fail here: manuelwedler/clear-signing-erc7730-registry#2

@llbartekll

Copy link
Copy Markdown
Owner Author

Good catch — you're right, and the revert was wrong. My original reasoning ("no fixture exercises legacy txs") was based on the single aave example fixture in #2586 at the time; I didn't account for the bulk migration in manuelwedler/clear-signing-erc7730-registry#2 which ports dozens of real historical fixtures that do use legacy rawTx values.

Reapplying your commit in #4 with your co-author attribution preserved. Verified end-to-end against the migrated 1inch AggregationRouterV3 fixture — both cases now run instead of erroring on decode.

Sorry for the noise on that one.

@llbartekll

Copy link
Copy Markdown
Owner Author

One question: why did you revert c19a9e6? I think this made a few tests fail here: manuelwedler/clear-signing-erc7730-registry#2

my bad -- restoring it

llbartekll added a commit that referenced this pull request Jun 4, 2026
Resolve library-side output mismatches surfaced by the upstream registry's
v2 test migration (manuelwedler/clear-signing-erc7730-registry #2/#3/#4):

- date: RFC-3339 "Z" suffix instead of " UTC"
- token amounts: zero renders "0" (not "0.0")
- addresses: EIP-55 checksum on the EIP-712 and raw-format paths (calldata parity)
- array-of-struct scopes: element-major ordering (fixes wrong field labels)
- interpolatedIntent: match {name} templates against #.-prefixed field paths
- amount format: render as a native-currency amount
- unit base: resolve $.metadata.constants.* references

Add cs-test regression fixtures (degate, lido, uniswap, aave, yieldxyz)
vendored from the registry. The nested-call case (kiln) is #[ignore]'d:
the runner resolves only the outer descriptor, a harness gap rather than an
engine bug. The aave Borrow case is dropped (its @.from field is unavailable
to the runner).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants