From 872d4db49439408ee01bcba71a9c925261c87f95 Mon Sep 17 00:00:00 2001 From: Ignacio Date: Thu, 11 Dec 2025 19:39:16 -0300 Subject: [PATCH 01/11] Added Otterscan to docker-compose files --- playground/docker-compose.fork.yml | 11 +++++++++++ playground/docker-compose.non-interactive.yml | 12 ++++++++++++ 2 files changed, 23 insertions(+) diff --git a/playground/docker-compose.fork.yml b/playground/docker-compose.fork.yml index 17f401cfb2..fde12109a0 100644 --- a/playground/docker-compose.fork.yml +++ b/playground/docker-compose.fork.yml @@ -213,6 +213,17 @@ services: ports: - 8000:80 + otterscan: + image: otterscan/otterscan:latest + restart: always + environment: + - ERIGON_URL=http://localhost:8545 + ports: + - 8003:80 + depends_on: + chain: + condition: service_healthy + explorer: build: context: . diff --git a/playground/docker-compose.non-interactive.yml b/playground/docker-compose.non-interactive.yml index f41847875b..5e4d7f9dec 100644 --- a/playground/docker-compose.non-interactive.yml +++ b/playground/docker-compose.non-interactive.yml @@ -202,6 +202,18 @@ services: ports: - 8000:80 + # Otterscan - Local blockchain block explorer for Anvil + otterscan: + image: otterscan/otterscan:latest + restart: always + environment: + - ERIGON_URL=http://localhost:8545 + ports: + - 8003:80 + depends_on: + chain: + condition: service_healthy + explorer: build: context: . From 3c11ff248dce9d5d94ad8254bc72bc2b34d8ff0b Mon Sep 17 00:00:00 2001 From: Ignacio Date: Mon, 15 Dec 2025 19:04:48 -0300 Subject: [PATCH 02/11] Fix Dockerfile --- playground/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/playground/Dockerfile b/playground/Dockerfile index a9439ec7db..08c4e97745 100644 --- a/playground/Dockerfile +++ b/playground/Dockerfile @@ -1,6 +1,6 @@ FROM debian:bookworm AS chef WORKDIR /src/ -RUN apt-get update && apt-get install -y curl git clang mold libssl-dev pkg-config git && apt-get clean +RUN apt-get update && apt-get install -y curl git clang mold libssl-dev pkg-config git make && apt-get clean RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y ENV PATH="$PATH:/root/.cargo/bin" RUN rustup component add clippy rustfmt From 6e8acb65895d429eb14972cf40c69f869108f903 Mon Sep 17 00:00:00 2001 From: Augusto Collerone Date: Thu, 18 Dec 2025 16:33:44 -0300 Subject: [PATCH 03/11] Update Otterscan readme details --- playground/README.md | 3 ++- playground/docker-compose.fork.yml | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/playground/README.md b/playground/README.md index 44af35a9ae..379d759398 100644 --- a/playground/README.md +++ b/playground/README.md @@ -115,6 +115,7 @@ await window.ethereum.request({ | Postgres | postgres | 5432 | 5432 | N/A | Local/Fork | | Adminer | adminer | 8082 | 8080 | N/A | Local/Fork | | Grafana | grafana | 3000 | 3000 | N/A | Local/Fork | +| Otterscan | otterscan | 8003 | 80 | N/A | Local/Fork | **NOTE**: Currently only **FORK** mode is supported. @@ -137,7 +138,7 @@ In this mode, the stack will spin up: - Postgres (with migrations) - Adminer - RPC (forked from `reth` or `erigon` node) -- Otterscan (*not yet implemented*) +- Otterscan - Orderbook - Autopilot - Driver diff --git a/playground/docker-compose.fork.yml b/playground/docker-compose.fork.yml index fde12109a0..5137bc7e29 100644 --- a/playground/docker-compose.fork.yml +++ b/playground/docker-compose.fork.yml @@ -217,7 +217,7 @@ services: image: otterscan/otterscan:latest restart: always environment: - - ERIGON_URL=http://localhost:8545 + - ERIGON_URL=http://127.0.0.1:8545 ports: - 8003:80 depends_on: From 8519ffac13b8e0aed4d8191e5b7d15b6ddc0ce81 Mon Sep 17 00:00:00 2001 From: Augusto Collerone Date: Thu, 18 Dec 2025 18:54:06 -0300 Subject: [PATCH 04/11] Sourcify included in docker-compose --- playground/docker-compose.fork.yml | 49 +++++++++++++++++++ playground/docker-compose.non-interactive.yml | 49 +++++++++++++++++++ playground/sourcify-chains.json | 12 +++++ 3 files changed, 110 insertions(+) create mode 100644 playground/sourcify-chains.json diff --git a/playground/docker-compose.fork.yml b/playground/docker-compose.fork.yml index 5137bc7e29..841235d52f 100644 --- a/playground/docker-compose.fork.yml +++ b/playground/docker-compose.fork.yml @@ -213,11 +213,59 @@ services: ports: - 8000:80 + # Sourcify - Contract verification service + sourcify-db: + image: postgres:15-alpine + restart: always + environment: + - POSTGRES_USER=sourcify + - POSTGRES_PASSWORD=sourcify + - POSTGRES_DB=sourcify + volumes: + - sourcify-postgres:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U sourcify"] + interval: 5s + timeout: 5s + retries: 5 + + sourcify-migrations: + build: + context: . + dockerfile: Dockerfile.sourcify-migrations + restart: "no" + depends_on: + sourcify-db: + condition: service_healthy + environment: + - DATABASE_URL=postgres://sourcify:sourcify@sourcify-db:5432/sourcify?sslmode=disable + + sourcify: + image: ghcr.io/ethereum/sourcify/server:latest + restart: always + depends_on: + sourcify-db: + condition: service_healthy + sourcify-migrations: + condition: service_completed_successfully + environment: + - SOURCIFY_POSTGRES_HOST=sourcify-db + - SOURCIFY_POSTGRES_PORT=5432 + - SOURCIFY_POSTGRES_USER=sourcify + - SOURCIFY_POSTGRES_PASSWORD=sourcify + - SOURCIFY_POSTGRES_DB=sourcify + - NODE_ENV=development + volumes: + - ./sourcify-chains.json:/home/app/services/server/dist/sourcify-chains.json + ports: + - 5555:5555 + otterscan: image: otterscan/otterscan:latest restart: always environment: - ERIGON_URL=http://127.0.0.1:8545 + - OTTERSCAN_CONFIG={"erigonURL":"http://127.0.0.1:8545","sourcify":{"sources":{"Sourcify":{"url":"https://repo.sourcify.dev","backendFormat":"RepositoryV1"},"Local Sourcify":{"url":"http://localhost:5555/repository","backendFormat":"RepositoryV2"}}}} ports: - 8003:80 depends_on: @@ -264,3 +312,4 @@ services: volumes: postgres: + sourcify-postgres: diff --git a/playground/docker-compose.non-interactive.yml b/playground/docker-compose.non-interactive.yml index 5e4d7f9dec..081e802f27 100644 --- a/playground/docker-compose.non-interactive.yml +++ b/playground/docker-compose.non-interactive.yml @@ -202,12 +202,60 @@ services: ports: - 8000:80 + # Sourcify - Contract verification service + sourcify-db: + image: postgres:15-alpine + restart: always + environment: + - POSTGRES_USER=sourcify + - POSTGRES_PASSWORD=sourcify + - POSTGRES_DB=sourcify + volumes: + - sourcify-postgres:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U sourcify"] + interval: 5s + timeout: 5s + retries: 5 + + sourcify-migrations: + build: + context: . + dockerfile: Dockerfile.sourcify-migrations + restart: "no" + depends_on: + sourcify-db: + condition: service_healthy + environment: + - DATABASE_URL=postgres://sourcify:sourcify@sourcify-db:5432/sourcify?sslmode=disable + + sourcify: + image: ghcr.io/ethereum/sourcify/server:latest + restart: always + depends_on: + sourcify-db: + condition: service_healthy + sourcify-migrations: + condition: service_completed_successfully + environment: + - SOURCIFY_POSTGRES_HOST=sourcify-db + - SOURCIFY_POSTGRES_PORT=5432 + - SOURCIFY_POSTGRES_USER=sourcify + - SOURCIFY_POSTGRES_PASSWORD=sourcify + - SOURCIFY_POSTGRES_DB=sourcify + - NODE_ENV=development + volumes: + - ./sourcify-chains.json:/home/app/services/server/dist/sourcify-chains.json + ports: + - 5555:5555 + # Otterscan - Local blockchain block explorer for Anvil otterscan: image: otterscan/otterscan:latest restart: always environment: - ERIGON_URL=http://localhost:8545 + - OTTERSCAN_CONFIG={"erigonURL":"http://localhost:8545","sourcify":{"sources":{"Sourcify":{"url":"https://repo.sourcify.dev","backendFormat":"RepositoryV1"},"Local Sourcify":{"url":"http://localhost:5555/repository","backendFormat":"RepositoryV2"}}}} ports: - 8003:80 depends_on: @@ -255,3 +303,4 @@ services: volumes: postgres: + sourcify-postgres: diff --git a/playground/sourcify-chains.json b/playground/sourcify-chains.json new file mode 100644 index 0000000000..b820af7586 --- /dev/null +++ b/playground/sourcify-chains.json @@ -0,0 +1,12 @@ +{ + "1": { + "sourcifyName": "Mainnet (Forked)", + "supported": true, + "rpc": ["http://chain:8545"] + }, + "31337": { + "sourcifyName": "Anvil Local", + "supported": true, + "rpc": ["http://chain:8545"] + } +} From 98fa7ec63f0b024173c1d80f76db3c33eb499bd8 Mon Sep 17 00:00:00 2001 From: Augusto Collerone Date: Thu, 18 Dec 2025 20:10:48 -0300 Subject: [PATCH 05/11] Sourcify cleanup --- playground/Dockerfile.sourcify | 31 +++++++++++++++++++ playground/docker-compose.fork.yml | 19 +++--------- playground/docker-compose.non-interactive.yml | 19 +++--------- playground/sourcify-entrypoint.sh | 22 +++++++++++++ 4 files changed, 61 insertions(+), 30 deletions(-) create mode 100644 playground/Dockerfile.sourcify create mode 100644 playground/sourcify-entrypoint.sh diff --git a/playground/Dockerfile.sourcify b/playground/Dockerfile.sourcify new file mode 100644 index 0000000000..ce280796c1 --- /dev/null +++ b/playground/Dockerfile.sourcify @@ -0,0 +1,31 @@ +FROM node:22-bookworm-slim + +# Install dependencies +RUN apt-get update && apt-get install -y git curl postgresql-client && \ + curl -fsSL -o /usr/local/bin/dbmate https://github.com/amacneil/dbmate/releases/download/v2.21.0/dbmate-linux-amd64 && \ + chmod +x /usr/local/bin/dbmate && \ + apt-get clean && rm -rf /var/lib/apt/lists/* + +# Clone Sourcify with submodules +RUN git clone --depth 1 --recurse-submodules https://github.com/ethereum/sourcify.git /sourcify + +WORKDIR /sourcify + +# Install dependencies and build +RUN npm install && npm run build:lerna + +# Prepare migrations +WORKDIR /sourcify/services/database +RUN mkdir -p /migrations && \ + cp -r database-specs/migrations/* /migrations/ 2>/dev/null || true && \ + cp -r migrations/* /migrations/ 2>/dev/null || true + +WORKDIR /sourcify/services/server + +# Copy custom entrypoint +COPY sourcify-entrypoint.sh /entrypoint.sh +RUN chmod +x /entrypoint.sh + +EXPOSE 5555 + +ENTRYPOINT ["/entrypoint.sh"] diff --git a/playground/docker-compose.fork.yml b/playground/docker-compose.fork.yml index 841235d52f..24fffb2749 100644 --- a/playground/docker-compose.fork.yml +++ b/playground/docker-compose.fork.yml @@ -229,25 +229,14 @@ services: timeout: 5s retries: 5 - sourcify-migrations: + sourcify: build: context: . - dockerfile: Dockerfile.sourcify-migrations - restart: "no" - depends_on: - sourcify-db: - condition: service_healthy - environment: - - DATABASE_URL=postgres://sourcify:sourcify@sourcify-db:5432/sourcify?sslmode=disable - - sourcify: - image: ghcr.io/ethereum/sourcify/server:latest + dockerfile: Dockerfile.sourcify restart: always depends_on: sourcify-db: condition: service_healthy - sourcify-migrations: - condition: service_completed_successfully environment: - SOURCIFY_POSTGRES_HOST=sourcify-db - SOURCIFY_POSTGRES_PORT=5432 @@ -256,7 +245,7 @@ services: - SOURCIFY_POSTGRES_DB=sourcify - NODE_ENV=development volumes: - - ./sourcify-chains.json:/home/app/services/server/dist/sourcify-chains.json + - ./sourcify-chains.json:/sourcify/services/server/dist/sourcify-chains.json ports: - 5555:5555 @@ -265,7 +254,7 @@ services: restart: always environment: - ERIGON_URL=http://127.0.0.1:8545 - - OTTERSCAN_CONFIG={"erigonURL":"http://127.0.0.1:8545","sourcify":{"sources":{"Sourcify":{"url":"https://repo.sourcify.dev","backendFormat":"RepositoryV1"},"Local Sourcify":{"url":"http://localhost:5555/repository","backendFormat":"RepositoryV2"}}}} + - OTTERSCAN_CONFIG={"erigonURL":"http://127.0.0.1:8545","sourcify":{"sources":{"Local Sourcify":{"url":"http://localhost:5555/repository","backendFormat":"RepositoryV1"},"Sourcify":{"url":"https://repo.sourcify.dev","backendFormat":"RepositoryV1"}}}} ports: - 8003:80 depends_on: diff --git a/playground/docker-compose.non-interactive.yml b/playground/docker-compose.non-interactive.yml index 081e802f27..98a9c7e5d9 100644 --- a/playground/docker-compose.non-interactive.yml +++ b/playground/docker-compose.non-interactive.yml @@ -218,25 +218,14 @@ services: timeout: 5s retries: 5 - sourcify-migrations: + sourcify: build: context: . - dockerfile: Dockerfile.sourcify-migrations - restart: "no" - depends_on: - sourcify-db: - condition: service_healthy - environment: - - DATABASE_URL=postgres://sourcify:sourcify@sourcify-db:5432/sourcify?sslmode=disable - - sourcify: - image: ghcr.io/ethereum/sourcify/server:latest + dockerfile: Dockerfile.sourcify restart: always depends_on: sourcify-db: condition: service_healthy - sourcify-migrations: - condition: service_completed_successfully environment: - SOURCIFY_POSTGRES_HOST=sourcify-db - SOURCIFY_POSTGRES_PORT=5432 @@ -245,7 +234,7 @@ services: - SOURCIFY_POSTGRES_DB=sourcify - NODE_ENV=development volumes: - - ./sourcify-chains.json:/home/app/services/server/dist/sourcify-chains.json + - ./sourcify-chains.json:/sourcify/services/server/dist/sourcify-chains.json ports: - 5555:5555 @@ -255,7 +244,7 @@ services: restart: always environment: - ERIGON_URL=http://localhost:8545 - - OTTERSCAN_CONFIG={"erigonURL":"http://localhost:8545","sourcify":{"sources":{"Sourcify":{"url":"https://repo.sourcify.dev","backendFormat":"RepositoryV1"},"Local Sourcify":{"url":"http://localhost:5555/repository","backendFormat":"RepositoryV2"}}}} + - OTTERSCAN_CONFIG={"erigonURL":"http://localhost:8545","sourcify":{"sources":{"Sourcify":{"url":"https://repo.sourcify.dev","backendFormat":"RepositoryV1"},"Local Sourcify":{"url":"http://localhost:5555/repository","backendFormat":"RepositoryV1"}}}} ports: - 8003:80 depends_on: diff --git a/playground/sourcify-entrypoint.sh b/playground/sourcify-entrypoint.sh new file mode 100644 index 0000000000..6ccada206c --- /dev/null +++ b/playground/sourcify-entrypoint.sh @@ -0,0 +1,22 @@ +#!/bin/bash +set -e + +echo "=== Sourcify Server Starting ===" + +# Wait for database to be ready +echo "Waiting for database..." +until pg_isready -h "$SOURCIFY_POSTGRES_HOST" -p "$SOURCIFY_POSTGRES_PORT" -U "$SOURCIFY_POSTGRES_USER" -d "$SOURCIFY_POSTGRES_DB" > /dev/null 2>&1; do + echo "Database not ready, waiting..." + sleep 2 +done +echo "Database is ready!" + +# Run migrations +echo "Running database migrations..." +DATABASE_URL="postgres://${SOURCIFY_POSTGRES_USER}:${SOURCIFY_POSTGRES_PASSWORD}@${SOURCIFY_POSTGRES_HOST}:${SOURCIFY_POSTGRES_PORT}/${SOURCIFY_POSTGRES_DB}?sslmode=disable" +dbmate --url "$DATABASE_URL" --migrations-dir /migrations --no-dump-schema up +echo "Migrations complete!" + +# Start the server +echo "Starting Sourcify server..." +exec node dist/server/cli.js From ff75c1ff1df28718ec5a059feed6b0d8985e3969 Mon Sep 17 00:00:00 2001 From: Augusto Collerone Date: Thu, 18 Dec 2025 20:17:46 -0300 Subject: [PATCH 06/11] Update docker-compose.fork.yml --- playground/docker-compose.fork.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/playground/docker-compose.fork.yml b/playground/docker-compose.fork.yml index 24fffb2749..e0069208ad 100644 --- a/playground/docker-compose.fork.yml +++ b/playground/docker-compose.fork.yml @@ -254,7 +254,7 @@ services: restart: always environment: - ERIGON_URL=http://127.0.0.1:8545 - - OTTERSCAN_CONFIG={"erigonURL":"http://127.0.0.1:8545","sourcify":{"sources":{"Local Sourcify":{"url":"http://localhost:5555/repository","backendFormat":"RepositoryV1"},"Sourcify":{"url":"https://repo.sourcify.dev","backendFormat":"RepositoryV1"}}}} + - OTTERSCAN_CONFIG={"erigonURL":"http://127.0.0.1:8545","sourcify":{"sources":{"Sourcify":{"url":"https://repo.sourcify.dev","backendFormat":"RepositoryV1"},"Local Sourcify":{"url":"http://localhost:5555/repository","backendFormat":"RepositoryV1"}}}} ports: - 8003:80 depends_on: From f7e8ff5131944c83dc200d83123fd666c25eacd9 Mon Sep 17 00:00:00 2001 From: Augusto Collerone Date: Fri, 19 Dec 2025 18:47:31 -0300 Subject: [PATCH 07/11] Sourcify env variable config --- playground/.env.example | 5 ++ playground/Dockerfile.otterscan | 6 +++ playground/docker-compose.fork.yml | 6 ++- playground/docker-compose.non-interactive.yml | 8 +-- playground/otterscan-entrypoint.sh | 50 +++++++++++++++++++ 5 files changed, 70 insertions(+), 5 deletions(-) create mode 100644 playground/Dockerfile.otterscan create mode 100755 playground/otterscan-entrypoint.sh diff --git a/playground/.env.example b/playground/.env.example index deb4362cd1..bb1dae5c47 100644 --- a/playground/.env.example +++ b/playground/.env.example @@ -6,3 +6,8 @@ POSTGRES_PASSWORD=123 TOML_TRACE_ERROR=1 CHAIN=1 ETHFLOW_CONTRACTS=0x04501b9b1d52e67f6862d157e00d13419d2d6e95 + +# Otterscan Sourcify source: "cloud" (default) or "local" +# - cloud: Shows publicly verified contracts from sourcify.dev +# - local: Shows contracts verified on the local Sourcify instance +SOURCIFY_MODE=cloud diff --git a/playground/Dockerfile.otterscan b/playground/Dockerfile.otterscan new file mode 100644 index 0000000000..e850887ee8 --- /dev/null +++ b/playground/Dockerfile.otterscan @@ -0,0 +1,6 @@ +FROM otterscan/otterscan:latest + +COPY otterscan-entrypoint.sh /entrypoint.sh +RUN chmod +x /entrypoint.sh + +ENTRYPOINT ["/entrypoint.sh"] diff --git a/playground/docker-compose.fork.yml b/playground/docker-compose.fork.yml index e0069208ad..a4a3423840 100644 --- a/playground/docker-compose.fork.yml +++ b/playground/docker-compose.fork.yml @@ -250,11 +250,13 @@ services: - 5555:5555 otterscan: - image: otterscan/otterscan:latest + build: + context: . + dockerfile: Dockerfile.otterscan restart: always environment: - ERIGON_URL=http://127.0.0.1:8545 - - OTTERSCAN_CONFIG={"erigonURL":"http://127.0.0.1:8545","sourcify":{"sources":{"Sourcify":{"url":"https://repo.sourcify.dev","backendFormat":"RepositoryV1"},"Local Sourcify":{"url":"http://localhost:5555/repository","backendFormat":"RepositoryV1"}}}} + - SOURCIFY_MODE=${SOURCIFY_MODE:-cloud} ports: - 8003:80 depends_on: diff --git a/playground/docker-compose.non-interactive.yml b/playground/docker-compose.non-interactive.yml index 98a9c7e5d9..7598e577b1 100644 --- a/playground/docker-compose.non-interactive.yml +++ b/playground/docker-compose.non-interactive.yml @@ -240,11 +240,13 @@ services: # Otterscan - Local blockchain block explorer for Anvil otterscan: - image: otterscan/otterscan:latest + build: + context: . + dockerfile: Dockerfile.otterscan restart: always environment: - - ERIGON_URL=http://localhost:8545 - - OTTERSCAN_CONFIG={"erigonURL":"http://localhost:8545","sourcify":{"sources":{"Sourcify":{"url":"https://repo.sourcify.dev","backendFormat":"RepositoryV1"},"Local Sourcify":{"url":"http://localhost:5555/repository","backendFormat":"RepositoryV1"}}}} + - ERIGON_URL=http://127.0.0.1:8545 + - SOURCIFY_MODE=${SOURCIFY_MODE:-cloud} ports: - 8003:80 depends_on: diff --git a/playground/otterscan-entrypoint.sh b/playground/otterscan-entrypoint.sh new file mode 100755 index 0000000000..81726c79ab --- /dev/null +++ b/playground/otterscan-entrypoint.sh @@ -0,0 +1,50 @@ +#!/bin/sh +# Otterscan entrypoint that configures Sourcify based on SOURCIFY_MODE + +CONFIG_FILE="/usr/share/nginx/html/config.json" +ERIGON_URL="${ERIGON_URL:-http://127.0.0.1:8545}" + +echo "=== Otterscan Entrypoint ===" +echo "SOURCIFY_MODE: ${SOURCIFY_MODE}" + +case "${SOURCIFY_MODE:-cloud}" in + local) + echo "Using LOCAL Sourcify as primary source" + cat > "$CONFIG_FILE" << 'EOF' +{ + "erigonURL": "${ERIGON_URL}", + "sourcify": { + "sources": { + "Local Sourcify": { + "url": "http://localhost:5555/repository", + "backendFormat": "RepositoryV1" + } + } + } +} +EOF + ;; + cloud|*) + echo "Using CLOUD Sourcify as primary source" + cat > "$CONFIG_FILE" << 'EOF' +{ + "erigonURL": "${ERIGON_URL}", + "sourcify": { + "sources": { + "Sourcify": { + "url": "https://repo.sourcify.dev", + "backendFormat": "RepositoryV1" + } + } + } +} +EOF + ;; +esac + +echo "Config written to $CONFIG_FILE:" +cat "$CONFIG_FILE" +echo "" +echo "=== Starting nginx ===" + +exec nginx -g "daemon off;" From 0660cb86574f400d92c3f7927e66b5f5511c0f76 Mon Sep 17 00:00:00 2001 From: Augusto Collerone Date: Fri, 19 Dec 2025 18:54:09 -0300 Subject: [PATCH 08/11] Sourcify local url param --- playground/docker-compose.fork.yml | 11 ++++++++++- playground/docker-compose.non-interactive.yml | 10 +++++++++- playground/otterscan-entrypoint.sh | 3 ++- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/playground/docker-compose.fork.yml b/playground/docker-compose.fork.yml index a4a3423840..5d5869df2f 100644 --- a/playground/docker-compose.fork.yml +++ b/playground/docker-compose.fork.yml @@ -248,6 +248,12 @@ services: - ./sourcify-chains.json:/sourcify/services/server/dist/sourcify-chains.json ports: - 5555:5555 + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:5555/health"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 30s otterscan: build: @@ -257,12 +263,15 @@ services: environment: - ERIGON_URL=http://127.0.0.1:8545 - SOURCIFY_MODE=${SOURCIFY_MODE:-cloud} + - LOCAL_SOURCIFY_URL=http://sourcify:5555 ports: - 8003:80 depends_on: chain: condition: service_healthy - + sourcify: + condition: service_healthy + explorer: build: context: . diff --git a/playground/docker-compose.non-interactive.yml b/playground/docker-compose.non-interactive.yml index 7598e577b1..fbd5b15d8b 100644 --- a/playground/docker-compose.non-interactive.yml +++ b/playground/docker-compose.non-interactive.yml @@ -237,6 +237,12 @@ services: - ./sourcify-chains.json:/sourcify/services/server/dist/sourcify-chains.json ports: - 5555:5555 + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:5555/health"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 30s # Otterscan - Local blockchain block explorer for Anvil otterscan: @@ -247,11 +253,14 @@ services: environment: - ERIGON_URL=http://127.0.0.1:8545 - SOURCIFY_MODE=${SOURCIFY_MODE:-cloud} + - LOCAL_SOURCIFY_URL=http://sourcify:5555 ports: - 8003:80 depends_on: chain: condition: service_healthy + sourcify: + condition: service_healthy explorer: build: @@ -282,7 +291,6 @@ services: volumes: - ./grafana-prometheus.yml:/etc/grafana/provisioning/datasources/prometheus.yml - prometheus: image: prom/prometheus:latest container_name: prometheus diff --git a/playground/otterscan-entrypoint.sh b/playground/otterscan-entrypoint.sh index 81726c79ab..6af791a8bd 100755 --- a/playground/otterscan-entrypoint.sh +++ b/playground/otterscan-entrypoint.sh @@ -3,6 +3,7 @@ CONFIG_FILE="/usr/share/nginx/html/config.json" ERIGON_URL="${ERIGON_URL:-http://127.0.0.1:8545}" +LOCAL_SOURCIFY_URL="${LOCAL_SOURCIFY_URL:-http://localhost:5555}" echo "=== Otterscan Entrypoint ===" echo "SOURCIFY_MODE: ${SOURCIFY_MODE}" @@ -16,7 +17,7 @@ case "${SOURCIFY_MODE:-cloud}" in "sourcify": { "sources": { "Local Sourcify": { - "url": "http://localhost:5555/repository", + "url": "${LOCAL_SOURCIFY_URL}/repository", "backendFormat": "RepositoryV1" } } From f92810ac9f72277a1a032e20641ef1abf4106611 Mon Sep 17 00:00:00 2001 From: Augusto Collerone Date: Fri, 19 Dec 2025 19:01:43 -0300 Subject: [PATCH 09/11] Update README.md --- playground/README.md | 47 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/playground/README.md b/playground/README.md index 379d759398..497d625cbd 100644 --- a/playground/README.md +++ b/playground/README.md @@ -116,6 +116,8 @@ await window.ethereum.request({ | Adminer | adminer | 8082 | 8080 | N/A | Local/Fork | | Grafana | grafana | 3000 | 3000 | N/A | Local/Fork | | Otterscan | otterscan | 8003 | 80 | N/A | Local/Fork | +| Sourcify | sourcify | 5555 | 5555 | N/A | Local/Fork | +| Sourcify DB | sourcify-db | N/A | 5432 | N/A | Local/Fork | **NOTE**: Currently only **FORK** mode is supported. @@ -139,6 +141,7 @@ In this mode, the stack will spin up: - Adminer - RPC (forked from `reth` or `erigon` node) - Otterscan +- Sourcify (contract verification) - Orderbook - Autopilot - Driver @@ -151,3 +154,47 @@ In this mode, the stack will spin up: **NOT YET IMPLEMENTED** - As per fork, but with a local node (not forked from Erigon) + +## Contract Verification with Sourcify + +The playground includes a local [Sourcify](https://sourcify.dev/) instance for contract verification. Sourcify is a decentralized contract verification service that matches deployed bytecode with source code. Verified contracts display their source code in Otterscan. + +**How it works:** + +- **Cloud mode** (`SOURCIFY_MODE=cloud`): Otterscan fetches verified source code from the public Sourcify repository. This shows source code for well-known contracts (CoW Protocol, USDC, etc.) that have been publicly verified. +- **Local mode** (`SOURCIFY_MODE=local`): Otterscan fetches from your local Sourcify instance. Use this when testing contracts you deploy and verify locally. + +### Sourcify Sources Configuration + +Configure which Sourcify source Otterscan uses in your `.env` file: + +```bash +# Use public Sourcify (default) - shows publicly verified contracts +SOURCIFY_MODE=cloud + +# Use local Sourcify - shows contracts verified on your local instance +SOURCIFY_MODE=local +``` + +After changing this value, recreate the Otterscan container: + +```bash +docker compose -f docker-compose.fork.yml up -d otterscan +``` + +or + +```bash +docker compose -f docker-compose.non-interactive.yml up -d otterscan +``` + +> **Note**: A simple `docker compose restart` won't work because it doesn't re-read `.env` - you need to recreate the container. + +### Verifying Contracts + +You can verify contracts on the local Sourcify instance using: + +1. **Sourcify API**: POST to `http://localhost:5555/verify` with your contract address, chain ID, and source files +2. **Foundry**: Use `forge verify-contract` with `--verifier sourcify --verifier-url http://localhost:5555` + +After verification, view the contract source in Otterscan at `http://localhost:8003/address/`. From 18555c07f4e1ef57bed5ebce4c5bcf0a96e5afcb Mon Sep 17 00:00:00 2001 From: Augusto Collerone Date: Tue, 23 Dec 2025 17:48:51 -0300 Subject: [PATCH 10/11] Add SPA routing for explorer and explorer URL config for cowswap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add nginx-spa.conf with try_files fallback for client-side routing - Update Dockerfile.explorer to use nginx SPA config (fixes 404 on direct URL access/refresh) - Add REACT_APP_EXPLORER_URL_DEV to cowswap build command - Fix README typo and remove "not yet implemented" from explorer 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- playground/Dockerfile.cowswap | 5 ++++- playground/Dockerfile.explorer | 1 + playground/README.md | 4 ++-- playground/nginx-spa.conf | 10 ++++++++++ 4 files changed, 17 insertions(+), 3 deletions(-) create mode 100644 playground/nginx-spa.conf diff --git a/playground/Dockerfile.cowswap b/playground/Dockerfile.cowswap index 7ba495ced9..0ac26cd84d 100644 --- a/playground/Dockerfile.cowswap +++ b/playground/Dockerfile.cowswap @@ -6,6 +6,7 @@ WORKDIR /usr/src/app ARG REACT_APP_NETWORK_URL_1=https://rpc.mevblocker.io ARG REACT_APP_NETWORK_URL_5=https://ethereum-goerli.publicnode.com ARG REACT_APP_NETWORK_URL_100=https://gnosis.publicnode.com +ARG REACT_APP_EXPLORER_URL_DEV=http://localhost:8001 # Orderbook URL args ARG REACT_APP_ORDER_BOOK_URLS='{"1":"https://api.cow.fi/mainnet","100":"https://api.cow.fi/goerli","5":"https://api.cow.fi/xdai"}' @@ -34,6 +35,7 @@ ENV REACT_APP_NETWORK_URL_1=$REACT_APP_NETWORK_URL_1 ENV REACT_APP_NETWORK_URL_5=$REACT_APP_NETWORK_URL_5 ENV REACT_APP_NETWORK_URL_100=$REACT_APP_NETWORK_URL_100 ENV REACT_APP_ORDER_BOOK_URLS=$REACT_APP_ORDER_BOOK_URLS +ENV REACT_APP_EXPLORER_URL_DEV=$REACT_APP_EXPLORER_URL_DEV # Update environment variables based on "chain" and "ETH_RPC_URL" and build the frontend RUN if [ -n "$ETH_RPC_URL" ]; then \ @@ -54,7 +56,8 @@ RUN if [ -n "$ETH_RPC_URL" ]; then \ NODE_OPTIONS="--max-old-space-size=4096" NX_NO_CLOUD=true yarn build --env REACT_APP_NETWORK_URL_1=$REACT_APP_NETWORK_URL_1 \ --env REACT_APP_NETWORK_URL_5=$REACT_APP_NETWORK_URL_5 \ --env REACT_APP_NETWORK_URL_100=$REACT_APP_NETWORK_URL_100 \ - --env REACT_APP_ORDER_BOOK_URLS="$REACT_APP_ORDER_BOOK_URLS"; \ + --env REACT_APP_ORDER_BOOK_URLS="$REACT_APP_ORDER_BOOK_URLS" \ + --env REACT_APP_EXPLORER_URL_DEV="$REACT_APP_EXPLORER_URL_DEV"; \ fi # Stage 2: Copy the frontend to the nginx container diff --git a/playground/Dockerfile.explorer b/playground/Dockerfile.explorer index 34a2929007..4463502766 100644 --- a/playground/Dockerfile.explorer +++ b/playground/Dockerfile.explorer @@ -21,5 +21,6 @@ RUN APP_ID=1 yarn build:explorer # Stage 2: Copy the frontend to the nginx container FROM docker.io/nginx:1.21-alpine AS frontend COPY --from=node-build /usr/src/app/build/explorer /usr/share/nginx/html +COPY nginx-spa.conf /etc/nginx/conf.d/default.conf EXPOSE 80 CMD ["nginx", "-g", "daemon off;"] diff --git a/playground/README.md b/playground/README.md index 497d625cbd..3269971f77 100644 --- a/playground/README.md +++ b/playground/README.md @@ -42,7 +42,7 @@ Now with Rabby configured, and the services started, you can browse to http://lo > The EthFlow is not configured by default, the next section explains how to set it up. > You can follow along with watching the logs of the `autopilot`, `driver`, and `baseline` solver to see how the Protocol interacts. > If you make any changes to the files in your repo directory, services will automatically be recompiled and restarted. -> The CoW Explorer is avialable at http://localhost:8001 to see more information about transaction status +> The CoW Explorer is available at http://localhost:8001 to see more information about transaction status ### Resetting the playground @@ -147,7 +147,7 @@ In this mode, the stack will spin up: - Driver - Baseline - Cow Swap -- Cow Explorer (*not yet implemented*) +- Cow Explorer ### Local diff --git a/playground/nginx-spa.conf b/playground/nginx-spa.conf new file mode 100644 index 0000000000..2d96d1589b --- /dev/null +++ b/playground/nginx-spa.conf @@ -0,0 +1,10 @@ +server { + listen 80; + server_name localhost; + root /usr/share/nginx/html; + index index.html; + + location / { + try_files $uri $uri/ /index.html; + } +} From 1a6161ff5e4279b357eee8b6d38f1574ce377290 Mon Sep 17 00:00:00 2001 From: Augusto Collerone Date: Fri, 26 Dec 2025 17:21:40 -0300 Subject: [PATCH 11/11] feat(playground): add block explorer URL override for local development --- playground/Dockerfile.cowswap | 7 ++++++- playground/Dockerfile.explorer | 4 +++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/playground/Dockerfile.cowswap b/playground/Dockerfile.cowswap index 0ac26cd84d..838f469465 100644 --- a/playground/Dockerfile.cowswap +++ b/playground/Dockerfile.cowswap @@ -8,6 +8,9 @@ ARG REACT_APP_NETWORK_URL_5=https://ethereum-goerli.publicnode.com ARG REACT_APP_NETWORK_URL_100=https://gnosis.publicnode.com ARG REACT_APP_EXPLORER_URL_DEV=http://localhost:8001 +# Block explorer URL (Otterscan for local development) +ARG REACT_APP_BLOCK_EXPLORER_URL=http://localhost:8003 + # Orderbook URL args ARG REACT_APP_ORDER_BOOK_URLS='{"1":"https://api.cow.fi/mainnet","100":"https://api.cow.fi/goerli","5":"https://api.cow.fi/xdai"}' @@ -36,6 +39,7 @@ ENV REACT_APP_NETWORK_URL_5=$REACT_APP_NETWORK_URL_5 ENV REACT_APP_NETWORK_URL_100=$REACT_APP_NETWORK_URL_100 ENV REACT_APP_ORDER_BOOK_URLS=$REACT_APP_ORDER_BOOK_URLS ENV REACT_APP_EXPLORER_URL_DEV=$REACT_APP_EXPLORER_URL_DEV +ENV REACT_APP_BLOCK_EXPLORER_URL=$REACT_APP_BLOCK_EXPLORER_URL # Update environment variables based on "chain" and "ETH_RPC_URL" and build the frontend RUN if [ -n "$ETH_RPC_URL" ]; then \ @@ -57,7 +61,8 @@ RUN if [ -n "$ETH_RPC_URL" ]; then \ --env REACT_APP_NETWORK_URL_5=$REACT_APP_NETWORK_URL_5 \ --env REACT_APP_NETWORK_URL_100=$REACT_APP_NETWORK_URL_100 \ --env REACT_APP_ORDER_BOOK_URLS="$REACT_APP_ORDER_BOOK_URLS" \ - --env REACT_APP_EXPLORER_URL_DEV="$REACT_APP_EXPLORER_URL_DEV"; \ + --env REACT_APP_EXPLORER_URL_DEV="$REACT_APP_EXPLORER_URL_DEV" \ + --env REACT_APP_BLOCK_EXPLORER_URL="$REACT_APP_BLOCK_EXPLORER_URL"; \ fi # Stage 2: Copy the frontend to the nginx container diff --git a/playground/Dockerfile.explorer b/playground/Dockerfile.explorer index 4463502766..71107c2420 100644 --- a/playground/Dockerfile.explorer +++ b/playground/Dockerfile.explorer @@ -13,10 +13,12 @@ RUN git clone https://github.com/cowprotocol/cowswap . && \ # Install npm dependencies RUN yarn install --frozen-lockfile --no-cache +# Build environment variables ENV REACT_APP_ORDER_BOOK_URLS='{"1":"http://localhost:8080"}' +ENV REACT_APP_BLOCK_EXPLORER_URL=http://localhost:8003 # Build the frontend -RUN APP_ID=1 yarn build:explorer +RUN APP_ID=1 REACT_APP_BLOCK_EXPLORER_URL=$REACT_APP_BLOCK_EXPLORER_URL yarn build:explorer # Stage 2: Copy the frontend to the nginx container FROM docker.io/nginx:1.21-alpine AS frontend