From 09641c4523e20585724f3673526e01fed48524ba Mon Sep 17 00:00:00 2001 From: thedavidmeister Date: Thu, 12 Mar 2026 16:35:18 +0400 Subject: [PATCH 1/8] Add CLAUDE.md for Claude Code onboarding Co-Authored-By: Claude Opus 4.6 --- CLAUDE.md | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000..fcf9b4a1 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,64 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +Rain Protocol metadata system — Solidity contracts, Rust CLI/bindings, and a Graph subgraph for emitting and indexing on-chain metadata following the [MetadataV1 spec](https://github.com/rainprotocol/specs/blob/main/metadata-v1.md). + +The core contract is **MetaBoard** — deterministically deployed at `0xfb8437AeFBB8031064E274527C5fc08e30Ac6928` across all supported networks. It emits `MetaV1_2` events that the subgraph indexes. + +## Build & Test Commands + +All commands require the Nix development shell. Use `nix develop` to enter it, or prefix commands with `nix develop -c`. + +| Task | Command | +|------|---------| +| Solidity tests | `nix develop -c rainix-sol-test` | +| Solidity static analysis | `nix develop -c rainix-sol-static` | +| Solidity build artifacts | `nix develop -c rainix-sol-artifacts` | +| Rust tests | `nix develop -c rainix-rs-test` | +| Rust static analysis | `nix develop -c rainix-rs-static` | +| Rust build artifacts | `nix develop -c rainix-rs-artifacts` | +| Subgraph build | `nix develop -c subgraph-build` | +| Subgraph tests | `nix develop -c subgraph-test` | +| REUSE license check | `nix develop -c rainix-sol-legal` | + +Run a single Solidity test (inside nix shell): +``` +forge test --match-test testFunctionName +forge test --match-contract MetaBoardTest +``` + +Run a single Rust test (inside nix shell): +``` +cargo test test_name +``` + +## Architecture + +### Solidity (`src/`) +- `src/concrete/MetaBoard.sol` — Main contract; emits `MetaV1_2` events with sender, subject, and metadata bytes +- `src/lib/LibMeta.sol` — Metadata validation; checks magic number prefix `0xff0a89c674ee7874` +- `src/lib/LibDescribedByMeta.sol` — Helper for contracts implementing `IDescribedByMetaV1` +- `src/lib/deploy/LibMetaBoardDeploy.sol` — Deterministic deployment using Zoltu deployer pattern + +### Rust (`crates/`) +- `crates/cli` — `rain-metadata` binary; metadata generation/validation for multiple types (authoring, dotrain, Solidity ABI, etc.) +- `crates/bindings` — Solidity bindings generated via `alloy::sol!` from JSON ABIs in `/out` +- `crates/metaboard` — GraphQL client (Cynic) for querying MetaBoard subgraph data + +### Subgraph (`subgraph/`) +- AssemblyScript handlers indexing `MetaV1_2` events from MetaBoard +- Deployed across ~15 networks (Arbitrum, Base, Polygon, Flare, etc.) + +## Key Configuration + +- **Solidity**: `foundry.toml` — solc 0.8.25, Cancun EVM, optimizer 1M runs, `bytecode_hash = "none"`, `cbor_metadata = false` +- **Rust workspace**: `Cargo.toml` at root, three crates +- **Fuzz runs**: 5,096 (foundry.toml `[fuzz]`) +- **Remappings**: `rain.deploy/=lib/rain.deploy/src/` + +## Licensing + +DecentraLicense 1.0 (DCL-1.0). REUSE 3.2 compliant — all files need SPDX license headers. From 5e73dd4f5b528dd40f5e2379ba635fe02cc1e48e Mon Sep 17 00:00:00 2001 From: thedavidmeister Date: Thu, 12 Mar 2026 17:07:21 +0400 Subject: [PATCH 2/8] Fix subgraph deployed addresses to match deterministic deploy All subgraph config files (networks.json, subgraph.yaml) and test utils were referencing old pre-deterministic MetaBoard addresses. Updated to the canonical address from LibMetaBoardDeploy and added Solidity tests to enforce consistency across all references. Co-Authored-By: Claude Opus 4.6 --- foundry.toml | 6 ++++ subgraph/networks.json | 12 ++++---- subgraph/subgraph.yaml | 2 +- subgraph/tests/address.ts | 5 ++++ subgraph/tests/utils.ts | 5 ++-- test/lib/deploy/LibMetaBoardDeploy.t.sol | 35 ++++++++++++++++++++++++ 6 files changed, 56 insertions(+), 9 deletions(-) create mode 100644 subgraph/tests/address.ts diff --git a/foundry.toml b/foundry.toml index 8896ee71..0b39a18b 100644 --- a/foundry.toml +++ b/foundry.toml @@ -9,11 +9,17 @@ evm_version = "cancun" bytecode_hash = "none" cbor_metadata = false +ffi = true remappings = [ "rain.deploy/=lib/rain.deploy/src/" ] +fs_permissions = [ + { access = "read", path = "subgraph/networks.json" }, + { access = "read", path = "subgraph/subgraph.yaml" } +] + [fuzz] runs = 5096 diff --git a/subgraph/networks.json b/subgraph/networks.json index 321c3a11..393feb5d 100644 --- a/subgraph/networks.json +++ b/subgraph/networks.json @@ -1,37 +1,37 @@ { "matic": { "metaboard0": { - "address": "0x23F77e7Bc935503e437166498D7D72f2Ea290E1f", + "address": "0xfb8437AeFBB8031064E274527C5fc08e30Ac6928", "startBlock": 55856789 } }, "arbitrum-one": { "metaboard0": { - "address": "0x1eFd85E6C384fAD9B80C6D508E9098Eb91C4eD30", + "address": "0xfb8437AeFBB8031064E274527C5fc08e30Ac6928", "startBlock": 256572417 } }, "base": { "metaboard0": { - "address": "0x59401C9302E79Eb8AC6aea659B8B3ae475715e86", + "address": "0xfb8437AeFBB8031064E274527C5fc08e30Ac6928", "startBlock": 18309350 } }, "mainnet": { "metaboard0": { - "address": "0x8F61d274aaB5D8CFD82dc266529EAe33020386a9", + "address": "0xfb8437AeFBB8031064E274527C5fc08e30Ac6928", "startBlock": 21041769 } }, "berachain-mainnet": { "metaboard0": { - "address": "0x8F61d274aaB5D8CFD82dc266529EAe33020386a9", + "address": "0xfb8437AeFBB8031064E274527C5fc08e30Ac6928", "startBlock": 3092258 } }, "sonic": { "metaboard0": { - "address": "0x8F61d274aaB5D8CFD82dc266529EAe33020386a9", + "address": "0xfb8437AeFBB8031064E274527C5fc08e30Ac6928", "startBlock": 17707513 } } diff --git a/subgraph/subgraph.yaml b/subgraph/subgraph.yaml index f7957ce5..27016f0d 100644 --- a/subgraph/subgraph.yaml +++ b/subgraph/subgraph.yaml @@ -6,7 +6,7 @@ dataSources: name: metaboard0 network: arbitrum-one source: - address: "0x017F5651eB8fa4048BBc17433149c6c035d391A6" + address: "0xfb8437AeFBB8031064E274527C5fc08e30Ac6928" abi: MetaBoard startBlock: 266485075 mapping: diff --git a/subgraph/tests/address.ts b/subgraph/tests/address.ts new file mode 100644 index 00000000..67abe7ef --- /dev/null +++ b/subgraph/tests/address.ts @@ -0,0 +1,5 @@ +// SPDX-License-Identifier: LicenseRef-DCL-1.0 +// SPDX-FileCopyrightText: Copyright (c) 2020 Rain Open Source Software Ltd +import { Address } from "@graphprotocol/graph-ts"; + +export const CONTRACT_ADDRESS = Address.fromString("0xfb8437AeFBB8031064E274527C5fc08e30Ac6928"); diff --git a/subgraph/tests/utils.ts b/subgraph/tests/utils.ts index 85c4937d..dfdec5bd 100644 --- a/subgraph/tests/utils.ts +++ b/subgraph/tests/utils.ts @@ -1,7 +1,8 @@ -import { MetaV1_2 } from "../generated/metaboard0/MetaBoard"; // Update the path as per your file structure +import { MetaV1_2 } from "../generated/metaboard0/MetaBoard"; import { ethereum, Address, BigInt, Bytes } from "@graphprotocol/graph-ts"; import { newMockEvent } from "matchstick-as"; import { handleMetaV1_2 } from "../src/metaBoard"; +import { CONTRACT_ADDRESS } from "./address"; export function createNewMetaV1Event(sender: string, subject: Bytes, meta: Bytes, transactionHash: string, transactionBlockNumber: number, transactionTimestamp: number): MetaV1_2 { @@ -32,4 +33,4 @@ export function handleNewMetaV1Events(events: MetaV1_2[]): void { }); } -export const CONTRACT_ADDRESS = Address.fromString("0x23F77e7Bc935503e437166498D7D72f2Ea290E1f"); +export { CONTRACT_ADDRESS } from "./address"; diff --git a/test/lib/deploy/LibMetaBoardDeploy.t.sol b/test/lib/deploy/LibMetaBoardDeploy.t.sol index e64ba24f..4b332cf2 100644 --- a/test/lib/deploy/LibMetaBoardDeploy.t.sol +++ b/test/lib/deploy/LibMetaBoardDeploy.t.sol @@ -55,4 +55,39 @@ contract LibMetaBoardDeployTest is Test { function testProdDeployPolygon() external { checkProdDeployment("CI_FORK_POLYGON_RPC_URL"); } + + function testSubgraphYamlAddress() external { + string[] memory inputs = new string[](3); + inputs[0] = "yq"; + inputs[1] = ".dataSources[0].source.address"; + inputs[2] = "subgraph/subgraph.yaml"; + bytes memory result = vm.ffi(inputs); + address addr = address(bytes20(result)); + assertEq(addr, LibMetaBoardDeploy.METABOARD_DEPLOYED_ADDRESS, "subgraph.yaml address mismatch"); + } + + function testSubgraphTestAddressTs() external { + string[] memory inputs = new string[](4); + inputs[0] = "grep"; + inputs[1] = "-oP"; + inputs[2] = "0x[0-9a-fA-F]{40}"; + inputs[3] = "subgraph/tests/address.ts"; + bytes memory result = vm.ffi(inputs); + address addr = address(bytes20(result)); + assertEq(addr, LibMetaBoardDeploy.METABOARD_DEPLOYED_ADDRESS, "subgraph/tests/address.ts address mismatch"); + } + + function testNetworksJsonAddresses() external view { + string memory json = vm.readFile("subgraph/networks.json"); + string[] memory networks = vm.parseJsonKeys(json, "$"); + for (uint256 i = 0; i < networks.length; i++) { + string memory path = string.concat(".", networks[i], ".metaboard0.address"); + address addr = vm.parseJsonAddress(json, path); + assertEq( + addr, + LibMetaBoardDeploy.METABOARD_DEPLOYED_ADDRESS, + string.concat("networks.json address mismatch: ", networks[i]) + ); + } + } } From 39ebe60bb12256619aa7c131b59966afa9d122c3 Mon Sep 17 00:00:00 2001 From: thedavidmeister Date: Thu, 12 Mar 2026 17:52:33 +0400 Subject: [PATCH 3/8] update deploy --- flake.lock | 42 +++++++++++++++++++++--------------------- foundry.lock | 2 +- lib/rain.deploy | 2 +- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/flake.lock b/flake.lock index 9a12e0c4..f8677cdc 100644 --- a/flake.lock +++ b/flake.lock @@ -75,11 +75,11 @@ "nixpkgs": "nixpkgs" }, "locked": { - "lastModified": 1769324704, - "narHash": "sha256-aef15vEgiMEls1hTMt46rJuKNSO2cIOfiP99patq9yc=", + "lastModified": 1773213477, + "narHash": "sha256-Pv1Z3QqGkSGEUV+9pM5vYIiI7VJo7Tfm6ZmR+JSp1zo=", "owner": "shazow", "repo": "foundry.nix", - "rev": "e830409ba1bdecdc5ef9a1ec92660fc2da9bc68d", + "rev": "3c73daa86c823d706824fd9bbcb85aa23fd0f668", "type": "github" }, "original": { @@ -120,11 +120,11 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1769364508, - "narHash": "sha256-Wy8EVYSLq5Fb/rYH3LRxAMCnW75f9hOg2562AXVFmPk=", + "lastModified": 1773260212, + "narHash": "sha256-lYbvufol78/ziIlZm043NT2byudb6NQpGHhYhVSlkVY=", "owner": "nixos", "repo": "nixpkgs", - "rev": "6077bc4fb29be43d525984f63b69d37b9b1e62fe", + "rev": "37e1923adb6229cea0d12a5921b75c976017ccfe", "type": "github" }, "original": { @@ -151,11 +151,11 @@ }, "nixpkgs_4": { "locked": { - "lastModified": 1766653575, - "narHash": "sha256-TPgxCS7+hWc4kPhzkU5dD2M5UuPhLuuaMNZ/IpwKQvI=", + "lastModified": 1771923393, + "narHash": "sha256-Fy0+UXELv9hOE8WjYhJt8fMDLYTU2Dqn3cX4BwoGBos=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "3c1016e6acd16ad96053116d0d3043029c9e2649", + "rev": "ea7f1f06811ce7fcc81d6c6fd4213150c23edcf2", "type": "github" }, "original": { @@ -175,11 +175,11 @@ "solc": "solc" }, "locked": { - "lastModified": 1769366341, - "narHash": "sha256-jeYOweTuJdKshW9lqVoNxvl4+flyRzWxEctRGabTW/8=", + "lastModified": 1773320222, + "narHash": "sha256-g23QULj3vRvtuOsYd0JvGFuM7rEklgkhxsQMqwYlAjg=", "owner": "rainprotocol", "repo": "rainix", - "rev": "e7bfe9c39d2de818eac241f88ecabc69e86ed734", + "rev": "a001dd86d352c374f31ee0dc850a683edbe09871", "type": "github" }, "original": { @@ -199,11 +199,11 @@ "nixpkgs": "nixpkgs_3" }, "locked": { - "lastModified": 1769309768, - "narHash": "sha256-AbOIlNO+JoqRJkK1VrnDXhxuX6CrdtIu2hSuy4pxi3g=", + "lastModified": 1773216618, + "narHash": "sha256-iZlowevS+xKLGOXtZwpIrz3SWe7PtoGUfEeVZNib+WE=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "140c9dc582cb73ada2d63a2180524fcaa744fad5", + "rev": "07d7dc6fcc5eae76b4fb0e19d4afd939437bec97", "type": "github" }, "original": { @@ -219,11 +219,11 @@ "solc-macos-amd64-list-json": "solc-macos-amd64-list-json" }, "locked": { - "lastModified": 1768831671, - "narHash": "sha256-0mmlYRtZK+eomevkQCCH7PL8QlSuALZQsjLroCWGE08=", + "lastModified": 1772085240, + "narHash": "sha256-+NEcuhT2A0QQumVx9Ze6g2iuNicyuW028Jq/HUJHGh4=", "owner": "hellwolf", "repo": "solc.nix", - "rev": "80ad871b93d15c7bccf71617f78f73c2d291a9c7", + "rev": "d3cc119973e484ea366f4b997b404bb00d7829ca", "type": "github" }, "original": { @@ -235,13 +235,13 @@ "solc-macos-amd64-list-json": { "flake": false, "locked": { - "narHash": "sha256-P+ZslplK4cQ/wnV/wykVKb+yTCviI0eylA3sk9uHmRo=", + "narHash": "sha256-oEiXc95EghuYCudzkPA9XBFOnMdgWFfTO2/4XUfSTpc=", "type": "file", - "url": "https://github.com/argotorg/solc-bin/raw/a11f1ad/macosx-amd64/list.json" + "url": "https://github.com/argotorg/solc-bin/raw/83cb756/macosx-amd64/list.json" }, "original": { "type": "file", - "url": "https://github.com/argotorg/solc-bin/raw/a11f1ad/macosx-amd64/list.json" + "url": "https://github.com/argotorg/solc-bin/raw/83cb756/macosx-amd64/list.json" } }, "systems": { diff --git a/foundry.lock b/foundry.lock index a6749934..e3cffd73 100644 --- a/foundry.lock +++ b/foundry.lock @@ -3,6 +3,6 @@ "rev": "1801b0541f4fda118a10798fd3486bb7051c5dd6" }, "lib/rain.deploy": { - "rev": "ccfdb378d2ab1ed95461ecf1355682250e487e4c" + "rev": "43a6ed3a98f0141e1963b4b9136e8c80e2889bd1" } } \ No newline at end of file diff --git a/lib/rain.deploy b/lib/rain.deploy index ccfdb378..43a6ed3a 160000 --- a/lib/rain.deploy +++ b/lib/rain.deploy @@ -1 +1 @@ -Subproject commit ccfdb378d2ab1ed95461ecf1355682250e487e4c +Subproject commit 43a6ed3a98f0141e1963b4b9136e8c80e2889bd1 From 701ea684830cac0e3617669365a72b586604a400 Mon Sep 17 00:00:00 2001 From: thedavidmeister Date: Thu, 12 Mar 2026 19:12:06 +0400 Subject: [PATCH 4/8] Add start block constants and enforce subgraph config consistency Add deploy start block constants for all 5 supported networks to LibMetaBoardDeploy. Add tests that verify start blocks via binary search (findDeployBlock) and boundary check (isStartBlock) against live RPCs. Add tests that parse networks.json to assert addresses and startBlock values match the Solidity constants. Remove unsupported networks (mainnet, berachain-mainnet, sonic) from networks.json. Add flare and base-sepolia. Fix all startBlock values to match the verified deploy blocks. Co-Authored-By: Claude Opus 4.6 --- flake.lock | 6 +- foundry.toml | 1 + src/lib/deploy/LibMetaBoardDeploy.sol | 6 ++ subgraph/networks.json | 20 ++--- test/lib/deploy/LibMetaBoardDeploy.t.sol | 97 ++++++++++++++++++++++++ 5 files changed, 114 insertions(+), 16 deletions(-) diff --git a/flake.lock b/flake.lock index f8677cdc..e9f0d4de 100644 --- a/flake.lock +++ b/flake.lock @@ -175,11 +175,11 @@ "solc": "solc" }, "locked": { - "lastModified": 1773320222, - "narHash": "sha256-g23QULj3vRvtuOsYd0JvGFuM7rEklgkhxsQMqwYlAjg=", + "lastModified": 1773326212, + "narHash": "sha256-NDEwqacK1T59486Nlb/Kx2ks1vh92TiHVHcEL9dZuYc=", "owner": "rainprotocol", "repo": "rainix", - "rev": "a001dd86d352c374f31ee0dc850a683edbe09871", + "rev": "266fd905bb0797c614b6f4baf8a747c2c07376cb", "type": "github" }, "original": { diff --git a/foundry.toml b/foundry.toml index 0b39a18b..db80bbb6 100644 --- a/foundry.toml +++ b/foundry.toml @@ -1,6 +1,7 @@ [profile.default] solc = "0.8.25" +auto_detect_solc = false # optimizer settings for snapshotting. optimizer = true diff --git a/src/lib/deploy/LibMetaBoardDeploy.sol b/src/lib/deploy/LibMetaBoardDeploy.sol index a7d051bc..29058ffa 100644 --- a/src/lib/deploy/LibMetaBoardDeploy.sol +++ b/src/lib/deploy/LibMetaBoardDeploy.sol @@ -19,4 +19,10 @@ library LibMetaBoardDeploy { /// than just checking the address. bytes32 constant METABOARD_DEPLOYED_CODEHASH = bytes32(0x60e0735a3406074fd8f85adb2813d0d7c346337ea4bcc6f2ef4eb25077a4933c); + + uint256 constant METABOARD_START_BLOCK_ARBITRUM = 431042729; + uint256 constant METABOARD_START_BLOCK_BASE = 42021282; + uint256 constant METABOARD_START_BLOCK_BASE_SEPOLIA = 38683088; + uint256 constant METABOARD_START_BLOCK_FLARE = 55347067; + uint256 constant METABOARD_START_BLOCK_POLYGON = 82855948; } diff --git a/subgraph/networks.json b/subgraph/networks.json index 393feb5d..712145e6 100644 --- a/subgraph/networks.json +++ b/subgraph/networks.json @@ -2,37 +2,31 @@ "matic": { "metaboard0": { "address": "0xfb8437AeFBB8031064E274527C5fc08e30Ac6928", - "startBlock": 55856789 + "startBlock": 82855948 } }, "arbitrum-one": { "metaboard0": { "address": "0xfb8437AeFBB8031064E274527C5fc08e30Ac6928", - "startBlock": 256572417 + "startBlock": 431042729 } }, "base": { "metaboard0": { "address": "0xfb8437AeFBB8031064E274527C5fc08e30Ac6928", - "startBlock": 18309350 + "startBlock": 42021282 } }, - "mainnet": { + "base-sepolia": { "metaboard0": { "address": "0xfb8437AeFBB8031064E274527C5fc08e30Ac6928", - "startBlock": 21041769 + "startBlock": 38683088 } }, - "berachain-mainnet": { + "flare": { "metaboard0": { "address": "0xfb8437AeFBB8031064E274527C5fc08e30Ac6928", - "startBlock": 3092258 - } - }, - "sonic": { - "metaboard0": { - "address": "0xfb8437AeFBB8031064E274527C5fc08e30Ac6928", - "startBlock": 17707513 + "startBlock": 55347067 } } } diff --git a/test/lib/deploy/LibMetaBoardDeploy.t.sol b/test/lib/deploy/LibMetaBoardDeploy.t.sol index 4b332cf2..5e7ba500 100644 --- a/test/lib/deploy/LibMetaBoardDeploy.t.sol +++ b/test/lib/deploy/LibMetaBoardDeploy.t.sol @@ -8,6 +8,8 @@ import {LibMetaBoardDeploy} from "src/lib/deploy/LibMetaBoardDeploy.sol"; import {MetaBoard} from "src/concrete/MetaBoard.sol"; contract LibMetaBoardDeployTest is Test { + /// Arbitrum Nitro genesis block. Archive RPCs can't serve blocks before this. + uint256 constant ARBITRUM_NITRO_GENESIS_BLOCK = 22207817; function testDeployAddress() external { vm.createSelectFork(vm.envString("CI_FORK_ETH_RPC_URL")); @@ -56,6 +58,70 @@ contract LibMetaBoardDeployTest is Test { checkProdDeployment("CI_FORK_POLYGON_RPC_URL"); } + function findStartBlock(string memory rpcEnvVar, uint256 searchFrom) internal returns (uint256) { + vm.createSelectFork(vm.envString(rpcEnvVar)); + return LibRainDeploy.findDeployBlock( + vm, LibMetaBoardDeploy.METABOARD_DEPLOYED_ADDRESS, LibMetaBoardDeploy.METABOARD_DEPLOYED_CODEHASH, searchFrom + ); + } + + /// Foundry's rollFork on Arbitrum maps to L1 block numbers, not L2, so + /// findDeployBlock returns wrong results. The Arbitrum start block was + /// found via manual binary search using eth_getCode RPC calls against L2 + /// block numbers, and is verified by testIsStartBlockArbitrum instead. + // function testStartBlockArbitrum() external { + // assertEq( + // findStartBlock("CI_FORK_ARB_RPC_URL", ARBITRUM_NITRO_GENESIS_BLOCK), + // LibMetaBoardDeploy.METABOARD_START_BLOCK_ARBITRUM + // ); + // } + + function testStartBlockBase() external { + assertEq(findStartBlock("CI_FORK_BASE_RPC_URL", 0), LibMetaBoardDeploy.METABOARD_START_BLOCK_BASE); + } + + function testStartBlockBaseSepolia() external { + assertEq(findStartBlock("CI_FORK_BASE_SEPOLIA_RPC_URL", 0), LibMetaBoardDeploy.METABOARD_START_BLOCK_BASE_SEPOLIA); + } + + function testStartBlockFlare() external { + assertEq(findStartBlock("CI_FORK_FLARE_RPC_URL", 0), LibMetaBoardDeploy.METABOARD_START_BLOCK_FLARE); + } + + function testStartBlockPolygon() external { + assertEq(findStartBlock("CI_FORK_POLYGON_RPC_URL", 0), LibMetaBoardDeploy.METABOARD_START_BLOCK_POLYGON); + } + + function checkIsStartBlock(string memory rpcEnvVar, uint256 startBlock) internal { + vm.createSelectFork(vm.envString(rpcEnvVar)); + assertTrue( + LibRainDeploy.isStartBlock( + vm, LibMetaBoardDeploy.METABOARD_DEPLOYED_ADDRESS, LibMetaBoardDeploy.METABOARD_DEPLOYED_CODEHASH, startBlock + ), + string.concat("not start block: ", rpcEnvVar) + ); + } + + function testIsStartBlockArbitrum() external { + checkIsStartBlock("CI_FORK_ARB_RPC_URL", LibMetaBoardDeploy.METABOARD_START_BLOCK_ARBITRUM); + } + + function testIsStartBlockBase() external { + checkIsStartBlock("CI_FORK_BASE_RPC_URL", LibMetaBoardDeploy.METABOARD_START_BLOCK_BASE); + } + + function testIsStartBlockBaseSepolia() external { + checkIsStartBlock("CI_FORK_BASE_SEPOLIA_RPC_URL", LibMetaBoardDeploy.METABOARD_START_BLOCK_BASE_SEPOLIA); + } + + function testIsStartBlockFlare() external { + checkIsStartBlock("CI_FORK_FLARE_RPC_URL", LibMetaBoardDeploy.METABOARD_START_BLOCK_FLARE); + } + + function testIsStartBlockPolygon() external { + checkIsStartBlock("CI_FORK_POLYGON_RPC_URL", LibMetaBoardDeploy.METABOARD_START_BLOCK_POLYGON); + } + function testSubgraphYamlAddress() external { string[] memory inputs = new string[](3); inputs[0] = "yq"; @@ -90,4 +156,35 @@ contract LibMetaBoardDeployTest is Test { ); } } + + function checkNetworksJsonStartBlock(string memory networkKey, uint256 expectedStartBlock) internal view { + string memory json = vm.readFile("subgraph/networks.json"); + string memory path = string.concat(".", networkKey, ".metaboard0.startBlock"); + uint256 startBlock = vm.parseJsonUint(json, path); + assertEq( + startBlock, + expectedStartBlock, + string.concat("networks.json startBlock mismatch: ", networkKey) + ); + } + + function testNetworksJsonStartBlockArbitrum() external view { + checkNetworksJsonStartBlock("arbitrum-one", LibMetaBoardDeploy.METABOARD_START_BLOCK_ARBITRUM); + } + + function testNetworksJsonStartBlockBase() external view { + checkNetworksJsonStartBlock("base", LibMetaBoardDeploy.METABOARD_START_BLOCK_BASE); + } + + function testNetworksJsonStartBlockBaseSepolia() external view { + checkNetworksJsonStartBlock("base-sepolia", LibMetaBoardDeploy.METABOARD_START_BLOCK_BASE_SEPOLIA); + } + + function testNetworksJsonStartBlockFlare() external view { + checkNetworksJsonStartBlock("flare", LibMetaBoardDeploy.METABOARD_START_BLOCK_FLARE); + } + + function testNetworksJsonStartBlockPolygon() external view { + checkNetworksJsonStartBlock("matic", LibMetaBoardDeploy.METABOARD_START_BLOCK_POLYGON); + } } From b4749f2c7d1c97289f04c3498a3ad9074ce6aa48 Mon Sep 17 00:00:00 2001 From: thedavidmeister Date: Thu, 12 Mar 2026 19:16:58 +0400 Subject: [PATCH 5/8] Add missing files to REUSE.toml license annotations Co-Authored-By: Claude Opus 4.6 --- REUSE.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/REUSE.toml b/REUSE.toml index 29965817..32fd7b98 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -2,6 +2,8 @@ version = 1 [[annotations]] path = [ + ".cargo/config.toml", + ".envrc", ".gas-snapshot", ".github/workflows/**/", ".gitignore", @@ -9,7 +11,9 @@ path = [ "audit/**/", "Cargo.lock", "Cargo.toml", + "CLAUDE.md", "crates/**/", + "foundry.lock", "subgraph/**/", "README.md", "flake.lock", From 28ff66bb62ea22edd6cd63f037599b14a6bd4198 Mon Sep 17 00:00:00 2001 From: thedavidmeister Date: Thu, 12 Mar 2026 19:19:24 +0400 Subject: [PATCH 6/8] Fix markdown lint in CLAUDE.md Co-Authored-By: Claude Opus 4.6 --- CLAUDE.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index fcf9b4a1..1df60390 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -25,13 +25,15 @@ All commands require the Nix development shell. Use `nix develop` to enter it, o | REUSE license check | `nix develop -c rainix-sol-legal` | Run a single Solidity test (inside nix shell): -``` + +```sh forge test --match-test testFunctionName forge test --match-contract MetaBoardTest ``` Run a single Rust test (inside nix shell): -``` + +```sh cargo test test_name ``` From c55c9334909acb50d90f7f8a68a1143b1e155c9b Mon Sep 17 00:00:00 2001 From: thedavidmeister Date: Thu, 12 Mar 2026 19:31:29 +0400 Subject: [PATCH 7/8] Suppress slither unindexed-event-address warnings and format Co-Authored-By: Claude Opus 4.6 --- src/interface/deprecated/IMetaV1.sol | 1 + src/interface/unstable/IMetaV1_2.sol | 2 +- test/lib/deploy/LibMetaBoardDeploy.t.sol | 21 +++++++++++++-------- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/interface/deprecated/IMetaV1.sol b/src/interface/deprecated/IMetaV1.sol index acf04c18..95c45ee6 100644 --- a/src/interface/deprecated/IMetaV1.sol +++ b/src/interface/deprecated/IMetaV1.sol @@ -27,5 +27,6 @@ interface IMetaV1 { /// hash of some data/thing that this metadata is about. /// @param meta Rain metadata V1 compliant metadata bytes. /// https://github.com/rainprotocol/specs/blob/main/metadata-v1.md + //slither-disable-next-line unindexed-event-address event MetaV1(address sender, uint256 subject, bytes meta); } diff --git a/src/interface/unstable/IMetaV1_2.sol b/src/interface/unstable/IMetaV1_2.sol index dbca2d27..c739ea65 100644 --- a/src/interface/unstable/IMetaV1_2.sol +++ b/src/interface/unstable/IMetaV1_2.sol @@ -20,6 +20,6 @@ interface IMetaV1_2 { /// hash of some data/thing that this metadata is about. /// @param meta Rain metadata V1 compliant metadata bytes. /// https://github.com/rainprotocol/specs/blob/main/metadata-v1.md - //slither-disable-next-line naming-convention + //slither-disable-next-line naming-convention,unindexed-event-address event MetaV1_2(address sender, bytes32 subject, bytes meta); } diff --git a/test/lib/deploy/LibMetaBoardDeploy.t.sol b/test/lib/deploy/LibMetaBoardDeploy.t.sol index 5e7ba500..89ec294b 100644 --- a/test/lib/deploy/LibMetaBoardDeploy.t.sol +++ b/test/lib/deploy/LibMetaBoardDeploy.t.sol @@ -10,6 +10,7 @@ import {MetaBoard} from "src/concrete/MetaBoard.sol"; contract LibMetaBoardDeployTest is Test { /// Arbitrum Nitro genesis block. Archive RPCs can't serve blocks before this. uint256 constant ARBITRUM_NITRO_GENESIS_BLOCK = 22207817; + function testDeployAddress() external { vm.createSelectFork(vm.envString("CI_FORK_ETH_RPC_URL")); @@ -61,7 +62,10 @@ contract LibMetaBoardDeployTest is Test { function findStartBlock(string memory rpcEnvVar, uint256 searchFrom) internal returns (uint256) { vm.createSelectFork(vm.envString(rpcEnvVar)); return LibRainDeploy.findDeployBlock( - vm, LibMetaBoardDeploy.METABOARD_DEPLOYED_ADDRESS, LibMetaBoardDeploy.METABOARD_DEPLOYED_CODEHASH, searchFrom + vm, + LibMetaBoardDeploy.METABOARD_DEPLOYED_ADDRESS, + LibMetaBoardDeploy.METABOARD_DEPLOYED_CODEHASH, + searchFrom ); } @@ -81,7 +85,9 @@ contract LibMetaBoardDeployTest is Test { } function testStartBlockBaseSepolia() external { - assertEq(findStartBlock("CI_FORK_BASE_SEPOLIA_RPC_URL", 0), LibMetaBoardDeploy.METABOARD_START_BLOCK_BASE_SEPOLIA); + assertEq( + findStartBlock("CI_FORK_BASE_SEPOLIA_RPC_URL", 0), LibMetaBoardDeploy.METABOARD_START_BLOCK_BASE_SEPOLIA + ); } function testStartBlockFlare() external { @@ -96,7 +102,10 @@ contract LibMetaBoardDeployTest is Test { vm.createSelectFork(vm.envString(rpcEnvVar)); assertTrue( LibRainDeploy.isStartBlock( - vm, LibMetaBoardDeploy.METABOARD_DEPLOYED_ADDRESS, LibMetaBoardDeploy.METABOARD_DEPLOYED_CODEHASH, startBlock + vm, + LibMetaBoardDeploy.METABOARD_DEPLOYED_ADDRESS, + LibMetaBoardDeploy.METABOARD_DEPLOYED_CODEHASH, + startBlock ), string.concat("not start block: ", rpcEnvVar) ); @@ -161,11 +170,7 @@ contract LibMetaBoardDeployTest is Test { string memory json = vm.readFile("subgraph/networks.json"); string memory path = string.concat(".", networkKey, ".metaboard0.startBlock"); uint256 startBlock = vm.parseJsonUint(json, path); - assertEq( - startBlock, - expectedStartBlock, - string.concat("networks.json startBlock mismatch: ", networkKey) - ); + assertEq(startBlock, expectedStartBlock, string.concat("networks.json startBlock mismatch: ", networkKey)); } function testNetworksJsonStartBlockArbitrum() external view { From c5c86e1fa72fb12047bce72fa04dd364d4e38f62 Mon Sep 17 00:00:00 2001 From: thedavidmeister Date: Thu, 12 Mar 2026 19:41:54 +0400 Subject: [PATCH 8/8] Skip findDeployBlock tests in CI to avoid RPC rate limits Co-Authored-By: Claude Opus 4.6 --- test/lib/deploy/LibMetaBoardDeploy.t.sol | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/test/lib/deploy/LibMetaBoardDeploy.t.sol b/test/lib/deploy/LibMetaBoardDeploy.t.sol index 89ec294b..94a9d2d8 100644 --- a/test/lib/deploy/LibMetaBoardDeploy.t.sol +++ b/test/lib/deploy/LibMetaBoardDeploy.t.sol @@ -69,10 +69,11 @@ contract LibMetaBoardDeployTest is Test { ); } - /// Foundry's rollFork on Arbitrum maps to L1 block numbers, not L2, so - /// findDeployBlock returns wrong results. The Arbitrum start block was - /// found via manual binary search using eth_getCode RPC calls against L2 - /// block numbers, and is verified by testIsStartBlockArbitrum instead. + /// findDeployBlock binary searches via rollFork which hits RPC rate limits + /// in CI. Skipped there; the isStartBlock tests verify correctness cheaply. + /// Arbitrum is always skipped because Foundry's rollFork maps to L1 block + /// numbers, not L2. The Arbitrum start block was found via manual binary + /// search using eth_getCode RPC calls against L2 block numbers. // function testStartBlockArbitrum() external { // assertEq( // findStartBlock("CI_FORK_ARB_RPC_URL", ARBITRUM_NITRO_GENESIS_BLOCK), @@ -81,20 +82,24 @@ contract LibMetaBoardDeployTest is Test { // } function testStartBlockBase() external { + vm.skip(vm.envOr("CI", false)); assertEq(findStartBlock("CI_FORK_BASE_RPC_URL", 0), LibMetaBoardDeploy.METABOARD_START_BLOCK_BASE); } function testStartBlockBaseSepolia() external { + vm.skip(vm.envOr("CI", false)); assertEq( findStartBlock("CI_FORK_BASE_SEPOLIA_RPC_URL", 0), LibMetaBoardDeploy.METABOARD_START_BLOCK_BASE_SEPOLIA ); } function testStartBlockFlare() external { + vm.skip(vm.envOr("CI", false)); assertEq(findStartBlock("CI_FORK_FLARE_RPC_URL", 0), LibMetaBoardDeploy.METABOARD_START_BLOCK_FLARE); } function testStartBlockPolygon() external { + vm.skip(vm.envOr("CI", false)); assertEq(findStartBlock("CI_FORK_POLYGON_RPC_URL", 0), LibMetaBoardDeploy.METABOARD_START_BLOCK_POLYGON); }