feat(safe): Ethereum token-owner Safe — distinct per-chain address pin + policy-parity - #232
Conversation
|
Warning Review limit reached
Next review available in: 37 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (2)
✨ 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 |
e603338 to
43cfd27
Compare
199bcee to
4b188bd
Compare
4b188bd to
779689c
Compare
43cfd27 to
002a564
Compare
779689c to
7b93f95
Compare
…thereum RAI-1109 (Josh, 2026-07-07): give Ethereum the SAME token-owner Safe address as Base rather than a fresh per-chain address. A Safe address is CREATE2(proxyFactory, keccak(keccak(initializer) ++ saltNonce), keccak(proxyCreationCode ++ singleton)), so replaying the exact genesis creation call on Ethereum yields the identical address. - LibStoxSafeGenesis pins the genesis params recovered from the Base creation tx (Safe tx service): the Safe was CREATED as v1.3.0 (factory 0xa6B71E26…, singleton 0x3E5c6364…), saltNonce 0, with the verbatim setup initializer — 3 owners / threshold 2 — then later upgraded to v1.4.1 and expanded to the current 6 owners / threshold 3. Address is fixed by the v1.3.0 creation params, so the reproduction uses those. The v1.3.0 factory + singleton + fallback are already live at the same addresses on Ethereum (verified 2026-07-07). - 20260707-deploy-safe-ethereum.s.sol: EOA-broadcast createProxyWithNonce with the pinned genesis inputs, asserting the produced proxy == LibSafeInvariants.STOX_TOKEN_OWNER_SAFE (a genesis-param drift fails loudly), guarded against re-running once the target has code. - Fork test PROVES it: runs the real v1.3.0 factory on an Ethereum fork and asserts the proxy is byte-for-byte the Base Safe address, in the genesis 3-owner/threshold-2 state. Scope boundary: this puts the matched ADDRESS on-chain in its genesis state. Reaching Base's CURRENT policy (upgrade to v1.4.1, add the 3 later owners, raise threshold to 3) is a separate post-deploy replay signed by the genesis owners — documented in the runbook § 3a. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VPs1hCTxusmaSeFKvoc4Kr
… reproduction Abandon the matched/deterministic Safe address (Josh, 2026-07-15). Each chain gets its OWN ST0x owner Safe at a distinct address — deployed out-of-band as a clean v1.4.1 Safe with Base's owner set + threshold + policy, then pinned. The address is a per-chain deploy artifact (like the clone + token addresses), NOT a principal; the whole POLICY is the shared pin set. - Delete the genesis-replay machinery: `20260707-deploy-safe-ethereum.s.sol` and `LibStoxSafeGenesis`. We deploy the Safe ourselves; no in-repo script. - `LibSafeInvariants`: add `STOX_TOKEN_OWNER_SAFE_ETHEREUM` (placeholder), `safeForChainId(block.chainid)`, an order-INSENSITIVE `assertOwnerSetUnordered` (a fresh Safe's getOwners() order is incidental — not a policy property), and `assertPolicyMatchesBase` = immutable v1.4.1 identity + owner SET + threshold. - Replace the deploy test with `EthereumTokenOwnerSafeParityTest`: placeholder- gated (PENDING until pinned), then asserts the live Ethereum Safe matches Base's policy in every way that matters, and is a distinct address. Runs in CI, so it stays red into the future on any drift. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VPs1hCTxusmaSeFKvoc4Kr
…leton Pin STOX_TOKEN_OWNER_SAFE_ETHEREUM to the deployed Safe (0x3840aeDaEc8e82f79d8F6a8F6ADCa271E13E0329) and make the Safe identity check L1/L2-tolerant: Ethereum mainnet runs the v1.4.1 L1 `Safe` singleton (the Safe UI default on mainnet) while Base runs the L2 `SafeL2`. assertImmutableInvariants now accepts either known SafeProxy codehash and either canonical v1.4.1 singleton (+ its codehash), keeping owners/threshold/version/modules/guard/ fallback as the shared policy. Proxy codehash stays the first (raw) check so an EOA/fake proxy is still rejected before any call. Verified on live forks: the Ethereum Safe matches Base's policy in every way EXCEPT threshold (still 1-of-6; being raised to 3-of-6 on-chain) — so the parity test is red on exactly that until the changeThreshold lands. Base's L2 path unchanged (LibSafeInvariantsTest 12/12). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VPs1hCTxusmaSeFKvoc4Kr
855d150 to
3cd70ce
Compare
…ddress(0) The Ethereum Safe is pinned (#232), so the token-deploy pre-flight reverts through assertPolicyMatchesBase's proxy-codehash check when the pinned Safe isn't live on the active chain (no code with no fork), rather than through the address(0) EthereumSafeNotReady guard. Update the authorize/verify inverted tests to expect SafeProxyCodehashMismatch. Stable regardless of the Safe's on-chain threshold. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VPs1hCTxusmaSeFKvoc4Kr

RAI-1109 (Josh, 2026-07-07): give Ethereum the SAME token-owner Safe
address as Base rather than a fresh per-chain address.
A Safe address is CREATE2(proxyFactory, keccak(keccak(initializer) ++
saltNonce), keccak(proxyCreationCode ++ singleton)), so replaying the
exact genesis creation call on Ethereum yields the identical address.
LibStoxSafeGenesis pins the genesis params recovered from the Base
creation tx (Safe tx service): the Safe was CREATED as v1.3.0 (factory
0xa6B71E26…, singleton 0x3E5c6364…), saltNonce 0, with the verbatim
setup initializer — 3 owners / threshold 2 — then later upgraded to
v1.4.1 and expanded to the current 6 owners / threshold 3. Address is
fixed by the v1.3.0 creation params, so the reproduction uses those.
The v1.3.0 factory + singleton + fallback are already live at the same
addresses on Ethereum (verified 2026-07-07).
20260707-deploy-safe-ethereum.s.sol: EOA-broadcast
createProxyWithNonce with the pinned genesis inputs, asserting the
produced proxy == LibSafeInvariants.STOX_TOKEN_OWNER_SAFE (a
genesis-param drift fails loudly), guarded against re-running once the
target has code.
Fork test PROVES it: runs the real v1.3.0 factory on an Ethereum fork
and asserts the proxy is byte-for-byte the Base Safe address, in the
genesis 3-owner/threshold-2 state.
Scope boundary: this puts the matched ADDRESS on-chain in its genesis
state. Reaching Base's CURRENT policy (upgrade to v1.4.1, add the 3
later owners, raise threshold to 3) is a separate post-deploy replay
signed by the genesis owners — documented in the runbook § 3a.
Co-Authored-By: Claude Fable 5 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01VPs1hCTxusmaSeFKvoc4Kr
Linear: RAI-1109. Sits right after the network-wiring PR (#227), below principals. Fork test proves it: the pinned genesis params reproduce the exact Base Safe address on a live Ethereum fork. Scope = the matched address in its genesis state (3 owners / threshold 2, v1.3.0); the genesis→current policy replay (upgrade to v1.4.1, +3 owners, threshold 3, signed by the genesis owners) is documented in the runbook § 3a as a follow-up. Alternative was a fresh v1.4.1 Safe with a different address.
🤖 Generated with Claude Code
https://claude.ai/code/session_01VPs1hCTxusmaSeFKvoc4Kr