You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
335 lines
10 KiB
335 lines
10 KiB
import { act, renderHook } from '@testing-library/react-hooks';
|
|
import { useSelector } from 'react-redux';
|
|
import { GAS_ESTIMATE_TYPES } from '../../shared/constants/gas';
|
|
import { multiplyCurrencies } from '../../shared/modules/conversion.utils';
|
|
import {
|
|
getConversionRate,
|
|
getNativeCurrency,
|
|
} from '../ducks/metamask/metamask';
|
|
import {
|
|
checkNetworkAndAccountSupports1559,
|
|
getCurrentCurrency,
|
|
getShouldShowFiat,
|
|
txDataSelector,
|
|
getSelectedAccount,
|
|
} from '../selectors';
|
|
|
|
import { ETH, PRIMARY } from '../helpers/constants/common';
|
|
|
|
import { useGasFeeEstimates } from './useGasFeeEstimates';
|
|
import { useGasFeeInputs } from './useGasFeeInputs';
|
|
import { useUserPreferencedCurrency } from './useUserPreferencedCurrency';
|
|
|
|
jest.mock('./useUserPreferencedCurrency', () => ({
|
|
useUserPreferencedCurrency: jest.fn(),
|
|
}));
|
|
|
|
jest.mock('./useGasFeeEstimates', () => ({
|
|
useGasFeeEstimates: jest.fn(),
|
|
}));
|
|
|
|
jest.mock('react-redux', () => {
|
|
const actual = jest.requireActual('react-redux');
|
|
|
|
return {
|
|
...actual,
|
|
useSelector: jest.fn(),
|
|
};
|
|
});
|
|
|
|
// Why this number?
|
|
// 20 gwei * 21000 gasLimit = 420,000 gwei
|
|
// 420,000 gwei is 0.00042 ETH
|
|
// 0.00042 ETH * 100000 = $42
|
|
const MOCK_ETH_USD_CONVERSION_RATE = 100000;
|
|
|
|
const LEGACY_GAS_ESTIMATE_RETURN_VALUE = {
|
|
gasEstimateType: GAS_ESTIMATE_TYPES.LEGACY,
|
|
gasFeeEstimates: {
|
|
low: '10',
|
|
medium: '20',
|
|
high: '30',
|
|
},
|
|
estimatedGasFeeTimeBounds: {},
|
|
};
|
|
|
|
const FEE_MARKET_ESTIMATE_RETURN_VALUE = {
|
|
gasEstimateType: GAS_ESTIMATE_TYPES.FEE_MARKET,
|
|
gasFeeEstimates: {
|
|
low: {
|
|
minWaitTimeEstimate: 180000,
|
|
maxWaitTimeEstimate: 300000,
|
|
suggestedMaxPriorityFeePerGas: '3',
|
|
suggestedMaxFeePerGas: '53',
|
|
},
|
|
medium: {
|
|
minWaitTimeEstimate: 15000,
|
|
maxWaitTimeEstimate: 60000,
|
|
suggestedMaxPriorityFeePerGas: '7',
|
|
suggestedMaxFeePerGas: '70',
|
|
},
|
|
high: {
|
|
minWaitTimeEstimate: 0,
|
|
maxWaitTimeEstimate: 15000,
|
|
suggestedMaxPriorityFeePerGas: '10',
|
|
suggestedMaxFeePerGas: '100',
|
|
},
|
|
estimatedBaseFee: '50',
|
|
},
|
|
estimatedGasFeeTimeBounds: {},
|
|
};
|
|
|
|
const HIGH_FEE_MARKET_ESTIMATE_RETURN_VALUE = {
|
|
gasEstimateType: GAS_ESTIMATE_TYPES.FEE_MARKET,
|
|
gasFeeEstimates: {
|
|
low: {
|
|
minWaitTimeEstimate: 180000,
|
|
maxWaitTimeEstimate: 300000,
|
|
suggestedMaxPriorityFeePerGas: '3',
|
|
suggestedMaxFeePerGas: '53000',
|
|
},
|
|
medium: {
|
|
minWaitTimeEstimate: 15000,
|
|
maxWaitTimeEstimate: 60000,
|
|
suggestedMaxPriorityFeePerGas: '7',
|
|
suggestedMaxFeePerGas: '70000',
|
|
},
|
|
high: {
|
|
minWaitTimeEstimate: 0,
|
|
maxWaitTimeEstimate: 15000,
|
|
suggestedMaxPriorityFeePerGas: '10',
|
|
suggestedMaxFeePerGas: '100000',
|
|
},
|
|
estimatedBaseFee: '50000',
|
|
},
|
|
estimatedGasFeeTimeBounds: {},
|
|
};
|
|
|
|
const generateUseSelectorRouter = ({
|
|
checkNetworkAndAccountSupports1559Response,
|
|
} = {}) => (selector) => {
|
|
if (selector === getConversionRate) {
|
|
return MOCK_ETH_USD_CONVERSION_RATE;
|
|
}
|
|
if (selector === getNativeCurrency) {
|
|
return ETH;
|
|
}
|
|
if (selector === getCurrentCurrency) {
|
|
return 'USD';
|
|
}
|
|
if (selector === getShouldShowFiat) {
|
|
return true;
|
|
}
|
|
if (selector === txDataSelector) {
|
|
return {
|
|
txParams: {
|
|
value: '0x5555',
|
|
},
|
|
};
|
|
}
|
|
if (selector === getSelectedAccount) {
|
|
return {
|
|
balance: '0x440aa47cc2556',
|
|
};
|
|
}
|
|
if (selector === checkNetworkAndAccountSupports1559) {
|
|
return checkNetworkAndAccountSupports1559Response;
|
|
}
|
|
return undefined;
|
|
};
|
|
|
|
function getTotalCostInETH(gwei, gasLimit) {
|
|
return multiplyCurrencies(gwei, gasLimit, {
|
|
fromDenomination: 'GWEI',
|
|
toDenomination: 'ETH',
|
|
multiplicandBase: 10,
|
|
multiplierBase: 10,
|
|
});
|
|
}
|
|
|
|
describe('useGasFeeInputs', () => {
|
|
beforeEach(() => {
|
|
jest.clearAllMocks();
|
|
useUserPreferencedCurrency.mockImplementation((type) => {
|
|
if (type === PRIMARY) {
|
|
return { currency: ETH, numberOfDecimals: 6 };
|
|
}
|
|
return { currency: 'USD', numberOfDecimals: 2 };
|
|
});
|
|
});
|
|
|
|
describe('when using gasPrice API for estimation', () => {
|
|
beforeEach(() => {
|
|
useGasFeeEstimates.mockImplementation(
|
|
() => LEGACY_GAS_ESTIMATE_RETURN_VALUE,
|
|
);
|
|
useSelector.mockImplementation(generateUseSelectorRouter());
|
|
});
|
|
it('passes through the raw estimate values from useGasFeeEstimates', () => {
|
|
const { result } = renderHook(() => useGasFeeInputs());
|
|
expect(result.current.gasFeeEstimates).toMatchObject(
|
|
LEGACY_GAS_ESTIMATE_RETURN_VALUE.gasFeeEstimates,
|
|
);
|
|
expect(result.current.gasEstimateType).toBe(
|
|
LEGACY_GAS_ESTIMATE_RETURN_VALUE.gasEstimateType,
|
|
);
|
|
expect(result.current.estimatedGasFeeTimeBounds).toMatchObject({});
|
|
});
|
|
|
|
it('returns gasPrice appropriately, and "0" for EIP1559 fields', () => {
|
|
const { result } = renderHook(() => useGasFeeInputs());
|
|
expect(result.current.gasPrice).toBe(
|
|
LEGACY_GAS_ESTIMATE_RETURN_VALUE.gasFeeEstimates.medium,
|
|
);
|
|
expect(result.current.maxFeePerGas).toBe('0');
|
|
expect(result.current.maxPriorityFeePerGas).toBe('0');
|
|
});
|
|
|
|
it('updates values when user modifies gasPrice', () => {
|
|
useSelector.mockImplementation(
|
|
generateUseSelectorRouter({
|
|
checkNetworkAndAccountSupports1559Response: false,
|
|
}),
|
|
);
|
|
const { result } = renderHook(() => useGasFeeInputs());
|
|
expect(result.current.gasPrice).toBe(
|
|
LEGACY_GAS_ESTIMATE_RETURN_VALUE.gasFeeEstimates.medium,
|
|
);
|
|
let totalEthGasFee = getTotalCostInETH(
|
|
LEGACY_GAS_ESTIMATE_RETURN_VALUE.gasFeeEstimates.medium,
|
|
result.current.gasLimit,
|
|
);
|
|
let totalFiat = (
|
|
Number(totalEthGasFee) * MOCK_ETH_USD_CONVERSION_RATE
|
|
).toFixed(2);
|
|
expect(result.current.estimatedMaximumNative).toBe(
|
|
`${totalEthGasFee} ETH`,
|
|
);
|
|
expect(result.current.estimatedMaximumFiat).toBe(`$${totalFiat}`);
|
|
expect(result.current.estimatedMinimumFiat).toBe(`$${totalFiat}`);
|
|
act(() => {
|
|
result.current.setGasPrice('30');
|
|
});
|
|
totalEthGasFee = getTotalCostInETH('30', result.current.gasLimit);
|
|
totalFiat = (
|
|
Number(totalEthGasFee) * MOCK_ETH_USD_CONVERSION_RATE
|
|
).toFixed(2);
|
|
expect(result.current.gasPrice).toBe('30');
|
|
expect(result.current.estimatedMaximumNative).toBe(
|
|
`${totalEthGasFee} ETH`,
|
|
);
|
|
expect(result.current.estimatedMaximumFiat).toBe(`$${totalFiat}`);
|
|
expect(result.current.estimatedMinimumFiat).toBe(`$${totalFiat}`);
|
|
});
|
|
});
|
|
|
|
describe('when using EIP 1559 API for estimation', () => {
|
|
beforeEach(() => {
|
|
useGasFeeEstimates.mockImplementation(
|
|
() => FEE_MARKET_ESTIMATE_RETURN_VALUE,
|
|
);
|
|
useSelector.mockImplementation(generateUseSelectorRouter());
|
|
});
|
|
it('passes through the raw estimate values from useGasFeeEstimates', () => {
|
|
const { result } = renderHook(() => useGasFeeInputs());
|
|
expect(result.current.gasFeeEstimates).toMatchObject(
|
|
FEE_MARKET_ESTIMATE_RETURN_VALUE.gasFeeEstimates,
|
|
);
|
|
expect(result.current.gasEstimateType).toBe(
|
|
FEE_MARKET_ESTIMATE_RETURN_VALUE.gasEstimateType,
|
|
);
|
|
expect(result.current.estimatedGasFeeTimeBounds).toMatchObject({});
|
|
});
|
|
|
|
it('returns EIP-1559 fields appropriately, and "0" for gasPrice fields', () => {
|
|
const { result } = renderHook(() => useGasFeeInputs());
|
|
expect(result.current.gasPrice).toBe('0');
|
|
expect(result.current.maxFeePerGas).toBe(
|
|
FEE_MARKET_ESTIMATE_RETURN_VALUE.gasFeeEstimates.medium
|
|
.suggestedMaxFeePerGas,
|
|
);
|
|
expect(result.current.maxPriorityFeePerGas).toBe(
|
|
FEE_MARKET_ESTIMATE_RETURN_VALUE.gasFeeEstimates.medium
|
|
.suggestedMaxPriorityFeePerGas,
|
|
);
|
|
});
|
|
|
|
it('updates values when user modifies maxFeePerGas', () => {
|
|
useSelector.mockImplementation(
|
|
generateUseSelectorRouter({
|
|
checkNetworkAndAccountSupports1559Response: true,
|
|
}),
|
|
);
|
|
const { result } = renderHook(() => useGasFeeInputs());
|
|
expect(result.current.maxFeePerGas).toBe(
|
|
FEE_MARKET_ESTIMATE_RETURN_VALUE.gasFeeEstimates.medium
|
|
.suggestedMaxFeePerGas,
|
|
);
|
|
let totalEthGasFee = getTotalCostInETH(
|
|
FEE_MARKET_ESTIMATE_RETURN_VALUE.gasFeeEstimates.medium
|
|
.suggestedMaxFeePerGas,
|
|
result.current.gasLimit,
|
|
);
|
|
let totalMaxFiat = (
|
|
Number(totalEthGasFee) * MOCK_ETH_USD_CONVERSION_RATE
|
|
).toFixed(2);
|
|
expect(result.current.estimatedMaximumNative).toBe(
|
|
`${totalEthGasFee} ETH`,
|
|
);
|
|
expect(result.current.estimatedMaximumFiat).toBe(`$${totalMaxFiat}`);
|
|
// TODO: test minimum fiat too
|
|
// expect(result.current.estimatedMinimumFiat).toBe(`$${totalMaxFiat}`);
|
|
act(() => {
|
|
result.current.setMaxFeePerGas('90');
|
|
});
|
|
totalEthGasFee = getTotalCostInETH('90', result.current.gasLimit);
|
|
totalMaxFiat = (
|
|
Number(totalEthGasFee) * MOCK_ETH_USD_CONVERSION_RATE
|
|
).toFixed(2);
|
|
expect(result.current.maxFeePerGas).toBe('90');
|
|
expect(result.current.estimatedMaximumNative).toBe(
|
|
`${totalEthGasFee} ETH`,
|
|
);
|
|
expect(result.current.estimatedMaximumFiat).toBe(`$${totalMaxFiat}`);
|
|
// TODO: test minimum fiat too
|
|
// expect(result.current.estimatedMinimumFiat).toBe(`$${totalMaxFiat}`);
|
|
});
|
|
});
|
|
|
|
describe('when balance is sufficient for minimum transaction cost', () => {
|
|
beforeEach(() => {
|
|
useGasFeeEstimates.mockImplementation(
|
|
() => FEE_MARKET_ESTIMATE_RETURN_VALUE,
|
|
);
|
|
useSelector.mockImplementation(generateUseSelectorRouter());
|
|
});
|
|
|
|
it('should return false', () => {
|
|
const { result } = renderHook(() => useGasFeeInputs());
|
|
expect(result.current.balanceError).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('when balance is insufficient for minimum transaction cost', () => {
|
|
beforeEach(() => {
|
|
useGasFeeEstimates.mockImplementation(
|
|
() => HIGH_FEE_MARKET_ESTIMATE_RETURN_VALUE,
|
|
);
|
|
useSelector.mockImplementation(
|
|
generateUseSelectorRouter({
|
|
checkNetworkAndAccountSupports1559Response: true,
|
|
}),
|
|
);
|
|
});
|
|
|
|
it('should return true', () => {
|
|
const { result } = renderHook(() =>
|
|
useGasFeeInputs(null, {
|
|
userFeeLevel: 'medium',
|
|
txParams: { gas: '0x5208' },
|
|
}),
|
|
);
|
|
expect(result.current.balanceError).toBe(true);
|
|
});
|
|
});
|
|
});
|
|
|