You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Uniswap v4 hook that implements trustless, keeper-free on-chain limit orders for ETH/USDC via single-tick concentrated liquidity. The AMM fills orders natively as price crosses the target tick — no off-chain bots or keepers required.
User calls placeOrder(poolKey, amountIn, targetTick, zeroForOne) — tokens are transferred to the hook, which adds concentrated liquidity at [targetTick, targetTick + tickSpacing] via poolManager.modifyLiquidity
Swap occurs in the pool — the AMM's own x·y=k logic converts the single-sided position as price crosses the target tick
afterSwap detects which ticks were crossed, removes filled liquidity positions, and records claimable output in ClaimManager
User calls claim(orderId, poolKey) at any time to withdraw the converted output tokens
Cancel path — unfilled orders can be cancelled at any time via cancelOrder(orderId, poolKey), which removes the liquidity position and returns input tokens
afterSwap short-circuits when sender == address(this) — prevents re-entrant fill loops
CEI enforced in all state-mutating functions — state written before any external calls
MAX_ORDERS_PER_TICK = 50 caps the afterSwap loop — prevents unbounded gas DoS
order.claimed flag set atomically before token transfer — prevents double-spend
require(!order.filled) guard on cancelOrder — prevents cancellation of filled orders
All poolManager.unlock callbacks validate msg.sender == address(poolManager)
Hook holds no permanent token balances — all tokens are in the PoolManager's custody as liquidity
Known Limitations
Orders beyond MAX_ORDERS_PER_TICK are not auto-filled by afterSwap. A forceFill escape hatch is planned for a future release.
Single-tick liquidity positions carry basis-point rounding at extreme tick values — enforce a minimum order size at the UI layer.
zeroForOne = true orders must be placed above the current tick; zeroForOne = false orders must be placed below. Validation is the caller's responsibility.