pin(invariants): HyperEVM invariant surface (RAI-1511) - #277
Conversation
📝 WalkthroughWalkthroughThe PR adds a 29-token HyperEVM production table and extends fork-based deployment validation. HyperEVM parity checks compare deployment legs with Base and enforce a bootstrap deadline. A production test verifies deployment pins and beacon ownership. ChangesHyperEVM production support
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant ParityTest
participant HyperEVM
participant Base
participant ProductionChecks
ParityTest->>HyperEVM: Fork and capture deployment legs
ParityTest->>Base: Read reference deployment state
ParityTest->>ParityTest: Compare policy, implementations, metadata, and ordering
ProductionChecks->>HyperEVM: Validate deployment pins and beacon ownership
Possibly related PRs
Suggested reviewers: Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
e62574b to
febb397
Compare
1754e75 to
e525075
Compare
06a06a1 to
8dcc2c4
Compare
e6ef7d0 to
9d29e63
Compare
103833b to
6a9b197
Compare
d7ff7bb to
ce3d0ee
Compare
6a9b197 to
866cabe
Compare
ce3d0ee to
3115e88
Compare
866cabe to
2d5c3bf
Compare
3115e88 to
9ee4c51
Compare
2d5c3bf to
e421b17
Compare
e421b17 to
eb0a0a4
Compare
9ee4c51 to
0265805
Compare
eb0a0a4 to
7e515e4
Compare
0265805 to
0c844b2
Compare
6097022 to
2b644e8
Compare
3548b6c to
1117683
Compare
2b644e8 to
6097022
Compare
1117683 to
3548b6c
Compare
6097022 to
2b644e8
Compare
3548b6c to
1117683
Compare
2b644e8 to
43080d8
Compare
- productionTokensHyperEvm(): 29 all-zero rows in Base row order — the explicit "missing on this chain" state the gap-filling deploy targets and the parity pin flags until hydrated from the executed deploy. - Cross-chain parity: HyperEVM third leg + full HyperEVM-vs-Base comparisons, gated on RPC availability (the shared rainix test workflow has no HyperEVM secret slot yet — loudly pending, and the new HYPEREVM_PARITY_DEADLINE (2026-11-01) turns a still-missing RPC or still-pending leg into a failure rather than letting the gap live forever). - StoxProdV4.testProdDeployHyperEvmV4: the impl-deploy + beacon-migration forcing function, env-gated the same way. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VPs1hCTxusmaSeFKvoc4Kr
1117683 to
6e82100
Compare
The premise these guards stated — the shared rainix test workflow has no HyperEVM secret slot, so CI cannot create the fork — is dead. #275 forwards `RPC_URL_HYPEREVM_FORK` to the shared workflow and #276 deleted the identical guards from `HyperEvmBeaconOwnership.t.sol` against a real fork. They were fail-open: had the RPC lapsed, `testProdDeployHyperEvmV4` and the HyperEVM half of `testCrossChainParity` would have gone green having asserted nothing. Both now fork unconditionally, so a missing RPC fails at fork time. `assertTrue(hyperRpcAvailable, ...)` in the parity deadline block goes with the bool — a missing RPC is no longer something to detect later. The deadline and its three leg assertions stay, as does every inner `base.X && hyper.X` gate. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Guard-removal experiment: the HyperEVM RPC gates are gone, and CI stayed green
Why they were staleEvery guard stated the same premise in code or comment: the shared rainix test workflow has no HyperEVM secret slot yet (RAI-1511), so in CI the fork cannot even be created. That premise is dead:
The guards were dead code. They were still worth removing because they were fail-open: had the RPC ever lapsed, both tests would have gone green having asserted nothing — the exact hole #275/#276 closed. What was removed
Deliberately kept: every inner Verdict: green, and demonstrably not vacuousRun 31388402997 — all six checks pass. A vacuous pass looks identical to a real one, so the evidence:
The gas deltas against the pre-removal run are the guard overhead and nothing else: One thing worth knowingNote the preflight line says Also out of scope but adjacent: Not merging — this is the experiment's result, not a merge request. |
Brings in #274, #275 and #276. #275's `RPC_URL_HYPEREVM_FORK` forwarding in `rainix-sol.yaml` is what lets this branch's own push run resolve the HyperEVM alias from the secret rather than the rainix preflight's public default, so the unconditional forks this branch introduces are exercised against the secret path the new comments describe.
All three token tables allocate `new TokenInstance[](29)` and assign indices 0..28 contiguously, and `productionReceiptVaults()` derives its array from `productionTokensBase().length`. Five NatSpec lines still said 28 — including a `@return` four lines under a `@notice` that already said 29, so one doc block contradicted itself. Counted from the arrays rather than assumed: Base, Ethereum and HyperEVM are 29 rows each with no `address(0)` entry in any of them. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/lib/LibTokenInvariants.sol (1)
335-338: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winCorrect the stale token count in the docstring.
productionTokensEthereum()allocates 29 entries and populates indices 0 through 28. The docstring still says "28 underlyings" and "The 28 production token instances on Ethereum". Update both to 29.📝 Proposed doc fix
/// `@notice` Returns the production token instance triples on Ethereum - /// mainnet — the same 28 underlyings as Base, in the same order, so the + /// mainnet — the same 29 underlyings as Base, in the same order, so the /// two tables pair by index as well as by key. - /// `@return` tokens The 28 production token instances on Ethereum. + /// `@return` tokens The 29 production token instances on Ethereum.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/lib/LibTokenInvariants.sol` around lines 335 - 338, Update the productionTokensEthereum() documentation to state 29 underlyings and 29 production token instances, matching its 29 allocated and populated entries.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/lib/LibTokenInvariants.sol`:
- Around line 534-709: Replace every tuple in productionTokensHyperEvm() with
the verified HyperEVM deployment-specific addresses rather than reusing Ethereum
records. Add the corresponding broadcast name, run ID, and date provenance, and
ensure tokenTableState does not mark tokenLegLive or pass assertAll until those
HyperEVM tuples are available.
In `@test/src/concrete/deploy/StoxCrossChainParity.t.sol`:
- Around line 572-627: Extract the duplicated per-chain parity assertions from
testCrossChainParity into one descriptively named helper that accepts the
baseline and comparison ChainLegs values plus the chain-specific message suffix.
Move all safe, clone, token-leg, and TokenConfigSnapshot field comparisons into
that helper, then invoke it for each non-baseline chain while preserving the
existing live-leg gating and assertion behavior.
---
Outside diff comments:
In `@src/lib/LibTokenInvariants.sol`:
- Around line 335-338: Update the productionTokensEthereum() documentation to
state 29 underlyings and 29 production token instances, matching its 29
allocated and populated entries.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 163abd4c-7926-4686-b70d-2092ab5d9aeb
📒 Files selected for processing (3)
src/lib/LibTokenInvariants.soltest/src/concrete/deploy/StoxCrossChainParity.t.soltest/src/concrete/deploy/StoxProdV4.t.sol
Follow-up:
|
|
Reviewed dbb7588: approve |

Pins the HyperEVM production surface into the shared invariant libraries and turns on the HyperEVM legs of the existing invariants.
mainis merged in, bringing #274, #275 and #276.productionTokensHyperEvm(): 29 real token rows in Base row order — 87 pinned production addresses (receipt, receipt vault and wrapped vault per underlying, MSTR through RKLB), matching Base's underlyings and ordering one-for-one. There is noaddress(0)in the table: every row is a live HyperEVM deployment, so the parity pin asserts against real chain state rather than flagging a gap. Verified in CI, not by inspection —StoxCrossChainParityTest.testCrossChainParityforks HyperEVM and checks ownership by the HyperEVM token-owner Safe, the clone as sole authoriser, per-token config, and the shared beacon, then compares all of it against Base.Cross-chain parity: HyperEVM third leg + full HyperEVM-vs-Base comparisons. The HyperEVM fork and its legs run unconditionally, exactly like Base and Ethereum — there is no RPC gate.
HYPEREVM_PARITY_DEADLINE(2026-11-01) remains as the bootstrap forcing function: past it, a HyperEVM Safe, authoriser or token leg still reporting pending fails the suite, so a leg that never arms cannot stay indistinguishable from one that passes. Each individual comparison is still gated on both chains carrying the relevant leg.StoxProdV4.testProdDeployHyperEvmV4: the impl-deploy + beacon-migration forcing function, forking unconditionally the same way.NatSpec counts corrected to 29 in
LibTokenInvariants. All three token tables allocate 29 and fill indices 0..28, andproductionReceiptVaults()derives its length fromproductionTokensBase(), but five doc lines still said 28 — including a@returnsitting four lines under a@noticethat already said 29.CI supplies
HYPEREVM_RPC_URLto the shared rainix test workflow from theRPC_URL_HYPEREVM_FORKsecret (#275), so a missing RPC is a hard failure at fork time rather than a test that passes having asserted nothing.Co-Authored-By: Claude Opus 4.8 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01VPs1hCTxusmaSeFKvoc4Kr
Summary by CodeRabbit
New Features
Tests