Skip to content

fix: batch sell tx bridge status bump (#31098)#31488

Merged
sleepytanya merged 3 commits into
release/7.81.0from
cherry-pick-7-81-0-0ea45fe
Jun 10, 2026
Merged

fix: batch sell tx bridge status bump (#31098)#31488
sleepytanya merged 3 commits into
release/7.81.0from
cherry-pick-7-81-0-0ea45fe

Conversation

@SteP-n-s

@SteP-n-s SteP-n-s commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

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

CHANGELOG entry: Fixed Batch Sell transactions failing to submit when gas-included EIP-7702 quotes were used with Smart Transactions enabled

Related issues

Refs: #31484

Manual testing steps

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:

yarn jest app/core/Engine/controllers/transaction-controller/transaction-controller-init.test.ts -t "7702 delegation hook"

Screenshots/Recordings

Before

N/A

After

N/A

Pre-merge author checklist

Performance checks (if applicable)

  • I've tested on Android
    • Ideally on a mid-range device; emulator is acceptable
  • I've tested with a power user scenario
    • Use these power-user SRPs to import wallets with many accounts and tokens
  • I've instrumented key operations with Sentry traces for production performance metrics

For performance guidelines and tooling, see the Performance Guide.

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.

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.

Reviewed by Cursor Bugbot for commit 6309f03. Bugbot is set up for automated code reviews on this repo. Configure here.

<!--
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 -->
@SteP-n-s SteP-n-s requested a review from a team as a code owner June 10, 2026 17:44
@github-actions

Copy link
Copy Markdown
Contributor

CLA Signature Action: All authors have signed the CLA. You may need to manually re-run the blocking PR check if it doesn't pass in a few minutes.

@mm-token-exchange-service mm-token-exchange-service Bot added the team-swaps-and-bridge Swaps and Bridge team label Jun 10, 2026
@github-actions github-actions Bot added the risk:high AI analysis: high risk label Jun 10, 2026
@socket-security

socket-security Bot commented Jun 10, 2026

Copy link
Copy Markdown

@infiniteflower infiniteflower requested review from a team as code owners June 10, 2026 19:24
@github-actions github-actions Bot added size-M and removed size-S labels Jun 10, 2026
@infiniteflower infiniteflower force-pushed the cherry-pick-7-81-0-0ea45fe branch from cf477f9 to 84bc91f Compare June 10, 2026 19:27
<!--
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.
-->
<!--
mms-check directive vocabulary — read by
.github/scripts/shared/pr-template-checks.ts
at module load to build the validation plan. Directives are invisible in
rendered
markdown and must NOT be removed or edited without updating the
validator registry.

  type=text           Section must contain non-placeholder prose.
  type=changelog      Section must have a valid CHANGELOG entry: line.
type=issue-link Section must have a Fixes:/Closes:/Refs: line with a
value.
type=manual-testing Section must have real testing steps or an explicit
N/A.
type=screenshot Section must have evidence (image/URL) or an explicit
N/A.
type=checklist Section must have all checkboxes consciously checked.
required=true|false Whether a missing/invalid section blocks the PR
check.

Sections without a directive are checked for structural presence only.
-->

## **Description**

<!-- mms-check: type=text required=true -->

### `gasless-swap.spec.ts`
- Fixed and re-enabled skipped E2E swap test.
- Added mocks to match the actual Sentinel API URLs used by the app, and
added relay endpoint mocks for the EIP-7702 gasless swap flow, which
uses a different transaction submission path than the standard Smart
Transactions flow.

### `bridge-action-smoke.spec.ts`
- Moved `dismissKeypad()` to **after** the network fee label assertion
(instead of before)
- The fee label needs time to appear (60s timeout) while the quote is
fetched; dismissing the keypad first could interfere with quote loading
or obscure the assertion
- Waiting for the fee label first, then dismissing the keypad, matches
the correct user flow
### `TestSnaps.ts`
- Removed `checkStability: true` from date-time picker, date picker, and
time picker OK button taps
- On iOS, calendar/time picker animations prevent Detox’s stability
check from passing, causing the interactive UI snap test to time out
- The OK button is stationary; the calendar view above it is what
animates
### `onramp-unified-buy.spec.ts`
- Fixed a flaky test caused by a race with remote feature flag loading:
1. Seeded V1/V2 flags into `RemoteFeatureFlagController` initial state
via `.withRampsUnifiedBuyRemoteFlagsSeededForE2E()`, so flags are
available on first render without waiting for the mock server
2. Switched mock server flags from
`remoteFeatureFlagRampsUnifiedEnabled` (`minimumVersion: '7.63.0'`) to
`remoteFeatureFlagRampsUnifiedMatrixForE2E(true, true)` (`'0.0.0'`),
keeping consistency with seeded state if the controller refreshes
mid-test
## `stake-action-smoke.spec.ts`
- Increased the Earn button visibility timeout from **45s → 60s**
- The Earn button depends on balance loading from fixture state, which
can take longer on Android / slower CI machines

## **Changelog**

<!-- mms-check: type=changelog required=true -->

<!--
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:

## **Related issues**

<!-- mms-check: type=issue-link required=true -->

Fixes: test falkiness on stake and bridge tests

## **Manual testing steps**

<!-- mms-check: type=manual-testing required=true -->

N/A

## **Screenshots/Recordings**

<!-- mms-check: type=screenshot required=true -->

<!-- If applicable, add screenshots and/or recordings to visualize the
before and after of your change. -->

### **Before**



https://github.com/user-attachments/assets/6d8b3707-917d-46a6-969d-943172f032f2



### **After**

<!-- [screenshots/recordings] -->

## **Pre-merge author checklist**

<!-- mms-check: type=checklist required=true -->

<!--
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`.
-->

- [x] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [x] 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]
> **Low Risk**
> Changes are limited to E2E tests and mock helpers; production wallet
code is untouched.
> 
> **Overview**
> Stabilizes smoke E2E by tightening waits, mocks, and test
ordering—**no production app behavior changes**.
> 
> **Swap / STX mocks:** Sentinel URL matchers now accept versioned
`*.api.cx.metamask.io` paths (not only `transaction.api…`). New mocks
cover the **EIP-7702 gasless relay** path (`eth_sendRelayTransaction` +
relay polling), with a dummy Anvil tx so receipts resolve. **Gasless
swap** specs are **re-enabled** (removed `it.skip`).
> 
> **Bridge smoke:** `dismissKeypad()` runs **after** waiting for the
network fee label (60s), so quote loading isn’t disrupted.
> 
> **Onramp unified buy:** Fixtures seed unified-buy remote flags via
`.withRampsUnifiedBuyRemoteFlagsSeededForE2E()` and mocks use
`remoteFeatureFlagRampsUnifiedMatrixForE2E` to avoid flag-load races.
> 
> **Detox / timing:** `waitForAppReady` default **60s**; stake Earn
button wait **60s**; TestSnaps picker OK taps drop `checkStability` on
iOS where calendar animation blocks stability checks.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
297372c. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
@github-actions

Copy link
Copy Markdown
Contributor

🔍 Smart E2E Test Selection

⏭️ Smart E2E selection skipped - PR targets a release or stable branch (release/* or stable)

All E2E tests pre-selected.

View GitHub Actions results

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

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.

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 6309f03. Configure here.

},
],
},
}));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Relay mock success without hash

Medium Severity

The 7702 relay GET mock always returns VALIDATED, even when the Anvil impersonation/send step failed and relayTxHash was never set. The publish hook then treats relay as successful with a missing transactionHash, so EIP-7702 gasless E2E can fail or flake instead of surfacing the mock setup error.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 6309f03. Configure here.

@sleepytanya sleepytanya merged commit b1075ec into release/7.81.0 Jun 10, 2026
339 of 399 checks passed
@sleepytanya sleepytanya deleted the cherry-pick-7-81-0-0ea45fe branch June 10, 2026 21:49
@github-actions github-actions Bot locked and limited conversation to collaborators Jun 10, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

risk:high AI analysis: high risk size-M team-swaps-and-bridge Swaps and Bridge team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants