diff --git a/FORK_TESTING.md b/FORK_TESTING.md
index 45c8f54..3a71a76 100644
--- a/FORK_TESTING.md
+++ b/FORK_TESTING.md
@@ -97,6 +97,41 @@ Forward any `forge test` flag through the script:
./script/run-fork-tests.sh -vvvv --match-test test_transfer_success_debitsSender
```
+## Faster: consume the published base-anvil image (skip the build)
+
+base/base's `base-anvil-package.yml` workflow builds `anvil` + `forge` against
+base/base on every merge to `main`, smoke-tests them with this suite, and pushes
+a multi-arch image to `ghcr.io/base/base-anvil` (with a `MANIFEST.json` recording
+the exact base/base, base-anvil, and base-std SHAs). You can consume that image
+instead of doing the ~30-minute local base-anvil build:
+
+```bash
+cd ~/code/base-std
+BASE_ANVIL_IMAGE=ghcr.io/base/base-anvil:main ./script/run-fork-tests.sh
+```
+
+The script pulls the image, extracts its `anvil` + `forge`, prints which base/base
+SHA they were built against (so a green run is never mistaken for "current main"),
+then runs exactly as it would with local binaries.
+
+Tags:
+
+- `:main` — built from the latest base/base `main` merge.
+- `:sha-` — pin to the binaries built from a specific base/base
+ commit (reproducible; what CI should pin to).
+- `:main--` — human-readable historical builds.
+
+Caveats:
+
+- **Linux only.** The published image ships linux binaries; no macOS (darwin)
+ build is published. On a Mac, use the local-build path above (or run this
+ script inside a linux container / dev box). The script detects a non-Linux host
+ and tells you so rather than extracting binaries that can't exec.
+- Requires `docker`. If the GHCR package is private, run `docker login ghcr.io`
+ first.
+- This is the path base-std's own fork-test CI can use to skip rebuilding
+ base-anvil on every PR.
+
## When the precompiles update — the loop
This is the main workflow. base/base's precompile crate changes; you want
diff --git a/script/run-fork-tests.sh b/script/run-fork-tests.sh
index a2c2dc4..a792f15 100755
--- a/script/run-fork-tests.sh
+++ b/script/run-fork-tests.sh
@@ -23,6 +23,13 @@
# back to debug if release is missing)
# FORGE_BIN path to the patched forge binary
# (default: `forge` next to ANVIL_BIN)
+# BASE_ANVIL_IMAGE prebuilt base-anvil package image to consume instead of a
+# local build, e.g. ghcr.io/base/base-anvil:main (or a
+# :sha- tag). When set (and ANVIL_BIN is not
+# explicitly overridden), the image's anvil + forge binaries
+# are extracted and used, skipping the ~30-min local build.
+# Linux-only: the image ships linux binaries (no darwin build
+# is published). Requires docker. See FORK_TESTING.md.
# PORT local RPC port for anvil (default: 8546)
# ACTIVATION_ADMIN address authorized to activate features
# (default: 0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc, the
@@ -44,6 +51,45 @@ REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
DEFAULT_ANVIL_RELEASE="$REPO_ROOT/../base-anvil/target/release/anvil"
DEFAULT_ANVIL_DEBUG="$REPO_ROOT/../base-anvil/target/debug/anvil"
+# Binary resolution precedence:
+# 1. Explicit ANVIL_BIN / FORGE_BIN env vars (full override).
+# 2. BASE_ANVIL_IMAGE — pull the prebuilt base-anvil package image published by
+# base/base on every merge to main and extract its anvil + forge binaries.
+# Skips the ~30-min local build. The published image ships LINUX binaries
+# only, so this path requires a Linux host (CI / Linux dev box / container);
+# on macOS, build base-anvil locally instead (see FORK_TESTING.md).
+# Tags: ghcr.io/base/base-anvil:main | :sha- | :main--
+# 3. A local ../base-anvil release (then debug) build — the default for local dev.
+if [[ -z "${ANVIL_BIN:-}" && -n "${BASE_ANVIL_IMAGE:-}" ]]; then
+ if [[ "$(uname -s)" != "Linux" ]]; then
+ echo "ERROR: BASE_ANVIL_IMAGE ($BASE_ANVIL_IMAGE) ships linux binaries that cannot" >&2
+ echo "run natively on $(uname -s). Build base-anvil locally (see FORK_TESTING.md)," >&2
+ echo "or run this script on a linux host / inside a linux container." >&2
+ exit 2
+ fi
+ command -v docker >/dev/null 2>&1 || { echo "ERROR: docker is required to use BASE_ANVIL_IMAGE." >&2; exit 2; }
+
+ img_dir="$(mktemp -d)"
+ echo "[run-fork-tests] pulling $BASE_ANVIL_IMAGE ..." >&2
+ docker pull "$BASE_ANVIL_IMAGE" >&2 \
+ || { echo "ERROR: failed to pull $BASE_ANVIL_IMAGE (private package? try 'docker login ghcr.io')." >&2; exit 2; }
+
+ cid="$(docker create "$BASE_ANVIL_IMAGE")"
+ docker cp "$cid:/usr/local/bin/anvil" "$img_dir/anvil" >/dev/null
+ docker cp "$cid:/usr/local/bin/forge" "$img_dir/forge" >/dev/null
+ docker cp "$cid:/usr/local/share/base-anvil/MANIFEST.json" "$img_dir/MANIFEST.json" >/dev/null 2>&1 || true
+ docker rm "$cid" >/dev/null
+ chmod +x "$img_dir/anvil" "$img_dir/forge"
+
+ ANVIL_BIN="$img_dir/anvil"
+ : "${FORGE_BIN:="$img_dir/forge"}"
+
+ # Surface provenance so a green run is never mistaken for "current base/base".
+ if [[ -f "$img_dir/MANIFEST.json" ]] && command -v jq >/dev/null 2>&1; then
+ echo "[run-fork-tests] image built against base/base $(jq -r '.base.sha // "?"' "$img_dir/MANIFEST.json") (base-std smoke-tested: $(jq -r '.base_std.smoke_tested // "?"' "$img_dir/MANIFEST.json"))" >&2
+ fi
+fi
+
if [[ -z "${ANVIL_BIN:-}" ]]; then
if [[ -x "$DEFAULT_ANVIL_RELEASE" ]]; then
ANVIL_BIN="$DEFAULT_ANVIL_RELEASE"