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
39 changes: 38 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,25 @@ define require-chain-config
exit 1; }
endef

# Recipe-time guard for the targets that FORK the chain: refuse a non-EVM chainFamily before forge is
# handed an endpoint. Without it, `make deploy-token CHAIN=<a solana chain>` sends eth_chainId to a
# Solana node and dies on `error code -32601: Method not found` - a transport-looking failure for a
# config-level answer. The other family-sensitive targets (add-lane, snapshot-chain, sync, doctor,
# probe-chain, verify-args, detect-evm-version) already answer by family in their own script.
# $(1) = chain name, $(2) = where the operator should go instead.
# Only an EXPLICIT non-evm declaration fires: chain-family.sh reads an absent or unparseable
# declaration as `evm`, so a broken config is still reported by the target that owns it.
define require-evm-chain
@fam="$$(bash script/config/chain-family.sh "$(1)")"; \
test "$$fam" = "evm" || { \
echo "$@: $(1) is chainFamily '$$fam' - this target is EVM-only."; \
echo "$(2)"; \
exit 1; }
endef

# Non-EVM chains are destination-only here (docs/config-schema.md, "Non-EVM (Solana) chain file").
NON_EVM_DEPLOY_HINT = Deploy it with that chain's own tooling, then record it: make adopt-token CHAIN=$(CHAIN) TOKEN_B58=<that chain's address> [POOL_B58=<that chain's address>]

# Canonical JSON format for config/chains/*.json: `jq --indent 2 -S .` (2-space indent, sorted keys,
# trailing newline — jq always emits one). The committed files use this exact style, and every target
# that writes a config re-canonicalizes it as its last step, so a no-drift `make sync` produces ZERO
Expand All @@ -90,11 +109,24 @@ help: ## List the available targets
@echo "Chain-config tooling golden path (raw commands: README.md > Chain config tooling):"
@awk 'BEGIN {FS = ":.*## "} /^[a-z][a-z-]*:.*## / {printf " %-16s %s\n", $$1, $$2}' $(MAKEFILE_LIST)

tools: ## Check the required tools are installed (forge, curl, jq)
# The chain this invocation targets, if any: CHAIN for the single-chain targets, LOCAL for the lane
# targets. Recursive (`=`) so it expands when the recipe runs.
TOOLS_CHAIN = $(or $(CHAIN),$(LOCAL))

tools: ## Check the required tools are installed (forge, curl, jq; CHAIN= adds that chain's family-specific check)
@command -v forge > /dev/null || { echo "missing: forge - install Foundry: https://book.getfoundry.sh/getting-started/installation"; exit 2; }
@command -v curl > /dev/null || { echo "missing: curl - install it (usually preinstalled; else brew install curl / apt install curl)"; exit 2; }
@command -v jq > /dev/null || { echo "missing: jq - install it (e.g. brew install jq / apt install jq)"; exit 2; }
@echo "tools: forge, curl and jq are all present"
@# forge/curl/jq are the baseline for every family - the config tooling itself is forge-based and
@# still runs against a non-EVM chain (sync SKIPs, doctor checks the schema). Anything
@# family-specific hangs off the chain argument, so an EVM-only user never sees another family's
@# stack, and never pays for the lookup. svm needs nothing extra TODAY: this repo has no non-EVM
@# write path, and the Solana CLI the docs mention is advisory, never installed or invoked here.
@test -z "$(TOOLS_CHAIN)" || { \
fam="$$(bash script/config/chain-family.sh "$(TOOLS_CHAIN)")"; \
test "$$fam" = "evm" || \
echo "tools: $(TOOLS_CHAIN) is chainFamily '$$fam' - destination-only here, no extra toolchain required (docs/config-schema.md)"; }

discover: tools ## List the CCIP API chain catalog vs local configs, both planes (FILTER=<term> narrows; ENVIRONMENT=<testnet|mainnet> narrows the plane)
@FILTER="$(FILTER)" ENVIRONMENT="$(ENVIRONMENT)" bash script/config/sync-discover.sh
Expand Down Expand Up @@ -164,6 +196,7 @@ ifdef TOKEN_B58
FOUNDRY_PROFILE=sync PROJECT_GROUP="$(GROUP)" forge script script/config/AdoptToken.s.sol $(call evm-version-flag,$(CHAIN)) --sig "runNonEvm(string,string,string)" "$(CHAIN)" "$(TOKEN_B58)" "$(POOL_B58)"
else
$(if $(TOKEN),,$(error TOKEN is required - the externally deployed token address to adopt (or TOKEN_B58 for a non-EVM chain)))
$(call require-evm-chain,$(CHAIN),TOKEN= is an EVM address - pass this family's own form: make adopt-token CHAIN=$(CHAIN) TOKEN_B58=<that chain's address> [POOL_B58=<that chain's address>])
@FOUNDRY_PROFILE=sync PROJECT_GROUP="$(GROUP)" bash script/config/forge-fork.sh "$(CHAIN)" -- forge script script/config/AdoptToken.s.sol $(call evm-version-flag,$(CHAIN)) --sig "run(string,address,address)" "$(CHAIN)" "$(TOKEN)" "$(or $(TOKEN_POOL),0x0000000000000000000000000000000000000000)"
endif

Expand Down Expand Up @@ -290,21 +323,25 @@ endef
deploy-token: tools ## Deploy a cross-chain token on <CHAIN> (CHAIN= + KEYSTORE_NAME= required; token params via env TOKEN_NAME= TOKEN_SYMBOL= ...; VERIFY=1 source-verifies; FORCE_REDEPLOY=1 overrides the redeploy guard; GROUP= scopes to a token group)
$(if $(CHAIN),,$(error CHAIN is required: make deploy-token CHAIN=<name> (token params via env: TOKEN_NAME= TOKEN_SYMBOL= TOKEN_DECIMALS= ...)))
$(require-chain-config)
$(call require-evm-chain,$(CHAIN),$(NON_EVM_DEPLOY_HINT))
$(call run-deploy,script/deploy/DeployToken.s.sol)

deploy-pool: tools ## Deploy a BurnMint token pool on <CHAIN> (CHAIN= + KEYSTORE_NAME= required; token resolved from the registry, else TOKEN=; opt POOL_HOOKS=; VERIFY=1; FORCE_REDEPLOY=1; GROUP= scopes to a token group)
$(if $(CHAIN),,$(error CHAIN is required: make deploy-pool CHAIN=<name>))
$(require-chain-config)
$(call require-evm-chain,$(CHAIN),$(NON_EVM_DEPLOY_HINT))
$(call run-deploy,script/deploy/DeployBurnMintTokenPool.s.sol)

deploy-lockbox: tools ## Deploy an ERC20 LockBox on <CHAIN> for the LockRelease liquidity model (CHAIN= + KEYSTORE_NAME= required; token from the registry, else TOKEN=; opt AUTHORIZED_CALLERS=; VERIFY=1; GROUP= scopes to a token group)
$(if $(CHAIN),,$(error CHAIN is required: make deploy-lockbox CHAIN=<name>))
$(require-chain-config)
$(call require-evm-chain,$(CHAIN),$(NON_EVM_DEPLOY_HINT))
$(call run-deploy,script/deploy/DeployERC20LockBox.s.sol)

deploy-lockrelease-pool: tools ## Deploy a LockRelease token pool on <CHAIN> (CHAIN= + KEYSTORE_NAME= required; token + lock box from the registry, else TOKEN= LOCK_BOX=; opt POOL_HOOKS=; VERIFY=1; FORCE_REDEPLOY=1; GROUP= scopes to a token group)
$(if $(CHAIN),,$(error CHAIN is required: make deploy-lockrelease-pool CHAIN=<name>))
$(require-chain-config)
$(call require-evm-chain,$(CHAIN),$(NON_EVM_DEPLOY_HINT))
$(call run-deploy,script/deploy/DeployLockReleaseTokenPool.s.sol)

deploy-new-chain: tools ## Guided deploy: add-chain -> deploy-token -> deploy-pool -> doctor (CHAIN= SELECTOR= + KEYSTORE_NAME= required; token params + VERIFY= via env). Register, set-pool, and wire-lane come next - see docs/workflows/greenfield-deploy.md; a green run means deployed, not yet cross-chain-live
Expand Down
7 changes: 7 additions & 0 deletions docs/config-schema.md
Original file line number Diff line number Diff line change
Expand Up @@ -728,6 +728,13 @@ source). `config/chains/solana-devnet.json` keeps the same shape but:
from the doctor's reciprocity rung), but `make add-lane LOCAL=solana-devnet ...` is refused (no lanes to
write). Its `addresses{}` subtree DOES apply, holding the Solana token/pool as **base58 strings** (see
below).
- **The EVM-only make targets refuse a non-EVM chain by family.** `deploy-token`, `deploy-pool`,
`deploy-lockbox`, `deploy-lockrelease-pool` (and `deploy-new-chain` through them) and `adopt-token
TOKEN=` name the family and point at the `adopt-token` path instead of handing forge an endpoint that
speaks no EVM JSON-RPC. The read paths are unaffected: `sync` and `detect-evm-version` SKIP,
`roles-check` SKIPs, `doctor` reports the unverifiable rungs, `probe-chain` and `verify-args` refuse
by family with their own message. `make tools CHAIN=<solana-chain>` names the family and asks for
nothing extra to install, because this repo has no non-EVM write path.
- **A non-EVM token/pool is stored as base58 and feeds `applyChainUpdates` from the store.** Adopt it with
`make adopt-token CHAIN=<solana-chain> TOKEN_B58=<base58> [POOL_B58=<base58>]` (the `runNonEvm` path),
which family-validates each value (base58 decodes to exactly 32 bytes) and writes
Expand Down
2 changes: 1 addition & 1 deletion docs/primitives/catalog.json
Original file line number Diff line number Diff line change
Expand Up @@ -1019,7 +1019,7 @@
},
{
"target": "tools",
"help": "Check the required tools are installed (forge, curl, jq)"
"help": "Check the required tools are installed (forge, curl, jq; CHAIN= adds that chain's family-specific check)"
},
{
"target": "discover",
Expand Down
18 changes: 18 additions & 0 deletions script/config/chain-family.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#!/usr/bin/env bash
# chain-family.sh <chain>: print the chain's declared chainFamily, lowercased and trimmed.
#
# Fails OPEN to `evm` when the chain is unknown, declares no family, or the file does not parse.
# Callers use this to REFUSE non-EVM work, so an unreadable file must never turn a working EVM path
# into a refusal; missing-config and schema errors belong to the targets that own them.
set -uo pipefail
cd "$(dirname "$0")/../.."

name="${1:-}"
file="config/chains/${name}.json"
if [ -z "$name" ] || [ ! -r "$file" ]; then
printf 'evm\n'
exit 0
fi

family="$(jq -r 'if (.chainFamily | type) == "string" then (.chainFamily | ascii_downcase | gsub("^\\s+|\\s+$"; "")) else empty end' "$file" 2> /dev/null)"
printf '%s\n' "${family:-evm}"
20 changes: 17 additions & 3 deletions script/config/roles-check.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
# with its group. With no chain args it checks every EVM chain whose group project store
# (project/[<group>/]<name>.json) DECLARES a roles{} block (chains without one are listed as SKIP —
# bootstrap them with `make snapshot-chain CHAIN=<name>`). With explicit chain args the run stays in
# the selected (or default) group — re-run per group to cover siblings. Chain family comes from
# the selected (or default) group — re-run per group to cover siblings — and a named non-EVM chain
# SKIPs, same as in the sweep. Chain family comes from
# config/chains; the declared roles{} lives in the project store. Classifies the forge
# output into the CI-ready exit-code contract (the contract belongs to THIS SCRIPT — GNU make remaps any
# recipe failure to exit 2, so `make roles-check` is pass/fail only; CI calls the script directly, the
Expand Down Expand Up @@ -85,8 +86,17 @@ fi
# Build the (group, chain) work list. Explicit chain args stay in the requested (or default) group; with
# no args, discover every chain that DECLARES roles{} in each scanned group.
declare -a pair_group=() pair_chain=()
skipped=0
if [ "$#" -gt 0 ]; then
for name in "$@"; do
# Same SKIP the no-args sweep has always applied: roles{} is an EVM authority surface, and a
# read path reports an absent surface as SKIP, never as drift. Naming the chain used to end
# in a forge revert classified as ROLES_DRIFT.
if [ "$(bash script/config/chain-family.sh "$name")" != "evm" ]; then
echo ">> roles-check [group: $(group_label "$requested_group")] $name: SKIP (non-EVM)"
skipped=$((skipped + 1))
continue
fi
pair_group+=("$requested_group")
pair_chain+=("$name")
done
Expand All @@ -96,7 +106,7 @@ else
name="$(basename "$f" .json)"
# Skip the gitignored zz-scratch-* files the test suites write here (fake selectors).
case "$name" in zz-scratch-*) continue ;; esac
if [ "$(jq -r '.chainFamily' "$f")" != "evm" ]; then
if [ "$(bash script/config/chain-family.sh "$name")" != "evm" ]; then
echo ">> roles-check [group: $(group_label "$g")] $name: SKIP (non-EVM)"
continue
fi
Expand All @@ -114,7 +124,11 @@ else
fi

if [ ${#pair_chain[@]} -eq 0 ]; then
echo "roles-check: CLEAN - no chain declares a roles{} block yet (bootstrap with make snapshot-chain)"
if [ "$skipped" -gt 0 ]; then
echo "roles-check: CLEAN - nothing to reconcile (every named chain SKIPped)"
else
echo "roles-check: CLEAN - no chain declares a roles{} block yet (bootstrap with make snapshot-chain)"
fi
exit 0
fi

Expand Down
Loading
Loading