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
43 changes: 28 additions & 15 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,20 +1,33 @@
### FOR TESTING (USE ARB SEPOLIA AND BASE SEPOLIA, DO NOT CHANGE)
ARBITRUM_SEPOLIA_RPC=https://arb-sepolia.g.alchemy.com/v2/ALCHEMY_KEY
BASE_SEPOLIA_RPC=https://base-sepolia.g.alchemy.com/v2/ALCHEMY_KEY
# Copy to .env and fill in your secrets.
# Network configuration (RPC URL, chain ID, OSx addresses) is managed by
# just-foundry:
# just switch <network> — activate a network (e.g. sepolia, mainnet, ...)
# just env — show resolved values and their sources

### `make test-e2e-fork` (mainnet forks). Leave empty to skip the fork suite.
### Note the fork suite falls back to RPC_URL below when MAINNET_RPC_URL is unset,
### so setting RPC_URL alone is enough to make `forge test` reach the network.
MAINNET_RPC_URL=
BASE_RPC_URL=
# Required for deployment (script/CreateRepo.sol and script/testnet/Test_Deploy.s.sol)
# DEPLOYER_KEY=0x

### Deployment
RPC_URL=
ETHERSCAN_API_KEY=
PRIVATE_KEY=
# Required for contract verification (when VERIFIER=etherscan)
# ETHERSCAN_API_KEY=

PLUGIN_ENS_SUBDOMAIN= # Optional (a random one is used if empty)
PLUGIN_REPO_MAINTAINER_ADDRESS=
# Optional: override the RPC endpoint of the active network
# RPC_URL=

# Required for the fork suite (`just test-fork`). Both endpoints must be set —
# the suite forks two chains in the same process. Leave empty to skip.
# MAINNET_RPC_URL=
# BASE_RPC_URL=

# Required for the testnet deploy script (script/testnet/Test_Deploy.s.sol)
# ARBITRUM_SEPOLIA_RPC=https://arb-sepolia.g.alchemy.com/v2/ALCHEMY_KEY
# BASE_SEPOLIA_RPC=https://base-sepolia.g.alchemy.com/v2/ALCHEMY_KEY

# Plugin repo publication (used by `just deploy` / `just predeploy`).
# The maintainer defaults to MANAGEMENT_DAO_ADDRESS from the active
# just-foundry network config; override it here for a non-standard maintainer.
# MANAGEMENT_DAO_ADDRESS=
RELEASE_METADATA_URI=
BUILD_METADATA_URI=
PLUGIN_REPO_FACTORY_ADDRESS=

# Optional: a random subdomain is used if empty
# PLUGIN_ENS_SUBDOMAIN=
9 changes: 8 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,12 @@ docs/

# Dotenv file
.env
# just-foundry per-network overrides
.env.*

broadcast/
# just-foundry logs & coverage
logs/
report/
lcov.info*

broadcast/
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,6 @@
[submodule "lib/ens-contracts"]
path = lib/ens-contracts
url = https://github.com/ensdomains/ens-contracts
[submodule "lib/just-foundry"]
path = lib/just-foundry
url = https://github.com/aragon/just-foundry.git
21 changes: 21 additions & 0 deletions .vars.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Secrets managed by vars (https://github.com/vars-cli/vars)
# Values are age-encrypted in your personal store — only key names are committed here.
# Usage:
# vars set DEPLOYER_KEY # store a value
# eval "$(vars resolve)" # load into current shell
# just deploy # justfile recipes call vars resolve automatically

keys:
- DEPLOYER_KEY
- ETHERSCAN_API_KEY
- RPC_URL # optional override: the public RPC from the network file
# - REFUND_ADDRESS # optional: burner wallet refund target

# Profiles are keyed by network name — just switch <network> activates the matching profile.
# Store scoped values with: vars set <scope>/DEPLOYER_KEY

profiles:
sepolia:
DEPLOYER_KEY: dev/DEPLOYER_KEY
zksync-sepolia:
DEPLOYER_KEY: dev/DEPLOYER_KEY
37 changes: 0 additions & 37 deletions Makefile

This file was deleted.

39 changes: 28 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,38 +66,55 @@ chain. See [Same Chain Delivery](./specs/SPEC.md#same-chain-delivery).

## Usage

Requires [Foundry](https://book.getfoundry.sh/getting-started/installation).
Requires [Foundry](https://book.getfoundry.sh/getting-started/installation) and
[just](https://github.com/casey/just). Task running and per-network env are
handled by [just-foundry](https://github.com/aragon/just-foundry), vendored as a
submodule under `lib/just-foundry`.

```shell
git submodule update --init
just init sepolia # or mainnet, base, ... — activates the network config
just help # list every recipe
```

```shell
forge build
forge fmt
make test # the whole suite
make test-e2e # the end-to-end suites
make test-e2e-fork # end-to-end against real CCIP routers; requires RPC endpoints
just test # the whole suite
just test-e2e # the end-to-end suites
just test-fork # end-to-end against real CCIP routers; needs RPC endpoints
```

The fork tests are not excluded by the first two targets — they skip themselves
The fork tests are not excluded by the first two targets: they skip themselves
unless `MAINNET_RPC_URL` (or `RPC_URL`) and `BASE_RPC_URL` are set, in which case
they will reach the network.
they reach the network. `just test-fork` refuses to run without both.

Unit suites live in `test/unit/`, mostly one file per function, plus a few
cross-cutting suites. The end-to-end suites carry a message the whole way through
both stacks — see [test/e2e/README.md](./test/e2e/README.md).
both stacks: see [test/e2e/README.md](./test/e2e/README.md).

## Deployment

`CrossChainController` is an OSx plugin, so it needs a `PluginRepo` before any DAO can
install it. `script/CreateRepo.sol` deploys the implementation, the setup contract and the
repo in one go.

Copy `.env.example` to `.env` and fill it in, then simulate and broadcast:
Copy `.env.example` to `.env` and fill it in, activate the target network, then
simulate and broadcast:

```shell
make predeploy # simulate
make deploy # broadcast and verify
make verify # re-verify from the last broadcast
just switch sepolia # or any other supported network
just env # verify the resolved env (RPC, factory addresses, ...)
just predeploy # simulate
just deploy # broadcast and verify
just verify # re-verify from the last broadcast
```

`DEPLOYER_KEY` and `ETHERSCAN_API_KEY` come from `.env` (or from
[vars](https://github.com/vars-cli/vars)); every other network parameter,
including `PLUGIN_REPO_FACTORY_ADDRESS`, is supplied by the active network
config under `lib/just-foundry/networks/`.

Installing the plugin on a DAO goes through the OSx `PluginSetupProcessor`, pointing at
that repo. Installation parameters are `(executor, guardian, minFailedMessageGas)` — see
`CrossChainControllerSetup.encodeInstallationParameters`. Passing `address(0)` as the
Expand Down
35 changes: 35 additions & 0 deletions justfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
default: help

import 'lib/just-foundry/justfile'

DEPLOY_SCRIPT := "script/CreateRepo.sol:CreateRepo"

# End-to-end suites (test/e2e/*.t.sol). Fork tests are included and self-skip
# when MAINNET_RPC_URL / BASE_RPC_URL are unset.
[group('test')]
test-e2e *args:
#!/usr/bin/env bash
set -euo pipefail
source {{ JUST_LIB }} && env_load
FORGE=$(just resolve-forge) || exit 1
BUILD_PARAMS=$(just resolve-build-params) || exit 1
ETHERSCAN_API_KEY="" $FORGE test $BUILD_PARAMS -vvv \
--match-path 'test/e2e/*.t.sol' {{ args }}

# End-to-end fork suite against real CCIP routers. Requires BOTH
# MAINNET_RPC_URL and BASE_RPC_URL — the suite forks two chains in the same
# process, so a single RPC won't do. Shadows the inherited `test-fork`.
[group('test')]
test-fork *args:
#!/usr/bin/env bash
set -euo pipefail
source {{ JUST_LIB }} && env_load
if [ -z "${MAINNET_RPC_URL:-}" ] || [ -z "${BASE_RPC_URL:-}" ]; then
echo "MAINNET_RPC_URL and BASE_RPC_URL must be set (in .env or the environment);"
echo "the suite skips every test without them."
exit 1
fi
FORGE=$(just resolve-forge) || exit 1
BUILD_PARAMS=$(just resolve-build-params) || exit 1
$FORGE test $BUILD_PARAMS -vvv \
--match-path 'test/e2e/fork/*' {{ args }}
1 change: 1 addition & 0 deletions lib/just-foundry
Submodule just-foundry added at 3b8e5c
28 changes: 16 additions & 12 deletions script/CreateRepo.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { CrossChainController } from "@src/CrossChainController.sol";
contract CreateRepo is Script {
address deployer;
string pluginEnsSubdomain;
address pluginRepoMaintainerAddress;
address managementDao;
PluginRepoFactory pluginRepoFactory;
bytes releaseMetadataUri;
bytes buildMetadataUri;
Expand All @@ -26,14 +26,14 @@ contract CreateRepo is Script {
address pluginSetup;

modifier broadcast() {
uint256 privKey = vm.envUint("PRIVATE_KEY");
uint256 privKey = vm.envUint("DEPLOYER_KEY");
vm.startBroadcast(privKey);

deployer = vm.addr(privKey);
console.log("General");
console.log("General:");
console.log("- Deploying from: ", deployer);
console.log("- Chain ID: ", block.chainid);
console.log("");
console.log();

_;

Expand All @@ -56,8 +56,11 @@ contract CreateRepo is Script {
pluginEnsSubdomain = string.concat("cross-chain-controller", vm.toString(block.timestamp));
}

pluginRepoMaintainerAddress = vm.envAddress("PLUGIN_REPO_MAINTAINER_ADDRESS");
vm.label(pluginRepoMaintainerAddress, "Maintainer");
// The Aragon management DAO becomes the repo maintainer.
// `MANAGEMENT_DAO_ADDRESS` is supplied by the active just-foundry
// network config; override in `.env` for a non-standard maintainer.
managementDao = vm.envAddress("MANAGEMENT_DAO_ADDRESS");
vm.label(managementDao, "Maintainer");

releaseMetadataUri = vm.envOr("RELEASE_METADATA_URI", bytes(" "));
buildMetadataUri = vm.envOr("BUILD_METADATA_URI", bytes(" "));
Expand All @@ -68,13 +71,14 @@ contract CreateRepo is Script {
pluginSetup = address(new CrossChainControllerSetup(address(new CrossChainController())));

myPluginRepo = pluginRepoFactory.createPluginRepoWithFirstVersion(
pluginEnsSubdomain, pluginSetup, pluginRepoMaintainerAddress, releaseMetadataUri, buildMetadataUri
pluginEnsSubdomain, pluginSetup, managementDao, releaseMetadataUri, buildMetadataUri
);

console.log("PluginRepo: ", address(myPluginRepo));
console.log("CrossChainControllerSetup: ", address(pluginSetup));
console.log("CrossChainController impl: ", IPluginSetup(pluginSetup).implementation());
console.log("Maintainer: ", pluginRepoMaintainerAddress);
console.log("Subdomain: ", pluginEnsSubdomain);
console.log("CrossChainController plugin:");
console.log("- PluginRepo: ", address(myPluginRepo));
console.log("- PluginSetup: ", pluginSetup);
console.log("- Implementation: ", IPluginSetup(pluginSetup).implementation());
console.log("- Maintainer (Management DAO): ", managementDao);
console.log("- ENS subdomain: ", pluginEnsSubdomain);
}
}
6 changes: 3 additions & 3 deletions script/testnet/Test_Deploy.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import { ChainIdRegistry } from "@src/registry/ChainIdRegistry.sol";
/// Bridge to Arb Sepolia: https://bridge.arbitrum.io/?sourceChain=sepolia&destinationChain=arbitrum-sepolia
/// Bridge to Base Sepolia: https://testnets.superbridge.app/base-sepolia
/// STEP 2: Include these in .env:
/// PRIVATE_KEY=
/// DEPLOYER_KEY=
/// ARBITRUM_SEPOLIA_RPC=
/// BASE_SEPOLIA_RPC=
/// STEP 3: run the following command:
Expand Down Expand Up @@ -87,7 +87,7 @@ contract CounterTarget {
/// submitted.
///
/// ENV
/// PRIVATE_KEY deployer key, funded with testnet ETH on BOTH chains
/// DEPLOYER_KEY deployer key, funded with testnet ETH on BOTH chains
/// ARBITRUM_SEPOLIA_RPC Arbitrum Sepolia RPC url
/// BASE_SEPOLIA_RPC Base Sepolia RPC url
/// FUND_AMOUNT_WEI optional; native pre-funding per controller
Expand Down Expand Up @@ -135,7 +135,7 @@ contract Test_Deploy is Script {
address internal deployer;

function run() external {
deployerKey = vm.envUint("PRIVATE_KEY");
deployerKey = vm.envUint("DEPLOYER_KEY");
deployer = vm.addr(deployerKey);
uint256 fundAmount = vm.envOr("FUND_AMOUNT_WEI", uint256(0.03 ether));

Expand Down
6 changes: 3 additions & 3 deletions test/e2e/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,12 @@ makes them fail. Every suite here goes through a real OSx `DAO`, except
## Running

```bash
make test-e2e # the end-to-end suites
make test-e2e-fork # needs MAINNET_RPC_URL + BASE_RPC_URL
just test-e2e # the end-to-end suites
just test-fork # needs MAINNET_RPC_URL + BASE_RPC_URL
```

`--match-path 'test/e2e/*.t.sol'` does not exclude `fork/` — the glob crosses
directory separators — so `make test-e2e` and a plain `forge test` both select
directory separators — so `just test-e2e` and a plain `forge test` both select
the fork suite. It `vm.skip`s every test when the RPC endpoints are absent, which
is why CI is unaffected; with endpoints configured it will reach the network.
Note it falls back to `RPC_URL` when `MAINNET_RPC_URL` is unset.
Expand Down