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

CodingYourLife
  • 729
  • 1
  • 6
  • 18

4 Answers4

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.

Diego Ferri
  • 926
  • 8
  • 12
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.

Lemuel
  • 21
  • 1
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.

goodvibration
  • 26,003
  • 5
  • 46
  • 86
  • 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 – CodingYourLife Jun 05 '20 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. – goodvibration Jun 05 '20 at 15:03
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
akp
  • 101
  • 1