From 37b1002dae2126df70850508dad27850732e6209 Mon Sep 17 00:00:00 2001 From: openhands Date: Fri, 13 Feb 2026 14:55:07 +0000 Subject: [PATCH] feat: add Otterscan block explorer to dev environment (#109) Co-Authored-By: Claude Opus 4.6 --- AGENTS.md | 2 +- containers/anvil-entrypoint.sh | 2 +- docker-compose.yml | 18 ++++++++++++++++++ scripts/dev.sh | 7 ++++--- web-app/src/views/CheatsView.vue | 14 ++++++++++++++ 5 files changed, 38 insertions(+), 5 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 1b79acc..8070bd6 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -30,7 +30,7 @@ Do not launch services individually — `dev.sh` enforces phased startup with he - **Stale Ponder state**: If Ponder fails with schema errors after contract changes, delete its state: `rm -rf services/ponder/.ponder/` then `./scripts/dev.sh restart --full`. - **kraiken-lib out of date**: If services fail with import errors or missing exports, rebuild: `./scripts/build-kraiken-lib.sh`. The dev script does this automatically on `start`, but manual rebuilds are needed if you change kraiken-lib while the stack is already running. - **Container not found errors**: `dev.sh` expects Docker Compose v2 container names (`harb-anvil-1`, hyphens not underscores). Verify with `docker compose version`. -- **Port conflicts**: The stack uses ports 8545 (Anvil), 5173 (webapp), 5174 (landing), 42069 (Ponder), 43069 (txnBot), 8081 (Caddy). Check with `lsof -i :` if startup fails. +- **Port conflicts**: The stack uses ports 8545 (Anvil), 5173 (webapp), 5174 (landing), 42069 (Ponder), 43069 (txnBot), 5100 (Otterscan), 8081 (Caddy). Check with `lsof -i :` if startup fails. - **npm ci failures in containers**: Named Docker volumes cache `node_modules/`. If dependencies change and installs fail, remove the volume: `docker volume rm harb_webapp_node_modules` (or similar), then restart. ### Environments diff --git a/containers/anvil-entrypoint.sh b/containers/anvil-entrypoint.sh index fe69c78..089e2ad 100755 --- a/containers/anvil-entrypoint.sh +++ b/containers/anvil-entrypoint.sh @@ -2,7 +2,7 @@ set -euo pipefail MNEMONIC_FILE=/workspace/onchain/.secret.local -ANVIL_CMD=(anvil --fork-url "${FORK_URL:-https://sepolia.base.org}" --chain-id 31337 --block-time 1 --host 0.0.0.0 --port 8545 --threads 4 --timeout 2000 --retries 2 --fork-retry-backoff 100) +ANVIL_CMD=(anvil --fork-url "${FORK_URL:-https://sepolia.base.org}" --chain-id 31337 --block-time 1 --host 0.0.0.0 --port 8545 --threads 4 --timeout 2000 --retries 2 --fork-retry-backoff 100 --steps-tracing) if [[ -f "$MNEMONIC_FILE" ]]; then MNEMONIC="$(tr -d '\n\r' <"$MNEMONIC_FILE")" diff --git a/docker-compose.yml b/docker-compose.yml index dae0b8a..76ed3c7 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -213,6 +213,24 @@ services: retries: 3 start_period: 2s + otterscan: + image: otterscan/otterscan:v2.6.0 + environment: + - ERIGON_URL=http://localhost:8545 + expose: + - "80" + ports: + - "127.0.0.1:5100:80" + restart: unless-stopped + networks: + - harb-network + logging: *default-logging + healthcheck: + test: ["CMD", "wget", "--spider", "-q", "http://127.0.0.1:80"] + interval: 5s + retries: 4 + start_period: 5s + volumes: postgres-data: ponder_node_modules: diff --git a/scripts/dev.sh b/scripts/dev.sh index fd7fbb9..72567e2 100755 --- a/scripts/dev.sh +++ b/scripts/dev.sh @@ -191,8 +191,8 @@ start_stack() { wait_for_healthy "$(container_name ponder)" "$PONDER_TIMEOUT" || exit 1 # Phase 4: Start frontend services (depend on ponder healthy) - echo " Starting webapp, landing, txn-bot..." - ${COMPOSE_CMD} up -d webapp landing txn-bot >/dev/null 2>&1 + echo " Starting webapp, landing, txn-bot, otterscan..." + ${COMPOSE_CMD} up -d webapp landing txn-bot otterscan >/dev/null 2>&1 wait_for_healthy "$(container_name webapp)" "$WEBAPP_TIMEOUT" || exit 1 @@ -219,6 +219,7 @@ start_stack() { echo "" echo "[ok] Stack started in ${total_time}s" echo " Web App: http://localhost:8081/app/" + echo " Explorer: http://localhost:5100" echo " RPC Proxy: http://localhost:8081/api/rpc" echo " GraphQL: http://localhost:8081/api/graphql" } @@ -241,7 +242,7 @@ stop_stack() { check_health() { echo "Checking health..." - local services=(anvil postgres ponder webapp landing txn-bot caddy) + local services=(anvil postgres ponder webapp landing txn-bot otterscan caddy) for service in "${services[@]}"; do local container container=$(${RUNTIME_CMD} ps --all \ diff --git a/web-app/src/views/CheatsView.vue b/web-app/src/views/CheatsView.vue index 837b6c8..3df6cbb 100644 --- a/web-app/src/views/CheatsView.vue +++ b/web-app/src/views/CheatsView.vue @@ -110,6 +110,13 @@ + + +
+

Browse transactions, blocks, and addresses on the local Anvil fork using Otterscan.

+ Open Otterscan +
+
@@ -1121,6 +1128,13 @@ async function forceRecenter() { color: #FFB347 font-size: 14px +.cheats-link + color: #60A5FA + text-decoration: none + font-weight: 500 + &:hover + text-decoration: underline + code font-family: "JetBrains Mono", monospace