Common Test Strategy - #2586
Common Test Strategy#2586
Conversation
Structured per-field assertions for aave/calldata-lpv2 descriptor (3 cases: Repay All USDC, Manage collateral, Withdraw Max WETH). New workflow_dispatch runs the cs-test binary from llbartekll/clear-signing against the file with hardened token permissions. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Move all Ledger-specific test steps (DMK + Speculos setup, coin-apps checkout, descriptor preprocessing, cs-tester invocation, artifact uploads) from the run-tests job into a composite action at .github/actions/run-ledger-tests/. The workflow now calls the action with the matrix values and secrets as inputs. Prepares the workflow for adding sibling Sourcify TS and Bartek Rust runner jobs without duplicating the orchestration logic. Behavior of the Ledger pipeline is unchanged. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Define the output contract between clear-signing test runners and the post-results job: every runner writes a single results.json per descriptor with a fixed shape (runner, implementation, cases[]). `rendered` mirrors the test schema's `expected` shape so the two can be compared structurally; status is pass/fail/error/skipped. Lives under .github/test-results/ with a README spec and four example files covering each status value. No CI wiring yet — this just nails down the contract so the Sourcify TS and Bartek Rust runners can be implemented against it in parallel. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds the @ethereum-sourcify/clear-signing-test-runner alongside the existing Ledger runner. The new runner consumes the unified results.json contract; future runners (Bartek Rust) can reuse the same plumbing. Per-runner: .github/actions/run-sourcify-tests/ — clones and builds sourcifyeth/clear-signing-test-runner, invokes its CLI against a tests fixture, writes results.json. Shared: .github/actions/upload-test-results/ — validates basic results.json structure with jq and uploads as results__<slug>__<entity>__<descriptor>. Double-underscore separator avoids ambiguity with hyphens in slugs/descriptors. Workflow: adds detect-shared-tests, sourcify-tests, and post-implementation-results jobs. Sourcify discovers tests in shared-tests/ (parallel to the existing tests/ path that Ledger still uses). Post job produces a separate PR comment with a per-case × per-implementation column table — Bartek joins later by slotting a column. Both detect jobs now also trigger when only a test file changes (previously only descriptor changes did), deduplicated when a PR changes both. Required-file existence is still enforced. Spec note added to .github/test-results/README.md: runners are responsible for resolving ERC-7730 includes themselves. Cleanup: hardcode the Ledger DMK repo/ref (removed dmk-repo and dmk-ref inputs, since neither is meaningful to override per call). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Change notify-missing-tests to gate only on detect-shared-tests rather than both detect jobs. Every descriptor change should have a matching shared-tests/ file going forward, regardless of whether an old tests/ file exists for Ledger. Comment already directs contributors at the shared-tests/ path. Excludes both tests/ and shared-tests/ folders from the changed-descriptor scan so test files are not misread as descriptor changes. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Rename registry/<entity>/shared-tests/ → registry/<entity>/testsv2/ across the codebase. Updates the only existing file under aave (via git mv to preserve history) and every reference in CI workflows: job name detect-shared-tests → detect-testsv2, path globs, files_ignore patterns, downstream needs/if expressions, the notify-missing-tests comment body, and the validate-rs.yml path. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
check-permissions previously used head.repo.fork to decide whether to require a maintainer-applied run-tests label. That property is true for any repo that is itself a fork — even when a PR is intra-fork (head and base are the same fork). Same-repo PRs are not a security risk and shouldn't be gated. Switch to comparing head.repo.full_name vs base.repo.full_name. Cross- repo PRs (the only case where head SHA is untrusted code with secret access) still require the label; same-repo PRs run without it. Behavior on upstream is unchanged for the two scenarios that matter there (internal PRs run; fork PRs need the label). Intra-fork PRs — used for CI testing — now work as expected. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The post-results comment was always saying "No implementation test
results found" even though the Sourcify artifact uploaded successfully.
Two root causes, both fixed here:
1. github-script's CWD assumption.
The script used a relative path `fs.readdirSync('artifacts')` inside
an empty try/catch. Any failure left artifactDirs = [] silently. Use
process.env.GITHUB_WORKSPACE for an absolute path and surface
diagnostics via core.info / core.warning so a future regression
can't hide.
2. actions/download-artifact@v8 single-artifact extraction.
When `pattern:` matches exactly one artifact, v8 extracts contents
directly into `path` instead of creating the documented
`<artifact-name>/` subdir. That made the per-subdir parsing match
nothing on a single-runner PR; when a second runner joins the
resulting `results.json` files would have collided in the same
directory anyway.
Fix the layout end-to-end: the upload composite copies the source
results.json to `<slug>__<entity>__<descriptor>.json` (guaranteed
unique per matrix entry) before uploading, the download step uses
`merge-multiple: true` to land all files flat, and the post-results
script reads every `*.json` in the artifacts root and parses
entity/descriptor from the filename. This shape works regardless
of how many runners are present and how v8 chooses to flatten.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
pull_request.yml previously only ignored registry/**/tests/**. The new testsv2/ folder wasn't excluded, so test files there matched the calldata-*.json glob (because .tests.json ends in .json) and were treated as descriptors by erc7730 lint / check-jsonschema, failing validation. Add registry/**/testsv2/** and a catch-all registry/**/*.tests.json to the files_ignore lists in both validate_descriptors and validate_schemas. Catches future test-format folders too. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The previous comment was a compact status table only. On a failure the reviewer had to dig into Actions logs to find out what diverged. Add a Details section below the table with one <details> block per non-pass case (collapsed by default). Scales cleanly to N runners. For fail: expected (loaded from the test file at PR head) vs got (rendered from the runner) as side-by-side JSON code blocks, plus the runner's optional message. For error / skipped: just the message — the only signal those statuses carry. The details summary line uses the same left-to-right column order as the status table: <icon> <entity>/<descriptor> · <case> · <impl>. Implementation: post-implementation-results now also checks out the PR head registry tree so it can load the expected block from registry/<entity>/testsv2/<descriptor>.tests.json. Read-only access to test data — no execution of PR code, so safe under pull_request_target. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Test files in testsv2/ follow a different shape than the original
tests/ files: top-level descriptor + dataProvider fields, every test
has a required description and a structured expected block
({intent, owner, fields}) instead of a flat expectedTexts array.
Field values are strings or — for calldata-formatted fields —
nested {intent, owner, fields} objects (recursive). See
.github/test-results/README.md for the matching results format.
Copied erc7730-tests.schema.json as the starting point and adapted.
The v1 schema stays in place to validate legacy tests/ files
during the migration.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
validate_schemas previously ignored all test files (kept them out of the changed-files glob entirely). Now it includes them and dispatches the schema by folder: registry/**/tests/*.tests.json → specs/erc7730-tests.schema.json registry/**/testsv2/*.tests.json → specs/erc7730-tests-v2.schema.json For everything else (descriptors, common-* files, ercs/*) the previous $schema-field-based logic stays unchanged. Folder-based dispatch is used because legacy v1 test files lack a $schema field and we don't want to require contributors to add it during the migration. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Point at testsv2/ as the new folder, describe the descriptor +
dataProvider top-level wrapper, replace expectedTexts arrays with
the structured expected.{intent, owner, fields} block. Updated
calldata and EIP-712 examples and field tables to match.
The registry structure diagram earlier in the README still points at
the legacy tests/ folder; that one will move with the broader test
file migration.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The example in the spec uses a calldata case; a reader could
plausibly wonder whether EIP-712 produces a different shape. It
doesn't — both calldata and EIP-712 test cases use the same
{intent, owner, fields} expected/rendered block. Add a single line
to make that explicit.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add a third implementation runner alongside Ledger and Sourcify. The runner builds llbartekll/clear-signing's cs-test binary from source and invokes it with the new --output flag so it emits a spec-conforming results.json (per .github/test-results/). Composite action: .github/actions/run-rust-tests/. Hardcoded to manuelwedler/llbartekll-clear-signing@cs-test-results-json for now; will switch to upstream llbartekll/clear-signing once the contract adaptation is merged there. Workflow: new rust-tests job mirrors sourcify-tests (matrix on detect-testsv2, calls run-rust-tests then upload-test-results with slug rust-clear-signing). post-implementation-results now also needs rust-tests so the PR comment table includes its column. The legacy validate-rs.yml workflow stays in place for now and will be deleted in a follow-up commit once this path is verified. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The Rust composite was failing at the checkout step because the cs-test-results-json branch in manuelwedler/llbartekll-clear-signing contains a stray .claude/worktrees/competent-joliot-876b10/ directory (leftover from a Claude Code subagent worktree that got committed by mistake). actions/checkout's post-step runs `git submodule foreach` unconditionally to clean up auth, and that fails with exit 128 since the directory looks like a submodule but isn't in .gitmodules. The checkout step then reports failure even though the files are on disk, and the rest of the composite (cargo build, cs-test run) is skipped. Replace actions/checkout with a plain `git clone` for that one repo and `rm -rf rust-test-runner/.claude` so cargo doesn't see anything weird either. Temporary — drop this once the source branch is cleaned up. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Superseded by the run-rust-tests composite + rust-tests job in clear-signing-tests.yml, which produces results.json conforming to .github/test-results/ and feeds into the unified PR comment. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…ment
Previously the PR could end up with two separate bot comments — one
"## Clear Signing Implementation Results" (per-runner status table)
and one "## Clear Signing Tests" (missing-tests warning) — and the
warning wouldn't go away once tests were added.
Unify under a single "## Clear Signing Tests" comment that both jobs
update via startsWith('## Clear Signing Tests') matcher:
- post-implementation-results (runs when has_tests=true) writes the
status table.
- notify-missing-tests (runs when has_tests=false) writes the warning,
or deletes the existing comment if no descriptors changed either
(handles the case where the user reverts a descriptor change after
the warning had been posted).
Mutually exclusive on has_tests, so no race between the two jobs.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The runner composite actions exit 0 when they ran cleanly even if some test cases failed (per the runner contract — non-zero is reserved for runner-level failures). Until now nothing aggregated that information into a failed status check, so PRs with failing tests showed an all-green CI. Have the two summary jobs set the job as failed where it matters: - post-implementation-results sets failed if any case has a non-pass status (fail / error / skipped), or if no result artifacts were uploaded at all when we expected them. - notify-missing-tests sets failed when it posts the warning (a descriptor changed without a matching testsv2 file). setFailed runs AFTER the comment is posted so the PR comment still shows the table or the warning even when the check goes red. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…ples For descriptors that use a templated intent (placeholders against transaction data), tests should be able to assert both the literal action label and the fully-rendered string separately. Adds an optional `interpolatedIntent` string to the renderedOutput shape that both `expected` (in test files) and `rendered` (in results.json) can carry. Updates: - specs/erc7730-tests-v2.schema.json: add interpolatedIntent (optional) - .github/test-results/README.md: field table + example shape - .github/test-results/pass.example.json: example with both fields - README.md: dedicated 'The expected block' field table Runner-side support is a separate todo — once runners emit interpolatedIntent the migration script will pick up matching expected values, and tests can opt in by including the field. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
🧪 Clear Signing Tests
This PR is from a fork. A maintainer needs to add the Once approved, the tests will run automatically and post screenshots here. |
Renames .github/test-results/ to .github/test-runner-docs/ and rewrites the README as a general guideline for implementing a clear-signing test runner: declares the input contract (pointing to the v2 tests schema and the contributor-facing Reference test cases section in the main README), keeps the results.json output spec, and links to run-sourcify-tests as a reference implementation. Updates path references in composite actions and the v2 tests schema description. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…ept permit & morpho)
Migrates 108 v1 fixtures across 34 entities (1inch, aave, benqi, circle,
consensus-specs, degate, dispatch, ethena, figment, hyperliquid, kiln,
ledgerquest, lens, lido, lifi, lombard, midas, okx, opensea, p2p, paraswap,
poap, rarible, safe, serenita, smartcredit, starkgate, swell, swissborg,
tally, uniswap, walletconnect, weth, yieldxyz) to the v2 schema.
Each fixture restructures v1's flat expectedTexts into v2's
{intent, owner, fields} and adds a dataProvider block with tokens/
addressNames/ensNames populated by Etherscan v2 + on-chain RPC lookups
(symbol/decimals/name). Addresses normalized to EIP-55; deadlines/
expirations converted to 24h UTC with Z suffix. Test descriptions
made unique within each file (required as join key by the runners).
Hand-fixed test-data bugs surfaced by Sourcify:
- 1inch: V3 rawTxs signed so the runner can recover Beneficiary
- circle: addresses, dataProvider, RFC-3339-ish timestamps
- kiln Vault-EURC: dataProvider symbol EURC (not Ledger CAL's EUROC)
- kiln Vault-{RLUSD,USDC,USDT,USDe}-Euler-Yield: added vault address to
dataProvider with vaultTicker symbol; updated Redeem amounts from
Ledger-truncated keey* to canonical kEuler*
- kiln batch-deposit-v2: replaced OCR-truncated Signatures with full hex
- kiln fee-splitter-factory: dropped Transaction:"signed" placeholder
- lens lenshub: 8 IPFS-URI cases were OCR-split with spaces; Comment/mint
had sibling labels leaking into values; Follow/Unfollow array fields
now use the last element (runner's behavior)
- lens token-handle-registry unlink_with_sig: typed-data domain corrected
from {name:"Example",version:"1"} → Lens Protocol Profiles v2
- lifi LIFIDiamond: added XPR/XEN/TRX/DEVVE to dataProvider; signed
unsigned rawTxs; fixed Swap (2)/(5) expected
- midas: switched to dataProvider.addressNames for token-name lookup
- paraswap V6.2: added the missing dataProvider.tokens block; added
Beneficiary to 8 cases; matched runner's "Sender" literal
- uniswap UniswapV3Router02: removed spurious space in "0.3 %" unit
Migrates 131 v1 fixtures: - permit: 74 EIP-712 ERC-2612 Permit messages across 12 chains. V1 had no field values (only labels); rebuilt expected.fields from data.message: Spender (EIP-55 checksum), Max spending amount (formatted via verifyingContract symbol/decimals), Valid until (24h UTC + Z). Each test's dataProvider.tokens[verifyingContract] is populated via on-chain symbol()/decimals()/name() lookups — Etherscan v2 for the chains they cover for free, public RPC for Avalanche/Optimism/Base/ BSC/Fantom/Linea. - morpho: 57 ERC-4626 vault fixtures. Each test's dataProvider.tokens was populated for the vault address itself plus the underlying token (RPC lookup for symbol/decimals/name). For calldata-MorphoBlue, rawTx is decoded and the full nested marketParams (Loan Token, Collateral Token, Oracle, Irm, Lltv) are emitted — the migration script only inferred top-level fields. calldata-MorphoBundlerV3 is intentionally omitted — the v1 fixture only labeled "Action" with no expected value (multicall calldata rendering); leaving it out until a runner can verify the nested render. Note: most of these will currently fail on Sourcify because the test-runner library doesn't follow `includes` when building the format index (permit's formats live in ercs/eip712-erc2612-permit.json; morpho vaults' formats live in ercs/calldata-erc4626-vaults.json). Tracked separately; PR brings the test data into v2 so it's ready when the library lands the fix.
Migrates 30 v1 fixtures across 5 entities upstream added (ethereum#2542+): - flyingtulip (14) - kyberswap (1) - layerswap (1) - ondo-finance (4) - threshold (10) Test data is the initial migration script output; TODO placeholders in expected.fields will be backfilled from runner artifacts in a follow-up based on the deterministic rendered values from the Sourcify runner.
test: migrate v1 .tests.json fixtures to v2 (part 1: all entities except permit & morpho)
test: migrate v1 .tests.json fixtures to v2 (part 2: permit & morpho)
test: migrate v1 .tests.json fixtures to v2 (part 3: 5 new entities)
|
I migrated all existing test data to the new format and fixed the Sourcify implementation to fully match the expectations. Test results can be seen in these PRs: manuelwedler#2 There is only one case left failing on the Sourcify library because it is expected that it is not supported at the moment. |
Temporarily mutes the per-case mismatch gate while test data and the runners stabilise. The "N case/runner combination(s) did not pass" finding still posts to the PR comment table and emits a yellow ::warning:: annotation in the run UI, but the step no longer fails the workflow. Other gates (JSON schema validation in pull_request.yml, the notify-missing-tests guard, and the upload-test-results structural check) are unchanged — broken test files, missing testsv2 fixtures, and missing or malformed results.json files still fail the run. Revert by un-commenting the core.setFailed(failureReason) call.
…<id>" form The Sourcify and Rust runners currently disagree on how to render nftName-format fields: Sourcify emits "Collection Name: pFT NFT - Token ID: 141" while the Rust (llbartekll/clear-signing) cs-test fork emits the shorter "pFT NFT ethereum#141" We're aligning the test fixtures to the shorter form because it's the better display for wallets. Sourcify will fail until its library adopts the same shape; that's tracked separately. Affects three flyingtulip fixtures (PftMarketplace, PftNft, PutManager) covering 16 places — both expected.fields[].value entries for Position fields and the interpolatedIntent strings that embed the same rendering.
…lver API Bumps sourcifyeth/clear-signing-test-runner from c48d3c3f to 9de9e288 — "Switch to library 0.2.0's filesystem resolver". The library dropped the embedded resolver in favour of a custom-resolver API plus a filesystem helper; this bump aligns the runner with the new shape.
Runners don't verify signatures, so signed rawTx hex was just noise in
the fixtures (plus it leaked the test signer's address into every diff
when we re-signed during migration). This switches the convention:
rawTx is now the unsigned canonical pre-signing form, and the
recovered signer — when the test needs one for `@.from` references —
is stored in a new optional `from` field beside rawTx and txHash.
Schema:
- specs/erc7730-tests-v2.schema.json — rawTx description updated to
"unsigned"; new optional `from` field defined (EIP-55 checksummed
string, only required when the descriptor uses `@.from`).
Docs:
- README.md — calldata reference table updated to match.
Test data:
- 103 cases across 32 testsv2 files (1inch, aave, flyingtulip, kyberswap,
layerswap, lido, ondo-finance, paraswap, poap, safe, threshold) had
their signed rawTx replaced with the canonical unsigned form for the
same envelope:
- EIP-1559 / EIP-2930: RLP payload's last 3 elements (yParity,r,s)
dropped.
- Legacy: re-emitted in EIP-155 unsigned shape [..., chainId, 0, 0].
The signer was recovered from the original signed bytes via
Account.recover_transaction and written to the new `from` field
(omitted if recovery failed — e.g. already-unsigned or non-tx blobs
like kyberswap's raw-calldata fixture).
…-field Bumps sourcifyeth/clear-signing-test-runner from 9de9e288 to 3006d68e — "Drop rawTx ecrecovery; take signer from the new optional `from` field". Aligns the runner with the schema change in 1504f20 that switched rawTx to always-unsigned and added a separate `from` field.
9e94a12 to
ff115fb
Compare
The submit() and stakeETH() formats had `interpolatedIntent: "Stake
{@.value} ETH"`. The runner formats `{@.value}` with the native unit
(e.g. "1.078608424000534547 ETH"), so the template rendered as
"Stake 1.078608424000534547 ETH ETH" — the unit appears twice.
Drops the literal trailing " ETH" from both descriptor templates and
updates the two affected testsv2 fixtures' expected.interpolatedIntent
to match the cleaner render.
Bumps llbartekll/clear-signing from 22196d32 to 0809971c — "fix(types): accept formats that omit `intent` (optional per spec)".
#8) The Rust runner resolves nested-call inner descriptors (e.g. the Safe inner call, the kiln inner deposit) only from directories it's told about. Without --registry it sees just the test file's own dir, so nested cases (kiln fee-splitter, Safe 1.4.1, SafeProxyFactory) can't find their inner descriptors and fail. Pass the registry root (derived from the test-file path) so callees resolve tree-wide. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Update: I worked together with @llbartekll to fix any issues on the Rust library too. Both libraries run all tests successfully now (only one expected failure as stated above). See the latest runs here: |
…o warning" This reverts commit 9d6ccb2. Per-case mismatches once again fail the workflow (core.setFailed instead of core.warning). Now that the test data and runners have stabilised, the original "red on any unmet expectation" gate is back.
hesterbruikman
left a comment
There was a problem hiding this comment.
Approving based on successful test runs against React and Rust library shared
manuelwedler#5
manuelwedler#6
manuelwedler#7
@elasticroentgen there are open change requests by you. Could you please give these another review to keep if blocking, dismiss if not?
Discussed offline that comments are non-blocking
Adopts the new test format introduced in ethereum#2586 (Common Test Strategy). The v2 shape lets the CI run the same fixture against multiple test runners (Ledger cs-tester, Sourcify's TypeScript runner, and the upcoming Rust runner) by declaring the rendered output structurally rather than as a loose list of expected strings. Changes per test file: - Moved from registry/figment/tests/ to registry/figment/testsv2/ - Points at specs/erc7730-tests-v2.schema.json - Adds a top-level descriptor field pointing at the .json under test - Adds a dataProvider block with token metadata (USDC/MUSDC as underlyings; xSOLY/xFIGSOL as vault share tokens) so runners execute without hitting a live RPC - Converts rawTx from the signed form to the unsigned EIP-1559 form required by v2 - Replaces the flat expectedTexts array with a structured expected block (intent, owner, fields[]) matching the descriptor's own field order Old-format tests under registry/figment/tests/ removed to avoid duplicate coverage; the pre-existing calldata-figment-batch-deposit.tests.json is untouched and remains under the old path since it belongs to a separate descriptor.
* feat(figment): add Stablecoin Staking Yield clear signing descriptors Adds ERC-7730 descriptors so wallets supporting the ERC-7730 standard render Figment's Stablecoin Staking Yield product transactions with human-readable intents instead of opaque calldata. Three descriptor files under registry/figment/ using the includes + metadata.constants convention: - common-figment-pool-dynamic.json — shared display formats for deposit and requestRedeem, parameterized over the underlying token - calldata-PoolDynamic-USDC.json — mainnet USDC vault (0xe1b1252652A2FF0CC3A4214eE73d9FeD1FEa5b4f) - calldata-PoolDynamic-MUSDC-sepolia.json — Sepolia vault (0xD1f0774ccff0CE4F36DeA57b6a28aB7FeB0a01B0); underlying is a Mock USDC (MUSDC) at 0xfd4f11A2aaE86165050688c85eC9ED6210C427A9 Two test files under registry/figment/tests/ with real on-chain deposit + requestRedeem transactions for both mainnet and Sepolia. On-device labels (all within the 30-char display limit): - Mainnet $id: "Stablecoin Staking Yield USDC" (29) - Sepolia $id: "Stablecoin Staking Yield MUSDC" (30) - deposit intent: "Deposit into Stablecoin Vault" (29) - requestRedeem intent: "Redeem from Stablecoin Vault" (28) Scope is customer-only: deposit + requestRedeem. Operator functions (acceptRedemption, repayRedemption, setExchangeRate, depositOffChain, changeRedemptionDestination, etc.) are signed by Figment's settlement infrastructure (HSM/MPC), never by end users. Mainnet and Sepolia are separate descriptors because metadata.constants is flat per descriptor and the underlying tokens differ across chains (USDC vs MUSDC at different addresses); both reference the same common-*.json so display logic stays single-sourced. * chore(figment): migrate tests to v2 format Adopts the new test format introduced in #2586 (Common Test Strategy). The v2 shape lets the CI run the same fixture against multiple test runners (Ledger cs-tester, Sourcify's TypeScript runner, and the upcoming Rust runner) by declaring the rendered output structurally rather than as a loose list of expected strings. Changes per test file: - Moved from registry/figment/tests/ to registry/figment/testsv2/ - Points at specs/erc7730-tests-v2.schema.json - Adds a top-level descriptor field pointing at the .json under test - Adds a dataProvider block with token metadata (USDC/MUSDC as underlyings; xSOLY/xFIGSOL as vault share tokens) so runners execute without hitting a live RPC - Converts rawTx from the signed form to the unsigned EIP-1559 form required by v2 - Replaces the flat expectedTexts array with a structured expected block (intent, owner, fields[]) matching the descriptor's own field order Old-format tests under registry/figment/tests/ removed to avoid duplicate coverage; the pre-existing calldata-figment-batch-deposit.tests.json is untouched and remains under the old path since it belongs to a separate descriptor. --------- Co-authored-by: hesterbruikman <hester.bruikman@ethereum.org>
Summary
specs/erc7730-tests-v2.schema.json) replaces the legacy flatexpectedTextsarray with a structured{ intent, interpolatedIntent?, owner, fields }shape that mirrors what wallets render. Tests live inregistry/<entity>/testsv2/and reference their descriptor explicitly. An optionaldataProviderblock supplies mock token metadata and address-name lookups so runners don't need network access..github/test-results/defines the singleresults.jsonshape every runner emits (runner,implementation,cases[].{status, rendered|error}), withpass/fail/error/skippedexamples.run-ledger-tests,run-sourcify-tests,run-rust-tests) run in parallel on every PR. Results are collated into a single PR comment with one column per implementation, showing per-case pass/fail and a diff against expected output. The workflow fails when any case fails or when a touched descriptor has notestsv2/file.run-testslabel before any runner executes; same-repo PRs run automatically. This gating can be removed if the updated Ledger test runner doesn't rely on private repos or secrets anymore.What's missing
testsv2/input format and emit the unifiedresults.jsonoutput format.tests/artifacts once Ledger's runner has migrated and all test files are ported; thetestsv2/folder can then be renamed back totests/.