#!/usr/bin/env bash set -euo pipefail root="${EVERY_CHANNEL_OP_STACK_ROOT:?set EVERY_CHANNEL_OP_STACK_ROOT}" private_key_file="${EVERY_CHANNEL_OP_STACK_PRIVATE_KEY_FILE:?set EVERY_CHANNEL_OP_STACK_PRIVATE_KEY_FILE}" l1_rpc_url="${EVERY_CHANNEL_OP_STACK_L1_RPC_URL:-https://ethereum-sepolia-rpc.publicnode.com}" l1_beacon_url="${EVERY_CHANNEL_OP_STACK_L1_BEACON_URL:-https://ethereum-sepolia-beacon-api.publicnode.com}" chain_id="${EVERY_CHANNEL_OP_STACK_CHAIN_ID:-245245}" p2p_advertise_ip="${EVERY_CHANNEL_OP_STACK_P2P_ADVERTISE_IP:-127.0.0.1}" l2_rpc_url="${EVERY_CHANNEL_OP_STACK_L2_RPC_URL:-http://127.0.0.1:28545}" rollup_rpc_url="${EVERY_CHANNEL_OP_STACK_ROLLUP_RPC_URL:-http://127.0.0.1:28547}" op_deployer_bin="${EVERY_CHANNEL_OP_DEPLOYER_BIN:-${root}/bin/op-deployer}" download_script="${EVERY_CHANNEL_OP_DEPLOYER_DOWNLOAD_SCRIPT:-}" download_tag="${EVERY_CHANNEL_OP_DEPLOYER_TAG:-op-deployer/v0.6.0-rc.3}" skip_apply="${EVERY_CHANNEL_OP_STACK_SKIP_APPLY:-false}" challenger_prestate_file="${EVERY_CHANNEL_OP_STACK_CHALLENGER_PRESTATE_FILE:-}" deployer_dir="${root}/deployer" sequencer_dir="${root}/sequencer" batcher_dir="${root}/batcher" proposer_dir="${root}/proposer" challenger_dir="${root}/challenger" dispute_mon_dir="${root}/dispute-mon" challenger_prestate_name="" log() { printf '[op-stack] %s\n' "$*" >&2 } require_cmd() { command -v "$1" >/dev/null 2>&1 || { log "missing required command: $1" exit 2 } } trimmed_file_contents() { tr -d '\r\n' <"$1" } normalize_rollup_config() { local path="$1" python - "$path" <<'PY' import json import sys from pathlib import Path path = Path(sys.argv[1]) data = json.loads(path.read_text()) system_config = data.setdefault("genesis", {}).setdefault("system_config", {}) system_config.pop("daFootprintGasScalar", None) chain_op_config = data.get("chain_op_config", {}) denominator = chain_op_config.get("eip1559DenominatorCanyon") or chain_op_config.get("eip1559Denominator") elasticity = chain_op_config.get("eip1559Elasticity") if ( isinstance(denominator, int) and isinstance(elasticity, int) and system_config.get("eip1559Params") in (None, "0x", "0x0000000000000000") ): system_config["eip1559Params"] = f"0x{denominator:08x}{elasticity:08x}" path.write_text(json.dumps(data, indent=2, sort_keys=False) + "\n") PY } set_toml_value() { local key="$1" local value="$2" local file="$3" python - "$key" "$value" "$file" <<'PY' import sys from pathlib import Path key, value, path = sys.argv[1:] text = Path(path).read_text() needle = f"{key} = " out = [] replaced = False for line in text.splitlines(): stripped = line.lstrip() if stripped.startswith(needle): indent = line[:len(line) - len(stripped)] out.append(f'{indent}{key} = "{value}"') replaced = True else: out.append(line) if not replaced: raise SystemExit(f"missing key {key} in {path}") Path(path).write_text("\n".join(out) + "\n") PY } mkdir -p "${root}/bin" "$deployer_dir" "$sequencer_dir" "$batcher_dir" "$proposer_dir" \ "$challenger_dir" "$challenger_dir/data" "$dispute_mon_dir" require_cmd cast require_cmd curl require_cmd jq require_cmd openssl if [[ ! -x "$op_deployer_bin" ]]; then if [[ -z "$download_script" ]]; then log "op-deployer missing and no download script configured" exit 2 fi log "downloading op-deployer via ${download_script}" EVERY_CHANNEL_OP_DEPLOYER_TAG="$download_tag" \ EVERY_CHANNEL_OP_DEPLOYER_OUT="$op_deployer_bin" \ "$download_script" fi private_key="$(trimmed_file_contents "$private_key_file")" private_key="${private_key#0x}" if [[ ${#private_key} -ne 64 ]]; then log "private key file must contain exactly 32 bytes of hex" exit 2 fi operator_address="$(cast wallet address --private-key "0x${private_key}")" cat > "${deployer_dir}/.env" </dev/null 2>&1 then "$op_deployer_bin" apply \ --workdir "${deployer_dir}/.deployer" \ --l1-rpc-url "${l1_rpc_url}" \ --private-key "${private_key}" fi fi if [[ ! -f "${state_json}" ]] || ! jq -e \ '.appliedIntent != null and .opChainDeployments != null' \ <"${state_json}" >/dev/null 2>&1 then log "state.json missing or unapplied; bootstrap did not complete" exit 1 fi "$op_deployer_bin" inspect genesis --workdir "${deployer_dir}/.deployer" "${chain_id_hex}" >"${sequencer_dir}/genesis.json" "$op_deployer_bin" inspect rollup --workdir "${deployer_dir}/.deployer" "${chain_id_hex}" >"${sequencer_dir}/rollup.json" normalize_rollup_config "${sequencer_dir}/rollup.json" openssl rand -hex 32 >"${sequencer_dir}/jwt.txt" chmod 0600 "${sequencer_dir}/jwt.txt" system_config_proxy="$(jq -r '.opChainDeployments[0].systemConfigProxyAddress // .opChainDeployments[0].SystemConfigProxy // empty' <"${state_json}")" dispute_game_factory="$(jq -r '.opChainDeployments[0].disputeGameFactoryProxyAddress // .opChainDeployments[0].DisputeGameFactoryProxy // empty' <"${state_json}")" l1_standard_bridge="$(jq -r '.opChainDeployments[0].l1StandardBridgeProxyAddress // .opChainDeployments[0].L1StandardBridgeProxy // empty' <"${state_json}")" if [[ -z "${system_config_proxy}" || "${system_config_proxy}" == "null" ]]; then log "failed to extract systemConfigProxyAddress from state.json" exit 1 fi if [[ -z "${dispute_game_factory}" || "${dispute_game_factory}" == "null" ]]; then log "failed to extract disputeGameFactoryProxyAddress from state.json" exit 1 fi if [[ -n "${challenger_prestate_file}" ]]; then if [[ ! -f "${challenger_prestate_file}" ]]; then log "challenger prestate file does not exist: ${challenger_prestate_file}" exit 1 fi challenger_prestate_name="$(basename "${challenger_prestate_file}")" cp "${challenger_prestate_file}" "${challenger_dir}/${challenger_prestate_name}" chmod 0640 "${challenger_dir}/${challenger_prestate_name}" fi cat > "${sequencer_dir}/.env" < "${batcher_dir}/.env" < "${proposer_dir}/.env" < "${challenger_dir}/.env" <> "${challenger_dir}/.env" < "${dispute_mon_dir}/.env" < "${root}/deployment.json" <