fix: transaction parameters for batch swap transactions#8979
Conversation
| skipInitialGasEstimate: quoteResponse.quote.gasIncluded7702 | ||
| ? isDelegatedAccount | ||
| : Boolean(gasFeeToken), | ||
| excludeNativeTokenForFee: !gasFeeToken, |
There was a problem hiding this comment.
Inverted excludeNativeTokenForFee flag
High Severity
Batch submission sets excludeNativeTokenForFee to !gasFeeToken, so when an ERC20 gasFeeToken is provided the flag is false. TransactionController only forces fee payment via gasFeeToken when excludeNativeTokenForFee is true with a token set, so ERC20 gasless swaps can fall back to native balance.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 9e44080. Configure here.
There was a problem hiding this comment.
This is intentional. Prevents failures if the user has a native balance but it's not enough to cover network fees
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
There are 2 total unresolved issues (including 1 from previous review).
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 402df45. Configure here.
| quoteResponse.quote.feeData[FeeType.TX_FEE]?.asset?.address && | ||
| isNativeAddress(quoteResponse.quote.feeData[FeeType.TX_FEE].asset.address) | ||
| ? undefined | ||
| : (quoteResponse.quote.feeData[FeeType.TX_FEE]?.asset?.address as Hex); |
There was a problem hiding this comment.
Empty native fee sets gasFeeToken
Low Severity
When TX_FEE uses a native asset with an empty-string address (valid per isNativeAddress), the gasFeeToken expression falls through to the else branch and passes an empty hex string instead of undefined, so batch params can carry a bogus gasFeeToken rather than treating the fee as native.
Reviewed by Cursor Bugbot for commit 402df45. Configure here.
There was a problem hiding this comment.
This is not possible, it's either zero address or a hex
## Explanation **Current state:** When submitting batch and batch-sell swap transactions, the gas-related transaction options were not being set correctly in several edge cases: 1. When the bridge fee is paid in an ERC20 token, the `gasFeeToken` field was not being populated, causing the TransactionController to default to native token fee payment. 2. The `excludeNativeTokenForFee` flag was not being set when a `gasFeeToken` was present, meaning the TransactionController might fall back to native assets even when an ERC20 gas fee token was explicitly specified. 3. For `gasIncluded7702` quotes submitted to accounts that haven't yet been upgraded to a smart account, `skipInitialGasEstimate` was incorrectly set, which could cause gas estimation issues. **Solution:** This release fixes all three cases in the gas-related transaction option construction for batch and batch-sell swaps: - `gasFeeToken` is now set from the quote's fee token when fees are paid in ERC20. - `excludeNativeTokenForFee` is set to `true` whenever `gasFeeToken` is populated, ensuring the ERC20 token is always used for fee payment in that scenario. - `skipInitialGasEstimate` is set to `false` when submitting `gasIncluded7702` quotes and the account has not yet been upgraded to a smart account, ensuring correct gas estimation for those transactions. ## References - [MetaMask#8979](MetaMask#8979) ## Checklist - [ ] I've updated the test suite for new or updated code as appropriate - [ ] I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate - [ ] I've communicated my changes to consumers by [updating changelogs for packages I've changed](https://github.com/MetaMask/core/tree/main/docs/processes/updating-changelogs.md) - [ ] I've introduced [breaking changes](https://github.com/MetaMask/core/tree/main/docs/processes/breaking-changes.md) in this PR and have prepared draft pull requests for clients and consumer packages to resolve them <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > The release carries a targeted fix to how gas/fee options are passed into TransactionController for batch and batch-sell swaps, which can change fee token selection and estimation behavior at submit time. > > **Overview** > **Release `1019.0.0`** bumps the core monorepo and publishes **`@metamask/bridge-status-controller@72.0.1`**, documenting the gas-option fix from [MetaMask#8979](MetaMask#8979) for **batch** and **batch-sell** swaps: ERC20-paid fees set **`gasFeeToken`**, **`excludeNativeTokenForFee=true`** when that token is used, and **`skipInitialGasEstimate=false`** for **`gasIncluded7702`** on accounts not yet upgraded to a smart account. > > **`@metamask/transaction-pay-controller`** is updated to depend on **`@metamask/bridge-status-controller@^72.0.1`** (changelog + `package.json` + `yarn.lock`). No application source changes appear in this diff—only version and release metadata. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit ed18202. 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>
<!-- Please submit this PR as a draft initially. Do not mark it as "Ready for review" until this PR meets the canonical Definition of Ready For Review in `docs/readme/ready-for-review.md`. In short: the template must be materially complete (not just section titles present), all status checks must be currently passing, and the only expected follow-up commits must be reviewer-driven. --> ## **Description** Fixes Batch Sell (and other gasless EIP-7702) transaction submission when Smart Transactions (STX) and send bundle are both enabled. **Problem:** Quotes with `gasIncluded7702: true` must be published via the EIP-7702 delegation relay (`Delegation7702PublishHook`). When STX and send bundle were enabled, `publishHook` skipped the 7702 path and submitted via STX instead, causing transaction failures. **Changes:** 1. **`@metamask/bridge-status-controller` `^72.0.0` → `^72.0.2`** — picks up the controller fix for the batch-sell submission edge case when `gasIncluded7702=true` and STX is enabled (see package changelog for parameter changes). 2. **`TransactionController` `publishHook`** — when `transactionMeta.isGasFeeIncluded` is true (set by bridge-status-controller for gasless-7702 quotes), attempt `Delegation7702PublishHook` before STX even if STX and send bundle are supported. Aligns with extension client behavior (`isSwapGasIncluded7702`). **Motivation:** The quote/backend contract requires 7702 processing; routing through STX was incorrect for this case. ## **Changelog** <!-- If this PR is not End-User-Facing and should not show up in the CHANGELOG, you can choose to either: 1. Write `CHANGELOG entry: null` 2. Label with `no-changelog` If this PR is End-User-Facing, please write a short User-Facing description in the past tense like: `CHANGELOG entry: Added a new tab for users to see their NFTs` `CHANGELOG entry: Fixed a bug that was causing some NFTs to flicker` (This helps the Release Engineer do their job more quickly and accurately) --> CHANGELOG entry: Fixed Batch Sell transactions failing to submit when gas-included EIP-7702 quotes were used with Smart Transactions enabled ## **Related issues** Refs: MetaMask/core#8979 ## **Manual testing steps** ```gherkin Feature: Batch Sell gas-included 7702 submission with Smart Transactions Scenario: Batch Sell submits successfully when gas is included via 7702 and STX is enabled Given I have an EVM account that supports EIP-7702 And Smart Transactions are enabled on a chain that supports send bundle And Batch Sell is available with multiple eligible source tokens And quotes return with gas included via 7702 (network fee shown as included / gasIncluded7702) When I complete Batch Sell review and submit Sell All Then transactions submit without failure And I am not blocked by incorrect STX-only submission for gasless-7702 quotes Scenario: Normal STX gas-included swap still uses Smart Transactions when isGasFeeIncluded is not set Given Smart Transactions and send bundle are enabled And I initiate a swap with STX gas-included (not gasIncluded7702 / no isGasFeeIncluded on tx meta) When I confirm and submit the transaction Then submission still goes through the Smart Transactions path as before ``` **Commands run locally:** ```bash yarn jest app/core/Engine/controllers/transaction-controller/transaction-controller-init.test.ts -t "7702 delegation hook" ``` ## **Screenshots/Recordings** <!-- If applicable, add screenshots and/or recordings to visualize the before and after of your change. --> ### **Before** <!-- [screenshots/recordings] --> N/A ### **After** <!-- [screenshots/recordings] --> N/A ## **Pre-merge author checklist** <!-- Every checklist item must be consciously assessed before marking this PR as "Ready for review". A checked box means you deliberately considered that responsibility, not that you literally performed every action listed. Unchecked boxes are ambiguous: they are not an implicit "N/A" and they are not a silent "skip". See `docs/readme/ready-for-review.md` for the full checklist semantics. --> - [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. #### Performance checks (if applicable) - [x] I've tested on Android - Ideally on a mid-range device; emulator is acceptable - [x] I've tested with a power user scenario - Use these [power-user SRPs](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/edit-v2/401401446401?draftShareId=9d77e1e1-4bdc-4be1-9ebb-ccd916988d93) to import wallets with many accounts and tokens - [x] I've instrumented key operations with Sentry traces for production performance metrics - See [`trace()`](/app/util/trace.ts) for usage and [`addToken`](/app/components/Views/AddAsset/components/AddCustomToken/AddCustomToken.tsx#L274) for an example For performance guidelines and tooling, see the [Performance Guide](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/400085549067/Performance+Guide+for+Engineers). ## **Pre-merge reviewer checklist** <!-- Reviewer checklist items follow the same semantics as the author checklist: an unchecked box is ambiguous, a checked box means the reviewer consciously assessed that responsibility. See `docs/readme/ready-for-review.md`. --> - [ ] 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** > Changes transaction submission routing for a specific meta flag; wrong routing could affect Batch Sell and gasless-7702 swaps, but scope is narrow and covered by a new unit test. > > **Overview** > Fixes **Batch Sell** and other **gas-included EIP-7702** flows failing when **Smart Transactions (STX)** and **send bundle** are both on. > > **`publishHook`** now treats `transactionMeta.isGasFeeIncluded` (gasless-7702 quotes from bridge status) like extension’s `isSwapGasIncluded7702`: it tries **`Delegation7702PublishHook`** before STX even when STX and bundle would otherwise win. A unit test asserts the 7702 hook runs and STX is skipped when `isGasFeeIncluded` is true. > > **`@metamask/bridge-status-controller`** is bumped **^72.0.0 → ^72.0.2** so tx meta and submission align with the gas-included-7702 contract; `yarn.lock` reflects related transitive package updates. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 7a0a735. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
<!-- Please submit this PR as a draft initially. Do not mark it as "Ready for review" until this PR meets the canonical Definition of Ready For Review in `docs/readme/ready-for-review.md`. In short: the template must be materially complete (not just section titles present), all status checks must be currently passing, and the only expected follow-up commits must be reviewer-driven. --> ## **Description** Fixes Batch Sell (and other gasless EIP-7702) transaction submission when Smart Transactions (STX) and send bundle are both enabled. **Problem:** Quotes with `gasIncluded7702: true` must be published via the EIP-7702 delegation relay (`Delegation7702PublishHook`). When STX and send bundle were enabled, `publishHook` skipped the 7702 path and submitted via STX instead, causing transaction failures. **Changes:** 1. **`@metamask/bridge-status-controller` `^72.0.0` → `^72.0.2`** — picks up the controller fix for the batch-sell submission edge case when `gasIncluded7702=true` and STX is enabled (see package changelog for parameter changes). 2. **`TransactionController` `publishHook`** — when `transactionMeta.isGasFeeIncluded` is true (set by bridge-status-controller for gasless-7702 quotes), attempt `Delegation7702PublishHook` before STX even if STX and send bundle are supported. Aligns with extension client behavior (`isSwapGasIncluded7702`). **Motivation:** The quote/backend contract requires 7702 processing; routing through STX was incorrect for this case. ## **Changelog** <!-- If this PR is not End-User-Facing and should not show up in the CHANGELOG, you can choose to either: 1. Write `CHANGELOG entry: null` 2. Label with `no-changelog` If this PR is End-User-Facing, please write a short User-Facing description in the past tense like: `CHANGELOG entry: Added a new tab for users to see their NFTs` `CHANGELOG entry: Fixed a bug that was causing some NFTs to flicker` (This helps the Release Engineer do their job more quickly and accurately) --> CHANGELOG entry: Fixed Batch Sell transactions failing to submit when gas-included EIP-7702 quotes were used with Smart Transactions enabled ## **Related issues** Refs: MetaMask/core#8979 ## **Manual testing steps** ```gherkin Feature: Batch Sell gas-included 7702 submission with Smart Transactions Scenario: Batch Sell submits successfully when gas is included via 7702 and STX is enabled Given I have an EVM account that supports EIP-7702 And Smart Transactions are enabled on a chain that supports send bundle And Batch Sell is available with multiple eligible source tokens And quotes return with gas included via 7702 (network fee shown as included / gasIncluded7702) When I complete Batch Sell review and submit Sell All Then transactions submit without failure And I am not blocked by incorrect STX-only submission for gasless-7702 quotes Scenario: Normal STX gas-included swap still uses Smart Transactions when isGasFeeIncluded is not set Given Smart Transactions and send bundle are enabled And I initiate a swap with STX gas-included (not gasIncluded7702 / no isGasFeeIncluded on tx meta) When I confirm and submit the transaction Then submission still goes through the Smart Transactions path as before ``` **Commands run locally:** ```bash yarn jest app/core/Engine/controllers/transaction-controller/transaction-controller-init.test.ts -t "7702 delegation hook" ``` ## **Screenshots/Recordings** <!-- If applicable, add screenshots and/or recordings to visualize the before and after of your change. --> ### **Before** <!-- [screenshots/recordings] --> N/A ### **After** <!-- [screenshots/recordings] --> N/A ## **Pre-merge author checklist** <!-- Every checklist item must be consciously assessed before marking this PR as "Ready for review". A checked box means you deliberately considered that responsibility, not that you literally performed every action listed. Unchecked boxes are ambiguous: they are not an implicit "N/A" and they are not a silent "skip". See `docs/readme/ready-for-review.md` for the full checklist semantics. --> - [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. #### Performance checks (if applicable) - [x] I've tested on Android - Ideally on a mid-range device; emulator is acceptable - [x] I've tested with a power user scenario - Use these [power-user SRPs](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/edit-v2/401401446401?draftShareId=9d77e1e1-4bdc-4be1-9ebb-ccd916988d93) to import wallets with many accounts and tokens - [x] I've instrumented key operations with Sentry traces for production performance metrics - See [`trace()`](/app/util/trace.ts) for usage and [`addToken`](/app/components/Views/AddAsset/components/AddCustomToken/AddCustomToken.tsx#L274) for an example For performance guidelines and tooling, see the [Performance Guide](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/400085549067/Performance+Guide+for+Engineers). ## **Pre-merge reviewer checklist** <!-- Reviewer checklist items follow the same semantics as the author checklist: an unchecked box is ambiguous, a checked box means the reviewer consciously assessed that responsibility. See `docs/readme/ready-for-review.md`. --> - [ ] 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** > Changes which publish path runs for gas-included transactions when STX is enabled—a core submission decision—but scope is gated on `isGasFeeIncluded` with new unit and E2E coverage. > > **Overview** > Fixes **Batch Sell** and other **gas-included EIP-7702** flows that were incorrectly published through **Smart Transactions** when STX and send bundle were enabled. > > **Transaction publish routing** now treats `transactionMeta.isGasFeeIncluded` as **gas-included 7702** (`isSwapGasIncluded7702`) and runs **`Delegation7702PublishHook` (Sentinel relay)** before STX, even when STX + send bundle would otherwise win. A unit test asserts STX is **not** called when that flag is set. > > **`@metamask/bridge-status-controller`** is bumped to **`^72.0.2`** so tx meta gets the right gas-included-7702 signaling (with related lockfile/controller dependency updates). > > **E2E/support:** STX HTTP mocks accept versioned `*.api.cx.metamask.io` paths; **7702 relay** submit/poll mocks were added (Anvil-backed receipt). Gasless swap smoke tests are **re-enabled**; ramps unified-buy fixtures/flags, longer app-ready/stake timeouts, bridge quote keypad timing, and minor TestSnaps tap stability tweaks. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 6309f03. 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: infiniteflower <139582705+infiniteflower@users.noreply.github.com> Co-authored-by: Davide Brocchetto <davide.brocchetto@consensys.net>


Explanation
Fixes batch-sell submission edge case which causes tx failure if
gasIncluded7702=trueand STX is enabled. See changelog for exact parameters changedIn addition to these changes, clients need to update the TransactionController
publishHookto prioritize publishing via 7702 ifisGasFeeIncluded=true. The current logic submits using STX although the quote requires 7702 processingExample transaction-controller-init change
References
Checklist
Note
High Risk
Changes how batched swaps choose 7702 vs STX paths and how fees are paid; misconfiguration can cause submission failures, and full behavior depends on a coordinated TransactionController publishHook update in clients.
Overview
Fixes gas and EIP-7702 batch options passed from
BridgeStatusControllerintoTransactionControllerfor batched swaps and batch-sell flows, addressing failures when quotes use gas-included 7702 with STX enabled or ERC-20–denominated fees.Batch swap strategy now derives
gasFeeTokenfrom the quote’sTX_FEEasset (omitted when the fee asset is native), and setsskipInitialGasEstimate/excludeNativeTokenForFeefrom that token and fromgasIncluded7702plus whether the account is already delegated.shouldDisable7702is tightened so delegated accounts only keep 7702 enabled when the quote is not plaingasIncludedSTX gasless.Batch-sell uses the same
skipInitialGasEstimateandexcludeNativeTokenForFeerules.toTransactionParamsaccepts bridge-api fee fields whether they are already hex or numeric strings.Tests and snapshots cover native vs ERC-20 gasless swaps and the updated batch params. Clients still need a separate
publishHookchange soisGasFeeIncludedquotes publish via 7702 instead of STX when required.Reviewed by Cursor Bugbot for commit 402df45. Bugbot is set up for automated code reviews on this repo. Configure here.