|
| 1 | +# Cow Protocol Services |
| 2 | + |
| 3 | +Backend services for Cow Protocol, a decentralized trading protocol with batch auctions on EVM networks. |
| 4 | + |
| 5 | +## Project Structure |
| 6 | + |
| 7 | +This is a Rust workspace containing multiple services and libraries: |
| 8 | + |
| 9 | +### Main Services (Binaries) |
| 10 | +- **orderbook** - HTTP API for order submission and queries |
| 11 | +- **autopilot** - Protocol driver that manages auctions |
| 12 | +- **driver** - Handles liquidity collection and solution selection |
| 13 | +- **solvers** - Internal solver engine (baseline) |
| 14 | +- **refunder** - Handles refunds |
| 15 | + |
| 16 | +### Key Libraries |
| 17 | +- **shared** - Common functionality (pricing, liquidity, gas estimation) |
| 18 | +- **database** - PostgreSQL abstraction and migrations |
| 19 | +- **model** - Serialization models for API |
| 20 | +- **contracts** - Smart contract bindings |
| 21 | + |
| 22 | +## Architecture Overview |
| 23 | + |
| 24 | +``` |
| 25 | +User signs order → Orderbook validates → Autopilot includes in auction |
| 26 | + ↓ |
| 27 | + ┌─────────────────────────┴─────────────────────────┐ |
| 28 | + ↓ ↓ |
| 29 | + Colocated External Solvers Our Drivers + Non-Colocated Solvers |
| 30 | + (run their own driver+solver) ↓ ↓ |
| 31 | + │ Our solvers External solver APIs |
| 32 | + │ (baseline, (non-colocated partners |
| 33 | + │ balancer, ...) like 1inch, 0x, etc) |
| 34 | + └─────────────────────────┬─────────────────────────┘ |
| 35 | + ↓ |
| 36 | + Autopilot ranks solutions, picks winner(s) |
| 37 | + ↓ |
| 38 | + Winning driver submits to chain (2-3 block window) |
| 39 | + ↓ |
| 40 | + Settlement contract executes: |
| 41 | + 1. Pre-interactions (incl user pre-hooks) |
| 42 | + 2. Transfer sell tokens in |
| 43 | + 3. Main interactions (swaps/routing) |
| 44 | + 4. Pay out buy tokens |
| 45 | + 5. Post-interactions (incl user post-hooks) |
| 46 | + ↓ |
| 47 | + Circuit breaker monitors compliance |
| 48 | +``` |
| 49 | + |
| 50 | +**Solver types:** |
| 51 | +- **Colocated**: External partners run their own driver + solver. Full control, full responsibility. |
| 52 | +- **Non-colocated**: We run the driver, configured with their solver API endpoint. We handle simulation/submission. |
| 53 | + |
| 54 | +**Key components:** |
| 55 | +- **Orderbook**: Validates + stores orders, handles quoting |
| 56 | +- **Autopilot**: Central auctioneer, runs every ~12-15s (eventually every block), filters orders, adds fee policies, sends auction to solvers, ranks solutions |
| 57 | +- **Driver**: Fetches liquidity, encodes solutions to calldata, simulates, submits to chain. Handles everything except route-finding. |
| 58 | +- **Solver Engine**: Pure math — finds best routes/matches. Can be internal (baseline, balancer) or external API calls. |
| 59 | +- **Circuit Breaker**: Monitors on-chain settlements match off-chain auction outcomes. Jails misbehaving solvers. |
| 60 | + |
| 61 | +## Technology Stack |
| 62 | + |
| 63 | +- **Language**: Rust 2021+ Edition |
| 64 | +- **Runtime**: Tokio async |
| 65 | +- **Database**: PostgreSQL with sqlx |
| 66 | +- **Web3**: Alloy |
| 67 | +- **HTTP**: Axum |
| 68 | + |
| 69 | +## Documentation |
| 70 | + |
| 71 | +- **Protocol Documentation**: https://docs.cow.fi/ |
| 72 | + - Technical Reference: API specs and SDK docs |
| 73 | + - Concepts: Protocol fundamentals and architecture |
| 74 | + |
| 75 | +## Testing |
| 76 | + |
| 77 | +- Use `just` commands for running tests (see Justfile) |
| 78 | +- E2E tests available in `crates/e2e` |
| 79 | +- Local development environment in `playground/` |
| 80 | + |
| 81 | +## Directory Structure |
| 82 | + |
| 83 | +``` |
| 84 | +crates/ # 25+ Rust crates (binaries + libraries) |
| 85 | +database/ # PostgreSQL migrations and schemas |
| 86 | +playground/ # Local dev environment |
| 87 | +configs/ # Configuration files |
| 88 | +``` |
| 89 | + |
| 90 | +# General Coding Instructions |
| 91 | + |
| 92 | +If there is a test you can run then run it or `cargo check` or `cargo build`; run it after you have made changes. |
| 93 | +Use rust-analyzer MCP when appropriate such as finding usages or renaming. After a change run "cargo +nightly fmt". |
| 94 | + |
| 95 | +## Code Style |
| 96 | + |
| 97 | +Instead of using full paths like `volume_fee_bucket_overrides: Vec<shared::arguments::TokenBucketFeeOverride>`, import the type at the beginning so you don't have to use the full path later. |
| 98 | + |
| 99 | +Don't add a lot of comments. Add comments only if the code is a bit weird or the concept is not clear. |
| 100 | + |
| 101 | +## CoW Protocol Database Access |
| 102 | + |
| 103 | +**Always show the SQL query before executing it** against postgres MCP tools (`mcp__postgres-protocol__query`, `mcp__postgres-analytics__query`). |
| 104 | + |
| 105 | +**Query timeout**: MCP servers are configured with a 120 second timeout. For potentially long-running queries, prefix with `SET statement_timeout = '30s';` (or appropriate duration) to fail fast: |
| 106 | +```sql |
| 107 | +SET statement_timeout = '30s'; |
| 108 | +SELECT ... FROM large_table ...; |
| 109 | +``` |
| 110 | +If a query times out, try a different approach (add more filters, use a smaller time range, simplify aggregations, or break into smaller queries). |
| 111 | + |
| 112 | +Read-only replica available via MCP. If that fails for some reason, then you can use psql with: |
| 113 | +```bash |
| 114 | +source .env.claude && PGPASSWORD="$COW_DB_PASSWORD" psql \ |
| 115 | + -h "$COW_DB_HOST" -p "$COW_DB_PORT" -U "$COW_DB_USER" -d <database> -c "<query>" |
| 116 | +``` |
| 117 | +but use MCP where possible. |
| 118 | + |
| 119 | +Databases: `mainnet`, `arbitrum-one`, `base`, `linea`, `polygon`, `xdai`, `sepolia`, `plasma`, `ink`, `bnb` etc. |
| 120 | + |
| 121 | +## RPC Node |
| 122 | + |
| 123 | +Use `$ETH_MAINNET_RPC` from `.env.claude` for mainnet. Use `cast` or whatever tools you want freely. |
| 124 | + |
| 125 | +## Grafana Logs Access |
| 126 | + |
| 127 | +Query logs via the Grafana API (credentials in `.env.claude`): |
| 128 | + |
| 129 | +```bash |
| 130 | +source .env.claude && curl -s -H "Authorization: Bearer $GRAFANA_API_TOKEN" \ |
| 131 | + "$GRAFANA_URL/api/ds/query" \ |
| 132 | + -X POST -H "Content-Type: application/json" \ |
| 133 | + -d '{ |
| 134 | + "queries": [{ |
| 135 | + "refId": "A", |
| 136 | + "datasource": {"type": "victoriametrics-logs-datasource", "uid": "'"$VICTORIA_LOGS_DATASOURCE_UID"'"}, |
| 137 | + "expr": "<search_term>", |
| 138 | + "queryType": "instant" |
| 139 | + }], |
| 140 | + "from": "now-1h", |
| 141 | + "to": "now" |
| 142 | + }' |
| 143 | +``` |
| 144 | +Adjust expr for search terms (e.g., plasma, ink, error) |
| 145 | +Adjust from/to for time range (e.g., now-15m, now-24h) |
| 146 | +Parse log lines with: | jq -r '.results.A.frames[0].data.values[1][]' |
| 147 | + |
| 148 | +## Etherscan API (V2) |
| 149 | + |
| 150 | +Use MCP `mcp__fetch__fetch` tool. API Key in `.env.claude` as `$ETHERSCAN_API_KEY`. |
| 151 | + |
| 152 | +**Important**: V1 API is deprecated. Use V2 with the `chainid` parameter: |
| 153 | +- Mainnet: `chainid=1` |
| 154 | +- Arbitrum: `chainid=42161` |
| 155 | +- Base: `chainid=8453` |
| 156 | + |
| 157 | +Example URL format: |
| 158 | +``` |
| 159 | +https://api.etherscan.io/v2/api?chainid=1&module=account&action=balance&address=<addr>&tag=latest&apikey=<key> |
| 160 | +``` |
| 161 | + |
| 162 | +Read the API key from `.env.claude` and use it directly in the URL (MCP fetch doesn't do shell variable substitution). |
| 163 | + |
| 164 | +## Investigating orders |
| 165 | + |
| 166 | +When asked to look into what happened to an order read file ./docs/COW_ORDER_DEBUG_SKILL.md and follow the instructions there. |
| 167 | +Make heavy use of logs and DB to find all info you need and present finding to the user with evidence. |
0 commit comments