Skip to main content
Solver engines provide the core optimization logic for finding optimal trade routes in CoW Protocol auctions. They receive abstract auction data from the Driver and return abstract solutions.

Overview

Solver engines are pure route-finding and optimization services:
Driver
  ↓ POST /solve (auction + liquidity)
Solver Engine
  ↓ optimization algorithm
Solver Engine
  ↓ abstract solution
Driver

Responsibilities

  • Route Finding: Determine optimal paths through available liquidity
  • Order Matching: Identify coincidence of wants between orders
  • Price Discovery: Compute uniform clearing prices
  • Gas Optimization: Balance execution cost vs quality
  • Constraint Satisfaction: Respect order limits, balances, and partial fill rules

Not Responsible For

  • Calldata encoding
  • Transaction simulation
  • Settlement submission
  • Liquidity collection
  • On-chain interactions

Solver Types

Internal Solvers

Provided by CoW Protocol:
  1. Baseline: Simple greedy solver for basic matching and routing
  2. Balancer: Specialized for Balancer pool routing
  3. Legacy solvers: Various historical implementations

External Solvers

Third-party optimization services:
  • 1inch: Aggregator API integration
  • 0x: RFQ and aggregator integration
  • ParaSwap: Multi-source aggregation
  • Custom: Proprietary algorithms from solver teams

API Specification

Solve Auction

curl -X POST "http://localhost:8000/solve" \
  -H "Content-Type: application/json" \
  -d @auction.json
Receives an auction and returns one or more solutions. Endpoint: POST /solve

Request Body

id
string
Opaque auction identifier (null for non-auction requests like quotes)
tokens
object
required
Mapping of token addresses to token information
orders
array
required
Array of CoW Protocol orders to solve
liquidity
array
required
Array of available on-chain liquidity sources
effectiveGasPrice
string
required
Current gas price in Wei for scoring
deadline
string
required
ISO 8601 UTC timestamp by which solution must be returned
surplusCapturingJitOrderOwners
array
Addresses whose JIT order surplus counts toward score

Token Information

Order Structure

Liquidity Sources

See Driver API - Liquidity Collection for detailed liquidity source structures. Types include:
  • constantProduct: Uniswap V2 style pools
  • weightedProduct: Balancer weighted pools
  • stable: Curve stable pools
  • concentratedLiquidity: Uniswap V3 style pools
  • limitOrder: External (0x) limit orders

Response

solutions
array
Array of proposed solutions (can be empty if no viable solution found)

Solution Structure

Trade Types

Interaction Types

Example Response:
{
  "solutions": [
    {
      "id": 1,
      "prices": {
        "0x6B175474E89094C44Da98b954EedeAC495271d0F": "1000000000000000000",
        "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2": "1100000000000000000"
      },
      "trades": [
        {
          "kind": "fulfillment",
          "order": "0xabc...123",
          "executedAmount": "1000000000000000000"
        }
      ],
      "interactions": [
        {
          "kind": "liquidity",
          "id": "uniswap-v2-dai-weth",
          "inputToken": "0x6B175474E89094C44Da98b954EedeAC495271d0F",
          "outputToken": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
          "inputAmount": "1000000000000000000",
          "outputAmount": "950000000000000000"
        }
      ],
      "gas": 150000
    }
  ]
}
Status Codes:
  • 200: Solutions computed (may be empty array if no solution found)
  • 400: Invalid request
  • 429: Solver too busy
  • 500: Internal error

Receive Notification

curl -X POST "http://localhost:8000/notify" \
  -H "Content-Type: application/json" \
  -d @notification.json
Receives notifications about how solutions performed. Endpoint: POST /notify

Request Body

auctionId
string
Auction ID for which notification applies
solutionId
integer
Solution ID within the auction
kind
string
Notification type

Notification Kinds

Status Codes:
  • 200: Notification received

Solution Requirements

Constraint Satisfaction

  1. Order Limits:
    • Sell orders: executedSell ≤ sellAmount
    • Buy orders: executedBuy ≥ buyAmount
  2. Partial Fills:
    • If partiallyFillable == false: Must fill completely or not at all
    • If partiallyFillable == true: Can fill any amount within limits
  3. Uniform Clearing Prices:
    • All trades use same price vector
    • Prices must satisfy all order limit prices
  4. Token Conservation:
    • Total tokens in = total tokens out (per token)
    • Account for fees, surplus, and slippage
  5. Gas Efficiency:
    • Reasonable gas estimate
    • Balance quality vs cost

Optimization Objectives

Primary: Maximize objective value:
score = Σ(user_surplus) + Σ(protocol_fees) - gas_cost_in_native_token
Where:
  • User Surplus: Difference between clearing price and limit price for each order
  • Protocol Fees: Sum of fees collected per fee policies
  • Gas Cost: Estimated gas × gas price × native token price
Secondary:
  • Maximize filled volume
  • Minimize slippage
  • Optimize gas usage

Fee Policy Application

Solver engines must apply protocol fee policies when computing solutions:

Surplus Fee

{
  "kind": "surplus",
  "factor": 0.5,
  "maxVolumeFactor": 0.05
}
Charge factor × surplus, capped at maxVolumeFactor × volume. Example:
  • Order: Sell 1000 DAI, limit price 1 DAI = 0.001 ETH
  • Execution: Receive 1.2 ETH for 1000 DAI (clearing price: 1 DAI = 0.0012 ETH)
  • Surplus: (0.0012 - 0.001) × 1000 = 0.2 ETH
  • Fee: min(0.5 × 0.2, 0.05 × 1000 × 0.0012) = min(0.1, 0.06) = 0.06 ETH

Volume Fee

{
  "kind": "volume",
  "factor": 0.002
}
Charge factor × executed volume. Example:
  • Order: Sell 1000 DAI
  • Execution: 1000 DAI at 0.0012 ETH per DAI
  • Fee: 0.002 × 1000 × 0.0012 = 0.0024 ETH

Price Improvement Fee

{
  "kind": "priceImprovement",
  "factor": 0.5,
  "maxVolumeFactor": 0.01,
  "quote": {
    "sell_amount": "1000000000000000000000",
    "buy_amount": "1000000000000000000",
    "fee": "10000000000000000"
  }
}
Charge factor × improvement over quote, capped at maxVolumeFactor × volume.

Baseline Solver Algorithm

The baseline solver provides a reference implementation:

Algorithm Outline

  1. Order Matching: Check for direct coincidence of wants
    • If sell(A→B) and sell(B→A) exist, match them directly
  2. Single Order Routing: For each unmatched order:
    • Find best path through available liquidity
    • Consider gas costs
    • Apply fee policies
  3. Price Computation: Compute uniform clearing prices
    • Use marginal rates from executed liquidity
    • Ensure all order limits satisfied
  4. Gas Estimation: Sum gas costs for all interactions
  5. Score Calculation: Compute objective value

Limitations

  • Greedy approach (no global optimization)
  • Handles one or two orders well
  • Struggles with complex multi-order scenarios
  • Doesn’t explore all possible routings

Advanced Solver Techniques

Batch Optimization

  • Consider all orders simultaneously
  • Find global optimum across order set
  • More computationally expensive

AMM Rebalancing

  • Use CoW orders to rebalance AMM pools
  • Arbitrage between liquidity sources
  • Improve overall liquidity efficiency

Ring Trades

  • Create cycles: A→B→C→A
  • Extract value from price differences
  • Can improve surplus for all participants

JIT Liquidity

  • Inject just-in-time orders to improve prices
  • Requires careful surplus accounting
  • Can bridge liquidity gaps

Smart Order Routing

  • Split orders across multiple paths
  • Reduce price impact
  • Consider gas costs vs quality tradeoff

Testing Solvers

Unit Tests

  1. Constraint Validation: Verify all order limits respected
  2. Price Consistency: Check uniform clearing prices
  3. Token Conservation: Ensure balanced token flows
  4. Fee Application: Validate fee policy calculations
  5. Gas Estimates: Compare estimated vs actual gas

Integration Tests

  1. Driver Integration: Test with reference driver
  2. Liquidity Sources: Use real pool data
  3. Historical Auctions: Replay past auctions
  4. Performance: Measure solve time vs auction deadline

Benchmarking

  • Compare scores against other solvers
  • Measure win rate on historical data
  • Track gas efficiency
  • Monitor response times

Best Practices

  1. Respect Deadlines: Always respond before deadline
  2. Validate Input: Check for malformed auction data
  3. Handle Edge Cases: Empty orders, zero amounts, etc.
  4. Accurate Gas Estimates: Critical for correct scoring
  5. Apply Fee Policies Correctly: Misapplied fees lead to invalid solutions
  6. Consider Partial Fills: Don’t ignore partially fillable orders
  7. Optimize Incrementally: Start simple, add complexity gradually
  8. Log Decisions: Track why certain paths were chosen
  9. Monitor Performance: Identify bottlenecks in optimization
  10. Test Thoroughly: Validate constraints before returning solutions

Resources


Example: Simple Sell Order Solution

Input Auction:
{
  "id": "12345",
  "tokens": {
    "0x6B17...": {
      "decimals": 18,
      "symbol": "DAI",
      "referencePrice": "500000000000000",
      "availableBalance": "0",
      "trusted": true
    },
    "0xC02a...": {
      "decimals": 18,
      "symbol": "WETH",
      "referencePrice": "1000000000000000000",
      "availableBalance": "1000000000000000000",
      "trusted": true
    }
  },
  "orders": [
    {
      "uid": "0xabc...123",
      "sellToken": "0x6B17...",
      "buyToken": "0xC02a...",
      "sellAmount": "1000000000000000000000",
      "buyAmount": "900000000000000000",
      "kind": "sell",
      "partiallyFillable": false,
      "class": "market",
      "feePolicies": [
        {
          "kind": "surplus",
          "factor": 0.5,
          "maxVolumeFactor": 0.05
        }
      ]
    }
  ],
  "liquidity": [
    {
      "id": "1",
      "address": "0xA478...",
      "kind": "constantProduct",
      "tokens": {
        "0x6B17...": { "balance": "1000000000000000000000000" },
        "0xC02a...": { "balance": "1000000000000000000000" }
      },
      "fee": "0.003",
      "router": "0x7a25...",
      "gasEstimate": "100000"
    }
  ],
  "effectiveGasPrice": "30000000000",
  "deadline": "2024-03-04T12:00:10Z"
}
Output Solution:
{
  "solutions": [
    {
      "id": 1,
      "prices": {
        "0x6B17...": "1000",
        "0xC02a...": "1050000"
      },
      "trades": [
        {
          "kind": "fulfillment",
          "order": "0xabc...123",
          "executedAmount": "1000000000000000000000"
        }
      ],
      "interactions": [
        {
          "kind": "liquidity",
          "id": "1",
          "inputToken": "0x6B17...",
          "outputToken": "0xC02a...",
          "inputAmount": "1000000000000000000000",
          "outputAmount": "997000000000000000"
        }
      ],
      "gas": 100000
    }
  ]
}
This solution:
  • Fulfills the order completely (not partially fillable)
  • Routes through the Uniswap V2 pool
  • Returns ~0.997 WETH for 1000 DAI
  • Exceeds the buy amount limit of 0.9 WETH
  • Generates surplus that can be partially captured as protocol fee

Build docs developers (and LLMs) love