Search code examples
typescripttype-conversionethereumbignumber.js

Typescript Convert a Bignumber Array to a normal number TypeError: array.toNumber is not a function


I saw this post here which shows how to convert a BigNumber to a normal number in typescript. The Ethers.jsis typically used for ethereum development and provides a BigNumber type and assists with converting values from onchain to offchain like in javascript applications.

My issue: I have an array called arr composed of BigNumbers that is returned from a function. If I console.log(arr) it will be formatted like so:

{
   size: BigNumber { value: '2' },
   pricePerDay: [ BigNumber { value: '103897429' }, BigNumber { value: '103827546' } ],
   unixDate: [ BigNumber { value: '1670268628' }, BigNumber { value: '1670268634' } ]
}

Typically to convert a BigNumber to a number the etherjs util you would use the provided toNumber() function. So I can convert size in the example easily:

arr.size = arr.size.toNumber();

But the other numbers within this arr response are pricePerDay and unixDate which are contained within an array. Is there an easy way to convert the entire array to a regular number?

// this fails with TypeError: arr.priceperDay.toNumber is not a function
arr.pricePerDay = arr.pricePerDay.toNumber()

I know I could use map but failed with an overflow error. Heres my attempt to apply the toNumber() function to transform all elements:

// note: i must create a new arr here because the original arr response is read-only
function arrayBigNumberToNumber(arr: BigNumber[] {
    const newArr = arr.map((element: BigNumber, index: number) => {  
        return element.toNumber();
    });
    return newArr;
}

// then I tried to apply the same 
let newArr;
newArr.pricePerDay = arrayBigNumberToNumber(arr.pricePerDay);
newArr.unixDate= arrayBigNumberToNumber(arr.pricePerDay);

The above attempt results in the following overflow error:

 Error: overflow [ See: https://links.ethers.org/v5-errors-NUMERIC_FAULT-overflow ] (fault="overflow", operation="toNumber", value="1000000000000000000", code=NUMERIC_FAULT, version=bignumber/5.7.0)

Solution

  • The error message that you provided explains the problem:

    Error: overflow [
      See: https://links.ethers.org/v5-errors-NUMERIC_FAULT-overflow
    ] (
      fault="overflow",
      operation="toNumber",
      value="1000000000000000000",
      code=NUMERIC_FAULT,
      version=bignumber/5.7.0
    )
    

    From the linked documentation:

    Overflow

    JavaScript uses IEEE 754 double-precision binary floating point numbers to represent numeric values. As a result, there are holes in the integer set after 9,007,199,254,740,991; which is problematic for Ethereum because that is only around 0.009 ether (in wei), which means any value over that will begin to experience rounding errors.

    As a result, any attempt to use a number which is outside the safe range, which would result in incorrect values, an error is thrown.

    In general, numbers should be kept as strings, BigNumber instances or using ES2020 bigints, which all can safely be used without loss of precision.

    In your case, the value 1000000000000000000 exceeds the maximum integer size for a number type in JavaScript, as demonstrated by the following code:

    console.log('Max number:', Number.MAX_SAFE_INTEGER);
    console.log('My number:', '1000000000000000000');
    
    console.log(
      'Is too large?',
      1000000000000000000 > Number.MAX_SAFE_INTEGER,
    );

    and because of that, the value is not representable as a number type in JavaScript.

    In order to work with values of that magnitude, you'll need to use the existing BigNumber instances or standard BigInt types.