Skip to content
4 changes: 2 additions & 2 deletions crates/float/abi/DecimalFloat.json

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions src/lib/deploy/LibDecimalFloatDeploy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,11 @@ library LibDecimalFloatDeploy {
/// @dev Address of the DecimalFloat contract deployed via Zoltu's
/// deterministic deployment proxy.
/// This address is the same across all EVM-compatible networks.
address constant ZOLTU_DEPLOYED_DECIMAL_FLOAT_ADDRESS = address(0x799632d282178e770C7465cad54aDA1021A913D6);
address constant ZOLTU_DEPLOYED_DECIMAL_FLOAT_ADDRESS = address(0x3963f256440e9D65D8a290eF1EfCA2d70C6EaeC9);

/// @dev The expected codehash of the DecimalFloat contract deployed via
/// Zoltu's deterministic deployment proxy.
bytes32 constant DECIMAL_FLOAT_CONTRACT_HASH = 0xdc468883c345d41c0abd98ef2fd933c370bd1682522d37e6f6b729793301f55e;
bytes32 constant DECIMAL_FLOAT_CONTRACT_HASH = 0x308dac13dccf56e44224b653a71f210bc20897d8f0f565ec2bae0ce5088c16de;

/// @dev Deploy constants pinned to each version published to the soldeer
/// registry. These are frozen literals — not aliases of the "current"
Expand Down
14 changes: 13 additions & 1 deletion src/lib/implementation/LibDecimalFloatImplementation.sol
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,19 @@ library LibDecimalFloatImplementation {
signedCoefficient = MAXIMIZED_ZERO_SIGNED_COEFFICIENT;
exponent = MAXIMIZED_ZERO_EXPONENT;
} else {
exponent = exponentA + exponentB;
unchecked {
exponent = exponentA + exponentB;
}
// Detect signed int256 wrap (same-sign inputs, opposite-sign result)
// or exponent above EXPONENT_MAX. Surfaces as ExponentOverflow rather
// than Panic(0x11), and bounding against EXPONENT_MAX prevents the
// adjustExponent += below from overflowing.
if (
(exponentB > 0 && exponent < exponentA) || (exponentB < 0 && exponent > exponentA)
|| exponent > EXPONENT_MAX
) {
revert ExponentOverflow(signedCoefficientA, exponentA);
}

// mulDiv only works with unsigned integers, so get the absolute
// values of the coefficients.
Expand Down
15 changes: 15 additions & 0 deletions test/src/lib/LibDecimalFloat.pow.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,21 @@ contract LibDecimalFloatPowTest is LogTest {
this.powExternal(a, LibDecimalFloat.packLossless(1, 10));
}

/// Negative-exponent `pow` recurses to `pow(a.inv(), -b)`, whose squaring
/// loop doubles a large negative exponent until it wraps int256. Previously
/// this surfaced as a raw Panic(0x11); now it surfaces as ExponentOverflow.
function testPowNegativeExponentSquaringNoRawPanic() external {
// pow(1e1700000000, -8e69): a.inv() has a large negative exponent that
// the squaring loop doubles ~230 times until it wraps int256.
Float a = LibDecimalFloat.packLossless(1, 1700000000);
Float b = LibDecimalFloat.packLossless(-8, 69);
try this.powExternal(a, b) {
assertTrue(false, "expected revert");
} catch (bytes memory reason) {
assertExpectedPowError(reason);
}
Comment on lines +215 to +217

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Assert the exact ExponentOverflow selector in this pinned regression.

This test currently passes for any “expected pow error”, so it can miss a regression where this specific case no longer throws ExponentOverflow.

Suggested tightening
         } catch (bytes memory reason) {
-            assertExpectedPowError(reason);
+            assertEq(bytes4(reason), ExponentOverflow.selector, "expected ExponentOverflow");
+            assertExpectedPowError(reason);
         }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/src/lib/LibDecimalFloat.pow.t.sol` around lines 215 - 217, The test at
the catch block in LibDecimalFloat.pow.t.sol is using
assertExpectedPowError(reason) which is too generic and will pass for any
expected pow error, potentially missing a regression where this specific case no
longer throws ExponentOverflow. Replace the assertExpectedPowError(reason) call
in the catch block with a more specific assertion that directly verifies the
exact ExponentOverflow error selector is present in the caught reason bytes,
ensuring this regression test validates the specific error type rather than
accepting any pow-related error.

}

/// The complete set of custom errors `pow` is designed to throw, derived by
/// reading the implementation. Each leg of the round trip is the same `pow`
/// call, so both legs share this set.
Expand Down
Loading