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
24 changes: 18 additions & 6 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,13 @@ These are referenced in `foundry.toml` under `[rpc_endpoints]`.
returns the deployed address
- `supportedNetworks()` — returns the list of Rain-supported network names (used
as foundry RPC config aliases)
- `isStartBlock(...)` / `findDeployBlock(...)` — binary search a fork's history
for the block a contract first appears at
- `isStartBlock(...)` — reads two adjacent blocks: true when the target has the
expected code hash at a block and does not have it at the block before
- `findDeployBlock(...)` — binary searches a fork's history for a block where
the target has the expected code hash and did not have it at the block before.
Either one is "the block a contract first appears at" only where that code
hash is monotone; against a target that held the hash, lost it and holds it
again both answer about an appearance neither can identify
- `checkResolvedAddresses(...)` — asserts an already-deployed contract holds the
addresses the deployment expected, on the currently selected fork, via
consumer-supplied static reads
Expand All @@ -92,8 +97,10 @@ These are referenced in `foundry.toml` under `[rpc_endpoints]`.
is the only point at which such a check means anything: registry bindings are
mutable, so a pre-deploy check would read a source that can change before the
constructor that consumes it
- `deployToNetworks(...)` — forks each network, verifies the factory and
dependencies, deploys via Zoltu, verifies address and code hash
- `deployToNetworks(...)` — forks each network; where the expected address is
still empty it verifies the factory and dependencies on that fork, deploys via
Zoltu and checks the address; the code hash is checked on every network,
deployed here or already there
- `deployAndBroadcast(...)` — the main entry point: derives the deployer from a
private key, then `deployToNetworks`

Expand Down Expand Up @@ -292,8 +299,13 @@ expected addresses, expected code hashes, and dependency lists.
- **Code hash verification**: Post-deploy bytecode integrity is verified against
`expectedCodeHash`. The address registry is verified the same way before it is
read.
- **Dependency checking**: Before deploying to any network, all dependencies
(contract addresses) are verified to have code on-chain.
- **Dependency checking**: per network, and only where the deploy actually runs.
On a network with no code at `expectedAddress`, the Zoltu factory must have
code and match `ZOLTU_FACTORY_CODEHASH` and every dependency must have code,
all read on that network's own fork immediately before broadcasting. A network
that already has the code skips the deploy and the dependency check with it: a
contract that is already deployed does not need its dependencies present to
stay deployed. There is no all-network pre-flight.
- **Idempotent deploys**: If code already exists at the expected address,
deployment is skipped for that network.
- **Resolve once, verify after**: registry bindings are mutable, so the
Expand Down
32 changes: 30 additions & 2 deletions src/lib/LibRainDeploy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,15 @@ library LibRainDeploy {
/// hash at `blockNumber` and does NOT have it at `blockNumber - 1`. At
/// block 0, only the first condition is checked. The fork is restored to
/// its original block number after checking.
///
/// "First" is only true of a target whose code hash is MONOTONE: once it
/// equals `expectedCodeHash` at some block it equals it at every later
/// block. This reads two adjacent blocks and nothing else, so on a target
/// that held `expectedCodeHash`, lost it and holds it again — a pre-Cancun
/// `SELFDESTRUCT` followed by a `CREATE2` redeploy of the same code at the
/// same address, say — it answers true at EVERY block where the hash
/// reappears, not only the earliest. Monotonicity is the caller's to know:
/// two blocks cannot show it.
/// @param vm The Vm instance for fork manipulation.
/// @param target The contract address to check.
/// @param expectedCodeHash The code hash to look for.
Expand All @@ -106,8 +115,27 @@ library LibRainDeploy {
/// searching the fork history. Requires an active fork with archive access
/// back to `startBlock`. The fork is restored to its original block
/// number before returning. The target's code hash is verified against the
/// expected value before searching. The result is validated via
/// `isStartBlock`.
/// expected value before searching.
///
/// The search REQUIRES the target's code hash to be monotone over
/// `[startBlock, block.number]`, in the sense `isStartBlock` describes.
/// Against a target that held `expectedCodeHash`, lost it and holds it
/// again, the search converges on one of those appearances with no way to
/// say which, and the result is meaningless as the subgraph start block it
/// is typically used as.
///
/// That is the caller's precondition because nothing here can check it, and
/// `isStartBlock` least of all. `high` is only ever a block where the code
/// hash matches: it starts at the current block, checked above, and
/// otherwise takes a `mid` the loop has just read as matching. `low` is
/// either `startBlock`, checked above as NOT matching, or one past a `mid`
/// the loop has just read as not matching. It cannot still be `startBlock`
/// when the loop ends, because that needs `high` down at `startBlock` and
/// `high` is only ever a matching block, so where they meet the hash
/// matches and did not match at the block before — `isStartBlock`'s exact
/// condition, satisfied by construction and satisfied on a non-monotone
/// history just the same. Running it on the result would read two more
/// archive blocks to agree with itself.
/// @param vm The Vm instance for fork manipulation.
/// @param target The contract address to search for.
/// @param expectedCodeHash The expected code hash of the target contract.
Expand Down
3 changes: 2 additions & 1 deletion test/src/lib/LibRainDeploy.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,8 @@ contract LibRainDeployTest is Test {
}

/// `deployZoltu` MUST deploy a contract via the Zoltu factory and return
/// the deterministic address predicted by the factory's nonce.
/// the deterministic address the factory derives with `CREATE2` over the
/// creation code under a zero salt.
function testDeployZoltu() external {
vm.createSelectFork(LibRainDeploy.ARBITRUM_ONE);
address deployed = this.externalDeployZoltu(type(MockDeployable).creationCode);
Expand Down
Loading