An AMM-backed RFQ quoter for UniswapX on Base
Abstract
We quote UniswapX RFQ requests on Base from on-chain AMM liquidity, and fill the orders we win atomically through the reactor callback. The swapper's input pays for the route, so we hold no inventory. We evaluate this two ways. A live shadow test replays fresh Base orders to our quote server and executes our quoted route at the block where the winning market maker filled. A retrospective simulation does the same over 300 maker-won orders. At a 5 bps margin, every would-be win executed in both (43 of 43, and 57 of 57). Our routes land a median of about 2 bps from the winning quotes, so we compete mainly on orders under $1,000; large orders stay with inventory-backed market makers. We also describe how the fill engine handles failures, the sources of failed fills we found and removed, and the limits of this evidence.
1. System
An order passes through five steps; we operate steps 2 and 4.
- Uniswap sends an RFQ price request.
- Our quote server re-prices the pair's pre-loaded AMM routes on the latest block (one call, raced across several nodes) and answers with the best route's output, less a 5 bps margin and the fill's gas, within a 350 ms deadline. Routes cover Uniswap v2/v3, hookless v4, Aerodrome, PancakeSwap and others. It declines with an empty 204 when it can't quote, including whenever the fill engine can't take another fill.
- Uniswap's auction picks the best quote. The winner has an exclusivity window of about two blocks.
- Our fill engine handles each won order on its own thread. It simulates the exact transaction on the in-progress block, records it with its worst-case gas reserved, and only then sends it. It replaces a fill that gets no receipt within the window, and after a restart it resumes anything still unsettled.
- The UniswapX reactor calls back our fill contract, which swaps the swapper's input along the route and pays the outputs. It reverts unless the route itself produces everything the reactor takes, so a fill can never draw on funds the contract already holds. Our worst case on a failed fill is its gas.
2. Method
We never use route quotes as evidence. Every number below comes from executing the fill through the real UniswapX reactor at a real gas price. Our executor's code is placed at the winning maker's address, so the reactor demands exactly what the maker was owed. When our route falls short, the revert reports how much it produced.
Live shadow test. Fresh Base orders seen by our collector (within 5 s) are sent to our quote server as the RFQ they came from. After the winning maker fills, we execute our quoted route at that block. A quote counts only if our response completed before the fill; 24 later ones were excluded.
Retrospective simulation. We took 300 randomly sampled Base orders won by existing quoters. For each, our server's rules are applied at the block where the order was created: routes ranked by their quotes, hooked v4 pools skipped, gas set aside with the server's fixed allowance. The chosen route is then executed at the fill block.
3. Results
The margin is the main lever. A lower margin wins more orders, but leaves less room for the price to move between quote and fill.
| Margin | Would-be wins | Executed | Fill rate | Wins/day | Est. P&L/day |
|---|---|---|---|---|---|
| 0 bps | 73 | 61 | 84% | 121 | $0 |
| 2 bps | 59 | 56 | 95% | 98 | $31 |
| 5 bps (ours) | 57 | 57 | 100% | 95 | $79 |
| 10 bps | 52 | 52 | 100% | 86 | $52 |
| 20 bps | 31 | 31 | 100% | 51 | $28 |
Table 1. Retrospective simulation over 300 maker-won Base orders. Per-day figures are scaled estimates, not earnings. About 72% of the profit at 5 bps comes from its five largest wins.
Figure 1. The margin trade-off (Table 1). Below 5 bps, wins start to fade (the route no longer covers the quote at fill time); above 5 bps, fewer orders are won. Our setting is the dark bar.
Live shadow test. Of 206 orders judged, our quote at 5 bps would have beaten the winning quote on 43, and all 43 routes executed for the full amount (37 of 37 at 10 bps). Eleven orders couldn't be judged, because no quoter won them or nobody filled them.
| Order size | Orders | Our quote would have won | Share |
|---|---|---|---|
| under $1,000 | 34 | 23 | 68% |
| $1,000 – $5,000 | 94 | 10 | 11% |
| $5,000 – $20,000 | 56 | 0 | 0% |
| over $20,000 | 9 | 0 | 0% |
Table 2. Share of orders our quote would have won, by order size (live shadow test, 10 bps margin, 19 September). How much of each day's flow is small orders largely decides our volume.
Response time. Measured end to end through the public URL from our server (US East), in runs of about 100 requests: p50 45–100 ms and p99 180–366 ms, with none over the 500 ms budget. The slowest run was taken during an external review. Server-side processing: p50 about 40 ms, p99 about 260 ms.
4. Findings
4.1 A quote is not an execution: hooked v4 pools
Some Uniswap v4 pools have hooks that quote normally but behave differently on a real swap. On Base, a cbBTC/USDC pool's hook rejects swaps from contracts like ours; it accounted for every miss in an earlier run of our simulation. On Robinhood Chain we found hooks that quote a good price but charge about 2.4% on real swaps, even through Uniswap's own router. We now skip every v4 pool with a hook. None of the roughly 1,000 quotes served before that change used one.
4.2 Where on-chain routes can compete
Our raw route lands a median of about 2 bps from the winning quote (95 of 126 orders within ±10 bps), so the margin decides most outcomes. Splitting orders across two routes gained a median of 0.1 bps over the same orders. On large orders, inventory-backed makers quote better than an AMM route can at that size.
4.3 Racing the open auction does not pay
Before quoting, we tested racing to fill orders once their exclusivity ends. Simulated at the real gas price, the edge on Base was about zero. Apparent profits on Robinhood Chain came from the hooks in 4.1.
5. Failure handling
A fade, winning an order and then not filling it, is the costly failure for a quoter. The design avoids or bounds it as follows:
- We quote only when we can fill. The quote server checks the fill engine's readiness for each order feed (production or beta), and each binding quote must claim a spare fill slot, recorded on disk.
- The simulated transaction is the one we send: same sender, calldata, gas limit and fees. A replacement is simulated on the latest sealed block.
- A stuck or lost fill is replaced (same nonce, higher fees, tip capped) while the window is open, and re-broadcast after it. A nonce still unused after that is cleared with a cancel, so every nonce ends in a receipt.
- Gas is a hard limit. Each transaction's worst-case fee is reserved before broadcast and released only on a receipt, across crashes and restarts.
- The contract never draws on held funds. It settles multi-output orders in one token with a single approval.
We tested each of these on a local Base fork with real orders: an exclusive fill, a replaced fill, a crash with a lost transaction, a two-output order, and a route that falls short. The design went through five rounds of external review before we applied.
6. Limitations
- All results are simulated or shadow measurements. We have not yet served a real Uniswap RFQ or made a live fill; the beta will be the first real test.
- We only see the winning quote. Beating it should win the request, but Uniswap may weigh other factors, and we cannot see RFQs that never became orders.
- The samples are small and the profit is concentrated: at 5 bps, five wins carry about 72% of the simulated profit. Zero observed fades does not prove that the fade rate is negligible.
- The simulation is not an exact replay. It uses today's pool index and discovers routes fresh at the quote block, whereas our server re-prices routes found up to two hours earlier. The order's creation block stands in for the moment of the RFQ.
- The 30 s lifetime of a quote commitment is an assumption; we log it on every win to check. The 5 bps margin is a monitored starting point, not a proven optimum.
7. Deployment and contact
| Chain | Base (8453) |
|---|---|
| Fill contract | 0x01c2a3482a4b51da150681dd74a13336e73626ad |
| Orders quoted | Exact-input Dutch V3. Other chains and exact-output requests get a 204. |
| Margin | 5 bps, plus the fill's gas |
| Response deadline | 350 ms internally (Uniswap's limit is 500 ms) |
| Contact | [email protected] |
Methods and scripts are available on request.