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
53 changes: 27 additions & 26 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ code in this repository.

rain.factory is a Solidity **library** repo: the `ICloneable*` interface surface
for EIP1167 minimal proxy (clone) factories in the Rain ecosystem. It is the
library half of the library/deploy split (rainlanguage/rain.factory#46) and holds
interfaces only — no concrete contract, no deploy pins, no deploy script, and no
tests.
library half of the library/deploy split (rainlanguage/rain.factory#46) and
holds interfaces only — no concrete contract, no deploy pins, no deploy script,
and no tests.

The concrete `CloneFactory` that implements these interfaces, its deployed
address + codehash pins (`LibCloneFactoryDeploy`), the frozen
Expand All @@ -28,46 +28,47 @@ 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. There is no
test suite here — the interfaces have no behaviour; the tests that exercise them
live in rain.factory.deploy against the concrete.
live against the concrete in rain.factory.deploy.

## 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. Superseded by
`ICloneableFactoryV3` for the concrete factory in rain.factory.deploy; still
published for other consumers.
- `src/interface/ICloneableFactoryV3.sol` — Current factory interface.
Deterministic-only: `cloneDeterministic(address, bytes, bytes32)` +
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`, because the non-deterministic `clone()` was
intentionally dropped.
NOT extend `ICloneableFactoryV2` and has no non-deterministic `clone()`. 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/interface/deprecated/` — Legacy interfaces (`ICloneableV1`,
`ICloneableFactoryV1`, `IFactory`). Do not use for new work.

`src/` holds nothing else. The interfaces import nothing — not each other, not a
third-party library — which is what makes this half a standalone publish.
`src/` holds nothing else. The interfaces import nothing from outside this repo
— intra-repo inheritance is allowed and `ICloneableFactoryV4` extends
`ICloneableFactoryV3` — which is what makes this half a standalone publish.

## Solidity Conventions

- Solidity version: every file here is an interface and floats `^` (the
interfaces use `^0.8.18`) so downstream soldeer consumers on a different
`0.8.x` can still compile them. The `=0.8.25` exact-pin rule applies to
concrete contracts, scripts and tests, which live in rain.factory.deploy.
- EVM target: Cancun
- Optimizer: enabled, 100,000 runs
- No CBOR metadata (`cbor_metadata = false`, `bytecode_hash = "none"`)
- 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 interfaces import nothing,
so the only entry is forge-std. `@openzeppelin-contracts`, `rain-extrospection`,
`rain-deploy` and `rain-sol-codegen` went with the deploy half and must not
come back: adding one here means concrete code has landed in a library repo.

## Deployment

Nothing here is deployed. The concrete `CloneFactory`, its address/codehash, and
the deploy scripts (Arbitrum, Base, Base Sepolia, Flare, Polygon) all live in
rain.factory.deploy.
`soldeer.lock`, vendored under `dependencies/`). The interfaces import nothing
external, so the only entry is forge-std. `@openzeppelin-contracts`,
`rain-extrospection`, `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.
39 changes: 30 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ possible on the implementation side.
The ideal would be that "any" contract can call an interpreter and magically be
supported but there's a lot that can go wrong, for example:

- Contracts can self destruct or even [redeployed with new bytecode](https://0age.medium.com/the-promise-and-the-peril-of-metamorphic-contracts-9eb8b8413c5e)
- Contracts can self-destruct or even be
[redeployed with new bytecode](https://0age.medium.com/the-promise-and-the-peril-of-metamorphic-contracts-9eb8b8413c5e)
- Proxies can point to new implementations and "upgrade"
- Discoverability of ABIs and other metadata subject to indexer limitations

Expand All @@ -41,13 +42,33 @@ Falling short of the ideal, we want to support:
- Support existing patterns such as EIP1167 for clones, etc.
- Avoid introducing Rain-isms as much as possible

The onchain tooling for analysis is found at https://github.com/rainprotocol/rain.extrospection
The onchain tooling for analysis is found at
https://github.com/rainprotocol/rain.extrospection

The current interfaces in this repository are for

- `ICloneableFactoryV3`, the current factory interface: deterministic-only
(`cloneDeterministic` + `predictDeterministicAddress`, CREATE2 with the salt
namespaced by `msg.sender`). Standalone rather than extending
- `ICloneableFactoryV4`, the current factory interface. Extends
`ICloneableFactoryV3` — nothing was dropped this time, so it inherits rather
than restates — and adds a second deterministic derivation,
`cloneDeterministicOpenSalt` + `predictDeterministicAddressOpenSalt`, whose
`CREATE2` salt hashes the caller-supplied salt together with the
initialization data and nothing about the caller. The two derivations differ
in what the clone's address commits to, and neither dominates: the V3 pair
namespaces the salt by `msg.sender`, so the address commits to WHO deployed
and not to WHAT — nobody else can reach the caller's address, but the
deploying account is baked into it forever and the deployer alone decides the
initial state. The open-salt pair commits to WHAT and not to WHO — every
account reaches the same address, and so can anyone, but everyone who reaches
it deploys the same contract initialized with the same bytes, because varying
either input lands somewhere else. Its cost is that the address is not
knowable until the data is final. The residual the address cannot fix —
implementations MUST NOT read `tx.origin` — and the address-registry pairing
it is intended for are spelled out in the NatSpec on
`ICloneableFactoryV4.cloneDeterministicOpenSalt`
- `ICloneableFactoryV3`, deterministic-only (`cloneDeterministic` +
`predictDeterministicAddress`, CREATE2 with the salt namespaced by
`msg.sender`). Superseded by `ICloneableFactoryV4`, still published for
consumers pinned to it. Standalone rather than extending
`ICloneableFactoryV2`, because the non-deterministic `clone()` was
intentionally dropped
- `ICloneableFactoryV2` that is expected to clone proxies from a reference
Expand All @@ -61,9 +82,9 @@ The current interfaces in this repository are for

#### `ICloneableV1`

This version of `ICloneable` did not have any explicit return value on success of
initialize. It is possible for contracts that do not implement `ICloneableV1` to
silently fail to initialize when cloned by an `ICloneableFactoryV1`.
This version of `ICloneable` did not have any explicit return value on success
of initialize. It is possible for contracts that do not implement `ICloneableV1`
to silently fail to initialize when cloned by an `ICloneableFactoryV1`.

Newer versions of the interface include an explicit success value and check.

Expand All @@ -80,4 +101,4 @@ This was suboptimal for several reasons:
- Redundant work to maintain a growing list of factories

The legacy interface is available as `IFactory` but it is NOT RECOMMENDED for
new contracts.
new contracts.
6 changes: 6 additions & 0 deletions src/interface/ICloneableFactoryV3.sol
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ interface ICloneableFactoryV3 {
/// the string "ICloneableV2.initialize". MUST emit `NewClone` with the
/// implementation and clone address.
///
/// @dev A factory that also implements `ICloneableFactoryV4` pins this
/// `msg.sender` namespacing to exact bytes: the effective `CREATE2` salt is
/// `keccak256(abi.encode(ICLONEABLE_FACTORY_V4_NAMESPACED_DOMAIN, msg.sender, salt))`.
/// See that interface for the full derivation and its disjointness from the
/// open-salt one.
///
/// @param implementation The contract to clone.
/// @param data As per `ICloneableV2`.
/// @param salt Caller-chosen salt; distinct salts yield distinct clones.
Expand Down
Loading
Loading