Repository navigation
Migrate dependency management from git submodules to soldeer #194
Description
Migrate dependency management from git submodules to soldeer
The repo currently uses 6 git submodules (forge-std, rain.string, rain.datacontract, rain.math.fixedpoint, rain.deploy, rain.sol.codegen) plus an openzeppelin-contracts submodule under rain.math.fixedpoint/lib/. Switch to soldeer for dependency management.
Why
- Smaller clones — soldeer fetches archives, not full git history of each dep.
- Cleaner versioning — semver lock via
soldeer.lockrather than tracking arbitrary commit SHAs across.gitmodules+foundry.lock. - First-class foundry support (
forge soldeer install). - Easier transitive resolution; today each indirect dep has to be added explicitly as a submodule.
- CI simpler: no
submodules: recursivecheckout.
What changes
- Delete
.gitmodules. Removelib/from version control (or keep it as the soldeer install target). - Replace
foundry.toml'sremappings = [...]with a[soldeer]and/or[dependencies]section. - Add
soldeer.lock(committed) that pins resolved versions. - Add
forge soldeer installto every CI job that builds (rainix.yaml,manual-sol-artifacts.yaml, etc.) and dropsubmodules: recursivefromactions/checkout@v4. nix developshell needs to provide a soldeer-awareforge(currentnix developalready does, modulo flake.lock bumps).
Per-dependency decisions
Each rain.* dep needs to be either:
- Published to the soldeer registry (https://soldeer.xyz) — clean semver consumption.
- Declared as a git-source dependency in
foundry.toml—rain.deploy = { url = "...", ref = "<sha>" }. Equivalent to submodules with different syntax; doesn't gain semver benefits.
Recommendation: publish rain.* to soldeer first; consume via published versions in this repo.
Bytecode-reproducibility check (must pass before merge)
The Zoltu deterministic deploy address is a function of creation code, which transitively depends on every byte of every dependency. Confirm keccak256(type(DecimalFloat).creationCode) is identical before and after the migration on the same source revision; if it shifts, LibDecimalFloatDeploy.ZOLTU_DEPLOYED_DECIMAL_FLOAT_ADDRESS and the contract codehash constants must be regenerated and a redeploy triggered.
Same check for LibDecimalFloatDeploy.combinedTables() initcode (log tables data contract).
Open questions to resolve before starting
- Which rain.* deps are already published to soldeer? Which need to be published as part of this migration?
- Are downstream consumers of
rain.math.floaton soldeer, submodules, or both? - Does
rain.deployitself need to migrate first (it uses submodules today per its CLAUDE.md)?
Out of scope
- Switching off forge entirely.
- Changing the deterministic deploy address intentionally (any change must be unintentional and verified zero-impact).
Activity
thedavidmeister commentedon May 9, 2026
Dependency graph
Verified by
git submodule status --recursiveandgrep -hrE '^import.*from'oversrc/test/scriptat HEAD of branch2026-05-09-189.Direct deps (6, declared in
.gitmodules)forge-std1801b054(v1.14.0)Script.sol,Test.solrain.datacontract3bff3078(2026-02-07-creation-code-2-g3bff307)lib/LibDataContract.solrain.deployccfdb378lib/LibRainDeploy.solrain.math.fixedpoint8308cbb6rain.sol.codegendfe95884src/lib/LibCodeGen.sol,src/lib/LibFs.solrain.string488f237cerror/ErrParse.sol,lib/parse/LibParseChar.sol,lib/parse/LibParseDecimal.solTransitive deps (6 unique repos beyond top level)
openzeppelin-contractsfcbae5394(v4.8.0-1034) — single SHA across 3 sitesrain.solmem2e47e41a(datacontract),f28da2f0(string) — 2 versionserc4626-tests232ff9ba8(v0.1.1) — same SHA across 3 siteshalmos-cheatcodes7328abe1— same SHA across 3 sitesrain.math.binaryf44f846arain.math.saturating22e58d70Version drift
forge-stdat 3 versions in the tree:1801b054(v1.14.0) — top level + datacontract + deploy + solmem (datacontract path)b8f065fd(v1.11.0-5) — fixedpoint + sol.codegen + math.saturating + math.binary + rain.solmem (string path)3b20d60d(v1.9.6) — all openzeppelin-contracts copiesrain.solmemat 2 versions (2e47e41avsf28da2f0).Imports actually used in this repo
rain.math.fixedpointhas no direct imports — verify whether it's reachable transitively (e.g. via shared types inrain.string) before declaring it droppable.Current
foundry.tomlremappingsThe other deps rely on foundry's default
lib/<name>resolution. Soldeer would generate all remappings explicitly — a one-time tabling exercise.Migration topological order
External sources first; Rain-controlled in dependency order. Within each level the items are independent and can be migrated in parallel.
Level 0 — external (no Rain migration work)
forge-stderc4626-testshalmos-cheatcodesopenzeppelin-contracts(treat as leaf; already published)Level 1 — Rain leaves (depend only on external)
rain.solmem→ forge-stdrain.math.binary→ forge-stdrain.math.saturating→ forge-stdrain.deploy→ forge-stdLevel 2 — depend on level 1
rain.datacontract→ rain.solmemrain.string→ openzeppelin-contracts, rain.solmemrain.sol.codegen→ rain.math.binaryLevel 3
rain.math.fixedpoint→ openzeppelin-contracts, rain.math.saturating, rain.stringLevel 4
rain.math.float(this repo) → rain.datacontract, rain.deploy, rain.math.fixedpoint, rain.sol.codegen, rain.stringBottleneck:
rain.math.fixedpoint. Migration of this repo is blocked until levels 1–3 are complete.Per-repo migration recipe (apply bottom up):
lib/submodules with soldeer entries infoundry.toml.soldeer.lock. Update CI to dropsubmodules: recursiveand addforge soldeer install.keccak256(creationCode)diff = 0 vs the pre-migration HEAD on the same source revision (especially critical forrain.deployandrain.math.float, which have deterministic deploys shipped to prod).thedavidmeister commentedon May 9, 2026
Progress 2026-05-09
rain.solmemis the first leaf migrated end-to-end:Side-effects from this leaf surfaced two rainix bugs that affect every sol-only consumer:
packageBoth fixed in rainlanguage/rainix#134 with a closure regression test (mutation-tested).
What's next
Migrate the next leaves in the topo-sort published higher up this issue:
Each leaf gets the same shape: soldeer init → ignore depth-anchoring → REUSE coverage → publish workflow as the rainix wrapper → tag v0.1.0 → confirm publish.
The registry-side project must exist on https://soldeer.xyz before the first publish lands; first attempt fails with
project <name> not foundand the project must be created manually.thedavidmeister commentedon May 9, 2026
Progress 2026-05-10
Levels 1-3 of the topo-sort are now migrated end-to-end.
rain.math.floatitself (level 4) is unblocked.The first PR per repo is the soldeer migration; subsequent PRs collapse the per-task workflow wrappers into the
rainix-solcomposite reusable.Rainix infrastructure shipped along the way
rust-toolchainandnodejsintosol-shell(rustfmt-conditional + prettier hook fixes; closure regression test, mutation-tested)publish-soldeer.yamlworkflowrainix-sol-static.yamlrainix-sol-legal.yamlrainix-sol-test.yamlrainix-sol.yamlthat fans out static/legal/test in parallel as separate runnersrust-shellandjs-shellslim devShells from thesol-shellprecedentWhat's left
rain.math.floatitself, the only level-4 leaf and the original target. Now unblocked sincerain.math.fixedpoint,rain.datacontract,rain.deploy,rain.sol.codegen,rain.stringall have soldeer publishes (or tags ready pending registry-side project creation).keccak256(type(DecimalFloat).creationCode)and the log-tables initcode must be identical pre/post migration. If they shift,LibDecimalFloatDeploy.ZOLTU_DEPLOYED_DECIMAL_FLOAT_ADDRESSand the codehash constants need regenerating with a fresh deploy.Operational note
soldeer.xyz rejects pushes to nonexistent projects with
error during IO operation for "": not connected(misleading message — actually a 404). First-time tag for any new package needs a one-time manual project creation on soldeer.xyz before the publish workflow lands. Confirmed againstrain-solmem,rain-math-binary,rain-math-saturating,rain-deploy,rain-string. Pending:rain-datacontract,rain-sol-codegen,rain-math-fixedpoint.soldeer migration