@reservoir0x/relay-kit-ui
Version:
Relay is the Fastest and Cheapest Way to Bridge and Transact Across Chains.
191 lines • 7.63 kB
JavaScript
import { formatUnits } from 'viem';
import { isSafariBrowser } from './browser.js';
const { format: formatUsdCurrency } = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD'
});
function formatDollar(price) {
if (price === undefined || price === null || price === 0) {
return '-';
}
// For values >= $1B, show ">$1B"
if (Math.abs(price) >= 1000000000) {
return '>$1B';
}
const formatted = formatUsdCurrency(price);
if (formatted === '$0.00' && price && price > 0) {
return '< $0.01';
}
return formatted;
}
function formatNumber(amount, maximumFractionDigits = 2, compact) {
const { format } = new Intl.NumberFormat('en-US', {
maximumFractionDigits: maximumFractionDigits,
notation: compact ? 'compact' : 'standard'
});
if (!amount) {
return '-';
}
const numAmount = Number(amount);
if (numAmount >= 1000000000) {
return '>1B';
}
// Handle very small positive values
if (numAmount > 0) {
const threshold = Math.pow(10, -maximumFractionDigits);
if (numAmount < threshold) {
const thresholdStr = threshold.toFixed(maximumFractionDigits);
return `< ${thresholdStr}`;
}
}
return format(numAmount).replace(/\.?0+$/, '');
}
const truncateFractionAndFormat = (parts, digits) => {
return parts
.map(({ type, value }) => {
if (type !== 'fraction' || !value || value.length < digits) {
return value;
}
let formattedValue = '';
for (let idx = 0; idx < value.length && idx < digits; idx++) {
formattedValue += value[idx];
}
return formattedValue;
})
.reduce((string, part) => string + part);
};
/**
* Convert ETH values to human readable formats
* @param amount An ETH amount
* @param maximumFractionDigits Number of decimal digits
* @param decimals Number of decimal digits for the atomic unit
* @param compact A boolean value used to specify the formatting notation
* @returns returns the ETH value as a `string` or `-` if the amount is `null` or `undefined`
*/
function formatBN(amount, maximumFractionDigits, decimals = 18, compact = true, formatOptionsParam = {}) {
if (typeof amount === 'undefined' || amount === null)
return '-';
const amountToFormat = typeof amount === 'number'
? amount
: +formatUnits(BigInt(amount), decimals ?? 18);
if (amountToFormat === 0) {
return `${amountToFormat}`;
}
const amountFraction = `${amount}`.split('.')[1];
const isSafari = isSafariBrowser();
const formatOptions = {
minimumFractionDigits: 0,
maximumFractionDigits: 20,
useGrouping: true,
notation: compact ? 'compact' : 'standard',
compactDisplay: 'short',
...formatOptionsParam
};
// New issue introduced in Safari v16 causes a regression and now need lessPrecision flagged in format options
if (isSafari) {
//@ts-ignore
formatOptions.roundingPriority = 'lessPrecision';
}
const parts = new Intl.NumberFormat('en-US', formatOptions).formatToParts(amountToFormat);
// Safari has a few bugs with the fraction part of formatToParts, sometimes rounding when unnecessary and
// when amount is in the thousands not properly representing the value in compact display. Until the bug is fixed
// this workaround should help. bugzilla bug report: https://bugs.webkit.org/show_bug.cgi?id=249231
// Update: this has been fixed, but still applied for >v15.3 and <v16
if (isSafari) {
const partTypes = parts.map((part) => part.type);
const partsIncludesFraction = partTypes.includes('fraction');
const partsIncludeCompactIdentifier = partTypes.includes('compact');
if (amountFraction) {
if (!partsIncludesFraction && !partsIncludeCompactIdentifier) {
const integerIndex = parts.findIndex((part) => part.type === 'integer');
parts.splice(integerIndex + 1, 0, {
type: 'decimal',
value: '.'
}, {
type: 'fraction',
value: amountFraction
});
}
}
else if (!partsIncludesFraction && partsIncludeCompactIdentifier) {
const compactIdentifier = parts.find((part) => part.type === 'compact');
const integerIndex = parts.findIndex((part) => part.type === 'integer');
const integer = parts[integerIndex];
if (compactIdentifier?.value === 'K' && integer) {
const fraction = `${amount}`.replace(integer.value, '')[0];
if (fraction && Number(fraction) > 0) {
parts.splice(integerIndex + 1, 0, {
type: 'decimal',
value: '.'
}, {
type: 'fraction',
value: fraction
});
}
}
}
}
if (parts && parts.length > 0) {
const lowestValue = Number(`0.${new Array(maximumFractionDigits).join('0')}1`);
if (amountToFormat > 1000) {
return truncateFractionAndFormat(parts, 1);
}
else if (amountToFormat < 1 && amountToFormat < lowestValue) {
return `< ${lowestValue}`;
}
else {
return truncateFractionAndFormat(parts, maximumFractionDigits);
}
}
else {
return typeof amount === 'string' || typeof amount === 'number'
? `${amount}`
: '';
}
}
function truncateBalance(balance) {
let formattedBalance = parseFloat(balance ? balance.substring(0, 6) : '0');
if (formattedBalance === 0) {
formattedBalance = 0;
}
return formattedBalance;
}
/**
* Formats a number represented by a string, ensuring the total length does not exceed a specified number of characters.
* @param amount The string to format
* @param maxLength The maximum total length of the string representation.
* @returns A plain string representation of the number, trimmed to the specified length.
*/
function formatFixedLength(amount, maxLength) {
if (!/^[-+]?\d*\.?\d*$/.test(amount))
return 'Invalid number';
const isNegative = amount.startsWith('-');
let result = amount.replace(/^-/, ''); // Remove negative sign for now
if (result.includes('.')) {
const parts = result.split('.');
const integerPart = parts[0];
const decimalPart = parts[1] || '';
// Calculate how many characters are left for the decimal part
const availableSpace = maxLength - integerPart.length;
if (integerPart.length >= maxLength) {
// If the integer part alone exceeds the maximum length, return just the integer part
result = integerPart;
}
else {
// Include as much of the decimal part as possible without exceeding the total length
result = integerPart + '.' + decimalPart.substring(0, availableSpace);
}
}
// Ensure no unnecessary trailing zeros and remove any trailing decimal points
result = result
.replace(/\.0+$/, '')
.replace(/\.(\d*[^0])0+$/, '.$1')
.replace(/\.$/, '');
// Add negative sign back if the number was negative
if (isNegative) {
result = '-' + result;
}
return result;
}
export { formatDollar, formatBN, formatFixedLength, formatNumber, truncateBalance };
//# sourceMappingURL=numbers.js.map