Skip to content

fix: transaction parameters for batch swap transactions#8979

Merged
GeorgeGkas merged 6 commits into
mainfrom
swaps-fix-stx-batch-sell
Jun 3, 2026
Merged

fix: transaction parameters for batch swap transactions#8979
GeorgeGkas merged 6 commits into
mainfrom
swaps-fix-stx-batch-sell

Conversation

@micaelae

@micaelae micaelae commented Jun 2, 2026

Copy link
Copy Markdown
Member

Explanation

Fixes batch-sell submission edge case which causes tx failure if gasIncluded7702=true and STX is enabled. See changelog for exact parameters changed

In addition to these changes, clients need to update the TransactionController publishHook to prioritize publishing via 7702 if isGasFeeIncluded=true. The current logic submits using STX although the quote requires 7702 processing

Example transaction-controller-init change

+  const isSwapGasIncluded7702 = transactionMeta.isGasFeeIncluded;
   let attemptedHook = false;
 
   if (
     keyringSupports7702 &&
     !isRevokeDelegation &&
-    (!isSmartTransaction || !sendBundleSupport || isExternalSign)
+    (isSwapGasIncluded7702 || !isSmartTransaction || !sendBundleSupport || isExternalSign)
    ) {
     const hook = new Delegation7702PublishHook({
      messenger: initMessenger,
    }).getHook();

References

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
  • I've introduced breaking changes in this PR and have prepared draft pull requests for clients and consumer packages to resolve them

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 BridgeStatusController into TransactionController for 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 gasFeeToken from the quote’s TX_FEE asset (omitted when the fee asset is native), and sets skipInitialGasEstimate / excludeNativeTokenForFee from that token and from gasIncluded7702 plus whether the account is already delegated. shouldDisable7702 is tightened so delegated accounts only keep 7702 enabled when the quote is not plain gasIncluded STX gasless.

Batch-sell uses the same skipInitialGasEstimate and excludeNativeTokenForFee rules. toTransactionParams accepts 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 publishHook change so isGasFeeIncluded quotes 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.

@micaelae micaelae marked this pull request as ready for review June 2, 2026 23:12
@micaelae micaelae requested a review from a team as a code owner June 2, 2026 23:12
@micaelae micaelae temporarily deployed to default-branch June 2, 2026 23:12 — with GitHub Actions Inactive
@micaelae micaelae requested a review from a team as a code owner June 3, 2026 00:58
skipInitialGasEstimate: quoteResponse.quote.gasIncluded7702
? isDelegatedAccount
: Boolean(gasFeeToken),
excludeNativeTokenForFee: !gasFeeToken,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 9e44080. Configure here.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This is intentional. Prevents failures if the user has a native balance but it's not enough to cover network fees

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

There are 2 total unresolved issues (including 1 from previous review).

Fix All in Cursor

❌ 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);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 402df45. Configure here.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This is not possible, it's either zero address or a hex

@micaelae micaelae enabled auto-merge June 3, 2026 01:53
@micaelae micaelae added this pull request to the merge queue Jun 3, 2026
@GeorgeGkas GeorgeGkas removed this pull request from the merge queue due to a manual request Jun 3, 2026
@GeorgeGkas GeorgeGkas added this pull request to the merge queue Jun 3, 2026
Merged via the queue into main with commit 87c98fa Jun 3, 2026
370 checks passed
@GeorgeGkas GeorgeGkas deleted the swaps-fix-stx-batch-sell branch June 3, 2026 13:49
@GeorgeGkas GeorgeGkas mentioned this pull request Jun 3, 2026
4 tasks
pull Bot pushed a commit to dmrazzy/core that referenced this pull request Jun 3, 2026
## 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>
pull Bot pushed a commit to Reality2byte/metamask-mobile that referenced this pull request Jun 5, 2026
<!--
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 -->
sleepytanya pushed a commit to MetaMask/metamask-mobile that referenced this pull request Jun 10, 2026
<!--
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>
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.

2 participants