From 6536504b246ef330eb5e0ec92d26eb13e39d8fdc Mon Sep 17 00:00:00 2001 From: RaviChauhan <1chauhanravi0@gmail.com> Date: Wed, 25 Feb 2026 02:16:34 +0530 Subject: [PATCH] Fix: Remove floating-point precision loss in currency conversions using BigInt --- djed-sdk/src/helpers.js | 97 ++++++++++++++++++++++++++++++----------- 1 file changed, 71 insertions(+), 26 deletions(-) diff --git a/djed-sdk/src/helpers.js b/djed-sdk/src/helpers.js index 6738053..c14e895 100644 --- a/djed-sdk/src/helpers.js +++ b/djed-sdk/src/helpers.js @@ -1,13 +1,14 @@ export function web3Promise(contract, method, ...args) { return contract.methods[method](...args).call(); } + // Function to build a transaction // Set gas limit to 500,000 by default export function buildTx(from_, to_, value_, data_, setGasLimit = true) { const tx = { to: to_, from: from_, - value: "0x" + BigInt(value_).toString(16), // Use BigInt instead of BN + value: "0x" + BigInt(value_).toString(16), data: data_, }; if (setGasLimit) { @@ -15,6 +16,7 @@ export function buildTx(from_, to_, value_, data_, setGasLimit = true) { } return tx; } + export function convertInt(promise) { return promise.then((value) => parseInt(value)); } @@ -52,7 +54,6 @@ export function decimalScaling(unscaledString, decimals, show = 6) { suffix = intersperseCommas(suffix); if (show <= decimals) { - // Remove commas after the decimal point suffix = suffix.replace(/,/g, ""); } @@ -71,14 +72,18 @@ export function decimalUnscaling(scaledString, decimals) { let s = scaledString.slice(0, pos) + scaledString.slice(pos + 1, pos + 1 + decimals); + if (scaledString.length - pos - 1 < decimals) { s += "0".repeat(decimals - (scaledString.length - pos - 1)); } + return s; } export function scaledPromise(promise, scaling) { - return promise.then((value) => decimalScaling(value.toString(10), scaling)); + return promise.then((value) => + decimalScaling(value.toString(10), scaling) + ); } export function scaledUnscaledPromise(promise, scaling) { @@ -89,7 +94,11 @@ export function scaledUnscaledPromise(promise, scaling) { } export function percentageScale(value, scaling, showSymbol = false) { - const calculatedValue = decimalScaling(value.toString(10), scaling - 2, 2); + const calculatedValue = decimalScaling( + value.toString(10), + scaling - 2, + 2 + ); if (showSymbol) { return calculatedValue + "%"; } @@ -97,35 +106,71 @@ export function percentageScale(value, scaling, showSymbol = false) { } export function percentScaledPromise(promise, scaling) { - return promise.then((value) => percentageScale(value, scaling, true)); + return promise.then((value) => + percentageScale(value, scaling, true) + ); } -// currency conversions: -export function calculateBcUsdEquivalent(coinsDetails, amountFloat) { - const adaPerUsd = parseFloat( - coinsDetails?.scaledScExchangeRate.replaceAll(",", "") + +/* ============================================================ + CURRENCY CONVERSIONS (BigInt Safe Version) + ============================================================ */ + +// BC → USD +export function calculateBcUsdEquivalent(coinsDetails, amount) { + if (!coinsDetails?.scaledScExchangeRate) return "0.000000"; + + const adaPerUsd = BigInt( + coinsDetails.scaledScExchangeRate.replaceAll(",", "") ); - const eqPrice = (1e6 * amountFloat) / adaPerUsd; - return decimalScaling(eqPrice.toFixed(0).toString(10), 6); + + const amountBig = BigInt(amount); + + // scaled to 6 decimals + const eqPrice = (amountBig * 1_000_000n) / adaPerUsd; + + return decimalScaling(eqPrice.toString(10), 6); } -export function getBcUsdEquivalent(coinsDetails, amountFloat) { - return "$" + calculateBcUsdEquivalent(coinsDetails, amountFloat); +export function getBcUsdEquivalent(coinsDetails, amount) { + return "$" + calculateBcUsdEquivalent(coinsDetails, amount); } -export function calculateRcUsdEquivalent(coinsDetails, amountFloat) { - const adaPerRc = parseFloat(coinsDetails?.scaledSellPriceRc); - const adaPerUsd = parseFloat( - coinsDetails?.scaledScExchangeRate.replaceAll(",", "") +// RC → USD +export function calculateRcUsdEquivalent(coinsDetails, amount) { + if ( + !coinsDetails?.scaledSellPriceRc || + !coinsDetails?.scaledScExchangeRate + ) + return "0.000000"; + + const adaPerRc = BigInt(coinsDetails.scaledSellPriceRc); + const adaPerUsd = BigInt( + coinsDetails.scaledScExchangeRate.replaceAll(",", "") ); - const eqPrice = (1e6 * amountFloat * adaPerRc) / adaPerUsd; - return decimalScaling(eqPrice.toFixed(0).toString(10), 6); -} -export function getRcUsdEquivalent(coinsDetails, amountFloat) { - return "$" + calculateRcUsdEquivalent(coinsDetails, amountFloat); + + const amountBig = BigInt(amount); + + const eqPrice = + (amountBig * adaPerRc * 1_000_000n) / adaPerUsd; + + return decimalScaling(eqPrice.toString(10), 6); } -export function getScAdaEquivalent(coinsDetails, amountFloat) { - const adaPerSc = parseFloat(coinsDetails?.scaledPriceSc.replaceAll(",", "")); - const eqPrice = 1e6 * amountFloat * adaPerSc; - return decimalScaling(eqPrice.toFixed(0).toString(10), 6); +export function getRcUsdEquivalent(coinsDetails, amount) { + return "$" + calculateRcUsdEquivalent(coinsDetails, amount); } + +// SC → ADA +export function getScAdaEquivalent(coinsDetails, amount) { + if (!coinsDetails?.scaledPriceSc) return "0.000000"; + + const adaPerSc = BigInt( + coinsDetails.scaledPriceSc.replaceAll(",", "") + ); + + const amountBig = BigInt(amount); + + const eqPrice = amountBig * adaPerSc * 1_000_000n; + + return decimalScaling(eqPrice.toString(10), 6); +} \ No newline at end of file