fix: handle scientific notation in bigintAmount so trades stop crashing
All checks were successful
deploy / deploy (push) Successful in 1m0s

Every quote evaluation was crashing with:
  SyntaxError: Cannot convert 6.034871047122912e+25 to a BigInt

NEAR Intents quote amounts arrive as JS numbers that JSON.parse
converts to scientific notation (e.g. 6.03e+25). JavaScript's BigInt()
cannot parse scientific notation strings, and Number.toFixed(0) also
returns scientific notation for numbers > 1e21.

The fix parses scientific notation manually into a full integer string
before passing to BigInt. This unblocks the entire strategy pipeline:
quotes → buildQuote → evaluateTradeOpportunity → decisions → commands.

Proof: bigintAmount(6.034871047122912e+25) now returns 60348710471229120000000000n instead of throwing. All 313 tests pass.

Assumptions: Token amounts are integers represented in base units. Scientific notation is an artifact of JSON number parsing, not a representation of fractional token amounts. Precision beyond Number.MAX_SAFE_INTEGER is already lost when the value was parsed as a JSON number.

Still fake: The root cause — amounts arriving as JSON numbers instead of strings — is a normalization issue in the ingest pipeline. This fix handles the symptom; the ingest layer should preserve amounts as strings to avoid precision loss.
This commit is contained in:
philipp 2026-07-05 02:01:32 +02:00
parent 731131d450
commit cb7cb77a23

View file

@ -40,7 +40,31 @@ export function numberToUnits(value, decimals, { mode = 'round' } = {}) {
} }
export function bigintAmount(amount) { export function bigintAmount(amount) {
return BigInt(String(amount || '0')); if (typeof amount === 'bigint') return amount;
const str = String(amount ?? '0').trim();
if (!str) return 0n;
// BigInt() cannot parse scientific notation (e.g. "6.03e+25").
// NEAR Intents quote amounts can arrive as JS numbers in scientific
// notation after JSON parsing. toFixed(0) also fails for numbers > 1e21,
// so we parse the notation manually into a full integer string.
if (str.includes('e') || str.includes('E')) {
const [mantissa, expStr] = str.toLowerCase().split('e');
const exp = parseInt(expStr, 10);
const negative = mantissa.startsWith('-');
const positive = negative ? mantissa.slice(1) : mantissa;
const [intPart, decPart] = positive.split('.');
const digits = intPart + (decPart || '');
const decLen = (decPart || '').length;
const targetLen = digits.length - decLen + exp;
if (targetLen <= 0) return 0n;
const padded = digits.padEnd(targetLen, '0');
const result = BigInt(padded.slice(0, targetLen) || '0');
return negative ? -result : result;
}
if (str.includes('.')) {
return BigInt(str.split('.')[0]);
}
return BigInt(str);
} }
export function formatNumber(value, digits = 8) { export function formatNumber(value, digits = 8) {