Audit reference
Protofire, March 2026, finding M03 — toFixedDecimalLossy Reverts on Positive-Exponent Overflow. Severity: Medium. Status at audit (19a65ffa): New.
What
toFixedDecimalLossy(Float, uint8 decimals) in src/lib/LibDecimalFloat.sol is documented as a lossy conversion returning (uint256 value, bool lossless). The implementation, when finalExponent > 0, scales via unsignedCoefficient * scale in checked arithmetic; large products trigger Panic(0x11) (arithmetic overflow) instead of returning lossless = false.
Impact
The ...Lossy suffix is a contract: callers may rely on the function NOT reverting and instead branching on the bool. A revert breaks that contract and can brick caller workflows that assumed safe fallback. Severity is medium because reachability requires a Float magnitude > uint256.max — large but constructible (e.g. 1e80).
Recommendation
Wrap the positive-exponent scaling in unchecked and pre-check with if (unsignedCoefficient > type(uint256).max / scale) return (0, false);. Alternatively revert with a typed ExponentOverflow error so callers at least see a meaningful selector instead of Panic(0x11). Returning (0, false) matches the ...Lossy API contract better.
Verification
Unit test (int224.max, 50) (or any input where coefficient * 10^exp > uint256.max) → assert returns (0, false), not a revert.
Audit reference
Protofire, March 2026, finding M03 — toFixedDecimalLossy Reverts on Positive-Exponent Overflow. Severity: Medium. Status at audit (
19a65ffa): New.What
toFixedDecimalLossy(Float, uint8 decimals)insrc/lib/LibDecimalFloat.solis documented as a lossy conversion returning(uint256 value, bool lossless). The implementation, whenfinalExponent > 0, scales viaunsignedCoefficient * scalein checked arithmetic; large products triggerPanic(0x11)(arithmetic overflow) instead of returninglossless = false.Impact
The
...Lossysuffix is a contract: callers may rely on the function NOT reverting and instead branching on the bool. A revert breaks that contract and can brick caller workflows that assumed safe fallback. Severity is medium because reachability requires a Float magnitude >uint256.max— large but constructible (e.g.1e80).Recommendation
Wrap the positive-exponent scaling in
uncheckedand pre-check withif (unsignedCoefficient > type(uint256).max / scale) return (0, false);. Alternatively revert with a typedExponentOverflowerror so callers at least see a meaningful selector instead ofPanic(0x11). Returning(0, false)matches the...LossyAPI contract better.Verification
Unit test
(int224.max, 50)(or any input wherecoefficient * 10^exp > uint256.max) → assert returns(0, false), not a revert.