UNPKG

@syncswap/sdk

Version:

SyncSwap TypeScript SDK for building DeFi applications

308 lines 11.6 kB
import { ethers } from 'ethers'; import { DECIMALS_10, DECIMALS_12, DECIMALS_15, DECIMALS_16, DECIMALS_17, DECIMALS_18, DECIMALS_20, DECIMALS_24, DECIMALS_3, DECIMALS_36, DECIMALS_6, DECIMALS_8, DECIMALS_9, ETHER, NINE_MILLION_BILLION, TEN, ZERO } from './constants.js'; // import { getTokenDisplayDecimals } from '../../components/SwapBox/SwapInput/TokenBox/swapInputInfo.js'; const NUMBER_SPLIT_REGEX = /\B(?=(\d{3})+(?!\d))/g; const STRING_DOT = '.'; const STRING_COMMA = ','; //const STRING_SPACE = ' '; export class Numbers { static numberWithCommas(s) { const len = s.length; if (len <= 3) { return s; } const dotIndex = s.indexOf(STRING_DOT); if (dotIndex === -1) { return s.replace(NUMBER_SPLIT_REGEX, STRING_COMMA); } else { const integer = s.slice(0, dotIndex); const decimal = s.slice(dotIndex); return integer.replace(NUMBER_SPLIT_REGEX, STRING_COMMA) + decimal; } } /* public static numberWithSpaces(s: string) { const parts = s.split(STRING_DOT); parts[0] = parts[0].replace(NUMBER_SPLIT_REGEX, STRING_SPACE); return parts.join(STRING_DOT); } */ // NO trimTailingZeros, NO ICS static formatInput(value, decimals, fractions) { const maxDecimals = 18; const formatted = ethers.utils.formatUnits(value, decimals); const fixed = this.fixPoints(formatted, fractions, maxDecimals); return fixed; } // NO ICS static formatPrice(value, decimals, fractions) { const maxDecimals = 18; const formatted = ethers.utils.formatUnits(value, decimals); const fixed = this.fixPoints(formatted, fractions, maxDecimals); return this.trimTailingZeros(fixed); } static formatPercent(numerator, denominator, fractions, capped = true, space = false, maxDecimals = 4) { if (numerator.isZero()) { return space ? '0 %' : '0%'; } if (denominator.isZero()) { return space ? '100 %' : '100%'; } const percentValue = numerator.mul(ETHER).div(denominator); if (capped && percentValue.gte(ETHER)) { return space ? '100 %' : '100%'; } else { const text = Numbers.format(percentValue, 16, fractions, { disableICS: true, maxDecimals: maxDecimals, }); return space ? `${text} %` : `${text}%`; } } static formatPercentBy(percentValue, fractions, capped = true, maxDecimals = 4) { if (percentValue.isZero()) { return '0%'; } if (capped && percentValue.gte(ETHER)) { return '100%'; } else { return `${Numbers.format(percentValue, 16, fractions, { disableICS: true, maxDecimals: maxDecimals, })}%`; } } static formatWithCommas(value, decimals, fractions, options) { return this.numberWithCommas(this.format(value, decimals, fractions, options)); } // public static formatWithToken( // value: BigNumber, // token: Token, // commas = true, // // options?: { // allowTailingZero?: boolean, // disableICS?: boolean, // disableOverflow?: boolean, // maxDecimals?: number, // //compensateDecimals?: number, // minimumICSValue?: BigNumber, // } // ): string { // const formatted: string = this.format(value, token.decimals, getTokenDisplayDecimals(token), options); // if (commas) { // return this.numberWithCommas(formatted); // } else { // return formatted; // } // } static format(value, decimals, fractions, options) { if (!value || value.isZero()) { return '0'; } const maxDecimals = options?.maxDecimals ?? 18; if (!options || (!options.disableICS)) { const BIG_VALUE = this.pow(decimals).mul(options && options.minimumICSValue ? options.minimumICSValue : DECIMALS_9); // 1b //console.log('format value', value.toString(), 'ICS', value.gte(BIG_VALUE), 'BIG_VALUE', BIG_VALUE.toString(), 'decimals', decimals, 'this.power(decimals)', this.power(decimals).toString()); if (value.gte(BIG_VALUE)) { return this.formatICS(value, decimals, fractions, maxDecimals, options?.disableOverflow ? { disableOverflow: options.disableOverflow } : undefined); } } const formatted = ethers.utils.formatUnits(value, decimals); const fixed = this.fixPoints(formatted, fractions, maxDecimals); if (options?.allowTailingZero) { return fixed; } else { return this.trimTailingZeros(fixed); } } static trimTailingZeros(s) { const i = s.indexOf('.'); if (i === -1) { return s; } let trim = s; // Removes all zeros after the dot. while (trim.endsWith('0')) { trim = trim.substring(0, trim.length - 1); } // Removes the dot at the end if it has. if (trim.endsWith('.')) { trim = trim.substring(0, trim.length - 1); } return trim; } static fixPoints(formatted, fixPoints, maxDecimals) { //console.log('fixPoints', formatted); const dotIndex = formatted.indexOf('.'); if (dotIndex === -1) { return formatted; } if (formatted === '0.0' || formatted.replaceAll('.', '').replaceAll('0', '') === '') { return formatted; // 0.0 } const fractionStartIndex = dotIndex + 1; for (let i = 0; i <= 18; i++) { // length of current testing fraction const curFractionLen = fixPoints + i; // index of end position of current fraction const curEndAt = fractionStartIndex + curFractionLen; const slice = formatted.slice(0, curEndAt); // whether current fraction has a number if (slice.replaceAll('.', '').replaceAll('0', '') !== '') { // deciamls of current slice const decimals = slice.length - dotIndex; // if decimals exceeds max if (decimals > maxDecimals) { const sliceOfMaxDecimals = slice.substring(0, dotIndex + maxDecimals); // limit decimals if possible if (sliceOfMaxDecimals.replaceAll('.', '').replaceAll('0', '') !== '') { // compensate one if reach //if (compensateDecimals !== -1 && decimals > compensateDecimals) { // return slice.substring(0, dotIndex + maxDecimals + 1); //} return sliceOfMaxDecimals; } } return slice; } if (curFractionLen >= maxDecimals) { return '<' + formatted.slice(0, curEndAt - 1) + '1'; } } return '0'; } static formatICS(value, valueDecimals, fixPoints, maxDecimals = 4, options) { const oneToken = Numbers.pow(valueDecimals); const b = DECIMALS_9; const m = DECIMALS_6; const k = DECIMALS_3; //console.log('formatICS', value.toString(), valueDecimals); if ((!options || !options.disableOverflow) && value.gte(NINE_MILLION_BILLION.mul(Numbers.pow(valueDecimals)).mul(oneToken))) { return '>9,000,000B'; } let s; if (value.gte(b.mul(oneToken))) { s = ethers.utils.formatUnits(value.div(b), valueDecimals); return this.fixPoints(s, fixPoints, maxDecimals) + 'B'; } else if (value.gte(m.mul(oneToken))) { s = ethers.utils.formatUnits(value.div(m), valueDecimals); return this.fixPoints(s, fixPoints, maxDecimals) + 'M'; } else if (value.gte(k.mul(oneToken))) { s = ethers.utils.formatUnits(value.div(k), valueDecimals); return this.fixPoints(s, fixPoints, maxDecimals) + 'K'; } else { s = ethers.utils.formatUnits(value, valueDecimals); return this.fixPoints(s, fixPoints, maxDecimals); } } /** * Reduce decimals from given number * @param num number * @param decimals decimals of number * @returns number after decimals reduced */ static reduceDecimals(num, decimals) { return (decimals === 0 || num.isZero()) ? ZERO : num.div(Numbers.pow(decimals)); } /** * Compare two numbers with their decimals deduced * @param a number A * @param decimalsA decimals of number A * @param b number B * @param decimalsB decimals of number B * @returns whether number A is greater than number B */ static compareWithDecimals(a, decimalsA, b, decimalsB) { const wadA = this.reduceDecimals(a, decimalsA); const wadB = this.reduceDecimals(b, decimalsB); return wadA.gt(wadB); } static pow(pow) { if (pow === 18) { return ETHER; } else if (pow === 6) { return DECIMALS_6; } else if (pow === 8) { return DECIMALS_8; } else if (pow === 18) { return DECIMALS_18; } else if (pow === 17) { return DECIMALS_17; } else if (pow === 16) { return DECIMALS_16; } else if (pow === 15) { return DECIMALS_15; //} else if (pow === 3) { // return DECIMALS_3; } else if (pow === 9) { return DECIMALS_9; } else if (pow === 10) { return DECIMALS_10; } else if (pow === 24) { return DECIMALS_24; } else if (pow === 20) { return DECIMALS_20; } else if (pow === 36) { return DECIMALS_36; } else { //console.log('Numbers: power uncached', pow); return TEN.pow(pow); } } static convertAmount(amount, fromDecimals, toDecimals) { if (fromDecimals === toDecimals) { return amount; } else { return amount.mul(this.pow(toDecimals)).div(this.pow(fromDecimals)); } } static asPercentDisplay(sample, sampleDecimals, total, totalDecimals) { if (sample.isZero() || total.isZero()) { return '0.0'; } const sampleAdjusted = this.convertAmount(sample, sampleDecimals, totalDecimals); const percent = sampleAdjusted.mul(ETHER) // precision .div(total); const percentAdjusted = percent.lt(DECIMALS_15) ? '<0.01' : Numbers.format(percent, 18 - 2, 2); return percentAdjusted; } static asPercentFloat(numerator, denominator, capped = true) { if (numerator.isZero()) { return 0.0; } if (denominator.isZero()) { return 1.0; } const percentValue = numerator.mul(ETHER).div(denominator); if (capped && percentValue.gte(ETHER)) { return 1.0; } else { return Number(percentValue.div(DECIMALS_12)) / 1000000; } } } //# sourceMappingURL=numbers.js.map