Skip to content

fix: ignore saved advanced gas fees for bridge transactions#9401

Merged
matthewwalsh0 merged 4 commits into
mainfrom
fix/bridge-ignore-saved-gas-fees
Jul 7, 2026
Merged

fix: ignore saved advanced gas fees for bridge transactions#9401
matthewwalsh0 merged 4 commits into
mainfrom
fix/bridge-ignore-saved-gas-fees

Conversation

@cloudonshore

@cloudonshore cloudonshore commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Explanation

Bridge transactions apply the user's saved advanced gas fees, whereas swaps deliberately ignore them.

In updateGasFees, saved gas fees are skipped only for SWAP_TRANSACTION_TYPES (swap, swapAndSend, swapApproval). bridge / bridgeApproval are 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_LOW on 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:

const SAVED_GAS_FEES_IGNORED_TRANSACTION_TYPES: TransactionType[] = [
  ...SWAP_TRANSACTION_TYPES,
  TransactionType.bridge,
  TransactionType.bridgeApproval,
];

I intentionally did not add the bridge types to SWAP_TRANSACTION_TYPES itself, because that constant also gates swap-specific behavior in updateSwapsTransaction (e.g. the simulationFails cancel-and-throw, and transactionNewSwap* events). Keeping a separate, purpose-named list decouples "ignore saved gas fees" from "is a swap".

References

  • Root-caused from production state logs where a bridge smart transaction was submitted with maxFeePerGas: 0x2faf080 (0.05 gwei) / maxPriorityFeePerGas: 0x0, sourced from advancedGasFee["0x1"] = { maxBaseFee: "0.05", priorityFee: "0" }.

Changelog

@metamask/transaction-controller

  • FIXED: bridge and bridgeApproval transactions now ignore user-saved (advanced) gas fees, matching swaps.

Checklist

  • I've updated the test suite for new or updated code as appropriate
  • I've updated documentation (JSDoc) for new or updated code as appropriate
  • I've highlighted breaking changes using the "BREAKING" category above as appropriate

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
updateGasFees no longer applies user-saved (advanced) gas fees when txMeta.isInternal is 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 and SWAP_TRANSACTION_TYPES import were removed in favor of isInternal.

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.

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 cloudonshore requested review from a team as code owners July 7, 2026 04:07
chaitanyapotti
chaitanyapotti previously approved these changes Jul 7, 2026
@matthewwalsh0 matthewwalsh0 requested a review from dan437 July 7, 2026 11:00
* 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[] = [

@matthewwalsh0 matthewwalsh0 Jul 7, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than doing this per type, should we just exclude based on isInternal so it only applies to dApp transactions as was the intent?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good, even simpler! I made the change

@dan437 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 matthewwalsh0 added this pull request to the merge queue Jul 7, 2026
Merged via the queue into main with commit 9b2a8b1 Jul 7, 2026
417 checks passed
@matthewwalsh0 matthewwalsh0 deleted the fix/bridge-ignore-saved-gas-fees branch July 7, 2026 18:35
@cloudonshore cloudonshore mentioned this pull request Jul 7, 2026
3 tasks
pull Bot pushed a commit to dmrazzy/core that referenced this pull request Jul 7, 2026
## Explanation

Releases two packages needed to fix stuck/underpriced bridge smart
transactions in the clients (extension + mobile).

### `@metamask/transaction-controller` — `68.2.2` → `68.3.0` (minor)
- **Added:** `failTransaction` method +
`TransactionController:failTransaction` messenger action
([MetaMask#9400](MetaMask#9400)) — fails a tx
through the standard path, emitting
`transactionFailed`/`transactionStatusUpdated`/`transactionFinished`.
- **Fixed:** Only apply user-saved (advanced) gas fees to dApp
transactions; ignored for internal (`isInternal`) txs such as swaps and
bridges ([MetaMask#9401](MetaMask#9401)).
- **Changed:** Bump `@metamask/messenger` `^1.2.0` → `^2.0.0`
([MetaMask#9392](MetaMask#9392)).

### `@metamask/smart-transactions-controller` — `24.2.4` → `25.0.0`
(major)
- **Changed (BREAKING):** Fail the associated regular transaction via
`TransactionController:failTransaction` instead of `updateTransaction`
when a smart transaction is cancelled
([MetaMask#9400](MetaMask#9400)). Consumers must
now grant the STX messenger access to
`TransactionController:failTransaction`.
- **Changed:** Bump `@metamask/messenger` `^1.2.0` → `^2.0.0`
([MetaMask#9392](MetaMask#9392)).

All other changed packages are `intentionally-skip` (owned by other
teams); dependent version ranges were updated automatically.

## References

- Fixes stuck-pending bridge STX (root cause in MetaMask#9400) and underpriced
bridge gas (MetaMask#9401).

## Checklist

- [x] I've followed the [release
process](https://github.com/MetaMask/core/blob/main/docs/processes/releasing.md)
- [x] Changelogs reviewed and categorized (no `Uncategorized`; breaking
change marked)
- [x] Version bumps follow SemVer (STX major for the breaking messenger
change)

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Touches transaction failure signaling and gas on internal bridge
flows; STX **25.0.0** is a breaking messenger permission change for
extension/mobile integrators.
> 
> **Overview**
> **Monorepo release `1102.0.0`** that publishes
**`@metamask/transaction-controller` `68.3.0`** and
**`@metamask/smart-transactions-controller` `25.0.0`**, and rolls
**`@metamask/transaction-controller` `^68.2.2` → `^68.3.0`** through
dependent packages and **`yarn.lock`**.
> 
> **`transaction-controller` `68.3.0`** (documented in its changelog for
this release): adds **`failTransaction`** /
**`TransactionController:failTransaction`** so out-of-band failures emit
normal lifecycle events; fixes **advanced gas fees** so they apply only
to dApp txs, not **`isInternal`** bridge/swap txs.
> 
> **`smart-transactions-controller` `25.0.0` (breaking):** on smart-tx
cancel, calls **`failTransaction`** instead of **`updateTransaction`**;
hosts must delegate **`TransactionController:failTransaction`** on the
STX messenger.
> 
> Other touched packages in the diff are mainly **version/changelog
dependency bumps** tied to this release, not new feature code in those
files.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
c423a08. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants