# CoW Protocol + MycoFi Integration: Architecture Diagrams ## 1. Complete Order Lifecycle ``` ┌─────────────────────────────────────────────────────────────────────────────┐ │ ORDER LIFECYCLE: USER INTENT → EXECUTION │ └─────────────────────────────────────────────────────────────────────────────┘ PHASE 1: USER CREATES ORDER (Off-chain, Private) ──────────────────────────────────────────────────────── User Wallet │ ├─ Signs intent message: │ { │ "buy": 1000 MYCO │ "sell": 500 USDC │ "minOut": 900 MYCO │ "validTo": block.timestamp + 24h │ } │ └─ Message stays PRIVATE (not in mempool) ↓ CoW API (off-chain orderbook) PHASE 2: BATCH COLLECTION (~30 seconds) ──────────────────────────────────────── Watch Tower Service │ ├─ Collects all pending intents (private) ├─ Polls MycoConditionalOrder.getTradeableOrder() │ ├─ Static input: {direction, amount, minOut, receiver} │ ├─ Calls BondingCurveAdapter.quoteBuy() for live quote │ └─ Returns GPv2Order.Data ready for solver │ └─ Broadcasts order batch to solvers PHASE 3: SOLVER AUCTION (Typically <5 seconds) ─────────────────────────────────────────────── Solver 1 Solver 2 Solver 3 (Uniswap routing) (Direct CoW matching) (1inch aggregation) │ │ │ └─ Computes solution ├─ Finds Alice ↔ Bob ├─ Finds best paths (all orders) │ direct swap │ through DEXs Surplus = $5 │ Surplus = $8 │ Surplus = $3 │ WINNER! ✓ │ GPv2Settlement.settle() receives solution from winning solver PHASE 4: ON-CHAIN SETTLEMENT ─────────────────────────── GPv2Settlement.settle() │ ├─ PRE-INTERACTION HOOKS │ └─ None for basic orders │ ├─ INPUT TRANSFERS (via VaultRelayer) │ └─ User's USDC transferred from Balancer Vault to adapter │ VaultRelayer (authorized) pulls 500 USDC │ ├─ CUSTOM LOGIC (via pre-interaction) │ └─ BondingCurveAdapter.executeBuyForCoW(500 USDC, 900 MYCO min) │ ├─ Calls MycoBondingCurve.buy(500 USDC) │ ├─ MYCO minted to adapter (1000 tokens) │ └─ Returns 1000 MYCO to settlement │ ├─ OUTPUT TRANSFERS (via VaultRelayer) │ └─ Settlement pulls 1000 MYCO from adapter to user │ └─ POST-INTERACTION HOOKS └─ None for basic orders FINAL STATE ─────────── ✓ User: -500 USDC, +1000 MYCO ✓ BondingCurve: +500 USDC reserve, -1000 MYCO supply ✓ Solver: Received 8 USDC surplus (profit for execution) ✓ Gas: Paid by solver (deducted from surplus) ``` --- ## 2. Contract Interaction Flow ``` ┌─────────────────────────────────────────────────────────────────┐ │ CONTRACT INTERACTION ARCHITECTURE │ └─────────────────────────────────────────────────────────────────┘ USER/WALLET │ ├─ Approves Balancer Vault (not Settlement!) │ └─ IERC20(USDC).approve(balancerVault, uint256.max) │ └─ Signs intent (off-chain) Watch Tower (off-chain bot) │ └─ Calls MycoConditionalOrder.getTradeableOrder() │ ├─ Decodes staticInput (OrderData) ├─ Decodes offchainInput (OffchainData from Watch Tower) ├─ Calls BondingCurveAdapter.quoteBuy() for live quote │ └─ Returns expected MYCO output │ └─ Generates GPv2Order.Data struct ├─ sellToken: USDC ├─ buyToken: MYCO ├─ sellAmount: 500 USDC ├─ buyAmount: 1000 MYCO (from quote) ├─ receiver: user address └─ validTo: quote.validTo Solver (winning solver) │ └─ Calls GPv2Settlement.settle() │ ├─ Verifies order signature (ERC-1271 via Authenticator if Safe) │ ├─ Calls VaultRelayer.transferFromAccounts() │ └─ Transfers 500 USDC from user's Vault allowance │ → Sends to BondingCurveAdapter │ ├─ INTERACTION: Calls BondingCurveAdapter.executeBuyForCoW() │ │ │ ├─ Checks only called from Settlement (modifier) │ ├─ Checks USDC balance (already received) │ ├─ Calls BondingCurve.buy(500 USDC, 900 MYCO min) │ │ │ │ │ ├─ Calculates price: P(supply) = basePrice + coeff*(supply^exp) │ │ ├─ Mints 1000 MYCO to adapter │ │ └─ Returns amount minted │ │ │ └─ Returns 1000 MYCO to Settlement │ ├─ Calls Vault.transferToAccounts() │ └─ Transfers 1000 MYCO from adapter → user wallet │ └─ Emits OrderFulfilled event TOKENS HELD DURING EXECUTION ───────────────────────────── Settlement: Temporarily holds order tokens (atomic) Adapter: Receives input, produces output (pre/post interaction logic) VaultRelayer: Only pulls/pushes via Settlement call ``` --- ## 3. MEV Attack Prevention Visualization ``` ┌──────────────────────────────────────────────────────────────┐ │ MEV PROTECTION: CoW vs Traditional AMM │ └──────────────────────────────────────────────────────────────┘ TRADITIONAL AMM (Uniswap-style): ────────────────────────────────── Mempool (public visibility) │ ├─ Alice's TX: Buy 1000 MYCO for 500 USDC │ (broadcast in mempool, visible to all) │ ├─ MEV Bot observes: │ "Alice buying MYCO, will push price up" │ ├─ Bot's sandwich attack: │ 1. Bot front-runs: Buy MYCO first │ ├─ Price moves: 0.5 → 0.51 MYCO/USDC │ └─ Bot pays: 490 USDC for 961 MYCO │ │ 2. Alice's TX executes │ ├─ Price higher: 0.51 MYCO/USDC │ └─ Alice pays: 500 USDC for 980 MYCO (loss vs no MEV!) │ │ 3. Bot back-runs: Sell MYCO │ ├─ Sells 961 MYCO for 490 USDC │ └─ Bot profit: ~10 USDC (extracted from Alice) │ └─ Alice LOSES ~20 MYCO due to MEV extraction! CoW PROTOCOL (Batch Auction): ────────────────────────────── Private Order Collection │ ├─ Alice's intent: "Buy 1000 MYCO for 500 USDC" [SIGNED, PRIVATE] │ ├─ Bob's intent: "Buy 600 MYCO for 300 USDC" [SIGNED, PRIVATE] │ └─ Both intents stay PRIVATE (not in mempool) MEV bots can't see them! Batch Closure (30 seconds) │ ├─ CoW collects orders: {Alice, Bob, Charlie, Dave, ...} │ └─ Solvers compete to find best settlement Solver Auction: │ ├─ Solver A (DEX routing): │ └─ Route through Uniswap → 950 MYCO for Alice │ ├─ Solver B (Direct CoW matching): │ ├─ Alice wants: 1000 MYCO for 500 USDC │ ├─ Bob has MYCO but wants stables │ ├─ Direct swap: Alice ↔ Bob │ │ └─ Alice: 500 USDC → 1000 MYCO (NO AMM, no slippage!) │ │ └─ Bob: 1000 MYCO → 500 USDC │ └─ Surplus: $0 MEV extracted (perfect execution!) │ └─ Solver B WINS (best price for users) On-chain Settlement: │ └─ Alice pays: 500 USDC Alice receives: 1000 MYCO No sandwich. No MEV extraction. MEV-protected! KEY PROTECTIONS: ───────────────── ✓ Private Order Flow: Intent never in public mempool ✓ Uniform Clearing Price: All trades at same price in batch ✓ Coincidence of Wants: Direct peer-to-peer when possible (No AMM slippage, no liquidity provider fees) ``` --- ## 4. BondingCurveAdapter Token Flow ``` ┌───────────────────────────────────────────────────────────────┐ │ TOKEN FLOW: BondingCurveAdapter in Settlement │ └───────────────────────────────────────────────────────────────┘ SCENARIO: User buys 1000 MYCO with 500 USDC via CoW Protocol INITIAL STATE: ────────────── User Wallet: ├─ 5000 USDC └─ 100 MYCO └─ Approval: Balancer Vault can spend 5000 USDC BondingCurveAdapter: └─ 0 USDC, 0 MYCO (fresh contract) Balancer Vault: └─ Holds user's approved allowances STEP 1: Solver calls GPv2Settlement.settle() ───────────────────────────────────────────── Settlement.settle({ orders: [order_from_user], inTransfers: [500 USDC], outTransfers: [1000 MYCO], interactions: [call BondingCurveAdapter.executeBuyForCoW()] }) STEP 2: Settlement pulls input tokens ────────────────────────────────────── VaultRelayer.transferFromAccounts( [User], [USDC], [500e6], // 500 USDC (6 decimals) [BondingCurveAdapter] ) User Wallet: BondingCurveAdapter: VaultRelayer: 5000 USDC → 0 USDC → (transfer) 4500 USDC ←─ pull ──┘ STEP 3: Settlement calls pre-interaction hook ────────────────────────────────────────────── BondingCurveAdapter.executeBuyForCoW( 500e6, // usdcAmount 900e18 // minMycoOut (slippage protection) ) │ ├─ Checks: "Only called from Settlement" ✓ ├─ Checks: "USDC balance >= 500e6" ✓ (we have 500 USDC) │ ├─ Calls BondingCurve.buy(500e6, 900e18) │ │ │ ├─ Current supply: 10,000,000 MYCO │ ├─ Price calc: P = basePrice + coeff * supply^exponent │ ├─ Calculates output: ~1000 MYCO │ ├─ Checks min output: 1000 >= 900 ✓ │ │ │ ├─ Burns tokens: bondingCurve.buy() path: │ │ ├─ Adapter already approved to spend curve's USDC │ │ ├─ USDC.transferFrom(adapter, curve, 500e6) ✓ │ │ └─ Curve updates: reserveBalance += 500 USDC │ │ │ └─ Mints to adapter: │ └─ MycoToken.mint(adapter, 1000e18) ✓ │ └─ Returns: 1000 MYCO minted State after hook: User: 4500 USDC, 100 MYCO Adapter: 0 USDC, 1000 MYCO (minted by curve) Curve: 500 USDC reserve, supply-1000 STEP 4: Settlement pulls output tokens (to user) ──────────────────────────────────────────────── Settlement registers: "Adapter owes 1000 MYCO" Vault.transferToAccounts( [BondingCurveAdapter], [MYCO], [1000e18], [User] ) VaultRelayer pulls from adapter (via Vault): Adapter: Vault: User: 1000 MYCO → (pull) → 100 MYCO 0 MYCO ←───────────────────────── 1100 MYCO FINAL STATE: ──────────── User Wallet: ├─ 4500 USDC (spent 500) ├─ 1100 MYCO (gained 1000) ✓ └─ Approval reset to available balance BondingCurveAdapter: └─ 0 USDC, 0 MYCO (drained, ready for next order) BondingCurve Contract: ├─ reserveBalance: +500 USDC ├─ totalSupply: -1000 MYCO └─ Ready for next user KEY SECURITY POINTS: ───────────────────── ✓ Adapter doesn't hold tokens permanently ✓ All transfers atomic (via Settlement) ✓ VaultRelayer prevents unauthorized fund access ✓ Reentrancy guard protects during curve execution ✓ Quote validation ensures minimum output respected ``` --- ## 5. Conditional Order State Machine ``` ┌─────────────────────────────────────────────────────────────┐ │ IConditionalOrder State & Polling Lifecycle │ └─────────────────────────────────────────────────────────────┘ CONDITIONAL ORDER LIFECYCLE: ──────────────────────────── [CREATED] │ └─ ComposableCoW.create({ handler: MycoConditionalOrder, salt: unique_identifier, staticInput: abi.encode({ direction: BUY, inputAmount: 500e6, // 500 USDC minOutputAmount: 900e18, // 900 MYCO minimum receiver: user, validityDuration: 86400 // 24 hours }) }) └─ Stored in ComposableCoW registry └─ Hashmap: owner → conditionalOrders[] [POLLING] (every block, ~12 seconds on Ethereum, ~2 sec on Base) │ └─ Watch Tower calls: MycoConditionalOrder.getTradeableOrder( owner: user_address, sender: watchtower, staticInput: above_encoded_data, offchainInput: abi.encode({ quotedOutput: 1000e18, // Current quote from adapter validTo: now + 30 min // Quote expiry }) ) └─ Handler validates: ├─ Quote not expired? ✓ ├─ quotedOutput >= minOutputAmount? ✓ ├─ User has sufficient balance? ✓ └─ Returns GPv2Order.Data ready for solver [SUBMITTED] │ └─ Watch Tower submits to solver network ├─ Submits valid orders └─ Retries failed orders next block [AWAITING_SOLVER] │ ├─ Solvers receive order batch ├─ Compete to solve efficiently └─ Winning solver selected [EXECUTED / FULFILLED] │ └─ Winning solver calls GPv2Settlement.settle() ├─ Order confirmed on-chain └─ User receives MYCO [REVERTED / CANCELLED] │ ├─ User calls ComposableCoW.cancel(orderHash) │ └─ Order removed from registry │ └─ Or if: ├─ Quote expires ├─ Balance insufficient └─ Validity duration exceeded WATCH TOWER ERROR HANDLING: ─────────────────────────── getTradeableOrder() returns error? What to do? PollTryNextBlock("reason") └─ Temporary issue (likely) └─ Retry next block (~12 sec) └─ Examples: ├─ User balance just transferred out ├─ Price quote slightly stale └─ Network congestion OrderNotValid("reason") └─ Order invalid right now └─ Will check again next block └─ Examples: ├─ Quote expired ├─ Output below minimum └─ Order not yet valid PollNever("reason") └─ STOP polling this order permanently └─ Remove from active orders └─ Examples: ├─ User disabled notifications ├─ Permanent configuration error └─ Handler contract error POLLING FREQUENCY (Base): ───────────────────────── Block time: ~2 seconds Watch Tower poll: every block Batch window: ~30 seconds Typical orders in batch: 100-1000+ Expected time from intent → execution: 5-60 seconds (depending on batch phase and solver competition) ``` --- ## 6. Security Architecture: Fund Protection ``` ┌──────────────────────────────────────────────────────────────┐ │ SECURITY: How Funds Are Protected │ └──────────────────────────────────────────────────────────────┘ THE PROBLEM: Direct Settlement Authorization ─────────────────────────────────────────────── If user approved Settlement directly: USDC.approve(GPv2Settlement, 5000 USDC) Malicious solver could: └─ In pre-interaction hook: ├─ Call Settlement.transferFrom(user, attacker, 5000 USDC) └─ Steal all approved funds! THE SOLUTION: Three-Layer Authorization ────────────────────────────────────────── LAYER 1: Balancer Vault (Token Holder) │ └─ User approves Vault (not Settlement): USDC.approve(BalancerVault, uint256.max) └─ Vault stores user allowances separately LAYER 2: VaultRelayer (Authorized to Pull) │ └─ VaultRelayer is ONLY authorized address └─ VaultRelayer.transferFromAccounts(users, tokens, amounts, recipients) └─ Can ONLY transfer TO Settlement └─ No other transfers allowed LAYER 3: Settlement (Atomic Execution) │ └─ Settlement.settle() orchestrates: ├─ Calls VaultRelayer to pull input tokens │ └─ VaultRelayer pulls from Vault (user's allowance) │ └─ Transfers to specified recipient (adapter/settlement) │ ├─ Executes interactions (pre, during, post) │ └─ Can't make arbitrary token calls (can call whitelisted) │ └─ Calls VaultRelayer to return output tokens └─ VaultRelayer pulls from adapter/settlement └─ Returns to user ATTACK SURFACE: What Malicious Solver CAN'T Do ──────────────────────────────────────────────── ❌ Drain USDC allowance └─ Settlement doesn't have direct access └─ VaultRelayer only transfers to Settlement └─ Settlement immediately re-transfers to user ❌ Call arbitrary token functions └─ Settlement.settle() params validated └─ Only authorized interactions executed └─ Adapter is whitelisted ❌ Reenter settlement └─ ReentrancyGuard on Settlement └─ ReentrancyGuard on Adapter ❌ Access Vault directly └─ VaultRelayer is the ONLY authorized puller └─ Solver can't call Vault directly SAFETY VERIFICATION CHECKLIST: ──────────────────────────────── Before deployment: ☑ Users approve BalancerVault, NOT Settlement ☑ Adapter approved for finite amounts (not uint256.max? Or whitelisted handler?) ☑ Adapter onlySettlement modifier on execution functions ☑ Settlement has ReentrancyGuard ☑ Adapter has ReentrancyGuard ☑ VaultRelayer address set correctly ☑ Quote expiry enforced in handler ☑ Slippage protection (minOutputAmount) enforced ☑ No direct token transfers without Settlement mediation ``` --- ## 7. Watch Tower Polling Sequence ``` ┌──────────────────────────────────────────────────────────┐ │ Watch Tower Polling Sequence (Every Block) │ └──────────────────────────────────────────────────────────┘ TIME: Every ~2 seconds (Base block time) Watch Tower Service (background job) │ ├─ FOR EACH registered conditional order handler: │ │ 1. GET all orders in ComposableCoW registry │ └─ Orders: owner → handler → [order1, order2, ...] │ │ 2. FOR EACH order in registry: │ │ ┌─ Fetch staticInput (from registry) │ │ {direction, inputAmount, minOutputAmount, receiver, validityDuration} │ │ │ ├─ Fetch current quote from adapter │ │ offchainData = { │ │ quotedOutput: BondingCurveAdapter.quoteBuy(inputAmount), │ │ validTo: now + 30min │ │ } │ │ │ ├─ CALL: MycoConditionalOrder.getTradeableOrder( │ │ owner, │ │ watchTowerAddress, │ │ staticInput, │ │ offchainData │ │ ) │ │ │ ├─ Handler validates & returns GPv2Order.Data │ │ └─ May throw: │ │ ├─ PollTryNextBlock → Skip this block, retry next │ │ ├─ OrderNotValid → Skip, retry later │ │ ├─ PollNever → Remove from polling │ │ │ ├─ Order is valid → Register in active orders │ │ └─ Add to "orders to submit to solvers" │ │ │ └─ Submit to solver network │ └─ POST /orders with signed order │ └─ LOOP: Wait for next block (in parallel, other handlers) TIMELINE EXAMPLE (Real-world MycoFi order): ──────────────────────────────────────────── T+0s: User signs intent (private, off-chain) └─ Creates conditional order in ComposableCoW T+2s: Watch Tower polls ├─ Fetches static order params ├─ Calls adapter.quoteBuy() for live price ├─ Calls getTradeableOrder() → returns valid GPv2Order └─ Submits to solver network T+4s: (Poll cycle 2) ← Order already submitted, will appear in next batch T+30s: Batch closes └─ CoW collects all orders submitted in this batch window T+32s: Solvers receive batch ├─ Solver A begins solving ├─ Solver B begins solving └─ Solver C begins solving T+35s: Solver B finds best solution ├─ Surplus: $8 profit for execution └─ Submits GPv2Settlement.settle() tx T+37s: Transaction included in block ├─ VaultRelayer pulls 500 USDC ├─ BondingCurveAdapter.executeBuyForCoW() executes ├─ MYCO minted, transferred to user └─ USER RECEIVES 1000 MYCO! ✓ Total latency: ~37 seconds (quite reasonable!) WHAT IF QUOTE EXPIRES? ────────────────────── T+0s: Order created, quote valid until T+30s T+28s: Watch Tower polls ├─ quotedOutput fetched └─ Order submitted to solver T+30s: Quote expires (validTo timestamp) T+35s: Solver tries to execute ├─ Settlement calls getTradeableOrder() ├─ Handler checks: block.timestamp > quote.validTo? │ └─ YES! Expired ├─ Throws OrderNotValid("Quote expired") └─ Settlement fails, order not executed T+40s: Watch Tower polls again ├─ Calls adapter for NEW quote ├─ Returns fresh GPv2Order with new validTo └─ Resubmits to solvers RATE LIMITING: ─────────────── Watch Tower polling: 1 per block (limited by block time) BondingCurveAdapter.quoteBuy(): Call-per-poll (cheap, view function) CoW API submission: Subject to API rate limits (5 reqs/sec) For 1000 active conditional orders: └─ Polls all 1000 orders per block └─ ~1000 quote calls per block └─ Adapter quotes must be fast (view function, no state change) ``` --- ## 8. Custom Order Type Decision Tree ``` ┌────────────────────────────────────────────────────────────┐ │ Which Custom Order Type Should You Use? │ └────────────────────────────────────────────────────────────┘ Need to execute a bonding curve trade? │ ├─ YES: Is it a simple "buy now" or "sell now" order? │ │ │ ├─ YES → Use MycoConditionalOrder │ │ ├─ Static input: amount, min output │ │ ├─ Offchain input: live quote │ │ └─ Executes immediately when quote valid │ │ │ └─ NO: Do you need additional logic? │ │ │ ├─ Need to trade at specific price level? │ │ └─ Use MycoLimitOrder │ │ └─ Only execute if price >= limit │ │ │ ├─ Need to split trade over time? │ │ └─ Use MycoTWAPOrder │ │ └─ Divide into N parts, execute every T minutes │ │ │ ├─ Need recurring purchases (e.g., daily)? │ │ └─ Use MycoDCAOrder │ │ └─ Buy $X every interval │ │ │ └─ Need complex conditions? │ └─ Combine handlers with BaseConditionalOrder │ ├─ Pre-hook: Check oracle price │ ├─ Handler: Generate GPv2Order │ └─ Post-hook: Update tracking ┌─────────────────────┐ │ DECISION TABLE │ └─────────────────────┘ ┌──────────────────┬──────────────────┬─────────────────┐ │ Use Case │ Handler │ Key Features │ ├──────────────────┼──────────────────┼─────────────────┤ │ Buy now │ MycoConditional │ Live quote, │ │ Sell now │ Order │ slippage prot. │ ├──────────────────┼──────────────────┼─────────────────┤ │ Buy at $0.50 │ MycoLimitOrder │ Price trigger, │ │ (no higher) │ │ post-only opt │ ├──────────────────┼──────────────────┼─────────────────┤ │ Break buy into │ MycoTWAPOrder │ N parts, │ │ 10x over 1 hr │ │ T minute gaps │ ├──────────────────┼──────────────────┼─────────────────┤ │ $100 daily for │ MycoDCAOrder │ Recurring, │ │ 30 days │ │ date-based │ ├──────────────────┼──────────────────┼─────────────────┤ │ Buy if oracle │ Custom │ Compose │ │ says bullish │ + Pre-hook │ multiple │ └──────────────────┴──────────────────┴─────────────────┘ IMPLEMENTATION EFFORT: ────────────────────── MycoConditionalOrder: ├─ Effort: 1-2 days ├─ Complexity: Medium └─ Dependencies: BondingCurveAdapter, GPv2Order interfaces MycoLimitOrder: ├─ Effort: 2-3 days ├─ Complexity: Medium-High └─ Dependencies: Oracle/price feed, limit order tracking MycoTWAPOrder: ├─ Effort: 3-4 days ├─ Complexity: High └─ Dependencies: Timing logic, part completion tracking MycoDCAOrder: ├─ Effort: 2-3 days ├─ Complexity: Medium └─ Dependencies: Cron-like execution (Watch Tower polling) Custom + Hooks: ├─ Effort: 4+ days ├─ Complexity: Very High └─ Dependencies: Oracle, pre/post logic, state management ``` --- **Document Version:** 1.0 **Last Updated:** 2026-04-03 **Status:** Visual Reference Complete