1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-11-22 18:00:18 +01:00

EIP-1559 V2: Adding default settings to advanced gas modal (#12911)

This commit is contained in:
Niranjana Binoy 2021-12-21 14:45:28 -05:00 committed by GitHub
parent 3a11800cb1
commit 8c77e37d2a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 261 additions and 13 deletions

View File

@ -170,6 +170,12 @@
"advancedBaseGasFeeToolTip": { "advancedBaseGasFeeToolTip": {
"message": "When your transaction gets included in the block, any difference between your max base fee and the actual base fee will be refunded. Total amount is calculated as max base fee (in GWEI) * gas limit." "message": "When your transaction gets included in the block, any difference between your max base fee and the actual base fee will be refunded. Total amount is calculated as max base fee (in GWEI) * gas limit."
}, },
"advancedGasFeeDefaultOptIn": {
"message": "Save these $1 as my default for \"Advanced\""
},
"advancedGasFeeDefaultOptOut": {
"message": "Always use these values and advanced setting as default."
},
"advancedGasFeeModalTitle": { "advancedGasFeeModalTitle": {
"message": "Advanced gas fee" "message": "Advanced gas fee"
}, },
@ -1851,6 +1857,9 @@
"newTransactionFee": { "newTransactionFee": {
"message": "New Transaction Fee" "message": "New Transaction Fee"
}, },
"newValues": {
"message": "new values"
},
"next": { "next": {
"message": "Next" "message": "Next"
}, },

View File

@ -0,0 +1,80 @@
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import Box from '../../../ui/box';
import Typography from '../../../ui/typography';
import CheckBox from '../../../ui/check-box';
import I18nValue from '../../../ui/i18n-value';
import {
COLORS,
DISPLAY,
FLEX_DIRECTION,
TYPOGRAPHY,
} from '../../../../helpers/constants/design-system';
import { getAdvancedGasFeeValues } from '../../../../selectors';
import { setAdvancedGasFee } from '../../../../store/actions';
import { useAdvancedGasFeePopoverContext } from '../context';
import { useI18nContext } from '../../../../hooks/useI18nContext';
const AdvancedGasFeeDefaults = () => {
const t = useI18nContext();
const dispatch = useDispatch();
const {
hasErrors,
baseFeeMultiplier,
maxPriorityFeePerGas,
} = useAdvancedGasFeePopoverContext();
const advancedGasFeeValues = useSelector(getAdvancedGasFeeValues);
const updateDefaultSettings = (value) => {
if (value) {
dispatch(
setAdvancedGasFee({
maxBaseFee: baseFeeMultiplier,
priorityFee: maxPriorityFeePerGas,
}),
);
} else {
dispatch(setAdvancedGasFee(null));
}
};
const isDefaultSettingsSelected =
Boolean(advancedGasFeeValues) &&
advancedGasFeeValues.maxBaseFee === baseFeeMultiplier &&
advancedGasFeeValues.priorityFee === maxPriorityFeePerGas;
const handleUpdateDefaultSettings = () =>
updateDefaultSettings(!isDefaultSettingsSelected);
return (
<Box
display={DISPLAY.FLEX}
flexDirection={FLEX_DIRECTION.ROW}
marginRight={4}
className="advanced-gas-fee-defaults"
>
<CheckBox
checked={isDefaultSettingsSelected}
className="advanced-gas-fee-defaults__checkbox"
onClick={handleUpdateDefaultSettings}
disabled={hasErrors}
/>
<Typography variant={TYPOGRAPHY.H7} color={COLORS.UI4} margin={0}>
{!isDefaultSettingsSelected && Boolean(advancedGasFeeValues) ? (
<I18nValue
messageKey="advancedGasFeeDefaultOptIn"
options={[
<strong key="default-value-change">{t('newValues')}</strong>,
]}
/>
) : (
<I18nValue messageKey="advancedGasFeeDefaultOptOut" />
)}
</Typography>
</Box>
);
};
export default AdvancedGasFeeDefaults;

View File

@ -0,0 +1,132 @@
import React from 'react';
import { fireEvent, screen } from '@testing-library/react';
import { GAS_ESTIMATE_TYPES } from '../../../../../shared/constants/gas';
import { renderWithProvider } from '../../../../../test/lib/render-helpers';
import mockEstimates from '../../../../../test/data/mock-estimates.json';
import mockState from '../../../../../test/data/mock-state.json';
import { AdvancedGasFeePopoverContextProvider } from '../context';
import { GasFeeContextProvider } from '../../../../contexts/gasFee';
import configureStore from '../../../../store/store';
import AdvancedGasFeeInputs from '../advanced-gas-fee-inputs';
import AdvancedGasFeeDefaults from './advanced-gas-fee-defaults';
jest.mock('../../../../store/actions', () => ({
disconnectGasFeeEstimatePoller: jest.fn(),
getGasFeeEstimatesAndStartPolling: jest
.fn()
.mockImplementation(() => Promise.resolve()),
addPollingTokenToAppState: jest.fn(),
removePollingTokenFromAppState: jest.fn(),
}));
const render = (defaultGasParams) => {
const store = configureStore({
metamask: {
...mockState.metamask,
...defaultGasParams,
accounts: {
[mockState.metamask.selectedAddress]: {
address: mockState.metamask.selectedAddress,
balance: '0x1F4',
},
},
featureFlags: { advancedInlineGas: true },
gasFeeEstimates:
mockEstimates[GAS_ESTIMATE_TYPES.FEE_MARKET].gasFeeEstimates,
},
});
return renderWithProvider(
<GasFeeContextProvider
transaction={{
userFeeLevel: 'custom',
txParams: {
maxFeePerGas: '0x174876E800',
maxPriorityFeePerGas: '0x77359400',
},
}}
>
<AdvancedGasFeePopoverContextProvider>
<AdvancedGasFeeInputs />
<AdvancedGasFeeDefaults />
</AdvancedGasFeePopoverContextProvider>
</GasFeeContextProvider>,
store,
);
};
describe('AdvancedGasFeeDefaults', () => {
it('should renders correct message when the default is not set', () => {
render({ advancedGasFee: null });
expect(
screen.queryByText(
'Always use these values and advanced setting as default.',
),
).toBeInTheDocument();
});
it('should renders correct message when the default values are set', () => {
render({
advancedGasFee: { maxBaseFee: 2, priorityFee: 2 },
});
expect(
screen.queryByText(
'Always use these values and advanced setting as default.',
),
).toBeInTheDocument();
});
it('should renders correct message when checkbox is selected and default values are saved', () => {
render({
advancedGasFee: null,
});
expect(
screen.queryByText(
'Always use these values and advanced setting as default.',
),
).toBeInTheDocument();
fireEvent.change(document.getElementsByTagName('input')[0], {
target: { value: 3 },
});
fireEvent.change(document.getElementsByTagName('input')[1], {
target: { value: 4 },
});
});
it('should renders correct message when the default values are set and the maxBaseFee values are updated', () => {
render({
advancedGasFee: { maxBaseFee: 2, priorityFee: 2 },
});
expect(document.getElementsByTagName('input')[2]).toBeChecked();
expect(
screen.queryByText(
'Always use these values and advanced setting as default.',
),
).toBeInTheDocument();
fireEvent.change(document.getElementsByTagName('input')[0], {
target: { value: 4 },
});
expect(document.getElementsByTagName('input')[0]).toHaveValue(4);
expect(screen.queryByText('new values')).toBeInTheDocument();
expect(
screen.queryByText('Save these as my default for "Advanced"'),
).toBeInTheDocument();
});
it('should renders correct message when the default values are set and the priorityFee values are updated', () => {
render({
advancedGasFee: { maxBaseFee: 2, priorityFee: 2 },
});
expect(document.getElementsByTagName('input')[2]).toBeChecked();
expect(
screen.queryByText(
'Always use these values and advanced setting as default.',
),
).toBeInTheDocument();
fireEvent.change(document.getElementsByTagName('input')[1], {
target: { value: 4 },
});
expect(document.getElementsByTagName('input')[1]).toHaveValue(4);
expect(screen.queryByText('new values')).toBeInTheDocument();
expect(
screen.queryByText('Save these as my default for "Advanced"'),
).toBeInTheDocument();
});
});

View File

@ -0,0 +1 @@
export { default } from './advanced-gas-fee-defaults';

View File

@ -0,0 +1,6 @@
.advanced-gas-fee-defaults {
& &__checkbox {
font-size: $font-size-h4;
margin: 0 8px 0 8px;
}
}

View File

@ -63,6 +63,7 @@ const AdvancedGasFeeGasLimit = () => {
tag={TYPOGRAPHY.Paragraph} tag={TYPOGRAPHY.Paragraph}
variant={TYPOGRAPHY.H7} variant={TYPOGRAPHY.H7}
className="advanced-gas-fee-gas-limit" className="advanced-gas-fee-gas-limit"
margin={[0, 2]}
> >
<strong> <strong>
<I18nValue messageKey="gasLimitV2" /> <I18nValue messageKey="gasLimitV2" />

View File

@ -6,7 +6,7 @@ import PriorityFeeInput from './priority-fee-input';
const AdvancedGasFeeInputs = () => { const AdvancedGasFeeInputs = () => {
return ( return (
<Box className="advanced-gas-fee-inputs" margin={[4, 0]}> <Box className="advanced-gas-fee-inputs">
<BaseFeeInput /> <BaseFeeInput />
<div className="advanced-gas-fee-inputs__separator" /> <div className="advanced-gas-fee-inputs__separator" />
<PriorityFeeInput /> <PriorityFeeInput />

View File

@ -10,7 +10,6 @@ import {
import { PRIMARY, SECONDARY } from '../../../../../helpers/constants/common'; import { PRIMARY, SECONDARY } from '../../../../../helpers/constants/common';
import { bnGreaterThan, bnLessThan } from '../../../../../helpers/utils/util'; import { bnGreaterThan, bnLessThan } from '../../../../../helpers/utils/util';
import { decGWEIToHexWEI } from '../../../../../helpers/utils/conversions.util'; import { decGWEIToHexWEI } from '../../../../../helpers/utils/conversions.util';
import { getAdvancedGasFeeValues } from '../../../../../selectors'; import { getAdvancedGasFeeValues } from '../../../../../selectors';
import { useGasFeeContext } from '../../../../../contexts/gasFee'; import { useGasFeeContext } from '../../../../../contexts/gasFee';
import { useI18nContext } from '../../../../../hooks/useI18nContext'; import { useI18nContext } from '../../../../../hooks/useI18nContext';
@ -77,11 +76,13 @@ const validateBaseFee = (
const BaseFeeInput = () => { const BaseFeeInput = () => {
const t = useI18nContext(); const t = useI18nContext();
const { gasFeeEstimates, estimateUsed, maxFeePerGas } = useGasFeeContext(); const { gasFeeEstimates, estimateUsed, maxFeePerGas } = useGasFeeContext();
const { const {
maxPriorityFeePerGas, maxPriorityFeePerGas,
setErrorValue, setErrorValue,
setMaxFeePerGas, setMaxFeePerGas,
setBaseFeeMultiplier,
} = useAdvancedGasFeePopoverContext(); } = useAdvancedGasFeePopoverContext();
const { const {
@ -177,6 +178,7 @@ const BaseFeeInput = () => {
if (baseFeeTrend !== 'level' && baseFeeTrend !== feeTrend) { if (baseFeeTrend !== 'level' && baseFeeTrend !== feeTrend) {
setFeeTrend(baseFeeTrend); setFeeTrend(baseFeeTrend);
} }
setBaseFeeMultiplier(maxBaseFeeMultiplier);
}, [ }, [
feeTrend, feeTrend,
editingInGwei, editingInGwei,
@ -184,14 +186,16 @@ const BaseFeeInput = () => {
gasFeeEstimates, gasFeeEstimates,
maxBaseFeeGWEI, maxBaseFeeGWEI,
maxPriorityFeePerGas, maxPriorityFeePerGas,
maxBaseFeeMultiplier,
setBaseFeeError, setBaseFeeError,
setErrorValue, setErrorValue,
setMaxFeePerGas, setMaxFeePerGas,
setFeeTrend, setFeeTrend,
setBaseFeeMultiplier,
]); ]);
return ( return (
<Box className="base-fee-input"> <Box className="base-fee-input" margin={[0, 2]}>
<FormField <FormField
error={baseFeeError ? t(baseFeeError) : ''} error={baseFeeError ? t(baseFeeError) : ''}
onChange={updateBaseFee} onChange={updateBaseFee}

View File

@ -9,6 +9,6 @@
&__separator { &__separator {
border-top: 1px solid $ui-grey; border-top: 1px solid $ui-grey;
margin: 24px 0 16px 0; margin: 16px 0;
} }
} }

View File

@ -11,6 +11,7 @@ import { useGasFeeContext } from '../../../../../contexts/gasFee';
import { useI18nContext } from '../../../../../hooks/useI18nContext'; import { useI18nContext } from '../../../../../hooks/useI18nContext';
import { useUserPreferencedCurrency } from '../../../../../hooks/useUserPreferencedCurrency'; import { useUserPreferencedCurrency } from '../../../../../hooks/useUserPreferencedCurrency';
import FormField from '../../../../ui/form-field'; import FormField from '../../../../ui/form-field';
import Box from '../../../../ui/box';
import { bnGreaterThan, bnLessThan } from '../../../../../helpers/utils/util'; import { bnGreaterThan, bnLessThan } from '../../../../../helpers/utils/util';
import { useAdvancedGasFeePopoverContext } from '../../context'; import { useAdvancedGasFeePopoverContext } from '../../context';
@ -103,7 +104,7 @@ const PriorityFeeInput = () => {
]); ]);
return ( return (
<> <Box margin={[0, 2]}>
<FormField <FormField
error={priorityFeeError ? t(priorityFeeError) : ''} error={priorityFeeError ? t(priorityFeeError) : ''}
onChange={updatePriorityFee} onChange={updatePriorityFee}
@ -119,7 +120,7 @@ const PriorityFeeInput = () => {
historical={renderFeeRange(historicalPriorityFeeRange)} historical={renderFeeRange(historicalPriorityFeeRange)}
feeTrend={feeTrend} feeTrend={feeTrend}
/> />
</> </Box>
); );
}; };

View File

@ -9,6 +9,7 @@ import { AdvancedGasFeePopoverContextProvider } from './context';
import AdvancedGasFeeInputs from './advanced-gas-fee-inputs'; import AdvancedGasFeeInputs from './advanced-gas-fee-inputs';
import AdvancedGasFeeGasLimit from './advanced-gas-fee-gas-limit'; import AdvancedGasFeeGasLimit from './advanced-gas-fee-gas-limit';
import AdvancedGasFeeSaveButton from './advanced-gas-fee-save'; import AdvancedGasFeeSaveButton from './advanced-gas-fee-save';
import AdvancedGasFeeDefaults from './advanced-gas-fee-defaults';
const AdvancedGasFeePopover = () => { const AdvancedGasFeePopover = () => {
const t = useI18nContext(); const t = useI18nContext();
@ -29,9 +30,11 @@ const AdvancedGasFeePopover = () => {
onClose={closeAllModals} onClose={closeAllModals}
footer={<AdvancedGasFeeSaveButton />} footer={<AdvancedGasFeeSaveButton />}
> >
<Box className="advanced-gas-fee-popover__wrapper" margin={4}> <Box margin={4}>
<AdvancedGasFeeInputs /> <AdvancedGasFeeInputs />
<div className="advanced-gas-fee-popover__separator" /> <div className="advanced-gas-fee-popover__separator" />
<AdvancedGasFeeDefaults />
<div className="advanced-gas-fee-popover__separator" />
<AdvancedGasFeeGasLimit /> <AdvancedGasFeeGasLimit />
</Box> </Box>
</Popover> </Popover>

View File

@ -20,6 +20,7 @@ export const AdvancedGasFeePopoverContextProvider = ({ children }) => {
}, },
[errors, setErrors], [errors, setErrors],
); );
const [baseFeeMultiplier, setBaseFeeMultiplier] = useState();
return ( return (
<AdvancedGasFeePopoverContext.Provider <AdvancedGasFeePopoverContext.Provider
@ -29,9 +30,11 @@ export const AdvancedGasFeePopoverContextProvider = ({ children }) => {
maxFeePerGas, maxFeePerGas,
maxPriorityFeePerGas, maxPriorityFeePerGas,
setErrorValue, setErrorValue,
baseFeeMultiplier,
setGasLimit, setGasLimit,
setMaxPriorityFeePerGas, setMaxPriorityFeePerGas,
setMaxFeePerGas, setMaxFeePerGas,
setBaseFeeMultiplier,
}} }}
> >
{children} {children}

View File

@ -1,14 +1,21 @@
.advanced-gas-fee-popover { .advanced-gas-fee-popover {
&__wrapper {
border-top: 1px solid $ui-grey;
}
&__separator { &__separator {
border-top: 1px solid $ui-grey; border-top: 1px solid $ui-grey;
margin: 24px 0 16px 0; margin: 16px 0;
} }
.form-field__heading-title > h6 { .form-field__heading-title > h6 {
font-size: $font-size-h7; font-size: $font-size-h7;
} }
.popover-header {
border-radius: 0;
border-top-left-radius: 10px;
border-top-right-radius: 10px;
border-bottom: 1px solid $ui-grey;
}
.popover-footer {
border-top: none;
}
} }

View File

@ -62,9 +62,10 @@
@import 'whats-new-popup/index'; @import 'whats-new-popup/index';
@import 'loading-network-screen/index'; @import 'loading-network-screen/index';
@import 'flask/experimental-area/index'; @import 'flask/experimental-area/index';
@import 'transaction-decoding/index';
@import 'advanced-gas-fee-popover/index'; @import 'advanced-gas-fee-popover/index';
@import 'advanced-gas-fee-popover/advanced-gas-fee-gas-limit/index'; @import 'advanced-gas-fee-popover/advanced-gas-fee-gas-limit/index';
@import 'advanced-gas-fee-popover/advanced-gas-fee-inputs/index'; @import 'advanced-gas-fee-popover/advanced-gas-fee-inputs/index';
@import 'advanced-gas-fee-popover/advanced-gas-fee-inputs/base-fee-input/index'; @import 'advanced-gas-fee-popover/advanced-gas-fee-inputs/base-fee-input/index';
@import 'advanced-gas-fee-popover/advanced-gas-fee-input-subtext/index'; @import 'advanced-gas-fee-popover/advanced-gas-fee-input-subtext/index';
@import 'transaction-decoding/index'; @import 'advanced-gas-fee-popover/advanced-gas-fee-defaults/index';