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

Enable editing L2 gas on optimism (#18217)

This commit is contained in:
Jyoti Puri 2023-04-26 05:43:38 +05:30 committed by Dan J Miller
parent ece1e48200
commit 02b537c0c1
34 changed files with 407 additions and 176 deletions

View File

@ -1432,9 +1432,6 @@
"message": "Diese Gasgebühr wurde von $1 vorgeschlagen. Dies kann ein Problem mit Ihrer Transaktion verursachen. Bei Fragen wenden Sie sich bitte an $1.",
"description": "$1 represents the Dapp's origin"
},
"gasFee": {
"message": "Gasgebühr"
},
"gasLimit": {
"message": "Gaslimit"
},

View File

@ -1432,9 +1432,6 @@
"message": "Αυτό το τέλος συναλλαγής έχει προταθεί από το $1. Η παράκαμψη μπορεί να προκαλέσει πρόβλημα με τη συναλλαγή σας. Εάν έχετε απορίες, επικοινωνήστε με $1.",
"description": "$1 represents the Dapp's origin"
},
"gasFee": {
"message": "Τέλη Συναλλαγής"
},
"gasLimit": {
"message": "Όριο τέλους συναλλαγής"
},

View File

@ -1552,9 +1552,6 @@
"message": "This gas fee has been suggested by $1. Overriding this may cause a problem with your transaction. Please reach out to $1 if you have questions.",
"description": "$1 represents the Dapp's origin"
},
"gasFee": {
"message": "Gas fee"
},
"gasLimit": {
"message": "Gas limit"
},
@ -1960,6 +1957,9 @@
"lastSold": {
"message": "Last sold"
},
"layer1Fees": {
"message": "Layer 1 fees"
},
"learnCancelSpeeedup": {
"message": "Learn how to $1",
"description": "$1 is link to cancel or speed up transactions"

View File

@ -1432,9 +1432,6 @@
"message": "Esta tarifa de gas ha sido sugerida por $1. Anularla puede causar un problema con su transacción. Comuníquese con $1 si tiene preguntas.",
"description": "$1 represents the Dapp's origin"
},
"gasFee": {
"message": "Cuota de gas"
},
"gasLimit": {
"message": "Límite de gas"
},

View File

@ -1432,9 +1432,6 @@
"message": "Ce prix de carburant a été suggéré par $1. Si vous nen tenez pas compte, vous risquez de rencontrer des difficultés lors de votre transaction. Veuillez contacter $1 pour toute question.",
"description": "$1 represents the Dapp's origin"
},
"gasFee": {
"message": "Frais de transaction"
},
"gasLimit": {
"message": "Montant maximal des frais de transaction"
},

View File

@ -1432,9 +1432,6 @@
"message": "यह गैस शुल्क $1 द्वारा सुझाया गया है। इसे ओवरराइड करने से आपके लेन-देन में समस्या हो सकती है। यदि आपके पास कोई सवाल हैं तो कृपया $1 तक पहुंचें।",
"description": "$1 represents the Dapp's origin"
},
"gasFee": {
"message": "गैस शुल्क"
},
"gasLimit": {
"message": "गैस की सीमा"
},

View File

@ -1432,9 +1432,6 @@
"message": "Biaya gas ini telah disarankan oleh $1. Pengabaian dapat menyebabkan masalah pada transaksi Anda. Hubungi $1 jika ada pertanyaan.",
"description": "$1 represents the Dapp's origin"
},
"gasFee": {
"message": "Biaya gas"
},
"gasLimit": {
"message": "Batas gas"
},

View File

@ -1432,9 +1432,6 @@
"message": "このガス代は$1により提案されています。これを上書きすると、トランザクションに問題が発生する可能性があります。ご質問がございましたら、$1までお問い合わせください。",
"description": "$1 represents the Dapp's origin"
},
"gasFee": {
"message": "ガス代"
},
"gasLimit": {
"message": "ガスリミット"
},

View File

@ -1432,9 +1432,6 @@
"message": "$1에서 이 가스 요금을 제안했습니다. 이를 무시하면 거래에 문제가 발생할 수 있습니다. 질문이 있는 경우 $1에 문의하세요.",
"description": "$1 represents the Dapp's origin"
},
"gasFee": {
"message": "가스 수수료"
},
"gasLimit": {
"message": "가스 한도"
},

View File

@ -1432,9 +1432,6 @@
"message": "Essa taxa de gás foi sugerida por $1. Sua substituição pode causar um problema com a sua transação. Entre em contato com $1 se tiver perguntas.",
"description": "$1 represents the Dapp's origin"
},
"gasFee": {
"message": "Taxa de gás"
},
"gasLimit": {
"message": "Limite de gás"
},

View File

@ -1432,9 +1432,6 @@
"message": "Эта плата за газ была предложена $1. Ее переопредление может вызвать проблемы с вашей транзакцией. При наличии вопросов обратитесь к $1.",
"description": "$1 represents the Dapp's origin"
},
"gasFee": {
"message": "Плата за газ"
},
"gasLimit": {
"message": "Лимит газа"
},

View File

@ -1432,9 +1432,6 @@
"message": "Ang gas fee na ito ay iminungkahi ng $1. Ang pag-override dito ay maaaring magdulot ng problema sa iyong transaksyon. Mangyaring makipag-ugnayan sa $1 kung mayroon kang mga tanong.",
"description": "$1 represents the Dapp's origin"
},
"gasFee": {
"message": "Bayarin sa Gas"
},
"gasLimit": {
"message": "Limitasyon sa Gas"
},

View File

@ -1432,9 +1432,6 @@
"message": "Bu gaz ücreti $1 tarafından önerilmiştir. Bu değerin başka bir değerle değiştirilmesi işleminizle ilgili bir soruna neden olabilir. Sorularınız olursa lütfen $1 ile iletişime geçin.",
"description": "$1 represents the Dapp's origin"
},
"gasFee": {
"message": "Gaz ücreti"
},
"gasLimit": {
"message": "Gaz limiti"
},

View File

@ -1432,9 +1432,6 @@
"message": "Phí gas này đã được gợi ý bởi $1. Việc sửa đổi có thể khiến giao dịch của bạn gặp sự cố. Vui lòng liên hệ với $1 nếu bạn có câu hỏi.",
"description": "$1 represents the Dapp's origin"
},
"gasFee": {
"message": "Phí gas"
},
"gasLimit": {
"message": "Giới hạn gas"
},

View File

@ -1432,9 +1432,6 @@
"message": "这笔燃料费是由 $1 建议的。忽略它可能会导致您的交易出现问题。如果您有疑问,请联系 $1。",
"description": "$1 represents the Dapp's origin"
},
"gasFee": {
"message": "燃料费"
},
"gasLimit": {
"message": "燃料上限"
},

View File

@ -1,11 +1,9 @@
import React, { useContext } from 'react';
import PropTypes from 'prop-types';
import { useSelector } from 'react-redux';
import { I18nContext } from '../../../contexts/i18n';
import FormField from '../../ui/form-field';
import { getGasFormErrorText } from '../../../helpers/constants/gas';
import { getNetworkSupportsSettingGasFees } from '../../../selectors';
export default function AdvancedGasControls({
onManualChange,
@ -18,10 +16,6 @@ export default function AdvancedGasControls({
}) {
const t = useContext(I18nContext);
const networkSupportsSettingGasFees = useSelector(
getNetworkSupportsSettingGasFees,
);
return (
<div className="advanced-gas-controls">
<FormField
@ -38,7 +32,6 @@ export default function AdvancedGasControls({
tooltipText={t('editGasLimitTooltip')}
value={gasLimit}
allowDecimals={false}
disabled={!networkSupportsSettingGasFees}
numeric
/>
<>
@ -58,7 +51,6 @@ export default function AdvancedGasControls({
? getGasFormErrorText(gasErrors.gasPrice, t)
: null
}
disabled={!networkSupportsSettingGasFees}
/>
</>
</div>

View File

@ -20,12 +20,10 @@ export default class AdvancedGasInputs extends Component {
customGasLimitMessage: PropTypes.string,
minimumGasLimit: PropTypes.number,
customPriceIsExcessive: PropTypes.bool,
networkSupportsSettingGasFees: PropTypes.bool,
};
static defaultProps = {
customPriceIsExcessive: false,
networkSupportsSettingGasFees: true,
};
constructor(props) {
@ -203,14 +201,9 @@ export default class AdvancedGasInputs extends Component {
customGasLimitMessage,
minimumGasLimit,
customPriceIsExcessive,
networkSupportsSettingGasFees,
} = this.props;
const { gasPrice, gasLimit } = this.state;
if (!networkSupportsSettingGasFees) {
return null;
}
const { errorText: gasPriceErrorText, errorType: gasPriceErrorType } =
this.gasPriceError({
insufficientBalance,
@ -253,7 +246,6 @@ export default class AdvancedGasInputs extends Component {
onChange: this.onChangeGasPrice,
errorComponent: gasPriceErrorComponent,
errorType: gasPriceErrorType,
disabled: !networkSupportsSettingGasFees,
})}
{this.renderGasInput({
label: this.context.t('gasLimit'),
@ -264,7 +256,6 @@ export default class AdvancedGasInputs extends Component {
errorComponent: gasLimitErrorComponent,
customMessageComponent: gasLimitCustomMessageComponent,
errorType: gasLimitErrorType,
disabled: !networkSupportsSettingGasFees,
})}
</div>
);

View File

@ -1,5 +1,4 @@
import { connect } from 'react-redux';
import { getNetworkSupportsSettingGasFees } from '../../../selectors/selectors';
import { MIN_GAS_LIMIT_DEC } from '../../../pages/send/send.constants';
import {
decGWEIToHexWEI,
@ -20,12 +19,6 @@ function convertMinimumGasLimitForInputs(minimumGasLimit = MIN_GAS_LIMIT_DEC) {
return parseInt(minimumGasLimit, 10);
}
function mapStateToProps(state) {
return {
networkSupportsSettingGasFees: getNetworkSupportsSettingGasFees(state),
};
}
function mergeProps(stateProps, dispatchProps, ownProps) {
const {
customGasPrice,
@ -47,4 +40,4 @@ function mergeProps(stateProps, dispatchProps, ownProps) {
};
}
export default connect(mapStateToProps, null, mergeProps)(AdvancedGasInputs);
export default connect(undefined, null, mergeProps)(AdvancedGasInputs);

View File

@ -1,6 +1,8 @@
import React, { useContext } from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import { useSelector } from 'react-redux';
import Box from '../../ui/box/box';
import Button from '../../ui/button';
import EditGasFeeButton from '../edit-gas-fee-button/edit-gas-fee-button';
@ -16,10 +18,14 @@ import {
TextColor,
TextVariant,
} from '../../../helpers/constants/design-system';
import { PRIMARY, SECONDARY } from '../../../helpers/constants/common';
import { I18nContext } from '../../../contexts/i18n';
import { getPreferences } from '../../../selectors';
import { ConfirmGasDisplay } from '../confirm-gas-display';
import MultiLayerFeeMessage from '../multilayer-fee-message/multi-layer-fee-message';
import { formatCurrency } from '../../../helpers/utils/confirm-tx.util';
import TransactionDetailItem from '../transaction-detail-item/transaction-detail-item.component';
import UserPreferencedCurrencyDisplay from '../user-preferenced-currency-display';
export default function ApproveContentCard({
showHeader = true,
@ -37,6 +43,7 @@ export default function ApproveContentCard({
ethTransactionTotal,
nativeCurrency,
fullTxData,
hexMinimumTransactionFee,
hexTransactionTotal,
fiatTransactionTotal,
currentCurrency,
@ -48,6 +55,7 @@ export default function ApproveContentCard({
useCurrencyRateCheck,
}) {
const t = useContext(I18nContext);
const { useNativeCurrencyAsPrimaryCurrency } = useSelector(getPreferences);
return (
<Box
@ -126,20 +134,27 @@ export default function ApproveContentCard({
flexDirection={FLEX_DIRECTION.COLUMN}
className="approve-content-card-container__transaction-details-extra-content"
>
<Box
display={DISPLAY.FLEX}
justifyContent={JustifyContent.spaceBetween}
>
<Text
variant={TextVariant.bodySm}
fontWeight={FONT_WEIGHT.NORMAL}
color={TextColor.textMuted}
as="h6"
>
<span>{t('transactionDetailLayer2GasHeading')}</span>
{`${ethTransactionTotal} ${nativeCurrency}`}
</Text>
</Box>
<TransactionDetailItem
key="total-item"
detailTitle={t('transactionDetailLayer2GasHeading')}
detailTotal={
<UserPreferencedCurrencyDisplay
type={PRIMARY}
value={hexMinimumTransactionFee}
hideLabel={!useNativeCurrencyAsPrimaryCurrency}
numberOfDecimals={18}
/>
}
detailText={
<UserPreferencedCurrencyDisplay
type={SECONDARY}
value={hexMinimumTransactionFee}
hideLabel={Boolean(useNativeCurrencyAsPrimaryCurrency)}
/>
}
noBold
flexWidthValues
/>
<MultiLayerFeeMessage
transaction={fullTxData}
layer2fee={hexTransactionTotal}
@ -303,6 +318,10 @@ ApproveContentCard.propTypes = {
* Total sum of the transaction converted to hex value
*/
hexTransactionTotal: PropTypes.string,
/**
* Minimum transaction fee converted to hex value
*/
hexMinimumTransactionFee: PropTypes.string,
/**
* Total sum of the transaction in fiat currency
*/

View File

@ -4,6 +4,7 @@ import { useSelector } from 'react-redux';
import { useI18nContext } from '../../../../hooks/useI18nContext';
import {
getIsMainnet,
getIsMultiLayerFeeNetwork,
getPreferences,
getUnapprovedTransactions,
getUseCurrencyRateCheck,
@ -23,6 +24,8 @@ import {
TextColor,
} from '../../../../helpers/constants/design-system';
import { useDraftTransactionGasValues } from '../../../../hooks/useDraftTransactionGasValues';
import { getNativeCurrency } from '../../../../ducks/metamask/metamask';
import MultilayerFeeMessage from '../../multilayer-fee-message/multi-layer-fee-message';
const renderHeartBeatIfNotInTest = () =>
process.env.IN_TEST ? null : <LoadingHeartBeat />;
@ -32,8 +35,10 @@ const ConfirmLegacyGasDisplay = () => {
// state selectors
const isMainnet = useSelector(getIsMainnet);
const isMultiLayerFeeNetwork = useSelector(getIsMultiLayerFeeNetwork);
const useCurrencyRateCheck = useSelector(getUseCurrencyRateCheck);
const { useNativeCurrencyAsPrimaryCurrency } = useSelector(getPreferences);
const nativeCurrency = useSelector(getNativeCurrency);
const unapprovedTxs = useSelector(getUnapprovedTransactions);
const { transactionData } = useDraftTransactionGasValues();
const txData = useSelector((state) => txDataSelector(state));
@ -45,6 +50,38 @@ const ConfirmLegacyGasDisplay = () => {
(state) => transactionFeeSelector(state, transaction),
);
if (isMultiLayerFeeNetwork) {
return [
<TransactionDetailItem
key="total-item"
detailTitle={t('transactionDetailLayer2GasHeading')}
detailTotal={
<UserPreferencedCurrencyDisplay
type={PRIMARY}
value={hexMinimumTransactionFee}
hideLabel={!useNativeCurrencyAsPrimaryCurrency}
numberOfDecimals={18}
/>
}
detailText={
<UserPreferencedCurrencyDisplay
type={SECONDARY}
value={hexMinimumTransactionFee}
hideLabel={Boolean(useNativeCurrencyAsPrimaryCurrency)}
/>
}
noBold
flexWidthValues
/>,
<MultilayerFeeMessage
key="confirm-layer-1"
transaction={txData}
layer2fee={hexMinimumTransactionFee}
nativeCurrency={nativeCurrency}
/>,
];
}
return (
<TransactionDetailItem
key="legacy-gas-details"

View File

@ -2,45 +2,25 @@ import React from 'react';
import { screen, waitFor } from '@testing-library/react';
import mockState from '../../../../../test/data/mock-state.json';
import { CHAIN_IDS } from '../../../../../shared/constants/network';
import { renderWithProvider } from '../../../../../test/jest';
import configureStore from '../../../../store/store';
import ConfirmLegacyGasDisplay from './confirm-legacy-gas-display';
const render = ({ contextProps } = {}) => {
const store = configureStore({
...mockState,
...contextProps,
metamask: {
...mockState.metamask,
accounts: {
[mockState.metamask.selectedAddress]: {
address: mockState.metamask.selectedAddress,
balance: '0x1F4',
},
},
unapprovedTxs: {
8393540981007587: {
...mockState.metamask.unapprovedTxs[8393540981007587],
txParams: {
from: '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc',
to: '0xc42edfcc21ed14dda456aa0756c153f7985d8813',
value: '0x0',
gas: '0x5208',
gasPrice: '0x3b9aca00',
type: '0x0',
},
},
},
preferences: {
useNativeCurrencyAsPrimaryCurrency: true,
const mmState = {
...mockState,
metamask: {
...mockState.metamask,
accounts: {
[mockState.metamask.selectedAddress]: {
address: mockState.metamask.selectedAddress,
balance: '0x1F4',
},
},
confirmTransaction: {
txData: {
id: 8393540981007587,
status: 'unapproved',
chainId: '0x5',
unapprovedTxs: {
8393540981007587: {
...mockState.metamask.unapprovedTxs[8393540981007587],
txParams: {
from: '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc',
to: '0xc42edfcc21ed14dda456aa0756c153f7985d8813',
@ -51,7 +31,29 @@ const render = ({ contextProps } = {}) => {
},
},
},
});
preferences: {
useNativeCurrencyAsPrimaryCurrency: true,
},
},
confirmTransaction: {
txData: {
id: 8393540981007587,
status: 'unapproved',
chainId: '0x5',
txParams: {
from: '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc',
to: '0xc42edfcc21ed14dda456aa0756c153f7985d8813',
value: '0x0',
gas: '0x5208',
gasPrice: '0x3b9aca00',
type: '0x0',
},
},
},
};
const render = ({ contextProps, state = mmState } = {}) => {
const store = configureStore({ ...state, ...contextProps });
return renderWithProvider(<ConfirmLegacyGasDisplay />, store);
};
@ -107,4 +109,48 @@ describe('ConfirmLegacyGasDisplay', () => {
expect(screen.queryAllByTitle('0.000021 ETH').length).toBeGreaterThan(0);
});
});
it('should contain L1 L2 fee details for optimism', async () => {
mmState.metamask.provider.chainId = CHAIN_IDS.OPTIMISM;
mmState.confirmTransaction.txData.chainId = CHAIN_IDS.OPTIMISM;
const state = {
metamask: {
...mmState.metamask,
provider: {
...mmState.metamask.provider,
chainId: CHAIN_IDS.OPTIMISM,
},
},
confirmTransaction: {
...mmState.confirmTransaction,
txData: {
...mmState.confirmTransaction.txData,
chainId: CHAIN_IDS.OPTIMISM,
},
},
};
render(
{
send: {
currentTransactionUUID: '1d40b578-6184-4607-8513-762c24d0a19b',
draftTransactions: {
'1d40b578-6184-4607-8513-762c24d0a19b': {
gas: {
error: null,
gasLimit: '0x5208',
gasPrice: '0x3b9aca00',
gasTotal: '0x157c9fbb9a000',
wasManuallyEdited: false,
},
},
},
},
},
state,
);
await waitFor(() => {
expect(screen.queryByText('Layer 1 fees')).toBeInTheDocument();
expect(screen.queryByText('Layer 2 gas fee')).toBeInTheDocument();
});
});
});

View File

@ -0,0 +1,98 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Multi layer fee message should match snapshot 1`] = `
<div>
<div
class="multi-layer-fee-message"
>
<div
class="transaction-detail-item"
>
<div
class="transaction-detail-item__row"
>
<h6
class="box box--margin-top-1 box--margin-bottom-1 box--display-flex box--flex-direction-row box--flex-wrap-nowrap box--align-items-center typography typography--h6 typography--weight-bold typography--style-normal typography--color-text-default"
>
Layer 1 fees
</h6>
<div
class="transaction-detail-item__detail-values"
>
<h6
class="box box--margin-top-1 box--margin-bottom-1 box--flex-direction-row typography typography--h6 typography--weight-normal typography--style-normal typography--color-text-alternative"
>
Unknown
</h6>
<h6
class="box box--margin-top-1 box--margin-bottom-1 box--margin-left-1 box--flex-direction-row box--text-align-right typography typography--h6 typography--weight-bold typography--style-normal typography--color-text-default"
>
Unknown
</h6>
</div>
</div>
<div
class="transaction-detail-item__row"
>
<h6
class="box box--margin-top-1 box--margin-bottom-1 box--flex-direction-row typography typography--h7 typography--weight-normal typography--style-normal typography--color-text-alternative"
/>
<h6
class="box box--margin-top-1 box--margin-bottom-1 box--flex-direction-row typography transaction-detail-item__row-subText typography--h7 typography--weight-normal typography--style-normal typography--align-end typography--color-text-alternative"
/>
</div>
</div>
<div
class="transaction-detail-item"
>
<div
class="transaction-detail-item__row"
>
<h6
class="box box--margin-top-1 box--margin-bottom-1 box--display-flex box--flex-direction-row box--flex-wrap-nowrap box--align-items-center typography typography--h6 typography--weight-bold typography--style-normal typography--color-text-default"
>
Total
</h6>
<div
class="transaction-detail-item__detail-values"
>
<h6
class="box box--margin-top-1 box--margin-bottom-1 box--flex-direction-row typography typography--h6 typography--weight-normal typography--style-normal typography--color-text-alternative"
>
<div
class="currency-display-component"
title="$0.56"
>
<span
class="currency-display-component__prefix"
/>
<span
class="currency-display-component__text"
>
$0.56
</span>
</div>
</h6>
<h6
class="box box--margin-top-1 box--margin-bottom-1 box--margin-left-1 box--flex-direction-row box--text-align-right typography typography--h6 typography--weight-bold typography--style-normal typography--color-text-default"
>
0.001000021000 ETH
</h6>
</div>
</div>
<div
class="transaction-detail-item__row"
>
<h6
class="box box--margin-top-1 box--margin-bottom-1 box--flex-direction-row typography typography--h7 typography--weight-normal typography--style-normal typography--color-text-alternative"
>
Amount + fees
</h6>
<h6
class="box box--margin-top-1 box--margin-bottom-1 box--flex-direction-row typography transaction-detail-item__row-subText typography--h7 typography--weight-normal typography--style-normal typography--align-end typography--color-text-alternative"
/>
</div>
</div>
</div>
</div>
`;

View File

@ -17,36 +17,17 @@ export default function MultilayerFeeMessage({
plainStyle,
}) {
const t = useContext(I18nContext);
const [fetchedLayer1Total, setLayer1Total] = useState(null);
let layer1Total = 'unknown';
let layer1TotalBN;
if (fetchedLayer1Total !== null) {
layer1TotalBN = new Numeric(fetchedLayer1Total, 16, EtherDenomination.WEI);
layer1Total = `${layer1TotalBN
.toDenomination(EtherDenomination.ETH)
.toFixed(12)} ${nativeCurrency}`;
}
const feeTotal = sumHexes(layer2fee || '0x0', fetchedLayer1Total || '0x0');
const totalInWeiHex = sumHexes(
feeTotal || '0x0',
transaction.txParams.value || '0x0',
);
const totalBN = new Numeric(totalInWeiHex, 16, EtherDenomination.WEI);
const totalInEth = `${totalBN
.toDenomination(EtherDenomination.ETH)
.toFixed(12)} ${nativeCurrency}`;
useEffect(() => {
if (!transaction?.txParams) {
return;
}
const getEstimatedL1Fee = async () => {
try {
const result = await fetchEstimatedL1Fee(
transaction.chainId,
transaction.txParams,
transaction?.chainId,
transaction,
);
setLayer1Total(result);
} catch (e) {
@ -57,15 +38,44 @@ export default function MultilayerFeeMessage({
getEstimatedL1Fee();
}, [transaction]);
const feeTotalInFiat = (
<UserPreferencedCurrencyDisplay
type={SECONDARY}
value={feeTotal}
showFiat
hideLabel
/>
if (!transaction?.txParams) {
return null;
}
let layer1Total = t('unknown');
let feeTotalInFiat = t('unknown');
if (fetchedLayer1Total !== null) {
const layer1TotalBN = new Numeric(
fetchedLayer1Total,
16,
EtherDenomination.WEI,
);
layer1Total = `${layer1TotalBN
.toDenomination(EtherDenomination.ETH)
.toFixed(12)} ${nativeCurrency}`;
feeTotalInFiat = (
<UserPreferencedCurrencyDisplay
type={SECONDARY}
value={fetchedLayer1Total}
showFiat
hideLabel
/>
);
}
const totalInWeiHex = sumHexes(
layer2fee || '0x0',
fetchedLayer1Total || '0x0',
transaction?.txParams?.value || '0x0',
);
const totalBN = new Numeric(totalInWeiHex, 16, EtherDenomination.WEI);
const totalInEth = `${totalBN
.toDenomination(EtherDenomination.ETH)
.toFixed(12)} ${nativeCurrency}`;
const totalInFiat = (
<UserPreferencedCurrencyDisplay
type={SECONDARY}
@ -79,7 +89,7 @@ export default function MultilayerFeeMessage({
<div className="multi-layer-fee-message">
<TransactionDetailItem
key="total-item-gas-fee"
detailTitle={t('gasFee')}
detailTitle={t('layer1Fees')}
detailTotal={layer1Total}
detailText={feeTotalInFiat}
noBold={plainStyle}

View File

@ -0,0 +1,46 @@
import React from 'react';
import mockState from '../../../../test/data/mock-state.json';
import { renderWithProvider } from '../../../../test/lib/render-helpers';
import configureStore from '../../../store/store';
import MultilayerFeeMessage from './multi-layer-fee-message';
jest.mock('../../../helpers/utils/optimism/fetchEstimatedL1Fee', () => '0x5');
describe('Multi layer fee message', () => {
const store = configureStore(mockState);
it('should match snapshot', () => {
const { container } = renderWithProvider(
<MultilayerFeeMessage
transaction={{
txParams: {
value: '0x38d7ea4c68000',
},
}}
layer2fee="0x4e3b29200"
nativeCurrency="ETH"
/>,
store,
);
expect(container).toMatchSnapshot();
});
it('should containe fee values', () => {
const { getByText } = renderWithProvider(
<MultilayerFeeMessage
transaction={{
txParams: {
value: '0x38d7ea4c68000',
},
}}
layer2fee="0x4e3b29200"
nativeCurrency="ETH"
/>,
store,
);
expect(getByText('Layer 1 fees')).toBeInTheDocument();
expect(getByText('Amount + fees')).toBeInTheDocument();
expect(getByText('0.001000021000 ETH')).toBeInTheDocument();
});
});

View File

@ -33,6 +33,9 @@ import {
} from '../../../components/component-library/icon/deprecated';
import { ButtonIcon } from '../../../components/component-library/button-icon/deprecated';
import { Text } from '../../../components/component-library';
import TransactionDetailItem from '../../../components/app/transaction-detail-item/transaction-detail-item.component';
import UserPreferencedCurrencyDisplay from '../../../components/app/user-preferenced-currency-display';
import { PRIMARY, SECONDARY } from '../../../helpers/constants/common';
import { ConfirmGasDisplay } from '../../../components/app/confirm-gas-display';
export default class ConfirmApproveContent extends Component {
@ -65,6 +68,7 @@ export default class ConfirmApproveContent extends Component {
rpcPrefs: PropTypes.object,
isContract: PropTypes.bool,
hexTransactionTotal: PropTypes.string,
hexMinimumTransactionFee: PropTypes.string,
isMultiLayerFeeNetwork: PropTypes.bool,
supportsEIP1559: PropTypes.bool,
assetName: PropTypes.string,
@ -77,6 +81,7 @@ export default class ConfirmApproveContent extends Component {
setUserAcknowledgedGasMissing: PropTypes.func,
renderSimulationFailureWarning: PropTypes.bool,
useCurrencyRateCheck: PropTypes.bool,
useNativeCurrencyAsPrimaryCurrency: PropTypes.bool,
};
state = {
@ -157,12 +162,14 @@ export default class ConfirmApproveContent extends Component {
ethTransactionTotal,
fiatTransactionTotal,
hexTransactionTotal,
hexMinimumTransactionFee,
txData,
isMultiLayerFeeNetwork,
supportsEIP1559,
userAcknowledgedGasMissing,
renderSimulationFailureWarning,
useCurrencyRateCheck,
useNativeCurrencyAsPrimaryCurrency,
} = this.props;
if (
!isMultiLayerFeeNetwork &&
@ -179,10 +186,27 @@ export default class ConfirmApproveContent extends Component {
<div className="confirm-approve-content__transaction-details-content">
{isMultiLayerFeeNetwork ? (
<div className="confirm-approve-content__transaction-details-extra-content">
<div className="confirm-approve-content__transaction-details-content__labelled-fee">
<span>{t('transactionDetailLayer2GasHeading')}</span>
{`${ethTransactionTotal} ${nativeCurrency}`}
</div>
<TransactionDetailItem
key="total-item"
detailTitle={t('transactionDetailLayer2GasHeading')}
detailTotal={
<UserPreferencedCurrencyDisplay
type={PRIMARY}
value={hexMinimumTransactionFee}
hideLabel={!useNativeCurrencyAsPrimaryCurrency}
numberOfDecimals={18}
/>
}
detailText={
<UserPreferencedCurrencyDisplay
type={SECONDARY}
value={hexMinimumTransactionFee}
hideLabel={Boolean(useNativeCurrencyAsPrimaryCurrency)}
/>
}
noBold
flexWidthValues
/>
<MultiLayerFeeMessage
transaction={txData}
layer2fee={hexTransactionTotal}

View File

@ -28,6 +28,7 @@ import {
getIsMultiLayerFeeNetwork,
checkNetworkAndAccountSupports1559,
getUseCurrencyRateCheck,
getPreferences,
} from '../../selectors';
import { useApproveTransaction } from '../../hooks/useApproveTransaction';
import { useSimulationFailureWarning } from '../../hooks/useSimulationFailureWarning';
@ -62,6 +63,7 @@ export default function ConfirmApprove({
ethTransactionTotal,
fiatTransactionTotal,
hexTransactionTotal,
hexMinimumTransactionFee,
isSetApproveForAll,
}) {
const dispatch = useDispatch();
@ -83,6 +85,7 @@ export default function ConfirmApprove({
isAddressLedgerByFromAddress(userAddress),
);
const useCurrencyRateCheck = useSelector(getUseCurrencyRateCheck);
const { useNativeCurrencyAsPrimaryCurrency } = useSelector(getPreferences);
const [customPermissionAmount, setCustomPermissionAmount] = useState('');
const [submitWarning, setSubmitWarning] = useState('');
const [isContract, setIsContract] = useState(false);
@ -187,6 +190,7 @@ export default function ConfirmApprove({
ethTransactionTotal={ethTransactionTotal}
fiatTransactionTotal={fiatTransactionTotal}
hexTransactionTotal={hexTransactionTotal}
hexMinimumTransactionFee={hexMinimumTransactionFee}
txData={transaction}
isMultiLayerFeeNetwork={isMultiLayerFeeNetwork}
supportsEIP1559={supportsEIP1559}
@ -253,6 +257,7 @@ export default function ConfirmApprove({
ethTransactionTotal={ethTransactionTotal}
fiatTransactionTotal={fiatTransactionTotal}
hexTransactionTotal={hexTransactionTotal}
hexMinimumTransactionFee={hexMinimumTransactionFee}
useNonceField={useNonceField}
nextNonce={nextNonce}
customNonceValue={customNonceValue}
@ -292,6 +297,9 @@ export default function ConfirmApprove({
isMultiLayerFeeNetwork={isMultiLayerFeeNetwork}
supportsEIP1559={supportsEIP1559}
useCurrencyRateCheck={useCurrencyRateCheck}
useNativeCurrencyAsPrimaryCurrency={
useNativeCurrencyAsPrimaryCurrency
}
/>
{showCustomizeGasPopover && !supportsEIP1559 && (
<EditGasPopover
@ -339,5 +347,6 @@ ConfirmApprove.propTypes = {
ethTransactionTotal: PropTypes.string,
fiatTransactionTotal: PropTypes.string,
hexTransactionTotal: PropTypes.string,
hexMinimumTransactionFee: PropTypes.string,
isSetApproveForAll: PropTypes.bool,
};

View File

@ -30,7 +30,6 @@ import TransactionDetail from '../../components/app/transaction-detail/transacti
import TransactionDetailItem from '../../components/app/transaction-detail-item/transaction-detail-item.component';
import LoadingHeartBeat from '../../components/ui/loading-heartbeat';
import LedgerInstructionField from '../../components/app/ledger-instruction-field';
import MultiLayerFeeMessage from '../../components/app/multilayer-fee-message';
import {
disconnectGasFeeEstimatePoller,
getGasFeeEstimatesAndStartPolling,
@ -70,7 +69,6 @@ export default class ConfirmTransactionBase extends Component {
fromAddress: PropTypes.string,
fromName: PropTypes.string,
hexTransactionAmount: PropTypes.string,
hexMinimumTransactionFee: PropTypes.string,
hexMaximumTransactionFee: PropTypes.string,
hexTransactionTotal: PropTypes.string,
methodData: PropTypes.object,
@ -295,7 +293,6 @@ export default class ConfirmTransactionBase extends Component {
const {
primaryTotalTextOverride,
secondaryTotalTextOverride,
hexMinimumTransactionFee,
hexMaximumTransactionFee,
hexTransactionTotal,
useNonceField,
@ -449,24 +446,15 @@ export default class ConfirmTransactionBase extends Component {
disabled={isDisabled()}
userAcknowledgedGasMissing={userAcknowledgedGasMissing}
onEdit={
renderSimulationFailureWarning || isMultiLayerFeeNetwork
? null
: () => this.handleEditGas()
renderSimulationFailureWarning ? null : () => this.handleEditGas()
}
rows={[
renderSimulationFailureWarning && simulationFailureWarning(),
!renderSimulationFailureWarning && !isMultiLayerFeeNetwork && (
!renderSimulationFailureWarning && (
<ConfirmGasDisplay
userAcknowledgedGasMissing={userAcknowledgedGasMissing}
/>
),
!renderSimulationFailureWarning && isMultiLayerFeeNetwork && (
<MultiLayerFeeMessage
transaction={txData}
layer2fee={hexMinimumTransactionFee}
nativeCurrency={nativeCurrency}
/>
),
!isMultiLayerFeeNetwork && (
<TransactionDetailItem
key="total-item"

View File

@ -152,7 +152,6 @@ const mapStateToProps = (state, ownProps) => {
const {
hexTransactionAmount,
hexMinimumTransactionFee,
hexMaximumTransactionFee,
hexTransactionTotal,
gasEstimationObject,
@ -206,7 +205,6 @@ const mapStateToProps = (state, ownProps) => {
toName,
toNickname,
hexTransactionAmount,
hexMinimumTransactionFee,
hexMaximumTransactionFee,
hexTransactionTotal,
txData: fullTxData,

View File

@ -146,12 +146,24 @@ const baseStore = {
};
describe('Confirm Transaction Base', () => {
const store = configureMockStore(middleware)(baseStore);
it('should match snapshot', () => {
const store = configureMockStore(middleware)(baseStore);
const { container } = renderWithProvider(
<ConfirmTransactionBase actionKey="confirm" />,
store,
);
expect(container).toMatchSnapshot();
});
it('should contain L1 L2 fee details for optimism', () => {
baseStore.metamask.provider.chainId = CHAIN_IDS.OPTIMISM;
baseStore.confirmTransaction.txData.chainId = CHAIN_IDS.OPTIMISM;
const store = configureMockStore(middleware)(baseStore);
const { getByText } = renderWithProvider(
<ConfirmTransactionBase actionKey="confirm" />,
store,
);
expect(getByText('Layer 1 fees')).toBeInTheDocument();
expect(getByText('Layer 2 gas fee')).toBeInTheDocument();
});
});

View File

@ -46,6 +46,7 @@ export default function ConfirmTokenTransactionSwitch({ transaction }) {
fiatTransactionTotal,
hexTransactionTotal,
hexMaximumTransactionFee,
hexMinimumTransactionFee,
} = useSelector((state) => transactionFeeSelector(state, transaction));
return (
@ -70,6 +71,7 @@ export default function ConfirmTokenTransactionSwitch({ transaction }) {
ethTransactionTotal={ethTransactionTotal}
fiatTransactionTotal={fiatTransactionTotal}
hexTransactionTotal={hexTransactionTotal}
hexMinimumTransactionFee={hexMinimumTransactionFee}
/>
)}
/>
@ -94,6 +96,7 @@ export default function ConfirmTokenTransactionSwitch({ transaction }) {
ethTransactionTotal={ethTransactionTotal}
fiatTransactionTotal={fiatTransactionTotal}
hexTransactionTotal={hexTransactionTotal}
hexMinimumTransactionFee={hexMinimumTransactionFee}
/>
)}
/>

View File

@ -34,6 +34,7 @@ export default class SendContent extends Component {
recipient: PropTypes.object,
acknowledgeRecipientWarning: PropTypes.func,
recipientWarningAcknowledged: PropTypes.bool,
isMultiLayerFeeNetwork: PropTypes.bool,
};
render() {
@ -48,6 +49,7 @@ export default class SendContent extends Component {
assetError,
recipient,
recipientWarningAcknowledged,
isMultiLayerFeeNetwork,
} = this.props;
let gasError;
@ -80,7 +82,7 @@ export default class SendContent extends Component {
<SendAmountRow />
{networkOrAccountNotSupports1559 ? <SendGasRow /> : null}
{showHexData ? <SendHexDataRow /> : null}
<GasDisplay gasError={gasError} />
{!isMultiLayerFeeNetwork && <GasDisplay gasError={gasError} />}
</div>
</PageContainerContent>
);

View File

@ -3,6 +3,7 @@ import {
getIsEthGasPriceFetched,
getNoGasPriceFetched,
checkNetworkOrAccountNotSupports1559,
getIsMultiLayerFeeNetwork,
} from '../../../selectors';
import {
getIsBalanceInsufficient,
@ -29,6 +30,7 @@ function mapStateToProps(state) {
assetError: getAssetError(state),
recipient,
recipientWarningAcknowledged,
isMultiLayerFeeNetwork: getIsMultiLayerFeeNetwork(state),
};
}

View File

@ -81,6 +81,7 @@ export default function TokenAllowance({
ethTransactionTotal,
fiatTransactionTotal,
hexTransactionTotal,
hexMinimumTransactionFee,
txData,
isMultiLayerFeeNetwork,
supportsEIP1559,
@ -463,6 +464,7 @@ export default function TokenAllowance({
userAcknowledgedGasMissing={userAcknowledgedGasMissing}
renderSimulationFailureWarning={renderSimulationFailureWarning}
hexTransactionTotal={hexTransactionTotal}
hexMinimumTransactionFee={hexMinimumTransactionFee}
fiatTransactionTotal={fiatTransactionTotal}
currentCurrency={currentCurrency}
useCurrencyRateCheck={useCurrencyRateCheck}
@ -514,6 +516,7 @@ export default function TokenAllowance({
isApprovalOrRejection={isApprovalOrRejection}
data={customTxParamsData || data}
useCurrencyRateCheck={useCurrencyRateCheck}
hexMinimumTransactionFee={hexMinimumTransactionFee}
/>
</Box>
</Box>
@ -595,6 +598,10 @@ TokenAllowance.propTypes = {
* Total sum of the transaction converted to hex value
*/
hexTransactionTotal: PropTypes.string,
/**
* Minimum transaction fee converted to hex value
*/
hexMinimumTransactionFee: PropTypes.string,
/**
* Current transaction
*/

View File

@ -839,7 +839,7 @@ export const getFullTxData = createDeepEqualSelector(
(txData, transaction, customTxParamsData) => {
let fullTxData = { ...txData, ...transaction };
if (transaction && transaction.simulationFails) {
txData.simulationFails = transaction.simulationFails;
fullTxData.simulationFails = { ...transaction.simulationFails };
}
if (customTxParamsData) {
fullTxData = {
@ -1215,10 +1215,6 @@ export function getIsOptimism(state) {
);
}
export function getNetworkSupportsSettingGasFees(state) {
return !getIsOptimism(state);
}
export function getIsMultiLayerFeeNetwork(state) {
return getIsOptimism(state);
}