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
-
Collection Phase (0-30 seconds)
- Users submit signed intents (off-chain)
- Orders accumulate in CoW API orderbook
- Private until batch closes
-
Auction Phase (at 30-second boundary)
- Protocol broadcasts all pending orders
- Solvers receive batch and begin solving
- Typically <5 seconds to find solution
-
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
-
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:
- Polls your
getTradeableOrder()periodically - Calls
BondingCurveAdapter.quoteBuy()/.quoteSell()for live quotes - Encodes quote as
OffchainData(quotedOutput + validity) - 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:
- Deploy MycoBondingCurve with USDC/MYCO addresses
- Deploy BondingCurveAdapter pointing to:
_bondingCurve: Your MycoBondingCurve_settlement:0x9008D19f...(GPv2Settlement)_usdc: USDC on Base (typically0x833589fC4D06F649c466dB920d0135aa6Df1cDEA)_mycoToken: Your MYCO token
- Deploy MycoConditionalOrder handler
- 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()andexecuteSellForCoW() - 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
-
VaultRelayer Authorization
- Users approve Balancer Vault, not Settlement directly
- Settlement calls VaultRelayer to pull tokens
- If you approve Settlement: malicious solver could drain funds
-
Quote Expiry
- Watch Tower quotes expire quickly (~30-60s)
- Always check
block.timestamp > quote.validToingetTradeableOrder - Revert with
OrderNotValidif stale
-
Reentrancy
- BondingCurveAdapter uses
nonReentrantguard - Settlement is reentrant-safe but individual hooks aren't
- Lock your curve during settlement
- BondingCurveAdapter uses
-
Slippage Management
minOutputAmountis 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
- CoW Protocol Docs - Main reference
- ComposableCoW GitHub - Conditional order framework
- CoW Contracts Repository - Settlement contract source
Concepts
- Understanding MEV Protection - How CoW prevents sandwich attacks
- Understanding Batch Auctions - Order collection and solving
- Fair Combinatorial Auction - Solver optimization model
SDKs & APIs
- CoW SDK - TypeScript/JavaScript
- API Integration - REST endpoints
- Order Book Package - Direct API client
Your Implementation Guides
- Flow of an Order - Lifecycle
- Solvers - How optimization works
- Intents - Intent-based model
Articles & Explainers
- CoW Swap Explained: Intent-Based DEX Trading With MEV Protection
- CoW Protocol Batch Auctions Explained
- How CoW Protocol Actually Works
11. Next Steps for MycoFi Implementation
Immediate (MVP)
- Review your current
MycoConditionalOrderandBondingCurveAdapter - Test against Base Sepolia testnet
- Get Watch Tower access from CoW Protocol team
- Deploy handler registry entry
Short-term (Production)
- Multi-token support (different community tokens)
- DCA/TWAP variants for recurring buys
- Limit order types (buy below price, sell above)
- Revenue sharing mechanism (solver rewards)
Long-term (Advanced)
- Bonding curve governance (adjust parameters via DAO)
- Cross-chain settlements (Arbitrum + Optimism)
- Custom solver incentives (reward MEV-minimizing solvers)
- Integration with rSpace for account-bound trading
Document Version: 1.0 Last Updated: 2026-04-03 Status: Research Complete - Ready for Implementation