Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 1 addition & 6 deletions .github/workflows/package-release.yaml
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
name: Package Release
# Deploy repo: a manual `sol-v*` tag is the sole release trigger. The tag names
# the version; rainix-tag-release verifies prod exists at the regenerated pins,
# publishes rain-factory-deploy to Soldeer, and commits the frozen snapshot back
# to main. The on-chain deploy is separate and manual (rainix-manual-sol-artifacts
# dispatch), run before tagging. Nothing publishes on merge, so [package].version
# and the frozen src/generated/<tag>/ snapshot it names only ever move together.
# Deploy repo: released by a manual `sol-v*` tag via rainix-tag-release.
on:
push:
tags:
Expand Down
177 changes: 42 additions & 135 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -1,137 +1,44 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with
code in this repository.

## Project Overview

rain.factory.deploy is the **deployment** half of `rain.factory`: the concrete
`CloneFactory` contract plus its deployed address + codehash pins. The core
contract `CloneFactory` clones any contract implementing `ICloneableV2` (an
interface it imports from the `rain-factory` Soldeer package) and atomically
initializes it.

License: LicenseRef-DCL-1.0 (DecentraLicense). All source files must include
SPDX headers.

## Build & Test Commands

This project uses **Nix + Foundry (Forge)**. Everything runs in the rainix
`sol-shell`, the same shell CI uses:

```bash
nix develop .#sol-shell
```

Soldeer dependencies are not committed (`dependencies/` is gitignored), so
install them before the first build:

```bash
nix develop .#sol-shell -c forge soldeer install
```

Each command below has a matching CI job:

```bash
# Build
nix develop .#sol-shell -c forge build

# Run all tests. The five LibCloneFactoryDeployProdTest fork tests read the
# live chains and need ARBITRUM_RPC_URL, BASE_RPC_URL, BASE_SEPOLIA_RPC_URL,
# FLARE_RPC_URL and POLYGON_RPC_URL; without them only those five fail.
nix develop .#sol-shell -c forge test

# Run a specific test, or a specific file
nix develop .#sol-shell -c forge test --match-test testCloneDeterministic
nix develop .#sol-shell -c forge test --match-path test/src/concrete/CloneFactoryCloneDeterministic.t.sol

# Formatting (CI runs `forge fmt --check`)
nix develop .#sol-shell -c forge fmt

# Static analysis
nix develop .#sol-shell -c slither .

# License/legal checks (REUSE compliance)
nix develop .#sol-shell -c reuse lint

# Regenerate the deploy pins for the current [package].version
nix develop .#sol-shell -c bash -c 'forge script ./script/BuildPointers.sol && forge fmt'
```

## Architecture

The `ICloneable*` interfaces are NOT in this repo. They live in
[`rain.factory`](https://github.com/rainlanguage/rain.factory) and arrive here
as the `rain-factory` Soldeer dependency, so they are read under
`dependencies/rain-factory-<version>/src/interface/`.

- `src/concrete/CloneFactory.sol` — The single concrete implementation of
`ICloneableFactoryV3`. Uses OpenZeppelin `Clones.cloneDeterministic()`; there
is no plain `clone()`.
- `src/lib/LibCloneFactoryDeploy.sol` — Deterministic deployment address and
codehash constants (generated; aliases the current tag's
`src/generated/<tag>/` snapshot).
- `src/generated/<tag>/CloneFactory.pointers.sol` — Frozen per-release
deploy-pin snapshots: creation code, runtime code, bytecode hash, deployed
address.
- `script/BuildPointers.sol` — Regenerates the snapshot for the current
`[package].version` and the `LibCloneFactoryDeploy` alias.
- `script/Deploy.sol` — The Zoltu deploy script.

## Solidity Conventions

- Solidity version: concrete contracts, scripts and tests pin `=0.8.25` (exact);
library and generated files float `^0.8.25` so downstream soldeer consumers on
a different `0.8.x` can still compile them
- EVM target: Cancun
- Optimizer: enabled, 100,000 runs
- No CBOR metadata (`cbor_metadata = false`, `bytecode_hash = "none"`)
- Dependencies are managed with Soldeer (`[dependencies]` in `foundry.toml` +
`soldeer.lock`, vendored under `dependencies/`): forge-std,
@openzeppelin-contracts, rain-extrospection, rain-deploy, rain-sol-codegen,
rain-factory

## Deployment

Deployed via the deterministic Zoltu deployer (from `rain.deploy`), so the
address is a pure function of the bytecode. The canonical address and codehash
are committed in `LibCloneFactoryDeploy.sol`. `script/Deploy.sol` deploys the
`clone-factory` suite to the five networks `LibRainDeploy.supportedNetworks()`
returns: Arbitrum One, Base, Base Sepolia, Flare and Polygon.

A deploy is a human-dispatched run of the `Manual sol artifacts` workflow
(`workflow_dispatch` → `rainix-manual-sol-artifacts`), never a merge and never
part of the release workflow.

## Releases and versioning

This is a **deploy repo**, not a library repo, so nothing publishes on merge:

- `[package].version` in `foundry.toml` is the **last released** version (it
names the current `src/generated/<tag>/` snapshot), not a next-version slot. A
normal PR does not bump it; only a release moves it.
- A human pushes a `sol-v<version>` tag, which runs `rainix-tag-release`: it
writes the version from the tag into `foundry.toml`, regenerates the snapshot
(`forge script ./script/BuildPointers.sol && forge fmt`), verifies the live
chains match the fresh pins with `forge test`, publishes `rain-factory-deploy`
to Soldeer, and commits the new snapshot back to `main`.
- The on-chain deploy happens **before** tagging, via the manual dispatch above;
`rainix-tag-release` never broadcasts, it only attests.
- Existing `src/generated/<tag>/` snapshots are frozen: a release adds a new tag
directory, it never edits or deletes an existing one. CI enforces this.

## CI

`.github/workflows/rainix-sol.yaml` calls the rainix `rainix-sol` reusable on
every push, which runs three parallel jobs:

- `test` — `forge test -vvv`. The `LibCloneFactoryDeployProdTest` fork tests
need the `RPC_URL_*_FORK` secrets.
- `static` — `slither .`, `forge fmt --check`, `rainix-sol-single-contract` (one
contract per `.sol` file), plus the org-wide gates: no ignored tests, no git
submodules, no `@custom:` NatSpec, and append-only `src/generated/<tag>/`
snapshots.
- `legal` — `reuse lint`.

The other two workflows never run on push: `package-release.yaml` fires only on
a `sol-v*` tag, and `manual-sol-artifacts.yaml` only on `workflow_dispatch`.
Only what a capable agent would get _wrong_ from this repo alone. Layout, dev
shells, build/test commands, dependency lists, and which command CI runs are all
discoverable and deliberately absent (rainlanguage/rainix#298).

## What this repo is

rain.factory.deploy is the **deploy half** of `rain.factory`: the concrete
`CloneFactory` (clones any `ICloneableV2` via OpenZeppelin
`Clones.cloneDeterministic()` — there is no plain `clone()`) plus its deployed
address + codehash pins. The `ICloneable*` **interfaces are NOT here** — they
live in `rain.factory` and arrive as the `rain-factory` Soldeer dependency
(`dependencies/rain-factory-<version>/src/interface/`).

## Conventions an agent would get wrong

- Pragma: concrete contracts, scripts and tests pin `=0.8.25` (exact); library
and generated files float `^0.8.25` so downstream soldeer consumers on another
`0.8.x` still compile them.
- Optimizer 100,000 runs; no CBOR metadata (`cbor_metadata = false`,
`bytecode_hash = "none"`). The deployed address is a pure function of the
bytecode (deterministic Zoltu deployer), so any of these changing moves the
pins.
- All source files need SPDX headers (LicenseRef-DCL-1.0).

## Deploy-pin invariants (the hazards)

- `src/generated/<tag>/` snapshots are **frozen**: a release ADDS a new tag dir,
never edits or deletes an existing one. CI enforces append-only.
- `[package].version` is the **last released** version — it names the current
`src/generated/<tag>/` snapshot. A normal PR does not bump it; only a release
moves it, in lockstep with the snapshot.
- `LibCloneFactoryDeploy.sol` aliases the current tag's snapshot;
`script/BuildPointers.sol` regenerates both. Generated files — do not
hand-edit.

## Release / deploy shape

- The on-chain deploy is a human-dispatched `Manual sol artifacts` run
(`workflow_dispatch`), done **before** tagging — never on merge, never part of
the release workflow, and it is what actually broadcasts.
- A manual `sol-v<version>` tag is the sole release trigger. The release
mechanics live in rainix's `rainix-tag-release` reusable, not here.
6 changes: 2 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,8 @@ This is a deploy repo: releases are **manual `sol-v*` tags**, not merges.

The on-chain deploy is a separate, human-dispatched step, run **before**
tagging: the `Manual sol artifacts` workflow runs `script/Deploy.sol` for the
`clone-factory` suite. Tagging then runs `rainix-tag-release`, which regenerates
the snapshot for the tagged version, verifies the live chains match those fresh
pins, publishes `rain-factory-deploy` to Soldeer, and commits the frozen
snapshot back to `main`. It never broadcasts a deploy itself.
`clone-factory` suite. Tagging then runs `rainix-tag-release`, which never
broadcasts a deploy itself; its mechanics live in rainix.

Nothing publishes on merge, so `[package].version` and the frozen
`src/generated/<tag>/` snapshot it names only ever move together.
Expand Down
Loading