ViewFacet.queryAssetBalances doesn't include uncollected uniswap fees for compounded maker position
| Title | ViewFacet.queryAssetBalances doesn't include uncollected uniswap fees for compounded maker position |
|---|---|
| Reward | $886, Unique |
| Contest | Ammplify - 2 Sep 2025 on Sherlock |
| Author | panprog |
| Context | Missing Logic |
When ViewFacet.queryAssetBalances calculates the earnings for a compounded maker, it ignores the uncollected Uniswap fees:
uint256 fee0DiffX128 = newFeeGrowthInside0X128 - node.liq.feeGrowthInside0X128;
uint256 fee1DiffX128 = newFeeGrowthInside1X128 - node.liq.feeGrowthInside1X128;
if (data.liq.liqType == LiqType.MAKER) {
// We just claim our shares.
// If the sliq and shares are zero, you should fail anyways.
// @audit-issue compounded maker should also include fee0DiffX128/fee1DiffX128 here
data.earningsX += FullMath.mulDiv(node.fees.xCFees, aNode.sliq, node.liq.shares);
data.earningsY += FullMath.mulDiv(node.fees.yCFees, aNode.sliq, node.liq.shares);
} else if (data.liq.liqType == LiqType.MAKER_NC) {
data.earningsX += FullMath.mulX128(
aNode.sliq,
fee0DiffX128 + node.fees.makerXFeesPerLiqX128 - aNode.fee0CheckX128,
false
);
data.earningsY += FullMath.mulX128(
aNode.sliq,
fee1DiffX128 + node.fees.makerYFeesPerLiqX128 - aNode.fee1CheckX128,
false
);
} else {
When the compounded maker asset is actually removed, the uncollected Uniswap fees are added to node.fees.xCFees and node.fees.yCFees and compounded as extra liquidity. If the liquidity is not increased, because the fees are below the compound threshold, they are simply added to those two fields. Either way the value reaches maker earnings — which is exactly what ViewFacet fails to do.
The reported earnings are therefore too low, and an integrator that fails to account for the fees can break its own logic and lose the difference.
The README puts the issue in scope:
Issues that lead to getting incorrect return values (i.e. deviates from the withdrawal value of the asset by more than 0.01%) from the queryAssetBalance function (even if the appropriate input is used), which will lead to issues when executing other functions, may be considered valid with Medium severity at max.
Alpha: it is very common for Uniswap fees to be missed when querying a position's value. Here the README put view functions in scope, but often the view function is also used internally by state-changing ones, and the bug matters regardless.
Conclusion
This finding would earn you $886. Always verify that Uniswap fees are tracked, compare the view function against the state-changing one, and read the README.