From 42502b6599c3c8149721a5679fcbf56c79deeebd Mon Sep 17 00:00:00 2001 From: Muhammad Altabba <24407834+Muhammad-Altabba@users.noreply.github.com> Date: Mon, 16 Oct 2023 12:56:11 +0200 Subject: [PATCH] Avoid using `**` with `BigInt` (#6506) * user static numbers to avoid `**` at web3-utils converters.ts * fill `numberLimits` in a loop without using ** * implement `bigintPower` to avoid `**` with `bigint` * update CHANGELOG.md files --- packages/web3-eth-abi/CHANGELOG.md | 6 +- .../web3-eth-abi/src/coders/base/number.ts | 20 +------ .../src/coders/base/numbersLimits.ts | 39 +++++++++++++ packages/web3-utils/CHANGELOG.md | 3 +- packages/web3-utils/src/converters.ts | 56 +++++++++---------- .../web3-utils/src/string_manipulation.ts | 6 +- packages/web3-validator/CHANGELOG.md | 3 +- .../web3-validator/src/validation/numbers.ts | 17 +++++- 8 files changed, 93 insertions(+), 57 deletions(-) create mode 100644 packages/web3-eth-abi/src/coders/base/numbersLimits.ts diff --git a/packages/web3-eth-abi/CHANGELOG.md b/packages/web3-eth-abi/CHANGELOG.md index 908a451032e..0765c91d578 100644 --- a/packages/web3-eth-abi/CHANGELOG.md +++ b/packages/web3-eth-abi/CHANGELOG.md @@ -142,4 +142,8 @@ Documentation: - Dependencies updated -## [Unreleased] \ No newline at end of file +## [Unreleased] + +### Fixed + +- Fix issue with default config with babel (and React): "TypeError: Cannot convert a BigInt value to a number #6187" (#6506) diff --git a/packages/web3-eth-abi/src/coders/base/number.ts b/packages/web3-eth-abi/src/coders/base/number.ts index 317ecd3780f..a86ee4f6acd 100644 --- a/packages/web3-eth-abi/src/coders/base/number.ts +++ b/packages/web3-eth-abi/src/coders/base/number.ts @@ -21,6 +21,7 @@ import { padLeft, toBigInt } from 'web3-utils'; import { utils } from 'web3-validator'; import { DecoderResult, EncoderResult } from '../types.js'; import { WORD_SIZE } from '../utils.js'; +import { numberLimits } from './numbersLimits.js'; // eslint-disable-next-line no-bitwise const mask = BigInt(1) << BigInt(256); @@ -43,25 +44,6 @@ function uint8ArrayToBigInt(value: Uint8Array, max: bigint): bigint { return result - mask; } -const numberLimits = new Map(); - -// precalculate all the limits -for (let i = 8; i <= 256; i += 8) { - numberLimits.set(`uint${i}`, { - min: BigInt(0), - max: BigInt(2) ** BigInt(i) - BigInt(1), - }); - numberLimits.set(`int${i}`, { - min: -(BigInt(2) ** BigInt(i - 1)), - max: BigInt(2) ** BigInt(i - 1) - BigInt(1), - }); -} - -// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -numberLimits.set(`int`, numberLimits.get('int256')!); -// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -numberLimits.set(`uint`, numberLimits.get('uint256')!); - export function encodeNumber(param: AbiParameter, input: unknown): EncoderResult { let value; try { diff --git a/packages/web3-eth-abi/src/coders/base/numbersLimits.ts b/packages/web3-eth-abi/src/coders/base/numbersLimits.ts new file mode 100644 index 00000000000..29f11462843 --- /dev/null +++ b/packages/web3-eth-abi/src/coders/base/numbersLimits.ts @@ -0,0 +1,39 @@ +/* +This file is part of web3.js. + +web3.js is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +web3.js is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with web3.js. If not, see . +*/ + +/* + * this variable contains the precalculated limits for all the numbers for uint and int types +*/ +export const numberLimits = new Map(); + +let base = BigInt(256); // 2 ^ 8 = 256 +for (let i = 8; i <= 256; i += 8) { + numberLimits.set(`uint${i}`, { + min: BigInt(0), + max: base - BigInt(1), + }); + numberLimits.set(`int${i}`, { + min: -base / BigInt(2), + max: base / BigInt(2) - BigInt(1), + }); + base *= BigInt(256); +} + +// eslint-disable-next-line @typescript-eslint/no-non-null-assertion +numberLimits.set(`int`, numberLimits.get('int256')!); +// eslint-disable-next-line @typescript-eslint/no-non-null-assertion +numberLimits.set(`uint`, numberLimits.get('uint256')!); diff --git a/packages/web3-utils/CHANGELOG.md b/packages/web3-utils/CHANGELOG.md index 5db90f9426b..c46f98df69f 100644 --- a/packages/web3-utils/CHANGELOG.md +++ b/packages/web3-utils/CHANGELOG.md @@ -162,4 +162,5 @@ Documentation: ### Added -- As a replacment of the node EventEmitter, a custom `EventEmitter` has been implemented and exported. (#6398) +- As a replacement of the node EventEmitter, a custom `EventEmitter` has been implemented and exported. (#6398) +- Fix issue with default config with babel (and React): "TypeError: Cannot convert a BigInt value to a number #6187" (#6506) diff --git a/packages/web3-utils/src/converters.ts b/packages/web3-utils/src/converters.ts index eed4e5caa1a..4e5f8592f77 100644 --- a/packages/web3-utils/src/converters.ts +++ b/packages/web3-utils/src/converters.ts @@ -38,39 +38,37 @@ import { InvalidUnitError, } from 'web3-errors'; -const base = BigInt(10); -const expo10 = (expo: number) => base ** BigInt(expo); - // Ref: https://ethdocs.org/en/latest/ether.html +// Note: this could be simplified using ** operator, but babel does not handle it well (https://github.com/babel/babel/issues/13109) /** @internal */ export const ethUnitMap = { - noether: BigInt('0'), + noether: BigInt(0), wei: BigInt(1), - kwei: expo10(3), - Kwei: expo10(3), - babbage: expo10(3), - femtoether: expo10(3), - mwei: expo10(6), - Mwei: expo10(6), - lovelace: expo10(6), - picoether: expo10(6), - gwei: expo10(9), - Gwei: expo10(9), - shannon: expo10(9), - nanoether: expo10(9), - nano: expo10(9), - szabo: expo10(12), - microether: expo10(12), - micro: expo10(12), - finney: expo10(15), - milliether: expo10(15), - milli: expo10(15), - ether: expo10(18), - kether: expo10(21), - grand: expo10(21), - mether: expo10(24), - gether: expo10(27), - tether: expo10(30), + kwei: BigInt(1000), + Kwei: BigInt(1000), + babbage: BigInt(1000), + femtoether: BigInt(1000), + mwei: BigInt(1000000), + Mwei: BigInt(1000000), + lovelace: BigInt(1000000), + picoether: BigInt(1000000), + gwei: BigInt(1000000000), + Gwei: BigInt(1000000000), + shannon: BigInt(1000000000), + nanoether: BigInt(1000000000), + nano: BigInt(1000000000), + szabo: BigInt(1000000000000), + microether: BigInt(1000000000000), + micro: BigInt(1000000000000), + finney: BigInt(1000000000000000), + milliether: BigInt(1000000000000000), + milli: BigInt(1000000000000000), + ether: BigInt('1000000000000000000'), + kether: BigInt('1000000000000000000000'), + grand: BigInt('1000000000000000000000'), + mether: BigInt('1000000000000000000000000'), + gether: BigInt('1000000000000000000000000000'), + tether: BigInt('1000000000000000000000000000000'), }; export type EtherUnits = keyof typeof ethUnitMap; diff --git a/packages/web3-utils/src/string_manipulation.ts b/packages/web3-utils/src/string_manipulation.ts index 7719afeba42..b7d0371d0b1 100644 --- a/packages/web3-utils/src/string_manipulation.ts +++ b/packages/web3-utils/src/string_manipulation.ts @@ -17,7 +17,7 @@ along with web3.js. If not, see . import { Numbers } from 'web3-types'; import { NibbleWidthError } from 'web3-errors'; -import { isHexStrict, validator, utils as validatorUtils } from 'web3-validator'; +import { isHexStrict, validator, utils as validatorUtils, bigintPower } from 'web3-validator'; import { numberToHex, toHex, toNumber } from './converters.js'; /** @@ -115,7 +115,7 @@ export const toTwosComplement = (value: Numbers, nibbleWidth = 64): string => { if (val >= 0) return padLeft(toHex(val), nibbleWidth); - const largestBit = BigInt(2) ** BigInt(nibbleWidth * 4); + const largestBit = bigintPower(BigInt(2), BigInt(nibbleWidth * 4)); if (-val >= largestBit) { throw new NibbleWidthError(`value: ${value}, nibbleWidth: ${nibbleWidth}`); } @@ -156,7 +156,7 @@ export const fromTwosComplement = (value: Numbers, nibbleWidth = 64): number | b // check the largest bit to see if negative if (nibbleWidth * 4 !== largestBit) return val; - const complement = BigInt(2) ** (BigInt(nibbleWidth) * BigInt(4)); + const complement = bigintPower(BigInt(2), BigInt(nibbleWidth) * BigInt(4)); return toNumber(BigInt(val) - complement); }; diff --git a/packages/web3-validator/CHANGELOG.md b/packages/web3-validator/CHANGELOG.md index fea00e98075..54cfe6080fd 100644 --- a/packages/web3-validator/CHANGELOG.md +++ b/packages/web3-validator/CHANGELOG.md @@ -151,4 +151,5 @@ Documentation: ## Fixed -- Multi-dimensional arrays are now handled properly when parsing ABIs +- Multi-dimensional arrays are now handled properly when parsing ABIs (#6435) +- Fix issue with default config with babel (and React): "TypeError: Cannot convert a BigInt value to a number #6187" (#6506) diff --git a/packages/web3-validator/src/validation/numbers.ts b/packages/web3-validator/src/validation/numbers.ts index 5ad606a5fe2..276fb84dd1e 100644 --- a/packages/web3-validator/src/validation/numbers.ts +++ b/packages/web3-validator/src/validation/numbers.ts @@ -24,6 +24,17 @@ import { isHexStrict } from './string.js'; */ export const isBigInt = (value: ValidInputTypes): boolean => typeof value === 'bigint'; +// Note: this could be simplified using ** operator, but babel does not handle it well +// you can find more at: https://github.com/babel/babel/issues/13109 and https://github.com/web3/web3.js/issues/6187 +/** @internal */ +export const bigintPower = (base: bigint, expo: bigint) => { + let res = base; + for (let index = 1; index < expo; index += 1) { + res *= base; + } + return res; +}; + export const isUInt = ( value: ValidInputTypes, options: { abiType: string; bitSize?: never } | { bitSize: number; abiType?: never } = { @@ -49,7 +60,7 @@ export const isUInt = ( size = options.bitSize; } - const maxSize = BigInt(2) ** BigInt(size ?? 256) - BigInt(1); + const maxSize = bigintPower(BigInt(2), BigInt(size ?? 256)) - BigInt(1); try { const valueToCheck = @@ -94,8 +105,8 @@ export const isInt = ( size = options.bitSize; } - const maxSize = BigInt(2) ** BigInt((size ?? 256) - 1); - const minSize = BigInt(-1) * BigInt(2) ** BigInt((size ?? 256) - 1); + const maxSize = bigintPower(BigInt(2), BigInt((size ?? 256) - 1)); + const minSize = BigInt(-1) * bigintPower(BigInt(2), BigInt((size ?? 256) - 1)); try { const valueToCheck =