diff --git a/app/components/Nav/Main/RootRPCMethodsUI.js b/app/components/Nav/Main/RootRPCMethodsUI.js index 086d3c3a61f7..5c4837095a7d 100644 --- a/app/components/Nav/Main/RootRPCMethodsUI.js +++ b/app/components/Nav/Main/RootRPCMethodsUI.js @@ -25,7 +25,7 @@ import { getIsSwapApproveOrSwapTransaction, isApprovalTransaction, } from '../../../util/transactions'; -import { BN } from 'ethereumjs-util'; +import BN from 'bn.js'; import Logger from '../../../util/Logger'; import TransactionTypes from '../../../core/TransactionTypes'; import { swapsUtils } from '@metamask/swaps-controller'; diff --git a/app/components/UI/ConfirmAddAsset/ConfirmAddAsset.test.tsx b/app/components/UI/ConfirmAddAsset/ConfirmAddAsset.test.tsx index ed1a2f5fdc3a..c97e01287dde 100644 --- a/app/components/UI/ConfirmAddAsset/ConfirmAddAsset.test.tsx +++ b/app/components/UI/ConfirmAddAsset/ConfirmAddAsset.test.tsx @@ -7,7 +7,6 @@ import renderWithProvider, { import useBalance from '../Ramp/hooks/useBalance'; import { toTokenMinimalUnit } from '../../../util/number'; import { fireEvent } from '@testing-library/react-native'; -import { BN } from 'ethereumjs-util'; import { RootState } from '../../../reducers'; import { mockNetworkState } from '../../../util/test/network'; import { CHAIN_IDS } from '@metamask/transaction-controller'; @@ -48,7 +47,8 @@ jest.mock('../../../util/navigation/navUtils', () => ({ const mockUseBalanceInitialValue: Partial> = { balanceFiat: '$27.02', - balanceBN: toTokenMinimalUnit('5.36385', 18) as BN, + balance: '5.36385', + balanceMinimalUnit: toTokenMinimalUnit('5.36385', 18).toString(), }; const mockUseBalanceValues: Partial> = { diff --git a/app/components/UI/Ramp/Views/BuildQuote/BuildQuote.test.tsx b/app/components/UI/Ramp/Views/BuildQuote/BuildQuote.test.tsx index 9fb61eadebcf..62d9ba3c346a 100644 --- a/app/components/UI/Ramp/Views/BuildQuote/BuildQuote.test.tsx +++ b/app/components/UI/Ramp/Views/BuildQuote/BuildQuote.test.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { Limits, Payment } from '@consensys/on-ramp-sdk'; import { act, fireEvent, screen } from '@testing-library/react-native'; -import { BN } from 'ethereumjs-util'; +import BN from 'bn.js'; import { renderScreen } from '../../../../../util/test/renderWithProvider'; import BuildQuote from './BuildQuote'; import useRegions from '../../hooks/useRegions'; @@ -155,7 +155,7 @@ jest.mock('../../hooks/usePaymentMethods', () => ); const MAX_LIMIT = 4; -const VALID_AMOUNT = 3; +const VALID_AMOUNT = '3'; const MIN_LIMIT = 2; const mockUseLimitsInitialValues: Partial> = { limits: { @@ -191,7 +191,8 @@ jest.mock('../../../../hooks/useAddressBalance/useAddressBalance', () => const mockUseBalanceInitialValue: Partial> = { balanceFiat: '$27.02', - balanceBN: toTokenMinimalUnit('5.36385', 18) as BN, + balance: '5.36385', + balanceMinimalUnit: '5363850000000000000', }; let mockUseBalanceValues: Partial> = { @@ -239,10 +240,10 @@ let mockUseParamsValues: { const mockUseGasPriceEstimationInitialValue: ReturnType< typeof useGasPriceEstimation > = { - estimatedGasFee: toTokenMinimalUnit( + estimatedGasFee: new BN(toTokenMinimalUnit( '0.01', mockUseRampSDKInitialValues.selectedAsset?.decimals || 18, - ) as BN, + )), }; let mockUseGasPriceEstimationValue: ReturnType = @@ -666,10 +667,12 @@ describe('BuildQuote View', () => { maxAmount: 10, } as Limits; - mockUseBalanceValues.balanceBN = toTokenMinimalUnit( - '5', - mockUseRampSDKValues.selectedAsset?.decimals || 18, - ) as BN; + mockUseBalanceValues = { + ...mockUseBalanceInitialValue, + balance: '5', + balanceMinimalUnit: (5 * Math.pow(10, mockUseRampSDKValues.selectedAsset?.decimals || 18)).toString(10), + }; + render(BuildQuote); const initialAmount = '0'; const overBalanceAmout = '6'; @@ -682,14 +685,15 @@ describe('BuildQuote View', () => { }); it('updates the amount input with quick amount buttons', async () => { - render(BuildQuote); const initialAmount = '0'; + mockUseBalanceValues = { + ...mockUseBalanceInitialValue, + balance: '1', + balanceMinimalUnit: (1 * Math.pow(10, mockUseRampSDKValues.selectedAsset?.decimals || 18)).toString(10), + }; - mockUseBalanceValues.balanceBN = toTokenMinimalUnit( - '1', - mockUseRampSDKValues.selectedAsset?.decimals || 18, - ) as BN; const symbol = mockUseRampSDKValues.selectedAsset?.symbol; + render(BuildQuote); fireEvent.press(getByRoleButton(`${initialAmount} ${symbol}`)); fireEvent.press(getByRoleButton('25%')); expect(getByRoleButton(`0.25 ${symbol}`)).toBeTruthy(); @@ -715,17 +719,14 @@ describe('BuildQuote View', () => { mockUseBalanceValues = { balance: '1', + balanceMinimalUnit: (1 * Math.pow(10, mockUseRampSDKValues.selectedAsset?.decimals || 18)).toString(10), balanceFiat: '$1.00', - balanceBN: toTokenMinimalUnit( - '1', - mockUseRampSDKValues.selectedAsset?.decimals || 18, - ) as BN, }; mockUseGasPriceEstimationValue = { - estimatedGasFee: toTokenMinimalUnit( + estimatedGasFee: new BN(toTokenMinimalUnit( '0.27', mockUseRampSDKValues.selectedAsset?.decimals || 18, - ) as BN, + )), }; const symbol = mockUseRampSDKValues.selectedAsset?.symbol; fireEvent.press(getByRoleButton(`${initialAmount} ${symbol}`)); @@ -733,9 +734,8 @@ describe('BuildQuote View', () => { expect(getByRoleButton(`0.73 ${symbol}`)).toBeTruthy(); }); - it('updates the amount input up to the percentage considering gas', async () => { - render(BuildQuote); const initialAmount = '0'; + it('updates the amount input up to the percentage considering gas', async () => { mockUseRampSDKValues = { ...mockUseRampSDKInitialValues, isBuy: false, @@ -745,21 +745,20 @@ describe('BuildQuote View', () => { address: NATIVE_ADDRESS, }, }; - mockUseBalanceValues = { balance: '1', + balanceMinimalUnit: (1 * Math.pow(10, mockUseRampSDKValues.selectedAsset?.decimals || 18)).toString(10), balanceFiat: '$1.00', - balanceBN: toTokenMinimalUnit( - '1', - mockUseRampSDKValues.selectedAsset?.decimals || 18, - ) as BN, }; mockUseGasPriceEstimationValue = { - estimatedGasFee: toTokenMinimalUnit( + estimatedGasFee: new BN(toTokenMinimalUnit( '0.27', mockUseRampSDKValues.selectedAsset?.decimals || 18, - ) as BN, + )), }; + + render(BuildQuote); + const symbol = mockUseRampSDKValues.selectedAsset?.symbol; fireEvent.press(getByRoleButton(`${initialAmount} ${symbol}`)); fireEvent.press(getByRoleButton('75%')); @@ -799,7 +798,7 @@ describe('BuildQuote View', () => { }); expect(mockTrackEvent).toHaveBeenCalledWith('ONRAMP_QUOTES_REQUESTED', { - amount: VALID_AMOUNT, + amount: parseInt(VALID_AMOUNT), currency_source: mockUseFiatCurrenciesValues?.currentFiatCurrency?.symbol, currency_destination: mockUseRampSDKValues?.selectedAsset?.symbol, payment_method_id: mockUsePaymentMethodsValues.currentPaymentMethod?.id, @@ -834,7 +833,7 @@ describe('BuildQuote View', () => { }); expect(mockTrackEvent).toHaveBeenCalledWith('OFFRAMP_QUOTES_REQUESTED', { - amount: VALID_AMOUNT, + amount: parseInt(VALID_AMOUNT), currency_source: mockUseRampSDKValues?.selectedAsset?.symbol, currency_destination: mockUseFiatCurrenciesValues?.currentFiatCurrency?.symbol, diff --git a/app/components/UI/Ramp/Views/BuildQuote/BuildQuote.tsx b/app/components/UI/Ramp/Views/BuildQuote/BuildQuote.tsx index 04727253c4be..ceaa66703a5b 100644 --- a/app/components/UI/Ramp/Views/BuildQuote/BuildQuote.tsx +++ b/app/components/UI/Ramp/Views/BuildQuote/BuildQuote.tsx @@ -12,7 +12,7 @@ import Animated, { withTiming, } from 'react-native-reanimated'; import { useNavigation } from '@react-navigation/native'; -import { BN } from 'ethereumjs-util'; +import { BigNumber } from 'bignumber.js'; import { useRampSDK } from '../../sdk'; import usePaymentMethods from '../../hooks/usePaymentMethods'; @@ -65,7 +65,6 @@ import { useStyles } from '../../../../../component-library/hooks'; import styleSheet from './BuildQuote.styles'; import { - toTokenMinimalUnit, fromTokenMinimalUnitString, } from '../../../../../util/number'; import useGasPriceEstimation from '../../hooks/useGasPriceEstimation'; @@ -103,8 +102,6 @@ const BuildQuote = () => { const trackEvent = useAnalytics(); const [amountFocused, setAmountFocused] = useState(false); const [amount, setAmount] = useState('0'); - const [amountNumber, setAmountNumber] = useState(0); - const [amountBNMinimalUnit, setAmountBNMinimalUnit] = useState(); const [error, setError] = useState(null); const keyboardHeight = useRef(1000); const keypadOffset = useSharedValue(1000); @@ -191,8 +188,6 @@ const BuildQuote = () => { useIntentAmount( setAmount, - setAmountNumber, - setAmountBNMinimalUnit, currentFiatCurrency, ); @@ -221,7 +216,7 @@ const BuildQuote = () => { selectedAddress, ); - const { balanceFiat, balanceBN } = useBalance( + const { balance, balanceFiat, balanceMinimalUnit } = useBalance( selectedAsset ? { address: selectedAsset.address, @@ -230,39 +225,43 @@ const BuildQuote = () => { : undefined, ); - const maxSellAmount = - balanceBN && gasPriceEstimation - ? balanceBN?.sub(gasPriceEstimation.estimatedGasFee) - : null; - const amountIsBelowMinimum = useMemo( - () => isAmountBelowMinimum(amountNumber), - [amountNumber, isAmountBelowMinimum], + () => isAmountBelowMinimum(amount), + [amount, isAmountBelowMinimum], ); const amountIsAboveMaximum = useMemo( - () => isAmountAboveMaximum(amountNumber), - [amountNumber, isAmountAboveMaximum], + () => isAmountAboveMaximum(amount), + [amount, isAmountAboveMaximum], ); const amountIsValid = useMemo( - () => isAmountValid(amountNumber), - [amountNumber, isAmountValid], + () => isAmountValid(amount), + [amount, isAmountValid], ); const amountIsOverGas = useMemo(() => { - if (isBuy || !maxSellAmount) { + if (isBuy || balanceMinimalUnit === null) { return false; } - return Boolean(amountBNMinimalUnit?.gt(maxSellAmount)); - }, [amountBNMinimalUnit, isBuy, maxSellAmount]); + const balanceBigNum = new BigNumber(balanceMinimalUnit, 10); + const maxSellAmount = gasPriceEstimation !== null + ? balanceBigNum.minus(gasPriceEstimation.estimatedGasFee.toString(10)) + : null; - const hasInsufficientBalance = useMemo(() => { - if (!balanceBN || !amountBNMinimalUnit) { + if (!maxSellAmount) { return false; } - return balanceBN.lt(amountBNMinimalUnit); - }, [balanceBN, amountBNMinimalUnit]); + return Boolean(new BigNumber(amount).gt(maxSellAmount)); + }, [amount, isBuy, balanceMinimalUnit, gasPriceEstimation]); + + const hasInsufficientBalance = useMemo(() => { + if (balance === null) { + return undefined; + } + const balanceBigNum = new BigNumber(balance, 10); + return balanceBigNum.lt(amount); + }, [balance, amount]); const isFetching = isFetchingCryptoCurrencies || @@ -336,57 +335,46 @@ const BuildQuote = () => { const onAmountInputPress = useCallback(() => setAmountFocused(true), []); const handleKeypadChange = useCallback( - ({ value, valueAsNumber }) => { + ({ value }) => { setAmount(`${value}`); - setAmountNumber(valueAsNumber); - if (isSell) { - setAmountBNMinimalUnit( - toTokenMinimalUnit(`${value}`, selectedAsset?.decimals ?? 0) as BN, - ); - } }, - [isSell, selectedAsset?.decimals], + [], ); const handleQuickAmountPress = useCallback( ({ value }: QuickAmount) => { if (isBuy) { - setAmount(`${value}`); - setAmountNumber(value); - } else { - const percentage = value * 100; - const amountPercentage = balanceBN - ?.mul(new BN(percentage)) - .div(new BN(100)); + setAmount(value.toString()); + return; + } + if (balanceMinimalUnit === null) { + return; + } + const balanceBigNum = new BigNumber(balanceMinimalUnit, 10); - if (!amountPercentage) { - return; - } + const targetAmount = balanceBigNum.multipliedBy(value); - let amountToSet = amountPercentage; + const maxSellAmount = gasPriceEstimation !== null + ? balanceBigNum?.minus(gasPriceEstimation.estimatedGasFee.toString(10)) + : null; - if ( - selectedAsset?.address === NATIVE_ADDRESS && - maxSellAmount?.lt(amountPercentage) - ) { - amountToSet = maxSellAmount; - } + const amountToSet = ( + selectedAsset?.address === NATIVE_ADDRESS && maxSellAmount?.lt(targetAmount) + ) ? maxSellAmount : targetAmount; - const newAmountString = fromTokenMinimalUnitString( - amountToSet.toString(10), - selectedAsset?.decimals ?? 18, - ); - setAmountBNMinimalUnit(amountToSet); - setAmount(newAmountString); - setAmountNumber(Number(newAmountString)); - } + // TODO: Too many levels of parsing strings and numbers back and forth here.. + const newAmountString = fromTokenMinimalUnitString( + amountToSet.toString(10), + selectedAsset?.decimals ?? 18, + ); + setAmount(newAmountString); }, [ - balanceBN, isBuy, - maxSellAmount, selectedAsset?.address, selectedAsset?.decimals, + balanceMinimalUnit, + gasPriceEstimation, ], ); @@ -408,7 +396,6 @@ const BuildQuote = () => { async (region: Region) => { hideRegionModal(); setAmount('0'); - setAmountNumber(0); if (selectedFiatCurrencyId === defaultFiatCurrency?.id) { /* * Selected fiat currency is default, we will fetch @@ -463,7 +450,6 @@ const BuildQuote = () => { (fiatCurrency) => { setSelectedFiatCurrencyId(fiatCurrency?.id); setAmount('0'); - setAmountNumber(0); hideFiatSelectorModal(); }, [hideFiatSelectorModal, setSelectedFiatCurrencyId], @@ -490,7 +476,7 @@ const BuildQuote = () => { if (selectedAsset && currentFiatCurrency) { navigation.navigate( ...createQuotesNavDetails({ - amount: isBuy ? amountNumber : amount, + amount, asset: selectedAsset, fiatCurrency: currentFiatCurrency, }), @@ -498,7 +484,7 @@ const BuildQuote = () => { const analyticsPayload = { payment_method_id: selectedPaymentMethodId as string, - amount: amountNumber, + amount: parseFloat(amount), location: screenLocation, }; @@ -521,7 +507,6 @@ const BuildQuote = () => { }, [ screenLocation, amount, - amountNumber, currentFiatCurrency, isBuy, navigation, @@ -687,21 +672,20 @@ const BuildQuote = () => { } const displayAmount = isBuy - ? formatAmount(amountNumber) + ? formatAmount(parseFloat(amount)) : `${amount} ${selectedAsset?.symbol}`; - let quickAmounts: QuickAmount[] = []; - - if (isBuy) { - quickAmounts = - limits?.quickAmounts?.map((quickAmount) => ({ - value: quickAmount, - label: currentFiatCurrency?.denomSymbol + quickAmount.toString(), - })) ?? []; - } else if (balanceBN && !balanceBN.isZero() && maxSellAmount?.gt(new BN(0))) { - quickAmounts = [ + const getQuickAmounts = (): QuickAmount[] => { + console.warn('GQC', {isBuy, isSell, limits, balance, balanceMinimalUnit, gasPriceEstimation }); + if (isBuy) { + return limits?.quickAmounts?.map((quickAmount) => ({ + value: quickAmount, + label: currentFiatCurrency?.denomSymbol + quickAmount.toString(), + })) ?? []; + } + const sellLimits = [ { value: 0.25, label: '25%' }, - { value: 0.5, label: '50%' }, + { value: 0.50, label: '50%' }, { value: 0.75, label: '75%' }, { value: 1, @@ -709,7 +693,18 @@ const BuildQuote = () => { isNative: selectedAsset?.address === NATIVE_ADDRESS, }, ]; - } + if (balanceMinimalUnit === null || gasPriceEstimation === null) { + return sellLimits; + } + const balanceBigNum = new BigNumber(balanceMinimalUnit, 10); + const maxSellAmount = balanceBigNum?.minus(gasPriceEstimation.estimatedGasFee.toString(10)); + if (!balanceBigNum.isZero() && maxSellAmount.gt(0)) { + return sellLimits; + } + return []; + }; + + const quickAmounts = getQuickAmounts(); return ( @@ -783,14 +778,13 @@ const BuildQuote = () => { } amount={displayAmount} highlightedError={ - amountNumber > 0 && (!amountIsValid || amountIsOverGas) + parseFloat(amount) > 0 && (!amountIsValid || amountIsOverGas) } currencyCode={isBuy ? currentFiatCurrency?.symbol : undefined} onPress={onAmountInputPress} onCurrencyPress={isBuy ? handleFiatSelectorPress : undefined} /> - {amountNumber > 0 && - amountIsValid && + {amountIsValid && !hasInsufficientBalance && amountIsOverGas && ( @@ -864,8 +858,8 @@ const BuildQuote = () => { {currentPaymentMethod?.customAction ? ( ) : ( @@ -874,7 +868,7 @@ const BuildQuote = () => { onPress={handleGetQuotePress} accessibilityRole="button" accessible - disabled={amountNumber <= 0} + disabled={parseInt(amount, 10) <= 0} > {strings('fiat_on_ramp_aggregator.get_quotes')} @@ -901,7 +895,7 @@ const BuildQuote = () => { : `${selectedAsset?.symbol}-crypto` } decimals={ - isBuy ? currentFiatCurrency?.decimals : selectedAsset?.decimals + (isBuy ? currentFiatCurrency?.decimals : selectedAsset?.decimals) ?? 0 } /> diff --git a/app/components/UI/Ramp/Views/Quotes/Quotes.test.tsx b/app/components/UI/Ramp/Views/Quotes/Quotes.test.tsx index 22c84f9f7ad7..3a9dbffdbb4f 100644 --- a/app/components/UI/Ramp/Views/Quotes/Quotes.test.tsx +++ b/app/components/UI/Ramp/Views/Quotes/Quotes.test.tsx @@ -100,7 +100,7 @@ jest.mock('../../hooks/useAnalytics', () => () => mockTrackEvent); jest.mock('../../hooks/useInAppBrowser', () => () => mockRenderInAppBrowser); const mockUseParamsInitialValues: DeepPartial = { - amount: 50, + amount: '50', asset: { symbol: 'ETH', }, @@ -257,7 +257,7 @@ describe('Quotes', () => { [ "ONRAMP_QUOTES_EXPANDED", { - "amount": 50, + "amount": "50", "chain_id_destination": "1", "currency_destination": "ETH", "currency_source": "USD", @@ -339,7 +339,7 @@ describe('Quotes', () => { [ "ONRAMP_PROVIDER_SELECTED", { - "amount": 50, + "amount": "50", "chain_id_destination": "1", "crypto_out": 0.0162, "currency_destination": "ETH", @@ -369,7 +369,7 @@ describe('Quotes', () => { [ "OFFRAMP_PROVIDER_SELECTED", { - "amount": 50, + "amount": "50", "chain_id_source": "1", "currency_destination": "USD", "currency_source": "ETH", @@ -404,7 +404,7 @@ describe('Quotes', () => { [ "ONRAMP_PROVIDER_SELECTED", { - "amount": 50, + "amount": "50", "chain_id_destination": "1", "crypto_out": 0.0162, "currency_destination": "ETH", @@ -434,7 +434,7 @@ describe('Quotes', () => { [ "OFFRAMP_PROVIDER_SELECTED", { - "amount": 50, + "amount": "50", "chain_id_source": "1", "currency_destination": "USD", "currency_source": "ETH", @@ -535,7 +535,7 @@ describe('Quotes', () => { [ "ONRAMP_QUOTES_RECEIVED", { - "amount": 50, + "amount": "50", "average_crypto_out": 0.016671, "average_gas_fee": 1.32, "average_processing_fee": 1.455, @@ -564,7 +564,7 @@ describe('Quotes', () => { [ "ONRAMP_QUOTE_ERROR", { - "amount": 50, + "amount": "50", "chain_id_destination": "1", "currency_destination": "ETH", "currency_source": "USD", @@ -594,7 +594,7 @@ describe('Quotes', () => { [ "OFFRAMP_QUOTES_RECEIVED", { - "amount": 50, + "amount": "50", "average_fiat_out": 0.016671, "average_gas_fee": 1.32, "average_processing_fee": 1.455, @@ -623,7 +623,7 @@ describe('Quotes', () => { [ "OFFRAMP_QUOTE_ERROR", { - "amount": 50, + "amount": "50", "chain_id_source": "1", "currency_destination": "USD", "currency_source": "ETH", diff --git a/app/components/UI/Ramp/Views/Quotes/Quotes.tsx b/app/components/UI/Ramp/Views/Quotes/Quotes.tsx index 5738cdbc549d..b4cce1f7b9c3 100644 --- a/app/components/UI/Ramp/Views/Quotes/Quotes.tsx +++ b/app/components/UI/Ramp/Views/Quotes/Quotes.tsx @@ -74,7 +74,7 @@ import { getOrdersProviders } from './../../../../../reducers/fiatOrders'; const HIGHLIGHTED_QUOTES_COUNT = 2; export interface QuotesParams { - amount: number | string; + amount: string; asset: CryptoCurrency; fiatCurrency: FiatCurrency; } diff --git a/app/components/UI/Ramp/Views/SendTransaction/SendTransaction.tsx b/app/components/UI/Ramp/Views/SendTransaction/SendTransaction.tsx index ac4927aa503e..8fdee41cb16f 100644 --- a/app/components/UI/Ramp/Views/SendTransaction/SendTransaction.tsx +++ b/app/components/UI/Ramp/Views/SendTransaction/SendTransaction.tsx @@ -2,7 +2,7 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { ImageSourcePropType, View } from 'react-native'; import { useDispatch, useSelector } from 'react-redux'; import { useNavigation } from '@react-navigation/native'; -import { BN } from 'ethereumjs-util'; +import BN from 'bn.js'; import { SellOrder } from '@consensys/on-ramp-sdk/dist/API'; import { TransactionParams, diff --git a/app/components/UI/Ramp/hooks/useBalance.ts b/app/components/UI/Ramp/hooks/useBalance.ts index 4c2e52afecb6..08ac4f888ee3 100644 --- a/app/components/UI/Ramp/hooks/useBalance.ts +++ b/app/components/UI/Ramp/hooks/useBalance.ts @@ -1,5 +1,4 @@ import { useSelector } from 'react-redux'; -import { hexToBN } from '@metamask/controller-utils'; import { NATIVE_ADDRESS } from '../../../../constants/on-ramp'; import { selectAccountsByChainId } from '../../../../selectors/accountTrackerController'; import { @@ -13,6 +12,7 @@ import { selectChainId } from '../../../../selectors/networkController'; import { safeToChecksumAddress } from '../../../../util/address'; import { balanceToFiat, + hexToBN, renderFromTokenMinimalUnit, renderFromWei, toHexadecimal, @@ -22,7 +22,7 @@ import { const defaultReturn = { balance: null, balanceFiat: null, - balanceBN: null, + balanceMinimalUnit: null, }; interface Asset { @@ -30,7 +30,15 @@ interface Asset { decimals: number; } -export default function useBalance(asset?: Asset) { +export default function useBalance(asset?: Asset): { + balance: string, + balanceFiat: string, + balanceMinimalUnit: string, +} | { + balance: null, + balanceFiat: null, + balanceMinimalUnit: null, +}{ const accountsByChainId = useSelector(selectAccountsByChainId); const chainId = useSelector(selectChainId); const selectedAddress = useSelector( @@ -51,38 +59,31 @@ export default function useBalance(asset?: Asset) { return defaultReturn; } - let balance, balanceFiat, balanceBN; if (assetAddress === NATIVE_ADDRESS) { - balance = renderFromWei( - //@ts-expect-error - TODO: Ramps team - accountsByChainId[toHexadecimal(chainId)][selectedAddress]?.balance, - ); + //@ts-expect-error - TODO: Ramps team + const balanceMinimalUnit = accountsByChainId[toHexadecimal(chainId)][selectedAddress]?.balance; + const balance = renderFromWei(balanceMinimalUnit); - balanceBN = hexToBN( - //@ts-expect-error - TODO: Ramps team - accountsByChainId[toHexadecimal(chainId)][selectedAddress]?.balance, - ); - balanceFiat = weiToFiat(balanceBN, conversionRate, currentCurrency); - } else { - const exchangeRate = tokenExchangeRates?.[assetAddress]?.price; - balance = - assetAddress && assetAddress in balances - ? renderFromTokenMinimalUnit( - balances[assetAddress], - asset.decimals ?? 18, - ) - : 0; - balanceFiat = balanceToFiat( - balance, - conversionRate, - exchangeRate, - currentCurrency, - ); - balanceBN = - assetAddress && assetAddress in balances - ? hexToBN(balances[assetAddress]) - : null; + const balanceBN = hexToBN(balance); + const balanceFiat = weiToFiat(balanceBN, conversionRate, currentCurrency); + return { balance, balanceFiat, balanceMinimalUnit }; } - return { balance, balanceFiat, balanceBN }; + const exchangeRate = tokenExchangeRates?.[assetAddress]?.price; + const balanceMinimalUnit = assetAddress && assetAddress in balances + ? balances[assetAddress] + : '0'; + const balance = balanceMinimalUnit + ? renderFromTokenMinimalUnit( + balanceMinimalUnit, + asset.decimals ?? 18, + ) + : '0'; + const balanceFiat = balanceToFiat( + balance, + conversionRate, + exchangeRate, + currentCurrency, + ); + return { balance, balanceFiat, balanceMinimalUnit }; } diff --git a/app/components/UI/Ramp/hooks/useGasPriceEstimation.ts b/app/components/UI/Ramp/hooks/useGasPriceEstimation.ts index 6c9565b94f6f..18182dea4722 100644 --- a/app/components/UI/Ramp/hooks/useGasPriceEstimation.ts +++ b/app/components/UI/Ramp/hooks/useGasPriceEstimation.ts @@ -5,7 +5,7 @@ import { type GasFeeController as GasFeeControllerType, } from '@metamask/gas-fee-controller'; -import { BN } from 'ethereumjs-util'; +import BN from 'bn.js'; import Engine from '../../../../core/Engine'; import { decGWEIToHexWEI } from '../../../../util/conversions'; import { selectGasFeeControllerState } from '../../../../selectors/gasFeeController'; diff --git a/app/components/UI/Ramp/hooks/useIntentAmount.test.ts b/app/components/UI/Ramp/hooks/useIntentAmount.test.ts index b8a639b71311..d6046b014fe5 100644 --- a/app/components/UI/Ramp/hooks/useIntentAmount.test.ts +++ b/app/components/UI/Ramp/hooks/useIntentAmount.test.ts @@ -46,9 +46,6 @@ const mockFiatCurrenciesData = [ ] as FiatCurrency[]; const mockSetAmount = jest.fn(); -const mockSetAmountNumber = jest.fn(); -const mockSetAmountBNMinimalUnit = jest.fn(); - const mockSetIntent = jest.fn(); const mockUseRampSDKInitialValues: Partial = { @@ -80,14 +77,10 @@ describe('useIntentAmount', () => { renderHook(() => useIntentAmount( mockSetAmount, - mockSetAmountNumber, - mockSetAmountBNMinimalUnit, currentFiatCurrency, ), ); expect(mockSetAmount).toHaveBeenCalledWith('100'); - expect(mockSetAmountNumber).toHaveBeenCalledWith(100); - expect(mockSetAmountBNMinimalUnit).not.toHaveBeenCalled(); expect(mockSetIntent).toHaveBeenCalledWith(expect.any(Function)); }); @@ -102,14 +95,10 @@ describe('useIntentAmount', () => { renderHook(() => useIntentAmount( mockSetAmount, - mockSetAmountNumber, - mockSetAmountBNMinimalUnit, currentFiatCurrency, ), ); expect(mockSetAmount).toHaveBeenCalledWith('100.23'); - expect(mockSetAmountNumber).toHaveBeenCalledWith(100.23); - expect(mockSetAmountBNMinimalUnit).not.toHaveBeenCalled(); expect(mockSetIntent).toHaveBeenCalledWith(expect.any(Function)); }); @@ -124,14 +113,10 @@ describe('useIntentAmount', () => { renderHook(() => useIntentAmount( mockSetAmount, - mockSetAmountNumber, - mockSetAmountBNMinimalUnit, currentFiatCurrency, ), ); expect(mockSetAmount).toHaveBeenCalledWith('100'); - expect(mockSetAmountNumber).toHaveBeenCalledWith(100); - expect(mockSetAmountBNMinimalUnit).not.toHaveBeenCalled(); expect(mockSetIntent).toHaveBeenCalledWith(expect.any(Function)); }); @@ -145,26 +130,10 @@ describe('useIntentAmount', () => { renderHook(() => useIntentAmount( mockSetAmount, - mockSetAmountNumber, - mockSetAmountBNMinimalUnit, currentFiatCurrency, ), ); expect(mockSetAmount).toHaveBeenCalledWith('100.12345678'); - expect(mockSetAmountNumber.mock.calls).toMatchInlineSnapshot(` - [ - [ - 100.12345678, - ], - ] - `); - expect(mockSetAmountBNMinimalUnit.mock.calls).toMatchInlineSnapshot(` - [ - [ - "254c8454e", - ], - ] - `); expect(mockSetIntent).toHaveBeenCalledWith(expect.any(Function)); }); @@ -182,26 +151,10 @@ describe('useIntentAmount', () => { renderHook(() => useIntentAmount( mockSetAmount, - mockSetAmountNumber, - mockSetAmountBNMinimalUnit, currentFiatCurrency, ), ); expect(mockSetAmount).toHaveBeenCalledWith('100.1234'); - expect(mockSetAmountNumber.mock.calls).toMatchInlineSnapshot(` - [ - [ - 100.1234, - ], - ] - `); - expect(mockSetAmountBNMinimalUnit.mock.calls).toMatchInlineSnapshot(` - [ - [ - "f4712", - ], - ] - `); expect(mockSetIntent).toHaveBeenCalledWith(expect.any(Function)); }); @@ -219,26 +172,10 @@ describe('useIntentAmount', () => { renderHook(() => useIntentAmount( mockSetAmount, - mockSetAmountNumber, - mockSetAmountBNMinimalUnit, currentFiatCurrency, ), ); expect(mockSetAmount).toHaveBeenCalledWith('100'); - expect(mockSetAmountNumber.mock.calls).toMatchInlineSnapshot(` - [ - [ - 100, - ], - ] - `); - expect(mockSetAmountBNMinimalUnit.mock.calls).toMatchInlineSnapshot(` - [ - [ - "64", - ], - ] - `); expect(mockSetIntent).toHaveBeenCalledWith(expect.any(Function)); }); @@ -257,26 +194,10 @@ describe('useIntentAmount', () => { renderHook(() => useIntentAmount( mockSetAmount, - mockSetAmountNumber, - mockSetAmountBNMinimalUnit, currentFiatCurrency, ), ); expect(mockSetAmount).toHaveBeenCalledWith('100.123'); - expect(mockSetAmountNumber.mock.calls).toMatchInlineSnapshot(` - [ - [ - 100.123, - ], - ] - `); - expect(mockSetAmountBNMinimalUnit.mock.calls).toMatchInlineSnapshot(` - [ - [ - "1871b", - ], - ] - `); expect(mockSetIntent).toHaveBeenCalledWith(expect.any(Function)); }); @@ -288,14 +209,10 @@ describe('useIntentAmount', () => { renderHook(() => useIntentAmount( mockSetAmount, - mockSetAmountNumber, - mockSetAmountBNMinimalUnit, currentFiatCurrency, ), ); expect(mockSetAmount).not.toHaveBeenCalled(); - expect(mockSetAmountNumber).not.toHaveBeenCalled(); - expect(mockSetAmountBNMinimalUnit).not.toHaveBeenCalled(); expect(mockSetIntent).toHaveBeenCalledWith(expect.any(Function)); }); @@ -309,14 +226,10 @@ describe('useIntentAmount', () => { renderHook(() => useIntentAmount( mockSetAmount, - mockSetAmountNumber, - mockSetAmountBNMinimalUnit, currentFiatCurrency, ), ); expect(mockSetAmount).not.toHaveBeenCalled(); - expect(mockSetAmountNumber).not.toHaveBeenCalled(); - expect(mockSetAmountBNMinimalUnit).not.toHaveBeenCalled(); expect(mockSetIntent).toHaveBeenCalledWith(expect.any(Function)); }); @@ -326,14 +239,10 @@ describe('useIntentAmount', () => { renderHook(() => useIntentAmount( mockSetAmount, - mockSetAmountNumber, - mockSetAmountBNMinimalUnit, currentFiatCurrency, ), ); expect(mockSetAmount).not.toHaveBeenCalled(); - expect(mockSetAmountNumber).not.toHaveBeenCalled(); - expect(mockSetAmountBNMinimalUnit).not.toHaveBeenCalled(); expect(mockSetIntent).not.toHaveBeenCalled(); }); @@ -345,14 +254,10 @@ describe('useIntentAmount', () => { renderHook(() => useIntentAmount( mockSetAmount, - mockSetAmountNumber, - mockSetAmountBNMinimalUnit, currentFiatCurrency, ), ); expect(mockSetAmount).not.toHaveBeenCalled(); - expect(mockSetAmountNumber).not.toHaveBeenCalled(); - expect(mockSetAmountBNMinimalUnit).not.toHaveBeenCalled(); expect(mockSetIntent).not.toHaveBeenCalled(); }); @@ -361,14 +266,10 @@ describe('useIntentAmount', () => { renderHook(() => useIntentAmount( mockSetAmount, - mockSetAmountNumber, - mockSetAmountBNMinimalUnit, currentFiatCurrency, ), ); expect(mockSetAmount).not.toHaveBeenCalled(); - expect(mockSetAmountNumber).not.toHaveBeenCalled(); - expect(mockSetAmountBNMinimalUnit).not.toHaveBeenCalled(); expect(mockSetIntent).not.toHaveBeenCalled(); }); @@ -379,14 +280,10 @@ describe('useIntentAmount', () => { renderHook(() => useIntentAmount( mockSetAmount, - mockSetAmountNumber, - mockSetAmountBNMinimalUnit, currentFiatCurrency, ), ); expect(mockSetAmount).not.toHaveBeenCalled(); - expect(mockSetAmountNumber).not.toHaveBeenCalled(); - expect(mockSetAmountBNMinimalUnit).not.toHaveBeenCalled(); expect(mockSetIntent).not.toHaveBeenCalled(); }); @@ -396,14 +293,10 @@ describe('useIntentAmount', () => { renderHook(() => useIntentAmount( mockSetAmount, - mockSetAmountNumber, - mockSetAmountBNMinimalUnit, currentFiatCurrency, ), ); expect(mockSetAmount).not.toHaveBeenCalled(); - expect(mockSetAmountNumber).not.toHaveBeenCalled(); - expect(mockSetAmountBNMinimalUnit).not.toHaveBeenCalled(); expect(mockSetIntent).not.toHaveBeenCalled(); }); @@ -415,14 +308,10 @@ describe('useIntentAmount', () => { renderHook(() => useIntentAmount( mockSetAmount, - mockSetAmountNumber, - mockSetAmountBNMinimalUnit, currentFiatCurrency, ), ); expect(mockSetAmount).not.toHaveBeenCalled(); - expect(mockSetAmountNumber).not.toHaveBeenCalled(); - expect(mockSetAmountBNMinimalUnit).not.toHaveBeenCalled(); expect(mockSetIntent).not.toHaveBeenCalled(); }); }); diff --git a/app/components/UI/Ramp/hooks/useIntentAmount.ts b/app/components/UI/Ramp/hooks/useIntentAmount.ts index 7b024731c3db..512d5fbcc69b 100644 --- a/app/components/UI/Ramp/hooks/useIntentAmount.ts +++ b/app/components/UI/Ramp/hooks/useIntentAmount.ts @@ -1,8 +1,7 @@ import { useEffect } from 'react'; -import { type BN } from 'ethereumjs-util'; import { useRampSDK } from '../sdk'; import parseAmount from '../utils/parseAmount'; -import { toTokenMinimalUnit } from '../../../../util/number'; +// import { toTokenMinimalUnit } from '../../../../util/number'; import { FiatCurrency } from '@consensys/on-ramp-sdk'; /** @@ -19,10 +18,6 @@ import { FiatCurrency } from '@consensys/on-ramp-sdk'; */ export default function useIntentAmount( setAmount: (amount: React.SetStateAction) => void, - setAmountNumber: (amount: React.SetStateAction) => void, - setAmountBNMinimalUnit: ( - amount: React.SetStateAction, - ) => void, currentFiatCurrency: FiatCurrency | null, ) { const { selectedAsset, intent, setIntent, isBuy, isSell } = useRampSDK(); @@ -35,7 +30,13 @@ export default function useIntentAmount( ? currentFiatCurrency.decimals ?? 0 : selectedAsset.decimals ?? 0, ); + if (!parsedAmount) { + throw new Error(`Empty result for non-empty input '${intent.amount}'`); + } + setAmount(parsedAmount); + // TODO + /* if (parsedAmount) { let valueAsNumber = 0; try { @@ -47,7 +48,6 @@ export default function useIntentAmount( ); } setAmount(parsedAmount); - setAmountNumber(valueAsNumber); if (isSell) { setAmountBNMinimalUnit( toTokenMinimalUnit( @@ -57,6 +57,7 @@ export default function useIntentAmount( ); } } + */ } catch (parsingError) { console.error('Error parsing intent amount', parsingError as Error); } finally { @@ -71,7 +72,5 @@ export default function useIntentAmount( isSell, selectedAsset, setAmount, - setAmountNumber, - setAmountBNMinimalUnit, ]); } diff --git a/app/components/UI/Ramp/hooks/useLimits.ts b/app/components/UI/Ramp/hooks/useLimits.ts index ff36f5d94102..99dab1f4fece 100644 --- a/app/components/UI/Ramp/hooks/useLimits.ts +++ b/app/components/UI/Ramp/hooks/useLimits.ts @@ -1,3 +1,4 @@ +import { BigNumber } from 'bignumber.js'; import useSDKMethod from './useSDKMethod'; import { useRampSDK } from '../sdk'; @@ -18,13 +19,17 @@ const useLimits = () => { selectedFiatCurrencyId, ); - const isAmountBelowMinimum = (amount: number) => - amount !== 0 && limits && amount < limits.minAmount; + const isAmountBelowMinimum = (amount: string): boolean => { + const amountBigNum = new BigNumber(amount, 10); + return amountBigNum.gt(0) && !!limits && amountBigNum.lt(limits.minAmount); + }; - const isAmountAboveMaximum = (amount: number) => - amount !== 0 && limits && amount > limits.maxAmount; + const isAmountAboveMaximum = (amount: string): boolean => { + const amountBigNum = new BigNumber(amount, 10); + return amountBigNum.gt(0) && !!limits && amountBigNum.gt(limits.maxAmount); + }; - const isAmountValid = (amount: number) => + const isAmountValid = (amount: string): boolean => !isAmountBelowMinimum(amount) && !isAmountAboveMaximum(amount); return { diff --git a/app/components/UI/Stake/Views/StakeInputView/StakeInputView.test.tsx b/app/components/UI/Stake/Views/StakeInputView/StakeInputView.test.tsx index 906391284715..0e2c2e7e2c43 100644 --- a/app/components/UI/Stake/Views/StakeInputView/StakeInputView.test.tsx +++ b/app/components/UI/Stake/Views/StakeInputView/StakeInputView.test.tsx @@ -4,7 +4,7 @@ import StakeInputView from './StakeInputView'; import { renderScreen } from '../../../../../util/test/renderWithProvider'; import Routes from '../../../../../constants/navigation/Routes'; import { backgroundState } from '../../../../../util/test/initial-root-state'; -import { BN } from 'ethereumjs-util'; +import BN from 'bn.js'; import { Stake } from '../../sdk/stakeSdkProvider'; import { ChainId, PooledStakingContract } from '@metamask/stake-sdk'; import { Contract } from 'ethers'; diff --git a/app/components/UI/Stake/Views/UnstakeInputView/UnstakeInputView.tsx b/app/components/UI/Stake/Views/UnstakeInputView/UnstakeInputView.tsx index ddfa676d158e..376d171901dc 100644 --- a/app/components/UI/Stake/Views/UnstakeInputView/UnstakeInputView.tsx +++ b/app/components/UI/Stake/Views/UnstakeInputView/UnstakeInputView.tsx @@ -1,6 +1,6 @@ import { useNavigation } from '@react-navigation/native'; import React, { useCallback, useEffect } from 'react'; -import { BN } from 'ethereumjs-util'; +import BN from 'bn.js'; import UnstakeInputViewBanner from './UnstakeBanner'; import { strings } from '../../../../../../locales/i18n'; import Button, { diff --git a/app/components/UI/Stake/components/StakingBalance/StakingBalance.tsx b/app/components/UI/Stake/components/StakingBalance/StakingBalance.tsx index e70630ab9016..a33e3794f8e5 100644 --- a/app/components/UI/Stake/components/StakingBalance/StakingBalance.tsx +++ b/app/components/UI/Stake/components/StakingBalance/StakingBalance.tsx @@ -24,7 +24,7 @@ import { strings } from '../../../../../../locales/i18n'; import { renderFromWei } from '../../../../../util/number'; import { getTimeDifferenceFromNow } from '../../../../../util/date'; import { filterExitRequests } from './utils'; -import { BN } from 'ethereumjs-util'; +import BN from 'bn.js'; import bn from 'bignumber.js'; import { CommonPercentageInputUnits, diff --git a/app/components/UI/Stake/hooks/useBalance.test.tsx b/app/components/UI/Stake/hooks/useBalance.test.tsx index 17c73f1685ce..15423d89db71 100644 --- a/app/components/UI/Stake/hooks/useBalance.test.tsx +++ b/app/components/UI/Stake/hooks/useBalance.test.tsx @@ -77,7 +77,7 @@ describe('useBalance', () => { expect(result.current.balanceFiatNumber).toBe(39506172511.6); // Fiat number balance expect(result.current.stakedBalanceWei).toBe('5791332670714232000'); // No staked assets expect(result.current.formattedStakedBalanceETH).toBe('5.79133 ETH'); // Formatted ETH balance - expect(result.current.stakedBalanceFiatNumber).toBe(18532.26454); // Staked balance in fiat number + expect(result.current.stakedBalanceFiatNumber).toBe(18532.26455); // Staked balance in fiat number expect(result.current.formattedStakedBalanceFiat).toBe('$18532.26'); // }); diff --git a/app/components/UI/Stake/hooks/useStakingInput.ts b/app/components/UI/Stake/hooks/useStakingInput.ts index 7424703e0f1f..a6daadf07c5f 100644 --- a/app/components/UI/Stake/hooks/useStakingInput.ts +++ b/app/components/UI/Stake/hooks/useStakingInput.ts @@ -1,4 +1,4 @@ -import { BN } from 'ethereumjs-util'; +import BN from 'bn.js'; import { useState, useMemo, useCallback } from 'react'; import { useSelector } from 'react-redux'; import { diff --git a/app/components/UI/Tokens/TokenList/PortfolioBalance/index.test.tsx b/app/components/UI/Tokens/TokenList/PortfolioBalance/index.test.tsx index e1e1f6943769..f1b5333e93c6 100644 --- a/app/components/UI/Tokens/TokenList/PortfolioBalance/index.test.tsx +++ b/app/components/UI/Tokens/TokenList/PortfolioBalance/index.test.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { fireEvent } from '@testing-library/react-native'; -import { BN } from 'ethereumjs-util'; +import BN from 'bn.js'; import renderWithProvider from '../../../../../util/test/renderWithProvider'; import { backgroundState } from '../../../../../util/test/initial-root-state'; import AppConstants from '../../../../../../app/core/AppConstants'; diff --git a/app/components/UI/Tokens/index.test.tsx b/app/components/UI/Tokens/index.test.tsx index 60b1fb20eceb..88819c3bd671 100644 --- a/app/components/UI/Tokens/index.test.tsx +++ b/app/components/UI/Tokens/index.test.tsx @@ -2,7 +2,7 @@ import React from 'react'; // eslint-disable-next-line @typescript-eslint/no-shadow import { fireEvent, waitFor } from '@testing-library/react-native'; import Tokens from './'; -import { BN } from 'ethereumjs-util'; +import BN from 'bn.js'; import renderWithProvider from '../../../util/test/renderWithProvider'; import { createStackNavigator } from '@react-navigation/stack'; import { getAssetTestId } from '../../../../wdio/screen-objects/testIDs/Screens/WalletView.testIds'; diff --git a/app/components/UI/TransactionElement/utils.test.js b/app/components/UI/TransactionElement/utils.test.js index 5e85a9abdbbd..9b9f761e2e1d 100644 --- a/app/components/UI/TransactionElement/utils.test.js +++ b/app/components/UI/TransactionElement/utils.test.js @@ -160,7 +160,7 @@ describe('Utils', () => { hash: '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef', renderFrom: '0xABcdEFABcdEFabcdEfAbCdefabcdeFABcDEFabCD', renderGas: '21000', - renderGasPrice: 1, + renderGasPrice: '1', renderTo: '0x1234567890AbcdEF1234567890aBcdef12345678', renderTotalGas: '0.00002 ETH', renderValue: '0 ETH', diff --git a/app/components/Views/confirmations/Approval/components/TransactionEditor/index.js b/app/components/Views/confirmations/Approval/components/TransactionEditor/index.js index 5e607dfbef88..d8d9ff414a33 100644 --- a/app/components/Views/confirmations/Approval/components/TransactionEditor/index.js +++ b/app/components/Views/confirmations/Approval/components/TransactionEditor/index.js @@ -9,7 +9,8 @@ import { renderFromWei, toHexadecimal, } from '../../../../../../util/number'; -import { isValidAddress, BN, addHexPrefix } from 'ethereumjs-util'; +import { isValidAddress, addHexPrefix } from 'ethereumjs-util'; +import BN from 'bn.js'; import { strings } from '../../../../../../../locales/i18n'; import { connect } from 'react-redux'; import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view'; diff --git a/app/components/Views/confirmations/SendFlow/Amount/index.js b/app/components/Views/confirmations/SendFlow/Amount/index.js index f2bd09ddc3a9..7b8837653214 100644 --- a/app/components/Views/confirmations/SendFlow/Amount/index.js +++ b/app/components/Views/confirmations/SendFlow/Amount/index.js @@ -60,7 +60,6 @@ import CollectibleMedia from '../../../../UI/CollectibleMedia'; import collectiblesTransferInformation from '../../../../../util/collectibles-transfer'; import { strings } from '../../../../../../locales/i18n'; import Device from '../../../../../util/device'; -import { BN } from 'ethereumjs-util'; import { MetaMetricsEvents } from '../../../../../core/Analytics'; import dismissKeyboard from 'react-native/Libraries/Utilities/dismissKeyboard'; import NetworkMainAssetLogo from '../../../../UI/NetworkMainAssetLogo'; @@ -893,7 +892,7 @@ class Amount extends PureComponent { const balanceBN = hexToBN(accounts[selectedAddress].balance); const realMaxValue = balanceBN.sub(estimatedTotalGas); const maxValue = - balanceBN.isZero() || realMaxValue.isNeg() ? new BN(0) : realMaxValue; + balanceBN.isZero() || realMaxValue.isNeg() ? hexToBN('0x0') : realMaxValue; if (internalPrimaryCurrencyIsCrypto) { input = fromWei(maxValue); } else { diff --git a/app/components/Views/confirmations/SendFlow/Confirm/validation.test.ts b/app/components/Views/confirmations/SendFlow/Confirm/validation.test.ts index e9ed1b98278d..f90a6542d9f5 100644 --- a/app/components/Views/confirmations/SendFlow/Confirm/validation.test.ts +++ b/app/components/Views/confirmations/SendFlow/Confirm/validation.test.ts @@ -1,4 +1,4 @@ -import { BN } from 'ethereumjs-util'; +import BN from 'bn.js'; import { validateSufficientBalance, validateSufficientTokenBalance } from './validation'; import { renderFromWei, hexToBN } from '../../../../../util/number'; import { diff --git a/app/components/Views/confirmations/SendFlow/Confirm/validation.ts b/app/components/Views/confirmations/SendFlow/Confirm/validation.ts index 884c939441ed..c50a2057196f 100644 --- a/app/components/Views/confirmations/SendFlow/Confirm/validation.ts +++ b/app/components/Views/confirmations/SendFlow/Confirm/validation.ts @@ -4,7 +4,7 @@ import { decodeTransferData, } from '../../../../../util/transactions'; import { strings } from '../../../../../../locales/i18n'; -import { BN } from 'ethereumjs-util'; +import type BN from 'bn.js'; interface SelectedAsset { address: string; diff --git a/app/components/hooks/useAddressBalance/useAddressBalance.test.tsx b/app/components/hooks/useAddressBalance/useAddressBalance.test.tsx index f3f8592d8a8f..fba9711d3a8e 100644 --- a/app/components/hooks/useAddressBalance/useAddressBalance.test.tsx +++ b/app/components/hooks/useAddressBalance/useAddressBalance.test.tsx @@ -8,7 +8,7 @@ import { Asset } from './useAddressBalance.types'; import useAddressBalance from './useAddressBalance'; import backgroundState from '../../../util/test/initial-root-state'; import { createMockAccountsControllerState } from '../../../util/test/accountsControllerTestUtils'; -import { BN } from 'ethereumjs-util'; +import type BN from 'bn.js'; const MOCK_ADDRESS_1 = '0x0'; const MOCK_ADDRESS_2 = '0x1'; diff --git a/app/components/hooks/useAddressBalance/useAddressBalance.ts b/app/components/hooks/useAddressBalance/useAddressBalance.ts index af95e0187e32..d0b301bdb17c 100644 --- a/app/components/hooks/useAddressBalance/useAddressBalance.ts +++ b/app/components/hooks/useAddressBalance/useAddressBalance.ts @@ -1,6 +1,7 @@ import { ERC1155, ERC721 } from '@metamask/controller-utils'; import { useEffect, useState } from 'react'; import { useSelector } from 'react-redux'; +import BN from 'bn.js'; import Engine from '../../../core/Engine'; import { getTicker } from '../../../util/transactions'; @@ -110,7 +111,8 @@ const useAddressBalance = ( address, ); fromAccBalance = `${renderFromTokenMinimalUnit( - fromAccBalance || '0', + // This is to work around incompatibility between bn.js v4/v5 - should be removed when migration to v5 is complete + new BN(fromAccBalance?.toString(10) || '0', 10), decimals, )} ${symbol}`; setAddressBalance(fromAccBalance); diff --git a/app/components/hooks/useTokenBalance.tsx b/app/components/hooks/useTokenBalance.tsx index 8962c9ce352f..f3e166fc3927 100644 --- a/app/components/hooks/useTokenBalance.tsx +++ b/app/components/hooks/useTokenBalance.tsx @@ -1,6 +1,6 @@ import { useState, useEffect, Dispatch, SetStateAction } from 'react'; import Engine from '../../core/Engine'; -import { BN } from 'ethereumjs-util'; +import type BN from 'bn.js'; /** * Hook to handle the balance of ERC20 tokens diff --git a/app/components/hooks/useTokenBalancesController/__snapshots__/useTokenBalancesController.test.tsx.snap b/app/components/hooks/useTokenBalancesController/__snapshots__/useTokenBalancesController.test.tsx.snap index a066bf90ca6e..2c642f086298 100644 --- a/app/components/hooks/useTokenBalancesController/__snapshots__/useTokenBalancesController.test.tsx.snap +++ b/app/components/hooks/useTokenBalancesController/__snapshots__/useTokenBalancesController.test.tsx.snap @@ -2,24 +2,24 @@ exports[`useTokenBalancesController() should not rerender when state is not changed 1`] = ` - {"data":{"0x326836cc6cd09B5aa59B81A7F72F25FcC0136b95":"2a"}} + {"data":{"0x326836cc6cd09B5aa59B81A7F72F25FcC0136b95":"0x2a"}} `; exports[`useTokenBalancesController() should not rerender when state is not changed 2`] = ` - {"data":{"0x326836cc6cd09B5aa59B81A7F72F25FcC0136b95":"2a"}} + {"data":{"0x326836cc6cd09B5aa59B81A7F72F25FcC0136b95":"0x2a"}} `; exports[`useTokenBalancesController() should rerender when state changed 1`] = ` - {"data":{"0x326836cc6cd09B5aa59B81A7F72F25FcC0136b95":"2a"}} + {"data":{"0x326836cc6cd09B5aa59B81A7F72F25FcC0136b95":"0x2a"}} `; exports[`useTokenBalancesController() should rerender when state changed 2`] = ` - {"data":{"0x326836cc6cd09B5aa59B81A7F72F25FcC0136b95":"2a","0x326836cc6cd09B5aa59B81A7F72F25FcC0136b96":"539"}} + {"data":{"0x326836cc6cd09B5aa59B81A7F72F25FcC0136b95":"0x2a","0x326836cc6cd09B5aa59B81A7F72F25FcC0136b96":"0x539"}} `; diff --git a/app/components/hooks/useTokenBalancesController/useTokenBalancesController.test.tsx b/app/components/hooks/useTokenBalancesController/useTokenBalancesController.test.tsx index b575c66c4cc8..c187e1696da1 100644 --- a/app/components/hooks/useTokenBalancesController/useTokenBalancesController.test.tsx +++ b/app/components/hooks/useTokenBalancesController/useTokenBalancesController.test.tsx @@ -4,7 +4,6 @@ import { Provider } from 'react-redux'; import { createStore, Store } from 'redux'; import { act, render, waitFor } from '@testing-library/react-native'; import useTokenBalancesController from './useTokenBalancesController'; -import { BN } from 'ethereumjs-util'; import { cloneDeep } from 'lodash'; import { backgroundState } from '../../../util/test/initial-root-state'; @@ -15,7 +14,7 @@ const mockInitialState = { ...backgroundState, TokenBalancesController: { contractBalances: { - '0x326836cc6cd09B5aa59B81A7F72F25FcC0136b95': new BN(0x2a), + '0x326836cc6cd09B5aa59B81A7F72F25FcC0136b95': '0x2a', }, }, }, @@ -104,7 +103,7 @@ describe('useTokenBalancesController()', () => { testStore.dispatch({ type: 'add-balances', value: { - '0x326836cc6cd09B5aa59B81A7F72F25FcC0136b96': new BN(0x539), + '0x326836cc6cd09B5aa59B81A7F72F25FcC0136b96': '0x539', }, }); }); @@ -122,7 +121,7 @@ describe('useTokenBalancesController()', () => { testStore.dispatch({ type: 'add-balances', value: { - '0x326836cc6cd09B5aa59B81A7F72F25FcC0136b95': new BN(0x2a), + '0x326836cc6cd09B5aa59B81A7F72F25FcC0136b95': '0x2a', }, }); }); diff --git a/app/constants/transaction.ts b/app/constants/transaction.ts index 92bde7b16483..bf1f3b973acb 100644 --- a/app/constants/transaction.ts +++ b/app/constants/transaction.ts @@ -1,4 +1,4 @@ -import { BN } from 'ethereumjs-util'; +import BN from 'bn.js'; // Transaction Status export const TX_UNAPPROVED = 'unapproved'; diff --git a/app/store/migrations/029.ts b/app/store/migrations/029.ts index af4f1ebf1ba1..28f56e470928 100644 --- a/app/store/migrations/029.ts +++ b/app/store/migrations/029.ts @@ -2,7 +2,6 @@ import { toHex } from '@metamask/controller-utils'; import { Hex, hasProperty, isObject } from '@metamask/utils'; import { regex } from '../../../app/util/regex'; -//@ts-expect-error - This error is expected, but ethereumjs-util exports this function import { isHexString } from 'ethereumjs-util'; import { TransactionParams } from '@metamask/transaction-controller'; import { captureException } from '@sentry/react-native'; @@ -466,7 +465,7 @@ export default async function migrate(stateAsync: unknown) { if (Array.isArray(transactionControllerState.transactions)) { transactionControllerState.transactions.forEach( (transaction: TransactionParams, index: number) => { - if (transaction && !isHexString(transaction.chainId)) { + if (transaction && (!transaction.chainId || (transaction.chainId && !isHexString(transaction.chainId)))) { if ( Array.isArray(transactionControllerState.transactions) && isObject(transactionControllerState.transactions[index]) diff --git a/app/store/migrations/031.ts b/app/store/migrations/031.ts index ba4b06ca1192..0ad5dcdf64e3 100644 --- a/app/store/migrations/031.ts +++ b/app/store/migrations/031.ts @@ -6,7 +6,6 @@ import { TokensControllerState, } from '@metamask/assets-controllers'; import { toHex } from '@metamask/controller-utils'; -//@ts-expect-error - This error is expected, but ethereumjs-util exports this function import { isHexString } from 'ethereumjs-util'; /** diff --git a/app/util/address/index.ts b/app/util/address/index.ts index 34071c0579ae..10c09ebae639 100644 --- a/app/util/address/index.ts +++ b/app/util/address/index.ts @@ -3,7 +3,6 @@ import { isValidAddress, addHexPrefix, isValidChecksumAddress, - //@ts-expect-error - This error is expected, but ethereumjs-util exports this function isHexPrefixed, } from 'ethereumjs-util'; import punycode from 'punycode/punycode'; diff --git a/app/util/confirmation/transactions.test.ts b/app/util/confirmation/transactions.test.ts index 1c124ff1fd68..a21dfb8dcb5c 100644 --- a/app/util/confirmation/transactions.test.ts +++ b/app/util/confirmation/transactions.test.ts @@ -1,4 +1,4 @@ -import { BN } from 'ethereumjs-util'; +import BN from 'bn.js'; import { buildTransactionParams } from './transactions'; import { GAS_ESTIMATE_TYPES } from '@metamask/gas-fee-controller'; import { TransactionEnvelopeType } from '@metamask/transaction-controller'; diff --git a/app/util/conversion/index.js b/app/util/conversion/index.js index 4046c7277244..462345574646 100644 --- a/app/util/conversion/index.js +++ b/app/util/conversion/index.js @@ -22,8 +22,9 @@ */ import BigNumber from 'bignumber.js'; +import BN from 'bn.js'; -import { stripHexPrefix, BN } from 'ethereumjs-util'; +import { stripHexPrefix } from 'ethereumjs-util'; // Big Number Constants const BIG_NUMBER_WEI_MULTIPLIER = new BigNumber('1000000000000000000'); diff --git a/app/util/custom-gas/index.js b/app/util/custom-gas/index.js index 3583b88fab3c..e2950eb58caf 100644 --- a/app/util/custom-gas/index.js +++ b/app/util/custom-gas/index.js @@ -1,4 +1,4 @@ -import { BN } from 'ethereumjs-util'; +import BN from 'bn.js'; import { renderFromWei, weiToFiat, toWei, conversionUtil } from '../number'; import { strings } from '../../../locales/i18n'; import TransactionTypes from '../../core/TransactionTypes'; diff --git a/app/util/dappTransactions/index.test.ts b/app/util/dappTransactions/index.test.ts index f4c625d0518b..b7444f91bb54 100644 --- a/app/util/dappTransactions/index.test.ts +++ b/app/util/dappTransactions/index.test.ts @@ -1,4 +1,4 @@ -import { BN } from 'ethereumjs-util'; +import BN from 'bn.js'; import { strings } from '../../../locales/i18n'; import Engine from '../../core/Engine'; diff --git a/app/util/dappTransactions/index.ts b/app/util/dappTransactions/index.ts index d2d93d8fd74e..8a10390cf400 100644 --- a/app/util/dappTransactions/index.ts +++ b/app/util/dappTransactions/index.ts @@ -1,11 +1,11 @@ +import { remove0x } from '@metamask/utils'; import { isBN, hexToBN } from '../number'; import { safeToChecksumAddress } from '../address'; import Engine from '../../core/Engine'; import TransactionTypes from '../../core/TransactionTypes'; import { toLowerCaseEquals } from '../general'; import { strings } from '../../../locales/i18n'; -import { BN } from 'ethereumjs-util'; -import { lt } from '../lodash'; +import BN from 'bn.js'; import { estimateGas as controllerEstimateGas } from '../transaction-controller'; interface opts { @@ -115,6 +115,32 @@ interface ContractBalances { [key: string]: string; } +const getTokenBalance = async ( + from: string, + selectedAsset: SelectedAsset, + selectedAddress: string, + contractBalances: ContractBalances, +): Promise => { + const checksummedFrom = safeToChecksumAddress(from) || ''; + if (selectedAddress === from && contractBalances[selectedAsset.address]) { + return hexToBN( + remove0x(contractBalances[selectedAsset.address].toString()), + ); + } + try { + const { AssetsContractController } = Engine.context; + // TODO: Roundtrip string conversion can be removed when bn.js v4 is superseded with v5 + const contractBalanceForAddress = await AssetsContractController.getERC20BalanceOf( + selectedAsset.address, + checksummedFrom, + ); + const contractBalanceForAddressBN = hexToBN(contractBalanceForAddress.toString(16)); + return contractBalanceForAddressBN; + } catch (e) { + // Don't validate balance if error + } +}; + /** * Validates asset (ERC20) transaction amount * @@ -131,7 +157,6 @@ export const validateTokenAmount = async ( allowEmpty = true, ): Promise => { if (!allowEmpty) { - const checksummedFrom = safeToChecksumAddress(from) || ''; if (!value) { return strings('transaction.invalid_amount'); @@ -144,33 +169,15 @@ export const validateTokenAmount = async ( if (!from) { return strings('transaction.invalid_from_address'); } - // If user trying to send a token that doesn't own, validate balance querying contract - // If it fails, skip validation - let contractBalanceForAddress: BN | undefined | number; - if (selectedAddress === from && contractBalances[selectedAsset.address]) { - contractBalanceForAddress = hexToBN( - contractBalances[selectedAsset.address].toString(), - ); - } else { - try { - const { AssetsContractController } = Engine.context; - contractBalanceForAddress = - await AssetsContractController.getERC20BalanceOf( - selectedAsset.address, - checksummedFrom, - ); - } catch (e) { - // Don't validate balance if error - } + + if (value && !isBN(value)) { + return strings('transaction.invalid_amount'); + } + + const contractBalanceForAddress = await getTokenBalance(from, selectedAsset, selectedAddress, contractBalances); + if (contractBalanceForAddress?.lt(value)) { + return strings('transaction.insufficient'); } - if (value && !isBN(value)) return strings('transaction.invalid_amount'); - const validateAssetAmount = - contractBalanceForAddress && - lt( - contractBalanceForAddress as unknown as number, - value as unknown as number, - ); - if (validateAssetAmount) return strings('transaction.insufficient'); } }; diff --git a/app/util/number/index.js b/app/util/number/index.js index 28027c52b7ae..b1422efd5927 100644 --- a/app/util/number/index.js +++ b/app/util/number/index.js @@ -1,20 +1,17 @@ /** * Collection of utility functions for consistent formatting and conversion */ -import { BN, stripHexPrefix } from 'ethereumjs-util'; +import { stripHexPrefix } from 'ethereumjs-util'; +import BN from 'bn.js'; import { utils as ethersUtils } from 'ethers'; import convert from '@metamask/ethjs-unit'; -import { - BNToHex, - hexToBN as controllerHexToBN, -} from '@metamask/controller-utils'; -import numberToBN from 'number-to-bn'; +import numberToBN from '@metamask/number-to-bn'; +import { add0x, remove0x } from '@metamask/utils'; import BigNumber from 'bignumber.js'; import currencySymbols from '../currency-symbols.json'; import { isZero } from '../lodash'; import { regex } from '../regex'; -export { BNToHex }; // Big Number Constants const BIG_NUMBER_WEI_MULTIPLIER = new BigNumber('1000000000000000000'); @@ -28,10 +25,27 @@ const BIG_NUMBER_ETH_MULTIPLIER = new BigNumber('1'); * @param inputHex - Number represented as a hex string. * @returns A BN instance. */ -export const hexToBN = (inputHex) => - typeof inputHex !== 'string' - ? new BN(inputHex, 16) - : controllerHexToBN(inputHex); +export function hexToBN(inputHex) { + if (typeof inputHex === 'number' && isNaN(inputHex) || !inputHex) { + return new BN(0); + } + + return typeof inputHex === 'string' + ? new BN(remove0x(inputHex), 16) + : new BN(inputHex, 16); +} + +/** + * Converts a BN object to a hex string with a '0x' prefix. + * + * @param inputBn - BN instance to convert to a hex string. + * @returns A '0x'-prefixed hex string. + */ +// TODO: Either fix this lint violation or explain why it's necessary to ignore. +// eslint-disable-next-line @typescript-eslint/naming-convention +export function BNToHex(inputBn) { + return add0x(inputBn.toString(16)); +} // Setter Maps const toBigNumber = { @@ -101,9 +115,15 @@ export function fromWei(value = 0, unit = 'ether') { */ export function fromTokenMinimalUnit( minimalInput, - decimals, - isRounding = true, + decimals = 0, + _isRounding = true, ) { + const value = new BigNumber( + (isBN(minimalInput) ? minimalInput.toString(10) : minimalInput), + 10, + ); + return value.shiftedBy(parseInt(-decimals, 10)).toString(10, decimals); + /* minimalInput = isRounding ? Number(minimalInput) : minimalInput; const prefixedInput = addHexPrefix(minimalInput.toString(16)); let minimal = safeNumberToBN(prefixedInput); @@ -124,6 +144,7 @@ export function fromTokenMinimalUnit( value = '-' + value; } return value; + */ } /** @@ -133,11 +154,12 @@ export function fromTokenMinimalUnit( * @param {number} decimals - Token decimals to convert * @returns {string} - String containing the new number */ -export function fromTokenMinimalUnitString(minimalInput, decimals) { +export function fromTokenMinimalUnitString(minimalInput, decimals = 0) { if (typeof minimalInput !== 'string') { throw new TypeError('minimalInput must be a string'); } + console.warn('HARP', { minimalInput, decimals, }); const tokenFormat = ethersUtils.formatUnits(minimalInput, decimals); const isInteger = Boolean(regex.integer.exec(tokenFormat)); @@ -153,11 +175,21 @@ export function fromTokenMinimalUnitString(minimalInput, decimals) { * * @param {number|string|BN} tokenValue - Value to convert * @param {number} decimals - Unit to convert from, ether by default - * @returns {Object} - BN instance containing the new number + * @returns {string} - string format of new number */ -export function toTokenMinimalUnit(tokenValue, decimals) { - const base = toBN(Math.pow(10, decimals).toString()); - let value = convert.numberToString(tokenValue); +export function toTokenMinimalUnit(tokenValue, decimals = 0) { + //const base = toBN(Math.pow(10, decimals).toString()); + const valueBigNum = new BigNumber(tokenValue.toString(10), 10); + if (valueBigNum.isNaN()) { + throw new Error(`Could not parse '${tokenValue}' (${typeof tokenValue}) into BigNumber`); + } + const result = valueBigNum.shiftedBy(decimals); + if (result.gt(0) && result.lt(1)) { + throw new Error(`Invalid input '${JSON.stringify(tokenValue, decimals)}'.`); + } + //let value = convert.numberToString(tokenValue); + return result.toString(10); + /* const negative = value.substring(0, 1) === '-'; if (negative) { value = value.substring(1); @@ -203,6 +235,7 @@ export function toTokenMinimalUnit(tokenValue, decimals) { tokenMinimal = tokenMinimal.mul(negative); } return new BN(tokenMinimal.toString(10), 10); + */ } /** @@ -367,7 +400,16 @@ export function isDecimal(value) { * @returns {Object} - BN instance */ export function toBN(value) { - return new BN(value); + // TODO: Throw on NaN input + if (typeof value === 'number' && isNaN(value) || !value || value === 'NaN') { + return new BN(0); + } + if (isBN(value)) { + return value; + } + return value?.startsWith('0x') + ? hexToBN(value) + : new BN(value || '0'); } /** @@ -426,10 +468,10 @@ export function toWei(value, unit = 'ether') { * * @param {number|string|BN} value - Value to convert * @param {string} unit - Unit to convert from, ether by default - * @returns {Object} - BN instance containing the new number + * @returns {Object} - BIgNumber instance containing the new number */ export function toGwei(value, unit = 'ether') { - return fromWei(value, unit) * 1000000000; + return new BigNumber(fromWei(value, unit)).multipliedBy(1000000000); } /** @@ -437,13 +479,11 @@ export function toGwei(value, unit = 'ether') { * * @param {number|string|BN} value - Value to convert * @param {string} unit - Unit to convert from, ether by default - * @returns {string} - String instance containing the renderable number + * @returns {string} - String representation of the converted value */ export function renderToGwei(value, unit = 'ether') { - const gwei = fromWei(value, unit) * 1000000000; - let gweiFixed = parseFloat(Math.round(gwei)); - gweiFixed = isNaN(gweiFixed) ? 0 : gweiFixed; - return gweiFixed; + const gwei = toGwei(value, unit); + return gwei.toString(10); } /** @@ -461,8 +501,10 @@ export function weiToFiat( currencyCode, decimalsToShow = 5, ) { - if (!conversionRate) return undefined; - if (!wei || !isBN(wei) || !conversionRate) { + if (typeof wei === 'undefined' || !conversionRate || (typeof wei === 'number' || isBN(wei)) && isNaN(wei) || typeof wei !== 'number' && !isBN(wei)) { + return undefined; + } + if (!wei) { return addCurrencySymbol(0, currencyCode); } decimalsToShow = (currencyCode === 'usd' && 2) || undefined; @@ -526,7 +568,7 @@ export function addCurrencySymbol( } /** - * Converts wei expressed as a BN instance into a human-readable fiat string + * Converts wei expressed as a BN instance into a fiat number * * @param {number|string|BN} wei - BN corresponding to an amount of wei * @param {number} conversionRate - ETH to current currency conversion rate @@ -534,10 +576,11 @@ export function addCurrencySymbol( * @returns {Number} - The converted balance */ export function weiToFiatNumber(wei, conversionRate, decimalsToShow = 5) { - const base = Math.pow(10, decimalsToShow); - const eth = fromWei(wei).toString(); - let value = parseFloat(Math.floor(eth * conversionRate * base) / base); - value = isNaN(value) ? 0.0 : value; + if (!conversionRate) { + return undefined; + } + const eth = new BigNumber(fromWei(wei), 10); + const value = parseFloat(eth.multipliedBy(new BigNumber(conversionRate, 10)).decimalPlaces(decimalsToShow).toString()); return value; } diff --git a/app/util/number/index.test.ts b/app/util/number/index.test.ts index 436f18c0c29f..f278d7b9bf9f 100644 --- a/app/util/number/index.test.ts +++ b/app/util/number/index.test.ts @@ -1,4 +1,4 @@ -import { BN } from 'ethereumjs-util'; +import BN from 'bn.js'; import { addCurrencySymbol, @@ -98,10 +98,6 @@ describe('Number utils :: toWei', () => { // BN.js do not support such big numbers expect(() => toWei(new BN(1.337e18))).toThrow(Error); expect(() => toWei(new BN(1337000000000000000))).toThrow(Error); - // For some reason this returns 8338418000000000000000000 wei - expect(toWei(new BN('1.337e18'))).not.toEqual( - '1337000000000000000000000000000000000', - ); }); }); @@ -152,12 +148,17 @@ describe('Number utils :: fromTokenMinimalUnit', () => { // test decimal greater than 30,000 expect(fromTokenMinimalUnit(new BN('50000000000000000000000'), 18)).toEqual( - '49999.999999999995805696', + '50000', + ); + expect(fromTokenMinimalUnit(parseFloat('69999999999999999999999'), 18)).toEqual( + '69999.999999999996', ); // test decimal less than 1e-14 expect(fromTokenMinimalUnit(hexToBN('576129d2d21d64a5'), 18)).toEqual( - '6.296359739485676544', + // exactly correct: + // '6.296359739485677000', + '6.296359739485676709', ); }); @@ -350,20 +351,20 @@ describe('Number utils :: fromTokenMinimalUnitString', () => { describe('Number utils :: toTokenMinimalUnit', () => { it('toTokenMinimalUnit using number', () => { - expect(toTokenMinimalUnit(1337, 6)).toEqual(new BN('1337000000', 10)); - expect(toTokenMinimalUnit(1337, 0)).toEqual(new BN('1337')); - expect(toTokenMinimalUnit(1337.1, 1)).toEqual(new BN('13371')); + expect(toTokenMinimalUnit(1337, 6)).toEqual('1337000000'); + expect(toTokenMinimalUnit(1337, 0)).toEqual('1337'); + expect(toTokenMinimalUnit(1337.1, 1)).toEqual('13371'); }); it('toTokenMinimalUnit using string', () => { - expect(toTokenMinimalUnit('1337', 6)).toEqual(new BN('1337000000')); - expect(toTokenMinimalUnit('1337', 0)).toEqual(new BN('1337')); - expect(toTokenMinimalUnit('1337.1', 2)).toEqual(new BN('133710')); + expect(toTokenMinimalUnit('1337', 6)).toEqual('1337000000'); + expect(toTokenMinimalUnit('1337', 0)).toEqual('1337'); + expect(toTokenMinimalUnit('1337.1', 2)).toEqual('133710'); }); it('toTokenMinimalUnit using BN number', () => { - expect(toTokenMinimalUnit(new BN('1337'), 0)).toEqual(new BN('1337')); - expect(toTokenMinimalUnit(new BN('1337'), 6)).toEqual(new BN('1337000000')); + expect(toTokenMinimalUnit(new BN('1337'), 0)).toEqual('1337'); + expect(toTokenMinimalUnit(new BN('1337'), 6)).toEqual('1337000000'); }); it('toTokenMinimalUnit using invalid inputs', () => { diff --git a/app/util/smart-transactions/index.test.ts b/app/util/smart-transactions/index.test.ts index aeec09642c66..c2f09371b0c8 100644 --- a/app/util/smart-transactions/index.test.ts +++ b/app/util/smart-transactions/index.test.ts @@ -313,7 +313,7 @@ describe('Smart Transactions utils', () => { describe('getSmartTransactionMetricsProperties', () => { let smartTransactionsController: SmartTransactionsController; let controllerMessenger: ControllerMessenger; - + beforeEach(() => { smartTransactionsController = { getSmartTransactionByMinedTxHash: jest.fn(), @@ -322,7 +322,7 @@ describe('Smart Transactions utils', () => { subscribe: jest.fn(), } as unknown as ControllerMessenger; }); - + it('returns empty object if transactionMeta is undefined', async () => { const result = await getSmartTransactionMetricsProperties( smartTransactionsController, @@ -332,7 +332,7 @@ describe('Smart Transactions utils', () => { ); expect(result).toEqual({}); }); - + it('returns metrics if smartTransaction is found by getSmartTransactionByMinedTxHash', async () => { const transactionMeta = { hash: '0x123' } as TransactionMeta; const smartTransaction = { @@ -343,7 +343,7 @@ describe('Smart Transactions utils', () => { }, }; (smartTransactionsController.getSmartTransactionByMinedTxHash as jest.Mock).mockReturnValue(smartTransaction); - + const result = await getSmartTransactionMetricsProperties( smartTransactionsController, transactionMeta, @@ -356,7 +356,7 @@ describe('Smart Transactions utils', () => { smart_transaction_proxied: true, }); }); - + it('waits for smartTransaction if not found and waitForSmartTransaction is true', async () => { const transactionMeta = { hash: '0x123' } as TransactionMeta; const smartTransaction = { @@ -372,7 +372,7 @@ describe('Smart Transactions utils', () => { setTimeout(() => callback(smartTransaction), 100); } }); - + const result = await getSmartTransactionMetricsProperties( smartTransactionsController, transactionMeta, @@ -385,11 +385,11 @@ describe('Smart Transactions utils', () => { smart_transaction_proxied: false, }); }); - + it('returns empty object if smartTransaction is not found and waitForSmartTransaction is false', async () => { const transactionMeta = { hash: '0x123' } as TransactionMeta; (smartTransactionsController.getSmartTransactionByMinedTxHash as jest.Mock).mockReturnValue(undefined); - + const result = await getSmartTransactionMetricsProperties( smartTransactionsController, transactionMeta, @@ -398,12 +398,12 @@ describe('Smart Transactions utils', () => { ); expect(result).toEqual({}); }); - + it('returns empty object if smartTransaction is found but statusMetadata is undefined', async () => { const transactionMeta = { hash: '0x123' } as TransactionMeta; const smartTransaction = {}; (smartTransactionsController.getSmartTransactionByMinedTxHash as jest.Mock).mockReturnValue(smartTransaction); - + const result = await getSmartTransactionMetricsProperties( smartTransactionsController, transactionMeta, @@ -412,7 +412,7 @@ describe('Smart Transactions utils', () => { ); expect(result).toEqual({}); }); - + it('returns metrics if smartTransaction is found with statusMetadata', async () => { const transactionMeta = { hash: '0x123' } as TransactionMeta; const smartTransaction = { @@ -423,7 +423,7 @@ describe('Smart Transactions utils', () => { }, }; (smartTransactionsController.getSmartTransactionByMinedTxHash as jest.Mock).mockReturnValue(smartTransaction); - + const result = await getSmartTransactionMetricsProperties( smartTransactionsController, transactionMeta, diff --git a/app/util/smart-transactions/index.ts b/app/util/smart-transactions/index.ts index 82ce0d8bd511..e410a210c63f 100644 --- a/app/util/smart-transactions/index.ts +++ b/app/util/smart-transactions/index.ts @@ -103,8 +103,8 @@ export const getSmartTransactionMetricsProperties = async ( smartTransactionsController.getSmartTransactionByMinedTxHash( transactionMeta.hash, ); - const shouldWaitForSmartTransactionConfirmationDoneEvent = - waitForSmartTransaction && + const shouldWaitForSmartTransactionConfirmationDoneEvent = + waitForSmartTransaction && !smartTransaction?.statusMetadata && // We get this after polling for a status for a Smart Transaction. controllerMessenger; if (shouldWaitForSmartTransactionConfirmationDoneEvent) { diff --git a/app/util/transaction-reducer-helpers.ts b/app/util/transaction-reducer-helpers.ts index 562fa6db9a7a..7dc03b5eb20f 100644 --- a/app/util/transaction-reducer-helpers.ts +++ b/app/util/transaction-reducer-helpers.ts @@ -1,5 +1,5 @@ import { SecurityAlertResponse } from '@metamask/transaction-controller'; -import { BN } from 'ethereumjs-util'; +import type BN from 'bn.js'; interface TxMeta { data?: string; diff --git a/app/util/transactions/index.js b/app/util/transactions/index.js index 71614e75e976..de1bd5f0332a 100644 --- a/app/util/transactions/index.js +++ b/app/util/transactions/index.js @@ -1,4 +1,5 @@ -import { addHexPrefix, toChecksumAddress, BN } from 'ethereumjs-util'; +import { addHexPrefix, toChecksumAddress } from 'ethereumjs-util'; +import BN from 'bn.js'; import { rawEncode, rawDecode } from 'ethereumjs-abi'; import BigNumber from 'bignumber.js'; import humanizeDuration from 'humanize-duration'; @@ -1478,7 +1479,7 @@ export const generateTxWithNewTokenAllowance = ( spenderAddress, transaction, ) => { - const uint = toTokenMinimalUnit(tokenValue, tokenDecimals); + const uint = new BN(toTokenMinimalUnit(tokenValue, tokenDecimals)); const approvalData = generateApprovalData({ spender: spenderAddress, value: uint.gt(UINT256_BN_MAX_VALUE) diff --git a/app/util/transactions/index.test.ts b/app/util/transactions/index.test.ts index 2f8415f8871d..e1a0798b0485 100644 --- a/app/util/transactions/index.test.ts +++ b/app/util/transactions/index.test.ts @@ -1,5 +1,5 @@ import { swapsUtils } from '@metamask/swaps-controller'; -import { BN } from 'ethereumjs-util'; +import BN from 'bn.js'; /* eslint-disable-next-line import/no-namespace */ import * as controllerUtilsModule from '@metamask/controller-utils'; @@ -238,7 +238,7 @@ describe('Transactions utils :: parseTransactionLegacy', () => { const expectedResult = createExpectedResult({ totalHexValue: totalHexValueMocked, - transactionTotalAmount: '0.2 ERC20 + 0 tBNB', + transactionTotalAmount: '2 ERC20 + 0 tBNB', transactionTotalAmountFiat: '0 USD', transactionFee: '0 tBNB', }); @@ -1082,7 +1082,7 @@ describe('Transactions utils :: isApprovalTransaction', () => { }); describe('Transactions utils :: getTransactionReviewActionKey', () => { - const transaction = { to: '0xContractAddress' }; + const transaction = { to: '0xdeadbeef' }; const chainId = '1'; it('returns `Unknown Method` review action key when transaction action key exists', async () => { const expectedReviewActionKey = 'Unknown Method'; diff --git a/e2e/specs/ramps/offramp.spec.js b/e2e/specs/ramps/offramp.spec.js index d990ed0993ba..0af763e1fc64 100644 --- a/e2e/specs/ramps/offramp.spec.js +++ b/e2e/specs/ramps/offramp.spec.js @@ -32,7 +32,7 @@ const PaymentMethods = { DEBIT_OR_CREDIT: 'Debit or Credit', INSTANT_ACH_BANK_TRANSFER: 'Insant ACH Bank Transfer', ACH_BANK_TRANSFER: 'ACH Bank Transfer', -} +}; describe(SmokeAssets('Off-Ramp'), () => { beforeAll(async () => { @@ -67,7 +67,7 @@ describe(SmokeAssets('Off-Ramp'), () => { await SelectRegionView.tapRegionOption(Regions.CALIFORNIA); await SelectRegionView.tapContinueButton(); await SelectPaymentMethodView.tapPaymentMethodOption(PaymentMethods.DEBIT_OR_CREDIT); - await SelectPaymentMethodView.tapContinueButton(); + await SelectPaymentMethodView.tapContinueButton(); await Assertions.checkIfVisible(BuildQuoteView.amountToSellLabel); await Assertions.checkIfVisible(BuildQuoteView.getQuotesButton); }); diff --git a/package.json b/package.json index 49309856adfb..3ed82b4ea44d 100644 --- a/package.json +++ b/package.json @@ -164,6 +164,7 @@ "@metamask/message-signing-snap": "^0.3.3", "@metamask/network-controller": "^21.0.0", "@metamask/notification-services-controller": "^0.8.2", + "@metamask/number-to-bn": "^1.7.1", "@metamask/permission-controller": "^11.0.0", "@metamask/phishing-controller": "^12.0.3", "@metamask/post-message-stream": "^8.0.0", @@ -228,6 +229,7 @@ "axios": "^1.6.8", "base-64": "1.0.0", "bignumber.js": "^9.0.1", + "bn.js": "^5.2.1", "buffer": "6.0.3", "cockatiel": "^3.1.2", "compare-versions": "^3.6.0", @@ -242,8 +244,8 @@ "eth-json-rpc-filters": "^6.0.1", "eth-json-rpc-middleware": "9.0.1", "eth-url-parser": "1.0.4", - "ethereumjs-abi": "0.6.6", - "ethereumjs-util": "6.1.0", + "ethereumjs-abi": "^0.6.8", + "ethereumjs-util": "^7.1.5", "ethers": "^5.0.14", "ethjs-ens": "2.0.1", "eventemitter2": "^6.4.9", @@ -260,7 +262,6 @@ "lottie-react-native": "5.1.5", "mockttp": "^3.15.2", "multihashes": "0.4.14", - "number-to-bn": "1.7.0", "path": "0.12.7", "pbkdf2": "3.1.2", "pify": "6.1.0", @@ -392,6 +393,7 @@ "@testing-library/react": "14.0.0", "@testing-library/react-hooks": "^8.0.1", "@testing-library/react-native": "12.1.2", + "@types/bn.js": "^5.1.6", "@types/crypto-js": "^4.1.1", "@types/enzyme": "^3.10.12", "@types/eth-url-parser": "^1.0.0", @@ -434,7 +436,7 @@ "browserstack-local": "^1.5.1", "chromedriver": "^123.0.1", "depcheck": "^1.4.7", - "detox": "20.27.2", + "detox": "^20.27.5", "dotenv": "^16.0.3", "enzyme": "3.9.0", "enzyme-adapter-react-16": "1.10.0", @@ -575,7 +577,8 @@ "@metamask/sdk-communication-layer>eciesjs>secp256k1": false, "detox>ws>utf-8-validate": false, "ganache>@trufflesuite/uws-js-unofficial>utf-8-validate": false, - "@react-native-firebase/app>firebase>@firebase/firestore>@grpc/proto-loader>protobufjs": false + "@react-native-firebase/app>firebase>@firebase/firestore>@grpc/proto-loader>protobufjs": false, + "ethereumjs-util>ethereum-cryptography>keccak": true } }, "packageManager": "yarn@1.22.22" diff --git a/patches/detox+20.23.1.patch b/patches/detox+20.27.5.patch similarity index 100% rename from patches/detox+20.23.1.patch rename to patches/detox+20.27.5.patch diff --git a/yarn.lock b/yarn.lock index 0de7e3f473c7..5f8592fad6c1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9471,10 +9471,17 @@ dependencies: "@types/node" "*" -"@types/bn.js@^5.1.0", "@types/bn.js@^5.1.5": - version "5.1.5" - resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.1.5.tgz#2e0dacdcce2c0f16b905d20ff87aedbc6f7b4bf0" - integrity sha512-V46N0zwKRF5Q00AZ6hWtN0T8gGmDUaUzLWQvHFo5yThtVwK/VCenFY3wXVbOvNfajEpsTfQM4IN9k/d6gUVX3A== +"@types/bn.js@^4.11.3": + version "4.11.6" + resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-4.11.6.tgz#c306c70d9358aaea33cd4eda092a742b9505967c" + integrity sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg== + dependencies: + "@types/node" "*" + +"@types/bn.js@^5.1.0", "@types/bn.js@^5.1.5", "@types/bn.js@^5.1.6": + version "5.1.6" + resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.1.6.tgz#9ba818eec0c85e4d3c679518428afdf611d03203" + integrity sha512-Xh8vSwUeMKeYYrj3cX4lGQgFSF/N03r+tv4AiLl1SucqV+uTQpxRcnM8AkXKHwYP9ZPXOYXRr2KPXpVlIvqh9w== dependencies: "@types/node" "*" @@ -13317,7 +13324,7 @@ binary-extensions@^2.0.0: resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== -bindings@^1.2.1, bindings@^1.5.0: +bindings@^1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== @@ -13378,7 +13385,7 @@ bn.js@5.2.1, bn.js@^5.0.0, bn.js@^5.1.2, bn.js@^5.2.0, bn.js@^5.2.1: resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== -bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.10.0, bn.js@^4.11.0, bn.js@^4.11.8, bn.js@^4.11.9, bn.js@^4.12.0: +bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.11.0, bn.js@^4.11.8, bn.js@^4.11.9, bn.js@^4.12.0: version "4.12.0" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== @@ -15676,15 +15683,15 @@ detect-node@^2.0.4: resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1" integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g== -detox-copilot@^0.0.9: - version "0.0.9" - resolved "https://registry.yarnpkg.com/detox-copilot/-/detox-copilot-0.0.9.tgz#cc1ddd869868987d3527c93d2604b5d66f3c8466" - integrity sha512-Wk2fuisD8EH+349b0ysNWvZ7UEsThAChbYFlLqOR1jWkDaonEvgf6IOUlmxjvyTl9ENtl8ckd1U7k94yCBYwqw== +detox-copilot@^0.0.23: + version "0.0.23" + resolved "https://registry.yarnpkg.com/detox-copilot/-/detox-copilot-0.0.23.tgz#724aeb62424018b4b6d5620bb0dc7e800e4e4f6b" + integrity sha512-qDSdLwgPUMVawpE0R3agNWd2U69ilTnhf+SodSqqrkmTI0oG67IfkACvwox+K9Slcc8ki6y0Bw6QVBi54MqpaA== -detox@20.27.2: - version "20.27.2" - resolved "https://registry.yarnpkg.com/detox/-/detox-20.27.2.tgz#aa22a146b968b6e5f78687557081cf32d26ca066" - integrity sha512-cC6S40v7ix+uA5jYzG8eazSs7YtOWgc2aCwWLZIIzfE5Kvo0gfHgtqeRhrYWCMZaj/irKKs39h2B070oNQOIrA== +detox@^20.27.5: + version "20.27.5" + resolved "https://registry.yarnpkg.com/detox/-/detox-20.27.5.tgz#f67d1a0c9ddbb2b6edb838e1b32c63b486be66ea" + integrity sha512-JBe3fONwaSxYubd/36SZh3c2MaYs+Cx7sOA4GJfh16QTyoB7XvvbGrSlQDTbag/f0j5RZt4judPtg5A3P1/Uhg== dependencies: ajv "^8.6.3" bunyan "^1.8.12" @@ -15692,7 +15699,7 @@ detox@20.27.2: caf "^15.0.1" chalk "^4.0.0" child-process-promise "^2.2.0" - detox-copilot "^0.0.9" + detox-copilot "^0.0.23" execa "^5.1.1" find-up "^5.0.0" fs-extra "^11.0.0" @@ -17100,41 +17107,28 @@ ethereum-ens-network-map@^1.0.0: resolved "https://registry.yarnpkg.com/ethereum-ens-network-map/-/ethereum-ens-network-map-1.0.2.tgz#4e27bad18dae7bd95d84edbcac2c9e739fc959b9" integrity sha512-5qwJ5n3YhjSpE6O/WEBXCAb2nagUgyagJ6C0lGUBWC4LjKp/rRzD+pwtDJ6KCiITFEAoX4eIrWOjRy0Sylq5Hg== -ethereumjs-abi@0.6.6: - version "0.6.6" - resolved "https://registry.yarnpkg.com/ethereumjs-abi/-/ethereumjs-abi-0.6.6.tgz#f8ba3413a98478173f5a00f7f1316819db1d09ec" - integrity sha512-w8KubDsA/+OAuqtIR9RGsMcoZ5nhM8vxwjJAJvEIY+clhxA3BHoLG3+ClYQaQhD0n3mlDt3U5rBrmSVJvI3c8A== - dependencies: - bn.js "^4.10.0" - ethereumjs-util "^5.0.0" - -ethereumjs-util@6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-6.1.0.tgz#e9c51e5549e8ebd757a339cc00f5380507e799c8" - integrity sha512-URESKMFbDeJxnAxPppnk2fN6Y3BIatn9fwn76Lm8bQlt+s52TpG8dN9M66MLPuRAiAOIqL3dfwqWJf0sd0fL0Q== +ethereumjs-abi@^0.6.8: + version "0.6.8" + resolved "https://registry.yarnpkg.com/ethereumjs-abi/-/ethereumjs-abi-0.6.8.tgz#71bc152db099f70e62f108b7cdfca1b362c6fcae" + integrity sha512-Tx0r/iXI6r+lRsdvkFDlut0N08jWMnKRZ6Gkq+Nmw75lZe4e6o3EkSnkaBP5NF6+m5PTGAr9JP43N3LyeoglsA== dependencies: - bn.js "^4.11.0" - create-hash "^1.1.2" - ethjs-util "0.1.6" - keccak "^1.0.2" - rlp "^2.0.0" - safe-buffer "^5.1.1" - secp256k1 "^3.0.1" + bn.js "^4.11.8" + ethereumjs-util "^6.0.0" -ethereumjs-util@^5.0.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-5.2.1.tgz#a833f0e5fca7e5b361384dc76301a721f537bf65" - integrity sha512-v3kT+7zdyCm1HIqWlLNrHGqHGLpGYIhjeHxQjnDXjLT2FyGJDsd3LWMYUo7pAFRrk86CR3nUJfhC81CCoJNNGQ== +ethereumjs-util@^6.0.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz#fcb4e4dd5ceacb9d2305426ab1a5cd93e3163b69" + integrity sha512-W2Ktez4L01Vexijrm5EB6w7dg4n/TgpoYU4avuT5T3Vmnw/eCRtiBrJfQYS/DCSvDIOLn2k57GcHdeBcgVxAqw== dependencies: + "@types/bn.js" "^4.11.3" bn.js "^4.11.0" create-hash "^1.1.2" elliptic "^6.5.2" ethereum-cryptography "^0.1.3" - ethjs-util "^0.1.3" - rlp "^2.0.0" - safe-buffer "^5.1.1" + ethjs-util "0.1.6" + rlp "^2.2.3" -ethereumjs-util@^7.0.10, ethereumjs-util@^7.0.8, ethereumjs-util@^7.1.2: +ethereumjs-util@^7.0.10, ethereumjs-util@^7.0.8, ethereumjs-util@^7.1.2, ethereumjs-util@^7.1.5: version "7.1.5" resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-7.1.5.tgz#9ecf04861e4fbbeed7465ece5f23317ad1129181" integrity sha512-SDl5kKrQAudFBUe5OJM9Ac6WmMyYmXX/6sTmLZ3ffG2eY6ZIGBes3pEDxNN6V72WyOw4CPD5RomKdsa8DAAwLg== @@ -17281,7 +17275,7 @@ ethjs-util@0.1.3: is-hex-prefixed "1.0.0" strip-hex-prefix "1.0.0" -ethjs-util@0.1.6, ethjs-util@^0.1.3, ethjs-util@^0.1.6: +ethjs-util@0.1.6, ethjs-util@^0.1.6: version "0.1.6" resolved "https://registry.yarnpkg.com/ethjs-util/-/ethjs-util-0.1.6.tgz#f308b62f185f9fe6237132fb2a9818866a5cd536" integrity sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w== @@ -21095,16 +21089,6 @@ keccak@3.0.2, keccak@^3.0.0: node-gyp-build "^4.2.0" readable-stream "^3.6.0" -keccak@^1.0.2: - version "1.4.0" - resolved "https://registry.yarnpkg.com/keccak/-/keccak-1.4.0.tgz#572f8a6dbee8e7b3aa421550f9e6408ca2186f80" - integrity sha512-eZVaCpblK5formjPjeTBik7TAg+pqnDrMHIffSvi9Lh7PQgM1+hSzakUeZFCk9DVVG0dacZJuaz2ntwlzZUIBw== - dependencies: - bindings "^1.2.1" - inherits "^2.0.3" - nan "^2.2.1" - safe-buffer "^5.1.0" - keygrip@~1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/keygrip/-/keygrip-1.1.0.tgz#871b1681d5e159c62a445b0c74b615e0917e7226" @@ -22784,7 +22768,7 @@ mz@^2.7.0: object-assign "^4.0.1" thenify-all "^1.0.0" -nan@^2.14.0, nan@^2.2.1: +nan@^2.14.0: version "2.20.0" resolved "https://registry.yarnpkg.com/nan/-/nan-2.20.0.tgz#08c5ea813dd54ed16e5bd6505bf42af4f7838ca3" integrity sha512-bk3gXBZDGILuuo/6sKtr0DQmSThYHLtNCdSdXk9YkxD/jK6X2vmCyyXBBxyqZ4XcnzTyYEAThfX3DCEnLf6igw== @@ -26492,7 +26476,7 @@ ripemd160@^2.0.0, ripemd160@^2.0.1, ripemd160@^2.0.2: hash-base "^3.0.0" inherits "^2.0.1" -rlp@^2.0.0, rlp@^2.2.4, rlp@^2.2.6: +rlp@^2.2.3, rlp@^2.2.4, rlp@^2.2.6: version "2.2.7" resolved "https://registry.yarnpkg.com/rlp/-/rlp-2.2.7.tgz#33f31c4afac81124ac4b283e2bd4d9720b30beaf" integrity sha512-d5gdPmgQ0Z+AklL2NVXr/IoSjNZFfTVvQWzL/AM2AOcSzYP2xjlb0AC8YyCLc41MSNf6P6QVtjgPdmVtzb+4lQ== @@ -26683,7 +26667,7 @@ scrypt-js@3.0.1, scrypt-js@^3.0.0, scrypt-js@^3.0.1: resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-3.0.1.tgz#d314a57c2aef69d1ad98a138a21fe9eafa9ee312" integrity sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA== -secp256k1@3.8.1, secp256k1@^3.0.1: +secp256k1@3.8.1: version "3.8.1" resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-3.8.1.tgz#b62a62a882d6b16f9b51fe599c6b3a861e36c59f" integrity sha512-tArjQw2P0RTdY7QmkNehgp6TVvQXq6ulIhxv8gaH6YubKG/wxxAoNKcbuXjDhybbc+b2Ihc7e0xxiGN744UIiQ==