From aaa15cbe03d13cf7b7cb85b319d38c7949f3015a Mon Sep 17 00:00:00 2001 From: Dan J Miller Date: Wed, 28 Jul 2021 15:00:34 -0230 Subject: [PATCH] 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. --- app/scripts/controllers/transactions/index.js | 13 +++-- .../transactions/pending-tx-tracker.js | 6 ++- .../transaction-breakdown.component.js | 53 ++++++++++++------- .../transaction-breakdown.container.js | 32 +++++++++-- ui/helpers/utils/conversions.util.js | 9 ++++ 5 files changed, 87 insertions(+), 26 deletions(-) diff --git a/app/scripts/controllers/transactions/index.js b/app/scripts/controllers/transactions/index.js index 932ad5b8a..7d5c130d9 100644 --- a/app/scripts/controllers/transactions/index.js +++ b/app/scripts/controllers/transactions/index.js @@ -812,7 +812,7 @@ export default class TransactionController extends EventEmitter { * @param {number} txId - The tx's ID * @returns {Promise} */ - async confirmTransaction(txId, txReceipt) { + async confirmTransaction(txId, txReceipt, baseFeePerGas) { // get the txReceipt before marking the transaction confirmed // to ensure the receipt is gotten before the ui revives the tx const txMeta = this.txStateManager.getTransaction(txId); @@ -833,6 +833,11 @@ export default class TransactionController extends EventEmitter { ...txReceipt, gasUsed, }; + + if (baseFeePerGas) { + txMeta.baseFeePerGas = baseFeePerGas; + } + this.txStateManager.setTxStatusConfirmed(txId); this._markNonceDuplicatesDropped(txId); @@ -1011,8 +1016,10 @@ export default class TransactionController extends EventEmitter { this.pendingTxTracker.on('tx:failed', (txId, error) => { this._failTransaction(txId, error); }); - this.pendingTxTracker.on('tx:confirmed', (txId, transactionReceipt) => - this.confirmTransaction(txId, transactionReceipt), + this.pendingTxTracker.on( + 'tx:confirmed', + (txId, transactionReceipt, baseFeePerGas) => + this.confirmTransaction(txId, transactionReceipt, baseFeePerGas), ); this.pendingTxTracker.on('tx:dropped', (txId) => { this._dropTransaction(txId); diff --git a/app/scripts/controllers/transactions/pending-tx-tracker.js b/app/scripts/controllers/transactions/pending-tx-tracker.js index cb0067445..52e686cf6 100644 --- a/app/scripts/controllers/transactions/pending-tx-tracker.js +++ b/app/scripts/controllers/transactions/pending-tx-tracker.js @@ -193,7 +193,11 @@ export default class PendingTransactionTracker extends EventEmitter { try { const transactionReceipt = await this.query.getTransactionReceipt(txHash); 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; } } catch (err) { diff --git a/ui/components/app/transaction-breakdown/transaction-breakdown.component.js b/ui/components/app/transaction-breakdown/transaction-breakdown.component.js index 3ebd3d20c..4be5879fd 100644 --- a/ui/components/app/transaction-breakdown/transaction-breakdown.component.js +++ b/ui/components/app/transaction-breakdown/transaction-breakdown.component.js @@ -4,7 +4,12 @@ import classnames from 'classnames'; import CurrencyDisplay from '../../ui/currency-display'; import UserPreferencedCurrencyDisplay from '../user-preferenced-currency-display'; 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'; export default class TransactionBreakdown extends PureComponent { @@ -25,7 +30,8 @@ export default class TransactionBreakdown extends PureComponent { totalInHex: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), baseFee: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), priorityFee: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), - effectiveGasPrice: PropTypes.number, + hexGasTotal: PropTypes.string, + supportsEIP1559: PropTypes.bool, }; static defaultProps = { @@ -47,7 +53,8 @@ export default class TransactionBreakdown extends PureComponent { isTokenApprove, baseFee, priorityFee, - effectiveGasPrice, + hexGasTotal, + supportsEIP1559, } = this.props; return (
@@ -91,7 +98,7 @@ export default class TransactionBreakdown extends PureComponent { /> )} - {process.env.SHOW_EIP_1559_UI && ( + {process.env.SHOW_EIP_1559_UI && supportsEIP1559 && ( {typeof baseFee === 'undefined' ? ( '?' @@ -102,12 +109,13 @@ export default class TransactionBreakdown extends PureComponent { currency={nativeCurrency} denomination={GWEI} value={baseFee} + numberOfDecimals={10} hideLabel /> )} )} - {process.env.SHOW_EIP_1559_UI && ( + {process.env.SHOW_EIP_1559_UI && supportsEIP1559 && ( {typeof priorityFee === 'undefined' ? ( '?' @@ -118,12 +126,13 @@ export default class TransactionBreakdown extends PureComponent { currency={nativeCurrency} denomination={GWEI} value={priorityFee} + numberOfDecimals={10} hideLabel /> )} )} - {!process.env.SHOW_EIP_1559_UI && ( + {(!process.env.SHOW_EIP_1559_UI || !supportsEIP1559) && ( {typeof gasPrice === 'undefined' ? ( '?' @@ -139,22 +148,28 @@ export default class TransactionBreakdown extends PureComponent { )} )} - - - {showFiat && ( + {process.env.SHOW_EIP_1559_UI && supportsEIP1559 && ( + - )} - + {showFiat && ( + + )} + + )} { const { transaction, isTokenApprove } = ownProps; const { txParams: { gas, gasPrice, value } = {}, - txReceipt: { gasUsed } = {}, + txReceipt: { gasUsed, effectiveGasPrice } = {}, + baseFeePerGas, } = transaction; 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 = - (gasLimit && gasPrice && getHexGasTotal({ gasLimit, gasPrice })) || '0x0'; + (gasLimit && + usedGasPrice && + getHexGasTotal({ gasLimit, gasPrice: usedGasPrice })) || + '0x0'; const totalInHex = sumHexes(hexGasTotal, value); + const supportsEIP1559 = isEIP1559Network(state); + return { nativeCurrency: getNativeCurrency(state), showFiat: getShouldShowFiat(state), @@ -26,6 +47,11 @@ const mapStateToProps = (state, ownProps) => { gasPrice, gasUsed, isTokenApprove, + effectiveGasPrice, + hexGasTotal, + priorityFee, + baseFee: baseFeePerGas, + supportsEIP1559, }; }; diff --git a/ui/helpers/utils/conversions.util.js b/ui/helpers/utils/conversions.util.js index 7f191f9fe..78a28293b 100644 --- a/ui/helpers/utils/conversions.util.js +++ b/ui/helpers/utils/conversions.util.js @@ -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) { return hexWEIs.filter(Boolean).reduce(addHexes); }