1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-12-22 09:23:21 +01:00

Replace usages of conversion util in rest of UI folder in favor of Numeric (#17334)

This commit is contained in:
Brad Decker 2023-01-24 08:44:49 -06:00 committed by GitHub
parent dd09245ff6
commit 3ff12d70e9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 290 additions and 539 deletions

View File

@ -278,7 +278,8 @@ export class Numeric {
this.value = bnToBigNumber(value);
} else if (
isNullOrUndefined(value) ||
(typeof value === 'number' && isNaN(value))
(typeof value === 'number' && isNaN(value)) ||
(typeof value === 'string' && value === '')
) {
// There are parts of the codebase that call this method without a value,
// or with a 'NaN' (which is probably a bug somewhere in our tests?).

View File

@ -13,8 +13,8 @@ import {
} from '../../helpers/utils/confirm-tx.util';
import {
conversionUtil,
getValueFromWeiHex,
hexToDecimal,
sumHexes,
} from '../../../shared/modules/conversion.utils';
import { getAveragePriceEstimateInHexWEI } from '../../selectors/custom-gas';
@ -293,10 +293,7 @@ export function setTransactionToConfirm(transactionId) {
}
if (txParams.nonce) {
const nonce = conversionUtil(txParams.nonce, {
fromNumericBase: 'hex',
toNumericBase: 'dec',
});
const nonce = hexToDecimal(txParams.nonce);
dispatch(updateNonce(nonce));
}

View File

@ -1,5 +1,6 @@
import { addHexPrefix } from 'ethereumjs-util';
import abi from 'human-standard-token-abi';
import BigNumber from 'bignumber.js';
import { GAS_LIMITS, MIN_GAS_LIMIT_HEX } from '../../../shared/constants/gas';
import { calcTokenAmount } from '../../../shared/lib/transactions-controller-utils';
import { CHAIN_ID_TO_GAS_LIMIT_BUFFER_MAP } from '../../../shared/constants/network';
@ -8,11 +9,6 @@ import {
TransactionEnvelopeType,
} from '../../../shared/constants/transaction';
import { readAddressAsContract } from '../../../shared/modules/contract-utils';
import {
conversionUtil,
multiplyCurrencies,
} from '../../../shared/modules/conversion.utils';
import { ETH, GWEI } from '../../helpers/constants/common';
import {
addGasBuffer,
generateERC20TransferData,
@ -21,6 +17,7 @@ import {
} from '../../pages/send/send.utils';
import { getGasPriceInHexWei } from '../../selectors';
import { estimateGas } from '../../store/actions';
import { Numeric } from '../../../shared/modules/Numeric';
export async function estimateGasLimitForSend({
selectedAddress,
@ -109,15 +106,10 @@ export async function estimateGasLimitForSend({
if (!isSimpleSendOnNonStandardNetwork) {
// If we do not yet have a gasLimit, we must call into our background
// process to get an estimate for gasLimit based on known parameters.
paramsForGasEstimate.gas = addHexPrefix(
multiplyCurrencies(blockGasLimit, 0.95, {
multiplicandBase: 16,
multiplierBase: 10,
roundDown: '0',
toNumericBase: 'hex',
}),
);
paramsForGasEstimate.gas = new Numeric(blockGasLimit, 16)
.times(new Numeric(0.95, 10))
.round(0, BigNumber.ROUND_DOWN)
.toPrefixedHexString();
}
// The buffer multipler reduces transaction failures by ensuring that the
@ -268,14 +260,9 @@ export function generateTransactionParams(sendState) {
* @returns {string}
*/
export function getRoundedGasPrice(gasPriceEstimate) {
const gasPriceInDecGwei = conversionUtil(gasPriceEstimate, {
numberOfDecimals: 9,
toDenomination: GWEI,
fromNumericBase: 'dec',
toNumericBase: 'dec',
fromCurrency: ETH,
fromDenomination: GWEI,
});
const gasPriceInDecGwei = new Numeric(gasPriceEstimate, 10)
.round(9)
.toString();
const gasPriceAsNumber = Number(gasPriceInDecGwei);
return getGasPriceInHexWei(gasPriceAsNumber);
}

View File

@ -4,12 +4,8 @@ import { addHexPrefix } from 'ethereumjs-util';
import { debounce } from 'lodash';
import { v4 as uuidv4 } from 'uuid';
import {
conversionGreaterThan,
conversionUtil,
decimalToHex,
getValueFromWeiHex,
multiplyCurrencies,
subtractCurrencies,
sumHexes,
} from '../../../shared/modules/conversion.utils';
import { GAS_ESTIMATE_TYPES, GAS_LIMITS } from '../../../shared/constants/gas';
import {
@ -109,6 +105,7 @@ import {
calcGasTotal,
calcTokenAmount,
} from '../../../shared/lib/transactions-controller-utils';
import { Numeric } from '../../../shared/modules/Numeric';
import {
estimateGasLimitForSend,
generateTransactionParams,
@ -902,31 +899,21 @@ const slice = createSlice({
let amount = '0x0';
if (draftTransaction.asset.type === AssetType.token) {
const decimals = draftTransaction.asset.details?.decimals ?? 0;
const multiplier = Math.pow(10, Number(decimals));
amount = multiplyCurrencies(
draftTransaction.asset.balance,
multiplier,
{
toNumericBase: 'hex',
multiplicandBase: 16,
multiplierBase: 10,
},
);
amount = new Numeric(draftTransaction.asset.balance, 16)
.times(multiplier, 10)
.toString();
} else {
const _gasTotal = sumHexes(
const _gasTotal = new Numeric(
draftTransaction.gas.gasTotal || '0x0',
state.gasTotalForLayer1 || '0x0',
);
amount = subtractCurrencies(
addHexPrefix(draftTransaction.asset.balance),
addHexPrefix(_gasTotal),
{
toNumericBase: 'hex',
aBase: 16,
bBase: 16,
},
);
16,
).add(new Numeric(state.gasTotalForLayer1 || '0x0', 16));
amount = new Numeric(draftTransaction.asset.balance, 16)
.minus(_gasTotal)
.toString();
}
slice.caseReducers.updateSendAmount(state, {
payload: amount,
@ -1266,6 +1253,9 @@ const slice = createSlice({
validateAmountField: (state) => {
const draftTransaction =
state.draftTransactions[state.currentTransactionUUID];
const amountValue = new Numeric(draftTransaction.amount.value, 16);
switch (true) {
// set error to INSUFFICIENT_FUNDS_FOR_GAS_ERROR if the account balance is lower
// than the total price of the transaction inclusive of gas fees.
@ -1289,10 +1279,7 @@ const slice = createSlice({
break;
// if the amount is negative, set error to NEGATIVE_ETH_ERROR
// TODO: change this to NEGATIVE_ERROR and remove the currency bias.
case conversionGreaterThan(
{ value: 0, fromNumericBase: 'dec' },
{ value: draftTransaction.amount.value, fromNumericBase: 'hex' },
):
case amountValue.isNegative():
draftTransaction.amount.error = NEGATIVE_ETH_ERROR;
break;
// If none of the above are true, set error to null
@ -1407,28 +1394,81 @@ const slice = createSlice({
validateSendState: (state) => {
const draftTransaction =
state.draftTransactions[state.currentTransactionUUID];
slice.caseReducers.addHistoryEntry(state, {
payload: 'Begin validating send state',
});
if (draftTransaction) {
switch (true) {
case Boolean(draftTransaction.amount.error):
slice.caseReducers.addHistoryEntry(state, {
payload: `Amount is in error ${draftTransaction.amount.error}`,
});
draftTransaction.status = SEND_STATUSES.INVALID;
break;
case Boolean(draftTransaction.gas.error):
slice.caseReducers.addHistoryEntry(state, {
payload: `Gas is in error ${draftTransaction.gas.error}`,
});
draftTransaction.status = SEND_STATUSES.INVALID;
break;
case Boolean(draftTransaction.asset.error):
slice.caseReducers.addHistoryEntry(state, {
payload: `Asset is in error ${draftTransaction.asset.error}`,
});
draftTransaction.status = SEND_STATUSES.INVALID;
break;
case draftTransaction.asset.type === AssetType.token &&
draftTransaction.asset.details === null:
slice.caseReducers.addHistoryEntry(state, {
payload: `Asset is TOKEN and token details is null`,
});
draftTransaction.status = SEND_STATUSES.INVALID;
break;
case state.stage === SEND_STAGES.ADD_RECIPIENT:
slice.caseReducers.addHistoryEntry(state, {
payload: `Form is invalid because stage is ADD_RECIPIENT`,
});
draftTransaction.status = SEND_STATUSES.INVALID;
break;
case state.stage === SEND_STAGES.INACTIVE:
slice.caseReducers.addHistoryEntry(state, {
payload: `Form is invalid because stage is INACTIVE`,
});
draftTransaction.status = SEND_STATUSES.INVALID;
break;
case state.gasEstimateIsLoading:
slice.caseReducers.addHistoryEntry(state, {
payload: `Form is invalid because gasEstimateIsLoading`,
});
draftTransaction.status = SEND_STATUSES.INVALID;
break;
case new BigNumber(draftTransaction.gas.gasLimit, 16).lessThan(
new BigNumber(state.gasLimitMinimum),
):
slice.caseReducers.addHistoryEntry(state, {
payload: `Form is invalid because ${draftTransaction.gas.gasLimit} is lessThan ${state.gasLimitMinimum}`,
});
draftTransaction.status = SEND_STATUSES.INVALID;
break;
case draftTransaction.recipient.warning === 'loading':
slice.caseReducers.addHistoryEntry(state, {
payload: `Form is invalid because recipient warning is loading`,
});
draftTransaction.status = SEND_STATUSES.INVALID;
break;
case draftTransaction.recipient.warning ===
KNOWN_RECIPIENT_ADDRESS_WARNING &&
draftTransaction.recipient.recipientWarningAcknowledged === false:
slice.caseReducers.addHistoryEntry(state, {
payload: `Form is invalid because recipient warning not acknolwedged`,
});
draftTransaction.status = SEND_STATUSES.INVALID;
break;
default:
slice.caseReducers.addHistoryEntry(state, {
payload: `Form is valid`,
});
draftTransaction.status = SEND_STATUSES.VALID;
}
}
@ -1730,13 +1770,7 @@ export function editExistingTransaction(assetType, transactionId) {
const address = getTokenAddressParam(tokenData);
const nickname = getAddressBookEntryOrAccountName(state, address) ?? '';
const tokenAmountInHex = addHexPrefix(
conversionUtil(tokenAmountInDec, {
fromNumericBase: 'dec',
toNumericBase: 'hex',
}),
);
const tokenAmountInHex = addHexPrefix(decimalToHex(tokenAmountInDec));
await dispatch(
actions.addNewDraft({
...draftTransactionInitialState,
@ -1936,14 +1970,13 @@ export function updateSendAmount(amount) {
10,
Number(draftTransaction.asset.details?.decimals || 0),
);
const decimalValueString = conversionUtil(addHexPrefix(amount), {
fromNumericBase: 'hex',
toNumericBase: 'dec',
toCurrency: draftTransaction.asset.details?.symbol,
conversionRate: multiplier,
invertConversionRate: true,
});
const decimalValueString = new Numeric(addHexPrefix(amount), 16)
.toBase(10)
.applyConversionRate(
draftTransaction.asset.details?.symbol ? multiplier : 1,
true,
)
.toString();
logAmount = `${Number(decimalValueString) ? decimalValueString : ''} ${
draftTransaction.asset.details?.symbol
}`;

View File

@ -53,7 +53,6 @@ import {
} from '../../pages/swaps/swaps.util';
import {
addHexes,
conversionLessThan,
decGWEIToHexWEI,
decimalToHex,
getValueFromWeiHex,
@ -91,6 +90,8 @@ import {
calcGasTotal,
calcTokenAmount,
} from '../../../shared/lib/transactions-controller-utils';
import { EtherDenomination } from '../../../shared/constants/common';
import { Numeric } from '../../../shared/modules/Numeric';
export const GAS_PRICES_LOADING_STATES = {
INITIAL: 'INITIAL',
@ -289,15 +290,13 @@ export function shouldShowCustomPriceTooLowWarning(state) {
return false;
}
const customPriceRisksSwapFailure = conversionLessThan(
{
value: customGasPrice,
fromNumericBase: 'hex',
fromDenomination: 'WEI',
toDenomination: 'GWEI',
},
{ value: average, fromNumericBase: 'dec' },
);
const customPriceRisksSwapFailure = new Numeric(
customGasPrice,
16,
EtherDenomination.WEI,
)
.toDenomination(EtherDenomination.GWEI)
.greaterThan(average, 10);
return customPriceRisksSwapFailure;
}

View File

@ -1,63 +1,31 @@
import currencyFormatter from 'currency-formatter';
import currencies from 'currency-formatter/currencies';
import BigNumber from 'bignumber.js';
import { addHexPrefix } from '../../../app/scripts/lib/util';
import { unconfirmedTransactionsCountSelector } from '../../selectors';
import {
conversionUtil,
addCurrencies,
multiplyCurrencies,
conversionGreaterThan,
} from '../../../shared/modules/conversion.utils';
import { Numeric } from '../../../shared/modules/Numeric';
import { EtherDenomination } from '../../../shared/constants/common';
export function increaseLastGasPrice(lastGasPrice) {
return addHexPrefix(
multiplyCurrencies(lastGasPrice || '0x0', 1.1, {
multiplicandBase: 16,
multiplierBase: 10,
toNumericBase: 'hex',
}),
);
export function getHexGasTotal({ gasLimit = '0x0', gasPrice = '0x0' }) {
return new Numeric(gasLimit, 16)
.times(new Numeric(gasPrice, 16))
.toPrefixedHexString();
}
export function hexGreaterThan(a, b) {
return conversionGreaterThan(
{ value: a, fromNumericBase: 'hex' },
{ value: b, fromNumericBase: 'hex' },
);
export function addEth(firstValue, ...otherValues) {
return otherValues
.reduce((numericAcc, ethAmount) => {
return numericAcc.add(new Numeric(ethAmount, 10)).round(6);
}, new Numeric(firstValue, 10))
.toString();
}
export function getHexGasTotal({ gasLimit, gasPrice }) {
return addHexPrefix(
multiplyCurrencies(gasLimit || '0x0', gasPrice || '0x0', {
toNumericBase: 'hex',
multiplicandBase: 16,
multiplierBase: 16,
}),
);
}
export function addEth(...args) {
return args.reduce((acc, ethAmount) => {
return addCurrencies(acc, ethAmount, {
toNumericBase: 'dec',
numberOfDecimals: 6,
aBase: 10,
bBase: 10,
});
});
}
export function addFiat(...args) {
return args.reduce((acc, fiatAmount) => {
return addCurrencies(acc, fiatAmount, {
toNumericBase: 'dec',
numberOfDecimals: 2,
aBase: 10,
bBase: 10,
});
});
export function addFiat(firstValue, ...otherValues) {
return otherValues
.reduce((numericAcc, fiatAmount) => {
return numericAcc.add(new Numeric(fiatAmount, 10)).round(2);
}, new Numeric(firstValue, 10))
.toString();
}
export function getTransactionFee({
@ -67,15 +35,14 @@ export function getTransactionFee({
conversionRate,
numberOfDecimals,
}) {
return conversionUtil(value, {
fromNumericBase: 'BN',
toNumericBase: 'dec',
fromDenomination: 'WEI',
fromCurrency,
toCurrency,
numberOfDecimals,
conversionRate,
});
let fee = new Numeric(value, 16, EtherDenomination.WEI)
.toDenomination(EtherDenomination.ETH)
.toBase(10);
if (fromCurrency !== toCurrency && conversionRate) {
fee = fee.applyConversionRate(conversionRate);
}
return fee.round(numberOfDecimals).toString();
}
export function formatCurrency(value, currencyCode) {
@ -98,14 +65,13 @@ export function convertTokenToFiat({
}) {
const totalExchangeRate = conversionRate * contractExchangeRate;
return conversionUtil(value, {
fromNumericBase: 'dec',
toNumericBase: 'dec',
fromCurrency,
toCurrency,
numberOfDecimals: 2,
conversionRate: totalExchangeRate,
});
let tokenInFiat = new Numeric(value, 10);
if (fromCurrency !== toCurrency && totalExchangeRate) {
tokenInFiat = tokenInFiat.applyConversionRate(totalExchangeRate);
}
return tokenInFiat.round(2).toString();
}
export function hasUnconfirmedTransactions(state) {

View File

@ -2,36 +2,6 @@ import { GAS_LIMITS } from '../../../shared/constants/gas';
import * as utils from './confirm-tx.util';
describe('Confirm Transaction utils', () => {
describe('increaseLastGasPrice', () => {
it('should increase the gasPrice by 10%', () => {
const increasedGasPrice = utils.increaseLastGasPrice('0xa');
expect(increasedGasPrice).toStrictEqual('0xb');
});
it('should prefix the result with 0x', () => {
const increasedGasPrice = utils.increaseLastGasPrice('a');
expect(increasedGasPrice).toStrictEqual('0xb');
});
});
describe('hexGreaterThan', () => {
it('should return true if the first value is greater than the second value', () => {
expect(utils.hexGreaterThan('0xb', '0xa')).toStrictEqual(true);
});
it('should return false if the first value is less than the second value', () => {
expect(utils.hexGreaterThan('0xa', '0xb')).toStrictEqual(false);
});
it('should return false if the first value is equal to the second value', () => {
expect(utils.hexGreaterThan('0xa', '0xa')).toStrictEqual(false);
});
it('should correctly compare prefixed and non-prefixed hex values', () => {
expect(utils.hexGreaterThan('0xb', 'a')).toStrictEqual(true);
});
});
describe('getHexGasTotal', () => {
it('should multiply the hex gasLimit and hex gasPrice values together', () => {
expect(

View File

@ -1,20 +1,19 @@
import { constant, times, uniq, zip } from 'lodash';
import BigNumber from 'bignumber.js';
import { addHexPrefix } from 'ethereumjs-util';
import {
GAS_RECOMMENDATIONS,
EDIT_GAS_MODES,
} from '../../../shared/constants/gas';
import {
hexWEIToDecGWEI,
multiplyCurrencies,
} from '../../../shared/modules/conversion.utils';
import { hexWEIToDecGWEI } from '../../../shared/modules/conversion.utils';
import { Numeric } from '../../../shared/modules/Numeric';
import {
bnGreaterThan,
isNullish,
roundToDecimalPlacesRemovingExtraZeroes,
} from './util';
const TEN_PERCENT_NUMERIC = new Numeric(1.1, 10);
export const gasEstimateGreaterThanGasUsedPlusTenPercent = (
gasUsed,
gasFeeEstimates,
@ -30,29 +29,6 @@ export const gasEstimateGreaterThanGasUsedPlusTenPercent = (
return bnGreaterThan(maxFeePerGasFromEstimate, maxFeePerGasInTransaction);
};
/**
* Simple helper to save on duplication to multiply the supplied wei hex string
* by 1.10 to get bare minimum new gas fee.
*
* @param {string | undefined} hexStringValue - hex value in wei to be incremented
* @param conversionOptions
* @returns {string | undefined} hex value in WEI 10% higher than the param.
*/
export function addTenPercent(hexStringValue, conversionOptions = {}) {
if (hexStringValue === undefined) {
return undefined;
}
return addHexPrefix(
multiplyCurrencies(hexStringValue, 1.1, {
toNumericBase: 'hex',
multiplicandBase: 16,
multiplierBase: 10,
numberOfDecimals: 0,
...conversionOptions,
}),
);
}
/**
* Simple helper to save on duplication to multiply the supplied wei hex string
* by 1.10 to get bare minimum new gas fee.
@ -61,7 +37,13 @@ export function addTenPercent(hexStringValue, conversionOptions = {}) {
* @returns {string | undefined} hex value in WEI 10% higher than the param.
*/
export function addTenPercentAndRound(hexStringValue) {
return addTenPercent(hexStringValue, { numberOfDecimals: 0 });
if (hexStringValue === undefined) {
return undefined;
}
return new Numeric(hexStringValue, 16)
.times(TEN_PERCENT_NUMERIC)
.round(0)
.toPrefixedHexString();
}
export function isMetamaskSuggestedGasEstimate(estimate) {

View File

@ -1,7 +1,6 @@
import { PRIORITY_LEVELS } from '../../../shared/constants/gas';
import {
addTenPercent,
gasEstimateGreaterThanGasUsedPlusTenPercent,
formatGasFeeOrFeeRange,
} from './gas';
@ -38,17 +37,6 @@ describe('Gas utils', () => {
});
});
describe('addTenPercent', () => {
it('should add 10% to hex value passed', () => {
const result = addTenPercent('0x59682f00');
expect(result).toStrictEqual('0x62590080');
});
it('should return undefined if undefined value is passed', () => {
const result = addTenPercent(undefined);
expect(result).toBeUndefined();
});
});
describe('formatGasFeeOrFeeRange', () => {
describe('given a singular fee', () => {
it('should return a string "X GWEI" where X is the fee rounded to the given precision', () => {

View File

@ -1,14 +1,11 @@
import log from 'loglevel';
import {
conversionUtil,
multiplyCurrencies,
} from '../../../shared/modules/conversion.utils';
import { getTokenStandardAndDetails } from '../../store/actions';
import { isEqualCaseInsensitive } from '../../../shared/modules/string-utils';
import { parseStandardTokenTransactionData } from '../../../shared/modules/transaction.utils';
import { TokenStandard } from '../../../shared/constants/transaction';
import { getTokenValueParam } from '../../../shared/lib/metamask-controller-utils';
import { calcTokenAmount } from '../../../shared/lib/transactions-controller-utils';
import { Numeric } from '../../../shared/modules/Numeric';
import * as util from './util';
import { formatCurrency } from './confirm-tx.util';
@ -189,21 +186,19 @@ export function getTokenFiatAmount(
return undefined;
}
const currentTokenToFiatRate = multiplyCurrencies(
contractExchangeRate,
conversionRate,
{
multiplicandBase: 10,
multiplierBase: 10,
},
);
const currentTokenInFiat = conversionUtil(tokenAmount, {
fromNumericBase: 'dec',
fromCurrency: tokenSymbol,
toCurrency: currentCurrency.toUpperCase(),
numberOfDecimals: 2,
conversionRate: currentTokenToFiatRate,
});
const currentTokenToFiatRate = new Numeric(contractExchangeRate, 10)
.times(new Numeric(conversionRate, 10))
.toString();
let currentTokenInFiat = new Numeric(tokenAmount, 10);
if (tokenSymbol !== currentCurrency.toUpperCase() && currentTokenToFiatRate) {
currentTokenInFiat = currentTokenInFiat.applyConversionRate(
currentTokenToFiatRate,
);
}
currentTokenInFiat = currentTokenInFiat.round(2).toString();
let result;
if (hideCurrencySymbol) {
result = formatCurrency(currentTokenInFiat, currentCurrency);

View File

@ -15,7 +15,7 @@ import {
TRUNCATED_NAME_CHAR_LIMIT,
TRUNCATED_ADDRESS_END_CHARS,
} from '../../../shared/constants/labels';
import { toBigNumber } from '../../../shared/modules/conversion.utils';
import { Numeric } from '../../../shared/modules/Numeric';
// formatData :: ( date: <Unix Timestamp> ) -> String
export function formatDate(date, format = "M/d/y 'at' T") {
@ -485,9 +485,10 @@ export function roundToDecimalPlacesRemovingExtraZeroes(
if (numberish === undefined || numberish === null) {
return '';
}
return toBigNumber
.dec(toBigNumber.dec(numberish).toFixed(numberOfDecimalPlaces))
.toNumber();
return new Numeric(
new Numeric(numberish, 10).toFixed(numberOfDecimalPlaces),
10,
).toNumber();
}
/**

View File

@ -1,10 +1,6 @@
import { useSelector } from 'react-redux';
import { GAS_ESTIMATE_TYPES } from '../../../shared/constants/gas';
import {
conversionUtil,
multiplyCurrencies,
} from '../../../shared/modules/conversion.utils';
import {
getConversionRate,
getNativeCurrency,
@ -26,6 +22,8 @@ import {
getCustomMaxFeePerGas,
getCustomMaxPriorityFeePerGas,
} from '../../ducks/swaps/swaps';
import { Numeric } from '../../../shared/modules/Numeric';
import { EtherDenomination } from '../../../shared/constants/common';
// Why this number?
// 20 gwei * 21000 gasLimit = 420,000 gwei
@ -148,30 +146,15 @@ export const generateUseSelectorRouter =
return undefined;
};
export function getTotalCostInETH(gwei, gasLimit) {
return multiplyCurrencies(gwei, gasLimit, {
fromDenomination: 'GWEI',
toDenomination: 'ETH',
multiplicandBase: 10,
multiplierBase: 10,
});
}
export function convertFromHexToFiat(value) {
const val = conversionUtil(value, {
fromNumericBase: 'hex',
toNumericBase: 'dec',
fromDenomination: 'WEI',
});
const val = new Numeric(value, 16).toBase(10).toString();
return `$${(val * MOCK_ETH_USD_CONVERSION_RATE).toFixed(2)}`;
}
export function convertFromHexToETH(value) {
const val = conversionUtil(value, {
fromNumericBase: 'hex',
toNumericBase: 'dec',
fromDenomination: 'WEI',
});
const val = new Numeric(value, 16, EtherDenomination.WEI)
.toBase(10)
.toDenomination(EtherDenomination.ETH);
return `${val} ETH`;
}

View File

@ -1,11 +1,6 @@
import { useMemo } from 'react';
import { shallowEqual, useSelector } from 'react-redux';
import { GAS_ESTIMATE_TYPES, GAS_LIMITS } from '../../../shared/constants/gas';
import {
conversionLessThan,
conversionGreaterThan,
addHexes,
} from '../../../shared/modules/conversion.utils';
import {
checkNetworkAndAccountSupports1559,
getSelectedAccount,
@ -13,13 +8,13 @@ import {
import { isLegacyTransaction } from '../../helpers/utils/transactions.util';
import { bnGreaterThan, bnLessThan } from '../../helpers/utils/util';
import { GAS_FORM_ERRORS } from '../../helpers/constants/gas';
import { Numeric } from '../../../shared/modules/Numeric';
const HIGH_FEE_WARNING_MULTIPLIER = 1.5;
const validateGasLimit = (gasLimit, minimumGasLimit) => {
const gasLimitTooLow = conversionLessThan(
{ value: gasLimit, fromNumericBase: 'dec' },
{ value: minimumGasLimit || GAS_LIMITS.SIMPLE, fromNumericBase: 'hex' },
const gasLimitTooLow = new Numeric(gasLimit, 10).lessThan(
new Numeric(minimumGasLimit || GAS_LIMITS.SIMPLE, 16),
);
if (gasLimitTooLow) {
@ -139,15 +134,12 @@ const hasBalanceError = (minimumCostInHexWei, transaction, ethBalance) => {
if (minimumCostInHexWei === undefined || ethBalance === undefined) {
return false;
}
const minimumTxCostInHexWei = addHexes(
minimumCostInHexWei,
transaction?.txParams?.value || '0x0',
const minimumTxCostInHexWei = new Numeric(minimumCostInHexWei, 16).add(
new Numeric(transaction?.txParams?.value || '0x0', 16),
);
const ethBalanceInHexWei = new Numeric(ethBalance, 16);
return conversionGreaterThan(
{ value: minimumTxCostInHexWei, fromNumericBase: 'hex' },
{ value: ethBalance, fromNumericBase: 'hex' },
);
return minimumTxCostInHexWei.greaterThan(ethBalanceInHexWei);
};
/**

View File

@ -2,10 +2,6 @@ import { useCallback } from 'react';
import { useDispatch } from 'react-redux';
import { EDIT_GAS_MODES, PRIORITY_LEVELS } from '../../../shared/constants/gas';
import {
decGWEIToHexWEI,
decimalToHex,
} from '../../../shared/modules/conversion.utils';
import {
addTenPercentAndRound,
editGasModeIsSpeedUpOrCancel,
@ -18,6 +14,10 @@ import {
updateSwapsUserFeeLevel,
updateTransactionGasFees,
} from '../../store/actions';
import {
decGWEIToHexWEI,
decimalToHex,
} from '../../../shared/modules/conversion.utils';
/**
* @typedef {object} TransactionFunctionsReturnType

View File

@ -7,11 +7,10 @@ import {
getNativeCurrency,
} from '../ducks/metamask/metamask';
import {
conversionUtil,
getValueFromWeiHex,
} from '../../shared/modules/conversion.utils';
import { getValueFromWeiHex } from '../../shared/modules/conversion.utils';
import { TEST_NETWORK_TICKER_MAP } from '../../shared/constants/network';
import { Numeric } from '../../shared/modules/Numeric';
import { EtherDenomination } from '../../shared/constants/common';
/**
* Defines the shape of the options parameter for useCurrencyDisplay
@ -61,13 +60,11 @@ export function useCurrencyDisplay(
currency === nativeCurrency ||
(!isUserPreferredCurrency && !nativeCurrency)
) {
return conversionUtil(inputValue, {
fromNumericBase: 'hex',
toNumericBase: 'dec',
fromDenomination: 'WEI',
numberOfDecimals: numberOfDecimals || 2,
toDenomination: denomination,
});
return new Numeric(inputValue, 16, EtherDenomination.WEI)
.toDenomination(denomination || EtherDenomination.ETH)
.round(numberOfDecimals || 2)
.toBase(10)
.toString();
} else if (isUserPreferredCurrency && conversionRate) {
return formatCurrency(
getValueFromWeiHex({

View File

@ -2,7 +2,7 @@ import BigNumber from 'bignumber.js';
import { useMemo } from 'react';
import { decGWEIToHexWEI } from '../../shared/modules/conversion.utils';
import { isEIP1559Transaction } from '../../shared/modules/transaction.utils';
import { addTenPercent } from '../helpers/utils/gas';
import { addTenPercentAndRound } from '../helpers/utils/gas';
import { useGasFeeEstimates } from './useGasFeeEstimates';
/**
@ -15,7 +15,7 @@ import { useGasFeeEstimates } from './useGasFeeEstimates';
* @returns {string} hexWei value of the higher of the two inputs.
*/
function getHighestIncrementedFee(originalFee, currentEstimate) {
const buffedOriginalHexWei = addTenPercent(originalFee);
const buffedOriginalHexWei = addTenPercentAndRound(originalFee);
const currentEstimateHexWei = decGWEIToHexWEI(currentEstimate);
return new BigNumber(buffedOriginalHexWei, 16).greaterThan(

View File

@ -11,7 +11,8 @@ import { PageContainerFooter } from '../../components/ui/page-container';
import { EVENT } from '../../../shared/constants/metametrics';
import { SECOND } from '../../../shared/constants/time';
import { conversionUtil } from '../../../shared/modules/conversion.utils';
import { Numeric } from '../../../shared/modules/Numeric';
import { EtherDenomination } from '../../../shared/constants/common';
export default class ConfirmDecryptMessage extends Component {
static contextTypes = {
@ -98,13 +99,14 @@ export default class ConfirmDecryptMessage extends Component {
} = this.state;
const { t } = this.context;
const nativeCurrencyBalance = conversionUtil(balance, {
fromNumericBase: 'hex',
toNumericBase: 'dec',
fromDenomination: 'WEI',
numberOfDecimals: 6,
conversionRate,
});
const nativeCurrencyBalance = new Numeric(
balance,
16,
EtherDenomination.WEI,
)
.applyConversionRate(conversionRate)
.round(6)
.toBase(10);
return (
<div className="request-decrypt-message__balance">

View File

@ -6,8 +6,9 @@ import Identicon from '../../components/ui/identicon';
import { PageContainerFooter } from '../../components/ui/page-container';
import { EVENT } from '../../../shared/constants/metametrics';
import { conversionUtil } from '../../../shared/modules/conversion.utils';
import SiteOrigin from '../../components/ui/site-origin';
import { Numeric } from '../../../shared/modules/Numeric';
import { EtherDenomination } from '../../../shared/constants/common';
export default class ConfirmEncryptionPublicKey extends Component {
static contextTypes = {
@ -74,13 +75,14 @@ export default class ConfirmEncryptionPublicKey extends Component {
} = this.props;
const { t } = this.context;
const nativeCurrencyBalance = conversionUtil(balance, {
fromNumericBase: 'hex',
toNumericBase: 'dec',
fromDenomination: 'WEI',
numberOfDecimals: 6,
conversionRate,
});
const nativeCurrencyBalance = new Numeric(
balance,
16,
EtherDenomination.WEI,
)
.applyConversionRate(conversionRate)
.round(6)
.toBase(10);
return (
<div className="request-encryption-public-key__balance">

View File

@ -67,12 +67,12 @@ import {
CHAIN_ID_TO_NETWORK_ID_MAP,
///: END:ONLY_INCLUDE_IN
} from '../../../shared/constants/network';
import TransactionAlerts from '../../components/app/transaction-alerts';
import {
addHexes,
hexToDecimal,
hexWEIToDecGWEI,
} from '../../../shared/modules/conversion.utils';
import TransactionAlerts from '../../components/app/transaction-alerts';
const renderHeartBeatIfNotInTest = () =>
process.env.IN_TEST ? null : <LoadingHeartBeat />;

View File

@ -1,31 +1,25 @@
import {
conversionUtil,
multiplyCurrencies,
} from '../../../shared/modules/conversion.utils';
import { addHexPrefix } from '../../../app/scripts/lib/util';
import { MIN_GAS_LIMIT_HEX } from '../../../shared/constants/gas';
import { Numeric } from '../../../shared/modules/Numeric';
import { EtherDenomination } from '../../../shared/constants/common';
const MIN_GAS_PRICE_DEC = '0';
const MIN_GAS_PRICE_HEX = parseInt(MIN_GAS_PRICE_DEC, 10).toString(16);
const MIN_GAS_LIMIT_DEC = '21000';
const MIN_GAS_LIMIT_DEC = new Numeric('21000', 10);
const MAX_GAS_LIMIT_DEC = '7920027';
const HIGH_FEE_WARNING_MULTIPLIER = 1.5;
const MIN_GAS_PRICE_GWEI = addHexPrefix(
conversionUtil(MIN_GAS_PRICE_HEX, {
fromDenomination: 'WEI',
toDenomination: 'GWEI',
fromNumericBase: 'hex',
toNumericBase: 'hex',
numberOfDecimals: 1,
}),
);
const MIN_GAS_PRICE_GWEI = new Numeric(
MIN_GAS_PRICE_HEX,
16,
EtherDenomination.WEI,
)
.toDenomination(EtherDenomination.GWEI)
.round(1)
.toPrefixedHexString();
const MIN_GAS_TOTAL = multiplyCurrencies(MIN_GAS_LIMIT_HEX, MIN_GAS_PRICE_HEX, {
toNumericBase: 'hex',
multiplicandBase: 16,
multiplierBase: 16,
});
const MIN_GAS_TOTAL = new Numeric(MIN_GAS_LIMIT_HEX, 16)
.times(new Numeric(MIN_GAS_PRICE_HEX, 16, EtherDenomination.WEI))
.toPrefixedHexString();
const TOKEN_TRANSFER_FUNCTION_SIGNATURE = '0xa9059cbb';
const COLLECTIBLE_TRANSFER_FROM_FUNCTION_SIGNATURE = '0x23b872dd';

View File

@ -1,16 +1,8 @@
import abi from 'ethereumjs-abi';
import {
addCurrencies,
conversionUtil,
conversionGTE,
multiplyCurrencies,
conversionGreaterThan,
conversionLessThan,
} from '../../../shared/modules/conversion.utils';
import { addHexPrefix } from '../../../app/scripts/lib/util';
import { TokenStandard } from '../../../shared/constants/transaction';
import { calcTokenAmount } from '../../../shared/lib/transactions-controller-utils';
import { Numeric } from '../../../shared/modules/Numeric';
import {
TOKEN_TRANSFER_FUNCTION_SIGNATURE,
COLLECTIBLE_TRANSFER_FROM_FUNCTION_SIGNATURE,
@ -33,46 +25,22 @@ function isBalanceSufficient({
gasTotal = '0x0',
primaryCurrency,
}) {
const totalAmount = addCurrencies(amount, gasTotal, {
aBase: 16,
bBase: 16,
toNumericBase: 'hex',
});
let totalAmount = new Numeric(amount, 16).add(new Numeric(gasTotal, 16));
let balanceNumeric = new Numeric(balance, 16);
const balanceIsSufficient = conversionGTE(
{
value: balance,
fromNumericBase: 'hex',
fromCurrency: primaryCurrency,
conversionRate,
},
{
value: totalAmount,
fromNumericBase: 'hex',
conversionRate,
fromCurrency: primaryCurrency,
},
);
if (typeof primaryCurrency !== 'undefined' && primaryCurrency !== null) {
totalAmount = totalAmount.applyConversionRate(conversionRate);
balanceNumeric = balanceNumeric.applyConversionRate(conversionRate);
}
return balanceIsSufficient;
return balanceNumeric.greaterThanOrEqualTo(totalAmount);
}
function isTokenBalanceSufficient({ amount = '0x0', tokenBalance, decimals }) {
const amountInDec = conversionUtil(amount, {
fromNumericBase: 'hex',
});
const amountNumeric = new Numeric(amount, 16).shiftedBy(decimals);
const tokenBalanceNumeric = new Numeric(tokenBalance, 16);
const tokenBalanceIsSufficient = conversionGTE(
{
value: tokenBalance,
fromNumericBase: 'hex',
},
{
value: calcTokenAmount(amountInDec, decimals),
},
);
return tokenBalanceIsSufficient;
return tokenBalanceNumeric.greaterThanOrEqualTo(amountNumeric);
}
function addGasBuffer(
@ -80,43 +48,25 @@ function addGasBuffer(
blockGasLimitHex,
bufferMultiplier = 1.5,
) {
const upperGasLimit = multiplyCurrencies(blockGasLimitHex, 0.9, {
toNumericBase: 'hex',
multiplicandBase: 16,
multiplierBase: 10,
numberOfDecimals: '0',
});
const bufferedGasLimit = multiplyCurrencies(
initialGasLimitHex,
bufferMultiplier,
{
toNumericBase: 'hex',
multiplicandBase: 16,
multiplierBase: 10,
numberOfDecimals: '0',
},
);
const initialGasLimit = new Numeric(initialGasLimitHex, 16);
const upperGasLimit = new Numeric(blockGasLimitHex, 16)
.times(new Numeric(0.9, 10))
.round(0);
const bufferedGasLimit = initialGasLimit
.times(new Numeric(bufferMultiplier, 10))
.round(0);
// if initialGasLimit is above blockGasLimit, dont modify it
if (
conversionGreaterThan(
{ value: initialGasLimitHex, fromNumericBase: 'hex' },
{ value: upperGasLimit, fromNumericBase: 'hex' },
)
) {
if (initialGasLimit.greaterThanOrEqualTo(upperGasLimit)) {
return initialGasLimitHex;
}
// if bufferedGasLimit is below blockGasLimit, use bufferedGasLimit
if (
conversionLessThan(
{ value: bufferedGasLimit, fromNumericBase: 'hex' },
{ value: upperGasLimit, fromNumericBase: 'hex' },
)
) {
return bufferedGasLimit;
if (bufferedGasLimit.lessThan(upperGasLimit)) {
return bufferedGasLimit.toString();
}
// otherwise use blockGasLimit
return upperGasLimit;
return upperGasLimit.toString();
}
function generateERC20TransferData({

View File

@ -1,11 +1,5 @@
import { rawEncode } from 'ethereumjs-abi';
import {
addCurrencies,
conversionGTE,
conversionUtil,
} from '../../../shared/modules/conversion.utils';
import {
generateERC20TransferData,
isBalanceSufficient,
@ -13,35 +7,6 @@ import {
ellipsify,
} from './send.utils';
jest.mock('../../../shared/modules/conversion.utils', () => ({
addCurrencies: jest.fn((a, b) => {
let [a1, b1] = [a, b];
if (String(a).match(/^0x.+/u)) {
a1 = Number(String(a).slice(2));
}
if (String(b).match(/^0x.+/u)) {
b1 = Number(String(b).slice(2));
}
return a1 + b1;
}),
conversionUtil: jest.fn((val) => parseInt(val, 16)),
conversionGTE: jest.fn((obj1, obj2) => obj1.value >= obj2.value),
multiplyCurrencies: jest.fn((a, b) => `${a}x${b}`),
conversionGreaterThan: (obj1, obj2) => obj1.value > obj2.value,
conversionLessThan: (obj1, obj2) => obj1.value < obj2.value,
}));
jest.mock('../../../shared/lib/transactions-controller-utils', () => {
const originalModule = jest.requireActual(
'../../../shared/lib/transactions-controller-utils',
);
return {
...originalModule,
calcTokenAmount: (a, d) => `calc:${a}${d}`,
};
});
jest.mock('ethereumjs-abi', () => ({
rawEncode: jest.fn().mockReturnValue(16, 1100),
}));
@ -84,7 +49,7 @@ describe('send utils', () => {
});
describe('isBalanceSufficient()', () => {
it('should correctly call addCurrencies and return the result of calling conversionGTE', () => {
it('should correctly sum the appropriate currencies and ensure that balance is greater', () => {
const result = isBalanceSufficient({
amount: 15,
balance: 100,
@ -92,53 +57,29 @@ describe('send utils', () => {
gasTotal: 17,
primaryCurrency: 'ABC',
});
expect(addCurrencies).toHaveBeenCalledWith(15, 17, {
aBase: 16,
bBase: 16,
toNumericBase: 'hex',
});
expect(conversionGTE).toHaveBeenCalledWith(
{
value: 100,
fromNumericBase: 'hex',
fromCurrency: 'ABC',
conversionRate: 3,
},
{
value: 32,
fromNumericBase: 'hex',
conversionRate: 3,
fromCurrency: 'ABC',
},
);
expect(result).toStrictEqual(true);
});
});
describe('isTokenBalanceSufficient()', () => {
it('should correctly call conversionUtil and return the result of calling conversionGTE', () => {
it('should return true for a sufficient balance for token spend', () => {
const result = isTokenBalanceSufficient({
amount: '0x10',
tokenBalance: 123,
decimals: 10,
});
expect(conversionUtil).toHaveBeenCalledWith('0x10', {
fromNumericBase: 'hex',
expect(result).toStrictEqual(true);
});
it('should return false for an insufficient balance for token spend', () => {
const result = isTokenBalanceSufficient({
amount: '0x10000',
tokenBalance: 123,
decimals: 10,
});
expect(conversionGTE).toHaveBeenCalledWith(
{
value: 123,
fromNumericBase: 'hex',
},
{
value: 'calc:1610',
},
);
expect(result).toStrictEqual(false);
expect(result).toStrictEqual(true);
});
});

View File

@ -1,9 +1,5 @@
import { addHexPrefix } from '../../app/scripts/lib/util';
import {
conversionUtil,
conversionGreaterThan,
decEthToConvertedCurrency,
} from '../../shared/modules/conversion.utils';
import { decEthToConvertedCurrency } from '../../shared/modules/conversion.utils';
import { formatCurrency } from '../helpers/utils/confirm-tx.util';
import { formatETHFee } from '../helpers/utils/formatters';
@ -15,6 +11,8 @@ import {
isEIP1559Network,
} from '../ducks/metamask/metamask';
import { calcGasTotal } from '../../shared/lib/transactions-controller-utils';
import { Numeric } from '../../shared/modules/Numeric';
import { EtherDenomination } from '../../shared/constants/common';
import { getIsMainnet } from '.';
export function getCustomGasLimit(state) {
@ -91,15 +89,9 @@ export function isCustomPriceSafe(state) {
return false;
}
const customPriceSafe = conversionGreaterThan(
{
value: customGasPrice,
fromNumericBase: 'hex',
fromDenomination: 'WEI',
toDenomination: 'GWEI',
},
{ value: safeLow, fromNumericBase: 'dec' },
);
const customPriceSafe = new Numeric(customGasPrice, 16, EtherDenomination.WEI)
.toDenomination(EtherDenomination.GWEI)
.greaterThan(safeLow, 10);
return customPriceSafe;
}
@ -117,15 +109,9 @@ export function isCustomPriceSafeForCustomNetwork(state) {
return false;
}
const customPriceSafe = conversionGreaterThan(
{
value: customGasPrice,
fromNumericBase: 'hex',
fromDenomination: 'WEI',
toDenomination: 'GWEI',
},
{ value: estimatedPrice, fromNumericBase: 'dec' },
);
const customPriceSafe = new Numeric(customGasPrice, 16, EtherDenomination.WEI)
.toDenomination(EtherDenomination.GWEI)
.greaterThan(estimatedPrice, 10);
return customPriceSafe;
}
@ -139,18 +125,13 @@ export function isCustomPriceExcessive(state, checkSend = false) {
}
// Custom gas should be considered excessive when it is 1.5 times greater than the fastest estimate.
const customPriceExcessive = conversionGreaterThan(
{
value: customPrice,
fromNumericBase: 'hex',
fromDenomination: 'WEI',
toDenomination: 'GWEI',
},
{
fromNumericBase: 'dec',
value: Math.floor(fastPrice * 1.5),
},
);
const customPriceExcessive = new Numeric(
customPrice,
16,
EtherDenomination.WEI,
)
.toDenomination(EtherDenomination.GWEI)
.greaterThan(Math.floor(fastPrice * 1.5), 10);
return customPriceExcessive;
}
@ -160,12 +141,14 @@ export function basicPriceEstimateToETHTotal(
gasLimit,
numberOfDecimals = 9,
) {
return conversionUtil(calcGasTotal(gasLimit, estimate), {
fromNumericBase: 'hex',
toNumericBase: 'dec',
fromDenomination: 'GWEI',
numberOfDecimals,
});
return new Numeric(
calcGasTotal(gasLimit, estimate),
16,
EtherDenomination.GWEI,
)
.round(numberOfDecimals)
.toBase(10)
.toString();
}
export function getRenderableEthFee(
@ -174,10 +157,7 @@ export function getRenderableEthFee(
numberOfDecimals = 9,
nativeCurrency = 'ETH',
) {
const value = conversionUtil(estimate, {
fromNumericBase: 'dec',
toNumericBase: 'hex',
});
const value = new Numeric(estimate, 10).toBase(16).toString();
const fee = basicPriceEstimateToETHTotal(value, gasLimit, numberOfDecimals);
return formatETHFee(fee, nativeCurrency);
}
@ -188,10 +168,7 @@ export function getRenderableConvertedCurrencyFee(
convertedCurrency,
conversionRate,
) {
const value = conversionUtil(estimate, {
fromNumericBase: 'dec',
toNumericBase: 'hex',
});
const value = new Numeric(estimate, 10).toBase(16).toString();
const fee = basicPriceEstimateToETHTotal(value, gasLimit);
const feeInCurrency = decEthToConvertedCurrency(
fee,
@ -202,20 +179,14 @@ export function getRenderableConvertedCurrencyFee(
}
export function priceEstimateToWei(priceEstimate) {
return conversionUtil(priceEstimate, {
fromNumericBase: 'hex',
toNumericBase: 'hex',
fromDenomination: 'GWEI',
toDenomination: 'WEI',
numberOfDecimals: 9,
});
return new Numeric(priceEstimate, 16, EtherDenomination.GWEI)
.toDenomination(EtherDenomination.WEI)
.round(9)
.toString();
}
export function getGasPriceInHexWei(price) {
const value = conversionUtil(price, {
fromNumericBase: 'dec',
toNumericBase: 'hex',
});
const value = new Numeric(price, 10).toBase(16).toString();
return addHexPrefix(priceEstimateToWei(value));
}