From 367b97207779e4c792b0f347ce369faafff08580 Mon Sep 17 00:00:00 2001 From: legobt <6wbvkn0j@anonaddy.me> Date: Fri, 13 Oct 2023 11:43:16 +0000 Subject: [PATCH] WIP: migrate from @ethereumjs/utils BN to native BigInt --- .../src/AccountTrackerController.ts | 8 +- .../src/AssetsContractController.ts | 13 ++- .../src/NftController.test.ts | 9 +- .../assets-controllers/src/NftController.ts | 8 +- .../src/Standards/ERC20Standard.ts | 14 +-- .../NftStandards/ERC1155/ERC1155Standard.ts | 10 +- .../src/TokenBalancesController.test.ts | 18 +--- .../src/TokenBalancesController.ts | 10 +- .../src/TokenDetectionController.test.ts | 21 ++--- packages/assets-controllers/src/assetsUtil.ts | 10 +- packages/controller-utils/package.json | 1 + packages/controller-utils/src/util.test.ts | 91 +++++++++---------- packages/controller-utils/src/util.ts | 42 ++++----- packages/gas-fee-controller/package.json | 2 +- .../src/fetchBlockFeeHistory.test.ts | 11 +-- .../src/fetchBlockFeeHistory.ts | 45 +++++---- .../fetchGasEstimatesViaEthFeeHistory.test.ts | 15 ++- ...teGasFeeEstimatesForPriorityLevels.test.ts | 32 +++---- ...lculateGasFeeEstimatesForPriorityLevels.ts | 38 ++++---- .../medianOf.ts | 8 +- .../types.ts | 6 +- packages/gas-fee-controller/src/gas-util.ts | 35 +++---- packages/transaction-controller/package.json | 1 + .../src/EtherscanRemoteTransactionSource.ts | 11 +-- .../src/TransactionController.ts | 29 +++--- yarn.lock | 4 +- 26 files changed, 231 insertions(+), 261 deletions(-) diff --git a/packages/assets-controllers/src/AccountTrackerController.ts b/packages/assets-controllers/src/AccountTrackerController.ts index 84aa9cbc0f3..ee662ef1a8a 100644 --- a/packages/assets-controllers/src/AccountTrackerController.ts +++ b/packages/assets-controllers/src/AccountTrackerController.ts @@ -1,10 +1,6 @@ import type { BaseConfig, BaseState } from '@metamask/base-controller'; import { BaseController } from '@metamask/base-controller'; -import { - BNToHex, - query, - safelyExecuteWithTimeout, -} from '@metamask/controller-utils'; +import { query, safelyExecuteWithTimeout } from '@metamask/controller-utils'; import EthQuery from '@metamask/eth-query'; import type { Provider } from '@metamask/eth-query'; import type { PreferencesState } from '@metamask/preferences-controller'; @@ -176,7 +172,7 @@ export class AccountTrackerController extends BaseController< for (const address of accountsToUpdate) { accounts[address] = { - balance: BNToHex(await this.getBalanceFromChain(address)), + balance: (await this.getBalanceFromChain(address)) || '0x0', }; } diff --git a/packages/assets-controllers/src/AssetsContractController.ts b/packages/assets-controllers/src/AssetsContractController.ts index 5aabf5a0542..0a4cae02578 100644 --- a/packages/assets-controllers/src/AssetsContractController.ts +++ b/packages/assets-controllers/src/AssetsContractController.ts @@ -1,4 +1,3 @@ -import type { BN } from '@ethereumjs/util'; import { Contract } from '@ethersproject/contracts'; import { Web3Provider } from '@ethersproject/providers'; import type { BaseConfig, BaseState } from '@metamask/base-controller'; @@ -63,7 +62,7 @@ export interface AssetsContractConfig extends BaseConfig { * @property [tokenAddress] - Address of the token */ export interface BalanceMap { - [tokenAddress: string]: BN; + [tokenAddress: string]: bigint; } /** @@ -218,13 +217,13 @@ export class AssetsContractController extends BaseController< * @param address - Asset ERC20 contract address. * @param selectedAddress - Current account public address. * @param networkClientId - Network Client ID to fetch the provider with. - * @returns Promise resolving to BN object containing balance for current account on specific asset contract. + * @returns Promise resolving to BigInt value containing balance for current account on specific asset contract. */ async getERC20BalanceOf( address: string, selectedAddress: string, networkClientId?: NetworkClientId, - ): Promise { + ): Promise { const erc20Standard = this.getERC20Standard(networkClientId); return erc20Standard.getBalanceOf(address, selectedAddress); } @@ -298,7 +297,7 @@ export class AssetsContractController extends BaseController< symbol?: string | undefined; name?: string | undefined; decimals?: string | undefined; - balance?: BN | undefined; + balance?: bigint | undefined; }> { // Asserts provider is available this.getProvider(networkClientId); @@ -441,7 +440,7 @@ export class AssetsContractController extends BaseController< nftAddress: string, nftId: string, networkClientId?: NetworkClientId, - ): Promise { + ): Promise { const erc1155Standard = this.getERC1155Standard(networkClientId); return erc1155Standard.getBalanceOf(nftAddress, userAddress, nftId); } @@ -507,7 +506,7 @@ export class AssetsContractController extends BaseController< /* istanbul ignore else */ if (result.length > 0) { tokensToDetect.forEach((tokenAddress, index) => { - const balance: BN = result[index]; + const balance: bigint = result[index]; /* istanbul ignore else */ if (String(balance) !== '0') { nonZeroBalances[tokenAddress] = balance; diff --git a/packages/assets-controllers/src/NftController.test.ts b/packages/assets-controllers/src/NftController.test.ts index 94cac46efe5..ad87ac2eadb 100644 --- a/packages/assets-controllers/src/NftController.test.ts +++ b/packages/assets-controllers/src/NftController.test.ts @@ -1,4 +1,3 @@ -import { BN } from '@ethereumjs/util'; import type { Network } from '@ethersproject/providers'; import type { AddApprovalRequest, @@ -148,7 +147,7 @@ function setupController({ tokenAddress: string, tokenId: string, userAddress: string, - ) => Promise; + ) => Promise; } = {}) { const preferences = new PreferencesController(); const onNetworkStateChangeListeners: ((state: NetworkState) => void)[] = []; @@ -416,7 +415,7 @@ describe('NftController', () => { it('should error if the user does not own the suggested ERC1155 NFT', async function () { const { nftController, messenger } = setupController({ - getERC1155BalanceOfStub: jest.fn().mockImplementation(() => new BN(0)), + getERC1155BalanceOfStub: jest.fn().mockImplementation(() => BigInt(0)), }); const callActionSpy = jest.spyOn(messenger, 'call').mockResolvedValue({}); @@ -659,7 +658,7 @@ describe('NftController', () => { getERC1155TokenURIStub: jest .fn() .mockImplementation(() => 'https://testtokenuri.com'), - getERC1155BalanceOfStub: jest.fn().mockImplementation(() => new BN(1)), + getERC1155BalanceOfStub: jest.fn().mockImplementation(() => BigInt(1)), }); preferences.setOpenSeaEnabled(false); preferences.setIsIpfsGatewayEnabled(true); @@ -717,7 +716,7 @@ describe('NftController', () => { getERC1155TokenURIStub: jest .fn() .mockImplementation(() => 'https://testtokenuri.com'), - getERC1155BalanceOfStub: jest.fn().mockImplementation(() => new BN(1)), + getERC1155BalanceOfStub: jest.fn().mockImplementation(() => BigInt(1)), }); preferences.setOpenSeaEnabled(true); preferences.setIsIpfsGatewayEnabled(true); diff --git a/packages/assets-controllers/src/NftController.ts b/packages/assets-controllers/src/NftController.ts index 28b27e9503e..f936e971860 100644 --- a/packages/assets-controllers/src/NftController.ts +++ b/packages/assets-controllers/src/NftController.ts @@ -1,4 +1,4 @@ -import { BN, stripHexPrefix } from '@ethereumjs/util'; +import { stripHexPrefix } from '@ethereumjs/util'; import { isAddress } from '@ethersproject/address'; import type { AddApprovalRequest } from '@metamask/approval-controller'; import type { @@ -11,7 +11,6 @@ import { safelyExecute, handleFetch, toChecksumHexAddress, - BNToHex, fetchWithErrorHandling, IPFS_DEFAULT_GATEWAY_URL, ERC721, @@ -460,7 +459,8 @@ export class NftController extends BaseController { return [tokenURI, ERC1155]; } - const hexTokenId = stripHexPrefix(BNToHex(new BN(tokenId))) + const hexTokenId = BigInt(tokenId) + .toString(16) .padStart(64, '0') .toLowerCase(); return [tokenURI.replace('{id}', hexTokenId), ERC1155]; @@ -1234,7 +1234,7 @@ export class NftController extends BaseController { tokenId, networkClientId, ); - return !balance.isZero(); + return balance !== BigInt(0); // eslint-disable-next-line no-empty } catch { // Ignore ERC-1155 contract error diff --git a/packages/assets-controllers/src/Standards/ERC20Standard.ts b/packages/assets-controllers/src/Standards/ERC20Standard.ts index 8790fa9d78c..ac2058ef064 100644 --- a/packages/assets-controllers/src/Standards/ERC20Standard.ts +++ b/packages/assets-controllers/src/Standards/ERC20Standard.ts @@ -1,5 +1,4 @@ import { toUtf8 } from '@ethereumjs/util'; -import type { BN } from '@ethereumjs/util'; import { Contract } from '@ethersproject/contracts'; import type { Web3Provider } from '@ethersproject/providers'; import { decodeSingle } from '@metamask/abi-utils'; @@ -7,7 +6,7 @@ import { ERC20 } from '@metamask/controller-utils'; import { abiERC20 } from '@metamask/metamask-eth-abis'; import { assertIsStrictHexString } from '@metamask/utils'; -import { ethersBigNumberToBN } from '../assetsUtil'; +import { ethersBigNumberToBigInt } from '../assetsUtil'; export class ERC20Standard { private readonly provider: Web3Provider; @@ -21,12 +20,15 @@ export class ERC20Standard { * * @param address - Asset ERC20 contract address. * @param selectedAddress - Current account public address. - * @returns Promise resolving to BN object containing balance for current account on specific asset contract. + * @returns Promise resolving to BigInt value containing balance for current account on specific asset contract. */ - async getBalanceOf(address: string, selectedAddress: string): Promise { + async getBalanceOf( + address: string, + selectedAddress: string, + ): Promise { const contract = new Contract(address, abiERC20, this.provider); const balance = await contract.balanceOf(selectedAddress); - return ethersBigNumberToBN(balance); + return ethersBigNumberToBigInt(balance); } /** @@ -117,7 +119,7 @@ export class ERC20Standard { standard: string; symbol: string | undefined; decimals: string | undefined; - balance: BN | undefined; + balance: bigint | undefined; }> { const [decimals, symbol] = await Promise.all([ this.getTokenDecimals(address), diff --git a/packages/assets-controllers/src/Standards/NftStandards/ERC1155/ERC1155Standard.ts b/packages/assets-controllers/src/Standards/NftStandards/ERC1155/ERC1155Standard.ts index 52f9ffd8ae0..baebcfc045f 100644 --- a/packages/assets-controllers/src/Standards/NftStandards/ERC1155/ERC1155Standard.ts +++ b/packages/assets-controllers/src/Standards/NftStandards/ERC1155/ERC1155Standard.ts @@ -1,4 +1,3 @@ -import type { BN } from '@ethereumjs/util'; import { Contract } from '@ethersproject/contracts'; import type { Web3Provider } from '@ethersproject/providers'; import { @@ -10,7 +9,10 @@ import { } from '@metamask/controller-utils'; import { abiERC1155 } from '@metamask/metamask-eth-abis'; -import { getFormattedIpfsUrl, ethersBigNumberToBN } from '../../../assetsUtil'; +import { + getFormattedIpfsUrl, + ethersBigNumberToBigInt, +} from '../../../assetsUtil'; export class ERC1155Standard { private readonly provider: Web3Provider; @@ -85,10 +87,10 @@ export class ERC1155Standard { contractAddress: string, address: string, tokenId: string, - ): Promise => { + ): Promise => { const contract = new Contract(contractAddress, abiERC1155, this.provider); const balance = await contract.balanceOf(address, tokenId); - return ethersBigNumberToBN(balance); + return ethersBigNumberToBigInt(balance); }; /** diff --git a/packages/assets-controllers/src/TokenBalancesController.test.ts b/packages/assets-controllers/src/TokenBalancesController.test.ts index 0a968cef1a8..539bb0c4805 100644 --- a/packages/assets-controllers/src/TokenBalancesController.test.ts +++ b/packages/assets-controllers/src/TokenBalancesController.test.ts @@ -1,4 +1,3 @@ -import { BN } from '@ethereumjs/util'; import { ControllerMessenger } from '@metamask/base-controller'; import { toHex } from '@metamask/controller-utils'; import type { NetworkControllerMessenger } from '@metamask/network-controller'; @@ -7,10 +6,7 @@ import { PreferencesController } from '@metamask/preferences-controller'; import * as sinon from 'sinon'; import { AssetsContractController } from './AssetsContractController'; -import { - BN as exportedBn, - TokenBalancesController, -} from './TokenBalancesController'; +import { TokenBalancesController } from './TokenBalancesController'; import type { Token } from './TokenRatesController'; import type { TokensControllerMessenger } from './TokensController'; import { TokensController } from './TokensController'; @@ -36,10 +32,6 @@ describe('TokenBalancesController', () => { sinon.restore(); }); - it('should re-export BN', () => { - expect(exportedBn).toStrictEqual(BN); - }); - it('should set default state', () => { const tokenBalances = new TokenBalancesController({ onTokensStateChange: sinon.stub(), @@ -154,7 +146,7 @@ describe('TokenBalancesController', () => { { onTokensStateChange: (listener) => assets.subscribe(listener), getSelectedAddress: () => preferences.state.selectedAddress, - getERC20BalanceOf: sinon.stub().returns(new BN(1)), + getERC20BalanceOf: sinon.stub().returns(BigInt(1)), }, { interval: 1337, @@ -213,7 +205,7 @@ describe('TokenBalancesController', () => { expect(mytoken?.balanceError).toHaveProperty('message', errorMsg); expect(tokenBalances.state.contractBalances[address].toNumber()).toBe(0); - getERC20BalanceOfStub.returns(new BN(1)); + getERC20BalanceOfStub.returns(BigInt(1)); await tokenBalances.updateBalances(); expect(mytoken?.balanceError).toBeNull(); expect(Object.keys(tokenBalances.state.contractBalances)).toContain( @@ -282,7 +274,7 @@ describe('TokenBalancesController', () => { { onTokensStateChange, getSelectedAddress: () => '0x1234', - getERC20BalanceOf: sinon.stub().returns(new BN(1)), + getERC20BalanceOf: sinon.stub().returns(BigInt(1)), }, { interval: 1337, @@ -308,7 +300,7 @@ describe('TokenBalancesController', () => { await tokenBalances.updateBalances(); expect(tokenBalances.state.contractBalances).toStrictEqual({ - '0x02': new BN(1), + '0x02': BigInt(1), }); }); }); diff --git a/packages/assets-controllers/src/TokenBalancesController.ts b/packages/assets-controllers/src/TokenBalancesController.ts index 796db303b4c..91a5fa4f8fa 100644 --- a/packages/assets-controllers/src/TokenBalancesController.ts +++ b/packages/assets-controllers/src/TokenBalancesController.ts @@ -1,4 +1,3 @@ -import { BN } from '@ethereumjs/util'; import type { BaseConfig, BaseState } from '@metamask/base-controller'; import { BaseController } from '@metamask/base-controller'; import { safelyExecute } from '@metamask/controller-utils'; @@ -8,9 +7,6 @@ import type { AssetsContractController } from './AssetsContractController'; import type { Token } from './TokenRatesController'; import type { TokensState } from './TokensController'; -// TODO: Remove this export in the next major release -export { BN }; - /** * @type TokenBalancesConfig * @@ -30,7 +26,7 @@ export interface TokenBalancesConfig extends BaseConfig { * @property contractBalances - Hash of token contract addresses to balances */ export interface TokenBalancesState extends BaseState { - contractBalances: { [address: string]: BN }; + contractBalances: { [address: string]: bigint }; } /** @@ -115,7 +111,7 @@ export class TokenBalancesController extends BaseController< return; } const { tokens } = this.config; - const newContractBalances: { [address: string]: BN } = {}; + const newContractBalances: { [address: string]: bigint } = {}; for (const i in tokens) { const { address } = tokens[i]; try { @@ -125,7 +121,7 @@ export class TokenBalancesController extends BaseController< ); tokens[i].balanceError = null; } catch (error) { - newContractBalances[address] = new BN(0); + newContractBalances[address] = BigInt(0); tokens[i].balanceError = error; } } diff --git a/packages/assets-controllers/src/TokenDetectionController.test.ts b/packages/assets-controllers/src/TokenDetectionController.test.ts index 9ad4c781aec..40cf294e407 100644 --- a/packages/assets-controllers/src/TokenDetectionController.test.ts +++ b/packages/assets-controllers/src/TokenDetectionController.test.ts @@ -1,4 +1,3 @@ -import { BN } from '@ethereumjs/util'; import { ControllerMessenger } from '@metamask/base-controller'; import { ChainId, @@ -275,7 +274,7 @@ describe('TokenDetectionController', () => { }); getBalancesInSingleCall.resolves({ - [sampleTokenA.address]: new BN(1), + [sampleTokenA.address]: BigInt(1), }); await tokenDetection.start(); expect(tokensController.state.detectedTokens).toStrictEqual([]); @@ -286,7 +285,7 @@ describe('TokenDetectionController', () => { changeNetwork(mainnet); getBalancesInSingleCall.resolves({ - [sampleTokenA.address]: new BN(1), + [sampleTokenA.address]: BigInt(1), }); await tokenDetection.start(); expect(tokensController.state.detectedTokens).toStrictEqual([sampleTokenA]); @@ -302,7 +301,7 @@ describe('TokenDetectionController', () => { changeNetwork(auroraMainnet); getBalancesInSingleCall.resolves({ - [sampleTokenA.address]: new BN(1), + [sampleTokenA.address]: BigInt(1), }); await tokenDetection.start(); expect(tokensController.state.detectedTokens).toStrictEqual([sampleTokenA]); @@ -315,13 +314,13 @@ describe('TokenDetectionController', () => { await tokenDetection.start(); getBalancesInSingleCall.resolves({ - [sampleTokenA.address]: new BN(1), + [sampleTokenA.address]: BigInt(1), }); await tokenDetection.detectTokens(); expect(tokensController.state.detectedTokens).toStrictEqual([sampleTokenA]); getBalancesInSingleCall.resolves({ - [sampleTokenB.address]: new BN(1), + [sampleTokenB.address]: BigInt(1), }); await tokenDetection.detectTokens(); expect(tokensController.state.detectedTokens).toStrictEqual([ @@ -353,7 +352,7 @@ describe('TokenDetectionController', () => { tokensController.ignoreTokens([sampleTokenA.address]); getBalancesInSingleCall.resolves({ - [sampleTokenA.address]: new BN(1), + [sampleTokenA.address]: BigInt(1), }); await tokenDetection.detectTokens(); expect(tokensController.state.tokens).toStrictEqual([sampleTokenB]); @@ -380,7 +379,7 @@ describe('TokenDetectionController', () => { preferences.setSelectedAddress('0x0002'); getBalancesInSingleCall.resolves({ - [sampleTokenA.address]: new BN(1), + [sampleTokenA.address]: BigInt(1), }); await tokenDetection.detectTokens(); expect(tokensController.state.detectedTokens).toStrictEqual([sampleTokenA]); @@ -393,7 +392,7 @@ describe('TokenDetectionController', () => { await tokenDetection.start(); getBalancesInSingleCall.resolves({ - [sampleTokenA.address]: new BN(1), + [sampleTokenA.address]: BigInt(1), }); await tokenDetection.detectTokens(); @@ -407,7 +406,7 @@ describe('TokenDetectionController', () => { it('should not detect tokens if there is no selectedAddress set', async () => { await tokenDetection.start(); getBalancesInSingleCall.resolves({ - [sampleTokenA.address]: new BN(1), + [sampleTokenA.address]: BigInt(1), }); await tokenDetection.detectTokens(); expect(tokensController.state.detectedTokens).toStrictEqual([]); @@ -418,7 +417,7 @@ describe('TokenDetectionController', () => { changeNetwork(mainnet); getBalancesInSingleCall.resolves({ - [sampleTokenA.address]: new BN(1), + [sampleTokenA.address]: BigInt(1), }); await tokenDetection.start(); expect(tokensController.state.detectedTokens).toStrictEqual([sampleTokenA]); diff --git a/packages/assets-controllers/src/assetsUtil.ts b/packages/assets-controllers/src/assetsUtil.ts index 4aeae85c019..7708c21930d 100644 --- a/packages/assets-controllers/src/assetsUtil.ts +++ b/packages/assets-controllers/src/assetsUtil.ts @@ -1,4 +1,4 @@ -import { BN, stripHexPrefix } from '@ethereumjs/util'; +import { addHexPrefix } from '@ethereumjs/util'; import type { BigNumber } from '@ethersproject/bignumber'; import { convertHexToDecimal, @@ -253,11 +253,11 @@ export function addUrlProtocolPrefix(urlString: string): string { } /** - * Converts an Ethers BigNumber to a BN. + * Converts an Ethers BigNumber to a BigInt. * * @param bigNumber - An Ethers BigNumber instance. - * @returns A BN object. + * @returns A bigint. */ -export function ethersBigNumberToBN(bigNumber: BigNumber): BN { - return new BN(stripHexPrefix(bigNumber.toHexString()), 'hex'); +export function ethersBigNumberToBigInt(bigNumber: BigNumber): bigint { + return BigInt(addHexPrefix(bigNumber.toHexString())); } diff --git a/packages/controller-utils/package.json b/packages/controller-utils/package.json index a66b5470fee..dd2cdc3138e 100644 --- a/packages/controller-utils/package.json +++ b/packages/controller-utils/package.json @@ -30,6 +30,7 @@ }, "dependencies": { "@ethereumjs/util": "^8.1.0", + "@ethersproject/bignumber": "^5.7.0", "@metamask/eth-query": "^3.0.1", "@metamask/utils": "^8.1.0", "@spruceid/siwe-parser": "1.1.3", diff --git a/packages/controller-utils/src/util.test.ts b/packages/controller-utils/src/util.test.ts index a4eff7554bf..443710d3c36 100644 --- a/packages/controller-utils/src/util.test.ts +++ b/packages/controller-utils/src/util.test.ts @@ -1,4 +1,3 @@ -import { BN } from '@ethereumjs/util'; import nock from 'nock'; import { MAX_SAFE_CHAIN_ID } from './constants'; @@ -19,11 +18,11 @@ describe('util', () => { }); it('bNToHex', () => { - expect(util.BNToHex(new BN('1337'))).toBe('0x539'); + expect(util.BNToHex(BigInt('1337'))).toBe('0x539'); }); - it('fractionBN', () => { - expect(util.fractionBN(new BN('1337'), 9, 10).toNumber()).toBe(1203); + it('fractionBigInt', () => { + expect(Number(util.fractionBigInt(BigInt('1337'), 9, 10))).toBe(1203); }); it('getBuyURL', () => { @@ -40,27 +39,27 @@ describe('util', () => { }); it('hexToBN', () => { - expect(util.hexToBN('0x1337').toNumber()).toBe(4919); + expect(Number(BigInt('0x1337'))).toBe(4919); }); describe('fromHex', () => { it('converts a string that represents a number in hexadecimal format with leading "0x" into a BN', () => { - expect(util.fromHex('0x1337')).toStrictEqual(new BN(4919)); + expect(util.fromHex('0x1337')).toStrictEqual(BigInt(4919)); }); it('converts a string that represents a number in hexadecimal format without leading "0x" into a BN', () => { - expect(util.fromHex('1337')).toStrictEqual(new BN(4919)); + expect(util.fromHex('1337')).toStrictEqual(BigInt(4919)); }); it('does nothing to a BN', () => { - const bn = new BN(4919); + const bn = BigInt(4919); expect(util.fromHex(bn)).toBe(bn); }); }); describe('toHex', () => { it('converts a BN to a hex string prepended with "0x"', () => { - expect(util.toHex(new BN(4919))).toBe('0x1337'); + expect(util.toHex(BigInt(4919))).toBe('0x1337'); }); it('parses a string as a number in decimal format and converts it to a hex string prepended with "0x"', () => { @@ -90,72 +89,72 @@ describe('util', () => { describe('gweiDecToWEIBN', () => { it('should convert a whole number to WEI', () => { - expect(util.gweiDecToWEIBN(1).toNumber()).toBe(1000000000); - expect(util.gweiDecToWEIBN(123).toNumber()).toBe(123000000000); - expect(util.gweiDecToWEIBN(101).toNumber()).toBe(101000000000); - expect(util.gweiDecToWEIBN(1234).toNumber()).toBe(1234000000000); - expect(util.gweiDecToWEIBN(1000).toNumber()).toBe(1000000000000); + expect(Number(util.gweiDecToWeiBigInt(1))).toBe(1000000000); + expect(Number(util.gweiDecToWeiBigInt(123))).toBe(123000000000); + expect(Number(util.gweiDecToWeiBigInt(101))).toBe(101000000000); + expect(Number(util.gweiDecToWeiBigInt(1234))).toBe(1234000000000); + expect(Number(util.gweiDecToWeiBigInt(1000))).toBe(1000000000000); }); it('should convert a number with a decimal part to WEI', () => { - expect(util.gweiDecToWEIBN(1.1).toNumber()).toBe(1100000000); - expect(util.gweiDecToWEIBN(123.01).toNumber()).toBe(123010000000); - expect(util.gweiDecToWEIBN(101.001).toNumber()).toBe(101001000000); - expect(util.gweiDecToWEIBN(100.001).toNumber()).toBe(100001000000); - expect(util.gweiDecToWEIBN(1234.567).toNumber()).toBe(1234567000000); + expect(Number(util.gweiDecToWeiBigInt(1.1))).toBe(1100000000); + expect(Number(util.gweiDecToWeiBigInt(123.01))).toBe(123010000000); + expect(Number(util.gweiDecToWeiBigInt(101.001))).toBe(101001000000); + expect(Number(util.gweiDecToWeiBigInt(100.001))).toBe(100001000000); + expect(Number(util.gweiDecToWeiBigInt(1234.567))).toBe(1234567000000); }); it('should convert a number < 1 to WEI', () => { - expect(util.gweiDecToWEIBN(0.1).toNumber()).toBe(100000000); - expect(util.gweiDecToWEIBN(0.01).toNumber()).toBe(10000000); - expect(util.gweiDecToWEIBN(0.001).toNumber()).toBe(1000000); - expect(util.gweiDecToWEIBN(0.567).toNumber()).toBe(567000000); + expect(Number(util.gweiDecToWeiBigInt(0.1))).toBe(100000000); + expect(Number(util.gweiDecToWeiBigInt(0.01))).toBe(10000000); + expect(Number(util.gweiDecToWeiBigInt(0.001))).toBe(1000000); + expect(Number(util.gweiDecToWeiBigInt(0.567))).toBe(567000000); }); it('should round to whole WEI numbers', () => { - expect(util.gweiDecToWEIBN(0.1001).toNumber()).toBe(100100000); - expect(util.gweiDecToWEIBN(0.0109).toNumber()).toBe(10900000); - expect(util.gweiDecToWEIBN(0.0014).toNumber()).toBe(1400000); - expect(util.gweiDecToWEIBN(0.5676).toNumber()).toBe(567600000); + expect(Number(util.gweiDecToWeiBigInt(0.1001))).toBe(100100000); + expect(Number(util.gweiDecToWeiBigInt(0.0109))).toBe(10900000); + expect(Number(util.gweiDecToWeiBigInt(0.0014))).toBe(1400000); + expect(Number(util.gweiDecToWeiBigInt(0.5676))).toBe(567600000); }); it('should handle inputs with more than 9 decimal places', () => { - expect(util.gweiDecToWEIBN(1.0000000162).toNumber()).toBe(1000000016); - expect(util.gweiDecToWEIBN(1.0000000165).toNumber()).toBe(1000000017); - expect(util.gweiDecToWEIBN(1.0000000199).toNumber()).toBe(1000000020); - expect(util.gweiDecToWEIBN(1.9999999999).toNumber()).toBe(2000000000); - expect(util.gweiDecToWEIBN(1.0000005998).toNumber()).toBe(1000000600); - expect(util.gweiDecToWEIBN(123456.0000005998).toNumber()).toBe( + expect(Number(util.gweiDecToWeiBigInt(1.0000000162))).toBe(1000000016); + expect(Number(util.gweiDecToWeiBigInt(1.0000000165))).toBe(1000000017); + expect(Number(util.gweiDecToWeiBigInt(1.0000000199))).toBe(1000000020); + expect(Number(util.gweiDecToWeiBigInt(1.9999999999))).toBe(2000000000); + expect(Number(util.gweiDecToWeiBigInt(1.0000005998))).toBe(1000000600); + expect(Number(util.gweiDecToWeiBigInt(123456.0000005998))).toBe( 123456000000600, ); - expect(util.gweiDecToWEIBN(1.000000016025).toNumber()).toBe(1000000016); - expect(util.gweiDecToWEIBN(1.0000000160000028).toNumber()).toBe( + expect(Number(util.gweiDecToWeiBigInt(1.000000016025))).toBe(1000000016); + expect(Number(util.gweiDecToWeiBigInt(1.0000000160000028))).toBe( 1000000016, ); - expect(util.gweiDecToWEIBN(1.000000016522).toNumber()).toBe(1000000017); - expect(util.gweiDecToWEIBN(1.000000016800022).toNumber()).toBe( + expect(Number(util.gweiDecToWeiBigInt(1.000000016522))).toBe(1000000017); + expect(Number(util.gweiDecToWeiBigInt(1.000000016800022))).toBe( 1000000017, ); }); it('should work if there are extraneous trailing decimal zeroes', () => { - expect(util.gweiDecToWEIBN('0.5000').toNumber()).toBe(500000000); - expect(util.gweiDecToWEIBN('123.002300').toNumber()).toBe(123002300000); - expect(util.gweiDecToWEIBN('123.002300000000').toNumber()).toBe( + expect(Number(util.gweiDecToWeiBigInt('0.5000'))).toBe(500000000); + expect(Number(util.gweiDecToWeiBigInt('123.002300'))).toBe(123002300000); + expect(Number(util.gweiDecToWeiBigInt('123.002300000000'))).toBe( 123002300000, ); - expect(util.gweiDecToWEIBN('0.00000200000').toNumber()).toBe(2000); + expect(Number(util.gweiDecToWeiBigInt('0.00000200000'))).toBe(2000); }); it('should work if there is no whole number specified', () => { - expect(util.gweiDecToWEIBN('.1').toNumber()).toBe(100000000); - expect(util.gweiDecToWEIBN('.01').toNumber()).toBe(10000000); - expect(util.gweiDecToWEIBN('.001').toNumber()).toBe(1000000); - expect(util.gweiDecToWEIBN('.567').toNumber()).toBe(567000000); + expect(Number(util.gweiDecToWeiBigInt('.1'))).toBe(100000000); + expect(Number(util.gweiDecToWeiBigInt('.01'))).toBe(10000000); + expect(Number(util.gweiDecToWeiBigInt('.001'))).toBe(1000000); + expect(Number(util.gweiDecToWeiBigInt('.567'))).toBe(567000000); }); it('should handle NaN', () => { - expect(util.gweiDecToWEIBN(NaN).toNumber()).toBe(0); + expect(Number(util.gweiDecToWeiBigInt(NaN))).toBe(0); }); }); diff --git a/packages/controller-utils/src/util.ts b/packages/controller-utils/src/util.ts index b07f010caf5..03b8eb5fffa 100644 --- a/packages/controller-utils/src/util.ts +++ b/packages/controller-utils/src/util.ts @@ -2,7 +2,6 @@ import { addHexPrefix, isValidAddress, isHexString, - BN, toChecksumAddress, stripHexPrefix, } from '@ethereumjs/util'; @@ -42,7 +41,7 @@ export function isSafeChainId(chainId: Hex): boolean { * @param inputBn - BN instance to convert to a hex string. * @returns A '0x'-prefixed hex string. */ -export function BNToHex(inputBn: any) { +export function BNToHex(inputBn: bigint) { return addHexPrefix(inputBn.toString(16)); } @@ -54,14 +53,14 @@ export function BNToHex(inputBn: any) { * @param denominator - Denominator of the fraction multiplier. * @returns Product of the multiplication. */ -export function fractionBN( - targetBN: any, +export function fractionBigInt( + targetBN: bigint, numerator: number | string, denominator: number | string, -) { - const numBN = new BN(numerator); - const denomBN = new BN(denominator); - return targetBN.mul(numBN).div(denomBN); +): bigint { + const numBN = BigInt(numerator); + const denomBN = BigInt(denominator); + return (targetBN * numBN) / denomBN; } /** @@ -70,9 +69,9 @@ export function fractionBN( * @param n - The base 10 number to convert to WEI. * @returns The number in WEI, as a BN. */ -export function gweiDecToWEIBN(n: number | string) { +export function gweiDecToWeiBigInt(n: number | string): bigint { if (Number.isNaN(n)) { - return new BN(0); + return BigInt(0); } const parts = n.toString().split('.'); @@ -94,7 +93,7 @@ export function gweiDecToWEIBN(n: number | string) { let wei = toWei(`${wholePart}.${decimalPart}`, 'gwei'); if (Number(decimalRoundingDigit) >= 5) { - wei = wei.add(new BN(1)); + wei = wei.add(BigInt(1)); } return wei; @@ -107,7 +106,7 @@ export function gweiDecToWEIBN(n: number | string) { * @returns The value in dec gwei as string. */ export function weiHexToGweiDec(hex: string) { - const hexWei = new BN(stripHexPrefix(hex), 16); + const hexWei = BigInt(addHexPrefix(hex)); return fromWei(hexWei, 'gwei').toString(10); } @@ -142,8 +141,8 @@ export function getBuyURL( * @param inputHex - Number represented as a hex string. * @returns A BN instance. */ -export function hexToBN(inputHex: string) { - return inputHex ? new BN(stripHexPrefix(inputHex), 16) : new BN(0); +export function hexToBigInt(inputHex: string) { + return inputHex ? BigInt(addHexPrefix(inputHex)) : BigInt(0); } /** @@ -170,11 +169,11 @@ export function hexToText(hex: string) { * @param value - A base-16 number encoded as a string. * @returns The number as a BN object in base-16 mode. */ -export function fromHex(value: string | BN): BN { - if (BN.isBN(value)) { +export function fromHex(value: string | bigint): bigint { + if (typeof value === 'bigint') { return value; } - return new BN(hexToBN(value).toString(10)); + return BigInt(hexToBigInt(value).toString(10)); } /** @@ -183,13 +182,14 @@ export function fromHex(value: string | BN): BN { * @param value - An integer, an integer encoded as a base-10 string, or a BN. * @returns The integer encoded as a hex string. */ -export function toHex(value: number | string | BN): Hex { +export function toHex(value: number | string | bigint): Hex { if (typeof value === 'string' && isStrictHexString(value)) { return value; } - const hexString = BN.isBN(value) - ? value.toString(16) - : new BN(value.toString(), 10).toString(16); + const hexString = + typeof value === 'bigint' + ? value.toString(16) + : BigInt(value.toString()).toString(16); return `0x${hexString}`; } diff --git a/packages/gas-fee-controller/package.json b/packages/gas-fee-controller/package.json index 226b9a9b183..aeb1fd2deff 100644 --- a/packages/gas-fee-controller/package.json +++ b/packages/gas-fee-controller/package.json @@ -29,7 +29,7 @@ "test:watch": "jest --watch" }, "dependencies": { - "@ethereumjs/util": "^8.1.0", + "@ethersproject/bignumber": "^5.7.0", "@metamask/base-controller": "^3.2.3", "@metamask/controller-utils": "^5.0.2", "@metamask/eth-query": "^3.0.1", diff --git a/packages/gas-fee-controller/src/fetchBlockFeeHistory.test.ts b/packages/gas-fee-controller/src/fetchBlockFeeHistory.test.ts index 39fea4415f1..f3c6c7dec1a 100644 --- a/packages/gas-fee-controller/src/fetchBlockFeeHistory.test.ts +++ b/packages/gas-fee-controller/src/fetchBlockFeeHistory.test.ts @@ -1,4 +1,3 @@ -import { BN } from '@ethereumjs/util'; import { query, fromHex, toHex } from '@metamask/controller-utils'; import { when } from 'jest-when'; @@ -43,7 +42,7 @@ describe('fetchBlockFeeHistory', () => { when(mockedQuery) // @ts-expect-error Mock eth query does not fulfill type requirements .calledWith(ethQuery, 'blockNumber') - .mockResolvedValue(new BN(latestBlockNumber)); + .mockResolvedValue(BigInt(latestBlockNumber)); }); it('should return a representation of fee history from the Ethereum network, organized by block rather than type of data', async () => { @@ -160,7 +159,7 @@ describe('fetchBlockFeeHistory', () => { when(mockedQuery) // @ts-expect-error Mock eth query does not fulfill type requirements .calledWith(ethQuery, 'blockNumber') - .mockResolvedValue(new BN(latestBlockNumber)); + .mockResolvedValue(BigInt(latestBlockNumber)); expectedChunks.forEach(({ startBlockNumber, endBlockNumber }) => { const baseFeePerGas = expectedBlocks @@ -206,7 +205,7 @@ describe('fetchBlockFeeHistory', () => { it('should pass it to the eth_feeHistory call', async () => { const latestBlockNumber = 3; const numberOfRequestedBlocks = 3; - const endBlock = new BN(latestBlockNumber); + const endBlock = BigInt(latestBlockNumber); when(mockedQuery) // @ts-expect-error Mock eth query does not fulfill type requirements .calledWith(ethQuery, 'eth_feeHistory', [ @@ -238,7 +237,7 @@ describe('fetchBlockFeeHistory', () => { when(mockedQuery) // @ts-expect-error Mock eth query does not fulfill type requirements .calledWith(ethQuery, 'blockNumber') - .mockResolvedValue(new BN(latestBlockNumber)); + .mockResolvedValue(BigInt(latestBlockNumber)); }); it('should match each item in the "reward" key from the response to its percentile', async () => { @@ -412,7 +411,7 @@ describe('fetchBlockFeeHistory', () => { it('should adjust fetched numberOfBlocks', async () => { const latestBlockNumber = 1024; const numberOfRequestedBlocks = 2048; - const endBlock = new BN(latestBlockNumber); + const endBlock = BigInt(latestBlockNumber); when(mockedQuery) // @ts-expect-error Mock eth query does not fulfill type requirements diff --git a/packages/gas-fee-controller/src/fetchBlockFeeHistory.ts b/packages/gas-fee-controller/src/fetchBlockFeeHistory.ts index 899e9786aed..33a2974ebb6 100644 --- a/packages/gas-fee-controller/src/fetchBlockFeeHistory.ts +++ b/packages/gas-fee-controller/src/fetchBlockFeeHistory.ts @@ -1,4 +1,3 @@ -import { BN } from '@ethereumjs/util'; import { query, fromHex, toHex } from '@metamask/controller-utils'; type EthQuery = any; @@ -12,7 +11,7 @@ type EthQuery = any; */ type RequestChunkSpecifier = { numberOfBlocks: number; - endBlockNumber: BN; + endBlockNumber: bigint; }; /** @@ -51,10 +50,10 @@ export type EthFeeHistoryResponse = { * on how this works.) */ type ExistingFeeHistoryBlock = { - number: BN; - baseFeePerGas: BN; + number: bigint; + baseFeePerGas: bigint; gasUsedRatio: number; - priorityFeesByPercentile: Record; + priorityFeesByPercentile: Record; }; /** @@ -65,8 +64,8 @@ type ExistingFeeHistoryBlock = { * @property baseFeePerGas - The estimated base fee per gas for the block in WEI, as a BN. */ type NextFeeHistoryBlock = { - number: BN; - baseFeePerGas: BN; + number: bigint; + baseFeePerGas: bigint; }; /** @@ -140,7 +139,7 @@ export default async function fetchBlockFeeHistory({ }: { ethQuery: EthQuery; numberOfBlocks: number; - endBlock?: 'latest' | BN; + endBlock?: 'latest' | bigint; percentiles?: readonly Percentile[]; includeNextBlock?: boolean; }): Promise[]> { @@ -205,8 +204,8 @@ function buildExistingFeeHistoryBlock({ priorityFeePercentileGroups, percentiles, }: { - baseFeePerGas: BN; - number: BN; + baseFeePerGas: bigint; + number: bigint; blockIndex: number; gasUsedRatios: number[]; priorityFeePercentileGroups: string[][]; @@ -219,7 +218,7 @@ function buildExistingFeeHistoryBlock({ const priorityFee = priorityFeesForEachPercentile[percentileIndex]; return { ...obj, [percentile]: fromHex(priorityFee) }; }, - {} as Record, + {} as Record, ); return { @@ -242,8 +241,8 @@ function buildNextFeeHistoryBlock({ baseFeePerGas, number, }: { - baseFeePerGas: BN; - number: BN; + baseFeePerGas: bigint; + number: bigint; }) { return { number, @@ -277,7 +276,7 @@ async function makeRequestForChunk({ }: { ethQuery: EthQuery; numberOfBlocks: number; - endBlockNumber: BN; + endBlockNumber: bigint; percentiles: readonly Percentile[]; includeNextBlock: boolean; }): Promise[]> { @@ -309,7 +308,7 @@ async function makeRequestForChunk({ return baseFeesPerGasAsHex.map((baseFeePerGasAsHex, blockIndex) => { const baseFeePerGas = fromHex(baseFeePerGasAsHex); - const number = startBlockNumber.addn(blockIndex); + const number = startBlockNumber + BigInt(blockIndex); return blockIndex >= numberOfExistingResults ? buildNextFeeHistoryBlock({ baseFeePerGas, number }) @@ -342,27 +341,27 @@ async function makeRequestForChunk({ * retrieve all of the requested blocks, sorted from oldest block to newest block. */ function determineRequestChunkSpecifiers( - endBlockNumber: BN, + endBlockNumber: bigint, totalNumberOfBlocks: number, ): RequestChunkSpecifier[] { - if (endBlockNumber.lt(new BN(totalNumberOfBlocks))) { - totalNumberOfBlocks = endBlockNumber.toNumber(); + if (endBlockNumber < BigInt(totalNumberOfBlocks)) { + totalNumberOfBlocks = Number(endBlockNumber); } const specifiers = []; for ( - let chunkStartBlockNumber = endBlockNumber.subn(totalNumberOfBlocks); - chunkStartBlockNumber.lt(endBlockNumber); - chunkStartBlockNumber = chunkStartBlockNumber.addn( + let chunkStartBlockNumber = endBlockNumber - BigInt(totalNumberOfBlocks); + chunkStartBlockNumber < endBlockNumber; + chunkStartBlockNumber += BigInt( MAX_NUMBER_OF_BLOCKS_PER_ETH_FEE_HISTORY_CALL, ) ) { - const distanceToEnd = endBlockNumber.sub(chunkStartBlockNumber).toNumber(); + const distanceToEnd = Number(endBlockNumber - chunkStartBlockNumber); const numberOfBlocks = distanceToEnd < MAX_NUMBER_OF_BLOCKS_PER_ETH_FEE_HISTORY_CALL ? distanceToEnd : MAX_NUMBER_OF_BLOCKS_PER_ETH_FEE_HISTORY_CALL; - const chunkEndBlockNumber = chunkStartBlockNumber.addn(numberOfBlocks); + const chunkEndBlockNumber = chunkStartBlockNumber + BigInt(numberOfBlocks); specifiers.push({ numberOfBlocks, endBlockNumber: chunkEndBlockNumber }); } return specifiers; diff --git a/packages/gas-fee-controller/src/fetchGasEstimatesViaEthFeeHistory.test.ts b/packages/gas-fee-controller/src/fetchGasEstimatesViaEthFeeHistory.test.ts index c2850c48146..3b1ffa8c176 100644 --- a/packages/gas-fee-controller/src/fetchGasEstimatesViaEthFeeHistory.test.ts +++ b/packages/gas-fee-controller/src/fetchGasEstimatesViaEthFeeHistory.test.ts @@ -1,4 +1,3 @@ -import { BN } from '@ethereumjs/util'; import { when } from 'jest-when'; import fetchBlockFeeHistory from './fetchBlockFeeHistory'; @@ -28,8 +27,8 @@ const mockedFetchLatestBlock = fetchLatestBlock as jest.Mock< describe('fetchGasEstimatesViaEthFeeHistory', () => { const latestBlock = { - number: new BN(1), - baseFeePerGas: new BN(100_000_000_000), + number: BigInt(1), + baseFeePerGas: BigInt(100_000_000_000), }; const ethQuery = { blockNumber: async () => latestBlock.number, @@ -39,13 +38,13 @@ describe('fetchGasEstimatesViaEthFeeHistory', () => { it('calculates target fees for low, medium, and high transaction priority levels', async () => { const blocks = [ { - number: new BN(3), - baseFeePerGas: new BN(1), + number: BigInt(3), + baseFeePerGas: BigInt(1), gasUsedRatio: 1, priorityFeesByPercentile: { - 10: new BN('0'), - 20: new BN('0'), - 30: new BN('0'), + 10: BigInt('0'), + 20: BigInt('0'), + 30: BigInt('0'), }, }, ]; diff --git a/packages/gas-fee-controller/src/fetchGasEstimatesViaEthFeeHistory/calculateGasFeeEstimatesForPriorityLevels.test.ts b/packages/gas-fee-controller/src/fetchGasEstimatesViaEthFeeHistory/calculateGasFeeEstimatesForPriorityLevels.test.ts index 78b14d1e32f..eb57c664524 100644 --- a/packages/gas-fee-controller/src/fetchGasEstimatesViaEthFeeHistory/calculateGasFeeEstimatesForPriorityLevels.test.ts +++ b/packages/gas-fee-controller/src/fetchGasEstimatesViaEthFeeHistory/calculateGasFeeEstimatesForPriorityLevels.test.ts @@ -1,38 +1,36 @@ -import { BN } from '@ethereumjs/util'; - import calculateGasFeeEstimatesForPriorityLevels from './calculateGasFeeEstimatesForPriorityLevels'; describe('calculateGasFeeEstimatesForPriorityLevels', () => { it('calculates a set of gas fee estimates targeting various priority levels based on the given blocks', () => { const estimates = calculateGasFeeEstimatesForPriorityLevels([ { - number: new BN(1), - baseFeePerGas: new BN(300_000_000_000), + number: BigInt(1), + baseFeePerGas: BigInt(300_000_000_000), gasUsedRatio: 1, priorityFeesByPercentile: { - 10: new BN(0), - 20: new BN(1_000_000_000), - 30: new BN(0), + 10: BigInt(0), + 20: BigInt(1_000_000_000), + 30: BigInt(0), }, }, { - number: new BN(2), - baseFeePerGas: new BN(100_000_000_000), + number: BigInt(2), + baseFeePerGas: BigInt(100_000_000_000), gasUsedRatio: 1, priorityFeesByPercentile: { - 10: new BN(500_000_000), - 20: new BN(1_600_000_000), - 30: new BN(3_000_000_000), + 10: BigInt(500_000_000), + 20: BigInt(1_600_000_000), + 30: BigInt(3_000_000_000), }, }, { - number: new BN(3), - baseFeePerGas: new BN(200_000_000_000), + number: BigInt(3), + baseFeePerGas: BigInt(200_000_000_000), gasUsedRatio: 1, priorityFeesByPercentile: { - 10: new BN(500_000_000), - 20: new BN(2_000_000_000), - 30: new BN(3_000_000_000), + 10: BigInt(500_000_000), + 20: BigInt(2_000_000_000), + 30: BigInt(3_000_000_000), }, }, ]); diff --git a/packages/gas-fee-controller/src/fetchGasEstimatesViaEthFeeHistory/calculateGasFeeEstimatesForPriorityLevels.ts b/packages/gas-fee-controller/src/fetchGasEstimatesViaEthFeeHistory/calculateGasFeeEstimatesForPriorityLevels.ts index 251510b5207..99799ba587e 100644 --- a/packages/gas-fee-controller/src/fetchGasEstimatesViaEthFeeHistory/calculateGasFeeEstimatesForPriorityLevels.ts +++ b/packages/gas-fee-controller/src/fetchGasEstimatesViaEthFeeHistory/calculateGasFeeEstimatesForPriorityLevels.ts @@ -1,4 +1,3 @@ -import { BN } from '@ethereumjs/util'; import { GWEI } from '@metamask/controller-utils'; import { fromWei } from 'ethjs-unit'; @@ -14,9 +13,9 @@ const PRIORITY_LEVEL_PERCENTILES = [10, 20, 30] as const; const SETTINGS_BY_PRIORITY_LEVEL = { low: { percentile: 10 as Percentile, - baseFeePercentageMultiplier: new BN(110), - priorityFeePercentageMultiplier: new BN(94), - minSuggestedMaxPriorityFeePerGas: new BN(1_000_000_000), + baseFeePercentageMultiplier: BigInt(110), + priorityFeePercentageMultiplier: BigInt(94), + minSuggestedMaxPriorityFeePerGas: BigInt(1_000_000_000), estimatedWaitTimes: { minWaitTimeEstimate: 15_000, maxWaitTimeEstimate: 30_000, @@ -24,9 +23,9 @@ const SETTINGS_BY_PRIORITY_LEVEL = { }, medium: { percentile: 20 as Percentile, - baseFeePercentageMultiplier: new BN(120), - priorityFeePercentageMultiplier: new BN(97), - minSuggestedMaxPriorityFeePerGas: new BN(1_500_000_000), + baseFeePercentageMultiplier: BigInt(120), + priorityFeePercentageMultiplier: BigInt(97), + minSuggestedMaxPriorityFeePerGas: BigInt(1_500_000_000), estimatedWaitTimes: { minWaitTimeEstimate: 15_000, maxWaitTimeEstimate: 45_000, @@ -34,9 +33,9 @@ const SETTINGS_BY_PRIORITY_LEVEL = { }, high: { percentile: 30 as Percentile, - baseFeePercentageMultiplier: new BN(125), - priorityFeePercentageMultiplier: new BN(98), - minSuggestedMaxPriorityFeePerGas: new BN(2_000_000_000), + baseFeePercentageMultiplier: BigInt(125), + priorityFeePercentageMultiplier: BigInt(98), + minSuggestedMaxPriorityFeePerGas: BigInt(2_000_000_000), estimatedWaitTimes: { minWaitTimeEstimate: 15_000, maxWaitTimeEstimate: 60_000, @@ -44,6 +43,7 @@ const SETTINGS_BY_PRIORITY_LEVEL = { }, }; +const bigIntMax = (x: bigint, y: bigint) => x > y ? x : y; /** * Calculates a set of estimates assigned to a particular priority level based on the data returned * by `eth_feeHistory`. @@ -61,28 +61,22 @@ function calculateEstimatesForPriorityLevel( const latestBaseFeePerGas = blocks[blocks.length - 1].baseFeePerGas; - const adjustedBaseFee = latestBaseFeePerGas - .mul(settings.baseFeePercentageMultiplier) - .divn(100); - const priorityFees = blocks + const adjustedBaseFee = (latestBaseFeePerGas * settings.baseFeePercentageMultiplier) / BigInt(100); + const priorityFees: bigint[] = blocks .map((block) => { return 'priorityFeesByPercentile' in block ? block.priorityFeesByPercentile[settings.percentile] : null; }) - .filter(BN.isBN); + .filter((n) => typeof n === 'bigint') as bigint[]; const medianPriorityFee = medianOf(priorityFees); - const adjustedPriorityFee = medianPriorityFee - .mul(settings.priorityFeePercentageMultiplier) - .divn(100); + const adjustedPriorityFee = (medianPriorityFee * settings.priorityFeePercentageMultiplier) / BigInt(100); - const suggestedMaxPriorityFeePerGas = BN.max( + const suggestedMaxPriorityFeePerGas = bigIntMax( adjustedPriorityFee, settings.minSuggestedMaxPriorityFeePerGas, ); - const suggestedMaxFeePerGas = adjustedBaseFee.add( - suggestedMaxPriorityFeePerGas, - ); + const suggestedMaxFeePerGas = adjustedBaseFee + suggestedMaxPriorityFeePerGas; return { ...settings.estimatedWaitTimes, diff --git a/packages/gas-fee-controller/src/fetchGasEstimatesViaEthFeeHistory/medianOf.ts b/packages/gas-fee-controller/src/fetchGasEstimatesViaEthFeeHistory/medianOf.ts index 0b6221de87f..82f6cf23201 100644 --- a/packages/gas-fee-controller/src/fetchGasEstimatesViaEthFeeHistory/medianOf.ts +++ b/packages/gas-fee-controller/src/fetchGasEstimatesViaEthFeeHistory/medianOf.ts @@ -1,14 +1,12 @@ -import type { BN } from '@ethereumjs/util'; - /** * Finds the median among a list of numbers. Note that this is different from the implementation - * in the MetaSwap API, as we want to hold to using BN as much as possible. + * in the MetaSwap API, as we want to hold to using bigint as much as possible. * * @param numbers - A list of numbers, as BNs. Will be sorted automatically if unsorted. * @returns The median number. */ -export default function medianOf(numbers: BN[]): BN { - const sortedNumbers = numbers.slice().sort((a, b) => a.cmp(b)); +export default function medianOf(numbers: bigint[]): bigint { + const sortedNumbers = numbers.slice().sort(); const len = sortedNumbers.length; const index = Math.floor((len - 1) / 2); return sortedNumbers[index]; diff --git a/packages/gas-fee-controller/src/fetchGasEstimatesViaEthFeeHistory/types.ts b/packages/gas-fee-controller/src/fetchGasEstimatesViaEthFeeHistory/types.ts index bbd90256ba1..5b7732b7c83 100644 --- a/packages/gas-fee-controller/src/fetchGasEstimatesViaEthFeeHistory/types.ts +++ b/packages/gas-fee-controller/src/fetchGasEstimatesViaEthFeeHistory/types.ts @@ -1,8 +1,6 @@ -import type { BN } from '@ethereumjs/util'; - export type EthBlock = { - number: BN; - baseFeePerGas: BN; + number: bigint; + baseFeePerGas: bigint; }; export type FeeRange = [string, string]; diff --git a/packages/gas-fee-controller/src/gas-util.ts b/packages/gas-fee-controller/src/gas-util.ts index 8638b3e1a88..de458a679b5 100644 --- a/packages/gas-fee-controller/src/gas-util.ts +++ b/packages/gas-fee-controller/src/gas-util.ts @@ -1,8 +1,7 @@ -import { BN } from '@ethereumjs/util'; import { query, handleFetch, - gweiDecToWEIBN, + gweiDecToWeiBigInt, weiHexToGweiDec, } from '@metamask/controller-utils'; import type EthQuery from '@metamask/eth-query'; @@ -24,7 +23,7 @@ const makeClientIdHeader = (clientId: string) => ({ 'X-Client-Id': clientId }); * @returns The decimal string GWEI amount. */ export function normalizeGWEIDecimalNumbers(n: string | number) { - const numberAsWEIHex = gweiDecToWEIBN(n).toString(16); + const numberAsWEIHex = gweiDecToWeiBigInt(n).toString(16); const numberAsGWEI = weiHexToGweiDec(numberAsWEIHex).toString(10); return numberAsGWEI; } @@ -126,6 +125,8 @@ export async function fetchEthGasPriceEstimate( }; } +const bigIntMin = (x: bigint, y: bigint) => (y < x ? y : x); + /** * Estimate the time it will take for a transaction to be confirmed. * @@ -141,44 +142,44 @@ export function calculateTimeEstimate( ): EstimatedGasFeeTimeBounds { const { low, medium, high, estimatedBaseFee } = gasFeeEstimates; - const maxPriorityFeePerGasInWEI = gweiDecToWEIBN(maxPriorityFeePerGas); - const maxFeePerGasInWEI = gweiDecToWEIBN(maxFeePerGas); - const estimatedBaseFeeInWEI = gweiDecToWEIBN(estimatedBaseFee); + const maxPriorityFeePerGasInWEI = gweiDecToWeiBigInt(maxPriorityFeePerGas); + const maxFeePerGasInWEI = gweiDecToWeiBigInt(maxFeePerGas); + const estimatedBaseFeeInWEI = gweiDecToWeiBigInt(estimatedBaseFee); - const effectiveMaxPriorityFee = BN.min( + const effectiveMaxPriorityFee = bigIntMin( maxPriorityFeePerGasInWEI, - maxFeePerGasInWEI.sub(estimatedBaseFeeInWEI), + maxFeePerGasInWEI - estimatedBaseFeeInWEI, ); - const lowMaxPriorityFeeInWEI = gweiDecToWEIBN( + const lowMaxPriorityFeeInWEI = gweiDecToWeiBigInt( low.suggestedMaxPriorityFeePerGas, ); - const mediumMaxPriorityFeeInWEI = gweiDecToWEIBN( + const mediumMaxPriorityFeeInWEI = gweiDecToWeiBigInt( medium.suggestedMaxPriorityFeePerGas, ); - const highMaxPriorityFeeInWEI = gweiDecToWEIBN( + const highMaxPriorityFeeInWEI = gweiDecToWeiBigInt( high.suggestedMaxPriorityFeePerGas, ); let lowerTimeBound; let upperTimeBound; - if (effectiveMaxPriorityFee.lt(lowMaxPriorityFeeInWEI)) { + if (effectiveMaxPriorityFee < lowMaxPriorityFeeInWEI) { lowerTimeBound = null; upperTimeBound = 'unknown' as unknownString; } else if ( - effectiveMaxPriorityFee.gte(lowMaxPriorityFeeInWEI) && - effectiveMaxPriorityFee.lt(mediumMaxPriorityFeeInWEI) + effectiveMaxPriorityFee >= lowMaxPriorityFeeInWEI && + effectiveMaxPriorityFee < mediumMaxPriorityFeeInWEI ) { lowerTimeBound = low.minWaitTimeEstimate; upperTimeBound = low.maxWaitTimeEstimate; } else if ( - effectiveMaxPriorityFee.gte(mediumMaxPriorityFeeInWEI) && - effectiveMaxPriorityFee.lt(highMaxPriorityFeeInWEI) + effectiveMaxPriorityFee >= mediumMaxPriorityFeeInWEI && + effectiveMaxPriorityFee < highMaxPriorityFeeInWEI ) { lowerTimeBound = medium.minWaitTimeEstimate; upperTimeBound = medium.maxWaitTimeEstimate; - } else if (effectiveMaxPriorityFee.eq(highMaxPriorityFeeInWEI)) { + } else if (effectiveMaxPriorityFee === highMaxPriorityFeeInWEI) { lowerTimeBound = high.minWaitTimeEstimate; upperTimeBound = high.maxWaitTimeEstimate; } else { diff --git a/packages/transaction-controller/package.json b/packages/transaction-controller/package.json index 53ac67727c8..2cc7d160d74 100644 --- a/packages/transaction-controller/package.json +++ b/packages/transaction-controller/package.json @@ -33,6 +33,7 @@ "@ethereumjs/tx": "^4.2.0", "@ethereumjs/util": "^8.1.0", "@ethersproject/abi": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", "@metamask/approval-controller": "^4.0.1", "@metamask/base-controller": "^3.2.3", "@metamask/controller-utils": "^5.0.2", diff --git a/packages/transaction-controller/src/EtherscanRemoteTransactionSource.ts b/packages/transaction-controller/src/EtherscanRemoteTransactionSource.ts index bf1404e2239..ad0a6d45612 100644 --- a/packages/transaction-controller/src/EtherscanRemoteTransactionSource.ts +++ b/packages/transaction-controller/src/EtherscanRemoteTransactionSource.ts @@ -1,4 +1,3 @@ -import { BN } from '@ethereumjs/util'; import { BNToHex } from '@metamask/controller-utils'; import type { Hex } from '@metamask/utils'; import { v1 as random } from 'uuid'; @@ -170,12 +169,12 @@ export class EtherscanRemoteTransactionSource txParams: { chainId: currentChainId, from: txMeta.from, - gas: BNToHex(new BN(txMeta.gas)), - gasPrice: BNToHex(new BN(txMeta.gasPrice)), - gasUsed: BNToHex(new BN(txMeta.gasUsed)), - nonce: BNToHex(new BN(txMeta.nonce)), + gas: BNToHex(BigInt(txMeta.gas)), + gasPrice: BNToHex(BigInt(txMeta.gasPrice)), + gasUsed: BNToHex(BigInt(txMeta.gasUsed)), + nonce: BNToHex(BigInt(txMeta.nonce)), to: txMeta.to, - value: BNToHex(new BN(txMeta.value)), + value: BNToHex(BigInt(txMeta.value)), }, verifiedOnBlockchain: false, }; diff --git a/packages/transaction-controller/src/TransactionController.ts b/packages/transaction-controller/src/TransactionController.ts index 8198d6ea717..61aaf22caa0 100644 --- a/packages/transaction-controller/src/TransactionController.ts +++ b/packages/transaction-controller/src/TransactionController.ts @@ -15,8 +15,7 @@ import type { import { BaseController } from '@metamask/base-controller'; import { BNToHex, - fractionBN, - hexToBN, + fractionBigInt, query, NetworkType, RPC, @@ -876,8 +875,8 @@ export class TransactionController extends BaseController< // 3. If this is a contract address, safely estimate gas using RPC estimatedTransaction.value = typeof value === 'undefined' ? '0x0' : /* istanbul ignore next */ value; - const gasLimitBN = hexToBN(gasLimit); - estimatedTransaction.gas = BNToHex(fractionBN(gasLimitBN, 19, 20)); + const gasLimitBN = BigInt(addHexPrefix(gasLimit)); + estimatedTransaction.gas = BNToHex(fractionBigInt(gasLimitBN, 19, 20)); let gasHex; let estimateGasError; @@ -890,23 +889,21 @@ export class TransactionController extends BaseController< } // 4. Pad estimated gas without exceeding the most recent block gasLimit. If the network is a // a custom network then return the eth_estimateGas value. - const gasBN = hexToBN(gasHex); - const maxGasBN = gasLimitBN.muln(0.9); - const paddedGasBN = gasBN.muln(1.5); - /* istanbul ignore next */ - if (gasBN.gt(maxGasBN) || isCustomNetwork) { + const gasBN = BigInt(addHexPrefix(gasHex)); + const maxGasBN = fractionBigInt(gasLimitBN, 9, 10); + const paddedGasBN = fractionBigInt(gasBN, 15, 10); + if (gasBN > maxGasBN || isCustomNetwork) { return { gas: addHexPrefix(gasHex), gasPrice, estimateGasError }; } - /* istanbul ignore next */ - if (paddedGasBN.lt(maxGasBN)) { + if (paddedGasBN < maxGasBN) { return { - gas: addHexPrefix(BNToHex(paddedGasBN)), + gas: BNToHex(paddedGasBN), gasPrice, estimateGasError, }; } - return { gas: addHexPrefix(BNToHex(maxGasBN)), gasPrice, estimateGasError }; + return { gas: BNToHex(maxGasBN), gasPrice, estimateGasError }; } /** @@ -1726,15 +1723,15 @@ export class TransactionController extends BaseController< signedTx: TypedTransaction, ): Promise { if (signedTx.r) { - transactionMeta.r = addHexPrefix(signedTx.r.toString(16)); + transactionMeta.r = BNToHex(signedTx.r); } if (signedTx.s) { - transactionMeta.s = addHexPrefix(signedTx.s.toString(16)); + transactionMeta.s = BNToHex(signedTx.s); } if (signedTx.v) { - transactionMeta.v = addHexPrefix(signedTx.v.toString(16)); + transactionMeta.v = BNToHex(signedTx.v); } } diff --git a/yarn.lock b/yarn.lock index b9135a35fe8..9af9809715f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1512,6 +1512,7 @@ __metadata: resolution: "@metamask/controller-utils@workspace:packages/controller-utils" dependencies: "@ethereumjs/util": ^8.1.0 + "@ethersproject/bignumber": ^5.7.0 "@metamask/auto-changelog": ^3.1.0 "@metamask/eth-query": ^3.0.1 "@metamask/utils": ^8.1.0 @@ -1815,7 +1816,7 @@ __metadata: version: 0.0.0-use.local resolution: "@metamask/gas-fee-controller@workspace:packages/gas-fee-controller" dependencies: - "@ethereumjs/util": ^8.1.0 + "@ethersproject/bignumber": ^5.7.0 "@metamask/auto-changelog": ^3.1.0 "@metamask/base-controller": ^3.2.3 "@metamask/controller-utils": ^5.0.2 @@ -2463,6 +2464,7 @@ __metadata: "@ethereumjs/tx": ^4.2.0 "@ethereumjs/util": ^8.1.0 "@ethersproject/abi": ^5.7.0 + "@ethersproject/bignumber": ^5.7.0 "@metamask/approval-controller": ^4.0.1 "@metamask/auto-changelog": ^3.1.0 "@metamask/base-controller": ^3.2.3