Architecture Overview
Geode is a single smart contract (GeodeHook.sol, ~1,190 lines) that implements the Uniswap v4 IHooks interface. It intercepts pool activity via the beforeSwap callback and provides batch settlement through a separate entry point.
Hook flags: BEFORE_SWAP | BEFORE_SWAP_RETURNS_DELTA
The hook has four immutables set at deployment:
| Immutable | Purpose |
|---|---|
poolManager | Uniswap v4 PoolManager — the core swap and liquidity engine |
permit2 | Canonical Permit2 contract — handles gasless token pulls |
protocolTreasury | Receives ~1/3 of direct swap fees |
factory | GeodeFactory address — authorized to register launch pools |
Two Swap Paths
Every swap on a Geode-configured pool takes one of two paths:Path 1: Direct Swap
A swap that passes through the hook’sbeforeSwap callback. Behavior depends on pool type:
Curve pools (launched tokens): The hook computes the bonding curve output and returns the full delta via BEFORE_SWAP_RETURNS_DELTA. The v4 pool has no liquidity — the hook IS the market maker. A 0.3% fee is split three ways:
- ~1/3 → curve ETH reserve (permanently increases the floor price)
- ~1/3 → protocol treasury
- ~1/3 → per-pool surplus (funds settler gas reimbursement)
- ~1/3 → protocol treasury (via
poolManager.take()) - ~2/3 → per-pool surplus (held by the hook as ERC20 balance)
When the hook routes residual flow through the AMM during batch settlement (
sender == address(this)), no direct swap fee is charged. Only external swappers pay.Path 2: Intent Batch Settlement
The core mechanism. A permissionless settler callsgeodeSettleBatch() with arrays of signed buy and sell intents.
Settlement Flow
The complete call flow from entry to final token distribution:Step-by-Step Breakdown
Batch Validation
The settler submits arrays of buy intents, sell intents, and their Permit2 signatures. The hook checks:
- Pool is configured (
poolInitialized[poolId]) - Enough blocks have passed since last settlement (
batchInterval) - All intents target the correct pool (
poolIdbinding) - All deadlines are in the future
Clearing Price Computation
The clearing price equals the AMM spot price (v1 simplification). For each intent:
- Buy intent fills if its implicit limit price ≥ clearing price
- Limit price =
amountIn × Q128 / minAmountOut
- Limit price =
- Sell intent fills if its implicit limit price ≤ clearing price
- Limit price =
minAmountOut × Q128 / amountIn
- Limit price =
Fee Deduction
Settlement fees are deducted from each filled intent’s input before any routing:The fee amount stays in the PoolManager as unrouted balance for the settler to claim.
Internal Matching
The algorithm determines how much buy and sell flow can cross internally:Internally matched flow executes at the clearing price with zero AMM spread.
Residual Routing
Unmatched flow routes through the appropriate venue:
- Standard pools:
poolManager.swap()against the AMM - Curve pools: Dispense/absorb via the bonding curve (tokens from/to hook reserve)
Token Distribution
Output tokens are distributed pro-rata to filled intents based on each intent’s
amountIn:- Buyers receive currency1 (from internal match + residual output)
- Sellers receive currency0 (from internal match + residual output)
Flash Accounting
Settlement happens inside Uniswap v4’sunlock() callback — the “flash accounting” context. During this window:
- The PoolManager tracks credits and debits for each currency
- Token pulls (via Permit2) create positive deltas
- Token distributions (via
take()) create negative deltas - AMM swaps create paired deltas
- All deltas must net to zero when
unlock()returns
Direct Curve Swap Mechanics
For curve pools,beforeSwap handles the entire swap through the bonding curve:
- The hook computes the curve output using
ConstantProductCurveLib.tokensForEth()(buy) orethReturnOnSell()(sell) - A 0.3% fee is deducted from the input and split three ways (reserve, treasury, surplus)
- For buys: ETH goes to
launchEthReserve, tokens are dispensed fromlaunchTokenReserve - For sells: Tokens are absorbed back, ETH is returned from reserve
- The hook returns the full delta via
BeforeSwapDelta, so the AMM processes nothing
Direct Swap Fee Capture (Standard Pools)
When a non-intent user swaps through a standard Geode-configured pool, the hook charges a fee using paired delta accounting:poolManager.take(unspecified, hook, feeAmount)— creates a negative hook deltaBeforeSwapDelta(0, +feeAmount)— creates a positive hook delta charged to the swapper