Add support for EIP1559 transactions to transaction breakdown (#11622)

* Add support for EIP1559 transactions to transaction breakdown

* Use userPreferencedCurrencyDisplay for effective gas price in transaction breakdown

* Hide eip1559 gas properties in transaction breakdown on non-1559 networks

* Add comment explaining gasPrice and effectiveGasPrice usage in transaction breakdown container.
feature/default_network_editable
Dan J Miller 3 years ago committed by GitHub
parent 52bac60a22
commit aaa15cbe03
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 13
      app/scripts/controllers/transactions/index.js
  2. 6
      app/scripts/controllers/transactions/pending-tx-tracker.js
  3. 53
      ui/components/app/transaction-breakdown/transaction-breakdown.component.js
  4. 32
      ui/components/app/transaction-breakdown/transaction-breakdown.container.js
  5. 9
      ui/helpers/utils/conversions.util.js

@ -812,7 +812,7 @@ export default class TransactionController extends EventEmitter {
* @param {number} txId - The tx's ID * @param {number} txId - The tx's ID
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
async confirmTransaction(txId, txReceipt) { async confirmTransaction(txId, txReceipt, baseFeePerGas) {
// get the txReceipt before marking the transaction confirmed // get the txReceipt before marking the transaction confirmed
// to ensure the receipt is gotten before the ui revives the tx // to ensure the receipt is gotten before the ui revives the tx
const txMeta = this.txStateManager.getTransaction(txId); const txMeta = this.txStateManager.getTransaction(txId);
@ -833,6 +833,11 @@ export default class TransactionController extends EventEmitter {
...txReceipt, ...txReceipt,
gasUsed, gasUsed,
}; };
if (baseFeePerGas) {
txMeta.baseFeePerGas = baseFeePerGas;
}
this.txStateManager.setTxStatusConfirmed(txId); this.txStateManager.setTxStatusConfirmed(txId);
this._markNonceDuplicatesDropped(txId); this._markNonceDuplicatesDropped(txId);
@ -1011,8 +1016,10 @@ export default class TransactionController extends EventEmitter {
this.pendingTxTracker.on('tx:failed', (txId, error) => { this.pendingTxTracker.on('tx:failed', (txId, error) => {
this._failTransaction(txId, error); this._failTransaction(txId, error);
}); });
this.pendingTxTracker.on('tx:confirmed', (txId, transactionReceipt) => this.pendingTxTracker.on(
this.confirmTransaction(txId, transactionReceipt), 'tx:confirmed',
(txId, transactionReceipt, baseFeePerGas) =>
this.confirmTransaction(txId, transactionReceipt, baseFeePerGas),
); );
this.pendingTxTracker.on('tx:dropped', (txId) => { this.pendingTxTracker.on('tx:dropped', (txId) => {
this._dropTransaction(txId); this._dropTransaction(txId);

@ -193,7 +193,11 @@ export default class PendingTransactionTracker extends EventEmitter {
try { try {
const transactionReceipt = await this.query.getTransactionReceipt(txHash); const transactionReceipt = await this.query.getTransactionReceipt(txHash);
if (transactionReceipt?.blockNumber) { if (transactionReceipt?.blockNumber) {
this.emit('tx:confirmed', txId, transactionReceipt); const { baseFeePerGas } = await this.query.getBlockByHash(
transactionReceipt?.blockHash,
false,
);
this.emit('tx:confirmed', txId, transactionReceipt, baseFeePerGas);
return; return;
} }
} catch (err) { } catch (err) {

@ -4,7 +4,12 @@ import classnames from 'classnames';
import CurrencyDisplay from '../../ui/currency-display'; import CurrencyDisplay from '../../ui/currency-display';
import UserPreferencedCurrencyDisplay from '../user-preferenced-currency-display'; import UserPreferencedCurrencyDisplay from '../user-preferenced-currency-display';
import HexToDecimal from '../../ui/hex-to-decimal'; import HexToDecimal from '../../ui/hex-to-decimal';
import { GWEI, PRIMARY, SECONDARY } from '../../../helpers/constants/common'; import {
GWEI,
PRIMARY,
SECONDARY,
ETH,
} from '../../../helpers/constants/common';
import TransactionBreakdownRow from './transaction-breakdown-row'; import TransactionBreakdownRow from './transaction-breakdown-row';
export default class TransactionBreakdown extends PureComponent { export default class TransactionBreakdown extends PureComponent {
@ -25,7 +30,8 @@ export default class TransactionBreakdown extends PureComponent {
totalInHex: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), totalInHex: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
baseFee: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), baseFee: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
priorityFee: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), priorityFee: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
effectiveGasPrice: PropTypes.number, hexGasTotal: PropTypes.string,
supportsEIP1559: PropTypes.bool,
}; };
static defaultProps = { static defaultProps = {
@ -47,7 +53,8 @@ export default class TransactionBreakdown extends PureComponent {
isTokenApprove, isTokenApprove,
baseFee, baseFee,
priorityFee, priorityFee,
effectiveGasPrice, hexGasTotal,
supportsEIP1559,
} = this.props; } = this.props;
return ( return (
<div className={classnames('transaction-breakdown', className)}> <div className={classnames('transaction-breakdown', className)}>
@ -91,7 +98,7 @@ export default class TransactionBreakdown extends PureComponent {
/> />
</TransactionBreakdownRow> </TransactionBreakdownRow>
)} )}
{process.env.SHOW_EIP_1559_UI && ( {process.env.SHOW_EIP_1559_UI && supportsEIP1559 && (
<TransactionBreakdownRow title={t('transactionHistoryBaseFee')}> <TransactionBreakdownRow title={t('transactionHistoryBaseFee')}>
{typeof baseFee === 'undefined' ? ( {typeof baseFee === 'undefined' ? (
'?' '?'
@ -102,12 +109,13 @@ export default class TransactionBreakdown extends PureComponent {
currency={nativeCurrency} currency={nativeCurrency}
denomination={GWEI} denomination={GWEI}
value={baseFee} value={baseFee}
numberOfDecimals={10}
hideLabel hideLabel
/> />
)} )}
</TransactionBreakdownRow> </TransactionBreakdownRow>
)} )}
{process.env.SHOW_EIP_1559_UI && ( {process.env.SHOW_EIP_1559_UI && supportsEIP1559 && (
<TransactionBreakdownRow title={t('transactionHistoryPriorityFee')}> <TransactionBreakdownRow title={t('transactionHistoryPriorityFee')}>
{typeof priorityFee === 'undefined' ? ( {typeof priorityFee === 'undefined' ? (
'?' '?'
@ -118,12 +126,13 @@ export default class TransactionBreakdown extends PureComponent {
currency={nativeCurrency} currency={nativeCurrency}
denomination={GWEI} denomination={GWEI}
value={priorityFee} value={priorityFee}
numberOfDecimals={10}
hideLabel hideLabel
/> />
)} )}
</TransactionBreakdownRow> </TransactionBreakdownRow>
)} )}
{!process.env.SHOW_EIP_1559_UI && ( {(!process.env.SHOW_EIP_1559_UI || !supportsEIP1559) && (
<TransactionBreakdownRow title={t('advancedGasPriceTitle')}> <TransactionBreakdownRow title={t('advancedGasPriceTitle')}>
{typeof gasPrice === 'undefined' ? ( {typeof gasPrice === 'undefined' ? (
'?' '?'
@ -139,22 +148,28 @@ export default class TransactionBreakdown extends PureComponent {
)} )}
</TransactionBreakdownRow> </TransactionBreakdownRow>
)} )}
<TransactionBreakdownRow {process.env.SHOW_EIP_1559_UI && supportsEIP1559 && (
title={t('transactionHistoryEffectiveGasPrice')} <TransactionBreakdownRow
> title={t('transactionHistoryEffectiveGasPrice')}
<UserPreferencedCurrencyDisplay >
className="transaction-breakdown__value transaction-breakdown__value--effective-gas-price"
type={PRIMARY}
value={effectiveGasPrice}
/>
{showFiat && (
<UserPreferencedCurrencyDisplay <UserPreferencedCurrencyDisplay
className="transaction-breakdown__value" className="transaction-breakdown__value"
type={SECONDARY} data-testid="transaction-breakdown__effective-gas-price"
value={effectiveGasPrice} currency={nativeCurrency}
denomination={ETH}
numberOfDecimals={6}
value={hexGasTotal}
type={PRIMARY}
/> />
)} {showFiat && (
</TransactionBreakdownRow> <UserPreferencedCurrencyDisplay
className="transaction-breakdown__value"
type={SECONDARY}
value={hexGasTotal}
/>
)}
</TransactionBreakdownRow>
)}
<TransactionBreakdownRow title={t('total')}> <TransactionBreakdownRow title={t('total')}>
<UserPreferencedCurrencyDisplay <UserPreferencedCurrencyDisplay
className="transaction-breakdown__value transaction-breakdown__value--eth-total" className="transaction-breakdown__value transaction-breakdown__value--eth-total"

@ -1,7 +1,11 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { getShouldShowFiat } from '../../../selectors'; import { getShouldShowFiat } from '../../../selectors';
import { getNativeCurrency } from '../../../ducks/metamask/metamask'; import {
getNativeCurrency,
isEIP1559Network,
} from '../../../ducks/metamask/metamask';
import { getHexGasTotal } from '../../../helpers/utils/confirm-tx.util'; import { getHexGasTotal } from '../../../helpers/utils/confirm-tx.util';
import { subtractHexes } from '../../../helpers/utils/conversions.util';
import { sumHexes } from '../../../helpers/utils/transactions.util'; import { sumHexes } from '../../../helpers/utils/transactions.util';
import TransactionBreakdown from './transaction-breakdown.component'; import TransactionBreakdown from './transaction-breakdown.component';
@ -9,15 +13,32 @@ const mapStateToProps = (state, ownProps) => {
const { transaction, isTokenApprove } = ownProps; const { transaction, isTokenApprove } = ownProps;
const { const {
txParams: { gas, gasPrice, value } = {}, txParams: { gas, gasPrice, value } = {},
txReceipt: { gasUsed } = {}, txReceipt: { gasUsed, effectiveGasPrice } = {},
baseFeePerGas,
} = transaction; } = transaction;
const gasLimit = typeof gasUsed === 'string' ? gasUsed : gas; const gasLimit = typeof gasUsed === 'string' ? gasUsed : gas;
const priorityFee =
effectiveGasPrice &&
baseFeePerGas &&
subtractHexes(effectiveGasPrice, baseFeePerGas);
// To calculate the total cost of the transaction, we use gasPrice if it is in the txParam,
// which will only be the case on non-EIP1559 networks. If it is not in the params, we can
// use the effectiveGasPrice from the receipt, which will ultimately represent to true cost
// of the transaction. Either of these are used the same way with gasLimit to calculate total
// cost. effectiveGasPrice will be available on the txReciept for all EIP1559 networks
const usedGasPrice = gasPrice || effectiveGasPrice;
const hexGasTotal = const hexGasTotal =
(gasLimit && gasPrice && getHexGasTotal({ gasLimit, gasPrice })) || '0x0'; (gasLimit &&
usedGasPrice &&
getHexGasTotal({ gasLimit, gasPrice: usedGasPrice })) ||
'0x0';
const totalInHex = sumHexes(hexGasTotal, value); const totalInHex = sumHexes(hexGasTotal, value);
const supportsEIP1559 = isEIP1559Network(state);
return { return {
nativeCurrency: getNativeCurrency(state), nativeCurrency: getNativeCurrency(state),
showFiat: getShouldShowFiat(state), showFiat: getShouldShowFiat(state),
@ -26,6 +47,11 @@ const mapStateToProps = (state, ownProps) => {
gasPrice, gasPrice,
gasUsed, gasUsed,
isTokenApprove, isTokenApprove,
effectiveGasPrice,
hexGasTotal,
priorityFee,
baseFee: baseFeePerGas,
supportsEIP1559,
}; };
}; };

@ -171,6 +171,15 @@ export function addHexes(aHexWEI, bHexWEI) {
}); });
} }
export function subtractHexes(aHexWEI, bHexWEI) {
return addCurrencies(aHexWEI, bHexWEI, {
aBase: 16,
bBase: 16,
toNumericBase: 'hex',
numberOfDecimals: 6,
});
}
export function sumHexWEIs(hexWEIs) { export function sumHexWEIs(hexWEIs) {
return hexWEIs.filter(Boolean).reduce(addHexes); return hexWEIs.filter(Boolean).reduce(addHexes);
} }

Loading…
Cancel
Save