smart-contracts/backlog/docs/CoW_Protocol_Research.md

22 KiB

CoW Protocol Deep Dive: Smart Contract Integration Guide

Executive Summary

CoW Protocol is an intent-based trading system that protects users from MEV (Maximal Extractable Value) through batch auctions and competing solvers. Unlike traditional order books, users submit signed intents (not executable transactions) that solvers optimize for best execution.

For MycoFi's community token economies, this means:

  • Bonding curve trades can be executed via batch auctions
  • MEV protection prevents sandwich attacks on token purchases/sales
  • Solvers compete to offer best prices automatically
  • Users don't pay gas upfront (solvers pay, deduct from surplus)

1. Key Contracts & Their Roles

Core Settlement Infrastructure

Contract Address Network Role
GPv2Settlement 0x9008D19f58AAbD9eD0D60971565AA8510560ab41 All (including Base 8453) Primary settlement contract; executes orders, manages interactions, enforces token transfers
GPv2VaultRelayer 0xc92e8bdf79f0507f65a392b0ab4667716bfe0110 All networks Pulls user tokens from Balancer Vault; only address authorized to access Vault allowances
GPv2Authenticator 0x2c4c28DDBdAc9C5E7055b4C863b72eA0149D8aFE Ethereum, Base ERC-1271 signature verification for smart contract orders
ComposableCoW Varies by network All Handler registry for conditional orders; creates/stores order definitions

Your Custom Contracts (payment-infra)

Contract Purpose Interfaces
MycoBondingCurve Polynomial pricing curve for $MYCO token ERC20 interaction, price calculation, buy/sell mechanics
MycoToken Community token with mint/burn controls ERC20, Burnable
BondingCurveAdapter Bridge between CoW settlement and bonding curve Pre-interaction hook for solvers; provides quotes
MycoConditionalOrder Stateless handler for bonding curve orders IConditionalOrder (ComposableCoW interface)

Architecture Flow

User Intent (signed)
    ↓
ComposableCoW (stores static params)
    ↓
Watch Tower (polls getTradeableOrder)
    ↓
Solvers (compete to provide best execution)
    ↓
GPv2Settlement.settle() (winning solver executes)
    ├→ Pre-interactions (BondingCurveAdapter executes buy/sell)
    ├→ VaultRelayer pulls tokens from Vault
    ├→ Settlement transfers output
    └→ Post-interactions (optional)

2. Intent-Based Trading Model

How Orders Work (Different from Traditional DEX)

Traditional order book:

User creates transaction → Mempool exposed → Bots sandwich → Execute
                                    ↓ MEV risk

CoW Protocol (Batch Auction):

User signs intent message (off-chain, private) → Batch accumulates (~30 sec)
→ Solvers solve together → Single on-chain transaction (solver pays gas)
                                    ↓ MEV protected

Key Differences from Uniswap-style Swaps

Aspect Traditional AMM CoW Protocol
Order Type Executable transaction Signed intent message
Visibility Mempool (public) Private until batch closure
Gas Payment User upfront Solver (deducted from surplus)
Price Discovery AMM curve Solver competition
Settlement Immediate Batch interval (~30s)
MEV Risk High (reordering, sandwiching) Low (uniform clearing prices)

The Batch Auction Process

  1. Collection Phase (0-30 seconds)

    • Users submit signed intents (off-chain)
    • Orders accumulate in CoW API orderbook
    • Private until batch closes
  2. Auction Phase (at 30-second boundary)

    • Protocol broadcasts all pending orders
    • Solvers receive batch and begin solving
    • Typically <5 seconds to find solution
  3. Solution Phase

    • Solvers compute optimal settlement
    • May involve:
      • Direct peer-to-peer matching (Coincidence of Wants)
      • Routing through Uniswap, Curve, other DEXs
      • Combined strategies for maximum surplus
  4. Execution Phase

    • Winning solver submits on-chain transaction
    • GPv2Settlement.settle() executes atomically
    • All tokens transferred, fees deducted, user receives output

Order Intent Structure

Users don't create GPv2Order.Data directly. Instead, they define intents that specify:

  • Token pair (e.g., USDC ↔ MYCO)
  • Amount and direction
  • Minimum acceptable output (slippage protection)
  • Time window for validity
  • Execution preferences (partial fill? post-only?)

Solvers then create concrete GPv2Order.Data from these intents.


3. Hooks System: Pre/Post Interaction Hooks

What Are Hooks?

Hooks are external contract calls executed at specific points during settlement:

// Settlement execution phases:
settle()
  ├─ PRE-INTERACTION HOOKS (before token transfers)
    └─ Can execute setup logic, swap prep
  ├─ Token transfers from VaultRelayer
  ├─ CUSTOM INTERACTION HOOKS (during settlement)
    └─ Your BondingCurveAdapter.executeBuyForCoW()
  ├─ Token transfers to user
  └─ POST-INTERACTION HOOKS (after settlement complete)
     └─ Can execute cleanup, notifications

Your Use Case: BondingCurveAdapter as Hook

In /payment-infra/contracts/contracts/cow/BondingCurveAdapter.sol:

modifier onlySettlement() {
    if (msg.sender != settlement) revert OnlySettlement();
    _;
}

function executeBuyForCoW(
    uint256 usdcAmount,
    uint256 minMycoOut
) external onlySettlement nonReentrant returns (uint256 mycoOut) {
    // Settlement calls this during pre-interactions
    // USDC already transferred by Settlement → Adapter
    mycoOut = bondingCurve.buy(usdcAmount, minMycoOut);
    // MYCO minted to Adapter, Settlement pulls it during next phase
    return mycoOut;
}

Token flow:

Settlement receives USDC from user via VaultRelayer
    ↓
Settlement calls BondingCurveAdapter.executeBuyForCoW(USDC_amount, min_myco)
    ├─ Adapter.buy() on BondingCurve with USDC
    └─ BondingCurve mints MYCO to Adapter
Settlement registers MYCO balance owed to adapter
    ↓
Settlement transfers MYCO from Adapter → User

Hook Types (Coming to ComposableCoW)

Soon, handlers can define optional hooks:

interface IConditionalOrder {
    struct Hooks {
        address preHook;       // Executed before order starts execution
        address postHook;      // Executed after order completes
        bytes preHookData;     // Encoded params for pre-hook
        bytes postHookData;    // Encoded params for post-hook
    }
}

Common hook use cases:

  • Pre-hooks: Approve tokens, verify conditions, signal price feeds
  • Post-hooks: Update position tracking, trigger notifications, execute follow-up actions

4. Building Custom Order Types: IConditionalOrder Interface

Core Interface (in your codebase)

interface IConditionalOrder {
    error PollTryNextBlock(string reason);  // Retry at next block
    error PollNever(string reason);         // Stop polling
    error OrderNotValid(string reason);     // Order invalid

    function getTradeableOrder(
        address owner,           // Safe wallet
        address sender,          // Usually Watch Tower
        bytes calldata staticInput,   // Your encoded params
        bytes calldata offchainInput  // Watch Tower's live data
    ) external view returns (GPv2Order.Data memory);

    function verify(
        address owner,
        address sender,
        bytes32 _hash,
        bytes32 domainSeparator,
        bytes calldata staticInput,
        bytes calldata offchainInput,
        GPv2Order.Data calldata order
    ) external view;
}

Your Implementation: MycoConditionalOrder

File: /payment-infra/contracts/contracts/cow/MycoConditionalOrder.sol

struct OrderData {
    TradeDirection direction;      // BUY or SELL
    uint256 inputAmount;           // USDC (buy) or MYCO (sell)
    uint256 minOutputAmount;       // Slippage floor
    address receiver;              // Order recipient
    uint256 validityDuration;      // Seconds valid
}

struct OffchainData {
    uint256 quotedOutput;          // From BondingCurveAdapter.quoteBuy/quoteSell
    uint32 validTo;                // Quote expiry
}

function getTradeableOrder(
    address owner,
    address,
    bytes calldata staticInput,
    bytes calldata offchainInput
) external view override returns (GPv2Order.Data memory order) {
    OrderData memory params = abi.decode(staticInput, (OrderData));
    OffchainData memory quote = abi.decode(offchainInput, (OffchainData));

    // Validate quote freshness and minimums
    if (block.timestamp > quote.validTo) {
        revert OrderNotValid("Quote expired");
    }
    if (quote.quotedOutput < params.minOutputAmount) {
        revert OrderNotValid("Quote below minimum");
    }

    // Generate GPv2Order from params + live quote
    // Settlement will use this order to execute
    return GPv2Order.Data({
        sellToken: direction == BUY ? usdcToken : mycoTokenAddr,
        buyToken: direction == BUY ? mycoTokenAddr : usdcToken,
        receiver: receiver,
        sellAmount: params.inputAmount,
        buyAmount: quote.quotedOutput,  // From Watch Tower's latest quote
        validTo: quote.validTo,
        appData: APP_DATA,
        feeAmount: 0,
        kind: GPv2Order.KIND_SELL,      // Always KIND_SELL (specify exact input)
        partiallyFillable: false,       // All-or-nothing
        sellTokenBalance: GPv2Order.BALANCE_ERC20,
        buyTokenBalance: GPv2Order.BALANCE_ERC20
    });
}

The Watch Tower Integration

The Watch Tower is CoW Protocol's off-chain service that:

  1. Polls your getTradeableOrder() periodically
  2. Calls BondingCurveAdapter.quoteBuy() / .quoteSell() for live quotes
  3. Encodes quote as OffchainData (quotedOutput + validity)
  4. Submits complete order to solver network

Your handler is stateless — all logic is in getTradeableOrder() called each poll.

Custom Order Type Examples in Your Repo

File Type Purpose
MycoConditionalOrder.sol Basic bonding curve Buy/sell MYCO with live quotes
MycoLimitOrder.sol Limit order wrapper Execute only if price reaches target
MycoTWAPOrder.sol Time-Weighted Average Price Split large trades across time
MycoDCAOrder.sol Dollar-Cost Averaging Recurring buys at intervals

5. MEV Protection Mechanisms

The Three Pillars of CoW's MEV Protection

1. Private Order Flow

  • Your intent never enters public mempool
  • Only visible to authorized solvers and CoW Protocol infrastructure
  • Attack prevented: Mempool monitoring bots can't sandwich you

2. Uniform Clearing Prices (UCP)

  • All trades of same pair in batch execute at same price
  • If Alice and Bob both buy MYCO with USDC in same batch:
    MYCO/USDC = $0.50 (cleared price for this batch)
    Alice: pay $500 USDC → get 1000 MYCO at $0.50
    Bob:   pay $300 USDC → get 600 MYCO at $0.50
    
  • Attack prevented: Reordering doesn't help attacker; everyone gets same price

3. Coincidence of Wants (CoW)

  • Orders matched peer-to-peer, not through AMM
  • Alice wants MYCO, Bob has MYCO → direct transfer
  • Attack prevented: No AMM curve manipulation; prices not affected by MEV order flow

Why This Matters for MycoFi Bonding Curves

Without CoW Protocol:

User submits bonding curve buy to mempool
    ↓
MEV bot sees pending tx, inserts "MEV sandwich"
    ├─ Bot buys MYCO first (pushes price up)
    ├─ User's tx executes at higher price
    └─ Bot sells MYCO (profits from price difference)
Result: User loses 1-5% due to MEV extraction

With CoW Protocol:

User signs intent (private, off-chain)
    ↓
Batch collected with other MYCO buyers
    ↓
Solvers compete (may match directly between users)
    ├─ If CoW: Alice ↔ Bob swap directly (no AMM)
    └─ If AMM: Best solver routing wins
Result: Uniform clearing price, no sandwich attacks

6. Deployment Addresses (Base & Other L2s)

Base (Chain ID: 8453)

Contract Address
GPv2Settlement 0x9008D19f58AAbD9eD0D60971565AA8510560ab41
GPv2VaultRelayer 0xc92e8bdf79f0507f65a392b0ab4667716bfe0110
Balancer Vault (same as Ethereum)
ComposableCoW Handler (deploy custom)
BondingCurveAdapter (deploy custom)

Ethereum Mainnet (Chain ID: 1)

Contract Address
GPv2Settlement 0x9008D19f58AAbD9eD0D60971565AA8510560ab41
GPv2VaultRelayer 0xc92e8bdf79f0507f65a392b0ab4667716bfe0110
Authenticator 0x2c4c28DDBdAc9C5E7055b4C863b72eA0149D8aFE

Other L2 Networks

  • Arbitrum, Optimism, Gnosis Chain — Same GPv2Settlement address (cross-chain deployment)
  • CoW Protocol designed for address consistency across networks

Deployment Requirements

To integrate your bonding curve with CoW on Base:

  1. Deploy MycoBondingCurve with USDC/MYCO addresses
  2. Deploy BondingCurveAdapter pointing to:
    • _bondingCurve: Your MycoBondingCurve
    • _settlement: 0x9008D19f... (GPv2Settlement)
    • _usdc: USDC on Base (typically 0x833589fC4D06F649c466dB920d0135aa6Df1cDEA)
    • _mycoToken: Your MYCO token
  3. Deploy MycoConditionalOrder handler
  4. Register in ComposableCoW with conditional order params

7. SDK/API for Programmatic Order Submission

CoW SDK vs. API

Method Best For Complexity
CoW SDK (@cowprotocol/sdk) Frontend, TypeScript clients Easy (high-level)
REST API Backend services, webhooks Medium (direct HTTP)
Conditional Orders Automated bots, recurring orders Complex (Watch Tower integration)

Programmatic Submission via SDK

import { CoWSDK } from "@cowprotocol/sdk";

const sdk = new CoWSDK({
    chainId: 8453,  // Base
    signer: ethers.Wallet  // User's signer
});

// Quote
const quote = await sdk.cowApi.getQuote({
    sellToken: USDC,
    buyToken: MYCO,
    sellAmount: ethers.parseUnits("1000", 6),  // 1000 USDC
    kind: "sell"  // Fixed input
});

// Submit order
const order = await sdk.postSwapOrderFromQuote({
    quote: quote,
    signer: userSigner,
    receiver: userAddress
});

console.log("Order UID:", order.uid);
// Wait for execution in next batch (~30s)

REST API Rate Limits

Quote requests:     10 requests/second
Order submission:   5 requests/second
General endpoints:  100 requests/minute

API Endpoints (Mainnet & Base)

POST /orders                # Submit order
GET  /orders/{uid}          # Order status
POST /quote                 # Get quote
GET  /tokens                # List supported tokens

Conditional Orders API

For Watch Tower polling of your bonding curve orders:

// Create conditional order (stored in ComposableCoW)
const conditionalOrder = await composableCoW.create({
    handler: mycoConditionalOrderAddress,
    salt: generateSalt(),
    staticInput: abi.encode(['OrderData'], [{
        direction: 0,  // BUY
        inputAmount: ethers.parseUnits("1000", 6),
        minOutputAmount: ethers.parseUnits("2000", 18),
        receiver: userAddress,
        validityDuration: 86400  // 24 hours
    }])
});

// Watch Tower polls getTradeableOrder automatically
// No manual submission needed!

8. Integration with MycoFi Bonding Curves

Your Current Architecture (Summary)

User → BondingCurveAdapter (quotes)
        BondingCurve (executes buy/sell)
        MycoConditionalOrder (defines order type)
        ComposableCoW (stores in handler registry)
        GPv2Settlement (executes on-chain)

Step-by-Step Integration Checklist

  • Phase 1: Setup

    • Deploy MycoBondingCurve on Base
    • Deploy MycoToken with mint/burn controls
    • Set up USDC approval from bonding curve to adapter
  • Phase 2: Adapter Integration

    • Deploy BondingCurveAdapter pointing to GPv2Settlement
    • Test executeBuyForCoW() and executeSellForCoW()
    • Verify quote functions (quoteBuy, quoteSell)
  • Phase 3: Conditional Order

    • Deploy MycoConditionalOrder handler
    • Implement Watch Tower polling simulation (local testing)
    • Verify getTradeableOrder() generates valid GPv2Order.Data
  • Phase 4: End-to-End Testing

    • Register handler in ComposableCoW test instance
    • Submit test conditional order
    • Verify execution in mock settlement
  • Phase 5: Watch Tower Integration

    • Register with CoW Protocol Watch Tower (request access)
    • Provide handler address and metadata
    • Monitor first live orders

Advanced: Multi-Curve Settlements

If you want multiple bonding curves (different tokens):

contract MultiTokenCurveHandler is IConditionalOrder {
    mapping(address token => address curve) public curveRegistry;
    mapping(address token => address adapter) public adapterRegistry;

    function getTradeableOrder(
        address owner,
        address,
        bytes calldata staticInput,
        bytes calldata offchainInput
    ) external view override returns (GPv2Order.Data memory) {
        (address token, uint256 amount, uint256 minOut) =
            abi.decode(staticInput, (address, uint256, uint256));

        address adapter = adapterRegistry[token];
        uint256 quote = IBondingCurveAdapter(adapter).quoteBuy(amount);

        return GPv2Order.Data({...});
    }
}

9. Key Gotchas & Best Practices

Critical Safety Points

  1. VaultRelayer Authorization

    • Users approve Balancer Vault, not Settlement directly
    • Settlement calls VaultRelayer to pull tokens
    • If you approve Settlement: malicious solver could drain funds
  2. Quote Expiry

    • Watch Tower quotes expire quickly (~30-60s)
    • Always check block.timestamp > quote.validTo in getTradeableOrder
    • Revert with OrderNotValid if stale
  3. Reentrancy

    • BondingCurveAdapter uses nonReentrant guard
    • Settlement is reentrant-safe but individual hooks aren't
    • Lock your curve during settlement
  4. Slippage Management

    • minOutputAmount is hard floor in conditional order
    • If quote falls below min, Watch Tower retries next block
    • Don't rely on percentage-based slippage in immutable adapter

Testing Strategy

// 1. Unit test bonding curve in isolation
test("buy increases price") { ... }

// 2. Test adapter with mock settlement
function testAdapterBuy() {
    usdc.transfer(adapter, 1000e6);
    uint256 mycoOut = adapter.executeBuyForCoW(1000e6, 2000e18);
    assert(mycoToken.balanceOf(adapter) >= 2000e18);
}

// 3. Test conditional order logic
function testGetTradeableOrder() {
    bytes memory staticInput = abi.encode(OrderData(...));
    bytes memory offchainInput = abi.encode(OffchainData(...));

    GPv2Order.Data memory order = handler.getTradeableOrder(
        owner, sender, staticInput, offchainInput
    );

    assert(order.buyAmount >= minOut);
    assert(order.validTo > block.timestamp);
}

// 4. Fork test against real Base settlement
function testLiveSettlement() {
    // Use foundry forge-std for Base fork
}

10. Resources & References

Official Documentation

Concepts

SDKs & APIs

Your Implementation Guides

Articles & Explainers


11. Next Steps for MycoFi Implementation

Immediate (MVP)

  1. Review your current MycoConditionalOrder and BondingCurveAdapter
  2. Test against Base Sepolia testnet
  3. Get Watch Tower access from CoW Protocol team
  4. Deploy handler registry entry

Short-term (Production)

  1. Multi-token support (different community tokens)
  2. DCA/TWAP variants for recurring buys
  3. Limit order types (buy below price, sell above)
  4. Revenue sharing mechanism (solver rewards)

Long-term (Advanced)

  1. Bonding curve governance (adjust parameters via DAO)
  2. Cross-chain settlements (Arbitrum + Optimism)
  3. Custom solver incentives (reward MEV-minimizing solvers)
  4. Integration with rSpace for account-bound trading

Document Version: 1.0 Last Updated: 2026-04-03 Status: Research Complete - Ready for Implementation