Ethers.js: BigNumber error on small value

Created on 8 Nov 2019  路  6Comments  路  Source: ethers-io/ethers.js

ethers.utils.parseEther('0.0007000000000000001')
throws Error: underflow occurred (operation="division", fault="underflow", version=4.0.39)
How can I fix it?

discussion

Most helpful comment

It also doesn't match .4, which should be valid and doesn't match negative numbers like -1, which should sometimes be valid.

The regular expression that matches a fully qualified (but still flexible) number is a bit more complicated, but it may make sense to include.

In v5, it would be more useful to probably use something like:

function valid(value) {
    try {
        ethers.FixedNumber.fromString(value, "fixed128x18");
        return true;
    } catch (e) { }
    return false; 
}

as this allows checking for total bit width as well. Other common things may be "ufixed128x18" for positive-only, or for satoshis, "ufixed128x8" could be used. In general, this function would be called text.oninput and used to update any "submit" button, or highlight the invalid field.

All 6 comments

That is arithmetic underflow though, you can鈥檛 have 0.1 wei. :)

I'm going to close this now, as this is expected behaviour, but please feel free to re-open or continue discussion here.

Thanks! :)

I think it's a rather common use case to convert a string value from an input html tag to a parsed big number, so it would be nice to have a regular expression as part of the constants object exported by ethers. Here's what I use in my app:

^[0-9]+.?[0-9]{0,18}$

Which matches:

  • 5
  • 5.12
  • 5.123456789101112131

But doesn't match:

  • Any kind of characters other than Arabic numerals (from 0 to 9)
  • Any number with more than 18 decimals
  • The empty string (""), so you may want to pick a different initialisation value

It also doesn't match .4, which should be valid and doesn't match negative numbers like -1, which should sometimes be valid.

The regular expression that matches a fully qualified (but still flexible) number is a bit more complicated, but it may make sense to include.

In v5, it would be more useful to probably use something like:

function valid(value) {
    try {
        ethers.FixedNumber.fromString(value, "fixed128x18");
        return true;
    } catch (e) { }
    return false; 
}

as this allows checking for total bit width as well. Other common things may be "ufixed128x18" for positive-only, or for satoshis, "ufixed128x8" could be used. In general, this function would be called text.oninput and used to update any "submit" button, or highlight the invalid field.

For the simpleminded folk amongst us.
The issue is that ether (and most ERC20 tokens) only support 18 decimals.
So what I use is a simple function to round the string down before I pass it to ethers.utils.parseEther

function roundCryptoValueString(str, decimalPlaces=18){
    const arr = str.split(".");
    const fraction = arr[1] .substr(0, decimalPlaces);
    return arr[0] + "." + fraction;
}

For what it's worth, I ended up building my own solution for this problem.

It's a tiny npm package called evm-fp, which works like this;

import { BigNumber } from "@ethersproject/bignumber";
import fp from "evm-fp";

const foo: BigNumber = fp("3.1415");
const bar: BigNumber = fp("115792089237316195423570985008687907853269984665640564039457.584007913129639935");
const baz: BigNumber = fp("100e6", 6);
Was this page helpful?
0 / 5 - 0 ratings