Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions playground/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -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
2 changes: 1 addition & 1 deletion playground/Dockerfile
Original file line number Diff line number Diff line change
@@ -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
Expand Down
10 changes: 9 additions & 1 deletion playground/Dockerfile.cowswap
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ 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

# 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"}'
Expand Down Expand Up @@ -34,6 +38,8 @@ 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
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 \
Expand All @@ -54,7 +60,9 @@ 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" \
--env REACT_APP_BLOCK_EXPLORER_URL="$REACT_APP_BLOCK_EXPLORER_URL"; \
fi

# Stage 2: Copy the frontend to the nginx container
Expand Down
5 changes: 4 additions & 1 deletion playground/Dockerfile.explorer
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,16 @@ 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
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;"]
6 changes: 6 additions & 0 deletions playground/Dockerfile.otterscan
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
FROM otterscan/otterscan:latest

COPY otterscan-entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh

ENTRYPOINT ["/entrypoint.sh"]
31 changes: 31 additions & 0 deletions playground/Dockerfile.sourcify
Original file line number Diff line number Diff line change
@@ -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"]
54 changes: 51 additions & 3 deletions playground/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -115,6 +115,9 @@ 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 |
| 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.

Expand All @@ -137,16 +140,61 @@ In this mode, the stack will spin up:
- Postgres (with migrations)
- Adminer
- RPC (forked from `reth` or `erigon` node)
- Otterscan (*not yet implemented*)
- Otterscan
- Sourcify (contract verification)
- Orderbook
- Autopilot
- Driver
- Baseline
- Cow Swap
- Cow Explorer (*not yet implemented*)
- Cow Explorer

### Local

**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/<contract_address>`.
60 changes: 60 additions & 0 deletions playground/docker-compose.fork.yml
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,65 @@ 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:
build:
context: .
dockerfile: Dockerfile.sourcify
restart: always
depends_on:
sourcify-db:
condition: service_healthy
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:/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:
context: .
dockerfile: Dockerfile.otterscan
restart: always
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: .
Expand Down Expand Up @@ -253,3 +312,4 @@ services:

volumes:
postgres:
sourcify-postgres:
62 changes: 61 additions & 1 deletion playground/docker-compose.non-interactive.yml
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,66 @@ 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:
build:
context: .
dockerfile: Dockerfile.sourcify
restart: always
depends_on:
sourcify-db:
condition: service_healthy
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:/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:
build:
context: .
dockerfile: Dockerfile.otterscan
restart: always
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: .
Expand Down Expand Up @@ -231,7 +291,6 @@ services:
volumes:
- ./grafana-prometheus.yml:/etc/grafana/provisioning/datasources/prometheus.yml


prometheus:
image: prom/prometheus:latest
container_name: prometheus
Expand All @@ -243,3 +302,4 @@ services:

volumes:
postgres:
sourcify-postgres:
10 changes: 10 additions & 0 deletions playground/nginx-spa.conf
Original file line number Diff line number Diff line change
@@ -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;
}
}
Loading