Decent Currant Halibut
High
The LMSR:: _cost()
function loses precision and incorrectly scales the result. This is caused by the multiplication after unwrapping from UD60x18
to uint256
. When unwrapping, the precision of UD60x18
(which scales the result by 1e18) is lost, and the subsequent multiplication with b
(which is in uint256
format) causes the result to be much larger than expected.
function _cost(
uint256 yesVotes,
uint256 noVotes,
uint256 liquidityParameter
) public pure returns (uint256 costResult) {
// Compute e^(yes/b) and e^(no/b)
(UD60x18 yesExp, UD60x18 noExp) = _getExponentials(yesVotes, noVotes, liquidityParameter);
// sumExp = e^(yes/b) + e^(no/b)
UD60x18 sumExp = yesExp.add(noExp);
// lnVal = ln(e^(yes/b) + e^(no/b))
UD60x18 lnVal = sumExp.ln();
// Unwrap lnVal and multiply by b (also in UD60x18) to get cost
uint256 lnValUnwrapped = unwrap(lnVal);
costResult = lnValUnwrapped * liquidityParameter;
}
No response
No response
No response
Financial Miscalculation: The loss of precision can lead to significant errors in cost calculations, which directly affect the pricing mechanism of votes in the market. This could result in: Overcharging or undercharging for votes.
Incorrect market dynamics where price does not accurately reflect supply and demand. Potential arbitrage opportunities if the discrepancy is large enough. Market Manipulation: If traders can predict or exploit the precision loss, they might be able to manipulate market outcomes or profit from the mispricing.
function test__IncorrectScaling() public {
uint256 yesVotes = 1000;
uint256 noVotes = 1000;
uint256 cost = LMSR._cost(yesVotes, noVotes, LIQUIDITY_PARAMETER);
//With equal votes the costs should be around b * ln(2) in UD60x18 format
uint256 expectedCost = 693147180559945309 * LIQUIDITY_PARAMETER; //ln(2) * 1e18 * b
//Check if there is significant deviation from the expected cost due to incorrect scaling
assertApproxEqAbs(cost, expectedCost, 1e15, "Cost is not approximately equal to b * ln(2)"); //Allowing for some imprecision
}
Recommended Mitigation:
Implement the calculation in UD60x18
format throughout, only unwrapping the result at the very end:
function _cost(
uint256 yesVotes,
uint256 noVotes,
uint256 liquidityParameter
) public pure returns (uint256 costResult) {
(UD60x18 yesExp, UD60x18 noExp) = _getExponentials(yesVotes, noVotes, liquidityParameter);
UD60x18 sumExp = yesExp.add(noExp);
UD60x18 lnVal = sumExp.ln();
// Convert liquidityParameter to UD60x18 for consistent arithmetic
UD60x18 b = convert(liquidityParameter);
// Perform multiplication in UD60x18 format before unwrapping
costResult = unwrap(lnVal.mul(b));
}