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

reviews:
path_filters:
- "!audit/**"
- "!.fixes/**"
4 changes: 4 additions & 0 deletions .github/workflows/rainix.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,8 @@ jobs:
CI_FORK_SEPOLIA_BLOCK_NUMBER: ${{ vars.CI_FORK_SEPOLIA_BLOCK_NUMBER }}
CI_FORK_SEPOLIA_DEPLOYER_ADDRESS: ${{ vars.CI_FORK_SEPOLIA_DEPLOYER_ADDRESS }}
CI_DEPLOY_SEPOLIA_RPC_URL: ${{ secrets.CI_DEPLOY_SEPOLIA_RPC_URL || vars.CI_DEPLOY_SEPOLIA_RPC_URL }}
ARBITRUM_RPC_URL: ${{ secrets.RPC_URL_ARBITRUM_FORK || vars.RPC_URL_ARBITRUM_FORK }}
BASE_RPC_URL: ${{ secrets.RPC_URL_BASE_FORK || vars.RPC_URL_BASE_FORK }}
FLARE_RPC_URL: ${{ secrets.RPC_URL_FLARE_FORK || vars.RPC_URL_FLARE_FORK }}
POLYGON_RPC_URL: ${{ secrets.RPC_URL_POLYGON_FORK || vars.RPC_URL_POLYGON_FORK }}
run: nix develop -c ${{ matrix.task }}
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
cache
out
out
.fixes
.env
72 changes: 72 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<!-- SPDX-License-Identifier: LicenseRef-DCL-1.0 -->
<!-- SPDX-FileCopyrightText: Copyright (c) 2020 Rain Open Source Software Ltd -->

# CLAUDE.md

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

## Project Overview

rain.deploy is a Solidity library for deploying Rain Protocol contracts via the Zoltu deterministic deployment proxy to multiple EVM networks. It ensures identical contract addresses across all supported chains (Arbitrum, Base, Flare, Polygon) because the Zoltu proxy is deployed at the same address on every chain and uses CREATE with a predictable nonce.

## Build & Development

This project uses **Foundry** (forge) for Solidity development and **Nix** for environment management.

```bash
# Enter the nix dev shell (provides forge and all tooling)
nix develop

# Build
nix develop -c forge build

# Run tests
nix develop -c rainix-sol-test

# Run a single test
nix develop -c forge test --match-test "testName"

# Static analysis / linting
nix develop -c rainix-sol-static

# License/legal checks (REUSE compliance)
nix develop -c rainix-sol-legal
```

CI runs three matrix tasks: `rainix-sol-legal`, `rainix-sol-test`, `rainix-sol-static`.

## RPC Configuration

Fork tests require RPC endpoints defined in `.env` (gitignored):
```bash
ARBITRUM_RPC_URL=https://arb1.arbitrum.io/rpc
BASE_RPC_URL=https://mainnet.base.org
FLARE_RPC_URL=https://flare-api.flare.network/ext/C/rpc
POLYGON_RPC_URL=https://polygon-rpc.com
```
These are referenced in `foundry.toml` under `[rpc_endpoints]`.

## Architecture

The entire library is a single file: `src/lib/LibRainDeploy.sol`.

**LibRainDeploy** provides:
- `etchZoltuFactory(Vm)` — etches the Zoltu factory bytecode at the factory address (for networks where it isn't deployed)
- `deployZoltu(bytes creationCode)` — deploys creation code via the Zoltu factory (`0x7A0D94F55792C434d74a40883C6ed8545E406D12`) using low-level `call`, returns the deployed address
- `supportedNetworks()` — returns the list of Rain-supported network names (used as foundry RPC config aliases)
- `checkDependencies(...)` — forks each network, verifies dependencies and Zoltu factory exist with expected codehashes
- `deployToNetworks(...)` — re-verifies dependencies, deploys via Zoltu, verifies address and code hash
- `deployAndBroadcast(...)` — the main entry point: derives deployer from private key, calls `checkDependencies` then `deployToNetworks`

The library is designed to be called from Foundry scripts (`forge script`) in consuming repos, not directly. Consuming repos provide their own creation code, expected addresses, expected code hashes, and dependency lists.

## Key Design Patterns

- **Deterministic addresses**: Zoltu proxy ensures same address on every chain. Deployments fail if the resulting address doesn't match `expectedAddress`.
- **Code hash verification**: Post-deploy bytecode integrity is verified against `expectedCodeHash`.
- **Dependency checking**: Before deploying to any network, all dependencies (contract addresses) are verified to have code on-chain.
- **Idempotent deploys**: If code already exists at the expected address, deployment is skipped for that network.

## License

DecentraLicense 1.0 (LicenseRef-DCL-1.0). All source files must have SPDX headers. REUSE compliance is enforced in CI.
32 changes: 32 additions & 0 deletions audit/2026-03-05-01/pass0/process.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Pass 0: Process Review

## Documents Reviewed

- `CLAUDE.md` (56 lines)
- `README.md` (32 lines)
- `flake.nix` (19 lines)
- `.github/workflows/rainix.yaml` (57 lines)
- `REUSE.toml` (17 lines)
- `.gitignore` (3 lines)

## Findings

### A01-1 [LOW] Missing foundry.toml

`REUSE.toml` lists `foundry.toml` in its annotation paths (line 14), but no `foundry.toml` exists at the project root. This means either:
- The project relies on a foundry.toml provided by the rainix nix devshell at runtime, which is not documented
- The REUSE.toml annotation is stale

A future session trying to customize forge settings (e.g., remappings, optimizer settings, solc version) would not know where configuration lives or whether it's expected to exist.

### A01-2 [LOW] CLAUDE.md describes Zoltu as "CREATE2-style" but it is not CREATE2

CLAUDE.md line 7 says "by using CREATE2-style deterministic deployments." The Zoltu proxy uses `CREATE` (not `CREATE2`) — the determinism comes from the proxy's nonce being predictable (nonce 1 on first use). This could mislead a future session into reasoning about CREATE2 salt mechanics that don't apply here.

### A01-3 [INFO] CI environment variables not documented in CLAUDE.md

The CI workflow references several env vars (`ETH_RPC_URL`, `DEPLOYMENT_KEY`, `DEPLOY_BROADCAST`, `DEPLOY_VERIFIER`, `DEPLOY_METABOARD_ADDRESS`, etc.) that are not mentioned in CLAUDE.md. A future session attempting to run deployment scripts locally would not know what variables are expected.

### A01-4 [INFO] No foundry.toml means no documented solc version or optimizer settings

Without a `foundry.toml` in the repo, there's no record of which Solidity compiler version or optimizer settings are used. The pragma in the source is `^0.8.25` which allows any 0.8.x >= 0.8.25. The actual compiler version used depends on whatever the nix devshell provides, which is opaque to someone reading only this repo.
72 changes: 72 additions & 0 deletions audit/2026-03-05-01/pass1/LibRainDeploy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# Pass 1: Security -- LibRainDeploy.sol

**Agent:** A01
**File:** `src/lib/LibRainDeploy.sol` (148 lines)

## Evidence of Thorough Reading

**Library name:** `LibRainDeploy` (line 14)

**Functions:**
| Function | Line | Visibility |
|----------|------|------------|
| `deployZoltu(bytes memory creationCode)` | 48 | internal |
| `supportedNetworks()` | 67 | internal pure |
| `deployAndBroadcastToSupportedNetworks(Vm, string[], uint256, bytes, string, address, bytes32, address[])` | 83 | internal |

**Errors:**
| Error | Line |
|-------|------|
| `DeployFailed(bool success, address deployedAddress)` | 18 |
| `MissingDependency(string network, address dependency)` | 21 |
| `UnexpectedDeployedAddress(address expected, address actual)` | 24 |
| `UnexpectedDeployedCodeHash(bytes32 expected, bytes32 actual)` | 27 |

**Constants:**
| Constant | Line | Value |
|----------|------|-------|
| `ZOLTU_FACTORY` | 30 | `0x7A0D94F55792C434d74a40883C6ed8545E406D12` |
| `ARBITRUM_ONE` | 33 | `"arbitrum"` |
| `BASE` | 36 | `"base"` |
| `FLARE` | 39 | `"flare"` |
| `POLYGON` | 42 | `"polygon"` |

**Types:** None defined.

**Imports:** `Vm` from `forge-std/Vm.sol` (line 5), `console2` from `forge-std/console2.sol` (line 6).

## Findings

### A01-1 [LOW] Silent `address(0)` return when `networks` array is empty

**Location:** `deployAndBroadcastToSupportedNetworks`, lines 92-147

**Description:**
If the `networks` parameter is an empty array, both for-loops (lines 98-115 and 118-146) are skipped entirely. The return variable `deployedAddress` is never assigned and defaults to `address(0)`. The function returns successfully with `address(0)`, with no error or revert.

A caller that accidentally passes an empty `networks` array -- for example by constructing it dynamically and encountering a filtering bug -- would receive `address(0)` as a valid deployed address with no indication of failure.

**Impact:** A script relying on the return value would proceed with `address(0)`, which could lead to downstream misconfiguration (e.g., setting `address(0)` as a trusted contract address in further deployment steps).

### A01-2 [LOW] TOCTOU gap between dependency checking and deployment

**Location:** `deployAndBroadcastToSupportedNetworks`, lines 98-146

**Description:**
The function performs dependency checking in a first loop (lines 98-115) by creating a fork for each network and verifying that the Zoltu factory and all dependencies have code. It then performs deployment in a second loop (lines 118-146) by creating a *new* fork for each network via `vm.createSelectFork`.

Each `createSelectFork` call fetches the chain state at the current block. Between the dependency-check fork and the deploy fork for a given network, the underlying chain advances. If a dependency contract were destroyed (via `SELFDESTRUCT` / `SELFDESTRUCT` scheduled in a prior transaction) in the intervening blocks, the deployment would proceed without re-checking, potentially deploying a contract whose constructor references a now-nonexistent dependency.

**Mitigating factors:**
- This runs as a Foundry script, not on-chain, so the window is small (seconds at most)
- `SELFDESTRUCT` of well-established dependency contracts is highly unlikely in practice
- The deployment itself would likely fail or produce wrong code, caught by the `expectedCodeHash` check on line 135

**Impact:** In a contrived scenario, a deployment could succeed on a network where a dependency has been removed between the check and deploy forks. Practically very unlikely but the two-loop pattern introduces an unnecessary gap.

### A01-3 [INFO] Typo in log message

**Location:** Line 140

**Description:**
The log message reads `"manual verficiation command:"` -- "verficiation" should be "verification". This is cosmetic and has no security impact, but could cause confusion when searching logs by keyword.
65 changes: 65 additions & 0 deletions audit/2026-03-05-01/pass2/LibRainDeploy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Pass 2: Test Coverage -- LibRainDeploy.sol

**Agent:** A01
**File:** `src/lib/LibRainDeploy.sol` (148 lines)

## Evidence of Thorough Reading

**Library name:** `LibRainDeploy` (line 14)

**Functions:**
| Function | Line | Visibility |
|----------|------|------------|
| `deployZoltu(bytes memory creationCode)` | 48 | internal |
| `supportedNetworks()` | 67 | internal pure |
| `deployAndBroadcastToSupportedNetworks(Vm, string[], uint256, bytes, string, address, bytes32, address[])` | 83 | internal |

**Errors:**
| Error | Line |
|-------|------|
| `DeployFailed(bool success, address deployedAddress)` | 18 |
| `MissingDependency(string network, address dependency)` | 21 |
| `UnexpectedDeployedAddress(address expected, address actual)` | 24 |
| `UnexpectedDeployedCodeHash(bytes32 expected, bytes32 actual)` | 27 |

**Constants:**
| Constant | Line |
|----------|------|
| `ZOLTU_FACTORY` | 30 |
| `ARBITRUM_ONE` | 33 |
| `BASE` | 36 |
| `FLARE` | 39 |
| `POLYGON` | 42 |

## Test File Search

- `test/` directory: does not exist
- Grep for `LibRainDeploy` across all `.sol` files: only found in `src/lib/LibRainDeploy.sol` itself
- No test files exist anywhere in the project (excluding `lib/forge-std/test/`)

## Findings

### A01-1 [HIGH] No test file exists for LibRainDeploy.sol

**Description:**
The sole source file in this project has zero test coverage. There is no `test/` directory and no test files anywhere in the repo (outside of the forge-std submodule).

Functions with no test coverage:
- `deployZoltu` — the core deployment mechanism using inline assembly
- `supportedNetworks` — pure function returning network list
- `deployAndBroadcastToSupportedNetworks` — the main orchestration function

Error paths with no test coverage:
- `DeployFailed` revert (line 61) — when Zoltu factory call fails or returns zero address
- `MissingDependency` revert (lines 106, 112) — when Zoltu factory or dependencies missing on a network
- `UnexpectedDeployedAddress` revert (line 132) — when deployed address doesn't match expected
- `UnexpectedDeployedCodeHash` revert (line 136) — when code hash doesn't match expected

Edge cases with no coverage:
- Empty `networks` array (returns `address(0)` silently — see Pass 1 A01-1)
- Empty `dependencies` array (valid but untested)
- Empty `creationCode` (behavior undefined)
- `creationCode` that reverts during construction
- Re-deployment when code already exists at expected address (skip path, line 127)

Note: Testing this library is non-trivial because it relies on Foundry VM cheatcodes (`createSelectFork`, `startBroadcast`, `rememberKey`) and external network state. However, Foundry supports forked-mode testing which can exercise these paths.
109 changes: 109 additions & 0 deletions audit/2026-03-05-01/pass3/LibRainDeploy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
# Pass 3: Documentation -- LibRainDeploy.sol

**Agent:** A01
**File:** `src/lib/LibRainDeploy.sol` (148 lines)

## Evidence of Thorough Reading

**Library name:** `LibRainDeploy` (line 14)

**Functions:**
| Function | Line | Visibility |
|----------|------|------------|
| `deployZoltu(bytes memory creationCode)` | 48 | internal |
| `supportedNetworks()` | 67 | internal pure |
| `deployAndBroadcastToSupportedNetworks(Vm, string[], uint256, bytes, string, address, bytes32, address[])` | 83 | internal |

**Errors:**
| Error | Line |
|-------|------|
| `DeployFailed(bool success, address deployedAddress)` | 18 |
| `MissingDependency(string network, address dependency)` | 21 |
| `UnexpectedDeployedAddress(address expected, address actual)` | 24 |
| `UnexpectedDeployedCodeHash(bytes32 expected, bytes32 actual)` | 27 |

**Constants:**
| Constant | Line | Value |
|----------|------|-------|
| `ZOLTU_FACTORY` | 30 | `0x7A0D94F55792C434d74a40883C6ed8545E406D12` |
| `ARBITRUM_ONE` | 33 | `"arbitrum"` |
| `BASE` | 36 | `"base"` |
| `FLARE` | 39 | `"flare"` |
| `POLYGON` | 42 | `"polygon"` |

**Types:** None defined.

**Imports:** `Vm` from `forge-std/Vm.sol` (line 5), `console2` from `forge-std/console2.sol` (line 6).

## Documentation Checklist

| Item | Has `@title`? | Has `@param` for all? | Has `@return` for all? | Description accurate? |
|------|---------------|------------------------|------------------------|-----------------------|
| Library `LibRainDeploy` | Yes (line 8) | N/A | N/A | See A01-2 |
| `deployZoltu` | N/A | Yes (1/1) | Yes (1/1) | Yes |
| `supportedNetworks` | N/A | Yes (0/0) | Yes (1/1) | Yes |
| `deployAndBroadcastToSupportedNetworks` | N/A | **No (3/8)** | Yes (1/1) | **No** |
| Error `DeployFailed` | N/A | N/A | N/A | Yes |
| Error `MissingDependency` | N/A | N/A | N/A | Yes |
| Error `UnexpectedDeployedAddress` | N/A | N/A | N/A | Yes |
| Error `UnexpectedDeployedCodeHash` | N/A | N/A | N/A | Yes |
| Constant `ZOLTU_FACTORY` | N/A | N/A | N/A | Yes |
| Constant `ARBITRUM_ONE` | N/A | N/A | N/A | Yes |
| Constant `BASE` | N/A | N/A | N/A | Yes |
| Constant `FLARE` | N/A | N/A | N/A | Yes |
| Constant `POLYGON` | N/A | N/A | N/A | Yes |

## Findings

### A01-1 [LOW] `deployAndBroadcastToSupportedNetworks` is missing `@param` tags for 5 of 8 parameters

**Location:** Lines 76-82

**Description:**
The function `deployAndBroadcastToSupportedNetworks` accepts 8 parameters but only documents 3 of them via `@param` tags:

Documented:
- `@param vm` (line 79)
- `@param deployerPrivateKey` (line 80)
- `@param creationCode` (line 81)

Missing:
- `networks` (parameter on line 85) -- the list of network names to deploy to
- `contractPath` (parameter on line 88) -- used in the `forge verify-contract` log output
- `expectedAddress` (parameter on line 89) -- the expected deterministic address; also used to skip deployment if code already exists
- `expectedCodeHash` (parameter on line 90) -- used to verify the deployed bytecode
- `dependencies` (parameter on line 91) -- addresses that must have code on each network before deployment proceeds

This is over half the function's parameters left undocumented. For a deployment function that handles private keys and cross-chain orchestration, callers need clear documentation of every parameter's purpose and expectations.

### A01-2 [LOW] NatSpec description for `deployAndBroadcastToSupportedNetworks` is inaccurate

**Location:** Lines 76-78

**Description:**
The NatSpec reads:

```
/// Deploys the given creation code via the Zoltu factory to all supported
/// networks, broadcasting the deployment transaction using the given private
/// key.
```

This says "to all supported networks", but the function does not call `supportedNetworks()` internally. It deploys to whatever `networks` array is passed by the caller (line 85). The caller may pass a subset of supported networks, a single network, or even networks not in the "supported" list.

The description should reflect that it deploys to the provided `networks` array, not implicitly "all supported networks."

### A01-3 [LOW] `@return` description for `deployAndBroadcastToSupportedNetworks` is misleading

**Location:** Line 82

**Description:**
The `@return` tag reads:

```
/// @return deployedAddress The address of the deployed contract on the last network.
```

The phrase "on the last network" implies the address may differ per network, but Zoltu factory deployments are deterministic -- the same creation code always produces the same address on every chain. The function enforces this by checking `deployedAddress == expectedAddress` on every iteration (line 131-133).

A more accurate description would state that this is the deterministic deployment address, verified to match `expectedAddress` on all networks.
Loading
Loading