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
1 change: 0 additions & 1 deletion .coderabbitai.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
# SPDX-License-Identifier: LicenseRef-DCL-1.0
# SPDX-FileCopyrightText: Copyright (c) 2020 Rain Open Source Software Ltd

reviews:
path_filters:
- "!audit/**"
Expand Down
42 changes: 42 additions & 0 deletions .gas-snapshot
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
LibICloneableFactoryV4CheckImplementationCodeTest:testCheckImplementationCodeContract() (gas: 248218)
LibICloneableFactoryV4CheckImplementationCodeTest:testCheckImplementationCodeEtched(address,bytes) (runs: 2048, μ: 7528, ~: 7527)
LibICloneableFactoryV4CheckImplementationCodeTest:testCheckImplementationCodeZero(address) (runs: 2048, μ: 7214, ~: 7267)
LibICloneableFactoryV4CloneCreationCodeTest:testCloneCreationCodeDeploysEIP1167Runtime(address,bytes32) (runs: 2048, μ: 45636, ~: 45636)
LibICloneableFactoryV4CloneCreationCodeTest:testCloneCreationCodeIsEIP1167(address) (runs: 2048, μ: 4245, ~: 4245)
LibICloneableFactoryV4CloneDeterministicOpenSaltTest:testCloneDeterministicOpenSaltCallerIndependent(bytes32,bytes,address,address) (runs: 2048, μ: 459061, ~: 443058)
LibICloneableFactoryV4CloneDeterministicOpenSaltTest:testCloneDeterministicOpenSaltDataInDerivation(bytes32,bytes,bytes) (runs: 2048, μ: 466856, ~: 450600)
LibICloneableFactoryV4CloneDeterministicOpenSaltTest:testCloneDeterministicOpenSaltDiffersFromSenderNamespaced(address,bytes,bytes32,bytes32,address) (runs: 2048, μ: 8139, ~: 8126)
LibICloneableFactoryV4CloneDeterministicOpenSaltTest:testCloneDeterministicOpenSaltDisjointTagsCloseTheSquat(address,bytes,bytes) (runs: 2048, μ: 467536, ~: 451203)
LibICloneableFactoryV4CloneDeterministicOpenSaltTest:testCloneDeterministicOpenSaltDoesNotConsumeNamespacedSalt(bytes32,bytes) (runs: 2048, μ: 456513, ~: 438428)
LibICloneableFactoryV4CloneDeterministicOpenSaltTest:testCloneDeterministicOpenSaltEvent(bytes32,bytes) (runs: 2048, μ: 359037, ~: 349969)
LibICloneableFactoryV4CloneDeterministicOpenSaltTest:testCloneDeterministicOpenSaltInitializeFailureFails(bytes32,bytes32) (runs: 2048, μ: 167203, ~: 167203)
LibICloneableFactoryV4CloneDeterministicOpenSaltTest:testCloneDeterministicOpenSaltIsDomainTaggedHash(address,bytes,bytes32) (runs: 2048, μ: 6499, ~: 6482)
LibICloneableFactoryV4CloneDeterministicOpenSaltTest:testCloneDeterministicOpenSaltManyClonesPerImpl(bytes32,bytes32,bytes) (runs: 2048, μ: 455295, ~: 438862)
LibICloneableFactoryV4CloneDeterministicOpenSaltTest:testCloneDeterministicOpenSaltMatchesPredict(bytes32,bytes) (runs: 2048, μ: 361663, ~: 352503)
LibICloneableFactoryV4CloneDeterministicOpenSaltTest:testCloneDeterministicOpenSaltPredictCallerIndependent(address,bytes,bytes32,address,address) (runs: 2048, μ: 12247, ~: 12224)
LibICloneableFactoryV4CloneDeterministicOpenSaltTest:testCloneDeterministicOpenSaltSecondDeployReverts(bytes32,bytes,address,address) (runs: 2048, μ: 1040442913, ~: 1040443151)
LibICloneableFactoryV4CloneDeterministicOpenSaltTest:testCloneDeterministicOpenSaltZeroImplementationCodeSize(address,bytes,bytes32) (runs: 2048, μ: 11124, ~: 11129)
LibICloneableFactoryV4CloneDeterministicTest:testCloneDeterministicDataNotInDerivation(bytes32,bytes,bytes) (runs: 2048, μ: 464734, ~: 448450)
LibICloneableFactoryV4CloneDeterministicTest:testCloneDeterministicEvent(bytes32,bytes) (runs: 2048, μ: 359015, ~: 349951)
LibICloneableFactoryV4CloneDeterministicTest:testCloneDeterministicInitializeFailureFails(bytes32,bytes32) (runs: 2048, μ: 166695, ~: 166695)
LibICloneableFactoryV4CloneDeterministicTest:testCloneDeterministicManyClonesPerImpl(bytes32,bytes32,bytes) (runs: 2048, μ: 455229, ~: 438823)
LibICloneableFactoryV4CloneDeterministicTest:testCloneDeterministicMatchesPredict(bytes32,bytes) (runs: 2048, μ: 361175, ~: 352030)
LibICloneableFactoryV4CloneDeterministicTest:testCloneDeterministicSaltIsDomainTaggedHash(address,bytes32,address) (runs: 2048, μ: 5563, ~: 5563)
LibICloneableFactoryV4CloneDeterministicTest:testCloneDeterministicSecondDeployReverts(bytes32,bytes) (runs: 2048, μ: 1040443447, ~: 1040443612)
LibICloneableFactoryV4CloneDeterministicTest:testCloneDeterministicSenderScoped(bytes32,bytes,address,address) (runs: 2048, μ: 459986, ~: 444001)
LibICloneableFactoryV4CloneDeterministicTest:testCloneDeterministicZeroImplementationCodeSize(address,bytes,bytes32) (runs: 2048, μ: 11086, ~: 11111)
LibICloneableFactoryV4PredictCloneAddressTest:testPredictCloneAddressIsCreate2Formula(address,address,bytes32) (runs: 2048, μ: 1724, ~: 1724)
LibICloneableFactoryV4PredictCloneAddressTest:testPredictCloneAddressMatchesOZ(address,address,bytes32) (runs: 2048, μ: 1570, ~: 1570)
LibICloneableFactoryV4PredictCloneAddressTest:testPredictCloneAddressMatchesRealDeploy(address,bytes32) (runs: 2048, μ: 42869, ~: 42869)
LibICloneableFactoryV4Test:testDerivationsDisjoint(address,bytes32,bytes32,bytes) (runs: 2048, μ: 1405, ~: 1398)
LibICloneableFactoryV4Test:testDomainTagsDistinct() (gas: 233)
LibICloneableFactoryV4Test:testDomainTagsPinned() (gas: 325)
LibICloneableFactoryV4Test:testEffectiveOpenSaltDataSensitive(bytes32,bytes,bytes) (runs: 2048, μ: 4687, ~: 4682)
LibICloneableFactoryV4Test:testEffectiveOpenSaltEmptyData(bytes32) (runs: 2048, μ: 826, ~: 826)
LibICloneableFactoryV4Test:testEffectiveOpenSaltMatchesFormula(bytes32,bytes) (runs: 2048, μ: 1269, ~: 1257)
LibICloneableFactoryV4Test:testEffectiveOpenSaltPreimageShape(bytes32,bytes) (runs: 2048, μ: 1360, ~: 1348)
LibICloneableFactoryV4Test:testEffectiveOpenSaltSaltSensitive(bytes32,bytes32,bytes) (runs: 2048, μ: 4177, ~: 4166)
LibICloneableFactoryV4Test:testEffectiveSaltDeployerSensitive(address,address,bytes32) (runs: 2048, μ: 3858, ~: 3858)
LibICloneableFactoryV4Test:testEffectiveSaltMatchesFormula(address,bytes32) (runs: 2048, μ: 865, ~: 865)
LibICloneableFactoryV4Test:testEffectiveSaltPreimageShape(address,bytes32) (runs: 2048, μ: 966, ~: 966)
LibICloneableFactoryV4Test:testEffectiveSaltSaltSensitive(address,bytes32,bytes32) (runs: 2048, μ: 3819, ~: 3819)
5 changes: 3 additions & 2 deletions .github/workflows/package-release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ jobs:
release:
uses: rainlanguage/rainix/.github/workflows/rainix-autopublish.yaml@main
with:
# Library repo: the ICloneable* interface surface. The deploy half (concrete +
# deploy-pin snapshots) lives in rain.factory.deploy (rainlanguage/rain.factory#46).
# Library repo: the ICloneable* interface surface plus the Lib* logic behind
# it. The deploy half (concrete + deploy-pin snapshots) lives in
# rain.factory.deploy (rainlanguage/rain.factory#46).
soldeer-package: rain-factory
secrets: inherit
4 changes: 2 additions & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"editor.rulers": [80]
}
"editor.rulers": [80]
}
72 changes: 36 additions & 36 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,17 @@ code in this repository.

rain.factory is a Solidity **library** repo for EIP1167 minimal proxy (clone)
factories in the Rain ecosystem: the `ICloneable*` interface surface and the
`LibICloneableFactoryV4` salt-derivation library. It is the library half of the
library/deploy split (rainlanguage/rain.factory#46) — no concrete contract, no
deploy pins, no deploy script.
`LibICloneableFactoryV4` library that implements it. It is the library half of
the library/deploy split (rainlanguage/rain.factory#46) — no concrete contract,
no deploy pins, no deploy script.

The concrete `CloneFactory` that implements these interfaces, its deployed
address + codehash pins (`LibCloneFactoryDeploy`), the frozen
`src/generated/<tag>/` deploy-pin snapshots and `script/Deploy.sol` all live in
[`rain.factory.deploy`](https://github.com/rainlanguage/rain.factory.deploy) and
publish as the `rain-factory-deploy` Soldeer package. Consumers that need only
the interfaces depend on `rain-factory`; consumers that need the deployed
address/codehash pins depend on `rain-factory-deploy`.
The concrete `CloneFactory` — meant to be a pure delegation into
`LibICloneableFactoryV4`, one call per entry point — its deployed address +
codehash pins (`LibCloneFactoryDeploy`), the frozen deploy-pin snapshots and
`script/Deploy.sol` all live in
[`rain.factory.deploy`](https://github.com/rainlanguage/rain.factory.deploy),
publishing as `rain-factory-deploy`. Depend on `rain-factory` for the interfaces
and library; on `rain-factory-deploy` for the deployed pins.

License: LicenseRef-DCL-1.0 (DecentraLicense). All source files must include
SPDX headers.
Expand All @@ -26,34 +26,33 @@ SPDX headers.

Nix + Foundry. Enter the shell with `nix develop`, then run rainix tasks:
`rainix-sol-static` (Slither), `rainix-sol-legal` (REUSE), `rainix-sol-prelude`
(deps, run first), `rainix-sol-test`, and `forge build` directly.
`rainix-sol-test` runs the derivation library's tests, which live here; the
interfaces have no behaviour to test.
(deps, run first), `rainix-sol-test`, and `forge build` / `forge test` directly.
Tests exercise the library through `TestCloneFactory`, a pure-delegation
concrete, with OZ `Clones` as foreign EIP1167 oracle.

## Architecture

- `src/interface/ICloneableV2.sol` — Interface for cloneable contracts.
`initialize(bytes)` must return `ICLONEABLE_V2_SUCCESS` (keccak256 hash) on
success.
- `src/interface/ICloneableFactoryV2.sol` — Legacy factory interface: the
nonce-dependent `clone(address, bytes)` and `NewClone` event. The concrete
factory in rain.factory.deploy implements `ICloneableFactoryV4`, not this;
still published for other consumers.
- `src/interface/ICloneableFactoryV3.sol` — Deterministic-only factory
interface: `cloneDeterministic(address, bytes, bytes32)` +
`predictDeterministicAddress(address, bytes32, address)` (CREATE2, salt
namespaced by `msg.sender`) and its own `NewClone` event. Standalone — does
NOT extend `ICloneableFactoryV2` and has no non-deterministic `clone()`. Still
published for consumers pinned to it.
- `src/interface/ICloneableV2.sol` — Cloneable contracts. `initialize(bytes)`
must return `ICLONEABLE_V2_SUCCESS` on success.
- `src/interface/ICloneableFactoryV2.sol` — Legacy factory interface
(nonce-dependent `clone`). Still published for other consumers.
- `src/interface/ICloneableFactoryV3.sol` — Deterministic-only factory interface
(CREATE2, salt namespaced by `msg.sender`). Standalone — does NOT extend V2;
the non-deterministic `clone()` was intentionally dropped. Still published for
consumers pinned to it.
- `src/interface/ICloneableFactoryV4.sol` — Current factory interface. Extends
`ICloneableFactoryV3` and defines the open-salt pair
`cloneDeterministicOpenSalt` / `predictDeterministicAddressOpenSalt`. Both
derivations are pinned to exact bytes, each `keccak256`-ing a 96-byte preimage
led by a distinct string-derived domain tag, so the two images are disjoint by
construction. The full spec is the NatSpec on the interface.
- `src/lib/LibICloneableFactoryV4.sol` — Pure `internal` library, the executable
form of the two `ICloneableFactoryV4` salt derivations; imports the domain
tags from the interface. Tested under `test/src/lib/`.
- `src/lib/LibICloneableFactoryV4.sol` — The whole factory as internal library
logic: both effective-salt derivations (pure, importing the domain tags from
the interface), the implementation-code guard, EIP1167 creation code + CREATE2
prediction (no external cloning dependency), and the atomic
clone-initialize-verify flow with the typed errors and `NewClone`.
`msg.sender` and `address(this)` are read inside the library, so a delegating
concrete cannot misroute them.
- `src/interface/deprecated/` — Legacy interfaces (`ICloneableV1`,
`ICloneableFactoryV1`, `IFactory`). Do not use for new work.

Expand All @@ -63,14 +62,15 @@ makes this half a standalone publish.

## Solidity Conventions

- Solidity version: the interfaces and the derivation library float `^`
(`^0.8.18`) so downstream soldeer consumers on a different `0.8.x` can still
compile them; the tests here pin `=0.8.25`, as do the concrete contracts and
scripts in rain.factory.deploy.
- Versions: interfaces float `^0.8.18` so downstream soldeer consumers on a
different `0.8.x` still compile them; the library floats `^0.8.25`; tests pin
`=0.8.25`, as do the concretes and scripts in rain.factory.deploy.
- No named return values.
- Compiler (`foundry.toml`): Cancun EVM, optimizer at 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/`). The only external entry is
forge-std. `@openzeppelin-contracts`, `rain-extrospection`, `rain-deploy` and
`soldeer.lock`, vendored under `dependencies/`). Everything there is
test-harness only: forge-std, plus `@openzeppelin-contracts` as the
equivalence oracle for the EIP1167 construction. `rain-deploy` and
`rain-sol-codegen` belong to the deploy half and must not be added here:
adding one means concrete code has landed in a library repo.
needing one means deploy-pin code has landed in a library repo.
34 changes: 21 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ Docs at https://rainprotocol.github.io/rain.factory

This repo is the **library** half of the library/deploy split
([#46](https://github.com/rainlanguage/rain.factory/issues/46)): the
`ICloneable*` interface surface and the `LibICloneableFactoryV4` salt-derivation
library. It publishes to Soldeer as `rain-factory`.
`ICloneable*` interface surface and the `LibICloneableFactoryV4` library that
implements it. It publishes to Soldeer as `rain-factory`.

## Concrete implementations

Expand All @@ -17,20 +17,28 @@ together with its deployed address + codehash pins, its frozen per-release
deploy-pin snapshots and its deploy script. That repo publishes as
`rain-factory-deploy`.

Depend on `rain-factory` if you need only the interfaces. Depend on
Depend on `rain-factory` if you need the interfaces or the library. Depend on
`rain-factory-deploy` if you need the deployed address or codehash of a live
`CloneFactory`.

## Derivation library

`src/lib/LibICloneableFactoryV4.sol` is the executable form of the two `CREATE2`
salt derivations `ICloneableFactoryV4` pins to exact bytes — the
`msg.sender`-namespaced one and the open-salt one — as pure `internal`
functions. It imports the domain tags from the interface, so the tags have a
single source of truth, and its tests recompute both formulas independently to
pin them to the interface's spec byte for byte. A factory, an indexer or a
consumer predicting a clone address computes the salt from one place. The tests
live here under `test/src/lib/`.
## Library

`src/lib/LibICloneableFactoryV4.sol` is the executable form of
`ICloneableFactoryV4` as internal library logic. The two `CREATE2` salt
derivations the interface pins to exact bytes — the `msg.sender`-namespaced one
and the open-salt one — are pure functions that import the domain tags from the
interface, so the tags have a single source of truth and a factory, an indexer
or a consumer predicting a clone address computes the salt from one place. On
top of them sit the implementation-code guard, the EIP1167 creation code and its
CREATE2 address prediction (constructed from the standard's own bytes, so the
published `src/` depends on no external cloning code), and the atomic
clone-initialize-verify flow with its typed errors and the `NewClone` event.
`msg.sender` and `address(this)` are read inside the library, so a concrete
factory is nothing but one delegation per entry point and cannot misroute
either. The tests live here under `test/src/lib/`: they recompute both salt
formulas independently to pin them to the interface's spec byte for byte, and
pin the EIP1167 construction against OpenZeppelin `Clones` as a foreign
implementation of the same standard.

## Interfaces

Expand Down
29 changes: 15 additions & 14 deletions REUSE.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,21 @@ version = 1

[[annotations]]
path = [
".github/workflows/**/",
".vscode/**/",
".coderabbitai.yaml",
".gitignore",
"audit/**/",
"CLAUDE.md",
"README.md",
"flake.lock",
"flake.nix",
"foundry.toml",
"slither.config.json",
"REUSE.toml",
"soldeer.lock",
".soldeerignore",
".gas-snapshot",
".github/workflows/**/",
".vscode/**/",
".coderabbitai.yaml",
".gitignore",
"audit/**/",
"CLAUDE.md",
"README.md",
"flake.lock",
"flake.nix",
"foundry.toml",
"slither.config.json",
"REUSE.toml",
"soldeer.lock",
".soldeerignore",
]
SPDX-FileCopyrightText = "Copyright (c) 2020 Rain Open Source Software Ltd"
SPDX-License-Identifier = "LicenseRef-DCL-1.0"
11 changes: 7 additions & 4 deletions foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,15 @@ libs = ["dependencies"]
[fuzz]
runs = 2048

# Library repo: the ICloneable* interface surface only. The concrete CloneFactory,
# its deploy-pin snapshots and deploy scripts live in rain.factory.deploy
# (rainlanguage/rain.factory#46). Interfaces import nothing, so there are no
# Solidity dependencies beyond the test harness.
# Library repo: the ICloneable* interface surface plus the Lib* logic that
# implements it. The concrete CloneFactory, its deploy-pin snapshots and deploy
# scripts live in rain.factory.deploy (rainlanguage/rain.factory#46). The
# published src/ imports nothing external, so every dependency here is
# test-harness only: forge-std, and OZ Clones as the independent equivalence
# oracle for the EIP1167 construction.
[dependencies]
forge-std = "1.16.1"
"@openzeppelin-contracts" = "5.6.1"

[soldeer]
recursive_deps = false
4 changes: 2 additions & 2 deletions slither.config.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"detectors_to_exclude": "assembly-usage,solc-version,pragma,unindexed-event-address",
"filter_paths": "dependencies/forge-std-1.16.1"
"detectors_to_exclude": "assembly-usage,solc-version,pragma,unindexed-event-address",
"filter_paths": "dependencies/forge-std-1.16.1"
}
7 changes: 7 additions & 0 deletions soldeer.lock
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
[[dependencies]]
name = "@openzeppelin-contracts"
version = "5.6.1"
url = "https://soldeer-revisions.s3.amazonaws.com/@openzeppelin-contracts/5_6_1_15-03-2026_09:19:50_contracts.zip"
checksum = "a3b6bc661be858c7c27f60a1708cbebe8c71034b4cc1e9fe270d0a05b069352f"
integrity = "bce03af7ada1eee21a7fff393f238bcd7cd75a022a4db55ffb6b0dbb32433d35"

[[dependencies]]
name = "forge-std"
version = "1.16.1"
Expand Down
Loading
Loading