A baited hook under a magnifying lens

FIELD NOTE 01 ·

Free to quote. Costly to swap.

报价免费,成交收钱

Three pools on Robinhood Chain showed near-zero fees in simulations, then charged up to 14% on real swaps. The people behind the hooks also supplied the liquidity—and collected the fees.

Robinhood Chain 上有三个池子,模拟时几乎不收手续费,真实交易却最高收 14%。写这些钩子的人,也在给池子提供流动性、收走手续费。

3
Confirmed bait pools
已确认的钓鱼池
Robinhood Chain
Robinhood Chain
6,419
USDG in estimated fees
USDG 估算手续费
1,003 swaps
1,003 笔交换
14%
Highest traced fee
轨迹中的最高费率
Near zero in simulation
模拟时接近零

What happened

Our solver checked a swap and got a green light. The real transaction failed with InsufficientOutput, after spending roughly one million gas. Five of six sampled failures had the same cause: the pool charged more than the simulation showed. (Replay evidence)

The trick was simple. A Uniswap V4 hook can set a pool's fee and read the transaction's gas price. In our zero-gas-price calls, these hooks returned a low fee. With a real transaction's gas price, they returned a higher one. Replaying the same transaction in both settings reproduced the mismatch. (Fee probes)

HookSimulation feeReal transaction fee
H1 · 0x0d83…e8800–0.20%14%
H2 · 0x62d4…40800%1.8947–4.90%, depending on the hop
H3 · 0x349c…80c4 · control0%0%

A router that trusts the cheap quote may choose the pool. The higher fee then reduces the output. If the user's minimum is no longer met, the swap reverts; if it still passes, the swap completes at the worse price.

The same hooks return different fees when the gas price changes.
The same hooks return different fees when the gas price changes.

source: fee_step.csv

How much it cost

In the September 4 snapshot, three confirmed bait pools processed 1,003 swaps with about 238,334 USDG in USDG-side volume. Their estimated fees totalled 6,419 USDG. These are historical measurements, not live totals. (Pool totals)

The estimate multiplies each swap's USDG-side amount by its recorded fee rate. It is not a measurement of fees withdrawn. The 17 distinct callers are contract addresses, often routers—not a count of individual users.

We found the pools in a census of 21,661 dynamic-fee pools and 2,203 hooks. Only four incident-related hooks were tested on Robinhood Chain; three showed the mismatch. Untested hooks have no verdict. (Browse the census)

Estimated fees accumulated across the three bait pools.
Estimated fees accumulated across the three bait pools.

source: cumulative_bait_fees.csv

Who collected the fees

The extra charge went to liquidity providers through normal Uniswap LP fee accounting. We found no hook claim transfers in the 993 swap transactions checked. (Accounting checks, mechanism evidence)

Each pool's observed liquidity traced to one owner. Across the three pools there were two owners: 0x22d5…2527 and 0x9924…9699. For two hooks, deployment and pool creation traced to those same owners. The third hook's deployment could not be established. (Ownership evidence)

USDG withdrawals are visible, but they mix deposited capital with fees. We cannot label the entire withdrawal as profit. (Withdrawal records)

It also appeared on other chains

Ethereum had one matching hook, with roughly 136 USDC in observed volume. Arbitrum had a cluster of six hooks that quoted 0.3% at zero gas price; higher-gas-price probes returned 50–53.3%. Some results depended on the probe context. (Cross-chain results, hook details)

We found no mismatch among the hooks we could test on Unichain, Optimism, or Avalanche. Some calls failed or pools lacked liquidity, so those hooks remain undecided. Base and Polygon scans were incomplete in this snapshot; BNB was not scanned.

This technique has been reported before. Our contribution is tracing this cluster from the quote mismatch to the fee records and liquidity owners. Prior reports and sources give the background.

What this means for users

A quote is an estimate. The minimum output is the protection. A direct swap may complete at a worse price if it still meets that minimum. Otherwise it reverts, and whoever submitted the transaction pays the gas.

With an intent, the user signs an order with a minimum output and an expiry. A solver submits the fill. If settlement cannot deliver the signed minimum, the fill reverts and the solver pays that transaction's gas. This protects the agreed floor; it does not guarantee the best available price.

For solvers, the practical fix is to simulate with a realistic gas price and caller, then check again before submitting. A zero-gas-price quote is not enough. (Replay evidence)

Check the evidence

Open the pool explorer to compare quoted and real-context fees, inspect individual pools, or run a fresh probe. The article and census use a fixed snapshot; a live probe may differ.

The research notes contain the method and limits. Download the fee data, swap records, or cross-chain summary to check the numbers.


发生了什么

我们的 solver 先模拟了一笔交换,结果显示可以成交。真正上链后,却因 InsufficientOutput 失败,白花了约一百万 gas。抽查的六笔失败交易中,五笔都是同一个原因:实际手续费比模拟时高。(重放证据

做法并不复杂。Uniswap V4 的钩子既能设置池子的手续费,也能读取交易的 gas price。我们把 gas price 设为零时,它报低费率;换成真实交易的 gas price,就报高费率。同一笔交易,用这两种设置重放,结果就不一样。(费率探针

钩子模拟费率真实交易费率
H1 · 0x0d83…e8800–0.20%14%
H2 · 0x62d4…40800%1.8947–4.90%,取决于交易步骤
H3 · 0x349c…80c4 · 对照组0%0%

路由器以为这个池子便宜,就可能选它。实际手续费变高后,用户拿到的币会变少:低于最低接收量,交易回滚;仍然达标,就按更差的价格成交。

同一个钩子,换个 gas price,手续费就变了。
同一个钩子,换个 gas price,手续费就变了。

source: fee_step.csv

收了多少钱

截至 9 月 4 日的快照,三个已确认的钓鱼池共处理 1,003 笔交换,USDG 侧交易量约 238,334 USDG,估算手续费共 6,419 USDG。这是当时的统计,不是实时数字。(池子统计

估算方法是:逐笔用 USDG 侧交易额乘以记录的费率,再加总。这不等于已经提走的手续费。17 个不同调用地址也不代表 17 个用户,其中不少是路由合约。

普查共找到 21,661 个动态费率池2,203 个钩子。Robinhood Chain 上只测试了与故障相关的四个钩子,三个有问题。其余没测过,不能算安全。(查看普查

三个钓鱼池累计产生的估算手续费。
三个钓鱼池累计产生的估算手续费。

source: cumulative_bait_fees.csv

钱进了谁的口袋

额外收费通过 Uniswap 正常的 LP 手续费机制,记给流动性提供者。检查的 993 笔交换交易中,没有发现钩子的 claim 转账。(记账检查机制证据

每个池子里观察到的流动性,都能追到一个所有者。三个池子共涉及两个地址:0x22d5…25270x9924…9699。其中两个钩子的部署者、池子创建者也是这些所有者。第三个钩子的部署者没有查明。(所有权证据

链上能看到 USDG 被提走,但本金和手续费混在一起,不能把整笔提款都说成利润。(提款记录

别的链也有

Ethereum 找到一个类似钩子,观察到的交易量约 136 USDC。Arbitrum 有六个同类钩子,gas price 为零时报 0.3%;提高 gas price 后,探针得到 50–53.3%。部分结果取决于测试时的交易环境。(跨链结果钩子明细

Unichain、Optimism、Avalanche 上能完成测试的钩子,没有发现费率差异。但有些调用失败,有些池子没有流动性,这些都不能下结论。快照中的 Base、Polygon 扫描尚未完成,BNB 未扫描。

这种手法以前就有人报告过。我们补充的是这组池子的完整证据:从报价差异,到手续费记录,再到流动性所有者。背景见已有研究

对用户有什么影响

报价只是估算,最低接收量才是底线。 直接交换时,只要还满足这个底线,就可能按更差的价格成交;不满足就回滚,由提交交易的人付 gas。

用 intent 时,用户签的是带最低接收量和有效期的订单,solver 负责提交成交。结算达不到签名约定的最低量,就回滚,这笔交易的 gas 由 solver 承担。它守住的是约定底线,不保证拿到市场最优价。

对 solver 来说,直接的改进是:用接近真实交易的 gas price 和调用者做模拟,提交前再检查。只看 gas price 为零的报价不够。(重放证据

自己查一遍

打开池子浏览器,可以比较两种交易环境的费率、查看单个池子,或重新运行探针。文章和普查用的是固定快照,实时测试结果可能不同。

方法和限制见研究记录。也可以下载费率数据交换记录跨链汇总,自己核对。

Look inside the pools.

打开池子看一眼。

Search 21,661 pools. Compare fees and inspect the evidence behind each verdict.

搜索 21,661 个池子,比较费率,查看每个判断背后的证据。

Open pool explorer ↗打开池子浏览器 ↗

Method & data

方法和数据

Research notes ↗

For a fresh check, open a pool and run its live probe. Each pool also includes commands you can copy into a terminal.

要重新验证,打开一个池子,运行实时探针。每个池子也提供了可复制到终端的命令。