Prehistoric Cobalt Aphid
Medium
The price in the strategy is scaled from the square root as:
FullMath.mulDiv(sqrtPriceX96, 1e15, 2 ** 96) ** 2;
The issue is that it does not take into account the decimals of the underlying tokens, which means that the precision of 1e30
is not enough for certain pairs, as proved in the POC. If a token worth 1e-5
USD with 18 decimals is coupled with WBTC (or similar), the sqrtPrice
will be very small and 1e15
precision is not enough. The sqrtPriceX96
should always be scaled by the decimal difference before dividing by 2**96
to preserve precision.
In Strategy:128
price and TWAP price have precision loss.
None.
Token with 18 decimals and low price is coupled with WBTC.
- Price is calculated for the mentioned token pair suffering significant precision loss, leading to 1-100 BIPS losses.
The protocol will use incorrect prices allowing users to do arbitrage in the Leverager and manipulate it.
The following test shows a 0.2 BIPS precision loss on the price with a token that is worth 1e-5
USD and WBTC.
function test_POC_price_precision() public {
uint256 sqrtPrice = 7922816211812353000; // sqrt((1e8-1)*2**96*2**96 / (1e10*e18)). This token is worth 10^-5 USD
uint256 price = (FullMath.mulDiv(sqrtPrice, 1e15, 2 ** 96) ** 2);
assertEq(price, 9999800001);
uint256 correctPrice = uint256(1e8-1)*1e30/(1e10*1e18);
assertEq(correctPrice, 9999999900); // error is 0.2 BIPS
uint256 decimalsAdjustedPrice = (FullMath.mulDiv(sqrtPrice, 1e20, 2 ** 96) ** 2); // add 1e5 precision to sqrt to account for decimal diff
assertEq(decimalsAdjustedPrice, 99999999000000002500);
assertEq(uint256(1e8-1)*1e40/(1e10*1e18), 99999999000000000000); // error is 0
}
Scale the price by the decimal difference before dividing by 2**96
. The POC above shows that the error is reduced to almost 0.