10

I want to display a human readable balance with less decimal places like metamask does it. The function formatEther(wei) returns a string, so for proper rounding I need to parse it etc. And I wanted to ask if there is an easier method or best practice how it's done.

enter image description here

0

4 Answers 4

29

As you said

const {utils, BigNumber} = require('ethers');
const balance = BigNumber.from('824213281784279560');
console.log(utils.formatEther(balance));

gives 0.82421328178427956 as string.

You can

Use BigNumber -> string (with truncation)

const remainder = balance.mod(1e14);
console.log(utils.formatEther(balance.sub(remainder)));

gives 0.8242 as string.

Use BigNumber -> string -> number -> string (with truncation)

let res = utils.formatEther(balance);
res = (+res).toFixed(4);
console.log(res);

gives 0.8242 as string.

Use BigNumber -> string -> number (with rounding)

let res = utils.formatEther(balance);
res = Math.round(res * 1e4) / 1e4;
console.log(res);

gives 0.8242 as number.

1
  • Worth mentioning that toFixed may round the number up
    – 0101
    Commented Nov 29, 2023 at 13:48
2

Here are two different ways to do it, given str as the string returned by function formatEther:

Option #1:

function truncate(str, maxDecimalDigits) {
    if (str.includes('.')) {
        const parts = str.split('.');
        return parts[0] + '.' + parts[1].slice(0, maxDecimalDigits);
    }
    return str;
}

Option #2:

const Decimal = require('decimal.js');

function truncate(str, maxDecimalDigits) {
    const num = new Decimal(str);
    return num.toFixed(maxDecimalDigits, Decimal.ROUND_DOWN);
}

Though probably not critical for your application, see here for a list of rounding modes.

2
  • Thanks but first solution does not round and second solution uses a library. I'd prefer not to use a library for this. This might be a personal preference Commented Jun 5, 2020 at 14:50
  • @CodingYourLife: You can use Number(str), then do some arithmetic on this. But knowing the typical magnitude of numeric values in this eco-system, I'm afraid that Number will be potentially less accurate than simply truncating the string, as I have shown in the first option above. Commented Jun 5, 2020 at 15:03
2

I am posting this for other people who might encounter a similar issue:

In this instance, I find it useful to use the formatUnits() method instead. It accepts two arguments which specifies the value - the second argument specifies the decimals. You find some ERC20 tokens which do not default to 18 decimals: some tokens might even use 9 or any other allowable arbitrary decimal-length.

So the above examples could be refactored as follows (with provider and address identifiers already declared):

const balance = await provider.getBalance(address);
console.log('balance of ${balance} --> ${ethers.utils.formatUnits(balance, 18)}`);

Try it out and see if it works.

0

You can use Number.prototype.toLocaleString() function with minimumFractionDigits option. The result is rounded too.

const balance = "0.82425328178427956"
const result = parseFloat(balance).toLocaleString("en", { minimumFractionDigits: 4 })
console.log(result) // 0.8243

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.