3

I want to do math - 100000 * 1.004 by using BigNumber values. The biggest problem here is that 1.004 is a float and BigNumber does not accept it. I am using ethers.js library for it.

I tried to use parseUnits("1.004", 18). I am using 18 because basically I am operating on stable coins prices.

I get BigNumber { value: "1004000000000000000" } from it. To do the math I have to parse 100000 also so I do it in the same way - parseUnits("100000", 18). After that I just do parseUnits("1.004", 18).mul(parseUnits("100000", 18)) to multiply them and I get BigNumber { value: "100400000000000000000000000000000000000000" }.

When I use formatUnits() method to get a number back I get 100400000000000000000000.0 where correct value should be 100000 * 1.004 = 100400.

What is the correct way of doing calculations like that by using the ethers.js library?

TylerH
  • 20,799
  • 66
  • 75
  • 101
Mickey
  • 61
  • 1
  • 5
  • `parseUnits("100000", 18)` gives you a big number value of 100000 times 10 to the 18th power. You then multiply that by 1.004 times 10 to the 18th power. That's where all the zeros are coming from. – Pointy Nov 28 '22 at 15:05
  • 1
    @Pointy yes that's right but what if I would like to do calculation like: 100000 * 0.9998 * 1.0004. I do `BigNumber.from(100000).mul(ethers.utils.parseUnits("1.0004", 18)).mul(ethers.utils.parseUnits("0.9998", 18))` and I get `BigNumber { value: "100019992000000000000000000000000000000000" }` where I want to get `100019.992` – Mickey Nov 28 '22 at 16:32
  • I would perform the multiplication of 100000 and 1.0004 *before* scaling out with 18 extra digits. – Pointy Nov 28 '22 at 16:33
  • @Pointy yes that's right but I use `BigNumber` values because of safety reasons. I get `1.0004` and `0.9998` values from endpoints so I want to convert them into `BigNumber` and do operations on `BigNumber` values. – Mickey Nov 28 '22 at 16:40
  • I mean make BigNumber instances for 100000 and 1.004, then do the `.mul()` between those, and then scale up the result. – Pointy Nov 28 '22 at 17:25
  • You might like my library that does most of this and is way faster than most of the bignumber libraries. I wrote this specifically for dealing with large crypto values: https://www.npmjs.com/package/bigint-money – Evert Jan 19 '23 at 01:09

3 Answers3

1

I finally decided to use bignumber.js library for that. ethers.js does not support float values with decimals when bignumber.js does it perfectly without any formatting.

Mickey
  • 61
  • 1
  • 5
1

We faced the same exact issue and we were able to solve it by just multiplying that floated number by one big number and dividing it back by one big number too, this utility class in typescript demonstrates that all:

import { BigNumber, utils } from "ethers";

export class BigNumberUtils {
    protected oneBN: BigNumber = utils.parseUnits("1", 18);
    constructor() {}

    public multiply(
        bn: BigNumber | string,
        number: number,
    ): BigNumber {
        const bnForSure = BigNumber.from(bn);
        const numberBN = utils.parseUnits(number.toString(), 18);

        return bnForSure.mul(numberBN).div(this.oneBN);
    }

    public divide(bn: BigNumber | string, number: number): BigNumber {
        const bnForSure = BigNumber.from(bn);
        const numberBN = utils.parseUnits(number.toString(), 18);

        return bnForSure.div(numberBN).div(this.oneBN);
    }
}

Muho
  • 3,188
  • 23
  • 34
0

Convert BigNumber to float, multiply and then convert the result to BigNumber:

import { BigNumber, ethers } from "ethers";

function mulBigNumbers(a: BigNumber, b: BigNumber): BigNumber {
    if (a == null || a.isZero() || b == null || b.isZero()) {
        return BigNumber.from(0);
    }

    const aFloat = parseFloat(ethers.utils.formatEther(a));
    const bFloat = parseFloat(ethers.utils.formatEther(b));

    if (isNaN(aFloat) || isNaN(bFloat)) {
        return BigNumber.from(0);
    }

    const resultFloat = aFloat * bFloat;

    return ethers.utils.parseEther(resultFloat.toFixed(18));
}
Sergik666
  • 127
  • 2
  • 6