Bug Report
While testing the vibenet explorer and TIPS transaction list with edge-case token amounts, we noticed that negative values display incorrectly in the UI.
Affected files
app/vibenet/library/explorer.ts → fmtTokenAmount
app/tips/library/explorer-format.ts → formatUnits
Reproduction
fmtTokenAmount(-1_500_000_000_000_000_000n, 18)
// Expected: "-1.5"
// Actual: "-1.-5"
fmtTokenAmount(-500_000_000_000_000_000n, 18)
// Expected: "-0.5"
// Actual: "0.-5"
Root cause
JavaScript's BigInt % operator preserves the sign of the dividend, not the divisor. So when raw is negative, frac = raw % divisor is also negative and frac.toString() produces a string starting with "-", which gets concatenated directly into the fractional part:
const frac = raw % divisor; // -500_000_000_000_000_000n (negative!)
const fracStr = frac.toString() // "-5"
.padStart(decimals, '0')
.replace(/0+$/, '');
return `${whole.toLocaleString()}.${fracStr}`; // "0.-5"
Fix
Take the absolute value of frac before formatting:
const whole = raw / divisor;
const frac = (raw % divisor + divisor) % divisor; // always positive
Or more explicitly:
const frac = raw < 0n ? -(raw % divisor) : raw % divisor;
Same fix applies to formatUnits in explorer-format.ts.
Notes
- All 131 existing vitest tests pass negative BigInt inputs are not currently covered by any test
- Positive amounts and zero are unaffected
fmtHexInt has a separate but related issue: it uses Number.parseInt(hex, 16) which silently loses precision for values above 2^53. Should use BigInt(hex) instead.
Bug Report
While testing the vibenet explorer and TIPS transaction list with edge-case token amounts, we noticed that negative values display incorrectly in the UI.
Affected files
app/vibenet/library/explorer.ts→fmtTokenAmountapp/tips/library/explorer-format.ts→formatUnitsReproduction
Root cause
JavaScript's BigInt
%operator preserves the sign of the dividend, not the divisor. So whenrawis negative,frac = raw % divisoris also negative andfrac.toString()produces a string starting with"-", which gets concatenated directly into the fractional part:Fix
Take the absolute value of
fracbefore formatting:Or more explicitly:
Same fix applies to
formatUnitsinexplorer-format.ts.Notes
fmtHexInthas a separate but related issue: it usesNumber.parseInt(hex, 16)which silently loses precision for values above2^53. Should useBigInt(hex)instead.