Summary
Accepted and paid Medium finding in the Sherlock Metric contest (2026-07-metric @ d2706927622678939ad77d8389159973192ea19c).
When AnchoredPriceProvider prices a pair using a two-leg synthetic oracle ratio (base / quote), it divides the two E8 oracle legs into an intermediate integer E8 value before converting the result into the pool’s Q64 fixed-point price domain:
mid = Math.mulDiv(mid, ORACLE_DECIMALS, mid2);
For any pair whose true price ratio sits in [1e-8, 2e-8), integer flooring in E8 replaces the midpoint with 1e-8 (up to a 50% understatement), and ratios below 1e-8 floor to 0. The subsequent conversion to Q64 (2^64 ≈ 1.84e19) cannot recover the discarded fraction.
Root cause
In smart-contracts-poc/contracts/AnchoredPriceProvider.sol (lines 258-271):
if (_quote != bytes32(0)) {
(uint256 mid2, uint256 spreadBps2, , bool ok2) = _readLeg(_quote);
if (!ok2 || mid2 == 0) return (0, type(uint128).max);
mid = Math.mulDiv(mid, ORACLE_DECIMALS, mid2);
spreadBps += spreadBps2;
}
return _computeBidAsk(mid, spreadBps);
Only after mid has been quantized to ORACLE_DECIMALS (1e8) does _bandEdge() scale mid by Q64:
return Math.mulDiv(mid, Q64 * edgeFactor, STEP_DENOM, rounding);
Concrete pair & oracle reproduction
On Ethereum mainnet, canonical token sorting orders Reserve Rights (RSR, 0x320623b8E4fF03373931769A31Fc52A4E78B5d70) as token0 and Coinbase Wrapped BTC (cbBTC, 0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf) as token1:
- Pyth feed
175(Crypto.RSR/USD):price = 124,495($0.00124495, exponent-8, confidence55->5 bps) - Pyth feed
397(Crypto.CBBTC/USD):price = 6,484,651,280,510($64,846.51280510, exponent-8, confidence4,207,967,514->7 bps)
true RSR/cbBTC ratio = 124,495 / 6,484,651,280,510
= 1.91984109267644e-8 (1.919841 E8 units)
stored E8 midpoint = floor(1.91984109267644) = 1 (1e-8)
midpoint understatement = 47.91235567%
Through the real AnchoredProviderFactory, MetricOmmPoolFactory, and MetricOmmPool.swap():
- An LP supplies
10,000,000 RSR(worth0.19198411 cbBTCat the authenticated oracle snapshot) into a token0-only bin. - A trader buys all
10,000,000 RSRfor0.10014002 cbBTC. - The LP loses
0.09184409 cbBTC(47.83% of principal, or$5,955.77) in a single swap.
Recommended fix
Compute synthetic ratios directly in Q64 (or a high-precision intermediate such as E18/Q128) before applying bid/ask spread bounds, rather than quantizing the ratio to E8 first:
midX64 = Math.mulDiv(mid, Q64, mid2, rounding);