Protocol · v0.3 · live on Robinhood Chain · paid in USDG
AMLIL
A hook aggregator for Uniswap v4. Hooks are indexed, scored and composed into stacks. Every call to a hook, a route or a GPU job is paid per request with x402. Nothing to sign up for, nothing to invoice.
Overview
Uniswap v4 lets a pool run code before and after a swap, around the fee, and on liquidity changes. These hooks are deployed independently, with no shared index and no way to compose them per swap. AMLIL sits in front of them:
- Registry — every hook is indexed from chain, scored on fill quality, latency and revert rate, and exposed over HTTP.
- Router — a swap request returns the best stack for that moment: which hooks, in which order, with what expected fee.
- x402 — each call costs a few thousandths of a USDC, settled with the HTTP 402 flow. Agents pay the same way humans do.
- GPU lane — hooks that need pricing models, simulations or inference run on GPU nodes off-chain, with the result attested on-chain.
x402 flow
Every paid endpoint answers an unpaid request with 402 Payment Required and a PAYMENT-REQUIRED header (x402 v2). The client signs a USDG TransferWithAuthorization (EIP-3009, domain Global Dollar v1), retries with PAYMENT-SIGNATURE, and gets the result. One extra round-trip, no API key, no gas for the payer.
→ POST /api/route
← 402 Payment Required
PAYMENT-REQUIRED: base64({
"x402Version": 2,
"accepts": [{ "scheme": "exact", "network": "eip155:4663", "amount": "2000",
"asset": "0x5fc5…d168", "payTo": "0xcbEb…c300", "maxTimeoutSeconds": 120,
"extra": { "name": "Global Dollar", "version": "1" } }]
})
→ POST /api/route
PAYMENT-SIGNATURE: base64(signed EIP-3009 authorization)
← 200 OK
PAYMENT-RESPONSE: base64({ "success": true, "transaction": "0x…", "payer": "0x…" })
The facilitator verifies the signature and balance, and submits transferWithAuthorization from its own gas wallet; the server only releases the result once verification passes, and settles after the response. On Robinhood Chain the facilitator is the AMLIL node itself. Settled calls show up in /api/ledger.
Hook registry
Hooks are grouped into four kinds: fee (dynamic fees, rebates), order (limit, TWAMM, stop-loss), guard (MEV, JIT, allowlists) and oracle (TWAP, volatility, impact). Each entry carries a score from 0 to 100 recomputed every block.
GET /api/hooks
← 402 Payment Required · $0.001 (unpaid: a 6-hook preview)
← 200 OK · paid
{
"hooks": [{
"address": "0x0469…50c0", "kind": "fee", "dynamicFee": true,
"flags": ["beforeInitialize", "beforeSwap", "afterSwap"],
"pools": 168, "pairs": ["ETH/USDG", "ETH/HOOKLINE"], "score": 84, "price": "580"
}],
"stats": { "pools": 6611, "hookedPools": 2765, "hooks": 337, "spanBlocks": 1000000 }
}
Hooks are discovered from PoolManager.Initialize events. The permission flags are decoded from the low 14 bits of the hook address, which is how v4 encodes them. The kind is derived from the flags (dynamic fee → fee, swap return-delta → order, beforeSwap only → guard, afterSwap → oracle). The score blends adoption (pools, distinct pairs), breadth (flags), blue-chip exposure and recency. Price per call is set per kind and flag count.
Aggregator hooks
Since October 2026, Uniswap v4 is an aggregator: Aggregator Hooks wrap external liquidity sources (Curve, Fluid, PancakeSwap and others) so a router sees them as v4 pools, live on Ethereum, Base and Robinhood Chain. Their addresses are not published. AMLIL finds them by behaviour.
- Candidates — hooks whose address carries
beforeSwapandbeforeSwapReturnDelta: the permissions a hook needs to take over the swap and fill it itself. - Evidence — up to three of the hook's pools are read through
StateView.getLiquidity. If they hold no native v4 liquidity, the Quoter is asked for a 0.001 swap on one of them. A hook that still returns output is filling from somewhere else. - Result — the hook is tagged
aggregator, its pools count as external liquidity, and every quote leg carries asource:native,hookedorexternal.
{
"address": "0x4eb1…e0cc", "kind": "aggregator", "pools": 32,
"flags": ["beforeSwap", "afterSwap", "beforeSwapReturnDelta", "afterSwapReturnDelta"],
"external": { "sampled": 3, "emptyPools": 2, "quotedPool": "0x87d5…187f", "quotedOut": "335605303374815733225" }
}
The heuristic catches every hook that fills swaps without native liquidity: Uniswap's own Aggregator Hooks, but also launchpad curves and RFQ bridges. That is the point for a router: where the fill comes from matters less than whether it wins. On Robinhood Chain the first pass found 13 such hooks behind 134 pools.
Routing
A route request names the pair, the amount and optional constraints. The router returns an ordered stack. Hooks you pin are kept; the rest is chosen from the registry.
POST /api/route · $0.002
{ "sell": "ETH", "buy": "USDG", "amount": "1", "pin": ["0x4e34…a544"] }
← 200 OK · paid
{
"candidates": 4, "quoted": 4,
"legs": [{ "feeLabel": "0.05%", "hooks": "0x0000…0000", "amountOutFormatted": "2375.431841", "liquidity": "45571513…", "gasEstimate": "63310", "why": "plain pool, no hook" }],
"best": { … }
}
Candidates are every pool for the pair in the index, pinned hooks first, then aggregator hooks, then the rest by score, plus the canonical hookless pools (derived from the pool key and kept when they hold liquidity). Each candidate is quoted with V4Quoter.quoteExactInputSingle and ranked by output. Each leg says where the fill comes from.
Each leg also carries a ready-to-send transaction for the Uniswap Universal Router: one V4_SWAP command with SWAP_EXACT_IN_SINGLE on that pool, SETTLE_ALL for the input and TAKE_ALL for the output, a 1% slippage floor and a 20-minute deadline. Native ETH goes in as value; an ERC-20 input needs the token approved for Permit2 and Permit2 approved for the router, which the console does once. The console sends the transaction from your wallet and follows the receipt through /api/tx/:hash.
"swap": {
"to": "0x8876…0904", // Universal Router on Robinhood Chain
"data": "0x3593564c…", // execute(commands, inputs, deadline)
"value": "500000000000000", // wei, when the input is ETH
"minOut": "1188059", "deadline": 1792760000, "needsApproval": false
}
Hookline
AMLIL is built on Hookline, the swap aggregator for Robinhood Chain. Every paid route also asks Hookline for its best price: it quotes every path across Uniswap v4, the Aggregator Hook pools and the other venues it maps, splits the order where it moves the price least, and simulates the route before you sign. When Hookline beats the single best pool, the console says so and offers it first.
- Execution — a fresh Hookline quote is taken for your wallet right before building (quotes live 60 seconds). An
approvestep is sent once per token, apermitstep is a gasless Permit2 signature, then/api/hookline/swapreturns the simulated Universal Router transaction (0x204f…0498, chain 4663) that your wallet sends. The swap is tracked on Hookline and gets a Proof of Savings link. - Private — tick "Private" and the swap runs on Hookline's private mode: best price, then the received tokens are delivered to a fresh wallet through Houdini Swap, with nothing on-chain linking the two wallets. This is a web flow on hookline.trade, 2–3% fee, 5–45 minutes, from about $25.
- Fees — Hookline takes 0.15% per swap, waived for wallets holding 1,000,000 HOOKLINE. AMLIL's own x402 call stays 0.002 USDG.
GPU lane
Some hook logic doesn't fit in a block: fitting a volatility surface, running a Monte-Carlo impact simulation, or inferring a fee from recent order flow. The GPU lane runs these jobs on registered nodes and returns a result with an attestation the hook verifies on-chain.
POST /api/jobs · $0.005
{ "kind": "impact-sim", "params": { "paths": 20000 } }
← 402 → 200 OK · paid
{
"jobId": "j_e9e9f29f", "node": { "id": "node-…", "accelerator": "cpu" }, "ms": 51,
"input": { "poolId": "0x…", "sqrtPriceX96": "…", "liquidity": "…", "paths": 20000 },
"output": { "buckets": [{ "sizeRel": 0.001, "meanImpact": 0.0019 }, …] },
"resultHash": "0x1528…", "signature": "0x…", "signer": "0xcbEb…c300"
}
The node reads the pool's live sqrtPriceX96 and liquidity from StateView, runs the simulation, hashes the result and signs the hash with its key. Anyone can verify the signature against the node address, which the console does in the browser.
List a hook
Deploy your hook, then register it with its price per call. From there it's indexed, scored and available to the router. You're paid every time it runs.
# Deploy a v4 hook on Base: its address encodes the permission flags.
# Initialize a pool with it; the registry picks it up on the next index pass.
# Pricing today is set by kind and flag count; per-hook payTo is next.
What runs today: the registry indexes Uniswap v4 pool initializations on Robinhood Chain (chain 4663) over the last million blocks, about a day, through the public RPC. Quotes hit the v4 Quoter on Robinhood Chain. Payments are USDG (Global Dollar) on Robinhood Chain, settled by a facilitator AMLIL hosts itself, because the public x402.org facilitator does not cover the chain; the same facilitator is exposed at /api/facilitator/* for other x402 servers on the chain. Compute jobs run on CPU nodes and are signed with the node key; GPU nodes are the next step. Base (USDC) remains available through CHAIN and X402_NETWORK.
Fee buyback and burn
Chosen by the token holders in the launch poll. The node's revenue, every x402 call settled in USDG and, once active, the Hookline partner fee, is swapped into $AMLIL and sent to the dead address 0x000000000000000000000000000000000000dEaD, where nothing can move it again.
- Source: the USDG that lands on the node's pay-to address. The creator fee paid by the launchpad on $AMLIL volume is a separate stream and is not part of the buyback.
- Trigger: every day at 12:00 UTC, when at least 5 USDG has accrued (below that, the run is skipped and the revenue waits). The node keeps 0.005 ETH for the gas of its settlements and burns everything else.
- Path: USDG to ETH through the router, ETH to $AMLIL through the launchpad pool, then an ERC-20 transfer of the full amount to the dead address. Three transactions, 3% slippage cap.
- Proof:
GET /api/burnsrebuilds the ledger from the token's Transfer logs (node to dead address) on every call, with the hash of each burn, the total, and the USDG waiting for the next one. The console's Ledger tab and the story's finale show the same numbers.
curl https://amlil.app/api/burns
# { token, burnAddress, policy, totalBurned, burns, pendingUsdg, ready, rows: [{ tx, block, at, amountFormatted }] }
For agents
AMLIL is built to be called by software that pays as it goes. Two files describe everything: openapi.json (OpenAPI 3.1, each paid endpoint carries an x-x402 block with price, asset and flow) and llms.txt (the same in plain text for language models).
Paying from a script takes a wallet with USDG on Robinhood Chain and the official x402 client. USDG is not a default asset for the client, so it has to be allowed in the spend controls:
import { wrapFetchWithPayment } from "@x402/fetch"
import { x402Client } from "@x402/core/client"
import { ExactEvmScheme } from "@x402/evm"
import { privateKeyToAccount } from "viem/accounts"
const account = privateKeyToAccount(process.env.KEY)
const client = new x402Client()
.register("eip155:*", new ExactEvmScheme(account))
.setSpendControls({ allowedAssets: [{ network: "eip155:4663", asset: "0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168", maxAmountPerPayment: "1000000" }] })
const pay = wrapFetchWithPayment(fetch, client)
const route = await pay("https://amlil.app/api/route", {
method: "POST", headers: { "content-type": "application/json" },
body: JSON.stringify({ sell: "ETH", buy: "USDG", amount: "0.01", taker: account.address }),
}).then(r => r.json())
// route.best.swap = { to, data, value } for the Universal Router; route.hookline for the aggregated route
The 402 round-trip, the EIP-3009 signature and the retry are handled by the client. The response carries the settlement transaction in PAYMENT-RESPONSE. A quote costs 0.002 USDG; a swap is then one transaction from the same key.