diff --git a/packages/transaction-controller/CHANGELOG.md b/packages/transaction-controller/CHANGELOG.md index 17809b45e58..dba4816ccdb 100644 --- a/packages/transaction-controller/CHANGELOG.md +++ b/packages/transaction-controller/CHANGELOG.md @@ -11,6 +11,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Bump `@metamask/messenger` from `^1.2.0` to `^2.0.0` ([#9392](https://github.com/MetaMask/core/pull/9392)) +### Fixed + +- Only apply user-saved (advanced) gas fees to dApp transactions ([#9401](https://github.com/MetaMask/core/pull/9401)) + - Saved advanced gas fees are now ignored for internal transactions (`isInternal`), such as swaps and bridges, whose gas fees are dictated by the aggregator or relay. Previously they were only ignored for swaps, so a user's saved gas fees (e.g. a low max base fee) could underprice a bridge transaction and cause it to fail or get stuck as pending. + ## [68.2.2] ### Changed diff --git a/packages/transaction-controller/src/utils/gas-fees.test.ts b/packages/transaction-controller/src/utils/gas-fees.test.ts index b72854dc086..e90dc0a1f66 100644 --- a/packages/transaction-controller/src/utils/gas-fees.test.ts +++ b/packages/transaction-controller/src/utils/gas-fees.test.ts @@ -130,6 +130,45 @@ describe('gas-fees', () => { ); }); + describe('saved (advanced) gas fees', () => { + const SAVED_GAS_FEES_MOCK = { maxBaseFee: '123', priorityFee: '456' }; + + it('are applied for dApp (non-internal) transactions', async () => { + updateGasFeeRequest.txMeta.isInternal = false; + updateGasFeeRequest.getSavedGasFees.mockReturnValueOnce( + SAVED_GAS_FEES_MOCK, + ); + + await updateGasFees(updateGasFeeRequest); + + expect(updateGasFeeRequest.getSavedGasFees).toHaveBeenCalledTimes(1); + expect(updateGasFeeRequest.txMeta.txParams.maxFeePerGas).toBe( + '0x1ca35f0e00', // 123 gwei + ); + expect(updateGasFeeRequest.txMeta.userFeeLevel).toBe( + UserFeeLevel.CUSTOM, + ); + }); + + it('are ignored for internal transactions (e.g. swaps and bridges)', async () => { + mockGasFeeFlowMockResponse(FLOW_RESPONSE_FEE_MARKET_MOCK); + updateGasFeeRequest.txMeta.isInternal = true; + updateGasFeeRequest.getSavedGasFees.mockReturnValueOnce( + SAVED_GAS_FEES_MOCK, + ); + + await updateGasFees(updateGasFeeRequest); + + expect(updateGasFeeRequest.getSavedGasFees).not.toHaveBeenCalled(); + expect(updateGasFeeRequest.txMeta.txParams.maxFeePerGas).toBe( + GAS_HEX_WEI_MOCK, + ); + expect(updateGasFeeRequest.txMeta.userFeeLevel).not.toBe( + UserFeeLevel.CUSTOM, + ); + }); + }); + it('deletes gasPrice property if maxPriorityFeePerGas set', async () => { updateGasFeeRequest.txMeta.txParams.maxPriorityFeePerGas = GAS_HEX_MOCK; updateGasFeeRequest.txMeta.txParams.gasPrice = GAS_HEX_MOCK; diff --git a/packages/transaction-controller/src/utils/gas-fees.ts b/packages/transaction-controller/src/utils/gas-fees.ts index 3598fac8099..7c3755f7035 100644 --- a/packages/transaction-controller/src/utils/gas-fees.ts +++ b/packages/transaction-controller/src/utils/gas-fees.ts @@ -12,13 +12,11 @@ import type { SavedGasFees, TransactionParams, TransactionMeta, - TransactionType, GasFeeFlow, } from '../types'; import { GasFeeEstimateType, UserFeeLevel } from '../types'; import { getGasFeeFlow } from './gas-flow'; import { rpcRequest } from './provider'; -import { SWAP_TRANSACTION_TYPES } from './swaps'; export type UpdateGasFeesRequest = { eip1559: boolean; @@ -56,10 +54,11 @@ export async function updateGasFees( const { txMeta } = request; const initialParams = { ...txMeta.txParams }; - const isSwap = SWAP_TRANSACTION_TYPES.includes( - txMeta.type as TransactionType, - ); - const savedGasFees = isSwap + // User-saved (advanced) gas fees only apply to dApp transactions. Internal + // transactions (e.g. swaps and bridges) have their fees dictated by the + // aggregator or relay, so applying saved gas fees could underprice them and + // cause them to fail or get stuck. + const savedGasFees = txMeta.isInternal ? undefined : request.getSavedGasFees(txMeta.chainId);