Adapter Architecture for Scanning 24 DEXs

🌐 한국어

Building a bot that scans multiple DEXs hits one problem immediately: each DEX swaps differently. UniswapV2-like constant product, V3's concentrated liquidity, Solidly's ve(3,3), DODO's PMM... Quote functions differ. Swap signatures differ. Everything is bespoke. Handle 24 DEXs and your bot's core quickly becomes unmaintainable if it has to understand each one.

The solution is classical: the adapter pattern.

Hide Differences Behind One Interface

Show the bot core only one interface. Two operations suffice: "Given X input, how much Y?" and "Actually swap for me."

interface IDEXAdapter {

    // Quote: how much amountOut for amountIn

    function getAmountOut(

        address pool,

        address tokenIn,

        uint256 amountIn

    ) external view returns (uint256 amountOut);

    // Execute: actual swap

    function swap(

        address pool,

        address tokenIn,

        uint256 amountIn,

        address to

    ) external returns (uint256 amountOut);

}

Each protocol implements its own adapter:

  • UniV2Adapterx * y = k constant product; quotes are cheap on-chain
  • V3Adapter — concentrated liquidity, tick math; must call Quoter contract
  • SolidlyAdapter — stable/volatile pool distinction, ve(3,3) fee structure
  • DODOAdapter — PMM model, oracle-based pricing curve; completely different math

The core doesn't care. It just calls adapter.getAmountOut(...).

Scale via Adapter Registration

Adding a DEX must not touch core logic. So register adapters at runtime. Assign each an ID; the router looks them up by ID.

mapping(uint8 => address) public adapters;

function registerAdapter(uint8 id, address adapter) external onlyOwner {

    adapters[id] = adapter;

}

New DEX appears? Deploy one adapter, call registerAdapter(24, newAdapter). Core untouched. Off-chain, add the name to adapterNames and it joins the scan roster. This open-closed structure let us grow to 24 adapters while core logic stayed frozen.

Liquidity Check — Filter Traps

This is the most critical production lesson. A quote that looks like "10% arb" might be a mirage if the pool has no liquidity. Slippage eats the profit, or the swap fails outright.

Validate liquidity before putting a path in the execution candidate list. Quote well enough, but actual execution size matters more. Each protocol measures liquidity differently, so this check lives in the adapter layer too.

// Concept: is swap size reasonable vs pool reserves

func hasEnoughLiquidity(reserveIn, amountIn *big.Int) bool {

    // Entry size vs pool reserves. If entry > threshold, slippage is excessive

    // (V2 = reserve, V3 = active tick liquidity, Stable = curve segment)

    threshold := new(big.Int).Div(reserveIn, big.NewInt(50)) // e.g., 2% of pool

    return amountIn.Cmp(threshold) <= 0

}

V2 reserves, V3 active tick liquidity, stable curves—each judges differently. The key insight: a good quote and actual execution capacity are separate. This gate alone filters most wasted transactions.

Summary

  • Hide DEX differences behind one adapter interface
  • Protocol-specific adapters (UniV2 / V3 / Solidly / PMM) each implement that interface
  • Adapter registration lets you add DEXs without touching core logic
  • Liquidity check pre-filters traps that quote well but can't execute

The adapter pattern is textbook, but its power emerges when you face 24 heterogeneous on-chain protocols. Extensibility without core modification—that's what lets a bot survive long-term.

This is educational material, not investment advice. DeFi automation carries risk of total loss. You alone are responsible for outcomes.

댓글

이 블로그의 인기 게시물

한국투자증권 KIS API로 실시간 시세 받기 (WebSocket 실전)

파이썬으로 업비트 API 연동하기 — 시세 조회부터 주문까지 기초

Go로 자동매매 신호봇 프레임워크 설계하기