Takers pay significantly higher fees than expected due to borrow amounts being split across segments
| Title | Takers pay significantly higher fees than expected due to borrow amounts being split across segments |
|---|---|
| Reward | $4432, Unique |
| Contest | Ammplify - 2 Sep 2025 on Sherlock |
| Author | panprog |
| Context | Fee Logic |
When takers pay their fees, the taker rate is applied to amounts of token0 and token1 derived from the geometric mean price of the interval, in Data.computeBorrows:
int24 gmTick = lowTick + (highTick - lowTick) / 2; // The tick of the geometric mean.
For example, if there is 1e18 taker liquidity in the [-122880..245760] range, the borrow amounts for this interval should be:
- borrowX = 0.046e18
- borrowY = 21.6e18
The issue is that taker liquidity is stored in a segment tree, so internally the range is broken into two segments. The amounts of token0 and token1 are then calculated from the geometric mean of each segment rather than the original price, which inflates the borrow amounts badly:
- for [-122880..0]: borrowX = 20.6e18, borrowY = 0.044e18
- for [0..245760]: borrowX = 0.00214e18, borrowY = 464.7e18
These sum to borrowX = 20.6e18 and borrowY = 464.8e18, at least 200x the expected borrow amount.
With a 10% taker rate, the taker expects to pay 0.0046e18 token0 and 2.16e18 token1. Because of how the liquidity is stored and calculated, they actually pay 2.06e18 token0 and 46.48e18 token1.
Alpha: the fee paid varies with the state — specifically the number of segments at that moment — which is inconsistent and always worth reporting. Check that fees are consistent for the same outcome.
Conclusion
This finding would earn you $4432. It requires knowing exactly how the fees work, and how much they can vary for the same outcome.