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
8 changes: 4 additions & 4 deletions .gas-snapshot
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
LibDescribedByMetaEmitForDescribedAddressTest:testEmitForDescribedAddressHappy(bytes) (runs: 5096, μ: 256737, ~: 256683)
LibDescribedByMetaEmitForDescribedAddressTest:testEmitForDescribedAddressMismatch(bytes,bytes) (runs: 5092, μ: 258640, ~: 258619)
LibDescribedByMetaEmitForDescribedAddressTest:testEmitForDescribedAddressHappy(bytes) (runs: 5096, μ: 256735, ~: 256683)
LibDescribedByMetaEmitForDescribedAddressTest:testEmitForDescribedAddressMismatch(bytes,bytes) (runs: 5096, μ: 258640, ~: 258619)
LibMetaCheckMetaHashedV1_2Test:testCheckMetaHashedV1_2BadMagicBadHash(bytes,bytes32) (runs: 5096, μ: 5633, ~: 5616)
LibMetaCheckMetaHashedV1_2Test:testCheckMetaHashedV1_2BadMagicGoodHash(bytes) (runs: 5096, μ: 5719, ~: 5694)
LibMetaCheckMetaHashedV1_2Test:testCheckMetaHashedV1_2GoodMagicBadHash(bytes,bytes32) (runs: 5096, μ: 5782, ~: 5771)
LibMetaCheckMetaHashedV1_2Test:testCheckMetaHashedV1_2GoodMagicBadHash(bytes,bytes32) (runs: 5096, μ: 5783, ~: 5771)
LibMetaCheckMetaHashedV1_2Test:testCheckMetaHashedV1_2Happy(bytes) (runs: 5096, μ: 1102, ~: 1095)
LibMetaCheckMetaUnhashedV1_2Test:testCheckMetaUnhashedV1_2Fuzz(bytes) (runs: 5096, μ: 5818, ~: 5797)
LibMetaIsRainMetaV1_2Test:testIsRainMetaV1_2Fuzz(bytes) (runs: 5096, μ: 1025, ~: 1020)
MetaBoardHashTest:testMetaboardHash(bytes) (runs: 5096, μ: 191156, ~: 191140)
MetaBoardTest:testEmitMeta(bytes32,bytes) (runs: 5096, μ: 202190, ~: 202079)
MetaBoardTest:testEmitMeta(bytes32,bytes) (runs: 5096, μ: 202188, ~: 202079)
51 changes: 13 additions & 38 deletions .github/workflows/manual-sol-artifacts.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,40 +2,11 @@ name: Manual sol artifacts
on:
workflow_dispatch:
inputs:
network:
description: "Network to deploy to"
required: true
type: choice
options:
- arbitrum
- arbitrum_sepolia
- avalanche
- base
- bsc
- ethereum
- flare
- mumbai
- oasis_sapphire
- polygon
- sepolia
- songbird
- linea
- matchain
- bera
- sonic

jobs:
deploy:
runs-on: ubuntu-latest
steps:
- run: |
network=${{ inputs.network }}
echo "etherscan_api_key_secret_name=CI_DEPLOY_${network^^}_ETHERSCAN_API_KEY" >> $GITHUB_ENV
echo "rpc_secret_name=CI_DEPLOY_${network^^}_RPC_URL" >> $GITHUB_ENV
echo "verify_secret_name=CI_DEPLOY_${network^^}_VERIFY" >> $GITHUB_ENV
echo "verifier_secret_name=CI_DEPLOY_${network^^}_VERIFIER" >> $GITHUB_ENV
echo "verifier_url_secret_name=CI_DEPLOY_${network^^}_VERIFIER_URL" >> $GITHUB_ENV

- uses: actions/checkout@v4
with:
submodules: recursive
Expand All @@ -58,14 +29,18 @@ jobs:
# 1G = 1073741824
gc-max-store-size-linux: 1G

# - run: nix develop --command rainix-sol-prelude
- name: deploy to ${{ inputs.network }}
run: nix develop --command rainix-sol-artifacts
- run: nix develop -c rainix-sol-prelude
- run: nix develop -c forge selectors up --all
- run: nix develop -c forge script script/Deploy.sol:Deploy -vvvvv --slow --broadcast --verify
env:
DEPLOY_BROADCAST: "1"
DEPLOYMENT_KEY: ${{ github.ref == 'refs/heads/main' && secrets.PRIVATE_KEY || secrets.PRIVATE_KEY_DEV }}
ETH_RPC_URL: ${{ secrets[env.rpc_secret_name] || vars[env.rpc_secret_name] || '' }}
ETHERSCAN_API_KEY: ${{ secrets[env.etherscan_api_key_secret_name] || vars[env.etherscan_api_key_secret_name] || ''}}
DEPLOY_VERIFY: ${{ secrets[env.verify_secret_name] || vars[env.verify_secret_name] || '' }}
DEPLOY_VERIFIER: ${{ secrets[env.verifier_secret_name] || vars[env.verifier_secret_name] || '' }}
DEPLOY_VERIFIER_URL: ${{ secrets[env.verifier_url_secret_name] || vars[env.verifier_url_secret_name] || '' }}

CI_DEPLOY_ARBITRUM_RPC_URL: ${{ secrets.CI_DEPLOY_ARBITRUM_RPC_URL || vars.CI_DEPLOY_ARBITRUM_RPC_URL || '' }}
CI_DEPLOY_BASE_RPC_URL: ${{ secrets.CI_DEPLOY_BASE_RPC_URL || vars.CI_DEPLOY_BASE_RPC_URL || '' }}
CI_DEPLOY_FLARE_RPC_URL: ${{ secrets.CI_DEPLOY_FLARE_RPC_URL || vars.CI_DEPLOY_FLARE_RPC_URL || '' }}
CI_DEPLOY_POLYGON_RPC_URL: ${{ secrets.CI_DEPLOY_POLYGON_RPC_URL || vars.CI_DEPLOY_POLYGON_RPC_URL || '' }}

CI_DEPLOY_ARBITRUM_ETHERSCAN_API_KEY: ${{ secrets.CI_DEPLOY_ARBITRUM_ETHERSCAN_API_KEY || vars.CI_DEPLOY_ARBITRUM_ETHERSCAN_API_KEY || '' }}
CI_DEPLOY_BASE_ETHERSCAN_API_KEY: ${{ secrets.CI_DEPLOY_BASE_ETHERSCAN_API_KEY || vars.CI_DEPLOY_BASE_ETHERSCAN_API_KEY || '' }}
CI_DEPLOY_FLARE_ETHERSCAN_API_KEY: ${{ secrets.CI_DEPLOY_FLARE_ETHERSCAN_API_KEY || vars.CI_DEPLOY_FLARE_ETHERSCAN_API_KEY || '' }}
CI_DEPLOY_POLYGON_ETHERSCAN_API_KEY: ${{ secrets.CI_DEPLOY_POLYGON_ETHERSCAN_API_KEY || vars.CI_DEPLOY_POLYGON_ETHERSCAN_API_KEY || '' }}
3 changes: 1 addition & 2 deletions .github/workflows/rainix.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ jobs:
task: rainix-sol-test
- os: ubuntu-latest
task: rainix-sol-static
- os: ubuntu-latest
task: rainix-sol-artifacts
# We don't need to do rust static analysis on multiple platforms
- os: ubuntu-latest
task: rainix-rs-static
Expand Down Expand Up @@ -52,6 +50,7 @@ jobs:
- name: Run ${{ matrix.task }}
env:
ETH_RPC_URL: ${{ secrets.CI_DEPLOY_SEPOLIA_RPC_URL || vars.CI_DEPLOY_SEPOLIA_RPC_URL }}
CI_FORK_ETH_RPC_URL: ${{ secrets.RPC_URL_ETHEREUM_FORK || vars.RPC_URL_ETHEREUM_FORK || '' }}
ETHERSCAN_API_KEY: ${{ secrets.EXPLORER_VERIFICATION_KEY }}
run: nix develop -c ${{ matrix.task }}

Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,7 @@ cache
.vscode
.direnv/

.env

# Rust
target
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
[submodule "lib/forge-std"]
path = lib/forge-std
url = https://github.com/foundry-rs/forge-std
[submodule "lib/rain.deploy"]
path = lib/rain.deploy
url = https://github.com/rainlanguage/rain.deploy
Comment on lines +4 to +6
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# Check .gitmodules file to see the full context
cat -n .gitmodules

Repository: rainlanguage/rain.metadata

Length of output: 313


🏁 Script executed:

# Attempt to verify the repository exists by checking Git metadata
# Since we can't use gh CLI in sandbox, we'll check if there are any setup docs mentioning submodules
fd -t f -e md -e txt -e rst | head -20 | xargs grep -l "submodule\|git.*init" 2>/dev/null || echo "No obvious setup documentation found mentioning submodules"

Repository: rainlanguage/rain.metadata

Length of output: 128


🌐 Web query:

rain.deploy GitHub repository rainlanguage

💡 Result:

rain.deploy is not a GitHub repository under the rainlanguage org—it’s a command (rain deploy) in AWS’s CloudFormation CLI tool Rain, hosted here: aws-cloudformation/rain. [1][2]

The rainlanguage GitHub organization (Rainlang / Rain Protocol ecosystem) is here, and its repos are separate/unrelated to AWS’s rain deploy tool. [3]


🏁 Script executed:

# Search for any references to rain.deploy in the repository
rg "rain\.deploy" --type-list
rg "rain\.deploy" -t md -t js -t sol -t toml

Repository: rainlanguage/rain.metadata

Length of output: 487


🏁 Script executed:

# Check foundry.toml and Deploy.sol mentioned in the original scratchpad to understand what this submodule should be
fd -t f -e toml -e sol | grep -i "foundry\|deploy" | head -10

Repository: rainlanguage/rain.metadata

Length of output: 179


🌐 Web query:

rainlanguage github organization repositories deploy

💡 Result:

The RainLanguage GitHub organization is rainlanguage (displayed as “rainlang” on GitHub). Its repositories are listed on the org page under the Repositories tab (currently ~125 public repos), including core projects like rain.orderbook, rain.interpreter, and others. [1]

For deployment, the org has a dedicated repository rainlanguage/rain.deploy, described as tooling to deploy Solidity code (Foundry + Nix) to production using the Zoltu deterministic deployment proxy, plus helpers to validate supported networks/dependencies and perform post-deploy bytecode integrity checks. [2]

Sources: [1] [2]


🌐 Web query:

rainlanguage rain.deploy github

💡 Result:

There isn’t a rain.deploy repo/command under the rainlanguage / Rainlang GitHub org (that org is here). [1]

What you’re likely looking for is AWS CloudFormation “Rain”, a CLI tool whose command is rain deploy:

  • GitHub repo: aws-cloudformation/rain (this is where rain deploy lives). [2]
  • Official docs for the command: rain deploy (deploys a CloudFormation stack/changeset from a local template). [3]

Quick install (Go):

go install github.com/aws-cloudformation/rain/cmd/rain@latest

[2]


🌐 Web query:

"rainlanguage/rain.deploy" repository github

💡 Result:

There doesn’t appear to be a GitHub repository named rainlanguage/rain.deploy under the rainlanguage organization (a search of the org page shows many rain.* repos, but not rain.deploy). [1]

If you meant the rain deploy command, that’s part of AWS’s Rain CLI for CloudFormation, in aws-cloudformation/rain (docs: “rain deploy”). [2], [3]

Sources:
[1] (github.com)
[2] (github.com)
[3] (aws-cloudformation.github.io)

Citations:


🏁 Script executed:

# Check if there's a .gitmodules entry with a specific commit hash or branch reference
cat -n .gitmodules | grep -A 5 "rain.deploy"

Repository: rainlanguage/rain.metadata

Length of output: 195


🏁 Script executed:

# Look for any documentation or configuration referencing rain.deploy in foundry.toml or other files
fd -t f -e toml | xargs cat | grep -i "rain.deploy" 2>/dev/null || echo "No rain.deploy references in .toml files"

Repository: rainlanguage/rain.metadata

Length of output: 109


The repository URL https://github.com/rainlanguage/rain.deploy does not exist and will prevent the project from being cloned or built.

The submodule points to a non-existent repository under the rainlanguage organization. This will cause git clone and git submodule update --init --recursive to fail. Verify the correct repository URL and update the submodule path accordingly, as foundry.toml references this dependency directly (rain.deploy/=lib/rain.deploy/src/).

🤖 Prompt for AI Agents
In @.gitmodules around lines 4 - 6, The .gitmodules entry for submodule
"lib/rain.deploy" points to a non-existent URL; update the submodule URL to the
correct repository so cloning and submodule init succeed. Locate the submodule
block named lib/rain.deploy in .gitmodules and replace the url value (currently
https://github.com/rainlanguage/rain.deploy) with the correct upstream URL; then
run git submodule sync --recursive and git submodule update --init --recursive
to propagate the change and verify that the path lib/rain.deploy referenced by
foundry.toml (rain.deploy/=lib/rain.deploy/src/) resolves correctly.

16 changes: 16 additions & 0 deletions foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,21 @@ evm_version = "cancun"
bytecode_hash = "none"
cbor_metadata = false

remappings = [
"rain.deploy/=lib/rain.deploy/src/"
]

[fuzz]
runs = 5096

[rpc_endpoints]
arbitrum = "${CI_DEPLOY_ARBITRUM_RPC_URL}"
base = "${CI_DEPLOY_BASE_RPC_URL}"
flare = "${CI_DEPLOY_FLARE_RPC_URL}"
polygon = "${CI_DEPLOY_POLYGON_RPC_URL}"

[etherscan]
arbitrum = { key = "${CI_DEPLOY_ARBITRUM_ETHERSCAN_API_KEY}" }
base = { key = "${CI_DEPLOY_BASE_ETHERSCAN_API_KEY}" }
flare = { key = "${CI_DEPLOY_FLARE_ETHERSCAN_API_KEY}" }
polygon = { key = "${CI_DEPLOY_POLYGON_ETHERSCAN_API_KEY}" }
Comment on lines +26 to +30
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

Foundry etherscan configuration chain field required Flare

💡 Result:

In Foundry, the chain field in [etherscan] is required unless the table key is a known chain name (e.g., mainnet, optimism). If you use a custom key like flare, Foundry will error with “chain field required” unless you set chain (or use a key that Foundry recognizes). [1]

For Flare, set the chain id and the explorer API URL explicitly:

[etherscan]
flare = { key = "${ETHERSCAN_API_KEY}", chain = 14, url = "https://flare-explorer.flare.network/api" }
coston2 = { key = "${ETHERSCAN_API_KEY}", chain = 114, url = "https://coston2-explorer.flare.network/api" }

Flare chain IDs: 14 (mainnet) and 114 (Coston2), and their explorer API bases are https://flare-explorer.flare.network/api and https://coston2-explorer.flare.network/api. [2]

Then verify with:

forge verify-contract --chain 14 <address> src/Your.sol:YourContract --verifier etherscan

Sources: [1], [2]


🌐 Web query:

Foundry supported chains etherscan verification auto-resolve

💡 Result:

  • How auto-resolve works (Foundry + Etherscan): when you run forge verify-contract with --verifier etherscan and provide --chain <name|chainId>, Foundry uses the chain identifier to derive the correct Etherscan(-family) API URL automatically, as long as that chain is known/supported. (learnblockchain.cn)
  • “Supported chains”: in practice, most chains that have an Etherscan explorer are supported just by using --chain. (docs.etherscan.io)
  • If the chain is too new / not natively supported: pass an explicit verifier URL, typically using Etherscan V2 with a chainid query param, e.g.
    --verifier-url "https://api.etherscan.io/v2/api?chainid=<CHAIN_ID>" (docs.etherscan.io)
  • Config-based resolution: you can also configure keys per chain alias in foundry.toml under [etherscan], and Foundry resolves the final config from chain/url/key. (docs.etherscan.io)
  • Chain aliases: Foundry’s standard library maintains a registry of chain aliases/IDs/RPC defaults, and the lookup priority is roughly (explicit override) → foundry.toml → env vars → defaults. (etherscan.io)

Citations:


🌐 Web query:

Flare network block explorer API etherscan integration

💡 Result:

To integrate an “Etherscan-style” block-explorer API for Flare, use the Blockscout explorer instance for Flare and swap your Etherscan base URL to the Blockscout /api endpoint:

  • Flare’s Blockscout explorer: https://flare-explorer.flare.network (listed in Flare’s official network overview). [1]
  • Blockscout provides an Etherscan-compatible query API: add /api and keep the familiar module= / action= pattern. [2][3]

Example (Etherscan → Flare/Blockscout)

Etherscan

https://api.etherscan.io/api?module=account&action=txlist&address=0x...&apikey=KEY

Flare (Blockscout)

https://flare-explorer.flare.network/api?module=account&action=txlist&address=0x...&apikey=KEY

This is the standard Blockscout migration approach: host changes; most parameters stay the same. [3]

Where to find endpoint docs

Blockscout instances typically expose live API documentation at /api-docs (and the general module list is in Blockscout’s RPC API docs). [2][3]

If instead you meant Flare’s non-Etherscan “Flare API / API Portal” (managed at api.flare.io with token-based auth), that’s a separate product from the explorer API. [4]

Sources: [1] [2] [3] [4]


Flare requires explicit chain and url in its etherscan config.

Foundry auto-resolves chain details for standard networks like Arbitrum, Base, and Polygon using only the key field. However, Flare is not a standard Foundry-recognized chain and requires explicit chain (chain ID) and url (block explorer API endpoint) fields. Without these, contract verification on Flare will fail.

Proposed fix
-flare = { key = "${CI_DEPLOY_FLARE_ETHERSCAN_API_KEY}" }
+flare = { key = "${CI_DEPLOY_FLARE_ETHERSCAN_API_KEY}", chain = 14, url = "https://flare-explorer.flare.network/api" }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
[etherscan]
arbitrum = { key = "${CI_DEPLOY_ARBITRUM_ETHERSCAN_API_KEY}" }
base = { key = "${CI_DEPLOY_BASE_ETHERSCAN_API_KEY}" }
flare = { key = "${CI_DEPLOY_FLARE_ETHERSCAN_API_KEY}" }
polygon = { key = "${CI_DEPLOY_POLYGON_ETHERSCAN_API_KEY}" }
[etherscan]
arbitrum = { key = "${CI_DEPLOY_ARBITRUM_ETHERSCAN_API_KEY}" }
base = { key = "${CI_DEPLOY_BASE_ETHERSCAN_API_KEY}" }
flare = { key = "${CI_DEPLOY_FLARE_ETHERSCAN_API_KEY}", chain = 14, url = "https://flare-explorer.flare.network/api" }
polygon = { key = "${CI_DEPLOY_POLYGON_ETHERSCAN_API_KEY}" }
🤖 Prompt for AI Agents
In `@foundry.toml` around lines 26 - 30, The Flare entry in the [etherscan] table
currently only supplies a key; update the 'flare' object in foundry.toml to
include explicit chain and url fields (add chain = <Flare chain ID> and url =
"<Flare block explorer API endpoint>") so Foundry can verify contracts on Flare;
locate the 'flare' entry and set chain to the Flare network's numeric chain ID
(e.g., 14) and url to the official Etherscan-compatible API endpoint for Flare's
explorer.

1 change: 1 addition & 0 deletions lib/rain.deploy
Submodule rain.deploy added at e419a4
17 changes: 12 additions & 5 deletions script/Deploy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ pragma solidity =0.8.25;

import {Script} from "forge-std/Script.sol";
import {MetaBoard} from "src/concrete/MetaBoard.sol";
import {LibRainDeploy} from "rain.deploy/lib/LibRainDeploy.sol";
import {LibMetaBoardDeploy} from "src/lib/deploy/LibMetaBoardDeploy.sol";

/// @title Deploy
/// @notice A script that deploys all contracts. This is intended to be run on
Expand All @@ -12,10 +14,15 @@ contract Deploy is Script {
function run() external {
uint256 deployerPrivateKey = vm.envUint("DEPLOYMENT_KEY");

vm.startBroadcast(deployerPrivateKey);

new MetaBoard();

vm.stopBroadcast();
LibRainDeploy.deployAndBroadcastToSupportedNetworks(
vm,
LibRainDeploy.supportedNetworks(),
deployerPrivateKey,
type(MetaBoard).creationCode,
"",
LibMetaBoardDeploy.METABOARD_DEPLOYED_ADDRESS,
LibMetaBoardDeploy.METABOARD_DEPLOYED_CODEHASH,
new address[](0)
);
}
}
22 changes: 22 additions & 0 deletions src/lib/deploy/LibMetaBoardDeploy.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// SPDX-License-Identifier: LicenseRef-DCL-1.0
// SPDX-FileCopyrightText: Copyright (c) 2020 Rain Open Source Software Ltd
pragma solidity ^0.8.25;

/// @title LibMetaBoardDeploy
/// A library containing the deployed address and code hash of the MetaBoard
/// contract when deployed with the rain standard zoltu deployer. This allows
/// idempotent deployments against precommitted addresses and hashes that can be
/// easily verified automatically in tests and scripts rather than relying on
/// registries or manual verification.
library LibMetaBoardDeploy {
/// The address of the `MetaBoard` contract when deployed with the rain
/// standard zoltu deployer.
address constant METABOARD_DEPLOYED_ADDRESS = address(0xfb8437AeFBB8031064E274527C5fc08e30Ac6928);

/// The code hash of the `MetaBoard` contract when deployed with the rain
/// standard zoltu deployer. This can be used to verify that the deployed
/// contract has the expected bytecode, which provides stronger guarantees
/// than just checking the address.
bytes32 constant METABOARD_DEPLOYED_CODEHASH =
bytes32(0x60e0735a3406074fd8f85adb2813d0d7c346337ea4bcc6f2ef4eb25077a4933c);
}
27 changes: 27 additions & 0 deletions test/lib/deploy/LibMetaBoardDeploy.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// SPDX-License-Identifier: LicenseRef-DCL-1.0
// SPDX-FileCopyrightText: Copyright (c) 2020 Rain Open Source Software Ltd
pragma solidity =0.8.25;

import {Test} from "forge-std/Test.sol";
import {LibRainDeploy} from "rain.deploy/lib/LibRainDeploy.sol";
import {LibMetaBoardDeploy} from "src/lib/deploy/LibMetaBoardDeploy.sol";
import {MetaBoard} from "src/concrete/MetaBoard.sol";

contract LibMetaBoardDeployTest is Test {
function testDeployAddress() external {
vm.createSelectFork(vm.envString("CI_FORK_ETH_RPC_URL"));

address deployedAddress = LibRainDeploy.deployZoltu(type(MetaBoard).creationCode);

assertEq(deployedAddress, LibMetaBoardDeploy.METABOARD_DEPLOYED_ADDRESS);
assertTrue(address(deployedAddress).code.length > 0, "Deployed address has no code");

assertEq(address(deployedAddress).codehash, LibMetaBoardDeploy.METABOARD_DEPLOYED_CODEHASH);
}

function testExpectedCodeHash() external {
MetaBoard metaBoard = new MetaBoard();

assertEq(address(metaBoard).codehash, LibMetaBoardDeploy.METABOARD_DEPLOYED_CODEHASH);
}
}