diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index c2b8bf3d6..0be730249 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -637,6 +637,10 @@ "customGas": { "message": "Customize Gas" }, + "customGasSettingToolTipMessage": { + "message": "Use $1 to customise the gas price. This can be confusing if you aren’t familiar. Interact at your own risk.", + "description": "$1 is key 'advanced' (text: 'Advanced') separated here so that it can be passed in with bold fontweight" + }, "customGasSubTitle": { "message": "Increasing fee may decrease processing times, but it is not guaranteed." }, @@ -649,6 +653,10 @@ "dappSuggested": { "message": "Site suggested" }, + "dappSuggestedGasSettingToolTipMessage": { + "message": "$1 has suggested this price.", + "description": "$1 is url for the dapp that has suggested gas settings" + }, "dappSuggestedShortLabel": { "message": "Site" }, @@ -1258,6 +1266,13 @@ "high": { "message": "Aggressive" }, + "highGasSettingToolTipDialog": { + "message": "High probability, even in volatile markets" + }, + "highGasSettingToolTipMessage": { + "message": "Use $1 to cover surges in network traffic due to things like popular NFT drops.", + "description": "$1 is key 'high' (text: 'Aggressive') separated here so that it can be passed in with bold fontweight" + }, "history": { "message": "History" }, @@ -1513,6 +1528,10 @@ "low": { "message": "Low" }, + "lowGasSettingToolTipMessage": { + "message": "Use $1 to wait for a cheaper price. Time estimates are much less accurate as prices are somewhat unpredicible.", + "description": "$1 is key 'low' separated here so that it can be passed in with bold fontweight" + }, "lowPriorityMessage": { "message": "Future transactions will queue after this one. This price was last seen was some time ago." }, @@ -1541,6 +1560,10 @@ "medium": { "message": "Market" }, + "mediumGasSettingToolTipMessage": { + "message": "Use $1 for fast processing at current market price.", + "description": "$1 is key 'medium' (text: 'Market') separated here so that it can be passed in with bold fontweight" + }, "memo": { "message": "memo" }, diff --git a/app/images/curve-high.svg b/app/images/curve-high.svg new file mode 100644 index 000000000..f5c918636 --- /dev/null +++ b/app/images/curve-high.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/images/curve-low.svg b/app/images/curve-low.svg new file mode 100644 index 000000000..fee21216e --- /dev/null +++ b/app/images/curve-low.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/images/curve-medium.svg b/app/images/curve-medium.svg new file mode 100644 index 000000000..c3cc1d2aa --- /dev/null +++ b/app/images/curve-medium.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/components/app/app-components.scss b/ui/components/app/app-components.scss index a0bc24e8e..dae43943a 100644 --- a/ui/components/app/app-components.scss +++ b/ui/components/app/app-components.scss @@ -18,6 +18,7 @@ @import 'edit-gas-fee-popover/network-status/index'; @import 'edit-gas-fee-popover/network-status/status-slider/index'; @import 'flask/snaps-authorship-pill/index'; +@import 'edit-gas-fee-popover/edit-gas-tooltip/index'; @import 'gas-customization/gas-modal-page-container/index'; @import 'gas-customization/gas-price-button-group/index'; @import 'gas-customization/index'; diff --git a/ui/components/app/edit-gas-fee-popover/edit-gas-item/edit-gas-item.js b/ui/components/app/edit-gas-fee-popover/edit-gas-item/edit-gas-item.js index 7cd7aa281..47c0e5b66 100644 --- a/ui/components/app/edit-gas-fee-popover/edit-gas-item/edit-gas-item.js +++ b/ui/components/app/edit-gas-fee-popover/edit-gas-item/edit-gas-item.js @@ -18,9 +18,10 @@ import { useGasFeeContext } from '../../../../contexts/gasFee'; import { useI18nContext } from '../../../../hooks/useI18nContext'; import { useTransactionModalContext } from '../../../../contexts/transaction-modal'; import I18nValue from '../../../ui/i18n-value'; -import InfoTooltip from '../../../ui/info-tooltip'; import UserPreferencedCurrencyDisplay from '../../user-preferenced-currency-display'; +import EditGasToolTip from '../edit-gas-tooltip/edit-gas-tooltip'; +import InfoTooltip from '../../../ui/info-tooltip'; import { useCustomTimeEstimate } from './useCustomTimeEstimate'; const EditGasItem = ({ priorityLevel }) => { @@ -43,6 +44,8 @@ const EditGasItem = ({ priorityLevel }) => { if (gasFeeEstimates?.[priorityLevel]) { maxFeePerGas = gasFeeEstimates[priorityLevel].suggestedMaxFeePerGas; + maxPriorityFeePerGas = + gasFeeEstimates[priorityLevel].suggestedMaxPriorityFeePerGas; } else if ( priorityLevel === PRIORITY_LEVELS.DAPP_SUGGESTED && dappSuggestedGasFees @@ -144,8 +147,18 @@ const EditGasItem = ({ priorityLevel }) => { '--' )} - - + + + } + position="top" + /> ); diff --git a/ui/components/app/edit-gas-fee-popover/edit-gas-tooltip/edit-gas-tooltip.js b/ui/components/app/edit-gas-fee-popover/edit-gas-tooltip/edit-gas-tooltip.js new file mode 100644 index 000000000..b04344e3f --- /dev/null +++ b/ui/components/app/edit-gas-fee-popover/edit-gas-tooltip/edit-gas-tooltip.js @@ -0,0 +1,142 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { PRIORITY_LEVELS } from '../../../../../shared/constants/gas'; +import { + COLORS, + FONT_WEIGHT, +} from '../../../../helpers/constants/design-system'; +import Typography from '../../../ui/typography'; +import { useGasFeeContext } from '../../../../contexts/gasFee'; + +const EditGasToolTip = ({ + priorityLevel, + // maxFeePerGas & maxPriorityFeePerGas are derived from conditional logic + // related to the source of the estimates. We pass these values from the + // the parent component (edit-gas-item) rather than recalculate them + maxFeePerGas, + maxPriorityFeePerGas, + t, +}) => { + const { + gasLimit, + maxFeePerGas: maxFeePerGasValue, + maxPriorityFeePerGas: maxPriorityFeePerGasValue, + transaction, + } = useGasFeeContext(); + + const toolTipMessage = () => { + switch (priorityLevel) { + case PRIORITY_LEVELS.LOW: + return t('lowGasSettingToolTipMessage', [ + + {t('low')} + , + ]); + case PRIORITY_LEVELS.MEDIUM: + return t('mediumGasSettingToolTipMessage', [ + + {t('medium')} + , + ]); + case PRIORITY_LEVELS.HIGH: + return t('highGasSettingToolTipMessage', [ + + {t('high')} + , + ]); + case PRIORITY_LEVELS.CUSTOM: + return t('customGasSettingToolTipMessage', [ + + {t('custom')} + , + ]); + case PRIORITY_LEVELS.DAPP_SUGGESTED: + return transaction?.origin + ? t('dappSuggestedGasSettingToolTipMessage', [ + {transaction?.origin}, + ]) + : null; + default: + return ''; + } + }; + return ( +
+ {priorityLevel !== PRIORITY_LEVELS.CUSTOM && + priorityLevel !== PRIORITY_LEVELS.DAPP_SUGGESTED ? ( + + ) : null} + {priorityLevel === PRIORITY_LEVELS.HIGH ? ( +
+ + {t('highGasSettingToolTipDialog')} + +
+ ) : null} +
+ {toolTipMessage()} +
+ {priorityLevel === PRIORITY_LEVELS.CUSTOM ? null : ( +
+
+ + {t('maxBaseFee')} + + + {maxFeePerGas ?? maxFeePerGasValue} + +
+
+ + {t('priorityFee')} + + + {maxPriorityFeePerGas ?? maxPriorityFeePerGasValue} + +
+
+ + {t('gasLimit')} + + + {gasLimit} + +
+
+ )} +
+ ); +}; + +EditGasToolTip.propTypes = { + priorityLevel: PropTypes.string, + maxFeePerGas: PropTypes.string, + maxPriorityFeePerGas: PropTypes.string, + t: PropTypes.func, +}; + +export default EditGasToolTip; diff --git a/ui/components/app/edit-gas-fee-popover/edit-gas-tooltip/edit-gas-tooltip.test.js b/ui/components/app/edit-gas-fee-popover/edit-gas-tooltip/edit-gas-tooltip.test.js new file mode 100644 index 000000000..23f7188ed --- /dev/null +++ b/ui/components/app/edit-gas-fee-popover/edit-gas-tooltip/edit-gas-tooltip.test.js @@ -0,0 +1,97 @@ +import React from 'react'; +import configureStore from '../../../../store/store'; +import { renderWithProvider } from '../../../../../test/jest'; +import { GasFeeContextProvider } from '../../../../contexts/gasFee'; +import EditGasToolTip from './edit-gas-tooltip'; + +jest.mock('../../../../store/actions', () => ({ + disconnectGasFeeEstimatePoller: jest.fn(), + getGasFeeEstimatesAndStartPolling: jest + .fn() + .mockImplementation(() => Promise.resolve()), + addPollingTokenToAppState: jest.fn(), + getGasFeeTimeEstimate: jest + .fn() + .mockImplementation(() => Promise.resolve('unknown')), +})); + +const LOW_GAS_OPTION = { + maxFeePerGas: '2.010203381', + maxPriorityFeePerGas: '1.20004164', +}; + +const MEDIUM_GAS_OPTION = { + maxFeePerGas: '2.383812808', + maxPriorityFeePerGas: '1.5', +}; + +const HIGH_GAS_OPTION = { + maxFeePerGas: '2.920638342', + maxPriorityFeePerGas: '2', +}; + +const renderComponent = (props, transactionProps, gasFeeContextProps) => { + const mockStore = { + metamask: { + provider: {}, + cachedBalances: {}, + accounts: { + '0xAddress': { + address: '0xAddress', + balance: '0x176e5b6f173ebe66', + }, + }, + selectedAddress: '0xAddress', + featureFlags: { advancedInlineGas: true }, + advancedGasFee: { + maxBaseFee: '1.5', + priorityFee: '2', + }, + }, + }; + + const store = configureStore(mockStore); + + return renderWithProvider( + + + , + store, + ); +}; + +describe('EditGasToolTip', () => { + it('should render correct values for priorityLevel low', () => { + const { queryByText } = renderComponent({ + priorityLevel: 'low', + ...LOW_GAS_OPTION, + }); + + expect(queryByText('2.010203381')).toBeInTheDocument(); + expect(queryByText('1.20004164')).toBeInTheDocument(); + expect(queryByText('21000')).toBeInTheDocument(); + }); + + it('should render correct values for priorityLevel medium', () => { + const { queryByText } = renderComponent({ + priorityLevel: 'medium', + ...MEDIUM_GAS_OPTION, + }); + expect(queryByText('2.383812808')).toBeInTheDocument(); + expect(queryByText('1.5')).toBeInTheDocument(); + expect(queryByText('21000')).toBeInTheDocument(); + }); + + it('should render correct values for priorityLevel high', () => { + const { queryByText } = renderComponent({ + priorityLevel: 'high', + ...HIGH_GAS_OPTION, + }); + expect(queryByText('2.920638342')).toBeInTheDocument(); + expect(queryByText('2')).toBeInTheDocument(); + expect(queryByText('21000')).toBeInTheDocument(); + }); +}); diff --git a/ui/components/app/edit-gas-fee-popover/edit-gas-tooltip/index.scss b/ui/components/app/edit-gas-fee-popover/edit-gas-tooltip/index.scss new file mode 100644 index 000000000..d3d4bc9f0 --- /dev/null +++ b/ui/components/app/edit-gas-fee-popover/edit-gas-tooltip/index.scss @@ -0,0 +1,53 @@ +.edit-gas-tooltip { + display: inline-block; + text-align: right; + width: 10%; + + &__container { + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; + width: 100%; + height: 100%; + + img { + margin-bottom: 8px; + } + + &__message { + width: 100%; + } + + &__dialog { + background-color: $Orange-500; + border-radius: 30px; + margin: 4px 0; + text-align: center; + padding: 4px; + } + + &__label { + width: 50%; + } + + &__value { + width: 50%; + } + + p { + margin-bottom: 0 !important; + } + + &__values { + width: 100%; + margin-top: 8px; + + div { + display: flex; + flex-direction: row; + text-align: left; + } + } + } +} diff --git a/ui/css/design-system/colors.scss b/ui/css/design-system/colors.scss index b66c07690..e8b7bd56a 100644 --- a/ui/css/design-system/colors.scss +++ b/ui/css/design-system/colors.scss @@ -122,6 +122,7 @@ $color-map: ( 'white': $ui-white, 'black': $ui-black, 'grey': $ui-grey, + 'neutral-grey': $neutral-grey, 'primary-1': $primary-1, 'primary-2': $primary-2, 'primary-3': $primary-3, diff --git a/ui/helpers/constants/design-system.js b/ui/helpers/constants/design-system.js index ae26f9d28..42a67b06b 100644 --- a/ui/helpers/constants/design-system.js +++ b/ui/helpers/constants/design-system.js @@ -11,6 +11,7 @@ export const COLORS = { UI4: 'ui-4', BLACK: 'black', GREY: 'grey', + NEUTRAL_GREY: 'neutral-grey', WHITE: 'white', PRIMARY1: 'primary-1', PRIMARY2: 'primary-2',