chore(pragma): pin every concrete .sol file to =0.8.25 - #23
Conversation
The convention is `^` for library and abstract files, `=` for concrete contracts including concrete test mocks. All three concrete .sol files in this repo floated `^0.8.25`; they now pin exactly. `src/lib/LibRainDeploy.sol` is a library that downstream soldeer consumers compile, so it keeps `^0.8.25` — a hard pin there would break a consumer on a different 0.8.x. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The Zoltu factory is a CREATE2 proxy with a zero salt, so the address it deploys to is a pure function of the creation code. Pinning the concrete test files to =0.8.25 moves the whole compilation unit from solc 0.8.35 (what ^0.8.25 floats to in the rainix toolchain) down to 0.8.25, which changes MockDeployable's creation code and therefore its deployed address and code hash. Address 0xC24016f2..0xC24016f209562fc151e5Ab7F88694ED5775feb36 becomes 0x1fa1bBf9Cf73B1aCCc1a3D9de5896E81Cd567854 and the code hash becomes 0x6ea525f6523fe148810254f04b9a74a379f59ca0f3a7fa83db90b691c78cc299. Both values are confirmed twice over: cast create2 derives the address from the CREATE2 formula given the factory, a zero salt and the 0.8.25 creation code, and live fork execution deploys to exactly that address. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
WalkthroughThe deployment test pragma changes from ChangesDeployment test consistency
Estimated code review effort: 1 (Trivial) | ~2 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 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 |
|
👤 human |
|
Correcting the emphasis of the reject above, because as written it could send the rework the wrong way. The defect is the magic numbers. Not the compiler version. My note led with the suite exercising the library at 0.8.25 rather than 0.8.35, and framed that as the headline. That was wrong. It is not a defect of this PR — it is the ordinary consequence of the convention itself: concrete files pin exactly, libraries float, so tests always compile a library at the pinned version while consumers may compile it anywhere in Read literally, my note invited the rework to preserve 0.8.35 coverage by un-pinning — the opposite of what the convention asks. Do not do that. The actual finding, which stands and is the whole reason this is a reject: the test file hardcodes Derive them instead, and the compiler question stops existing: the expected value recomputes from the creation code under whatever solc is in use, so neither a pragma pin nor a future rainix bump can falsify it. So: land #21 first, keep the pragma pins from this PR, and replace every hardcoded literal with the derivation — except in |
Resolves the conflict in test/src/lib/LibRainDeploy.t.sol by taking main's side in all nine hunks. #21 landed `LibRainDeploy.zoltuAddress`, and main already derives MockDeployable's Zoltu address and codehash from `type(MockDeployable).creationCode` instead of hardcoding them, and moved the concrete mocks to test/concrete/. The hand-updated literals this branch carried are exactly what the send-back rejected, so none of them survive. What survives from this branch is the pragma pin itself: the test file is a concrete contract and pins `=0.8.25`. The three mocks already pin on main. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
test/src/lib/LibRainDeploy.t.sol (1)
305-309: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winMove the hardcoded address to the independent-oracle test.
testDeployZoltustill checks Line 310 with a compiler-dependent literal. UsemockDeployableAddress()in that test. Keep the literal intestZoltuAddressMatchesFactory, where it can independently validate both the factory result andLibRainDeploy.zoltuAddress. This avoids manual updates when compiler settings orMockDeployablebytecode changes.Suggested test split
- // Pinned literal, deliberately not `mockDeployableAddress()`. - // The live factory on the fork is the oracle here. - assertEq(deployed, 0x1fa1bBf9Cf73B1aCCc1a3D9de5896E81Cd567854); + assertEq(deployed, mockDeployableAddress()); function testZoltuAddressMatchesFactory() external { vm.createSelectFork(LibRainDeploy.ARBITRUM_ONE); + address deployed = this.externalDeployZoltu(type(MockDeployable).creationCode); + assertEq(deployed, 0x1fa1bBf9Cf73B1aCCc1a3D9de5896E81Cd567854); assertEq( LibRainDeploy.zoltuAddress(type(MockDeployable).creationCode), - this.externalDeployZoltu(type(MockDeployable).creationCode) + deployed );Also applies to: 523-540
🤖 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 `@test/src/lib/LibRainDeploy.t.sol` around lines 305 - 309, Update testDeployZoltu to use mockDeployableAddress() instead of the compiler-dependent hardcoded address when checking the deployed result. Keep the pinned literal exclusively in testZoltuAddressMatchesFactory so that test independently compares the factory result with LibRainDeploy.zoltuAddress, including the corresponding assertions in the later referenced test section.
🤖 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.
Outside diff comments:
In `@test/src/lib/LibRainDeploy.t.sol`:
- Around line 305-309: Update testDeployZoltu to use mockDeployableAddress()
instead of the compiler-dependent hardcoded address when checking the deployed
result. Keep the pinned literal exclusively in testZoltuAddressMatchesFactory so
that test independently compares the factory result with
LibRainDeploy.zoltuAddress, including the corresponding assertions in the later
referenced test section.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 950a7aec-ef78-48f4-bc56-a9caf9c2b5c7
📒 Files selected for processing (1)
test/src/lib/LibRainDeploy.t.sol
|
🤖 ai:producer |
…enumerated #22 enumerated three files because three was all the repo had. Its check clause is the category: every concrete contract pins exactly, only libraries and abstracts float. #26 landed twelve more concrete `contract` declarations under `test/src/**.t.sol`, every one of them floating `^0.8.25`, so closing #22 on the single file it named would have closed it with the category unmet. Every `.t.sol` under `test/src/` declares a plain `contract` — concrete, nothing downstream compiles it — so all thirteen now pin `=0.8.25`. Left floating, and correctly so: `src/lib/*` libraries, `src/abstract/*` and `test/abstract/*` abstracts, `src/interface/*`, and `src/generated/candidate/AddressRegistry.sol`, which declares no contract at all and is emitted by rain-sol-codegen. The two `"pragma solidity ^0.8.25;\n\n"` string literals in LibRainDeploySnapshot.t.sol are untouched on purpose: they are the expected TEXT of a generated file, and the generator emits a caret. Bytecode-neutral. `foundry.toml` pins `solc = "0.8.25"` since #26, so every file here already compiled at 0.8.25 and `forge build` reports the same 64 files at the same compiler before and after. The pin makes the constraint explicit at the file that states it instead of leaving it to a build setting that a future bump would silently move. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Closes #22
The convention is
^for library and abstract files,=for concretecontracts including concrete test contracts. This applies it to every
concrete
.solin the repo.What this pins
Every
.t.solundertest/src/declares a plaincontract—contract LibRainDeployTest is Test,contract AddressRegistryDeployChainTest is AddressRegistryDeploySuites, RainDeployVerifyChain,and so on for all thirteen. Declarations were read from source, not inferred
from paths. All thirteen move
^0.8.25→=0.8.25, one line each:Thirteen files, thirteen lines. No reformatting, no reordering, no other edit of
any kind.
It closes #22's category, not its three-row table
#22 tabulated three files because three was all the repo had when it was filed.
Two of those were pinned and moved to
test/concrete/by #21. #26 then landedtwelve more concrete
contractdeclarations undertest/src/, every one of themfloating.
The clause that binds is #22's own check — "Every concrete contract in this repo
pins exactly." That is a property, not a list. Pinning only the single file #22
happened to name would have closed the issue with the property false in twelve
places, and CodeRabbit's linked-issues check flagged exactly that. So the sweep
covers the category as
mainstands today.What stays floating, and why
src/lib/*.sollibrary0.8.xsrc/abstract/*.sol,test/abstract/ExampleDeploySuites.solabstract contractsrc/interface/IAddressRegistryV1.solinterfacesrc/generated/candidate/AddressRegistry.solsrc/lib/LibRainDeploy.solis the file the convention exists to protect, and itis absent from the diff.
The generated record is the one that needs spelling out. It declares no contract
at all — four file-level constants and a header — so it is not a concrete
contract in the first place. It is also autogenerated by
rain-sol-codegen, andthe caret is the text that generator emits:
test/src/lib/LibRainDeploySnapshot.t.sol:584and:627assert"pragma solidity ^0.8.25;\n\n"as the expected generated output, so pinning itwould fail the suite. It keeps the caret deliberately. Only line 3 of each file
in the diff was edited, for exactly this reason — those two string literals sit
in a file this PR does touch.
Bytecode-neutral, and no longer an argument about 0.8.35
An earlier revision of this PR argued that
^0.8.25floats to whatever solcrainix ships — 0.8.35 at the time — so pinning moved
MockDeployable's creationcode, moved its zero-salt CREATE2 address, and broke seven tests that hardcoded
it. That argument is dead, and every claim downstream of it goes too. #26
added to
foundry.toml:The compiler is now pinned by build config, so every file in the repo already
compiles at 0.8.25 whatever its pragma says. Measured on unmodified
main:forge build --force→Compiling 64 files with Solc 0.8.25. Identical on thishead. No creation code moves, no address moves, no code hash moves, and not one
literal is touched anywhere in the diff.
What the pragma pin still buys is that the constraint is declared at the file
that has it, rather than inherited from a build setting three directories away.
A future
solcbump infoundry.tomlthen fails loudly on the concrete fileswhose bytecode is pinned under
src/generated/, instead of silently recompilingthem at a new compiler.
QA
runtime footprint, so no test can observe it directly. It is also
bytecode-neutral on this base —
foundry.tomlpinssolc = "0.8.25", so allthirteen files already compiled at 0.8.25 and the pin makes explicit what an
import graph and a build setting were enforcing implicitly. The evidence that
nothing moved is the suite run as a differential:
forge test --no-match-contract Chaingives 93 passed / 26 failed on thishead and 93 passed / 26 failed on bare
origin/main— same counts, sametest names. All 26 are
vm.createSelectFork: environment variable ARBITRUM_RPC_URL / BASE_RPC_URL not found: no.envin the run, soenvironmental, and all of them in
LibRainDeployTest.mutate. The mutation that mattered ran on the head rejected on 2026-07-30:
hand-pinning moved the CREATE2 address, 7 tests failed, and the response was to
hand-update 18 literals. That head is gone.
mainderives the address andcodehash from
type(MockDeployable).creationCodeviaLibRainDeploy.zoltuAddress(landed in fix(deploy): check the derived Zoltu address before skipping a network #21), so a compiler bump recomputes theexpected value instead of falsifying it and no literal in this diff can go
stale.
^for library and abstract files,=forconcrete ones including test contracts — applied to every concrete
.solinthe repo rather than to the three All three concrete .sol files float their pragma; only the library is correct #22 listed, because the issue's check clause
states the property and the table only illustrated it.
.solfiles pin exactly,that
LibRainDeploy.solbe left alone, and that the suite stay green. Allthree hold on this head.
git grep 'pragma solidity'shows=0.8.25on everyconcrete contract — both
script/*.sol,src/concrete/AddressRegistry.sol,all seven files under
test/concrete/, and all thirteentest/src/**/*.t.sol— and^0.8.25only on libraries, abstracts, theinterface, and the generated record. The linter follow-up All three concrete .sol files float their pragma; only the library is correct #22 mentions stays
out of scope.
Verification
All via
nix develop -c, on this head withmain(8d1e5fd) merged in:forge build --forceCompiling 64 files with Solc 0.8.25→Compiler run successful!forge test --no-match-contract Chainmainslither .forge fmt --checkreuse lintThe 26 failures are environmental and pre-existing: there is no
.env, and everyone is a
vm.createSelectForkenv-var error rather than an assertion. The samecommand on bare
origin/maingives the same 93 / 26.One thing that surfaced during that run and is not this PR's to fix: CLAUDE.md
describes
forge test --no-match-contract Chainas the fork-free snapshot gate,"nothing reachable from those contracts forks anything".
LibRainDeployTestforks and is not matched by that filter, and it is where all 26 failures come
from. Worth an issue against the docs or the test layout, separately.
Relationship to #20
#20 is still open and adds a fourteenth concrete file,
test/concrete/MockAddressRevertingFactory.sol. It already pins=0.8.25there, so there is nothing left for this PR to sweep on its behalf. The two
touch disjoint files and land in either order; whichever lands second wants a
rebuild, since #20 also changes
src/lib/LibRainDeploy.sol.