Skip to content

Commit

Permalink
Added formatAmount.ts (#5)
Browse files Browse the repository at this point in the history
  • Loading branch information
razvantomegea authored Dec 18, 2024
1 parent a0e070e commit 3dd3f71
Show file tree
Hide file tree
Showing 9 changed files with 245 additions and 2 deletions.
10 changes: 10 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"singleQuote": true,
"semi": true,
"tabWidth": 2,
"bracketSpacing": true,
"jsxBracketSameLine": false,
"arrowParens": "always",
"trailingComma": "none",
"printWidth": 80
}
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

- [Added formatAmount](https://github.com/multiversx/mx-sdk-dapp-utils/pull/5)

## [[v1.0.1]](https://github.com/multiversx/mx-sdk-dapp-utils/pull/4)] - 2024-10-24

- [Upgrade sdk-core](https://github.com/multiversx/mx-sdk-dapp-utils/pull/4)
- [Updated provider interface and updated sdk-core](https://github.com/multiversx/mx-sdk-dapp-utils/pull/3)

Expand Down
3 changes: 3 additions & 0 deletions src/constants/dappConstants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const DECIMALS = 18;
export const DIGITS = 4;
export const ZERO = '0'
1 change: 1 addition & 0 deletions src/constants/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from "./browserConstants";
export * from './dappConstants';
140 changes: 140 additions & 0 deletions src/helpers/formatAmount.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import { TokenTransfer } from '@multiversx/sdk-core';
import BigNumber from 'bignumber.js';
import { DECIMALS, DIGITS, ZERO } from '../constants';
import { stringIsInteger } from './stringIsInteger';
import { pipe } from './pipe';

BigNumber.config({ ROUNDING_MODE: BigNumber.ROUND_FLOOR });

export interface FormatAmountType {
input: string;
decimals?: number;
digits?: number;
showIsLessThanDecimalsLabel?: boolean;
showLastNonZeroDecimal?: boolean;
addCommas?: boolean;
}

export function formatAmount({
input,
decimals = DECIMALS,
digits = DIGITS,
showLastNonZeroDecimal = true,
showIsLessThanDecimalsLabel = false,
addCommas = false
}: FormatAmountType) {
if (!stringIsInteger(input, false)) {
throw new Error('Invalid input');
}

const isNegative = new BigNumber(input).isNegative();
let modInput = input;

if (isNegative) {
// remove - at start of input
modInput = input.substring(1);
}

return (
pipe(modInput as string)
// format
.then(() =>
TokenTransfer.fungibleFromBigInteger('', modInput as string, decimals)
.amountAsBigInteger.shiftedBy(-decimals)
.toFixed(decimals)
)

// format
.then((current) => {
const bnBalance = new BigNumber(current);

if (bnBalance.isZero()) {
return ZERO;
}
const balance = bnBalance.toString(10);
const [integerPart, decimalPart] = balance.split('.');
const bNdecimalPart = new BigNumber(decimalPart || 0);

const decimalPlaces = pipe(0)
.if(Boolean(decimalPart && showLastNonZeroDecimal))
.then(() => Math.max(decimalPart.length, digits))

.if(bNdecimalPart.isZero() && !showLastNonZeroDecimal)
.then(0)

.if(Boolean(decimalPart && !showLastNonZeroDecimal))
.then(() => Math.min(decimalPart.length, digits))

.valueOf();

const shownDecimalsAreZero =
decimalPart &&
digits >= 1 &&
digits <= decimalPart.length &&
bNdecimalPart.isGreaterThan(0) &&
new BigNumber(decimalPart.substring(0, digits)).isZero();

const formatted = bnBalance.toFormat(decimalPlaces);

const formattedBalance = pipe(balance)
.if(addCommas)
.then(formatted)

.if(Boolean(shownDecimalsAreZero))
.then((current) => {
const integerPartZero = new BigNumber(integerPart).isZero();
const [numericPart, decimalSide] = current.split('.');

const zeroPlaceholders = new Array(digits - 1).fill(0);
const zeros = [...zeroPlaceholders, 0].join('');
const minAmount = [...zeroPlaceholders, 1].join(''); // 00..1

if (!integerPartZero) {
return `${numericPart}.${zeros}`;
}

if (showIsLessThanDecimalsLabel) {
return `<${numericPart}.${minAmount}`;
}

if (!showLastNonZeroDecimal) {
return numericPart;
}

return `${numericPart}.${decimalSide}`;
})

.if(Boolean(!shownDecimalsAreZero && decimalPart))
.then((current) => {
const [numericPart] = current.split('.');
let decimalSide = decimalPart.substring(0, decimalPlaces);

if (showLastNonZeroDecimal) {
const noOfZerosAtEnd = digits - decimalSide.length;

if (noOfZerosAtEnd > 0) {
const zeroPadding = Array(noOfZerosAtEnd).fill(0).join('');
decimalSide = `${decimalSide}${zeroPadding}`;
return `${numericPart}.${decimalSide}`;
}

return current;
}

if (!decimalSide) {
return numericPart;
}

return `${numericPart}.${decimalSide}`;
})

.valueOf();

return formattedBalance;
})
.if(isNegative)
.then((current) => `-${current}`)

.valueOf()
);
}
7 changes: 5 additions & 2 deletions src/helpers/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
export * from "./providerNotInitializedError";
export * from "./isWindowAvailable";
export * from './formatAmount';
export * from './isWindowAvailable';
export * from './providerNotInitializedError';
export * from './stringIsFloat';
export * from './stringIsInteger';
26 changes: 26 additions & 0 deletions src/helpers/pipe.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
export function pipe<ValueType>(previous: ValueType) {
return {
if: function (condition: boolean) {
if (condition) {
return {
then: (newValue: ValueType | ((prop: ValueType) => ValueType)) =>
// if a callback is passed, callback is executed with previous value
newValue instanceof Function
? pipe(newValue(previous))
: pipe(newValue)
};
} else {
return {
then: () => pipe(previous)
};
}
},

then: (newValue: ValueType | ((prop: ValueType) => ValueType)) =>
newValue instanceof Function ? pipe(newValue(previous)) : pipe(newValue),

valueOf: function () {
return previous;
}
};
}
41 changes: 41 additions & 0 deletions src/helpers/stringIsFloat.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import BigNumber from 'bignumber.js';
import { ZERO } from '../constants';

export const stringIsFloat = (amount: string) => {
if (isNaN(amount as any)) {
return false;
}
if (amount == null) {
return false;
}
if (String(amount).includes('Infinity')) {
return false;
}

// eslint-disable-next-line
let [wholes, decimals] = amount.split('.');
const LocalBigNumber = BigNumber.clone();

if (decimals) {
const areAllNumbers = decimals
.split('')
.every((digit) => !isNaN(parseInt(digit)));

LocalBigNumber.set({
DECIMAL_PLACES: areAllNumbers
? decimals.length
: BigNumber.config().DECIMAL_PLACES
});

while (decimals.charAt(decimals.length - 1) === ZERO) {
decimals = decimals.slice(0, -1);
}
}
const number = decimals ? [wholes, decimals].join('.') : wholes;
const bNparsed = LocalBigNumber(number);

const output =
bNparsed.toString(10) === number && bNparsed.comparedTo(0) >= 0;

return output;
};
16 changes: 16 additions & 0 deletions src/helpers/stringIsInteger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import BigNumber from 'bignumber.js';

export const stringIsInteger = (
integer: string,
positiveNumbersOnly = true
) => {
const stringInteger = String(integer);
if (!stringInteger.match(/^[-]?\d+$/)) {
return false;
}
const bNparsed = new BigNumber(stringInteger);
const limit = positiveNumbersOnly ? 0 : -1;
return (
bNparsed.toString(10) === stringInteger && bNparsed.comparedTo(0) >= limit
);
};

0 comments on commit 3dd3f71

Please sign in to comment.