smart-contracts/backlog/docs/CoW_Architecture_Diagrams.md

29 KiB

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