Implicit precision loss in stable_curve::lp_value
Description
In stable_curve::lp_value
, coins with more than eight decimals experience implicit precision loss. The current implementation returns the LP value scaled by (10 ^ 8) ^ 4
in order to maintain precision across division:
public fun lp_value(x_coin: u128, x_scale: u64, y_coin: u128, y_scale: u64): U256 {
let x_u256 = u256::from_u128(x_coin);
let y_u256 = u256::from_u128(y_coin);
let u2561e8 = u256::from_u128(ONE_E_8);
let x_scale_u256 = u256::from_u64(x_scale);
let y_scale_u256 = u256::from_u64(y_scale);
let _x = u256::div(
u256::mul(x_u256, u2561e8),
x_scale_u256,
);
let _y = u256::div(
u256::mul(y_u256, u2561e8),
y_scale_u256,
);
let _a = u256::mul(_x, _y);
// ((_x * _x) / 1e18 + (_y * _y) / 1e18)
let _b = u256::add(
u256::mul(_x, _x),
u256::mul(_y, _y),
);
u256::mul(_a, _b)
}
However, this means that stable_curve::lp_value
will return inaccurate values when coins have more decimals.
Impact
Loss of precision in LP value calculations can cause fees to be unexpectedly high: Situations where a swap would theoretically increase LP value might fail. This precision loss will also affect the accuracy of router functions.
Recommendations
When coins have more than eight decimals, either rounding should be handled explicitly or they should be disallowed from the protocol.
Another option is to use the numerator max(x_scale, y_scale)
instead of 10 ^ 8
to mitigate precision loss. Still, coins with unusually high precision would need to be either disallowed or explicitly considered in order to avoid overflow problems.
Remediation
This issue has been acknowledged by Pontem Network.