fix: ignore saved advanced gas fees for bridge transactions - #9401
Merged
Merged
Conversation
Bridge and bridge approval transactions applied the user's saved advanced gas fees, which swaps already deliberately ignore. A user with a low saved max base fee (e.g. 0.05 gwei on mainnet) would therefore submit underpriced bridge transactions that fail or get stuck as pending. Introduce a dedicated SAVED_GAS_FEES_IGNORED_TRANSACTION_TYPES list (swap + bridge types) to decide whether saved gas fees apply, keeping the "ignore saved gas fees" concern decoupled from SWAP_TRANSACTION_TYPES, which also gates swap-specific behavior.
cloudonshore
temporarily deployed
to
default-branch
July 7, 2026 04:07 — with
GitHub Actions
Inactive
chaitanyapotti
previously approved these changes
Jul 7, 2026
matthewwalsh0
requested changes
Jul 7, 2026
| * fees are dictated by the swap/bridge aggregator or relay; applying saved gas | ||
| * fees could underprice the transaction and cause it to fail or get stuck. | ||
| */ | ||
| const SAVED_GAS_FEES_IGNORED_TRANSACTION_TYPES: TransactionType[] = [ |
Member
There was a problem hiding this comment.
Rather than doing this per type, should we just exclude based on isInternal so it only applies to dApp transactions as was the intent?
Contributor
Author
There was a problem hiding this comment.
Sounds good, even simpler! I made the change
dan437
removed their request for review
July 7, 2026 11:34
Per review feedback, skip user-saved (advanced) gas fees for all internal transactions rather than maintaining a list of swap/bridge types. Saved gas fees are only intended for dApp transactions; internal transactions (swaps, bridges, etc.) have their fees dictated by the aggregator or relay.
matthewwalsh0
approved these changes
Jul 7, 2026
This was referenced Jul 7, 2026
Merged
Merged
pull Bot
pushed a commit
to Reality2byte/metamask-mobile
that referenced
this pull request
Jul 21, 2026
…ask#33175) ## **Description** Integrates the published core fix for stuck bridge smart transactions into mobile. Changes: - Bump `@metamask/smart-transactions-controller` `^24.2.2` → `25.0.0` - Bump `@metamask/transaction-controller` to `68.4.0` (dependency `^68.3.0` + resolution `68.4.0`) - Update the smart transactions controller messenger to delegate `TransactionController:failTransaction` instead of `TransactionController:updateTransaction` **Why:** When the relay cancelled a smart transaction, the STX controller previously called `updateTransaction`, which only patches state and does not emit transaction lifecycle events. Consumers that react to `transactionFailed`/`transactionStatusUpdated` (the bridge status controller and metrics) were never notified, so cancelled bridge smart transactions stayed **stuck pending indefinitely**. The core fix ([MetaMask/core#9400](MetaMask/core#9400)) adds a `failTransaction` action that fails the tx through the standard path and emits those events; this PR wires mobile's STX messenger to use it. **Note on versions:** `smart-transactions-controller` is pinned to `25.0.0` (and the `transaction-controller` resolution to `68.4.0`) to keep `transaction-controller` within `68.x`. `stx@25.0.1` requires `transaction-controller@^69.0.0`, a larger major bump out of scope for this fix. ## **Changelog** CHANGELOG entry: Fixed bridge smart transactions that could remain stuck as pending after being cancelled by the relay ## **Related issues** Refs: - [MetaMask/core#9400](MetaMask/core#9400) — fail cancelled smart transactions through the standard path - [MetaMask/core#9401](MetaMask/core#9401) — ignore saved gas fees for internal transactions - Released in [MetaMask/core#9421](MetaMask/core#9421) ## **Manual testing steps** 1. Build the branch and set up a wallet with a bridge route on a supported chain. 2. Initiate a bridge that is submitted as a smart transaction. 3. Observe a relay-side cancellation of the smart transaction. 4. Confirm the transaction transitions to **failed** in the activity list (instead of remaining pending) and the bridge status updates accordingly. ## **Screenshots/Recordings** N/A — dependency bump + messenger wiring (no UI changes). ## **Pre-merge author checklist** - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I've included tests if applicable - [x] I've documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I've applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Touches transaction lifecycle integration for smart transactions; incorrect wiring could affect how cancelled STX are surfaced, but scope is limited to messenger delegation and a controlled dependency bump. > > **Overview** > **Integrates the core fix for bridge smart transactions that stayed pending after relay cancellation.** > > Bumps `@metamask/smart-transactions-controller` from `^24.2.2` to `25.0.0` and updates the Smart Transactions controller messenger (and related test harnesses) to delegate **`TransactionController:failTransaction`** instead of **`TransactionController:updateTransaction`**. The upgraded STX controller uses the fail path when a relay cancels a smart transaction so **`transactionFailed` / `transactionStatusUpdated`** fire and bridge status and activity UI can leave the pending state. > > No app-layer transaction logic changes beyond messenger wiring and dependency lock updates. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 02840f1. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
pull Bot
pushed a commit
to Reality2byte/metamask-extension
that referenced
this pull request
Jul 21, 2026
…ask#44372) ## **Description** Integrates the published core fix for stuck bridge smart transactions into the extension. Changes: - Bump `@metamask/smart-transactions-controller` `^24.2.2` → `25.0.0` - Bump `@metamask/transaction-controller` `^68.2.2` → `^68.3.0` (resolves to `68.4.0`) - Update the smart transactions controller messenger to delegate `TransactionController:failTransaction` instead of `TransactionController:updateTransaction` **Why:** When the relay cancelled a smart transaction, the STX controller previously called `updateTransaction`, which only patches state and does not emit transaction lifecycle events. Consumers that react to `transactionFailed`/`transactionStatusUpdated` (the bridge status controller and metrics) were never notified, so cancelled bridge smart transactions stayed **stuck pending indefinitely**. The core fix ([MetaMask/core#9400](MetaMask/core#9400)) adds a `failTransaction` action that fails the tx through the standard path and emits those events; this PR wires the extension's STX messenger to use it. **Note on versions:** `smart-transactions-controller` is pinned to `25.0.0` (not a caret range) to keep `transaction-controller` within `68.x`. `stx@25.0.1` requires `transaction-controller@^69.0.0`, a larger major bump out of scope for this fix. ## **Changelog** CHANGELOG entry: Fixed bridge smart transactions that could remain stuck as pending after being cancelled by the relay ## **Related issues** Integrates the published core fix: - [MetaMask/core#9400](MetaMask/core#9400) — fail cancelled smart transactions through the standard path - [MetaMask/core#9401](MetaMask/core#9401) — ignore saved gas fees for internal transactions - Released in [MetaMask/core#9421](MetaMask/core#9421) ## **Manual testing steps** 1. Build the branch and set up a wallet with a bridge route on a supported chain. 2. Initiate a bridge that is submitted as a smart transaction. 3. Observe a relay-side cancellation of the smart transaction. 4. Confirm the transaction transitions to **failed** in the activity list (instead of remaining pending) and the bridge status updates accordingly. ## **Screenshots/Recordings** N/A — dependency bump + messenger wiring (no UI changes). ### **Before** Cancelled bridge smart transactions stayed pending indefinitely. ### **After** Cancelled bridge smart transactions transition to failed and notify the bridge status controller / metrics. ## **Pre-merge author checklist** - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I’ve included tests if applicable - [x] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Touches smart-transaction and transaction lifecycle integration; behavior change when relays cancel STXs, but scoped to messenger delegation and a targeted dependency bump. > > **Overview** > Integrates the core fix for **bridge smart transactions stuck pending** after relay cancellation by bumping `@metamask/smart-transactions-controller` to **25.0.0** (pinned) and aligning the lockfile. > > The extension wires the smart transactions controller messenger to delegate **`TransactionController:failTransaction`** instead of **`TransactionController:updateTransaction`**, in both production init and unit test setup, so cancelled STXs go through the standard failure path and emit **`transactionFailed`** / **`transactionStatusUpdated`** for bridge status and metrics. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 3985dc9. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
seaona
pushed a commit
to MetaMask/metamask-extension
that referenced
this pull request
Jul 22, 2026
## **Description** Integrates the published core fix for stuck bridge smart transactions into the extension. Changes: - Bump `@metamask/smart-transactions-controller` `^24.2.2` → `25.0.0` - Bump `@metamask/transaction-controller` `^68.2.2` → `^68.3.0` (resolves to `68.4.0`) - Update the smart transactions controller messenger to delegate `TransactionController:failTransaction` instead of `TransactionController:updateTransaction` **Why:** When the relay cancelled a smart transaction, the STX controller previously called `updateTransaction`, which only patches state and does not emit transaction lifecycle events. Consumers that react to `transactionFailed`/`transactionStatusUpdated` (the bridge status controller and metrics) were never notified, so cancelled bridge smart transactions stayed **stuck pending indefinitely**. The core fix ([MetaMask/core#9400](MetaMask/core#9400)) adds a `failTransaction` action that fails the tx through the standard path and emits those events; this PR wires the extension's STX messenger to use it. **Note on versions:** `smart-transactions-controller` is pinned to `25.0.0` (not a caret range) to keep `transaction-controller` within `68.x`. `stx@25.0.1` requires `transaction-controller@^69.0.0`, a larger major bump out of scope for this fix. ## **Changelog** CHANGELOG entry: Fixed bridge smart transactions that could remain stuck as pending after being cancelled by the relay ## **Related issues** Integrates the published core fix: - [MetaMask/core#9400](MetaMask/core#9400) — fail cancelled smart transactions through the standard path - [MetaMask/core#9401](MetaMask/core#9401) — ignore saved gas fees for internal transactions - Released in [MetaMask/core#9421](MetaMask/core#9421) ## **Manual testing steps** 1. Build the branch and set up a wallet with a bridge route on a supported chain. 2. Initiate a bridge that is submitted as a smart transaction. 3. Observe a relay-side cancellation of the smart transaction. 4. Confirm the transaction transitions to **failed** in the activity list (instead of remaining pending) and the bridge status updates accordingly. ## **Screenshots/Recordings** N/A — dependency bump + messenger wiring (no UI changes). ### **Before** Cancelled bridge smart transactions stayed pending indefinitely. ### **After** Cancelled bridge smart transactions transition to failed and notify the bridge status controller / metrics. ## **Pre-merge author checklist** - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I’ve included tests if applicable - [x] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Touches smart-transaction and transaction lifecycle integration; behavior change when relays cancel STXs, but scoped to messenger delegation and a targeted dependency bump. > > **Overview** > Integrates the core fix for **bridge smart transactions stuck pending** after relay cancellation by bumping `@metamask/smart-transactions-controller` to **25.0.0** (pinned) and aligning the lockfile. > > The extension wires the smart transactions controller messenger to delegate **`TransactionController:failTransaction`** instead of **`TransactionController:updateTransaction`**, in both production init and unit test setup, so cancelled STXs go through the standard failure path and emit **`transactionFailed`** / **`transactionStatusUpdated`** for bridge status and metrics. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 3985dc9. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
pull Bot
pushed a commit
to Reality2byte/core
that referenced
this pull request
Jul 29, 2026
…bridges (MetaMask#9682) ## Summary Saved gas preferences were excluded from every transaction with `isInternal: true`. This also excluded wallet-initiated transfers, which are marked internal by the extension API even though they should use saved gas settings. This changes the exclusion to a dedicated transaction-type list containing swaps and bridge transactions. Wallet transfers can now apply saved gas preferences while swaps and bridges remain protected from underpriced saved fees. ## Related PRs - MetaMask#9401 - MetaMask/metamask-extension#43317 ## Testing - `yarn jest --config packages/transaction-controller/jest.config.js --runInBand packages/transaction-controller/src/utils/gas-fees.test.ts --coverage=false` - Added coverage for internal wallet transactions and swap/bridge exclusions. ## Changelog Added an Unreleased changelog entry for `@metamask/transaction-controller`. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Changes which transactions receive saved gas fees at submit time; wallet sends gain user prefs while swap/bridge behavior stays guarded, but any misclassified `type` could get the wrong fee path. > > **Overview** > **Saved gas preferences** were skipped for every transaction with `isInternal: true`, which incorrectly blocked wallet-initiated transfers (often marked internal) from using the user’s advanced gas settings. > > `updateGasFees` now ignores saved gas only for **swap and bridge** transaction types (`SWAP_TRANSACTION_TYPES`, `bridge`, `bridgeApproval`) instead of all internal transactions. Internal `simpleSend` transfers can apply saved fees again; aggregator/relay-driven swaps and bridges still avoid user-saved fees that could underprice them. > > Tests cover internal wallet sends applying saved gas and parameterized cases for swap/bridge types still ignoring them. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 61d4074. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
This was referenced Jul 30, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Explanation
Bridge transactions apply the user's saved advanced gas fees, whereas swaps deliberately ignore them.
In
updateGasFees, saved gas fees are skipped only forSWAP_TRANSACTION_TYPES(swap,swapAndSend,swapApproval).bridge/bridgeApprovalare not in that list, so a user's saved advanced gas fees are applied to bridge transactions.For a user who has saved a low max base fee (observed in production:
maxBaseFee: "0.05"gwei,priorityFee: "0"for mainnet), every bridge is submitted underpriced — below the current base fee — causing it to fail (GAS_TOO_LOWon the relay) or get stuck as pending. This is exactly the failure swaps were already protected from.Fix
Ignore saved gas fees for bridge transactions as well, via a dedicated list:
I intentionally did not add the bridge types to
SWAP_TRANSACTION_TYPESitself, because that constant also gates swap-specific behavior inupdateSwapsTransaction(e.g. thesimulationFailscancel-and-throw, andtransactionNewSwap*events). Keeping a separate, purpose-named list decouples "ignore saved gas fees" from "is a swap".References
maxFeePerGas: 0x2faf080(0.05 gwei) /maxPriorityFeePerGas: 0x0, sourced fromadvancedGasFee["0x1"] = { maxBaseFee: "0.05", priorityFee: "0" }.Changelog
@metamask/transaction-controllerbridgeandbridgeApprovaltransactions now ignore user-saved (advanced) gas fees, matching swaps.Checklist
Note
Medium Risk
Changes gas pricing for all internal transactions (bridges, swaps, etc.); behavior is intentional but affects submission paths where wrong fees caused production failures.
Overview
updateGasFeesno longer applies user-saved (advanced) gas fees whentxMeta.isInternalis true. Saved fees are only loaded for non-internal (dApp) transactions.The previous logic skipped saved fees only for
SWAP_TRANSACTION_TYPES, so bridge and other internal flows could inherit a low saved max base fee and submit underpriced txs. The swap-type check andSWAP_TRANSACTION_TYPESimport were removed in favor ofisInternal.Tests cover applying saved fees for non-internal txs and ignoring them (without calling
getSavedGasFees) for internal txs. The changelog documents the fix.Reviewed by Cursor Bugbot for commit eb622eb. Bugbot is set up for automated code reviews on this repo. Configure here.