fix: bound money conversion after cents scaling

This commit is contained in:
jb
2026-09-18 23:55:32 -04:00
parent aea41d8cea
commit 9ddd12fbc5
2 changed files with 15 additions and 9 deletions
+10 -7
View File
@@ -7,8 +7,9 @@
export type IskLike = string | bigint | number;
const INTEGER_TEXT = /^-?\d+$/;
// The final lookahead makes the end assertion strict: JavaScript's `$` alone
// would also accept a match immediately before one trailing newline.
// The final lookahead makes the end assertion strict. This intentionally
// tightens the old ESI regex, because JavaScript's `$` alone also accepts a
// match immediately before one trailing newline.
const DECIMAL_TEXT = /^-?(?:0|[1-9][0-9]*)(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?$(?![\s\S])/;
/** Resource bounds for signed decimal ingestion. These reject excess input; no truncation is performed. */
@@ -68,18 +69,20 @@ export function decimalIskToCents(value: string): string {
}
const exponent = exponentText === undefined ? 0n : parseDecimalExponent(exponentText);
const decimalPlaces = BigInt(fraction.length) - exponent;
if (decimalPlaces < -MAX_DECIMAL_SHIFT || decimalPlaces > MAX_DECIMAL_SHIFT) {
// Convert the decimal coefficient to cents before applying the resource
// bound: value * 10^(exponent - fractionDigits) * 100.
const centsShift = exponent + 2n - BigInt(fraction.length);
if (centsShift < -MAX_DECIMAL_SHIFT || centsShift > MAX_DECIMAL_SHIFT) {
throw new RangeError('ISK decimal shift exceeds resource bounds');
}
const digits = `${whole}${fraction}`.replace(/^0+(?=\d)/, '');
const significand = BigInt(digits);
let magnitude: bigint;
if (decimalPlaces <= 2n) {
magnitude = significand * 10n ** (2n - decimalPlaces);
if (centsShift >= 0n) {
magnitude = significand * 10n ** centsShift;
} else {
const divisor = 10n ** (decimalPlaces - 2n);
const divisor = 10n ** -centsShift;
const quotient = significand / divisor;
const remainder = significand % divisor;
// Half-away-from-zero is half-up for this non-negative magnitude.