Skip to content

Sparrow dom/foundry updates - #2957

Open
sparrowDom wants to merge 5 commits into
masterfrom
sparrowDom/foundryUpdates
Open

Sparrow dom/foundry updates#2957
sparrowDom wants to merge 5 commits into
masterfrom
sparrowDom/foundryUpdates

Conversation

@sparrowDom

@sparrowDom sparrowDom commented Aug 1, 2026

Copy link
Copy Markdown
Member

Foundry does the deploying now, but the Talos crons and the Hardhat task CLI still resolve contracts from deployments/<network>/<Name>.json, and Foundry writes only addresses, to build/deployments-<chainId>.json — so a redeploy silently left those descriptors pointing at the previous contract. After each broadcast a script now rewrites them in place, keyed off contractName from run-latest.json, which turns out to be the descriptor filename verbatim. Nothing downstream changes.

The descriptor also becomes the storage-layout baseline, replacing the Hardhat upgrade gate — which is broken on this branch anyway, since cache/validations.json is stale and VaultCore is now abstract. _recordDeployment takes type(X).name and compares the new layout against what is deployed, aborting the run before anything broadcasts. Comparison is @openzeppelin/upgrades-core, which already implements the __gap shrink arithmetic; we add only two policies — a rename passes only when the new label derives from the old (assets_deprecated_assets), since two same-typed variables trading labels also reports as two renames, and enum members, which solc omits from layouts, are ignored. Baselines were seeded from each deployed implementation's verified source on chain, which lets storageLayout/ and the orphaned solcInputs/ go (~139 MB). The gate commit and the seeding commit have to land together.

Also here: @oplabs/talos-client is imported lazily so hardhat deploy in the ABI publish workflow no longer needs GitHub Packages auth, and storage gaps are unified on __gap.

Code Change Checklist

To be completed before internal review begins:

  • The contract code is complete
  • Executable deployment file
  • Fork tests that test after the deployment file runs
  • Unit tests *if needed
  • The owner has done a full checklist review of the code + tests

Internal review:

  • Two approvals by internal reviewers

Deploy checklist

Two reviewers complete the following checklist:

- [ ] All deployed contracts are listed in the deploy PR's description
- [ ] Deployed contract's verified code (and all dependencies) match the code in master
- [ ] Contract constructors have correct arguments
- [ ] The transactions that interacted with the newly deployed contract match the deploy script.
- [ ] Governance proposal matches the deploy script
- [ ] Smoke tests pass after fork test execution of the governance proposal

sparrowDom and others added 5 commits July 30, 2026 18:31
Three contracts used non-standard gap names (______gap, _____gap, ___gap)
that scripts/check-storage-layout.js did not recognise as gaps, so a
legitimate gap shrink in them would have been reported as a removed
variable. Initializable is inherited widely, so most upgradeable layouts
were affected.

Renaming is storage-neutral -- verified with forge inspect before/after
across OUSD, OETHVault, VaultCore, WOETH, OETHHarvesterSimple and
OUSDResolutionUpgrade: slot, offset, type and the types dictionary are
identical, only labels change. OZ assertStorageUpgradeSafe does flag a
rename, so the committed layout snapshots are relabelled in the same
change; without that the next deploy of anything inheriting Initializable
would fail the upgrade gate.

OUSD keeps its legacy _gap: it declares both _gap and __gap in the same
contract, so they cannot share a name.
The package is an optional peer dependency so CI and external contributors
can install without GitHub Packages auth, but tasks/lib/network.ts imported
it at module scope. That pulled it into hardhat.config.js's load path via
tasks.js -> utils/morpho.js -> utils/resolvers.js, so any `npx hardhat`
command failed with "Cannot find module" in a fresh checkout -- and the
abi.yml release workflow, which installs without auth and then runs
`hardhat deploy`, would fail on the next v* tag.

Move the import inside rpcUrlFor(), the only place it is used, so importing
the module no longer requires the package. Verified: with the package
removed, `hardhat --help` still registers every task and `hardhat compile`
succeeds; with it present, chain and RPC resolution are unchanged and the
action catalog still dumps 43 actions.

Add `pnpm install:talos` for the case where it is genuinely needed (running
actions, KMS signer). It sources a token from the gh CLI, checks the token
can actually read the package -- `gh auth login` does not request
read:packages -- installs via a throwaway npm config, then restores
package.json and pnpm-lock.yaml so the package is never committed as a
dependency, which would break CI.
…t tasks and talos task code can remain unchanged
Replaces the Hardhat-based upgrade gate, which is broken on this branch
(cache/validations.json is stale and VaultCore is now abstract).

The baseline moves into the deployment descriptor, so it is written by the
same step that records the address and the write side cannot silently stop.
The check runs inside _recordDeployment, which is mandatory, and takes the
contract name as type(X).name so a rename cannot point it at the wrong
baseline. A revert there still aborts before broadcast — forge runs the whole
script against the fork before submitting — verified against anvil: no
transactions, no block mined, ledger untouched.

Comparison is @openzeppelin/upgrades-core, which already implements the
__gap shrink arithmetic. Two policies are ours: a rename is ignored only when
the new label derives from the old (assets -> _deprecated_assets), since two
same-typed variables trading labels also reports as two renames; and enum
member data, absent from solc layouts, is ignored — an enum is one byte
regardless of variant count, so no slot moves.

Gap labels are normalised to __gap in memory because OZ's isGap() matches
only __gap/__gap_*, while contracts deployed before the rename still carry
______gap on chain. Stored layouts stay faithful to their source.

Scope is mainnet and Base. foundry.toml gains extra_output = ["storageLayout"]
(extra_output_files alone emitted nothing, which also broke forge inspect).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GueER8JLB5U4MBLEbFERAK
…y trees

Descriptors become the single record of a deployed contract:
{address, abi, storageLayout}. 103 layouts were read from each deployed
implementation's verified source on chain via cast storage (71 mainnet,
32 Base); the remaining 61 came from the legacy Hardhat snapshots, used only
where the chain fetch could not run — contracts compiled with solc 0.5.x that
the current toolchain cannot rebuild, and stateless contracts whose layout is
empty anyway. 16 descriptors get no layout: 12 proxies, which declare no
storage, and 4 deprecated contracts whose source has left the repo.

Layouts are stored faithfully, exactly as their source produced them, so a bug
in the in-memory gap normalisation never costs a re-fetch. Note this means
some baselines legitimately carry ______gap, which is what is deployed.

Deletes storageLayout/ (216 files) — baselines now live in descriptors, and
the name-keyed snapshots had already rotted into fossils for VaultCore and
VaultAdmin, both abstract and undeployable since #2714. Re-supporting a chain
means re-fetching its layouts; see CLAUDE.md.

Deletes deployments/*/solcInputs/ (217 files, ~139 MB) — only hardhat-deploy's
verification path read them, and verification does not run through Hardhat.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GueER8JLB5U4MBLEbFERAK
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.

1 participant