feat: add Otterscan block explorer to dev environment (#109)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
openhands 2026-02-13 14:55:07 +00:00
parent 61b7ee435f
commit 37b1002dae
5 changed files with 38 additions and 5 deletions

View file

@ -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`. - **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. - **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`. - **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 :<port>` 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 :<port>` 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. - **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 ### Environments

View file

@ -2,7 +2,7 @@
set -euo pipefail set -euo pipefail
MNEMONIC_FILE=/workspace/onchain/.secret.local 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 if [[ -f "$MNEMONIC_FILE" ]]; then
MNEMONIC="$(tr -d '\n\r' <"$MNEMONIC_FILE")" MNEMONIC="$(tr -d '\n\r' <"$MNEMONIC_FILE")"

View file

@ -213,6 +213,24 @@ services:
retries: 3 retries: 3
start_period: 2s 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: volumes:
postgres-data: postgres-data:
ponder_node_modules: ponder_node_modules:

View file

@ -191,8 +191,8 @@ start_stack() {
wait_for_healthy "$(container_name ponder)" "$PONDER_TIMEOUT" || exit 1 wait_for_healthy "$(container_name ponder)" "$PONDER_TIMEOUT" || exit 1
# Phase 4: Start frontend services (depend on ponder healthy) # Phase 4: Start frontend services (depend on ponder healthy)
echo " Starting webapp, landing, txn-bot..." echo " Starting webapp, landing, txn-bot, otterscan..."
${COMPOSE_CMD} up -d webapp landing txn-bot >/dev/null 2>&1 ${COMPOSE_CMD} up -d webapp landing txn-bot otterscan >/dev/null 2>&1
wait_for_healthy "$(container_name webapp)" "$WEBAPP_TIMEOUT" || exit 1 wait_for_healthy "$(container_name webapp)" "$WEBAPP_TIMEOUT" || exit 1
@ -219,6 +219,7 @@ start_stack() {
echo "" echo ""
echo "[ok] Stack started in ${total_time}s" echo "[ok] Stack started in ${total_time}s"
echo " Web App: http://localhost:8081/app/" echo " Web App: http://localhost:8081/app/"
echo " Explorer: http://localhost:5100"
echo " RPC Proxy: http://localhost:8081/api/rpc" echo " RPC Proxy: http://localhost:8081/api/rpc"
echo " GraphQL: http://localhost:8081/api/graphql" echo " GraphQL: http://localhost:8081/api/graphql"
} }
@ -241,7 +242,7 @@ stop_stack() {
check_health() { check_health() {
echo "Checking 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 for service in "${services[@]}"; do
local container local container
container=$(${RUNTIME_CMD} ps --all \ container=$(${RUNTIME_CMD} ps --all \

View file

@ -110,6 +110,13 @@
</template> </template>
</div> </div>
</FCard> </FCard>
<FCard title="Block Explorer">
<div class="cheats-form">
<p class="cheats-hint">Browse transactions, blocks, and addresses on the local Anvil fork using Otterscan.</p>
<a href="http://localhost:5100" target="_blank" rel="noopener noreferrer" class="cheats-link">Open Otterscan</a>
</div>
</FCard>
</div> </div>
</div> </div>
</template> </template>
@ -1121,6 +1128,13 @@ async function forceRecenter() {
color: #FFB347 color: #FFB347
font-size: 14px font-size: 14px
.cheats-link
color: #60A5FA
text-decoration: none
font-weight: 500
&:hover
text-decoration: underline
code code
font-family: "JetBrains Mono", monospace font-family: "JetBrains Mono", monospace
</style> </style>