Edit transaction screen changes for EIP-1559 V2 (#12493)

Edit transaction screen changes for EIP-1559 V2
feature/default_network_editable
Jyoti Puri 3 years ago committed by GitHub
parent e6ae6e09b8
commit 3dfc1cc5f6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 6
      app/_locales/en/messages.json
  2. 2
      development/build/scripts.js
  3. 8
      ui/components/app/gas-timing/gas-timing.component.js
  4. 19
      ui/components/app/transaction-detail-item/index.scss
  5. 18
      ui/components/app/transaction-detail/index.scss
  6. 15
      ui/components/ui/i18n-value/i18n-value.component.js
  7. 1
      ui/components/ui/i18n-value/index.js
  8. 3
      ui/components/ui/typography/typography.js
  9. 6
      ui/components/ui/typography/typography.scss
  10. 1
      ui/css/design-system/attributes.scss
  11. 233
      ui/pages/confirm-transaction-base/confirm-transaction-base.component.js
  12. 136
      ui/pages/confirm-transaction-base/gas-details-item/gas-details-item.js
  13. 17
      ui/pages/confirm-transaction-base/gas-details-item/gas-details-item.scss
  14. 41
      ui/pages/confirm-transaction-base/gas-details-item/gas-details-item.test.js
  15. 1
      ui/pages/confirm-transaction-base/gas-details-item/index.js
  16. 1
      ui/pages/pages.scss

@ -2800,6 +2800,12 @@
"transactionDetailGasHeading": { "transactionDetailGasHeading": {
"message": "Estimated gas fee" "message": "Estimated gas fee"
}, },
"transactionDetailGasHeadingV2": {
"message": "Gas"
},
"transactionDetailGasInfoV2": {
"message": "estimated"
},
"transactionDetailGasTooltipConversion": { "transactionDetailGasTooltipConversion": {
"message": "Learn more about gas fees" "message": "Learn more about gas fees"
}, },

@ -34,6 +34,7 @@ const metamaskrc = require('rc')('metamask', {
INFURA_PROD_PROJECT_ID: process.env.INFURA_PROD_PROJECT_ID, INFURA_PROD_PROJECT_ID: process.env.INFURA_PROD_PROJECT_ID,
ONBOARDING_V2: process.env.ONBOARDING_V2, ONBOARDING_V2: process.env.ONBOARDING_V2,
COLLECTIBLES_V1: process.env.COLLECTIBLES_V1, COLLECTIBLES_V1: process.env.COLLECTIBLES_V1,
EIP_1559_V2: process.env.EIP_1559_V2,
SEGMENT_HOST: process.env.SEGMENT_HOST, SEGMENT_HOST: process.env.SEGMENT_HOST,
SEGMENT_WRITE_KEY: process.env.SEGMENT_WRITE_KEY, SEGMENT_WRITE_KEY: process.env.SEGMENT_WRITE_KEY,
SEGMENT_BETA_WRITE_KEY: process.env.SEGMENT_BETA_WRITE_KEY, SEGMENT_BETA_WRITE_KEY: process.env.SEGMENT_BETA_WRITE_KEY,
@ -758,6 +759,7 @@ function getEnvironmentVariables({ buildType, devMode, testing }) {
SWAPS_USE_DEV_APIS: process.env.SWAPS_USE_DEV_APIS === '1', SWAPS_USE_DEV_APIS: process.env.SWAPS_USE_DEV_APIS === '1',
ONBOARDING_V2: metamaskrc.ONBOARDING_V2 === '1', ONBOARDING_V2: metamaskrc.ONBOARDING_V2 === '1',
COLLECTIBLES_V1: metamaskrc.COLLECTIBLES_V1 === '1', COLLECTIBLES_V1: metamaskrc.COLLECTIBLES_V1 === '1',
EIP_1559_V2: metamaskrc.EIP_1559_V2 === '1',
}; };
} }

@ -27,6 +27,8 @@ import { GAS_FORM_ERRORS } from '../../../helpers/constants/gas';
// Once we reach this second threshold, we switch to minutes as a unit // Once we reach this second threshold, we switch to minutes as a unit
const SECOND_CUTOFF = 90; const SECOND_CUTOFF = 90;
// eslint-disable-next-line prefer-destructuring
const EIP_1559_V2 = process.env.EIP_1559_V2;
// Shows "seconds" as unit of time if under SECOND_CUTOFF, otherwise "minutes" // Shows "seconds" as unit of time if under SECOND_CUTOFF, otherwise "minutes"
const toHumanReadableTime = (milliseconds = 1, t) => { const toHumanReadableTime = (milliseconds = 1, t) => {
@ -164,6 +166,12 @@ export default function GasTiming({
toHumanReadableTime(Number(customEstimatedTime?.upperTimeBound), t), toHumanReadableTime(Number(customEstimatedTime?.upperTimeBound), t),
]); ]);
} }
}
// code below needs to cleaned-up once EIP_1559_V2 flag is removed
else if (EIP_1559_V2) {
text = t('gasTimingNegative', [
toHumanReadableTime(low.maxWaitTimeEstimate, t),
]);
} else { } else {
text = ( text = (
<> <>

@ -1,5 +1,7 @@
.transaction-detail-item { .transaction-detail-item {
color: $ui-4; color: $ui-4;
padding: 20px 0;
border-bottom: 1px solid $ui-3;
&__row { &__row {
display: flex; display: flex;
@ -36,4 +38,21 @@
.currency-display-component { .currency-display-component {
display: inline; display: inline;
} }
&:first-of-type {
padding-top: 0;
}
&:last-of-type {
margin-bottom: 20px;
}
&:first-child {
padding-top: 0;
}
&:last-child {
padding-bottom: 0;
border-bottom: 0;
}
} }

@ -15,22 +15,4 @@
text-transform: uppercase; text-transform: uppercase;
} }
} }
&-rows &-item:first-child {
padding-top: 0;
}
&-item {
padding: 20px 0;
border-bottom: 1px solid $ui-3;
&:first-child {
padding-top: 0;
}
&:last-child {
padding-bottom: 0;
border-bottom: 0;
}
}
} }

@ -0,0 +1,15 @@
import PropTypes from 'prop-types';
import { useI18nContext } from '../../../hooks/useI18nContext';
const I18nValue = ({ messageKey, options }) => {
const t = useI18nContext();
return t(messageKey, options);
};
I18nValue.propTypes = {
messageKey: PropTypes.string.isRequired,
options: PropTypes.array,
};
export default I18nValue;

@ -0,0 +1 @@
export { default } from './i18n-value.component';

@ -20,6 +20,7 @@ export default function Typography({
children, children,
fontWeight = 'normal', fontWeight = 'normal',
fontStyle = 'normal', fontStyle = 'normal',
fontSize,
align, align,
boxProps = {}, boxProps = {},
margin = [1, 0], margin = [1, 0],
@ -33,6 +34,7 @@ export default function Typography({
{ {
[`typography--align-${align}`]: Boolean(align), [`typography--align-${align}`]: Boolean(align),
[`typography--color-${color}`]: Boolean(color), [`typography--color-${color}`]: Boolean(color),
[`typography--size-${fontSize}`]: Boolean(fontSize),
}, },
); );
@ -67,6 +69,7 @@ Typography.propTypes = {
margin: MultipleSizes, margin: MultipleSizes,
fontWeight: PropTypes.oneOf(Object.values(FONT_WEIGHT)), fontWeight: PropTypes.oneOf(Object.values(FONT_WEIGHT)),
fontStyle: PropTypes.oneOf(Object.values(FONT_STYLE)), fontStyle: PropTypes.oneOf(Object.values(FONT_STYLE)),
fontSize: PropTypes.string,
tag: PropTypes.oneOf([ tag: PropTypes.oneOf([
'p', 'p',
'h1', 'h1',

@ -32,6 +32,12 @@
} }
} }
@each $size in design-system.$font-size {
&--size-#{$size} {
font-size: $size;
}
}
@each $alignment in design-system.$text-align { @each $alignment in design-system.$text-align {
&--align-#{$alignment} { &--align-#{$alignment} {
text-align: $alignment; text-align: $alignment;

@ -82,3 +82,4 @@ $display: block, grid, flex, inline-block, inline-grid, inline-flex, list-item;
$text-align: left, right, center, justify, end; $text-align: left, right, center, justify, end;
$font-weight: bold, normal, 100, 200, 300, 400, 500, 600, 700, 800, 900; $font-weight: bold, normal, 100, 200, 300, 400, 500, 600, 700, 800, 900;
$font-style: normal, italic, oblique; $font-style: normal, italic, oblique;
$font-size: 12px;

@ -52,6 +52,11 @@ import {
import Typography from '../../components/ui/typography/typography'; import Typography from '../../components/ui/typography/typography';
import { MIN_GAS_LIMIT_DEC } from '../send/send.constants'; import { MIN_GAS_LIMIT_DEC } from '../send/send.constants';
import GasDetailsItem from './gas-details-item';
// eslint-disable-next-line prefer-destructuring
const EIP_1559_V2 = process.env.EIP_1559_V2;
const renderHeartBeatIfNotInTest = () => const renderHeartBeatIfNotInTest = () =>
process.env.IN_TEST === 'true' ? null : <LoadingHeartBeat />; process.env.IN_TEST === 'true' ? null : <LoadingHeartBeat />;
@ -408,114 +413,130 @@ export default class ConfirmTransactionBase extends Component {
<TransactionDetail <TransactionDetail
onEdit={() => this.handleEditGas()} onEdit={() => this.handleEditGas()}
rows={[ rows={[
<TransactionDetailItem EIP_1559_V2 ? (
key="gas-item" <GasDetailsItem
detailTitle={ key="gas_details"
txData.dappSuggestedGasFees ? ( hexMaximumTransactionFee={hexMaximumTransactionFee}
<> hexMinimumTransactionFee={hexMinimumTransactionFee}
{t('transactionDetailGasHeading')} isMainnet={isMainnet}
<InfoTooltip maxFeePerGas={maxFeePerGas}
contentText={t('transactionDetailDappGasTooltip')} maxPriorityFeePerGas={maxPriorityFeePerGas}
position="top" supportsEIP1559={supportsEIP1559}
> txData={txData}
<i className="fa fa-info-circle" /> useNativeCurrencyAsPrimaryCurrency={
</InfoTooltip> useNativeCurrencyAsPrimaryCurrency
</> }
) : ( />
<> ) : (
{t('transactionDetailGasHeading')} <TransactionDetailItem
<InfoTooltip key="gas-item"
contentText={ detailTitle={
<> txData.dappSuggestedGasFees ? (
<p> <>
{t('transactionDetailGasTooltipIntro', [ {t('transactionDetailGasHeading')}
isMainnet ? t('networkNameEthereum') : '', <InfoTooltip
])} contentText={t('transactionDetailDappGasTooltip')}
</p> position="top"
<p>{t('transactionDetailGasTooltipExplanation')}</p> >
<p> <i className="fa fa-info-circle" />
<a </InfoTooltip>
href="https://community.metamask.io/t/what-is-gas-why-do-transactions-take-so-long/3172" </>
target="_blank"
rel="noopener noreferrer"
>
{t('transactionDetailGasTooltipConversion')}
</a>
</p>
</>
}
position="top"
>
<i className="fa fa-info-circle" />
</InfoTooltip>
</>
)
}
detailTitleColor={COLORS.BLACK}
detailText={
<div className="confirm-page-container-content__currency-container">
{renderHeartBeatIfNotInTest()}
<UserPreferencedCurrencyDisplay
type={SECONDARY}
value={hexMinimumTransactionFee}
hideLabel={Boolean(useNativeCurrencyAsPrimaryCurrency)}
/>
</div>
}
detailTotal={
<div className="confirm-page-container-content__currency-container">
{renderHeartBeatIfNotInTest()}
<UserPreferencedCurrencyDisplay
type={PRIMARY}
value={hexMinimumTransactionFee}
hideLabel={!useNativeCurrencyAsPrimaryCurrency}
/>
</div>
}
subText={t('editGasSubTextFee', [
<b key="editGasSubTextFeeLabel">
{t('editGasSubTextFeeLabel')}
</b>,
<div
key="editGasSubTextFeeValue"
className="confirm-page-container-content__currency-container"
>
{renderHeartBeatIfNotInTest()}
<UserPreferencedCurrencyDisplay
key="editGasSubTextFeeAmount"
type={PRIMARY}
value={hexMaximumTransactionFee}
hideLabel={!useNativeCurrencyAsPrimaryCurrency}
/>
</div>,
])}
subTitle={
<>
{txData.dappSuggestedGasFees ? (
<Typography
variant={TYPOGRAPHY.H7}
fontStyle={FONT_STYLE.ITALIC}
color={COLORS.UI4}
>
{t('transactionDetailDappGasMoreInfo')}
</Typography>
) : ( ) : (
'' <>
)} {t('transactionDetailGasHeading')}
{supportsEIP1559 && ( <InfoTooltip
<GasTiming contentText={
maxPriorityFeePerGas={hexWEIToDecGWEI( <>
maxPriorityFeePerGas || <p>
txData.txParams.maxPriorityFeePerGas, {t('transactionDetailGasTooltipIntro', [
)} isMainnet ? t('networkNameEthereum') : '',
maxFeePerGas={hexWEIToDecGWEI( ])}
maxFeePerGas || txData.txParams.maxFeePerGas, </p>
)} <p>{t('transactionDetailGasTooltipExplanation')}</p>
<p>
<a
href="https://community.metamask.io/t/what-is-gas-why-do-transactions-take-so-long/3172"
target="_blank"
rel="noopener noreferrer"
>
{t('transactionDetailGasTooltipConversion')}
</a>
</p>
</>
}
position="top"
>
<i className="fa fa-info-circle" />
</InfoTooltip>
</>
)
}
detailTitleColor={COLORS.BLACK}
detailText={
<div className="confirm-page-container-content__currency-container">
{renderHeartBeatIfNotInTest()}
<UserPreferencedCurrencyDisplay
type={SECONDARY}
value={hexMinimumTransactionFee}
hideLabel={Boolean(useNativeCurrencyAsPrimaryCurrency)}
/> />
)} </div>
</> }
} detailTotal={
/>, <div className="confirm-page-container-content__currency-container">
{renderHeartBeatIfNotInTest()}
<UserPreferencedCurrencyDisplay
type={PRIMARY}
value={hexMinimumTransactionFee}
hideLabel={!useNativeCurrencyAsPrimaryCurrency}
/>
</div>
}
subText={t('editGasSubTextFee', [
<b key="editGasSubTextFeeLabel">
{t('editGasSubTextFeeLabel')}
</b>,
<div
key="editGasSubTextFeeValue"
className="confirm-page-container-content__currency-container"
>
{renderHeartBeatIfNotInTest()}
<UserPreferencedCurrencyDisplay
key="editGasSubTextFeeAmount"
type={PRIMARY}
value={hexMaximumTransactionFee}
hideLabel={!useNativeCurrencyAsPrimaryCurrency}
/>
</div>,
])}
subTitle={
<>
{txData.dappSuggestedGasFees ? (
<Typography
variant={TYPOGRAPHY.H7}
fontStyle={FONT_STYLE.ITALIC}
color={COLORS.UI4}
>
{t('transactionDetailDappGasMoreInfo')}
</Typography>
) : (
''
)}
{supportsEIP1559 && (
<GasTiming
maxPriorityFeePerGas={hexWEIToDecGWEI(
maxPriorityFeePerGas ||
txData.txParams.maxPriorityFeePerGas,
)}
maxFeePerGas={hexWEIToDecGWEI(
maxFeePerGas || txData.txParams.maxFeePerGas,
)}
/>
)}
</>
}
/>
),
<TransactionDetailItem <TransactionDetailItem
key="total-item" key="total-item"
detailTitle={t('total')} detailTitle={t('total')}

@ -0,0 +1,136 @@
import React from 'react';
import PropTypes from 'prop-types';
import { COLORS } from '../../../helpers/constants/design-system';
import { PRIMARY, SECONDARY } from '../../../helpers/constants/common';
import { hexWEIToDecGWEI } from '../../../helpers/utils/conversions.util';
import { useI18nContext } from '../../../hooks/useI18nContext';
import Box from '../../../components/ui/box';
import Typography from '../../../components/ui/typography/typography';
import GasTiming from '../../../components/app/gas-timing/gas-timing.component';
import I18nValue from '../../../components/ui/i18n-value';
import InfoTooltip from '../../../components/ui/info-tooltip/info-tooltip';
import LoadingHeartBeat from '../../../components/ui/loading-heartbeat';
import TransactionDetailItem from '../../../components/app/transaction-detail-item/transaction-detail-item.component';
import UserPreferencedCurrencyDisplay from '../../../components/app/user-preferenced-currency-display';
const HeartBeat = () =>
process.env.IN_TEST === 'true' ? null : <LoadingHeartBeat />;
const GasDetailItem = ({
hexMaximumTransactionFee,
hexMinimumTransactionFee,
isMainnet,
maxFeePerGas,
maxPriorityFeePerGas,
supportsEIP1559,
txData,
useNativeCurrencyAsPrimaryCurrency,
}) => {
const t = useI18nContext();
return (
<TransactionDetailItem
key="gas-item"
detailTitle={
<Box display="flex">
<Box marginRight={1}>
<I18nValue messageKey="transactionDetailGasHeadingV2" />
</Box>
<span className="gas-details-item__estimate">
(<I18nValue messageKey="transactionDetailGasInfoV2" />)
</span>
<InfoTooltip
contentText={
<>
<Typography fontSize="12px">
{t('transactionDetailGasTooltipIntro', [
isMainnet ? t('networkNameEthereum') : '',
])}
</Typography>
<Typography fontSize="12px">
{t('transactionDetailGasTooltipExplanation')}
</Typography>
<Typography fontSize="12px">
<a
href="https://community.metamask.io/t/what-is-gas-why-do-transactions-take-so-long/3172"
target="_blank"
rel="noopener noreferrer"
>
{t('transactionDetailGasTooltipConversion')}
</a>
</Typography>
</>
}
position="top"
/>
</Box>
}
detailTitleColor={COLORS.BLACK}
detailText={
<div className="gas-details-item__currency-container">
<HeartBeat />
<UserPreferencedCurrencyDisplay
type={SECONDARY}
value={hexMinimumTransactionFee}
hideLabel={Boolean(useNativeCurrencyAsPrimaryCurrency)}
/>
</div>
}
detailTotal={
<div className="gas-details-item__currency-container">
<HeartBeat />
<UserPreferencedCurrencyDisplay
type={PRIMARY}
value={hexMinimumTransactionFee}
hideLabel={!useNativeCurrencyAsPrimaryCurrency}
/>
</div>
}
subText={t('editGasSubTextFee', [
<Box key="editGasSubTextFeeLabel" display="inline-flex">
<Box marginRight={1} className="gas-details-item__gasfee-label">
<I18nValue messageKey="editGasSubTextFeeLabel" />
</Box>
<div
key="editGasSubTextFeeValue"
className="gas-details-item__currency-container"
>
<HeartBeat />
<UserPreferencedCurrencyDisplay
key="editGasSubTextFeeAmount"
type={PRIMARY}
value={hexMaximumTransactionFee}
hideLabel={!useNativeCurrencyAsPrimaryCurrency}
/>
</div>
</Box>,
])}
subTitle={
supportsEIP1559 && (
<GasTiming
maxPriorityFeePerGas={hexWEIToDecGWEI(
maxPriorityFeePerGas || txData.txParams.maxPriorityFeePerGas,
)}
maxFeePerGas={hexWEIToDecGWEI(
maxFeePerGas || txData.txParams.maxFeePerGas,
)}
/>
)
}
/>
);
};
GasDetailItem.propTypes = {
hexMaximumTransactionFee: PropTypes.string,
hexMinimumTransactionFee: PropTypes.string,
isMainnet: PropTypes.bool,
maxFeePerGas: PropTypes.string,
maxPriorityFeePerGas: PropTypes.string,
supportsEIP1559: PropTypes.bool,
txData: PropTypes.object,
useNativeCurrencyAsPrimaryCurrency: PropTypes.bool,
};
export default GasDetailItem;

@ -0,0 +1,17 @@
.gas-details-item {
&__estimate {
font-weight: 400;
font-style: italic;
font-size: 12px;
color: $Grey-500;
line-height: inherit;
}
&__gasfee-label {
font-weight: bold;
}
&__currency-container {
position: relative;
}
}

@ -0,0 +1,41 @@
import React from 'react';
import { screen } from '@testing-library/react';
import { ETH } from '../../../helpers/constants/common';
import { renderWithProvider } from '../../../../test/jest';
import configureStore from '../../../store/store';
import GasDetailsItem from './gas-details-item';
const render = (props) => {
const store = configureStore({
metamask: {
nativeCurrency: ETH,
preferences: {
useNativeCurrencyAsPrimaryCurrency: true,
},
provider: {},
},
});
return renderWithProvider(<GasDetailsItem txData={{}} {...props} />, store);
};
describe('GasDetailsItem', () => {
it('should render label', () => {
render();
expect(screen.queryByText('Gas')).toBeInTheDocument();
expect(screen.queryByText('(estimated)')).toBeInTheDocument();
expect(screen.queryByText('Max fee:')).toBeInTheDocument();
});
it('should render gas fee details', () => {
render({
hexMinimumTransactionFee: '0x1ca62a4f7800',
hexMaximumTransactionFee: '0x290ee75e3d900',
});
expect(screen.queryAllByText('0.000031')).toHaveLength(2);
expect(screen.queryByText('ETH')).toBeInTheDocument();
expect(screen.queryByText('0.000722')).toBeInTheDocument();
});
});

@ -0,0 +1 @@
export { default } from './gas-details-item';

@ -5,6 +5,7 @@
@import 'confirm-approve/index'; @import 'confirm-approve/index';
@import 'confirm-decrypt-message/confirm-decrypt-message'; @import 'confirm-decrypt-message/confirm-decrypt-message';
@import 'confirm-encryption-public-key/confirm-encryption-public-key'; @import 'confirm-encryption-public-key/confirm-encryption-public-key';
@import 'confirm-transaction-base/gas-details-item/gas-details-item';
@import 'confirmation/confirmation'; @import 'confirmation/confirmation';
@import 'connected-sites/index'; @import 'connected-sites/index';
@import 'connected-accounts/index'; @import 'connected-accounts/index';

Loading…
Cancel
Save