Whole-repo audit 2026-08-24 at commit 3e76bab (audit skill 0.35.0). Pass 2 status: ledgered (audit/mutation-test-scans.json, scan of 2026-08-24T08:55:39Z at 35496d8).
test/src/lib/parse/LibParseDecimal.unsafeDecimalStringToSignedInt.t.sol:17-255
The unsigned conversion has a spec-derived slow reference (LibParseDecimalSlow) fuzzed over arbitrary and digit-dense bytes, but the signed conversion has none — its coverage is a targeted branch matrix (all branches are individually discriminated: sign consumption, both overflow boundaries including int256 min, error propagation for all three selectors, lone '-', '+', '--'). The asymmetry means a future edit to the signed wrapper is protected only by the enumerated cases, not by an oracle over arbitrary input.
Proposed fix
Extend test/lib/parse/LibParseDecimalSlow.sol with a signed reference and fuzz it from the existing signed test contract:
function decimalStringToSignedIntSlow(bytes memory data) internal pure returns (bytes4, int256) {
if (data.length == 0) {
return (ParseEmptyDecimalString.selector, 0);
}
bool isNeg = data[0] == "-";
bytes memory digits = data;
if (isNeg) {
digits = new bytes(data.length - 1);
for (uint256 i = 0; i < digits.length; i++) {
digits[i] = data[i + 1];
}
}
(bytes4 errorSelector, uint256 value) = decimalStringToIntSlow(digits);
if (errorSelector != bytes4(0)) {
return (errorSelector, 0);
}
if (isNeg) {
if (value > uint256(type(int256).max) + 1) {
return (ParseDecimalOverflow.selector, 0);
}
if (value == uint256(type(int256).max) + 1) {
return (bytes4(0), type(int256).min);
}
// forge-lint: disable-next-line(unsafe-typecast)
return (bytes4(0), -int256(value));
}
if (value > uint256(type(int256).max)) {
return (ParseDecimalOverflow.selector, 0);
}
// forge-lint: disable-next-line(unsafe-typecast)
return (bytes4(0), int256(value));
}
// In TestLibParseDecimalUnsafeDecimalStringToSignedInt:
function testUnsafeStrToSignedIntReference(bytes memory data) external pure {
(bytes4 slowSelector, int256 slowValue) = LibParseDecimalSlow.decimalStringToSignedIntSlow(data);
(bytes4 errorSelector, int256 result) = LibParseDecimal.unsafeDecimalStringToSignedInt(
Pointer.unwrap(data.dataPointer()), Pointer.unwrap(data.endDataPointer())
);
assertEq(errorSelector, slowSelector);
assertEq(result, slowValue);
}
Whole-repo audit 2026-08-24 at commit 3e76bab (audit skill 0.35.0). Pass 2 status: ledgered (audit/mutation-test-scans.json, scan of 2026-08-24T08:55:39Z at 35496d8).
test/src/lib/parse/LibParseDecimal.unsafeDecimalStringToSignedInt.t.sol:17-255
The unsigned conversion has a spec-derived slow reference (LibParseDecimalSlow) fuzzed over arbitrary and digit-dense bytes, but the signed conversion has none — its coverage is a targeted branch matrix (all branches are individually discriminated: sign consumption, both overflow boundaries including int256 min, error propagation for all three selectors, lone '-', '+', '--'). The asymmetry means a future edit to the signed wrapper is protected only by the enumerated cases, not by an oracle over arbitrary input.
Proposed fix
Extend test/lib/parse/LibParseDecimalSlow.sol with a signed reference and fuzz it from the existing signed test contract: