From c291d98e97c70bfe5c8021b5aabbad7f7052bf1e Mon Sep 17 00:00:00 2001
From: huggingbot <83656073+huggingbot@users.noreply.github.com>
Date: Mon, 3 Nov 2025 20:44:05 +0800
Subject: [PATCH 01/11] feat: Add MetaMetrics event for "Use different login
method" click during social login rehydration (#21903)
## **Description**
This PR adds MetaMetrics tracking for when users click the "Use a
different login method" button during social login rehydration flow
**What is the reason for the change?**
We need to track when users decide to switch login methods during the
rehydration phase
**What is the improvement/solution?**
Added a new MetaMetrics event `UseDifferentLoginMethodClicked` that
fires when:
- User is in social login flow
- Onboarding is not completed (rehydration phase)
- User clicks "Use a different login method" on the welcome back screen
## **Changelog**
CHANGELOG entry: null
## **Related issues**
Fixes: https://consensyssoftware.atlassian.net/browse/SL-241
## **Manual testing steps**
1. Start a social login import flow (Google or Apple)
2. Complete authentication with the provider
3. Navigate to the unlock/rehydration screen
4. Click "Use a different login method" button
5. Verify the `USE_DIFFERENT_LOGIN_METHOD_CLICKED` event is tracked with
`account_type: 'social'`
## **Screenshots/Recordings**
### **Before**
No MetaMetrics event tracked when clicking "Use a different login
method" during rehydration
### **After**
`USE_DIFFERENT_LOGIN_METHOD_CLICKED` event tracked with proper
account_type property
## **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**
- [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.
---
> [!NOTE]
> Adds a new MetaMetrics event and triggers it when users in OAuth
rehydration click "Use a different login method".
>
> - **Analytics**
> - Add `USE_DIFFERENT_LOGIN_METHOD_CLICKED` to `EVENT_NAME` and expose
via `MetaMetricsEvents` in `app/core/Analytics/MetaMetrics.events.ts`.
> - **Login**
> - In `app/components/Views/Login/index.tsx`, fire
`MetaMetricsEvents.USE_DIFFERENT_LOGIN_METHOD_CLICKED` with `{
account_type: 'social' }` when `handleUseOtherMethod` is used during
OAuth onboarding rehydration.
>
> Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
99512087a1294d922693f7bf3a0f6a3f73538df9. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).
---
app/components/Views/Login/index.tsx | 5 +++++
app/core/Analytics/MetaMetrics.events.ts | 4 ++++
2 files changed, 9 insertions(+)
diff --git a/app/components/Views/Login/index.tsx b/app/components/Views/Login/index.tsx
index 344d8677aaf..f1b61065660 100644
--- a/app/components/Views/Login/index.tsx
+++ b/app/components/Views/Login/index.tsx
@@ -364,6 +364,11 @@ const Login: React.FC = ({ saveOnboardingEvent }) => {
};
const handleUseOtherMethod = () => {
+ if (isComingFromOauthOnboarding) {
+ track(MetaMetricsEvents.USE_DIFFERENT_LOGIN_METHOD_CLICKED, {
+ account_type: 'social',
+ });
+ }
navigation.goBack();
OAuthService.resetOauthState();
};
diff --git a/app/core/Analytics/MetaMetrics.events.ts b/app/core/Analytics/MetaMetrics.events.ts
index 2e58ea3e48a..8a07dd00855 100644
--- a/app/core/Analytics/MetaMetrics.events.ts
+++ b/app/core/Analytics/MetaMetrics.events.ts
@@ -145,6 +145,7 @@ enum EVENT_NAME {
REHYDRATION_PASSWORD_FAILED = 'Rehydration Password Failed',
PASSWORD_CHANGED = 'Password Changed',
FORGOT_PASSWORD_CLICKED = 'Forgot Password Clicked',
+ USE_DIFFERENT_LOGIN_METHOD_CLICKED = 'Use Different Login Method Clicked',
RESET_WALLET = 'Reset Wallet',
// Account
@@ -767,6 +768,9 @@ const events = {
),
PASSWORD_CHANGED: generateOpt(EVENT_NAME.PASSWORD_CHANGED),
FORGOT_PASSWORD_CLICKED: generateOpt(EVENT_NAME.FORGOT_PASSWORD_CLICKED),
+ USE_DIFFERENT_LOGIN_METHOD_CLICKED: generateOpt(
+ EVENT_NAME.USE_DIFFERENT_LOGIN_METHOD_CLICKED,
+ ),
RESET_WALLET: generateOpt(EVENT_NAME.RESET_WALLET),
SWITCHED_ACCOUNT: generateOpt(EVENT_NAME.SWITCHED_ACCOUNT),
BROWSER_OPENED: generateOpt(EVENT_NAME.BROWSER_OPENED),
From b8a8f61b3155efa6e06976725bfc3e9a1c90d5ad Mon Sep 17 00:00:00 2001
From: OGPoyraz
Date: Mon, 3 Nov 2025 14:20:40 +0100
Subject: [PATCH 02/11] fix: cp-7.58.0 Make burn and null address sending a
blocking alert (#22047)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
## **Description**
This PR aims to make burn/null address sending alert to be a blocking
alert.
See the task details for further information
## **Changelog**
CHANGELOG entry: Make burn and null address sending a blocking alert
## **Related issues**
Fixes: https://github.com/MetaMask/metamask-mobile/issues/22046
## **Manual testing steps**
N/A
## **Screenshots/Recordings**
### **Before**
### **After**
## **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.
---
> [!NOTE]
> Make burn/null address alerts blocking and update tests accordingly.
>
> - **Alerts**:
> - `useBurnAddressAlert`: Set `isBlocking: true` for `BurnAddress`
alert in `Severity.Danger`.
> - **Tests**:
> - Update `useBurnAddressAlert.test.ts` to expect `isBlocking: true`
for burn/null address cases (direct and nested recipients,
mixed/uppercase casing).
>
> Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
9093654edde09d3d7ba119e51e058b936e77404f. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).
---
.../confirmations/hooks/alerts/useBurnAddressAlert.test.ts | 6 +++---
.../Views/confirmations/hooks/alerts/useBurnAddressAlert.ts | 2 +-
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/app/components/Views/confirmations/hooks/alerts/useBurnAddressAlert.test.ts b/app/components/Views/confirmations/hooks/alerts/useBurnAddressAlert.test.ts
index 5d1041754e5..fd2ab52746e 100644
--- a/app/components/Views/confirmations/hooks/alerts/useBurnAddressAlert.test.ts
+++ b/app/components/Views/confirmations/hooks/alerts/useBurnAddressAlert.test.ts
@@ -56,7 +56,7 @@ describe('useBurnAddressAlert', () => {
key: AlertKeys.BurnAddress,
field: RowAlertKey.BurnAddress,
severity: Severity.Danger,
- isBlocking: false,
+ isBlocking: true,
});
expect(result.current[0].title).toBeDefined();
expect(result.current[0].message).toBeDefined();
@@ -72,7 +72,7 @@ describe('useBurnAddressAlert', () => {
key: AlertKeys.BurnAddress,
field: RowAlertKey.BurnAddress,
severity: Severity.Danger,
- isBlocking: false,
+ isBlocking: true,
});
});
@@ -100,7 +100,7 @@ describe('useBurnAddressAlert', () => {
key: AlertKeys.BurnAddress,
field: RowAlertKey.BurnAddress,
severity: Severity.Danger,
- isBlocking: false,
+ isBlocking: true,
});
});
diff --git a/app/components/Views/confirmations/hooks/alerts/useBurnAddressAlert.ts b/app/components/Views/confirmations/hooks/alerts/useBurnAddressAlert.ts
index c243d018fd7..94b3f40583d 100644
--- a/app/components/Views/confirmations/hooks/alerts/useBurnAddressAlert.ts
+++ b/app/components/Views/confirmations/hooks/alerts/useBurnAddressAlert.ts
@@ -41,7 +41,7 @@ export function useBurnAddressAlert(): Alert[] {
message: strings('alert_system.burn_address.message'),
title: strings('alert_system.burn_address.title'),
severity: Severity.Danger,
- isBlocking: false,
+ isBlocking: true,
},
];
}, [hasBurnAddressRecipient]);
From 2bae3fafbb15028ded62aef3e78b17038cf6b17b Mon Sep 17 00:00:00 2001
From: ieow <4881057+ieow@users.noreply.github.com>
Date: Mon, 3 Nov 2025 21:28:08 +0800
Subject: [PATCH 03/11] fix: remove not needed workaround on e2e (#21992)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
## **Description**
Remove not needed workaround for change password e2e test issue
## **Changelog**
CHANGELOG entry: null
## **Related issues**
Fixes:
https://github.com/MetaMask/metamask-mobile/issues/16758
## **Manual testing steps**
```gherkin
Feature: my feature name
Scenario: user [verb for user action]
Given [describe expected initial app state]
When user [verb for user action]
Then [describe expected outcome]
```
## **Screenshots/Recordings**
### **Before**
### **After**
## **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.
---
> [!NOTE]
> Remove iOS CI-specific workaround from the change-password flow in the
delete wallet E2E test.
>
> - **E2E tests**:
> - Remove iOS CI-specific workaround in
`e2e/specs/settings/delete-wallet.spec.ts` that tapped
`TabBarComponent.tapWallet()` after changing password.
>
> Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
6e9f9314cf0e572c9251bb3549d861bf2d822a29. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).
---
e2e/specs/settings/delete-wallet.spec.ts | 7 -------
1 file changed, 7 deletions(-)
diff --git a/e2e/specs/settings/delete-wallet.spec.ts b/e2e/specs/settings/delete-wallet.spec.ts
index e88210f9ae7..c2cee1d3c3a 100644
--- a/e2e/specs/settings/delete-wallet.spec.ts
+++ b/e2e/specs/settings/delete-wallet.spec.ts
@@ -54,13 +54,6 @@ describe(
await ChangePasswordView.tapIUnderstandCheckBox();
await ChangePasswordView.tapSubmitButton();
- // bug on CI when tap wallet button makes change password continue
- // github issue: https://github.com/MetaMask/metamask-mobile/issues/16758
- // TODO: remove this once the issue is fixed
- if (device.getPlatform() === 'ios' && process.env.CI) {
- await TabBarComponent.tapWallet();
- }
-
//wait for screen transitions after password change
await Assertions.expectElementToNotBeVisible(
ChangePasswordView.submitButton,
From 2b8d519c8483e1d3dcabf8616e097861513316fc Mon Sep 17 00:00:00 2001
From: Ulisses Ferreira
Date: Mon, 3 Nov 2025 13:52:52 +0000
Subject: [PATCH 04/11] fix: Tron addresses not being derived for all accounts
(#22050)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
## **Description**
We were not getting Tron addresses for all BIP44 accounts due to a
mismatch between what the `multichain-account-service`'s `TrxProvider`
called the Snap with and what the Snap's keyring handler expected. Both
of these dependencies have been fixed. This PR bumps the version of both
dependencies to a healthy one.
## **Changelog**
CHANGELOG entry: Bump `@metamask/multichain-account-service` to version
2.0.1
CHANGELOG entry: Bump `@metamask/tron-wallet-snap` to version 1.6.1
## **Related issues**
Fixes: NWNT-646
## **Manual testing steps**
```gherkin
Feature: Tron address generation
Scenario:
When user finishes onboarding with his Seed Phrase
Then we should have X accounts initialized and all of them should have a Tron address associated with them
When user finishes onboarding with his Seed Phrase and adds a new account
Then the new account should have a Tron address associated with it
```
## **Screenshots/Recordings**
### **Before**
n/a
### **After**
n/a
## **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.
---
> [!NOTE]
> Bumps `@metamask/multichain-account-service` to ^2.0.1 and
`@metamask/tron-wallet-snap` to ^1.6.1 with corresponding lockfile
updates.
>
> - **Dependencies**:
> - Bump `@metamask/multichain-account-service` to `^2.0.1` in
`package.json` (updated `yarn.lock`).
> - Bump `@metamask/tron-wallet-snap` to `^1.6.1` in `package.json`
(updated `yarn.lock`).
>
> Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
f7dc608171e6ee572b0fe063e781834321f2f248. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).
---
package.json | 4 ++--
yarn.lock | 20 ++++++++++----------
2 files changed, 12 insertions(+), 12 deletions(-)
diff --git a/package.json b/package.json
index 39c673fd52a..20577bae664 100644
--- a/package.json
+++ b/package.json
@@ -244,7 +244,7 @@
"@metamask/metamask-eth-abis": "3.1.1",
"@metamask/mobile-wallet-protocol-core": "^0.3.1",
"@metamask/mobile-wallet-protocol-wallet-client": "^0.2.1",
- "@metamask/multichain-account-service": "^2.0.0",
+ "@metamask/multichain-account-service": "^2.0.1",
"@metamask/multichain-api-client": "^0.7.0",
"@metamask/multichain-api-middleware": "1.2.4",
"@metamask/multichain-network-controller": "^2.0.0",
@@ -289,7 +289,7 @@
"@metamask/swaps-controller": "^15.0.0",
"@metamask/token-search-discovery-controller": "^4.0.0",
"@metamask/transaction-controller": "patch:@metamask/transaction-controller@npm%3A61.0.0#~/.yarn/patches/@metamask-transaction-controller-npm-61.0.0-cccac388c7.patch",
- "@metamask/tron-wallet-snap": "^1.5.1",
+ "@metamask/tron-wallet-snap": "^1.6.1",
"@metamask/utils": "^11.8.1",
"@ngraveio/bc-ur": "^1.1.6",
"@nktkas/hyperliquid": "^0.25.4",
diff --git a/yarn.lock b/yarn.lock
index d4156207fbf..85eca594b1f 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -7923,9 +7923,9 @@ __metadata:
languageName: node
linkType: hard
-"@metamask/multichain-account-service@npm:^2.0.0":
- version: 2.0.0
- resolution: "@metamask/multichain-account-service@npm:2.0.0"
+"@metamask/multichain-account-service@npm:^2.0.1":
+ version: 2.0.1
+ resolution: "@metamask/multichain-account-service@npm:2.0.1"
dependencies:
"@ethereumjs/util": "npm:^9.1.0"
"@metamask/base-controller": "npm:^9.0.0"
@@ -7948,7 +7948,7 @@ __metadata:
"@metamask/providers": ^22.0.0
"@metamask/snaps-controllers": ^14.0.0
webextension-polyfill: ^0.10.0 || ^0.11.0 || ^0.12.0
- checksum: 10/40e84247ca763b508b0a7db654b5b36f8062126f9323beefc56b938fe85b1cced452f474e3a9a9154a5b8dd6e00945bc4ba8a1f1a54cf09631266abdd30e1334
+ checksum: 10/787f4bdb486cd32a52951edb66a384a1676086e5daab9fc4318cf353a12938aab5d297936c6bcb3face2b725a858820d569fc57c9014b8f6bcd7bc71347801d0
languageName: node
linkType: hard
@@ -8914,10 +8914,10 @@ __metadata:
languageName: node
linkType: hard
-"@metamask/tron-wallet-snap@npm:^1.5.1":
- version: 1.5.1
- resolution: "@metamask/tron-wallet-snap@npm:1.5.1"
- checksum: 10/89bab80d3d9a6892650da1904f5312a6b0a1b07070757d911373463b8b95baa2daeeea605df7541035948bfb75621170b52d82b8c39930e914ad04b7dce9b57b
+"@metamask/tron-wallet-snap@npm:^1.6.1":
+ version: 1.6.1
+ resolution: "@metamask/tron-wallet-snap@npm:1.6.1"
+ checksum: 10/549f0dad4bfaee091bb5ff9bc3d5b9db124121ac77c40da67261d32f71a239cd4db0914436e0c9fb5a2eb9b82b254078d86b41dab6ba32b92359954595fc39c3
languageName: node
linkType: hard
@@ -34328,7 +34328,7 @@ __metadata:
"@metamask/mobile-provider": "npm:^3.0.0"
"@metamask/mobile-wallet-protocol-core": "npm:^0.3.1"
"@metamask/mobile-wallet-protocol-wallet-client": "npm:^0.2.1"
- "@metamask/multichain-account-service": "npm:^2.0.0"
+ "@metamask/multichain-account-service": "npm:^2.0.1"
"@metamask/multichain-api-client": "npm:^0.7.0"
"@metamask/multichain-api-middleware": "npm:1.2.4"
"@metamask/multichain-network-controller": "npm:^2.0.0"
@@ -34378,7 +34378,7 @@ __metadata:
"@metamask/test-dapp-solana": "npm:^0.3.0"
"@metamask/token-search-discovery-controller": "npm:^4.0.0"
"@metamask/transaction-controller": "patch:@metamask/transaction-controller@npm%3A61.0.0#~/.yarn/patches/@metamask-transaction-controller-npm-61.0.0-cccac388c7.patch"
- "@metamask/tron-wallet-snap": "npm:^1.5.1"
+ "@metamask/tron-wallet-snap": "npm:^1.6.1"
"@metamask/utils": "npm:^11.8.1"
"@ngraveio/bc-ur": "npm:^1.1.6"
"@nktkas/hyperliquid": "npm:^0.25.4"
From c0e1a90e811ef34a1f36a82cf94ff2e2d70fbcc9 Mon Sep 17 00:00:00 2001
From: cmd-ob
Date: Mon, 3 Nov 2025 14:02:26 +0000
Subject: [PATCH 05/11] test: Add waitForElementToDisappear option for confirm
button (#22055)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
## **Description**
- Add option to wait for the confirm button to disappear on Android,
addressing cases where the button may be obscured, this should force and
error and cause the action to be retried
## **Changelog**
CHANGELOG entry:
## **Related issues**
Fixes:
## **Manual testing steps**
```gherkin
Feature: my feature name
Scenario: user [verb for user action]
Given [describe expected initial app state]
When user [verb for user action]
Then [describe expected outcome]
```
## **Screenshots/Recordings**
### **Before**
### **After**
## **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
- [ ] 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.
---
> [!NOTE]
> Updates the e2e confirm action to wait for the confirm button to
disappear on Android before proceeding.
>
> - **E2E / Browser Confirmations**
> - `e2e/pages/Browser/Confirmations/FooterActions.ts`
> - Update `tapConfirmButton()` to pass `waitForElementToDisappear`
(Android-only) to `Gestures.waitAndTap`.
>
> Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
f51e416954143c6a21615acd1545a70bfb4dd000. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).
---
e2e/pages/Browser/Confirmations/FooterActions.ts | 1 +
1 file changed, 1 insertion(+)
diff --git a/e2e/pages/Browser/Confirmations/FooterActions.ts b/e2e/pages/Browser/Confirmations/FooterActions.ts
index f3930303108..b304b9c80e4 100644
--- a/e2e/pages/Browser/Confirmations/FooterActions.ts
+++ b/e2e/pages/Browser/Confirmations/FooterActions.ts
@@ -17,6 +17,7 @@ class FooterActions {
await Gestures.waitAndTap(this.confirmButton, {
elemDescription: 'Confirm button',
delay: 1800,
+ waitForElementToDisappear: device.getPlatform() === 'android',
});
}
From 96e502424278680d7ba05443d12e24a1e0b67cb3 Mon Sep 17 00:00:00 2001
From: Pedro Figueiredo
Date: Mon, 3 Nov 2025 14:46:54 +0000
Subject: [PATCH 06/11] feat: Await simulation results before disclosing gas
fees (#22007)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
## **Description**
Adds a skeleton loader to hide the gas fees indicator until simulation
details are determined and gasUsed can be shown.
## **Changelog**
CHANGELOG entry:
## **Related issues**
Fixes:
## **Manual testing steps**
```gherkin
Feature: my feature name
Scenario: user [verb for user action]
Given [describe expected initial app state]
When user [verb for user action]
Then [describe expected outcome]
```
## **Screenshots/Recordings**
### **Before**
### **After**
## **Pre-merge author checklist**
- [ ] 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).
- [ ] I've completed the PR template to the best of my ability
- [ ] I’ve included tests if applicable
- [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [ ] 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.
---
> [!NOTE]
> Adds a skeleton loader and waits for simulation results before
showing/editing gas fees in confirmations.
>
> - **UI (Gas Fees)**
> - Adds `SkeletonPlaceholder` and `SkeletonEstimationInfo` and defers
gas fee display until simulation data is available
(`isSimulationLoading`).
> - Integrates `useBalanceChanges` with `simulationData`, `chainId`, and
`networkClientId` from `useTransactionMetadataRequest` to gate
rendering.
> - Updates `EstimationInfo`/`RenderEstimationInfo` logic to show
skeleton or "Paid by MetaMask" when sponsored; otherwise display
fiat/native and `SelectedGasFeeToken`.
> - Disables clickable edit state while simulation is loading.
> - **Tests**
> - Mocks `useBalanceChanges` and injects `simulationData` into test
state; adjusts expectations across gas-fee row tests.
> - Updates contract interaction test to mock `useBalanceChanges` and
maintain existing assertions.
>
> Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
ce0b2fb07890b26b232ca5532b765f357fbddca4. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).
---
.../contract-interaction.test.tsx | 7 ++
.../gas-fee-details-row.test.tsx | 74 +++++++++++++------
.../gas-fee-details-row.tsx | 55 +++++++++++---
3 files changed, 104 insertions(+), 32 deletions(-)
diff --git a/app/components/Views/confirmations/components/info/contract-interaction/contract-interaction.test.tsx b/app/components/Views/confirmations/components/info/contract-interaction/contract-interaction.test.tsx
index cac8aa89918..86e5ee44210 100644
--- a/app/components/Views/confirmations/components/info/contract-interaction/contract-interaction.test.tsx
+++ b/app/components/Views/confirmations/components/info/contract-interaction/contract-interaction.test.tsx
@@ -10,6 +10,7 @@ import {
upgradeAccountConfirmation,
} from '../../../../../../util/test/confirm-data-helpers';
import renderWithProvider from '../../../../../../util/test/renderWithProvider';
+import useBalanceChanges from '../../../../../UI/SimulationDetails/useBalanceChanges';
// eslint-disable-next-line import/no-namespace
import * as EditNonceHook from '../../../../../../components/hooks/useEditNonce';
import { useConfirmActions } from '../../../hooks/useConfirmActions';
@@ -36,6 +37,7 @@ jest.mock('../../../hooks/useAutomaticGasFeeTokenSelect');
jest.mock('../../../hooks/alerts/useInsufficientBalanceAlert', () => ({
useInsufficientBalanceAlert: jest.fn().mockReturnValue([]),
}));
+jest.mock('../../../../../UI/SimulationDetails/useBalanceChanges');
jest.mock('../../../hooks/7702/use7702TransactionType', () => ({
use7702TransactionType: jest
@@ -111,6 +113,7 @@ describe('ContractInteraction', () => {
const mockUseConfirmationMetricEvents = jest.mocked(
useConfirmationMetricEvents,
);
+ const mockUseBalanceChanges = jest.mocked(useBalanceChanges);
beforeEach(() => {
jest.clearAllMocks();
@@ -122,6 +125,10 @@ describe('ContractInteraction', () => {
mockUseConfirmationMetricEvents.mockReturnValue({
trackPageViewedEvent: mockTrackPageViewedEvent,
} as unknown as ReturnType);
+ mockUseBalanceChanges.mockReturnValue({
+ pending: false,
+ value: [],
+ });
const mockTxId = '7e62bcb1-a4e9-11ef-9b51-ddf21c91a998';
jest
diff --git a/app/components/Views/confirmations/components/rows/transactions/gas-fee-details-row/gas-fee-details-row.test.tsx b/app/components/Views/confirmations/components/rows/transactions/gas-fee-details-row/gas-fee-details-row.test.tsx
index 18f0de583e0..c12cab9c61b 100644
--- a/app/components/Views/confirmations/components/rows/transactions/gas-fee-details-row/gas-fee-details-row.test.tsx
+++ b/app/components/Views/confirmations/components/rows/transactions/gas-fee-details-row/gas-fee-details-row.test.tsx
@@ -9,10 +9,12 @@ import { useConfirmationMetricEvents } from '../../../../hooks/metrics/useConfir
import { TOOLTIP_TYPES } from '../../../../../../../core/Analytics/events/confirmations';
import GasFeesDetailsRow from './gas-fee-details-row';
import { toHex } from '@metamask/controller-utils';
+import { SimulationData } from '@metamask/transaction-controller';
import { useSelectedGasFeeToken } from '../../../../hooks/gas/useGasFeeToken';
import { useIsGaslessSupported } from '../../../../hooks/gas/useIsGaslessSupported';
import { useInsufficientBalanceAlert } from '../../../../hooks/alerts/useInsufficientBalanceAlert';
import useHideFiatForTestnet from '../../../../../../hooks/useHideFiatForTestnet';
+import useBalanceChanges from '../../../../../../UI/SimulationDetails/useBalanceChanges';
jest.mock('../../../gas/gas-speed', () => ({
GasSpeed: () => null,
@@ -34,6 +36,7 @@ jest.mock('../../../../hooks/gas/useIsGaslessSupported');
jest.mock('../../../../hooks/alerts/useInsufficientBalanceAlert');
jest.mock('../../../../../../hooks/useHideFiatForTestnet');
jest.mock('../../../../hooks/tokens/useTokenWithBalance');
+jest.mock('../../../../../../UI/SimulationDetails/useBalanceChanges');
const GAS_FEE_TOKEN_MOCK: ReturnType = {
amount: toHex(10000),
@@ -56,6 +59,31 @@ const GAS_FEE_TOKEN_MOCK: ReturnType = {
transferTransaction: {},
};
+const SIMULATION_DATA_MOCK: SimulationData = {
+ nativeBalanceChange: {
+ previousBalance: '0x0',
+ newBalance: '0x0',
+ difference: '0x0',
+ isDecrease: false,
+ },
+ tokenBalanceChanges: [],
+};
+
+const createStateWithSimulationData = (
+ baseState = stakingDepositConfirmationState,
+) => {
+ const stateWithSimulation = cloneDeep(baseState);
+ const transactions =
+ stateWithSimulation.engine.backgroundState.TransactionController
+ .transactions;
+
+ if (transactions?.[0]) {
+ transactions[0].simulationData = cloneDeep(SIMULATION_DATA_MOCK);
+ }
+
+ return stateWithSimulation;
+};
+
describe('GasFeesDetailsRow', () => {
const useConfirmationMetricEventsMock = jest.mocked(
useConfirmationMetricEvents,
@@ -67,6 +95,7 @@ describe('GasFeesDetailsRow', () => {
useInsufficientBalanceAlert,
);
const mockUseHideFiatForTestnet = jest.mocked(useHideFiatForTestnet);
+ const mockUseBalanceChanges = jest.mocked(useBalanceChanges);
beforeEach(() => {
useConfirmationMetricEventsMock.mockReturnValue({
@@ -79,11 +108,15 @@ describe('GasFeesDetailsRow', () => {
});
mockUseInsufficientBalanceAlert.mockReturnValue([]);
mockUseHideFiatForTestnet.mockReturnValue(false);
+ mockUseBalanceChanges.mockReturnValue({
+ pending: false,
+ value: [],
+ });
});
it('contains required text', async () => {
const { getByText } = renderWithProvider(, {
- state: stakingDepositConfirmationState,
+ state: createStateWithSimulationData(),
});
expect(getByText('Network Fee')).toBeDefined();
expect(getByText('$0.34')).toBeDefined();
@@ -91,9 +124,8 @@ describe('GasFeesDetailsRow', () => {
});
it('shows fiat if showFiatOnTestnets is true', async () => {
- const clonedStakingDepositConfirmationState = cloneDeep(
- stakingDepositConfirmationState,
- );
+ const clonedStakingDepositConfirmationState =
+ createStateWithSimulationData();
clonedStakingDepositConfirmationState.engine.backgroundState.TransactionController.transactions[0].chainId =
NETWORKS_CHAIN_ID.SEPOLIA;
@@ -104,9 +136,8 @@ describe('GasFeesDetailsRow', () => {
});
it('hides fiat if showFiatOnTestnets is false', async () => {
- const clonedStakingDepositConfirmationState = cloneDeep(
- stakingDepositConfirmationState,
- );
+ const clonedStakingDepositConfirmationState =
+ createStateWithSimulationData();
clonedStakingDepositConfirmationState.engine.backgroundState.TransactionController.transactions[0].chainId =
NETWORKS_CHAIN_ID.SEPOLIA;
clonedStakingDepositConfirmationState.settings.showFiatOnTestnets = false;
@@ -118,9 +149,8 @@ describe('GasFeesDetailsRow', () => {
});
it('hides fiat if nativeConversionRate is undefined', async () => {
- const clonedStakingDepositConfirmationState = cloneDeep(
- stakingDepositConfirmationState,
- );
+ const clonedStakingDepositConfirmationState =
+ createStateWithSimulationData();
// No type is exported for CurrencyRate, so we need to cast it to the correct type
clonedStakingDepositConfirmationState.engine.backgroundState.CurrencyRateController.currencyRates.ETH =
@@ -138,7 +168,7 @@ describe('GasFeesDetailsRow', () => {
it('tracks tooltip clicked event', async () => {
const { getByTestId } = renderWithProvider(, {
- state: stakingDepositConfirmationState,
+ state: createStateWithSimulationData(),
});
fireEvent.press(getByTestId('info-row-tooltip-open-btn'));
@@ -153,7 +183,7 @@ describe('GasFeesDetailsRow', () => {
it('shows gas speed row', async () => {
const { getByText } = renderWithProvider(, {
- state: stakingDepositConfirmationState,
+ state: createStateWithSimulationData(),
});
expect(getByText('Speed')).toBeDefined();
});
@@ -162,7 +192,7 @@ describe('GasFeesDetailsRow', () => {
const { queryByText } = renderWithProvider(
,
{
- state: stakingDepositConfirmationState,
+ state: createStateWithSimulationData(),
},
);
@@ -173,7 +203,7 @@ describe('GasFeesDetailsRow', () => {
const { getByText, queryByText } = renderWithProvider(
,
{
- state: stakingDepositConfirmationState,
+ state: createStateWithSimulationData(),
},
);
@@ -188,7 +218,7 @@ describe('GasFeesDetailsRow', () => {
>,
);
const { getByText } = renderWithProvider(, {
- state: stakingDepositConfirmationState,
+ state: createStateWithSimulationData(),
});
expect(getByText('USDC')).toBeDefined();
@@ -197,9 +227,8 @@ describe('GasFeesDetailsRow', () => {
it('shows native amount when is a testnet', async () => {
mockUseHideFiatForTestnet.mockReturnValue(true);
- const clonedStakingDepositConfirmationState = cloneDeep(
- stakingDepositConfirmationState,
- );
+ const clonedStakingDepositConfirmationState =
+ createStateWithSimulationData();
clonedStakingDepositConfirmationState.engine.backgroundState.TransactionController.transactions[0].chainId =
NETWORKS_CHAIN_ID.SEPOLIA;
@@ -212,9 +241,8 @@ describe('GasFeesDetailsRow', () => {
});
it(`shows 'Paid by MetaMask' when gas is sponsored`, async () => {
- const clonedStakingDepositConfirmationState = cloneDeep(
- stakingDepositConfirmationState,
- );
+ const clonedStakingDepositConfirmationState =
+ createStateWithSimulationData();
clonedStakingDepositConfirmationState.engine.backgroundState.TransactionController.transactions[0].isGasFeeSponsored = true;
const { getByText, queryByText } = renderWithProvider(
,
@@ -239,7 +267,7 @@ describe('GasFeesDetailsRow', () => {
);
const { queryByText } = renderWithProvider(, {
- state: stakingDepositConfirmationState,
+ state: createStateWithSimulationData(),
});
expect(queryByText('MetaMask fee: $0.12')).toBeNull();
@@ -259,7 +287,7 @@ describe('GasFeesDetailsRow', () => {
const { getByTestId, getByText } = renderWithProvider(
,
{
- state: stakingDepositConfirmationState,
+ state: createStateWithSimulationData(),
},
);
diff --git a/app/components/Views/confirmations/components/rows/transactions/gas-fee-details-row/gas-fee-details-row.tsx b/app/components/Views/confirmations/components/rows/transactions/gas-fee-details-row/gas-fee-details-row.tsx
index 0ba7f7df5bf..183d2bd7cb0 100644
--- a/app/components/Views/confirmations/components/rows/transactions/gas-fee-details-row/gas-fee-details-row.tsx
+++ b/app/components/Views/confirmations/components/rows/transactions/gas-fee-details-row/gas-fee-details-row.tsx
@@ -4,41 +4,55 @@ import {
} from '@metamask/transaction-controller';
import React, { useState } from 'react';
import { TouchableOpacity, View } from 'react-native';
+import SkeletonPlaceholder from 'react-native-skeleton-placeholder';
import { ConfirmationRowComponentIDs } from '../../../../../../../../e2e/selectors/Confirmation/ConfirmationView.selectors';
import { strings } from '../../../../../../../../locales/i18n';
import Icon, {
IconName,
IconSize,
} from '../../../../../../../component-library/components/Icons/Icon';
+import {
+ TextColor,
+ TextVariant,
+} from '../../../../../../../component-library/components/Texts/Text';
import Text from '../../../../../../../component-library/components/Texts/Text/Text';
import { useStyles } from '../../../../../../../component-library/hooks';
import { TOOLTIP_TYPES } from '../../../../../../../core/Analytics/events/confirmations';
import useHideFiatForTestnet from '../../../../../../hooks/useHideFiatForTestnet';
+import useBalanceChanges from '../../../../../../UI/SimulationDetails/useBalanceChanges';
import { useFeeCalculations } from '../../../../hooks/gas/useFeeCalculations';
import { useFeeCalculationsTransactionBatch } from '../../../../hooks/gas/useFeeCalculationsTransactionBatch';
+import { useSelectedGasFeeToken } from '../../../../hooks/gas/useGasFeeToken';
import { useConfirmationMetricEvents } from '../../../../hooks/metrics/useConfirmationMetricEvents';
import { useTransactionBatchesMetadata } from '../../../../hooks/transactions/useTransactionBatchesMetadata';
import { useTransactionMetadataRequest } from '../../../../hooks/transactions/useTransactionMetadataRequest';
+import { useAutomaticGasFeeTokenSelect } from '../../../../hooks/useAutomaticGasFeeTokenSelect';
+import { GasFeeTokenToast } from '../../../gas/gas-fee-token-toast';
import { GasSpeed } from '../../../gas/gas-speed';
+import { SelectedGasFeeToken } from '../../../gas/selected-gas-fee-token';
import { GasFeeModal } from '../../../modals/gas-fee-modal';
import AlertRow from '../../../UI/info-row/alert-row';
import { RowAlertKey } from '../../../UI/info-row/alert-row/constants';
import InfoSection from '../../../UI/info-row/info-section';
import styleSheet from './gas-fee-details-row.styles';
-import { SelectedGasFeeToken } from '../../../gas/selected-gas-fee-token';
-import { useSelectedGasFeeToken } from '../../../../hooks/gas/useGasFeeToken';
-import {
- TextColor,
- TextVariant,
-} from '../../../../../../../component-library/components/Texts/Text';
-import { GasFeeTokenToast } from '../../../gas/gas-fee-token-toast';
-import { useAutomaticGasFeeTokenSelect } from '../../../../hooks/useAutomaticGasFeeTokenSelect';
const PaidByMetaMask = () => (
{strings('transactions.paid_by_metamask')}
);
+
+const SkeletonEstimationInfo = () => (
+
+
+
+);
+
const EstimationInfo = ({
hideFiatForTestnet,
feeCalculations,
@@ -64,11 +78,22 @@ const EstimationInfo = ({
hideFiatForTestnet || !fiatValue
? styles.primaryValue
: styles.secondaryValue;
+ const transactionMetadata = useTransactionMetadataRequest();
+ const { chainId, simulationData, networkClientId } =
+ (transactionMetadata as TransactionMeta) ?? {};
+ const balanceChangesResult = useBalanceChanges({
+ chainId,
+ simulationData,
+ networkClientId,
+ });
+ const isSimulationLoading = !simulationData || balanceChangesResult.pending;
return (
{isGasFeeSponsored ? (
+ ) : isSimulationLoading ? (
+
) : (
<>
{displayValue && {displayValue}}
@@ -228,6 +253,15 @@ const GasFeesDetailsRow = ({
: strings('transactions.network_fee_tooltip');
const Container = noSection ? View : InfoSection;
+
+ const { chainId, simulationData, networkClientId } =
+ (transactionMetadata as TransactionMeta) ?? {};
+ const balanceChangesResult = useBalanceChanges({
+ chainId,
+ simulationData,
+ networkClientId,
+ });
+ const isSimulationLoading = !simulationData || balanceChangesResult.pending;
return (
<>
@@ -238,7 +272,10 @@ const GasFeesDetailsRow = ({
onTooltipPress={handleNetworkFeeTooltipClickedEvent}
>
- {disableUpdate || gasFeeToken || isGasFeeSponsored ? (
+ {disableUpdate ||
+ gasFeeToken ||
+ isGasFeeSponsored ||
+ isSimulationLoading ? (
Date: Mon, 3 Nov 2025 15:12:32 +0000
Subject: [PATCH 07/11] feat: use new asset comparisson (#22049)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
## **Description**
Use new asset sorting when balances are the same (or $0):
ETH Mainnet
ETH Linea
SOL Solana
BTC Bitcoin
TRX Tron
ETH Base
ETH Arbitrum
BNB Binance
ETH Optimism
POL Polygon
## **Changelog**
CHANGELOG entry: Changed order of assets when their fiat balances are
the same
## **Related issues**
Fixes: https://consensyssoftware.atlassian.net/browse/ASSETS-1259
## **Manual testing steps**
```gherkin
Feature: my feature name
Scenario: user [verb for user action]
Given [describe expected initial app state]
When user [verb for user action]
Then [describe expected outcome]
```
## **Screenshots/Recordings**
### **Before**
https://github.com/user-attachments/assets/e8474a20-740f-4503-831f-17f0747d0183
### **After**
https://github.com/user-attachments/assets/64b7f149-b5f5-45ca-9b68-fc9a4b23a417
## **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.
---
> [!NOTE]
> Adds priority-based asset sorting (fiat desc with native/network
tie-breakers) and integrates it into the assets selector with tests.
>
> - **Assets Sorting**:
> - Introduces `sortAssetsWithPriority` with
`compareFiatBalanceWithPriority` to sort by fiat desc, then prioritize
native assets and a default native chain order; fall back to name.
> - Defines `defaultNativeAssetOrder` (e.g., `POLYGON`, `OPTIMISM`,
`BSC`, `ARBITRUM`, `BASE`, `TRX`, `BTC`, `SOL`, `LINEA_MAINNET`,
`MAINNET`).
> - **Integration**:
> - Replaces `sortAssets` with `sortAssetsWithPriority` in
`app/selectors/assets/assets-list.ts` when applying `tokenSortConfig`.
> - **Tests**:
> - Adds `sortAssetsWithPriority.test.ts` covering name sort, fiat
balance sort, native vs token handling, and chain priority tie-breakers.
>
> Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
2f0a9dd2bd5f629a1f0e85fe8fef8088839352cf. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).
---
.../util/sortAssetsWithPriority.test.ts | 190 ++++++++++++++++++
.../UI/Tokens/util/sortAssetsWithPriority.ts | 82 ++++++++
app/selectors/assets/assets-list.ts | 4 +-
3 files changed, 274 insertions(+), 2 deletions(-)
create mode 100644 app/components/UI/Tokens/util/sortAssetsWithPriority.test.ts
create mode 100644 app/components/UI/Tokens/util/sortAssetsWithPriority.ts
diff --git a/app/components/UI/Tokens/util/sortAssetsWithPriority.test.ts b/app/components/UI/Tokens/util/sortAssetsWithPriority.test.ts
new file mode 100644
index 00000000000..59b501b9a61
--- /dev/null
+++ b/app/components/UI/Tokens/util/sortAssetsWithPriority.test.ts
@@ -0,0 +1,190 @@
+import type { Asset } from '@metamask/assets-controllers';
+import { CHAIN_IDS } from '@metamask/transaction-controller';
+import type { CaipChainId, Hex } from '@metamask/utils';
+import {
+ compareFiatBalanceWithPriority,
+ sortAssetsWithPriority,
+} from './sortAssetsWithPriority';
+
+function createMockAsset({
+ name,
+ fiatBalance,
+ isNative = true,
+ chainId = CHAIN_IDS.MAINNET,
+}: {
+ name: string;
+ fiatBalance?: number;
+ isNative?: boolean;
+ chainId?: Hex | CaipChainId;
+}): Asset {
+ return {
+ name,
+ fiat: fiatBalance === undefined ? undefined : { balance: fiatBalance },
+ isNative,
+ chainId,
+ } as unknown as Asset;
+}
+
+describe('sortAssetsWithPriority', () => {
+ function extractAssetNames(assets: Asset[]): string[] {
+ return assets.map((asset) => asset.name);
+ }
+
+ it('sorts by name when key is "name"', () => {
+ const assets = [
+ createMockAsset({ name: 'Asset B', fiatBalance: 400 }),
+ createMockAsset({ name: 'Asset A', fiatBalance: 600 }),
+ createMockAsset({ name: 'Asset Z', fiatBalance: 500 }),
+ ];
+
+ const sortedAssets = sortAssetsWithPriority(assets, {
+ key: 'name',
+ order: 'asc',
+ sortCallback: 'alphaNumeric',
+ });
+
+ expect(extractAssetNames(sortedAssets)).toStrictEqual([
+ 'Asset A',
+ 'Asset B',
+ 'Asset Z',
+ ]);
+ });
+
+ it('sorts by fiat balance when key is "tokenFiatAmount"', () => {
+ const assets = [
+ createMockAsset({ name: 'Asset B', fiatBalance: 400 }),
+ createMockAsset({ name: 'Asset A', fiatBalance: 600 }),
+ createMockAsset({ name: 'Asset Z', fiatBalance: 500 }),
+ ];
+
+ const sortedAssets = sortAssetsWithPriority(assets, {
+ key: 'tokenFiatAmount',
+ order: 'dsc',
+ sortCallback: 'stringNumeric',
+ });
+
+ expect(extractAssetNames(sortedAssets)).toStrictEqual([
+ 'Asset A',
+ 'Asset Z',
+ 'Asset B',
+ ]);
+ });
+});
+
+describe('compareFiatBalanceWithPriority', () => {
+ describe('fiat balance comparison', () => {
+ it('compares second value above first if the second asset has a fiat balance and the first asset does not', () => {
+ const assetA = createMockAsset({
+ name: 'Asset A',
+ fiatBalance: undefined,
+ });
+ const assetB = createMockAsset({ name: 'Asset B', fiatBalance: 400 });
+
+ const result = compareFiatBalanceWithPriority(assetA, assetB);
+
+ expect(result).toBeGreaterThan(0);
+ });
+
+ it('compares second value above the first value if both assets have fiat balances and the second value is greater', () => {
+ const assetA = createMockAsset({ name: 'Asset A', fiatBalance: 300 });
+ const assetB = createMockAsset({ name: 'Asset B', fiatBalance: 400 });
+
+ const result = compareFiatBalanceWithPriority(assetA, assetB);
+
+ expect(result).toBeGreaterThan(0);
+ });
+ });
+
+ describe('non-native asset comparison', () => {
+ it('returns -1 if the first asset is native and the second asset is not', () => {
+ const assetA = createMockAsset({ name: 'Asset A', isNative: true });
+ const assetB = createMockAsset({ name: 'Asset B', isNative: false });
+
+ const result = compareFiatBalanceWithPriority(assetA, assetB);
+
+ expect(result).toBeLessThan(0);
+ });
+
+ it('returns 1 if the second asset is native and the first asset is not', () => {
+ const assetA = createMockAsset({ name: 'Asset A', isNative: false });
+ const assetB = createMockAsset({ name: 'Asset B', isNative: true });
+
+ const result = compareFiatBalanceWithPriority(assetA, assetB);
+
+ expect(result).toBeGreaterThan(0);
+ });
+
+ it('compares name values if neither asset is native', () => {
+ const assetA = createMockAsset({ name: 'Asset A', isNative: false });
+ const assetB = createMockAsset({ name: 'Asset B', isNative: false });
+
+ const result = compareFiatBalanceWithPriority(assetA, assetB);
+
+ expect(result).toBeLessThan(0);
+ });
+ });
+
+ describe('native asset comparison', () => {
+ it('compares name values if neither asset is in the defaultNativeAssetOrder', () => {
+ const assetA = createMockAsset({
+ name: 'Asset A',
+ chainId: '0xeeeeeeeeeeeee1',
+ });
+ const assetB = createMockAsset({
+ name: 'Asset B',
+ chainId: '0xeeeeeeeeeeeee2',
+ });
+
+ const result = compareFiatBalanceWithPriority(assetA, assetB);
+
+ expect(result).toBeLessThan(0);
+ });
+
+ it('compares second value above first if the second asset is in the defaultNativeAssetOrder and the first asset is not', () => {
+ const assetA = createMockAsset({
+ name: 'Asset A',
+ chainId: '0xeeeeeeeeeeeee1',
+ });
+ const assetB = createMockAsset({
+ name: 'Asset B',
+ chainId: CHAIN_IDS.BASE,
+ });
+
+ const result = compareFiatBalanceWithPriority(assetA, assetB);
+
+ expect(result).toBeGreaterThan(0);
+ });
+
+ it('compares second value above first if both assets are in the defaultNativeAssetOrder and the second value has higher priority', () => {
+ const assetA = createMockAsset({
+ name: 'Asset A',
+ chainId: CHAIN_IDS.ARBITRUM,
+ });
+ const assetB = createMockAsset({
+ name: 'Asset B',
+ chainId: CHAIN_IDS.LINEA_MAINNET,
+ });
+
+ const result = compareFiatBalanceWithPriority(assetA, assetB);
+
+ expect(result).toBeGreaterThan(0);
+ });
+
+ it('compares second value above first if both assets are in the defaultNativeAssetOrder and the second value has higher priority (both values have balances of zero)', () => {
+ const assetA = createMockAsset({
+ name: 'Asset A',
+ chainId: CHAIN_IDS.ARBITRUM,
+ fiatBalance: 0,
+ });
+ const assetB = createMockAsset({
+ name: 'Asset B',
+ chainId: CHAIN_IDS.LINEA_MAINNET,
+ fiatBalance: 0,
+ });
+
+ const result = compareFiatBalanceWithPriority(assetA, assetB);
+
+ expect(result).toBeGreaterThan(0);
+ });
+ });
+});
diff --git a/app/components/UI/Tokens/util/sortAssetsWithPriority.ts b/app/components/UI/Tokens/util/sortAssetsWithPriority.ts
new file mode 100644
index 00000000000..3fa463e82e7
--- /dev/null
+++ b/app/components/UI/Tokens/util/sortAssetsWithPriority.ts
@@ -0,0 +1,82 @@
+import type { Asset } from '@metamask/assets-controllers';
+import { CHAIN_IDS } from '@metamask/transaction-controller';
+import type { CaipChainId, Hex } from '@metamask/utils';
+import { BtcScope, SolScope, TrxScope } from '@metamask/keyring-api';
+import { sortAssets, type SortCriteria } from './sortAssets';
+
+// These are the only two options for sorting assets
+// {"key": "name", "order": "asc", "sortCallback": "alphaNumeric"}
+// {"key": "tokenFiatAmount", "order": "dsc", "sortCallback": "stringNumeric"}
+export function sortAssetsWithPriority(
+ array: Asset[],
+ criteria: SortCriteria,
+): Asset[] {
+ if (criteria.key === 'name') {
+ return sortAssets(array, {
+ key: 'name',
+ order: 'asc',
+ sortCallback: 'alphaNumeric',
+ });
+ }
+
+ return [...array].sort(compareFiatBalanceWithPriority);
+}
+
+// Higher priority assets are last in the array to facilitate sorting
+const defaultNativeAssetOrder: (Hex | CaipChainId)[] = [
+ CHAIN_IDS.POLYGON,
+ CHAIN_IDS.OPTIMISM,
+ CHAIN_IDS.BSC,
+ CHAIN_IDS.ARBITRUM,
+ CHAIN_IDS.BASE,
+ TrxScope.Mainnet,
+ BtcScope.Mainnet,
+ SolScope.Mainnet,
+ CHAIN_IDS.LINEA_MAINNET,
+ CHAIN_IDS.MAINNET,
+];
+
+/**
+ * Compares assets by fiat balance with priority sorting.
+ *
+ * @param a - The first asset to compare.
+ * @param b - The second asset to compare.
+ * @returns A negative number if the first asset should appear before the second, a positive number if the first asset should appear after the second, or 0 if they are equal.
+ */
+export function compareFiatBalanceWithPriority(a: Asset, b: Asset) {
+ // If one of the fiat balances is greater than the other, return the comparison
+ const fiatBalanceComparison = (b.fiat?.balance ?? 0) - (a.fiat?.balance ?? 0);
+
+ // Only return comparison if it is not zero
+ if (fiatBalanceComparison) {
+ return fiatBalanceComparison;
+ }
+
+ // With equal fiat balances
+ // Always return native assets before token assets
+ // Apply the priority defined in defaultNativeAssetOrder if both are native assets
+ // If both assets are tokens or none is in the defaultNativeAssetOrder, compare by name
+ if (a.isNative && !b.isNative) {
+ return -1;
+ }
+
+ if (b.isNative && !a.isNative) {
+ return 1;
+ }
+
+ if (!a.isNative && !b.isNative) {
+ return a.name.localeCompare(b.name);
+ }
+
+ const nativeAssetOrderA = defaultNativeAssetOrder.indexOf(a.chainId);
+ const nativeAssetOrderB = defaultNativeAssetOrder.indexOf(b.chainId);
+
+ const nativeAssetOrderComparison = nativeAssetOrderB - nativeAssetOrderA;
+
+ if (nativeAssetOrderComparison) {
+ return nativeAssetOrderComparison;
+ }
+
+ // If neither asset is in the defaultNativeAssetOrder, compare by name
+ return a.name.localeCompare(b.name);
+}
diff --git a/app/selectors/assets/assets-list.ts b/app/selectors/assets/assets-list.ts
index f69fa90dfc8..dc5add73145 100644
--- a/app/selectors/assets/assets-list.ts
+++ b/app/selectors/assets/assets-list.ts
@@ -10,7 +10,6 @@ import { createSelector } from 'reselect';
import I18n from '../../../locales/i18n';
import { TokenI } from '../../components/UI/Tokens/types';
-import { sortAssets } from '../../components/UI/Tokens/util';
import { RootState } from '../../reducers';
import { formatWithThreshold } from '../../util/assets';
import { selectEvmNetworkConfigurationsByChainId } from '../networkController';
@@ -27,6 +26,7 @@ import {
TRON_RESOURCE_SYMBOLS_SET,
TronResourceSymbol,
} from '../../core/Multichain/constants';
+import { sortAssetsWithPriority } from '../../components/UI/Tokens/util/sortAssetsWithPriority';
export const selectAssetsBySelectedAccountGroup = createDeepEqualSelector(
(state: RootState) => {
@@ -234,7 +234,7 @@ export const selectSortedAssetsBySelectedAccountGroup = createDeepEqualSelector(
// Current sorting options
// {"key": "name", "order": "asc", "sortCallback": "alphaNumeric"}
// {"key": "tokenFiatAmount", "order": "dsc", "sortCallback": "stringNumeric"}
- const tokensSorted = sortAssets(
+ const tokensSorted = sortAssetsWithPriority(
assets.map((asset) => ({
...asset,
tokenFiatAmount: asset.fiat?.balance.toString(),
From 4f6ede117805de325d11bf1548113e1a54b813e7 Mon Sep 17 00:00:00 2001
From: Curtis David
Date: Mon, 3 Nov 2025 11:37:46 -0500
Subject: [PATCH 08/11] test: add intial predictions claim e2e test (#21546)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
## **Description**
The purpose of this PR is to add e2e coverage for the claim flow in
predictions.
## **Changelog**
CHANGELOG entry:
## **Related issues**
Fixes:
## **Manual testing steps**
```gherkin
Feature: my feature name
Scenario: user [verb for user action]
Given [describe expected initial app state]
When user [verb for user action]
Then [describe expected outcome]
```
## **Screenshots/Recordings**
### **Before**
### **After**
## **Pre-merge author checklist**
- [ ] 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).
- [ ] I've completed the PR template to the best of my ability
- [ ] I’ve included tests if applicable
- [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [ ] 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.
---
> [!NOTE]
> Adds predictions claim E2E tests and overhauls Polymarket mocks
(resolved/winning positions, balance/receipt handling), plus testID
updates and Polygon gas fee mock.
>
> - **E2E Tests (Predictions)**:
> - New spec `predict-claim-positions.spec.ts` to validate claim flow
end-to-end.
> - Refines cash-out spec to use new helpers/method names.
> - Adds `PredictClaimPage` and `PredictHelpers` (Portugal location
setup).
> - **E2E Mocks/Fixtures (Polymarket)**:
> - Splits resolved positions into
`POLYMARKET_RESOLVED_LOST_POSITIONS_RESPONSE` and separate
`POLYMARKET_WINNING_POSITIONS_RESPONSE`; controllable inclusion via
`POLYMARKET_POSITIONS_WITH_WINNINGS_MOCKS`.
> - New balance update flow `POLYMARKET_UPDATE_USDC_BALANCE_MOCKS`
(handles USDC balance refresh, nonce, receipts) and removal of claimed
positions `POLYMARKET_REMOVE_CLAIMED_POSITIONS_MOCKS`.
> - Adds mock transaction receipts and priorities; enhances RPC mocks
(EIP-7702 code, transaction count, receipt).
> - Introduces Polygon gas fees mock (network 137) and updates default
gas API mocks.
> - Reorganizes/exports comprehensive `POLYMARKET_COMPLETE_MOCKS` and
`POLYMARKET_ALL_POSITIONS_MOCKS`.
> - **App Components/TestIDs**:
> - Replace dynamic selector usage with enum IDs in `PredictPosition`
and `PredictPositionResolved` (`CURRENT_POSITION_CARD`,
`RESOLVED_POSITION_CARD`).
> - Add testIDs to claim confirmation amount/footer (e.g.,
`CLAIM_AMOUNT_CONTAINER`, `CLAIM_CONFIRM_BUTTON`).
> - **Selectors/Pages Updates**:
> - New `PredictPositionSelectorsIDs`; cleanup of old helper.
> - Wallet/Activities pages: add getters (resolved card, claim button),
rename tap helpers (`tapPredictPosition`).
>
> Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
e5f581e5175912cff5dc49ebf1172e4e8ef0e5af. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).
---
.../PredictPosition/PredictPosition.tsx | 4 +-
.../PredictPositionResolved.tsx | 4 +-
.../predict-claim-amount.tsx | 6 +-
.../predict-claim-footer.tsx | 2 +
.../mock-responses/defaults/gas-api.ts | 39 +-
.../polymarket-activity-response.ts | 200 +++----
.../polymarket/polymarket-constants.ts | 5 +-
.../polymarket/polymarket-mocks.ts | 511 +++++++-----------
.../polymarket-positions-response.ts | 4 +-
.../polymarket/polymarket-rpc-response.ts | 20 +
e2e/pages/Predict/PredictClaimPage.ts | 22 +
e2e/pages/Transactions/ActivitiesView.ts | 4 +-
e2e/pages/wallet/WalletView.ts | 21 +-
e2e/selectors/Predict/Predict.selectors.ts | 11 +-
e2e/specs/predict/helpers/predict-helpers.ts | 28 +
e2e/specs/predict/predict-cash-out.spec.ts | 4 +-
.../predict/predict-claim-positions.spec.ts | 107 ++++
17 files changed, 558 insertions(+), 434 deletions(-)
create mode 100644 e2e/pages/Predict/PredictClaimPage.ts
create mode 100644 e2e/specs/predict/helpers/predict-helpers.ts
create mode 100644 e2e/specs/predict/predict-claim-positions.spec.ts
diff --git a/app/components/UI/Predict/components/PredictPosition/PredictPosition.tsx b/app/components/UI/Predict/components/PredictPosition/PredictPosition.tsx
index 8cb663d5d7a..0a4fbd8cd7e 100644
--- a/app/components/UI/Predict/components/PredictPosition/PredictPosition.tsx
+++ b/app/components/UI/Predict/components/PredictPosition/PredictPosition.tsx
@@ -8,7 +8,7 @@ import { useStyles } from '../../../../../component-library/hooks';
import { PredictPosition as PredictPositionType } from '../../types';
import { formatPercentage, formatPrice } from '../../utils/format';
import styleSheet from './PredictPosition.styles';
-import { getPredictPositionSelector } from '../../../../../../e2e/selectors/Predict/Predict.selectors';
+import { PredictPositionSelectorsIDs } from '../../../../../../e2e/selectors/Predict/Predict.selectors';
interface PredictPositionProps {
position: PredictPositionType;
@@ -32,7 +32,7 @@ const PredictPosition: React.FC = ({
return (
onPress?.(position)}
>
diff --git a/app/components/UI/Predict/components/PredictPositionResolved/PredictPositionResolved.tsx b/app/components/UI/Predict/components/PredictPositionResolved/PredictPositionResolved.tsx
index fcccf7b03db..d1d8cbac977 100644
--- a/app/components/UI/Predict/components/PredictPositionResolved/PredictPositionResolved.tsx
+++ b/app/components/UI/Predict/components/PredictPositionResolved/PredictPositionResolved.tsx
@@ -10,7 +10,7 @@ import { useStyles } from '../../../../../component-library/hooks';
import { PredictPosition as PredictPositionType } from '../../types';
import { formatPrice } from '../../utils/format';
import styleSheet from './PredictPositionResolved.styles';
-import { getPredictPositionSelector } from '../../../../../../e2e/selectors/Predict/Predict.selectors';
+import { PredictPositionSelectorsIDs } from '../../../../../../e2e/selectors/Predict/Predict.selectors';
dayjs.extend(relativeTime);
@@ -46,7 +46,7 @@ const PredictPositionResolved: React.FC = ({
return (
onPress?.(position)}
>
diff --git a/app/components/Views/confirmations/components/predict-confirmations/predict-claim-amount/predict-claim-amount.tsx b/app/components/Views/confirmations/components/predict-confirmations/predict-claim-amount/predict-claim-amount.tsx
index 3dafd866f62..5cb31b4eb4a 100644
--- a/app/components/Views/confirmations/components/predict-confirmations/predict-claim-amount/predict-claim-amount.tsx
+++ b/app/components/Views/confirmations/components/predict-confirmations/predict-claim-amount/predict-claim-amount.tsx
@@ -15,6 +15,7 @@ import {
formatPercentage,
formatPrice,
} from '../../../../../UI/Predict/utils/format';
+import { PredictClaimConfirmationSelectorsIDs } from '../../../../../../../e2e/selectors/Predict/Predict.selectors';
import styleSheet from './predict-claim-amount.styles';
export function PredictClaimAmount() {
@@ -35,7 +36,10 @@ export function PredictClaimAmount() {
})} (${formatPercentage((winningsPnl / winningsFiat) * 100)})`;
return (
-
+
{strings('confirm.predict_claim.summary')}
diff --git a/app/components/Views/confirmations/components/predict-confirmations/predict-claim-footer/predict-claim-footer.tsx b/app/components/Views/confirmations/components/predict-confirmations/predict-claim-footer/predict-claim-footer.tsx
index 02269f36900..8332d340906 100644
--- a/app/components/Views/confirmations/components/predict-confirmations/predict-claim-footer/predict-claim-footer.tsx
+++ b/app/components/Views/confirmations/components/predict-confirmations/predict-claim-footer/predict-claim-footer.tsx
@@ -15,6 +15,7 @@ import Text, {
} from '../../../../../../component-library/components/Texts/Text';
import { useStyles } from '../../../../../../component-library/hooks';
import { Box } from '../../../../../UI/Box/Box';
+import { PredictClaimConfirmationSelectorsIDs } from '../../../../../../../e2e/selectors/Predict/Predict.selectors';
import styleSheet from './predict-claim-footer.styles';
import { selectPredictWonPositions } from '../../../../../UI/Predict/selectors/predictController';
@@ -52,6 +53,7 @@ export function PredictClaimFooter({ onPress }: PredictClaimFooterProps) {
label={strings('confirm.predict_claim.button_label')}
onPress={onPress}
isInverse
+ testID={PredictClaimConfirmationSelectorsIDs.CLAIM_CONFIRM_BUTTON}
/>
{
await setupMockRequest(mockServer, {
requestMethod: 'GET',
@@ -173,7 +194,7 @@ export const POLYMARKET_CURRENT_POSITIONS_MOCKS = async (
),
);
})
- .asPriority(999)
+ .asPriority(PRIORITY.BASE)
.thenCallback((request) => {
const url = new URL(request.url).searchParams.get('url');
const userMatch = url?.match(/user=(0x[a-fA-F0-9]{40})/);
@@ -208,11 +229,13 @@ export const POLYMARKET_CURRENT_POSITIONS_MOCKS = async (
* Mock for Polymarket positions API with controllable winning positions
* Returns positions data for user with optional winning positions
* This mock will trigger the CLAIM button
+ * Winning positions (redeemable=true) should be in resolved markets, not current positions
*/
export const POLYMARKET_POSITIONS_WITH_WINNINGS_MOCKS = async (
mockServer: Mockttp,
includeWinnings: boolean = false,
) => {
+ // Mock for current positions (redeemable=false) - never include winning positions here
await mockServer
.forGet('/proxy')
.matching((request) => {
@@ -221,10 +244,11 @@ export const POLYMARKET_POSITIONS_WITH_WINNINGS_MOCKS = async (
url &&
/^https:\/\/data-api\.polymarket\.com\/positions\?.*user=0x[a-fA-F0-9]{40}.*$/.test(
url,
- ),
+ ) &&
+ !url.includes('redeemable=true'),
);
})
- .asPriority(999)
+ .asPriority(PRIORITY.BASE)
.thenCallback((request) => {
const url = new URL(request.url).searchParams.get('url');
const userMatch = url?.match(/user=(0x[a-fA-F0-9]{40})/);
@@ -234,13 +258,10 @@ export const POLYMARKET_POSITIONS_WITH_WINNINGS_MOCKS = async (
const eventIdMatch = url?.match(/eventId=([0-9]+)/);
const eventId = eventIdMatch ? eventIdMatch[1] : null;
- // Use the new function to control whether to include winning positions
- const positionsData = createPositionsWithWinnings(includeWinnings);
-
- // Filter positions by eventId if provided
- let filteredPositions = positionsData;
+ // Current positions should never include winning positions
+ let filteredPositions = POLYMARKET_CURRENT_POSITIONS_RESPONSE;
if (eventId) {
- filteredPositions = positionsData.filter(
+ filteredPositions = POLYMARKET_CURRENT_POSITIONS_RESPONSE.filter(
(position) => position.eventId === eventId,
);
}
@@ -256,6 +277,50 @@ export const POLYMARKET_POSITIONS_WITH_WINNINGS_MOCKS = async (
json: dynamicResponse,
};
});
+
+ // Mock for resolved markets (redeemable=true) - add winning positions here if includeWinnings is true
+ await mockServer
+ .forGet('/proxy')
+ .matching((request) => {
+ const url = new URL(request.url).searchParams.get('url');
+ return Boolean(
+ url &&
+ /^https:\/\/data-api\.polymarket\.com\/positions\?.*user=0x[a-fA-F0-9]{40}.*$/.test(
+ url,
+ ) &&
+ url.includes('redeemable=true'),
+ );
+ })
+ .asPriority(PRIORITY.BASE)
+ .thenCallback((request) => {
+ const url = new URL(request.url).searchParams.get('url');
+ const userMatch = url?.match(/user=(0x[a-fA-F0-9]{40})/);
+ const userAddress = userMatch ? userMatch[1] : USER_WALLET_ADDRESS;
+
+ // Combine lost positions with winning positions if includeWinnings is true
+ const resolvedMarkets = POLYMARKET_RESOLVED_LOST_POSITIONS_RESPONSE.map(
+ (position) => ({
+ ...position,
+ proxyWallet: userAddress,
+ }),
+ );
+
+ let resolvedPositions = resolvedMarkets;
+ if (includeWinnings) {
+ const winningPositions = POLYMARKET_WINNING_POSITIONS_RESPONSE.map(
+ (position) => ({
+ ...position,
+ proxyWallet: userAddress,
+ }),
+ );
+ resolvedPositions = [...resolvedMarkets, ...winningPositions];
+ }
+
+ return {
+ statusCode: 200,
+ json: resolvedPositions,
+ };
+ });
};
/**
@@ -272,7 +337,7 @@ export const POLYMARKET_ORDER_BOOK_MOCKS = async (mockServer: Mockttp) => {
/^https:\/\/clob\.polymarket\.com\/book\?token_id=\d+$/.test(url),
);
})
- .asPriority(999)
+ .asPriority(PRIORITY.BASE)
.thenCallback((request) => {
const url = new URL(request.url).searchParams.get('url');
const tokenIdMatch = url?.match(/token_id=(\d+)/);
@@ -353,17 +418,18 @@ export const POLYMARKET_RESOLVED_MARKETS_POSITIONS_MOCKS = async (
);
return matches;
})
- .asPriority(999)
+ .asPriority(PRIORITY.BASE)
.thenCallback((request) => {
const url = new URL(request.url).searchParams.get('url');
const userMatch = url?.match(/user=(0x[a-fA-F0-9]{40})/);
const userAddress = userMatch ? userMatch[1] : USER_WALLET_ADDRESS;
- const dynamicResponse =
- POLYMARKET_RESOLVED_MARKETS_POSITIONS_RESPONSE.map((position) => ({
+ const dynamicResponse = POLYMARKET_RESOLVED_LOST_POSITIONS_RESPONSE.map(
+ (position) => ({
...position,
proxyWallet: userAddress,
- }));
+ }),
+ );
return {
statusCode: 200,
@@ -388,7 +454,7 @@ export const POLYMARKET_ACTIVITY_MOCKS = async (mockServer: Mockttp) => {
),
);
})
- .asPriority(999)
+ .asPriority(PRIORITY.BASE)
.thenCallback((request) => {
const url = new URL(request.url).searchParams.get('url');
const userMatch = url?.match(/user=(0x[a-fA-F0-9]{40})/);
@@ -422,7 +488,7 @@ export const POLYMARKET_UPNL_MOCKS = async (mockServer: Mockttp) => {
),
);
})
- .asPriority(999)
+ .asPriority(PRIORITY.BASE)
.thenCallback((request) => {
const url = new URL(request.url).searchParams.get('url');
const userMatch = url?.match(/user=(0x[a-fA-F0-9]{40})/);
@@ -441,18 +507,6 @@ export const POLYMARKET_UPNL_MOCKS = async (mockServer: Mockttp) => {
});
};
-/**
- * Mock for both Polymarket positions endpoints (regular and redeemable)
- * Returns both types of positions data for user 0x5f7c8f3c8bedf5e7db63a34ef2f39322ca77fe72
- */
-export const POLYMARKET_ALL_POSITIONS_MOCKS = async (mockServer: Mockttp) => {
- await POLYMARKET_CURRENT_POSITIONS_MOCKS(mockServer);
- await POLYMARKET_RESOLVED_MARKETS_POSITIONS_MOCKS(mockServer);
-};
-
-// Global variable to track current USDC balance
-let currentUSDCBalance = MOCK_RPC_RESPONSES.USDC_BALANCE_RESULT;
-
/**
* Mock for USDC balance calls on Polygon
* Returns mock USDC balance for the test user
@@ -509,13 +563,14 @@ export const POLYMARKET_USDC_BALANCE_MOCKS = async (
return false;
})
- .asPriority(999)
+ .asPriority(PRIORITY.BASE)
.thenCallback(async (request) => {
const bodyText = await request.body.getText();
const body = bodyText ? JSON.parse(bodyText) : undefined;
// Return appropriate mock response based on the call
- let result = '0x';
+ // Can be string (hex) or object (transaction receipt)
+ let result: string | object = '0x';
if (body?.method === 'eth_call') {
const toAddress = body?.params?.[0]?.to;
@@ -583,6 +638,11 @@ export const POLYMARKET_USDC_BALANCE_MOCKS = async (
} else if (body?.method === 'eth_estimateGas') {
// Return a reasonable gas estimate
result = '0xa49f3'; // ~675,683 gas
+ } else if (body?.method === 'eth_getTransactionReceipt') {
+ // Return a mock transaction receipt indicating the transaction is confirmed
+ // This is critical for TransactionController to mark transactions as confirmed
+ // TransactionController polls for receipts to determine transaction status
+ result = MOCK_RPC_RESPONSES.TRANSACTION_RECEIPT_RESULT;
}
// Note: We don't mock eth_gasPrice for Polygon - the app should use the gas API
// (already mocked in DEFAULT_GAS_API_MOCKS) which provides EIP-1559 fields.
@@ -617,7 +677,7 @@ export const POLYMARKET_MARKET_FEEDS_MOCKS = async (mockServer: Mockttp) => {
/^https:\/\/gamma-api\.polymarket\.com\/events\/pagination/.test(url),
);
})
- .asPriority(999)
+ .asPriority(PRIORITY.BASE)
.thenCallback((request) => {
const url = new URL(request.url).searchParams.get('url');
@@ -675,7 +735,7 @@ export const POLYMARKET_MARKET_FEEDS_MOCKS = async (mockServer: Mockttp) => {
url && /^https:\/\/gamma-api\.polymarket\.com\/public-search/.test(url),
);
})
- .asPriority(999)
+ .asPriority(PRIORITY.BASE)
.thenCallback(() => ({
statusCode: 200,
json: {
@@ -685,21 +745,6 @@ export const POLYMARKET_MARKET_FEEDS_MOCKS = async (mockServer: Mockttp) => {
}));
};
-/**
- * Mock for all Polymarket endpoints (positions, redeemable positions, activity, UpNL, and value)
- * Returns data for proxy wallet: 0x5f7c8f3c8bedf5e7db63a34ef2f39322ca77fe72
- */
-export const POLYMARKET_COMPLETE_MOCKS = async (mockServer: Mockttp) => {
- await POLYMARKET_ALL_POSITIONS_MOCKS(mockServer);
- await POLYMARKET_ACTIVITY_MOCKS(mockServer);
- await POLYMARKET_UPNL_MOCKS(mockServer);
- await POLYMARKET_USDC_BALANCE_MOCKS(mockServer); // Uses default balance
- await POLYMARKET_EVENT_DETAILS_MOCKS(mockServer);
- await POLYMARKET_ORDER_BOOK_MOCKS(mockServer);
- await POLYMARKET_MARKET_FEEDS_MOCKS(mockServer);
- // Only user-specific data (positions, activity, UpNL) should be mocked
-};
-
/**
* Mocks transaction sentinel for Polygon transactions
* Mocks the infura_simulateTransactions method for transaction simulation
@@ -710,8 +755,8 @@ export const POLYMARKET_TRANSACTION_SENTINEL_MOCKS = async (
mockServer: Mockttp,
) => {
await mockServer
- .forPost('https://tx-sentinel-polygon-mainnet.api.cx.metamask.io/')
- .asPriority(999)
+ .forPost('https://tx-sentinel-polygon-mainnet.api.cx.metamask.io/') //
+ .asPriority(PRIORITY.BASE)
.thenCallback(async (request) => {
try {
const bodyText = await request.body.getText();
@@ -751,116 +796,78 @@ export const POLYMARKET_TRANSACTION_SENTINEL_MOCKS = async (
}
});
};
-
-export const MOCK_BATCH_TRANSACTIONS = async (mockServer: Mockttp) => {
- await POLYMARKET_USDC_BALANCE_MOCKS(mockServer, POST_CLAIM_USDC_BALANCE_WEI);
- await POLYMARKET_TRANSACTION_SENTINEL_MOCKS(mockServer);
- // Only user-specific data (positions, activity, UpNL) should be mocked
-};
-
/**
- * Post-claim mock for USDC balance update
- * This mock should be triggered after claim button is tapped
- * Returns updated USDC balance reflecting claimed positions
- * Also sets up EIP-7702 and transaction sentinel mocks specifically for claim flow
+ * Sets up mocks for USDC balance refresh calls after claim or cash-out operations
+ * This mock should be triggered after claim/cash-out transactions to update the displayed balance
+ * - Updates global USDC balance variable (like POLYMARKET_USDC_BALANCE_MOCKS)
+ * - Mocks balance refresh calls via /proxy endpoint (Polygon RPC)
+ * - Mocks direct polygon-rpc.com calls for USDC balance queries
+ * - Mocks eth_getTransactionCount calls (needed for claim flow transaction construction)
+ * - Returns the appropriate balance based on positionType ('claim' or 'cash-out')
+ * @param mockServer - The Mockttp server instance to configure mocks on
+ * @param positionType - The type of operation: 'claim' (returns 48.16 USDC) or 'cash-out' (returns 58.66 USDC)
*/
-export const POLYMARKET_POST_CLAIM_MOCKS = async (mockServer: Mockttp) => {
- // Update USDC balance to claim amount using the reusable function
- await POLYMARKET_USDC_BALANCE_MOCKS(mockServer, POST_CLAIM_USDC_BALANCE_WEI);
+export const POLYMARKET_UPDATE_USDC_BALANCE_MOCKS = async (
+ mockServer: Mockttp,
+ positionType: string,
+) => {
+ // Update global balance based on position type (similar to POLYMARKET_USDC_BALANCE_MOCKS pattern)
+ let balance: string;
+ if (positionType === 'claim') {
+ balance = POST_CLAIM_USDC_BALANCE_WEI; // 48.16 USDC
+ } else if (positionType === 'cash-out') {
+ balance = POST_CASH_OUT_USDC_BALANCE_WEI; // 58.66 USDC
+ } else {
+ throw new Error(`Unknown positionType: ${positionType}`);
+ }
- // Mock EIP-7702 eth_getCode for user wallet address (claim flow requires this)
await mockServer
.forPost('/proxy')
- .matching(async (request) => {
+ .matching((request) => {
const urlParam = new URL(request.url).searchParams.get('url');
- const isPolygonRPC = Boolean(urlParam?.includes('polygon'));
-
- if (isPolygonRPC) {
- try {
- const bodyText = await request.body.getText();
- const body = bodyText ? JSON.parse(bodyText) : undefined;
- if (body?.method === 'eth_getCode') {
- const address = body?.params?.[0];
- return address?.toLowerCase() === USER_WALLET_ADDRESS.toLowerCase();
- }
- } catch (error) {
- return false;
- }
- }
- return false;
+ return Boolean(
+ urlParam?.includes('polygon') || urlParam?.includes('infura'),
+ );
})
- .asPriority(1000) // High priority for claim flow
- .thenCallback(() => ({
- statusCode: 200,
- json: {
- id: 1,
- jsonrpc: '2.0',
- result: EIP7702_CODE_FORMAT(POLYGON_EIP7702_CONTRACT_ADDRESS),
- },
- }));
+ .asPriority(PRIORITY.BALANCE_REFRESH_PROXY) // Higher priority (1005) to catch balance refresh calls before base mocks
+ .thenCallback(async (request) => {
+ const bodyText = await request.body.getText();
+ const body = bodyText ? JSON.parse(bodyText) : undefined;
- // Mock proxy wallet eth_call (0xaffed0e0 selector) for claim flow
- await mockServer
- .forPost('/proxy')
- .matching(async (request) => {
- const urlParam = new URL(request.url).searchParams.get('url');
- const isPolygonRPC = Boolean(urlParam?.includes('polygon'));
+ let result: string | object = '0x';
- if (isPolygonRPC) {
- try {
- const bodyText = await request.body.getText();
- const body = bodyText ? JSON.parse(bodyText) : undefined;
- if (body?.method === 'eth_call') {
- const toAddress = body?.params?.[0]?.to;
- const callData = body?.params?.[0]?.data;
- return (
- (toAddress?.toLowerCase() ===
- PROXY_WALLET_ADDRESS.toLowerCase() ||
- toAddress?.toLowerCase() ===
- '0x254955bE605cf7c4E683E92b157187550bd5e639'.toLowerCase()) &&
- callData === '0xaffed0e0'
- );
- }
- } catch (error) {
- return false;
+ // Handle USDC balance calls
+ if (body?.method === 'eth_call') {
+ const toAddress = body?.params?.[0]?.to?.toLowerCase();
+ if (toAddress === USDC_CONTRACT_ADDRESS.toLowerCase()) {
+ // USDC contract call - return updated balance
+ result = balance;
+ } else {
+ // For other eth_call, return empty result (let base mocks handle if needed)
+ result = MOCK_RPC_RESPONSES.EMPTY_RESULT;
}
+ } else if (body?.method === 'eth_getTransactionCount') {
+ // Return a valid nonce (transaction count) - needed for claim flow
+ // This is critical for transaction construction, must be a valid hex number
+ result = MOCK_RPC_RESPONSES.TRANSACTION_COUNT_RESULT;
+ } else if (body?.method === 'eth_getTransactionReceipt') {
+ // Return a mock transaction receipt indicating the transaction is confirmed
+ // This is CRITICAL for TransactionController to mark transactions as confirmed
+ // TransactionController polls for receipts to determine transaction status
+ // Without this, transactions will remain in "pending" status
+ result = MOCK_RPC_RESPONSES.TRANSACTION_RECEIPT_RESULT;
}
- return false;
- })
- .asPriority(1000) // High priority for claim flow
- .thenCallback(() => ({
- statusCode: 200,
- json: {
- id: 1,
- jsonrpc: '2.0',
- result:
- '0x0000000000000000000000000000000000000000000000000000000000000006',
- },
- }));
+ // For other methods, return empty result (base mocks will handle them)
- // Mock transaction sentinel for Polygon claim transactions
- await POLYMARKET_TRANSACTION_SENTINEL_MOCKS(mockServer);
-
- // Mock updated UPNL reflecting claimed positions
- await mockServer
- .forGet('/proxy')
- .matching((request) => {
- const url = request.url;
- return Boolean(
- url && /^https:\/\/data-api\.polymarket\.com\/upnl/.test(url),
- );
- })
- .asPriority(999)
- .thenCallback(() => ({
- statusCode: 200,
- json: [
- {
- user: '0x5f7c8f3c8bedf5e7db63a34ef2f39322ca77fe72',
- cashUpnl: 30.282462133473, // Increased by claimed amount
- percentUpnl: 60.02623256406863,
+ return {
+ statusCode: 200,
+ json: {
+ id: body?.id ?? 50,
+ jsonrpc: '2.0',
+ result,
},
- ],
- }));
+ };
+ });
};
/**
@@ -868,7 +875,6 @@ export const POLYMARKET_POST_CLAIM_MOCKS = async (mockServer: Mockttp) => {
* This mock should be triggered before tapping the cash-out button
* - Mocks the CLOB API (polymarket order submission)
* - Updates global USDC balance to post-cash-out amount (58.66 USDC)
- * - Sets up high-priority mocks for balance refresh calls
*/
export const POLYMARKET_POST_CASH_OUT_MOCKS = async (mockServer: Mockttp) => {
// Mock CLOB API for cash-out order submission
@@ -878,7 +884,7 @@ export const POLYMARKET_POST_CASH_OUT_MOCKS = async (mockServer: Mockttp) => {
const urlParam = new URL(request.url).searchParams.get('url');
return Boolean(urlParam?.includes('clob.polymarket.com'));
})
- .asPriority(1000) // Higher priority to catch all clob requests
+ .asPriority(PRIORITY.API_OVERRIDE) // Higher priority to catch all clob requests
.thenCallback(async () => {
// Return success for any clob request
const response = {
@@ -900,93 +906,7 @@ export const POLYMARKET_POST_CASH_OUT_MOCKS = async (mockServer: Mockttp) => {
return response;
});
- // Update the global USDC balance to cash out amount (58.66 USDC)
- // This updates currentUSDCBalance so all base RPC mocks return the updated balance
- await POLYMARKET_USDC_BALANCE_MOCKS(
- mockServer,
- POST_CASH_OUT_USDC_BALANCE_WEI,
- );
-
- // High-priority mock to catch balance refresh calls after cash-out via /proxy
- await mockServer
- .forPost('/proxy')
- .matching(async (request) => {
- const urlParam = new URL(request.url).searchParams.get('url');
- const isPolygonRPC = Boolean(
- urlParam?.includes('polygon') || urlParam?.includes('infura'),
- );
-
- if (isPolygonRPC) {
- try {
- const bodyText = await request.body.getText();
- const body = bodyText ? JSON.parse(bodyText) : undefined;
- const isUSDCBalanceCall =
- body?.method === 'eth_call' &&
- body?.params?.[0]?.to?.toLowerCase() ===
- USDC_CONTRACT_ADDRESS.toLowerCase();
-
- return isUSDCBalanceCall;
- } catch (error) {
- return false;
- }
- }
- return false;
- })
- .asPriority(1005) // High priority to catch cash-out balance refresh calls
- .thenCallback(() => ({
- statusCode: 200,
- json: {
- id: 50,
- jsonrpc: '2.0',
- result: POST_CASH_OUT_USDC_BALANCE_WEI, // 58.66 USDC
- },
- }));
-
- // Also mock direct polygon-rpc.com calls for cash-out balance refresh
- await mockServer
- .forPost()
- .matching((request) => request.url.includes('polygon-rpc.com'))
- .asPriority(1007)
- .thenCallback(async (request) => {
- const bodyText = await request.body.getText();
- const body = bodyText ? JSON.parse(bodyText) : undefined;
-
- if (
- body?.method === 'eth_call' &&
- body?.params?.[0]?.to?.toLowerCase() ===
- USDC_CONTRACT_ADDRESS.toLowerCase()
- ) {
- // Return cash-out balance for USDC balance calls
- return {
- statusCode: 200,
- body: JSON.stringify({
- id: body?.id ?? 1,
- jsonrpc: '2.0',
- result: POST_CASH_OUT_USDC_BALANCE_WEI, // 58.66 USDC
- }),
- };
- }
-
- // For other calls, return empty result
- return {
- statusCode: 200,
- body: JSON.stringify({
- id: body?.id ?? 1,
- jsonrpc: '2.0',
- result: '0x',
- }),
- };
- });
-};
-
-/**
- * @deprecated Use POLYMARKET_POST_CASH_OUT_MOCKS instead
- * This function is kept for backward compatibility but is now integrated into POLYMARKET_POST_CASH_OUT_MOCKS
- */
-export const POLYMARKET_CASH_OUT_BALANCE_LOAD_MOCKS = async (
- _mockServer: Mockttp,
-) => {
- // No-op: Functionality moved to POLYMARKET_POST_CASH_OUT_MOCKS
+ await POLYMARKET_UPDATE_USDC_BALANCE_MOCKS(mockServer, 'cash-out');
};
/**
@@ -1022,7 +942,7 @@ export const POLYMARKET_WITHDRAW_BALANCE_LOAD_MOCKS = async (
}
return false;
})
- .asPriority(1006) // High priority for withdraw balance calls (different from cash-out)
+ .asPriority(PRIORITY.BALANCE_REFRESH_WITHDRAW) // High priority for withdraw balance calls (different from cash-out)
.thenCallback(() => ({
statusCode: 200,
json: {
@@ -1036,82 +956,33 @@ export const POLYMARKET_WITHDRAW_BALANCE_LOAD_MOCKS = async (
};
/**
- * Force additional USDC balance calls after navigation
- * This simulates the app refreshing the balance when navigating between screens
+ * Removes claimed positions from redeemable positions (resolved markets)
+ * After claiming, redeemable positions should be removed so the UI updates correctly
* @param mockServer - The mockttp server instance
- * @deprecated Use POLYMARKET_POST_CASH_OUT_MOCKS instead, which includes balance refresh logic
*/
-export const POLYMARKET_FORCE_BALANCE_REFRESH_MOCKS = async (
+export const POLYMARKET_REMOVE_CLAIMED_POSITIONS_MOCKS = async (
mockServer: Mockttp,
) => {
- // Inline the balance refresh logic that was previously in POLYMARKET_CASH_OUT_BALANCE_LOAD_MOCKS
- // This is kept for backward compatibility but should use POLYMARKET_POST_CASH_OUT_MOCKS instead
+ // Override redeemable positions (resolved markets) to remove winning positions after claiming
+ // This removes all resolved market positions (including winning positions) so the UI updates correctly
await mockServer
- .forPost('/proxy')
- .matching(async (request) => {
- const urlParam = new URL(request.url).searchParams.get('url');
- const isPolygonRPC = Boolean(
- urlParam?.includes('polygon') || urlParam?.includes('infura'),
+ .forGet('/proxy')
+ .matching((request) => {
+ const url = new URL(request.url).searchParams.get('url');
+ return Boolean(
+ url &&
+ /^https:\/\/data-api\.polymarket\.com\/positions\?.*user=0x[a-fA-F0-9]{40}.*$/.test(
+ url,
+ ) &&
+ url.includes('redeemable=true'),
);
-
- if (isPolygonRPC) {
- try {
- const bodyText = await request.body.getText();
- const body = bodyText ? JSON.parse(bodyText) : undefined;
- const isUSDCBalanceCall =
- body?.method === 'eth_call' &&
- body?.params?.[0]?.to?.toLowerCase() ===
- USDC_CONTRACT_ADDRESS.toLowerCase();
-
- return isUSDCBalanceCall;
- } catch (error) {
- return false;
- }
- }
- return false;
})
- .asPriority(1005)
+ .asPriority(PRIORITY.API_OVERRIDE) // Higher priority to override the original redeemable positions mock
.thenCallback(() => ({
+ // Return empty array - all resolved market positions (including winning positions) are removed after claiming
statusCode: 200,
- json: {
- id: 50,
- jsonrpc: '2.0',
- result: POST_CASH_OUT_USDC_BALANCE_WEI, // 58.66 USDC
- },
+ json: [],
}));
-
- await mockServer
- .forPost()
- .matching((request) => request.url.includes('polygon-rpc.com'))
- .asPriority(1007)
- .thenCallback(async (request) => {
- const bodyText = await request.body.getText();
- const body = bodyText ? JSON.parse(bodyText) : undefined;
-
- if (
- body?.method === 'eth_call' &&
- body?.params?.[0]?.to?.toLowerCase() ===
- USDC_CONTRACT_ADDRESS.toLowerCase()
- ) {
- return {
- statusCode: 200,
- body: JSON.stringify({
- id: body?.id ?? 1,
- jsonrpc: '2.0',
- result: POST_CASH_OUT_USDC_BALANCE_WEI, // 58.66 USDC
- }),
- };
- }
-
- return {
- statusCode: 200,
- body: JSON.stringify({
- id: body?.id ?? 1,
- jsonrpc: '2.0',
- result: '0x',
- }),
- };
- });
};
/**
@@ -1137,7 +1008,7 @@ export const POLYMARKET_REMOVE_CASHED_OUT_POSITION_MOCKS = async (
!url.includes('redeemable=true'),
);
})
- .asPriority(1000) // Higher priority to override the original positions mock
+ .asPriority(PRIORITY.API_OVERRIDE) // Higher priority to override the original positions mock
.thenCallback((request) => {
const url = new URL(request.url).searchParams.get('url');
const userMatch = url?.match(/user=(0x[a-fA-F0-9]{40})/);
@@ -1184,7 +1055,7 @@ export const POLYMARKET_REMOVE_CASHED_OUT_POSITION_MOCKS = async (
),
);
})
- .asPriority(1000) // Higher priority to override the original activity mock
+ .asPriority(PRIORITY.API_OVERRIDE) // Higher priority to override the original activity mock
.thenCallback((request) => {
const url = new URL(request.url).searchParams.get('url');
const userMatch = url?.match(/user=(0x[a-fA-F0-9]{40})/);
@@ -1236,3 +1107,27 @@ export const POLYMARKET_REMOVE_CASHED_OUT_POSITION_MOCKS = async (
};
});
};
+
+/**
+ * Mock for both Polymarket positions endpoints (current and resolved positions)
+ * Returns both types of positions data for user 0x5f7c8f3c8bedf5e7db63a34ef2f39322ca77fe72
+ */
+export const POLYMARKET_ALL_POSITIONS_MOCKS = async (mockServer: Mockttp) => {
+ await POLYMARKET_CURRENT_POSITIONS_MOCKS(mockServer);
+ await POLYMARKET_RESOLVED_MARKETS_POSITIONS_MOCKS(mockServer);
+};
+
+/**
+ * This can be considered the default user profile
+ * Mock for all Polymarket endpoints (positions, redeemable positions, activity, UpNL, and value)
+ * Returns data for proxy wallet: 0x5f7c8f3c8bedf5e7db63a34ef2f39322ca77fe72
+ */
+export const POLYMARKET_COMPLETE_MOCKS = async (mockServer: Mockttp) => {
+ await POLYMARKET_ALL_POSITIONS_MOCKS(mockServer);
+ await POLYMARKET_ACTIVITY_MOCKS(mockServer);
+ await POLYMARKET_UPNL_MOCKS(mockServer);
+ await POLYMARKET_USDC_BALANCE_MOCKS(mockServer); // Uses default balance
+ await POLYMARKET_EVENT_DETAILS_MOCKS(mockServer);
+ await POLYMARKET_ORDER_BOOK_MOCKS(mockServer);
+ await POLYMARKET_MARKET_FEEDS_MOCKS(mockServer);
+};
diff --git a/e2e/api-mocking/mock-responses/polymarket/polymarket-positions-response.ts b/e2e/api-mocking/mock-responses/polymarket/polymarket-positions-response.ts
index d656490a686..3ac1042a036 100644
--- a/e2e/api-mocking/mock-responses/polymarket/polymarket-positions-response.ts
+++ b/e2e/api-mocking/mock-responses/polymarket/polymarket-positions-response.ts
@@ -164,9 +164,9 @@ export const POLYMARKET_CURRENT_POSITIONS_RESPONSE = [
];
/*
*endpoint: /positions?user&redeemable=true
-This is all your positions in resolved markets
+This contains all lost positions in resolved markets (no winning positions)
*/
-export const POLYMARKET_RESOLVED_MARKETS_POSITIONS_RESPONSE = [
+export const POLYMARKET_RESOLVED_LOST_POSITIONS_RESPONSE = [
{
proxyWallet: PROXY_WALLET_ADDRESS,
asset:
diff --git a/e2e/api-mocking/mock-responses/polymarket/polymarket-rpc-response.ts b/e2e/api-mocking/mock-responses/polymarket/polymarket-rpc-response.ts
index 7ac440daae0..1dc19c7feb5 100644
--- a/e2e/api-mocking/mock-responses/polymarket/polymarket-rpc-response.ts
+++ b/e2e/api-mocking/mock-responses/polymarket/polymarket-rpc-response.ts
@@ -40,6 +40,26 @@ export const MOCK_RPC_RESPONSES = {
// Mock contract code for deployed contracts
CONTRACT_CODE_RESULT: '0x608060405234801561001057600080fd5b50',
+
+ // Mock transaction receipt for confirmed transactions
+ // This receipt indicates a successful transaction confirmation
+ TRANSACTION_RECEIPT_RESULT: {
+ transactionHash:
+ '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef',
+ transactionIndex: '0x0',
+ blockNumber: '0x1234568',
+ blockHash:
+ '0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890',
+ from: USER_WALLET_ADDRESS.toLowerCase(),
+ to: PROXY_WALLET_ADDRESS.toLowerCase(),
+ cumulativeGasUsed: '0x94670',
+ gasUsed: '0x94670',
+ contractAddress: null,
+ logs: [],
+ status: '0x1', // Success status
+ logsBloom:
+ '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
+ },
};
// Re-export for backward compatibility
diff --git a/e2e/pages/Predict/PredictClaimPage.ts b/e2e/pages/Predict/PredictClaimPage.ts
new file mode 100644
index 00000000000..d4db100a71e
--- /dev/null
+++ b/e2e/pages/Predict/PredictClaimPage.ts
@@ -0,0 +1,22 @@
+import { Matchers, Gestures } from '../../framework';
+import { PredictClaimConfirmationSelectorsIDs } from '../../selectors/Predict/Predict.selectors';
+
+class PredictClaimPage {
+ get container(): DetoxElement {
+ return Matchers.getElementByID(
+ PredictClaimConfirmationSelectorsIDs.CLAIM_BACKGROUND_CONTAINER,
+ );
+ }
+ get claimConfirmButton(): DetoxElement {
+ return Matchers.getElementByID(
+ PredictClaimConfirmationSelectorsIDs.CLAIM_CONFIRM_BUTTON,
+ );
+ }
+ async tapClaimConfirmButton(): Promise {
+ await Gestures.waitAndTap(this.claimConfirmButton, {
+ elemDescription: 'Claim confirm button',
+ });
+ }
+}
+
+export default new PredictClaimPage();
diff --git a/e2e/pages/Transactions/ActivitiesView.ts b/e2e/pages/Transactions/ActivitiesView.ts
index 970a79e2820..cacf483151f 100644
--- a/e2e/pages/Transactions/ActivitiesView.ts
+++ b/e2e/pages/Transactions/ActivitiesView.ts
@@ -120,10 +120,10 @@ class ActivitiesView {
async tapOnPredictionsTab(): Promise {
await Gestures.waitAndTap(this.predictionsTab);
}
- async tapCashedOutPosition(positionName: string): Promise {
+ async tapPredictPosition(positionName: string): Promise {
const el = Matchers.getElementByText(positionName);
await Gestures.waitAndTap(el, {
- elemDescription: `Tapping Cashed Out Position: ${positionName}`,
+ elemDescription: `Tapping Predict Position: ${positionName}`,
});
}
}
diff --git a/e2e/pages/wallet/WalletView.ts b/e2e/pages/wallet/WalletView.ts
index 08591e12d62..8ce927f3422 100644
--- a/e2e/pages/wallet/WalletView.ts
+++ b/e2e/pages/wallet/WalletView.ts
@@ -5,7 +5,7 @@ import {
import {
PredictTabViewSelectorsIDs,
PredictPositionsHeaderSelectorsIDs,
- getPredictPositionSelector,
+ PredictPositionSelectorsIDs,
} from '../../selectors/Predict/Predict.selectors';
import Gestures from '../../framework/Gestures';
import Matchers from '../../framework/Matchers';
@@ -163,7 +163,14 @@ class WalletView {
}
getPredictCurrentPositionCardByIndex(index: number = 0): DetoxElement {
return Matchers.getElementByID(
- getPredictPositionSelector.currentPositionCard,
+ PredictPositionSelectorsIDs.CURRENT_POSITION_CARD,
+ index,
+ );
+ }
+
+ getPredictResolvedPositionCardByIndex(index: number = 0): DetoxElement {
+ return Matchers.getElementByID(
+ PredictPositionSelectorsIDs.RESOLVED_POSITION_CARD,
index,
);
}
@@ -437,6 +444,11 @@ class WalletView {
WalletViewSelectorsIDs.DEFI_POSITIONS_CONTAINER,
);
}
+ get claimButton(): DetoxElement {
+ return Matchers.getElementByID(
+ PredictPositionsHeaderSelectorsIDs.CLAIM_BUTTON,
+ );
+ }
get defiPositionDetailsContainer(): DetoxElement {
return Matchers.getElementByID(
@@ -495,10 +507,7 @@ class WalletView {
}
async tapClaimButton(): Promise {
- const elem = Matchers.getElementByID(
- PredictPositionsHeaderSelectorsIDs.CLAIM_BUTTON,
- );
- await Gestures.waitAndTap(elem, {
+ await Gestures.waitAndTap(this.claimButton, {
elemDescription: 'Claim Button',
});
}
diff --git a/e2e/selectors/Predict/Predict.selectors.ts b/e2e/selectors/Predict/Predict.selectors.ts
index 63a2670fa20..93581c84ef3 100644
--- a/e2e/selectors/Predict/Predict.selectors.ts
+++ b/e2e/selectors/Predict/Predict.selectors.ts
@@ -96,12 +96,11 @@ export const PredictPositionsSelectorsIDs = {
RESOLVED_MARKETS_HEADER: 'predict-resolved-markets-header',
} as const;
-// Helper functions for position selectors with unique identifiers
-export const getPredictPositionSelector = {
- currentPositionCard: 'predict-current-position-card',
- resolvedPositionCard: (positionId: string) =>
- `predict-resolved-position-card-${positionId}`,
-};
+// Predict position selectors
+export const PredictPositionSelectorsIDs = {
+ CURRENT_POSITION_CARD: 'predict-current-position-card',
+ RESOLVED_POSITION_CARD: 'predict-resolved-position-card',
+} as const;
// ========================================
// PREDICT CASH OUT SELECTORS
diff --git a/e2e/specs/predict/helpers/predict-helpers.ts b/e2e/specs/predict/helpers/predict-helpers.ts
new file mode 100644
index 00000000000..371345b5668
--- /dev/null
+++ b/e2e/specs/predict/helpers/predict-helpers.ts
@@ -0,0 +1,28 @@
+import { createLogger, LogLevel } from '../../../framework/logger';
+
+const logger = createLogger({
+ name: 'PredictHelpers',
+ level: LogLevel.INFO,
+});
+
+/**
+ * Location coordinates for Portugal (required for Predictions, specifically when US engineers aredebugging locally)
+ */
+export const PORTUGAL_LOCATION = {
+ lat: 41.1318702,
+ lon: -7.798836,
+};
+
+export class PredictHelpers {
+ /**
+ * Sets the device location to Portugal coordinates.
+ * Required for Predictions feature access in e2e tests.
+ */
+ static async setPortugalLocation() {
+ logger.info('[PredictHelpers] Setting device location to Portugal...');
+ await device.setLocation(PORTUGAL_LOCATION.lat, PORTUGAL_LOCATION.lon);
+ logger.info(
+ '[PredictHelpers] Device location set to Portugal successfully',
+ );
+ }
+}
diff --git a/e2e/specs/predict/predict-cash-out.spec.ts b/e2e/specs/predict/predict-cash-out.spec.ts
index cc65f174d9f..fd95927db53 100644
--- a/e2e/specs/predict/predict-cash-out.spec.ts
+++ b/e2e/specs/predict/predict-cash-out.spec.ts
@@ -88,9 +88,9 @@ describe(SmokePredictions('Predictions'), () => {
await TabBarComponent.tapActivity();
await ActivitiesView.tapOnPredictionsTab();
- // await ActivitiesView.tapCashedOutPosition(positionDetails.name);
+ // await ActivitiesView.tapPredictPosition(positionDetails.name);
await Assertions.expectTextDisplayed('Cashed out');
- await ActivitiesView.tapCashedOutPosition(positionDetails.name);
+ await ActivitiesView.tapPredictPosition(positionDetails.name);
await Assertions.expectElementToBeVisible(
PredictActivityDetails.container,
);
diff --git a/e2e/specs/predict/predict-claim-positions.spec.ts b/e2e/specs/predict/predict-claim-positions.spec.ts
new file mode 100644
index 00000000000..6c36737cbe8
--- /dev/null
+++ b/e2e/specs/predict/predict-claim-positions.spec.ts
@@ -0,0 +1,107 @@
+import { withFixtures } from '../../framework/fixtures/FixtureHelper';
+import FixtureBuilder from '../../framework/fixtures/FixtureBuilder';
+import { SmokePredictions } from '../../tags';
+import { loginToApp } from '../../viewHelper';
+import Assertions from '../../framework/Assertions';
+import WalletView from '../../pages/wallet/WalletView';
+import {
+ remoteFeatureFlagPredictEnabled,
+ confirmationsRedesignedFeatureFlags,
+} from '../../api-mocking/mock-responses/feature-flags-mocks';
+import {
+ POLYMARKET_COMPLETE_MOCKS,
+ POLYMARKET_POSITIONS_WITH_WINNINGS_MOCKS,
+ POLYMARKET_REMOVE_CLAIMED_POSITIONS_MOCKS,
+ POLYMARKET_TRANSACTION_SENTINEL_MOCKS,
+ POLYMARKET_UPDATE_USDC_BALANCE_MOCKS,
+} from '../../api-mocking/mock-responses/polymarket/polymarket-mocks';
+import { Mockttp } from 'mockttp';
+import { setupRemoteFeatureFlagsMock } from '../../api-mocking/helpers/remoteFeatureFlagsHelper';
+import PredictClaimPage from '../../pages/Predict/PredictClaimPage';
+
+import {
+ POLYMARKET_RESOLVED_LOST_POSITIONS_RESPONSE,
+ POLYMARKET_WINNING_POSITIONS_RESPONSE,
+} from '../../api-mocking/mock-responses/polymarket/polymarket-positions-response';
+import { PredictHelpers } from './helpers/predict-helpers';
+
+/*
+Test Scenario: Claim positions
+ Verifies the claim flow for a predictions position:
+ 1. Navigate to Predictions tab and verify balance is $28.16
+ 2. Verify Claim Button is visible with $20.00 claimable amount
+ 3. Tap Claim Button
+ 4. Complete claim transaction
+ 5. Verify balance updated to $48.16 and all resolved positions (loss positions + winning positions) are removed from the UI
+ */
+
+const PredictionMarketFeature = async (mockServer: Mockttp) => {
+ await setupRemoteFeatureFlagsMock(mockServer, {
+ ...remoteFeatureFlagPredictEnabled(true),
+ ...Object.assign({}, ...confirmationsRedesignedFeatureFlags),
+ });
+ await POLYMARKET_COMPLETE_MOCKS(mockServer);
+ await POLYMARKET_TRANSACTION_SENTINEL_MOCKS(mockServer);
+ await POLYMARKET_POSITIONS_WITH_WINNINGS_MOCKS(mockServer, true); // Include winnings for claim flow
+};
+describe(SmokePredictions('Predictions'), () => {
+ it('should claim positions', async () => {
+ await withFixtures(
+ {
+ fixture: new FixtureBuilder().withPolygon().build(),
+ restartDevice: true,
+ testSpecificMock: PredictionMarketFeature,
+ },
+ async ({ mockServer }) => {
+ await PredictHelpers.setPortugalLocation();
+ await loginToApp();
+
+ // Claim button is animated - disabling sync to prevent test hang
+ await device.disableSynchronization();
+
+ await WalletView.tapOnPredictionsTab();
+
+ await Assertions.expectElementToBeVisible(
+ WalletView.PredictionsTabContainer,
+ );
+
+ await WalletView.tapClaimButton();
+ await Assertions.expectElementToBeVisible(PredictClaimPage.container);
+
+ // Set up mocks to remove claimed positions after tapping claim button
+ await POLYMARKET_REMOVE_CLAIMED_POSITIONS_MOCKS(mockServer);
+ await POLYMARKET_UPDATE_USDC_BALANCE_MOCKS(mockServer, 'claim');
+
+ await PredictClaimPage.tapClaimConfirmButton();
+ await device.enableSynchronization();
+
+ await Assertions.expectElementToBeVisible(WalletView.container);
+
+ /*
+ Verify that all resolved positions (lost positions + winning positions) are removed after claiming
+ Resolved positions include both:
+ 1. Lost positions (from POLYMARKET_RESOLVED_LOST_POSITIONS_RESPONSE)
+ 2. Winning positions (from POLYMARKET_WINNING_POSITIONS_RESPONSE)
+ */
+ const allResolvedPositions = [
+ ...POLYMARKET_RESOLVED_LOST_POSITIONS_RESPONSE,
+ ...POLYMARKET_WINNING_POSITIONS_RESPONSE,
+ ];
+
+ for (const position of allResolvedPositions) {
+ await Assertions.expectTextNotDisplayed(position.title, {
+ description: `Resolved position "${position.title}" should not be visible after claiming`,
+ });
+ }
+
+ await Assertions.expectElementToNotBeVisible(WalletView.claimButton, {
+ description: 'Claim button should not be visible',
+ });
+ /* there is a bug where balances are not updating quick enough.
+ Leaving this commented for now. Once the bug is fixed we shoudl uncomment.
+ */
+ // await Assertions.expectTextDisplayed('$48.16');
+ },
+ );
+ });
+});
From b289a00a3045959957c3800636ce2696e5348bf6 Mon Sep 17 00:00:00 2001
From: Bruno Nascimento
Date: Mon, 3 Nov 2025 13:56:09 -0300
Subject: [PATCH 09/11] fix(card): cp-7.58.0 fix non-gated feature flag
(#22057)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
## **Description**
This PR updates the Card Experimental Switch feature flag to include
version gating.
## **Changelog**
CHANGELOG entry:
## **Related issues**
Fixes:
## **Manual testing steps**
```gherkin
Feature: my feature name
Scenario: user [verb for user action]
Given [describe expected initial app state]
When user [verb for user action]
Then [describe expected outcome]
```
## **Screenshots/Recordings**
### **Before**
### **After**
## **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.
---
> [!NOTE]
> Adds version-gated evaluation for the card experimental switch
(`cardExperimentalSwitch2`) using `validatedVersionGatedFeatureFlag`,
updates typing to `GateVersionedFeatureFlag`, and adjusts tests
accordingly.
>
> - **Feature Flags**:
> - Implement version-gated logic for `selectCardExperimentalSwitch`
using `validatedVersionGatedFeatureFlag` and new remote flag
`cardExperimentalSwitch2`.
> - Rename/standardize flag type to `GateVersionedFeatureFlag` and apply
to `displayCardButton` and experimental switch selectors.
> - **Tests**:
> - Add/adjust tests for version-gated behaviors
(enabled/disabled/version unmet/malformed) for both `displayCardButton`
and `cardExperimentalSwitch2`.
> - Mock `validatedVersionGatedFeatureFlag` and
`core/Engine/controllers/remote-feature-flag-controller` override.
>
> Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
24824b045b976475c039accd410709dd4cce1209. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).
---
.../featureFlagController/card/index.test.ts | 67 +++++++++++++++----
.../featureFlagController/card/index.ts | 11 ++-
2 files changed, 63 insertions(+), 15 deletions(-)
diff --git a/app/selectors/featureFlagController/card/index.test.ts b/app/selectors/featureFlagController/card/index.test.ts
index 7735cb67628..2c301f4ae0c 100644
--- a/app/selectors/featureFlagController/card/index.test.ts
+++ b/app/selectors/featureFlagController/card/index.test.ts
@@ -20,6 +20,13 @@ jest.mock('../../../util/remoteFeatureFlag', () => ({
validatedVersionGatedFeatureFlag: jest.fn(),
}));
+jest.mock(
+ '../../../core/Engine/controllers/remote-feature-flag-controller',
+ () => ({
+ isRemoteFeatureFlagOverrideActivated: false,
+ }),
+);
+
const originalEnv = process.env;
beforeEach(() => {
@@ -493,25 +500,43 @@ describe('selectDisplayCardButtonFeatureFlag', () => {
});
describe('selectCardExperimentalSwitch', () => {
+ const mockedValidatedVersionGatedFeatureFlag =
+ validatedVersionGatedFeatureFlag as jest.MockedFunction<
+ typeof validatedVersionGatedFeatureFlag
+ >;
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
it('returns false when feature flag state is empty', () => {
+ mockedValidatedVersionGatedFeatureFlag.mockReturnValue(undefined);
+
const result = selectCardExperimentalSwitch(mockedEmptyFlagsState);
expect(result).toBe(false);
});
it('returns false when RemoteFeatureFlagController state is undefined', () => {
+ mockedValidatedVersionGatedFeatureFlag.mockReturnValue(undefined);
+
const result = selectCardExperimentalSwitch(mockedUndefinedFlagsState);
expect(result).toBe(false);
});
- it('returns true when cardExperimentalSwitch is enabled', () => {
+ it('returns true when feature flag is enabled and version requirement is met', () => {
+ mockedValidatedVersionGatedFeatureFlag.mockReturnValue(true);
+
const stateWithExperimentalSwitch = {
engine: {
backgroundState: {
RemoteFeatureFlagController: {
remoteFeatureFlags: {
- cardExperimentalSwitch: true,
+ cardExperimentalSwitch2: {
+ enabled: true,
+ minimumVersion: '7.0.0',
+ },
},
cacheTimestamp: 0,
},
@@ -523,15 +548,24 @@ describe('selectCardExperimentalSwitch', () => {
const result = selectCardExperimentalSwitch(stateWithExperimentalSwitch);
expect(result).toBe(true);
+ expect(mockedValidatedVersionGatedFeatureFlag).toHaveBeenCalledWith({
+ enabled: true,
+ minimumVersion: '7.0.0',
+ });
});
- it('returns false when cardExperimentalSwitch is disabled', () => {
+ it('returns false when feature flag is disabled', () => {
+ mockedValidatedVersionGatedFeatureFlag.mockReturnValue(false);
+
const stateWithDisabledSwitch = {
engine: {
backgroundState: {
RemoteFeatureFlagController: {
remoteFeatureFlags: {
- cardExperimentalSwitch: false,
+ cardExperimentalSwitch2: {
+ enabled: false,
+ minimumVersion: '7.0.0',
+ },
},
cacheTimestamp: 0,
},
@@ -545,13 +579,18 @@ describe('selectCardExperimentalSwitch', () => {
expect(result).toBe(false);
});
- it('returns false when cardExperimentalSwitch is null', () => {
- const stateWithNullSwitch = {
+ it('returns false when version requirement is not met', () => {
+ mockedValidatedVersionGatedFeatureFlag.mockReturnValue(false);
+
+ const stateWithVersionGate = {
engine: {
backgroundState: {
RemoteFeatureFlagController: {
remoteFeatureFlags: {
- cardExperimentalSwitch: null,
+ cardExperimentalSwitch2: {
+ enabled: true,
+ minimumVersion: '99.0.0',
+ },
},
cacheTimestamp: 0,
},
@@ -560,18 +599,22 @@ describe('selectCardExperimentalSwitch', () => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} as any;
- const result = selectCardExperimentalSwitch(stateWithNullSwitch);
+ const result = selectCardExperimentalSwitch(stateWithVersionGate);
expect(result).toBe(false);
});
- it('returns false when cardExperimentalSwitch is undefined', () => {
- const stateWithUndefinedSwitch = {
+ it('returns false when validatedVersionGatedFeatureFlag returns undefined', () => {
+ mockedValidatedVersionGatedFeatureFlag.mockReturnValue(undefined);
+
+ const stateWithMalformedFlag = {
engine: {
backgroundState: {
RemoteFeatureFlagController: {
remoteFeatureFlags: {
- cardExperimentalSwitch: undefined,
+ cardExperimentalSwitch2: {
+ enabled: 'true', // Invalid type
+ },
},
cacheTimestamp: 0,
},
@@ -580,7 +623,7 @@ describe('selectCardExperimentalSwitch', () => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} as any;
- const result = selectCardExperimentalSwitch(stateWithUndefinedSwitch);
+ const result = selectCardExperimentalSwitch(stateWithMalformedFlag);
expect(result).toBe(false);
});
diff --git a/app/selectors/featureFlagController/card/index.ts b/app/selectors/featureFlagController/card/index.ts
index 377069a014a..bb7e3572b2c 100644
--- a/app/selectors/featureFlagController/card/index.ts
+++ b/app/selectors/featureFlagController/card/index.ts
@@ -149,7 +149,7 @@ const defaultCardSupportedCountries: CardSupportedCountries = {
export type CardSupportedCountries = Record;
-export interface DisplayCardButtonFeatureFlag {
+export interface GateVersionedFeatureFlag {
enabled: boolean;
minimumVersion: string;
}
@@ -188,7 +188,7 @@ export const selectDisplayCardButtonFeatureFlag = createSelector(
selectRemoteFeatureFlags,
(remoteFeatureFlags) => {
const remoteFlag =
- remoteFeatureFlags?.displayCardButton as unknown as DisplayCardButtonFeatureFlag;
+ remoteFeatureFlags?.displayCardButton as unknown as GateVersionedFeatureFlag;
return validatedVersionGatedFeatureFlag(remoteFlag) ?? false;
},
@@ -196,7 +196,12 @@ export const selectDisplayCardButtonFeatureFlag = createSelector(
export const selectCardExperimentalSwitch = createSelector(
selectRemoteFeatureFlags,
- (remoteFeatureFlags) => remoteFeatureFlags?.cardExperimentalSwitch ?? false,
+ (remoteFeatureFlags) => {
+ const remoteFlag =
+ remoteFeatureFlags?.cardExperimentalSwitch2 as unknown as GateVersionedFeatureFlag;
+
+ return validatedVersionGatedFeatureFlag(remoteFlag) ?? false;
+ },
);
export const selectCardFeatureFlag = createSelector(
From 3c8c4285528f0438cbc8b73fc94cb5e5b3a8f86f Mon Sep 17 00:00:00 2001
From: javiergarciavera <76975121+javiergarciavera@users.noreply.github.com>
Date: Mon, 3 Nov 2025 17:58:55 +0100
Subject: [PATCH 10/11] test: fixes for some tests that were failing (#22059)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
## **Description**
## **Changelog**
CHANGELOG entry:
## **Related issues**
Fixes:
## **Manual testing steps**
```gherkin
Feature: my feature name
Scenario: user [verb for user action]
Given [describe expected initial app state]
When user [verb for user action]
Then [describe expected outcome]
```
## **Screenshots/Recordings**
### **Before**
### **After**
## **Pre-merge author checklist**
- [ ] 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).
- [ ] I've completed the PR template to the best of my ability
- [ ] I’ve included tests if applicable
- [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [ ] 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.
---
> [!NOTE]
> Updates selectors and waits to stabilize Send and Bridge test flows,
including using `review-button`, simplifying iOS token selection, and
extending Solana confirmation wait.
>
> - **E2E/Tests**:
> - **Send flow**:
> - Use `review-button` ID for `SendScreen` review action and assert
visibility before tapping.
> - Solana test waits for `ConfirmationScreen.isVisible('Solana',
180000)` to allow longer confirmation time.
> - **Bridge flow**:
> - Simplify iOS token selection to use `getElementByID` for
`asset--`.
> - **Selectors/UX waits**:
> - Add explicit visibility checks and logs to improve test reliability.
>
> Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
79dbcee7db2f864b4fd29a3f0e16c49b85edc6a0. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).
---
appwright/tests/performance/login/send-flows.spec.js | 2 +-
wdio/screen-objects/BridgeScreen.js | 11 ++---------
wdio/screen-objects/SendScreen.js | 8 +++++---
3 files changed, 8 insertions(+), 13 deletions(-)
diff --git a/appwright/tests/performance/login/send-flows.spec.js b/appwright/tests/performance/login/send-flows.spec.js
index f87f800e8c3..ba2f2681c91 100644
--- a/appwright/tests/performance/login/send-flows.spec.js
+++ b/appwright/tests/performance/login/send-flows.spec.js
@@ -128,7 +128,7 @@ test('Send flow - Solana, SRP 1 + SRP 2 + SRP 3', async ({
await SendScreen.typeAddressInSendAddressField(solanaAddress);
await SendScreen.clickOnReviewButton();
timer4.start();
- await ConfirmationScreen.isVisible();
+ await ConfirmationScreen.isVisible('Solana', 180000);
timer4.stop();
performanceTracker.addTimer(timer1);
diff --git a/wdio/screen-objects/BridgeScreen.js b/wdio/screen-objects/BridgeScreen.js
index 2f3997da6b1..3381dc86b12 100644
--- a/wdio/screen-objects/BridgeScreen.js
+++ b/wdio/screen-objects/BridgeScreen.js
@@ -113,15 +113,8 @@ class BridgeScreen {
else {
// Try multiple iOS element selection strategies
console.log(`Looking for iOS token with ID: asset-${tokenNetworkId}-${token}`);
-
- try {
- tokenButton = await AppwrightSelectors.getElementByNameiOS(this._device, `asset-${tokenNetworkId}-${token}`);
- console.log('Found token button by Name');
- } catch (error) {
- console.log('Name selector failed, trying ID selector for iOS...');
- tokenButton = await AppwrightSelectors.getElementByID(this._device, `asset-${tokenNetworkId}-${token}`);
- console.log('Found token button by ID');
- }
+ tokenButton = await AppwrightSelectors.getElementByID(this._device, `asset-${tokenNetworkId}-${token}`);
+
}
await appwrightExpect(tokenButton).toBeVisible({ timeout: 10000 });
console.log('Token button found and visible');
diff --git a/wdio/screen-objects/SendScreen.js b/wdio/screen-objects/SendScreen.js
index 61abff6e5c8..3037ae4a4a8 100644
--- a/wdio/screen-objects/SendScreen.js
+++ b/wdio/screen-objects/SendScreen.js
@@ -44,7 +44,7 @@ class SendScreen {
}
get reviewButton() {
- return AppwrightSelectors.getElementByID(this._device, 'review-button-send');
+ return AppwrightSelectors.getElementByID(this._device, 'review-button');
}
get sendAddressInputField() {
@@ -100,13 +100,15 @@ class SendScreen {
} else {
console.log('Typing address in send address field');
const element = await AppwrightSelectors.getElementByCatchAll(this._device, 'Enter address to send to');
- console.log('element got found', address);
await AppwrightGestures.typeText(element, address);
}
}
async clickOnReviewButton() {
- const reviewButton = await this.reviewButton;
+ const reviewButton = await AppwrightSelectors.getElementByID(this._device, 'review-button');
+ await appwrightExpect(reviewButton).toBeVisible({timeout: 30000});
+
+ console.log('Review button visible, tapping');
await reviewButton.tap();
}
From 4ac877326ad3835ef572a6cf3aea68814135b43b Mon Sep 17 00:00:00 2001
From: Christopher Ferreira
<104831203+christopherferreira9@users.noreply.github.com>
Date: Mon, 3 Nov 2025 17:14:52 +0000
Subject: [PATCH 11/11] test: adds function handling for fixture (#21883)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
## **Description**
This PR adds the ability to send fixtures to `withFixtures` as a
function as some fixtures require dynamic ports and fixtures are
provided before having these ports.
## **Changelog**
CHANGELOG entry:
## **Related issues**
Fixes:
## **Manual testing steps**
```gherkin
Feature: my feature name
Scenario: user [verb for user action]
Given [describe expected initial app state]
When user [verb for user action]
Then [describe expected outcome]
```
## **Screenshots/Recordings**
### **Before**
### **After**
## **Pre-merge author checklist**
- [ ] 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).
- [ ] I've completed the PR template to the best of my ability
- [ ] I’ve included tests if applicable
- [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [ ] 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.
---
> [!NOTE]
> withFixtures now accepts a fixture factory resolved after local nodes
start, enabling dynamic Anvil RPC ports; tests migrated from
withGanacheNetwork to withNetworkController; docs and types updated;
withGanacheNetwork deprecated.
>
> - **E2E Framework**:
> - `withFixtures` can now take a function `fixture(ctx)` and resolves
it after local nodes start so dynamic ports (`localNodes`) are
available.
> - Update `WithFixturesOptions` types and docs to accept
`FixtureBuilder | (ctx) => FixtureBuilder | Promise<...>`.
> - Resolve fixture in `FixtureHelper` post local-node startup; pass
`localNodes` to factory.
> - Add `AnvilManager#getPort()` for retrieving the actual RPC port.
> - Deprecate `FixtureBuilder.withGanacheNetwork` (adds optional `port =
AnvilPort()`); prefer `withNetworkController`.
> - Docs: README tables/examples updated; remove Ganache-specific
example.
> - **Test Migration**:
> - Replace `.withGanacheNetwork()` with function-based fixtures using
`.withNetworkController({ providerConfig: { chainId: '0x539', rpcUrl:
\`http://localhost:${rpcPort ?? AnvilPort()}\`, ... } })` across specs
in `e2e/specs/**`.
> - Import and use `AnvilManager`/`AnvilPort` to derive dynamic RPC
URLs; minor test tweaks (e.g., removed manual network selection, added
waits).
>
> Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
16409c8bbe0d5bd653c4db0816170571f7ea08d8. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).
---
e2e/docs/README.md | 3 -
e2e/framework/fixtures/FixtureBuilder.ts | 11 +-
e2e/framework/fixtures/FixtureHelper.ts | 12 +-
e2e/framework/fixtures/README.md | 30 +--
e2e/framework/types.ts | 8 +-
e2e/seeder/anvil-manager.ts | 7 +
.../error-boundary-srp-backup.spec.ts | 35 +++-
e2e/specs/assets/import-custom-token.spec.ts | 36 +++-
.../import-tokens-via-asset-watcher.spec.ts | 35 +++-
e2e/specs/assets/nft-details.spec.ts | 35 +++-
e2e/specs/assets/transaction.spec.ts | 31 ++-
.../signatures/signatures-typed.spec.ts | 35 +++-
.../signatures/signatures.spec.ts | 35 +++-
.../transactions/contract-deployment.spec.ts | 35 +++-
.../transactions/contract-interaction.spec.ts | 35 +++-
.../dapp-initiated-transfer.spec.ts | 43 +++-
.../per-dapp-selected-network.spec.ts | 63 ++++--
.../transactions/send-max-transfer.spec.ts | 41 +++-
.../token-approve/approve.spec.ts | 63 ++++--
.../token-approve/increase-allowance.spec.ts | 35 +++-
.../set-approval-for-all.spec.ts | 110 ++++++++--
.../wallet-initiated-transfer.spec.ts | 41 +++-
.../advanced-gas-fees.mock.spec.ts | 23 +-
.../approve-custom-erc20.spec.ts | 35 +++-
.../approve-default-erc20.spec.ts | 35 +++-
.../confirmations/approve-erc721.spec.ts | 35 +++-
.../batch-transfer-erc1155.spec.ts | 35 +++-
.../increase-allowance-erc20.spec.ts | 35 +++-
.../send-erc20-with-dapp.spec.ts | 39 +++-
e2e/specs/confirmations/send-erc721.spec.ts | 35 +++-
.../send-failing-contract.spec.ts | 35 +++-
.../send-to-contract-address.spec.ts | 23 +-
.../set-approval-for-all-erc1155.spec.ts | 35 +++-
.../set-approve-for-all-erc721.spec.ts | 35 +++-
.../signatures/ethereum-sign.spec.ts | 35 +++-
.../signatures/personal-sign.spec.ts | 35 +++-
.../signatures/typed-sign-v3.spec.ts | 35 +++-
.../signatures/typed-sign-v4.spec.ts | 35 +++-
.../signatures/typed-sign.spec.ts | 35 +++-
.../edit-recipient-address.failing.ts | 23 +-
.../security-alert-send-eth.failing.ts | 23 +-
e2e/specs/quarantine/send-eth.failing.ts | 63 +++++-
.../swap-action-regression.failing.ts | 28 ++-
e2e/specs/send/send-erc20-token.spec.ts | 198 +++++++++++-------
e2e/specs/send/send-native-token.spec.ts | 29 ++-
.../addressbook-send-add-contact.spec.ts | 93 ++++++--
e2e/specs/settings/example-anvil-e2e.spec.ts | 24 ++-
.../wallet/balance-privacy-toggle.spec.ts | 27 ++-
e2e/specs/wallet/send-ERC-token.spec.ts | 31 ++-
49 files changed, 1482 insertions(+), 411 deletions(-)
diff --git a/e2e/docs/README.md b/e2e/docs/README.md
index d1180c0b535..85de560fedf 100644
--- a/e2e/docs/README.md
+++ b/e2e/docs/README.md
@@ -193,9 +193,6 @@ new FixtureBuilder().build();
// With popular networks
new FixtureBuilder().withPopularNetworks().build();
-// With Ganache network
-new FixtureBuilder().withGanacheNetwork().build();
-
// With connected test dapp
new FixtureBuilder()
.withPermissionControllerConnectedToTestDapp(buildPermissions(['0x539']))
diff --git a/e2e/framework/fixtures/FixtureBuilder.ts b/e2e/framework/fixtures/FixtureBuilder.ts
index 2f541d2e66f..1e2dfc4d8bb 100644
--- a/e2e/framework/fixtures/FixtureBuilder.ts
+++ b/e2e/framework/fixtures/FixtureBuilder.ts
@@ -4,6 +4,7 @@ import {
getTestDappLocalUrl,
getMockServerPort,
getTestDappLocalUrlByDappCounter,
+ AnvilPort,
} from './FixtureUtils';
import { merge } from 'lodash';
import { encryptVault } from './helpers';
@@ -1267,7 +1268,13 @@ class FixtureBuilder {
return this;
}
- withGanacheNetwork(chainId = '0x539') {
+ /**
+ * @deprecated Use withNetworkController instead
+ * @param chainId
+ * @param port
+ * @returns
+ */
+ withGanacheNetwork(chainId = '0x539', port = AnvilPort()) {
const fixtures = this.fixture.state.engine.backgroundState;
// Generate a unique key for the new network client ID
@@ -1282,7 +1289,7 @@ class FixtureBuilder {
rpcEndpoints: [
{
networkClientId: newNetworkClientId,
- url: `http://localhost:${getGanachePort()}`,
+ url: `http://localhost:${port}`,
type: 'custom',
name: 'Localhost',
},
diff --git a/e2e/framework/fixtures/FixtureHelper.ts b/e2e/framework/fixtures/FixtureHelper.ts
index 89995795465..ed157dc8203 100644
--- a/e2e/framework/fixtures/FixtureHelper.ts
+++ b/e2e/framework/fixtures/FixtureHelper.ts
@@ -405,7 +405,7 @@ export async function withFixtures(
testSuite: TestSuiteFunction,
) {
const {
- fixture,
+ fixture: fixtureOption,
restartDevice = false,
smartContracts,
disableLocalNodes = false,
@@ -465,6 +465,14 @@ export async function withFixtures(
);
}
+ // Resolve fixture after local nodes are started so dynamic ports are known
+ let resolvedFixture: FixtureBuilder;
+ if (typeof fixtureOption === 'function') {
+ resolvedFixture = await fixtureOption({ localNodes });
+ } else {
+ resolvedFixture = fixtureOption;
+ }
+
// Handle dapps
if (dapps && dapps.length > 0) {
await handleDapps(dapps, dappServer);
@@ -472,7 +480,7 @@ export async function withFixtures(
// Start fixture server
await startFixtureServer(fixtureServer);
- await loadFixture(fixtureServer, { fixture });
+ await loadFixture(fixtureServer, { fixture: resolvedFixture });
logger.debug(
'The fixture server is started, and the initial state is successfully loaded.',
);
diff --git a/e2e/framework/fixtures/README.md b/e2e/framework/fixtures/README.md
index 5c94642d423..5995f2b8ac6 100644
--- a/e2e/framework/fixtures/README.md
+++ b/e2e/framework/fixtures/README.md
@@ -29,21 +29,21 @@ describe('My Test Suite', () => {
## WithFixturesOptions Reference
-| Option | Type | Required | Default | Description |
-| ----------------------- | ----------------------- | -------- | ------- | ----------------------------------------------------------------------------------------------------- |
-| `fixture` | `FixtureBuilder` | `true` | - | The fixture object created via FixtureBuilder |
-| `restartDevice` | `boolean` | `false` | `false` | Whether to restart the device before the test |
-| `smartContracts` | `string[]` | `false` | - | The list of contract strings to be deployed via the first seeder |
-| `disableLocalNodes` | `boolean` | `false` | `false` | Disables all local nodes for the test |
-| `dapps` | `DappOptions[]` | `false` | - | Lists the dapps that should be launched before the tests |
-| `localNodeOptions` | `LocalNodeOptionsInput` | `false` | Anvil | Allows overriding the use of Anvil in favor of any other node |
-| `testSpecificMock` | `TestSpecificMock` | `false` | - | Allows to set mocks that are specific to the test |
-| `launcArgs` | `LaunchArgs` | `false` | `-` | Allows sending arbitrary launchArgs such as the fixtureServerPort |
-| `languageAndLocale` | `LanguageAndLocale` | `false` | - | Set the device Language and Locale of the device |
-| `permissions` | `object` | `false` | - | Allows setting specific device permissions |
-| `endTestfn` | `fn()` | `false` | - | Allows providing a function that is executed at the end of the test before the cleanup |
-| `skipReactNativeReload` | `boolean` | `false` | `false` | Skip React Native reload during cleanup to preserve app state between tests |
-| `useCommandQueueServer` | `boolean` | `false` | `false` | Launches an instance of CommandQueueServer to create a queue of items the app consumes on E2E context |
+| Option | Type | Required | Default | Description |
+| ----------------------- | ------------------------------------------------------- | -------- | ------- | ----------------------------------------------------------------------------------------------------- |
+| `fixture` | `FixtureBuilder \| (ctx: { localNodes?: LocalNode[] })` | `true` | - | The fixture object created via FixtureBuilder or function that returns a fixtureBuilder |
+| `restartDevice` | `boolean` | `false` | `false` | Whether to restart the device before the test |
+| `smartContracts` | `string[]` | `false` | - | The list of contract strings to be deployed via the first seeder |
+| `disableLocalNodes` | `boolean` | `false` | `false` | Disables all local nodes for the test |
+| `dapps` | `DappOptions[]` | `false` | - | Lists the dapps that should be launched before the tests |
+| `localNodeOptions` | `LocalNodeOptionsInput` | `false` | Anvil | Allows overriding the use of Anvil in favor of any other node |
+| `testSpecificMock` | `TestSpecificMock` | `false` | - | Allows to set mocks that are specific to the test |
+| `launcArgs` | `LaunchArgs` | `false` | `-` | Allows sending arbitrary launchArgs such as the fixtureServerPort |
+| `languageAndLocale` | `LanguageAndLocale` | `false` | - | Set the device Language and Locale of the device |
+| `permissions` | `object` | `false` | - | Allows setting specific device permissions |
+| `endTestfn` | `fn()` | `false` | - | Allows providing a function that is executed at the end of the test before the cleanup |
+| `skipReactNativeReload` | `boolean` | `false` | `false` | Skip React Native reload during cleanup to preserve app state between tests |
+| `useCommandQueueServer` | `boolean` | `false` | `false` | Launches an instance of CommandQueueServer to create a queue of items the app consumes on E2E context |
## Migration from Legacy Options
diff --git a/e2e/framework/types.ts b/e2e/framework/types.ts
index 7e8856dcfb3..74429ebe8f2 100644
--- a/e2e/framework/types.ts
+++ b/e2e/framework/types.ts
@@ -213,7 +213,7 @@ export type TestSpecificMock = (mockServer: Mockttp) => Promise;
/**
* The options for the withFixtures function.
- * @param {FixtureBuilder} fixture - The state of the fixture to load.
+ * @param {FixtureBuilder | ((ctx: { localNodes?: LocalNode[] }) => FixtureBuilder | Promise)} fixture - The state of the fixture to load or a function that returns a fixture builder.
* @param {boolean} [restartDevice=false] - If true, restarts the app to apply the loaded fixture.
* @param {string[]} [smartContracts] - The smart contracts to load for test. These will be deployed on the different {localNodeOptions}
* @param {LocalNodeOptionsInput} [localNodeOptions] - The local node options to use for the test.
@@ -226,7 +226,11 @@ export type TestSpecificMock = (mockServer: Mockttp) => Promise;
* @param {() => Promise} [endTestfn] - The function to execute after the test is finished.
*/
export interface WithFixturesOptions {
- fixture: FixtureBuilder;
+ fixture:
+ | FixtureBuilder
+ | ((ctx: {
+ localNodes?: LocalNode[];
+ }) => FixtureBuilder | Promise);
restartDevice?: boolean;
smartContracts?: string[];
disableLocalNodes?: boolean;
diff --git a/e2e/seeder/anvil-manager.ts b/e2e/seeder/anvil-manager.ts
index 49bedfc7208..76b2715eade 100644
--- a/e2e/seeder/anvil-manager.ts
+++ b/e2e/seeder/anvil-manager.ts
@@ -249,6 +249,13 @@ class AnvilManager {
return { walletClient, publicClient, testClient };
}
+ /**
+ * Returns the port the Anvil server is listening on (if started).
+ */
+ getPort(): number | undefined {
+ return this.serverPort;
+ }
+
/**
* Get all accounts available on the Anvil server
* @returns {Promise} Array of account addresses
diff --git a/e2e/specs/accounts/error-boundary-srp-backup.spec.ts b/e2e/specs/accounts/error-boundary-srp-backup.spec.ts
index 78b2c719017..ce2f816359d 100644
--- a/e2e/specs/accounts/error-boundary-srp-backup.spec.ts
+++ b/e2e/specs/accounts/error-boundary-srp-backup.spec.ts
@@ -11,7 +11,10 @@ import TestHelpers from '../../helpers';
import Assertions from '../../framework/Assertions';
import RevealSecretRecoveryPhrase from '../../pages/Settings/SecurityAndPrivacy/RevealSecretRecoveryPhrase';
import ErrorBoundaryView from '../../pages/ErrorBoundaryView/ErrorBoundaryView';
-import { buildPermissions } from '../../framework/fixtures/FixtureUtils';
+import {
+ AnvilPort,
+ buildPermissions,
+} from '../../framework/fixtures/FixtureUtils';
import { setupMockPostRequest } from '../../api-mocking/helpers/mockHelpers';
import { Mockttp } from 'mockttp';
import {
@@ -19,6 +22,8 @@ import {
SECURITY_ALERTS_REQUEST_BODY,
securityAlertsUrl,
} from '../../api-mocking/mock-responses/security-alerts-mock';
+import { LocalNode } from '../../framework/types';
+import { AnvilManager } from '../../seeder/anvil-manager';
const PASSWORD = '123123123';
@@ -36,12 +41,28 @@ describe(RegressionAccounts('Error Boundary Screen'), () => {
dappVariant: DappVariants.TEST_DAPP,
},
],
- fixture: new FixtureBuilder()
- .withGanacheNetwork()
- .withPermissionControllerConnectedToTestDapp(
- buildPermissions(['0x539']),
- )
- .build(),
+ fixture: ({ localNodes }: { localNodes?: LocalNode[] }) => {
+ const node = localNodes?.[0] as unknown as AnvilManager;
+ const rpcPort =
+ node instanceof AnvilManager
+ ? (node.getPort() ?? AnvilPort())
+ : undefined;
+
+ return new FixtureBuilder()
+ .withNetworkController({
+ providerConfig: {
+ chainId: '0x539',
+ rpcUrl: `http://localhost:${rpcPort ?? AnvilPort()}`,
+ type: 'custom',
+ nickname: 'Local RPC',
+ ticker: 'ETH',
+ },
+ })
+ .withPermissionControllerConnectedToTestDapp(
+ buildPermissions(['0x539']),
+ )
+ .build();
+ },
restartDevice: true,
testSpecificMock: async (mockServer: Mockttp) => {
await setupMockPostRequest(
diff --git a/e2e/specs/assets/import-custom-token.spec.ts b/e2e/specs/assets/import-custom-token.spec.ts
index 2a871fb3f58..25359899282 100644
--- a/e2e/specs/assets/import-custom-token.spec.ts
+++ b/e2e/specs/assets/import-custom-token.spec.ts
@@ -8,6 +8,9 @@ import { withFixtures } from '../../framework/fixtures/FixtureHelper';
import FixtureBuilder from '../../framework/fixtures/FixtureBuilder';
import { loginToApp } from '../../viewHelper';
import { SMART_CONTRACTS } from '../../../app/util/test/smart-contracts';
+import { AnvilPort } from '../../framework/fixtures/FixtureUtils';
+import { LocalNode } from '../../framework/types';
+import { AnvilManager } from '../../seeder/anvil-manager';
describe(RegressionAssets('Import custom token'), () => {
beforeAll(async () => {
@@ -18,12 +21,28 @@ describe(RegressionAssets('Import custom token'), () => {
it('should Import custom token with auto-population', async () => {
await withFixtures(
{
- fixture: new FixtureBuilder()
- .withGanacheNetwork()
- .withNetworkEnabledMap({
- eip155: { '0x539': true },
- })
- .build(),
+ fixture: ({ localNodes }: { localNodes?: LocalNode[] }) => {
+ const node = localNodes?.[0] as unknown as AnvilManager;
+ const rpcPort =
+ node instanceof AnvilManager
+ ? (node.getPort() ?? AnvilPort())
+ : undefined;
+
+ return new FixtureBuilder()
+ .withNetworkController({
+ providerConfig: {
+ chainId: '0x539',
+ rpcUrl: `http://localhost:${rpcPort ?? AnvilPort()}`,
+ type: 'custom',
+ nickname: 'Local RPC',
+ ticker: 'ETH',
+ },
+ })
+ .withNetworkEnabledMap({
+ eip155: { '0x539': true },
+ })
+ .build();
+ },
restartDevice: true,
smartContracts: [SMART_CONTRACTS.HST],
},
@@ -34,11 +53,8 @@ describe(RegressionAssets('Import custom token'), () => {
await loginToApp();
await WalletView.tapImportTokensButton();
- await ImportTokensView.switchToCustomTab();
- await ImportTokensView.tapOnNetworkInput();
- await ImportTokensView.swipeNetworkList();
- await ImportTokensView.tapNetworkOption('Localhost');
await ImportTokensView.typeTokenAddress(hstAddress);
+ await new Promise((resolve) => setTimeout(resolve, 20000));
await Assertions.expectElementToHaveText(
ImportTokensView.symbolInput,
'TST',
diff --git a/e2e/specs/assets/import-tokens-via-asset-watcher.spec.ts b/e2e/specs/assets/import-tokens-via-asset-watcher.spec.ts
index f7ff84ffcf4..845666bbc41 100644
--- a/e2e/specs/assets/import-tokens-via-asset-watcher.spec.ts
+++ b/e2e/specs/assets/import-tokens-via-asset-watcher.spec.ts
@@ -12,12 +12,17 @@ import Assertions from '../../framework/Assertions';
import AssetWatchBottomSheet from '../../pages/Transactions/AssetWatchBottomSheet';
import WalletView from '../../pages/wallet/WalletView';
import NetworkListModal from '../../pages/Network/NetworkListModal';
-import { buildPermissions } from '../../framework/fixtures/FixtureUtils';
+import {
+ AnvilPort,
+ buildPermissions,
+} from '../../framework/fixtures/FixtureUtils';
import { DappVariants } from '../../framework/Constants';
import {
setEthAccounts,
Caip25EndowmentPermissionName,
} from '@metamask/chain-agnostic-permission';
+import { LocalNode } from '../../framework/types';
+import { AnvilManager } from '../../seeder/anvil-manager';
const ERC20_CONTRACT = SMART_CONTRACTS.HST;
@@ -47,12 +52,28 @@ describe(RegressionNetworkAbstractions('Asset Watch:'), () => {
dappVariant: DappVariants.TEST_DAPP,
},
],
- fixture: new FixtureBuilder()
- .withGanacheNetwork()
- .withPermissionControllerConnectedToTestDapp(
- buildERC20PermsForAddress(),
- )
- .build(),
+ fixture: ({ localNodes }: { localNodes?: LocalNode[] }) => {
+ const node = localNodes?.[0] as unknown as AnvilManager;
+ const rpcPort =
+ node instanceof AnvilManager
+ ? (node.getPort() ?? AnvilPort())
+ : undefined;
+
+ return new FixtureBuilder()
+ .withNetworkController({
+ providerConfig: {
+ chainId: '0x539',
+ rpcUrl: `http://localhost:${rpcPort ?? AnvilPort()}`,
+ type: 'custom',
+ nickname: 'Local RPC',
+ ticker: 'ETH',
+ },
+ })
+ .withPermissionControllerConnectedToTestDapp(
+ buildERC20PermsForAddress(),
+ )
+ .build();
+ },
restartDevice: true,
smartContracts: [ERC20_CONTRACT],
},
diff --git a/e2e/specs/assets/nft-details.spec.ts b/e2e/specs/assets/nft-details.spec.ts
index be7b9ff175c..a218c172c00 100644
--- a/e2e/specs/assets/nft-details.spec.ts
+++ b/e2e/specs/assets/nft-details.spec.ts
@@ -8,8 +8,13 @@ import WalletView from '../../pages/wallet/WalletView';
import ImportNFTView from '../../pages/wallet/ImportNFTFlow/ImportNFTView';
import Assertions from '../../framework/Assertions';
import enContent from '../../../locales/languages/en.json';
-import { buildPermissions } from '../../framework/fixtures/FixtureUtils';
+import {
+ AnvilPort,
+ buildPermissions,
+} from '../../framework/fixtures/FixtureUtils';
import { DappVariants } from '../../framework/Constants';
+import { LocalNode } from '../../framework/types';
+import { AnvilManager } from '../../seeder/anvil-manager';
describe.skip(RegressionAssets('NFT Details page'), () => {
const NFT_CONTRACT = SMART_CONTRACTS.NFTS;
@@ -22,12 +27,28 @@ describe.skip(RegressionAssets('NFT Details page'), () => {
it('show nft details', async () => {
await withFixtures(
{
- fixture: new FixtureBuilder()
- .withGanacheNetwork()
- .withPermissionControllerConnectedToTestDapp(
- buildPermissions(['0x539']),
- )
- .build(),
+ fixture: ({ localNodes }: { localNodes?: LocalNode[] }) => {
+ const node = localNodes?.[0] as unknown as AnvilManager;
+ const rpcPort =
+ node instanceof AnvilManager
+ ? (node.getPort() ?? AnvilPort())
+ : undefined;
+
+ return new FixtureBuilder()
+ .withNetworkController({
+ providerConfig: {
+ chainId: '0x539',
+ rpcUrl: `http://localhost:${rpcPort ?? AnvilPort()}`,
+ type: 'custom',
+ nickname: 'Local RPC',
+ ticker: 'ETH',
+ },
+ })
+ .withPermissionControllerConnectedToTestDapp(
+ buildPermissions(['0x539']),
+ )
+ .build();
+ },
dapps: [
{
dappVariant: DappVariants.TEST_DAPP,
diff --git a/e2e/specs/assets/transaction.spec.ts b/e2e/specs/assets/transaction.spec.ts
index 578a31b97e4..1e95a2f33d8 100644
--- a/e2e/specs/assets/transaction.spec.ts
+++ b/e2e/specs/assets/transaction.spec.ts
@@ -15,6 +15,9 @@ import ToastModal from '../../pages/wallet/ToastModal';
import { Mockttp } from 'mockttp';
import { setupRemoteFeatureFlagsMock } from '../../api-mocking/helpers/remoteFeatureFlagsHelper';
import { oldConfirmationsRemoteFeatureFlags } from '../../api-mocking/mock-responses/feature-flags-mocks';
+import { LocalNode } from '../../framework/types';
+import { AnvilPort } from '../../framework/fixtures/FixtureUtils';
+import { AnvilManager } from '../../seeder/anvil-manager';
describe(RegressionAssets('Transaction'), () => {
beforeAll(async () => {
@@ -29,12 +32,28 @@ describe(RegressionAssets('Transaction'), () => {
const TOKEN_NAME = enContent.unit.eth;
await withFixtures(
{
- fixture: new FixtureBuilder()
- .withGanacheNetwork()
- .withNetworkEnabledMap({
- eip155: { '0x539': true },
- })
- .build(),
+ fixture: ({ localNodes }: { localNodes?: LocalNode[] }) => {
+ const node = localNodes?.[0] as unknown as AnvilManager;
+ const rpcPort =
+ node instanceof AnvilManager
+ ? (node.getPort() ?? AnvilPort())
+ : undefined;
+
+ return new FixtureBuilder()
+ .withNetworkController({
+ providerConfig: {
+ chainId: '0x539',
+ rpcUrl: `http://localhost:${rpcPort ?? AnvilPort()}`,
+ type: 'custom',
+ nickname: 'Local RPC',
+ ticker: 'ETH',
+ },
+ })
+ .withNetworkEnabledMap({
+ eip155: { '0x539': true },
+ })
+ .build();
+ },
restartDevice: true,
testSpecificMock: async (mockServer: Mockttp) => {
await setupRemoteFeatureFlagsMock(
diff --git a/e2e/specs/confirmations-redesigned/signatures/signatures-typed.spec.ts b/e2e/specs/confirmations-redesigned/signatures/signatures-typed.spec.ts
index d772a20f40a..f90df7ae272 100644
--- a/e2e/specs/confirmations-redesigned/signatures/signatures-typed.spec.ts
+++ b/e2e/specs/confirmations-redesigned/signatures/signatures-typed.spec.ts
@@ -8,12 +8,17 @@ import TestDApp from '../../../pages/Browser/TestDApp';
import { loginToApp } from '../../../viewHelper';
import { withFixtures } from '../../../framework/fixtures/FixtureHelper';
import { SmokeConfirmationsRedesigned } from '../../../tags';
-import { buildPermissions } from '../../../framework/fixtures/FixtureUtils';
+import {
+ buildPermissions,
+ AnvilPort,
+} from '../../../framework/fixtures/FixtureUtils';
import RowComponents from '../../../pages/Browser/Confirmations/RowComponents';
import { DappVariants } from '../../../framework/Constants';
import { Mockttp } from 'mockttp';
import { setupRemoteFeatureFlagsMock } from '../../../api-mocking/helpers/remoteFeatureFlagsHelper';
import { confirmationsRedesignedFeatureFlags } from '../../../api-mocking/mock-responses/feature-flags-mocks';
+import { LocalNode } from '../../../framework/types';
+import { AnvilManager } from '../../../seeder/anvil-manager';
const SIGNATURE_LIST = [
{
@@ -68,12 +73,28 @@ describe(SmokeConfirmationsRedesigned('Typed Signature Requests'), () => {
dappVariant: DappVariants.TEST_DAPP,
},
],
- fixture: new FixtureBuilder()
- .withGanacheNetwork()
- .withPermissionControllerConnectedToTestDapp(
- buildPermissions(['0x539']),
- )
- .build(),
+ fixture: ({ localNodes }: { localNodes?: LocalNode[] }) => {
+ const node = localNodes?.[0] as unknown as AnvilManager;
+ const rpcPort =
+ node instanceof AnvilManager
+ ? (node.getPort() ?? AnvilPort())
+ : undefined;
+
+ return new FixtureBuilder()
+ .withNetworkController({
+ providerConfig: {
+ chainId: '0x539',
+ rpcUrl: `http://localhost:${rpcPort ?? AnvilPort()}`,
+ type: 'custom',
+ nickname: 'Local RPC',
+ ticker: 'ETH',
+ },
+ })
+ .withPermissionControllerConnectedToTestDapp(
+ buildPermissions(['0x539']),
+ )
+ .build();
+ },
restartDevice: true,
testSpecificMock,
},
diff --git a/e2e/specs/confirmations-redesigned/signatures/signatures.spec.ts b/e2e/specs/confirmations-redesigned/signatures/signatures.spec.ts
index 21b5399283d..7e4a98e9cec 100644
--- a/e2e/specs/confirmations-redesigned/signatures/signatures.spec.ts
+++ b/e2e/specs/confirmations-redesigned/signatures/signatures.spec.ts
@@ -8,12 +8,17 @@ import TestDApp from '../../../pages/Browser/TestDApp';
import { loginToApp } from '../../../viewHelper';
import { withFixtures } from '../../../framework/fixtures/FixtureHelper';
import { SmokeConfirmationsRedesigned } from '../../../tags';
-import { buildPermissions } from '../../../framework/fixtures/FixtureUtils';
+import {
+ buildPermissions,
+ AnvilPort,
+} from '../../../framework/fixtures/FixtureUtils';
import RowComponents from '../../../pages/Browser/Confirmations/RowComponents';
import { DappVariants } from '../../../framework/Constants';
import { Mockttp } from 'mockttp';
import { setupRemoteFeatureFlagsMock } from '../../../api-mocking/helpers/remoteFeatureFlagsHelper';
import { confirmationsRedesignedFeatureFlags } from '../../../api-mocking/mock-responses/feature-flags-mocks';
+import { LocalNode } from '../../../framework/types';
+import { AnvilManager } from '../../../seeder/anvil-manager';
const SIGNATURE_LIST = [
{
@@ -62,12 +67,28 @@ describe(SmokeConfirmationsRedesigned('Signature Requests'), () => {
dappVariant: DappVariants.TEST_DAPP,
},
],
- fixture: new FixtureBuilder()
- .withGanacheNetwork()
- .withPermissionControllerConnectedToTestDapp(
- buildPermissions(['0x539']),
- )
- .build(),
+ fixture: ({ localNodes }: { localNodes?: LocalNode[] }) => {
+ const node = localNodes?.[0] as unknown as AnvilManager;
+ const rpcPort =
+ node instanceof AnvilManager
+ ? (node.getPort() ?? AnvilPort())
+ : undefined;
+
+ return new FixtureBuilder()
+ .withNetworkController({
+ providerConfig: {
+ chainId: '0x539',
+ rpcUrl: `http://localhost:${rpcPort ?? AnvilPort()}`,
+ type: 'custom',
+ nickname: 'Local RPC',
+ ticker: 'ETH',
+ },
+ })
+ .withPermissionControllerConnectedToTestDapp(
+ buildPermissions(['0x539']),
+ )
+ .build();
+ },
restartDevice: true,
testSpecificMock,
},
diff --git a/e2e/specs/confirmations-redesigned/transactions/contract-deployment.spec.ts b/e2e/specs/confirmations-redesigned/transactions/contract-deployment.spec.ts
index ebc949e32df..0c97c356718 100644
--- a/e2e/specs/confirmations-redesigned/transactions/contract-deployment.spec.ts
+++ b/e2e/specs/confirmations-redesigned/transactions/contract-deployment.spec.ts
@@ -7,7 +7,10 @@ import ConfirmationUITypes from '../../../pages/Browser/Confirmations/Confirmati
import FooterActions from '../../../pages/Browser/Confirmations/FooterActions';
import Assertions from '../../../framework/Assertions';
import { withFixtures } from '../../../framework/fixtures/FixtureHelper';
-import { buildPermissions } from '../../../framework/fixtures/FixtureUtils';
+import {
+ buildPermissions,
+ AnvilPort,
+} from '../../../framework/fixtures/FixtureUtils';
import RowComponents from '../../../pages/Browser/Confirmations/RowComponents';
import { SIMULATION_ENABLED_NETWORKS_MOCK } from '../../../api-mocking/mock-responses/simulations';
import TestDApp from '../../../pages/Browser/TestDApp';
@@ -16,6 +19,8 @@ import { Mockttp } from 'mockttp';
import { setupMockRequest } from '../../../api-mocking/helpers/mockHelpers';
import { setupRemoteFeatureFlagsMock } from '../../../api-mocking/helpers/remoteFeatureFlagsHelper';
import { confirmationsRedesignedFeatureFlags } from '../../../api-mocking/mock-responses/feature-flags-mocks';
+import { LocalNode } from '../../../framework/types';
+import { AnvilManager } from '../../../seeder/anvil-manager';
describe(SmokeConfirmationsRedesigned('Contract Deployment'), () => {
const testSpecificMock = async (mockServer: Mockttp) => {
@@ -43,12 +48,28 @@ describe(SmokeConfirmationsRedesigned('Contract Deployment'), () => {
dappVariant: DappVariants.TEST_DAPP,
},
],
- fixture: new FixtureBuilder()
- .withGanacheNetwork()
- .withPermissionControllerConnectedToTestDapp(
- buildPermissions(['0x539']),
- )
- .build(),
+ fixture: ({ localNodes }: { localNodes?: LocalNode[] }) => {
+ const node = localNodes?.[0] as unknown as AnvilManager;
+ const rpcPort =
+ node instanceof AnvilManager
+ ? (node.getPort() ?? AnvilPort())
+ : undefined;
+
+ return new FixtureBuilder()
+ .withNetworkController({
+ providerConfig: {
+ chainId: '0x539',
+ rpcUrl: `http://localhost:${rpcPort ?? AnvilPort()}`,
+ type: 'custom',
+ nickname: 'Local RPC',
+ ticker: 'ETH',
+ },
+ })
+ .withPermissionControllerConnectedToTestDapp(
+ buildPermissions(['0x539']),
+ )
+ .build();
+ },
restartDevice: true,
testSpecificMock,
},
diff --git a/e2e/specs/confirmations-redesigned/transactions/contract-interaction.spec.ts b/e2e/specs/confirmations-redesigned/transactions/contract-interaction.spec.ts
index ea829841da9..3c70fd75ba9 100644
--- a/e2e/specs/confirmations-redesigned/transactions/contract-interaction.spec.ts
+++ b/e2e/specs/confirmations-redesigned/transactions/contract-interaction.spec.ts
@@ -7,7 +7,10 @@ import ConfirmationUITypes from '../../../pages/Browser/Confirmations/Confirmati
import FooterActions from '../../../pages/Browser/Confirmations/FooterActions';
import Assertions from '../../../framework/Assertions';
import { withFixtures } from '../../../framework/fixtures/FixtureHelper';
-import { buildPermissions } from '../../../framework/fixtures/FixtureUtils';
+import {
+ buildPermissions,
+ AnvilPort,
+} from '../../../framework/fixtures/FixtureUtils';
import RowComponents from '../../../pages/Browser/Confirmations/RowComponents';
import { SIMULATION_ENABLED_NETWORKS_MOCK } from '../../../api-mocking/mock-responses/simulations';
import TestDApp from '../../../pages/Browser/TestDApp';
@@ -16,6 +19,8 @@ import { Mockttp } from 'mockttp';
import { setupMockRequest } from '../../../api-mocking/helpers/mockHelpers';
import { setupRemoteFeatureFlagsMock } from '../../../api-mocking/helpers/remoteFeatureFlagsHelper';
import { confirmationsRedesignedFeatureFlags } from '../../../api-mocking/mock-responses/feature-flags-mocks';
+import { LocalNode } from '../../../framework/types';
+import { AnvilManager } from '../../../seeder/anvil-manager';
describe(SmokeConfirmationsRedesigned('Contract Interaction'), () => {
const NFT_CONTRACT = SMART_CONTRACTS.NFTS;
@@ -44,12 +49,28 @@ describe(SmokeConfirmationsRedesigned('Contract Interaction'), () => {
dappVariant: DappVariants.TEST_DAPP,
},
],
- fixture: new FixtureBuilder()
- .withGanacheNetwork()
- .withPermissionControllerConnectedToTestDapp(
- buildPermissions(['0x539']),
- )
- .build(),
+ fixture: ({ localNodes }: { localNodes?: LocalNode[] }) => {
+ const node = localNodes?.[0] as unknown as AnvilManager;
+ const rpcPort =
+ node instanceof AnvilManager
+ ? (node.getPort() ?? AnvilPort())
+ : undefined;
+
+ return new FixtureBuilder()
+ .withNetworkController({
+ providerConfig: {
+ chainId: '0x539',
+ rpcUrl: `http://localhost:${rpcPort ?? AnvilPort()}`,
+ type: 'custom',
+ nickname: 'Local RPC',
+ ticker: 'ETH',
+ },
+ })
+ .withPermissionControllerConnectedToTestDapp(
+ buildPermissions(['0x539']),
+ )
+ .build();
+ },
restartDevice: true,
testSpecificMock,
smartContracts: [NFT_CONTRACT],
diff --git a/e2e/specs/confirmations-redesigned/transactions/dapp-initiated-transfer.spec.ts b/e2e/specs/confirmations-redesigned/transactions/dapp-initiated-transfer.spec.ts
index 6a7e24debb0..6400ba89a13 100644
--- a/e2e/specs/confirmations-redesigned/transactions/dapp-initiated-transfer.spec.ts
+++ b/e2e/specs/confirmations-redesigned/transactions/dapp-initiated-transfer.spec.ts
@@ -7,7 +7,10 @@ import ConfirmationUITypes from '../../../pages/Browser/Confirmations/Confirmati
import FooterActions from '../../../pages/Browser/Confirmations/FooterActions';
import Assertions from '../../../framework/Assertions';
import { withFixtures } from '../../../framework/fixtures/FixtureHelper';
-import { buildPermissions } from '../../../framework/fixtures/FixtureUtils';
+import {
+ AnvilPort,
+ buildPermissions,
+} from '../../../framework/fixtures/FixtureUtils';
import RowComponents from '../../../pages/Browser/Confirmations/RowComponents';
import {
SEND_ETH_SIMULATION_MOCK,
@@ -30,6 +33,8 @@ import {
} from '../../../api-mocking/mock-responses/security-alerts-mock';
import { setupRemoteFeatureFlagsMock } from '../../../api-mocking/helpers/remoteFeatureFlagsHelper';
import { confirmationsRedesignedFeatureFlags } from '../../../api-mocking/mock-responses/feature-flags-mocks';
+import { LocalNode } from '../../../framework/types';
+import { AnvilManager } from '../../../seeder/anvil-manager';
const expectedEvents = {
TRANSACTION_ADDED: 'Transaction Added',
@@ -102,16 +107,32 @@ describe.skip(SmokeConfirmationsRedesigned('DApp Initiated Transfer'), () => {
dappVariant: DappVariants.TEST_DAPP,
},
],
- fixture: new FixtureBuilder()
- .withGanacheNetwork()
- .withNetworkEnabledMap({
- eip155: { '0x539': true },
- })
- .withMetaMetricsOptIn()
- .withPermissionControllerConnectedToTestDapp(
- buildPermissions(['0x539']),
- )
- .build(),
+ fixture: ({ localNodes }: { localNodes?: LocalNode[] }) => {
+ const node = localNodes?.[0] as unknown as AnvilManager;
+ const rpcPort =
+ node instanceof AnvilManager
+ ? (node.getPort() ?? AnvilPort())
+ : undefined;
+
+ return new FixtureBuilder()
+ .withNetworkController({
+ providerConfig: {
+ chainId: '0x539',
+ rpcUrl: `http://localhost:${rpcPort ?? AnvilPort()}`,
+ type: 'custom',
+ nickname: 'Local RPC',
+ ticker: 'ETH',
+ },
+ })
+ .withNetworkEnabledMap({
+ eip155: { '0x539': true },
+ })
+ .withMetaMetricsOptIn()
+ .withPermissionControllerConnectedToTestDapp(
+ buildPermissions(['0x539']),
+ )
+ .build();
+ },
restartDevice: true,
testSpecificMock,
endTestfn: async ({ mockServer }) => {
diff --git a/e2e/specs/confirmations-redesigned/transactions/per-dapp-selected-network.spec.ts b/e2e/specs/confirmations-redesigned/transactions/per-dapp-selected-network.spec.ts
index 035b0c3160f..743916fc8da 100644
--- a/e2e/specs/confirmations-redesigned/transactions/per-dapp-selected-network.spec.ts
+++ b/e2e/specs/confirmations-redesigned/transactions/per-dapp-selected-network.spec.ts
@@ -1,6 +1,9 @@
import FixtureBuilder from '../../../framework/fixtures/FixtureBuilder.ts';
import { withFixtures } from '../../../framework/fixtures/FixtureHelper.ts';
-import { buildPermissions } from '../../../framework/fixtures/FixtureUtils.ts';
+import {
+ buildPermissions,
+ AnvilPort,
+} from '../../../framework/fixtures/FixtureUtils.ts';
import Browser from '../../../pages/Browser/BrowserView.ts';
import ConfirmationFooterActions from '../../../pages/Browser/Confirmations/FooterActions.ts';
import ConfirmationUITypes from '../../../pages/Browser/Confirmations/ConfirmationUITypes.ts';
@@ -19,6 +22,8 @@ import { DappVariants } from '../../../framework/Constants.ts';
import { setupRemoteFeatureFlagsMock } from '../../../api-mocking/helpers/remoteFeatureFlagsHelper.ts';
import { confirmationsRedesignedFeatureFlags } from '../../../api-mocking/mock-responses/feature-flags-mocks.ts';
import { Mockttp } from 'mockttp';
+import { LocalNode } from '../../../framework/types';
+import { AnvilManager } from '../../../seeder/anvil-manager';
const LOCAL_CHAIN_ID = '0x539';
const LOCAL_CHAIN_NAME = 'Localhost';
@@ -67,12 +72,28 @@ describe(SmokeConfirmationsRedesigned('Per Dapp Selected Network'), () => {
dappVariant: DappVariants.TEST_DAPP,
},
],
- fixture: new FixtureBuilder()
- .withGanacheNetwork()
- .withPermissionControllerConnectedToTestDapp(
- buildPermissions([LOCAL_CHAIN_ID]),
- )
- .build(),
+ fixture: ({ localNodes }: { localNodes?: LocalNode[] }) => {
+ const node = localNodes?.[0] as unknown as AnvilManager;
+ const rpcPort =
+ node instanceof AnvilManager
+ ? (node.getPort() ?? AnvilPort())
+ : undefined;
+
+ return new FixtureBuilder()
+ .withNetworkController({
+ providerConfig: {
+ chainId: LOCAL_CHAIN_ID,
+ rpcUrl: `http://localhost:${rpcPort ?? AnvilPort()}`,
+ type: 'custom',
+ nickname: LOCAL_CHAIN_NAME,
+ ticker: 'ETH',
+ },
+ })
+ .withPermissionControllerConnectedToTestDapp(
+ buildPermissions([LOCAL_CHAIN_ID]),
+ )
+ .build();
+ },
restartDevice: true,
testSpecificMock,
},
@@ -147,12 +168,28 @@ describe(RegressionConfirmations('Per Dapp Selected Network'), () => {
dappVariant: DappVariants.TEST_DAPP,
},
],
- fixture: new FixtureBuilder()
- .withGanacheNetwork()
- .withPermissionControllerConnectedToTestDapp(
- buildPermissions([LOCAL_CHAIN_ID]),
- )
- .build(),
+ fixture: ({ localNodes }: { localNodes?: LocalNode[] }) => {
+ const node = localNodes?.[0] as unknown as AnvilManager;
+ const rpcPort =
+ node instanceof AnvilManager
+ ? (node.getPort() ?? AnvilPort())
+ : undefined;
+
+ return new FixtureBuilder()
+ .withNetworkController({
+ providerConfig: {
+ chainId: LOCAL_CHAIN_ID,
+ rpcUrl: `http://localhost:${rpcPort ?? AnvilPort()}`,
+ type: 'custom',
+ nickname: LOCAL_CHAIN_NAME,
+ ticker: 'ETH',
+ },
+ })
+ .withPermissionControllerConnectedToTestDapp(
+ buildPermissions([LOCAL_CHAIN_ID]),
+ )
+ .build();
+ },
restartDevice: true,
testSpecificMock,
},
diff --git a/e2e/specs/confirmations-redesigned/transactions/send-max-transfer.spec.ts b/e2e/specs/confirmations-redesigned/transactions/send-max-transfer.spec.ts
index 3058233af37..36526ae87f0 100644
--- a/e2e/specs/confirmations-redesigned/transactions/send-max-transfer.spec.ts
+++ b/e2e/specs/confirmations-redesigned/transactions/send-max-transfer.spec.ts
@@ -1,7 +1,10 @@
import { SmokeConfirmationsRedesigned } from '../../../tags';
import { loginToApp } from '../../../viewHelper';
import { withFixtures } from '../../../framework/fixtures/FixtureHelper';
-import { buildPermissions } from '../../../framework/fixtures/FixtureUtils';
+import {
+ buildPermissions,
+ AnvilPort,
+} from '../../../framework/fixtures/FixtureUtils';
import {
SEND_ETH_SIMULATION_MOCK,
SIMULATION_ENABLED_NETWORKS_MOCK,
@@ -20,6 +23,8 @@ import {
import { Mockttp } from 'mockttp';
import { setupRemoteFeatureFlagsMock } from '../../../api-mocking/helpers/remoteFeatureFlagsHelper';
import { confirmationsRedesignedFeatureFlags } from '../../../api-mocking/mock-responses/feature-flags-mocks';
+import { LocalNode } from '../../../framework/types';
+import { AnvilManager } from '../../../seeder/anvil-manager';
const RECIPIENT = '0x0c54fccd2e384b4bb6f2e405bf5cbc15a017aafb';
@@ -63,15 +68,31 @@ describe(SmokeConfirmationsRedesigned('Send Max Transfer'), () => {
it('handles max native asset', async () => {
await withFixtures(
{
- fixture: new FixtureBuilder()
- .withGanacheNetwork()
- .withNetworkEnabledMap({
- eip155: { '0x539': true },
- })
- .withPermissionControllerConnectedToTestDapp(
- buildPermissions(['0x539']),
- )
- .build(),
+ fixture: ({ localNodes }: { localNodes?: LocalNode[] }) => {
+ const node = localNodes?.[0] as unknown as AnvilManager;
+ const rpcPort =
+ node instanceof AnvilManager
+ ? (node.getPort() ?? AnvilPort())
+ : undefined;
+
+ return new FixtureBuilder()
+ .withNetworkController({
+ providerConfig: {
+ chainId: '0x539',
+ rpcUrl: `http://localhost:${rpcPort ?? AnvilPort()}`,
+ type: 'custom',
+ nickname: 'Local RPC',
+ ticker: 'ETH',
+ },
+ })
+ .withNetworkEnabledMap({
+ eip155: { '0x539': true },
+ })
+ .withPermissionControllerConnectedToTestDapp(
+ buildPermissions(['0x539']),
+ )
+ .build();
+ },
restartDevice: true,
testSpecificMock,
},
diff --git a/e2e/specs/confirmations-redesigned/transactions/token-approve/approve.spec.ts b/e2e/specs/confirmations-redesigned/transactions/token-approve/approve.spec.ts
index b47b66b7360..f31bf18b3b9 100644
--- a/e2e/specs/confirmations-redesigned/transactions/token-approve/approve.spec.ts
+++ b/e2e/specs/confirmations-redesigned/transactions/token-approve/approve.spec.ts
@@ -7,7 +7,10 @@ import ConfirmationUITypes from '../../../../pages/Browser/Confirmations/Confirm
import FooterActions from '../../../../pages/Browser/Confirmations/FooterActions';
import Assertions from '../../../../framework/Assertions';
import { withFixtures } from '../../../../framework/fixtures/FixtureHelper';
-import { buildPermissions } from '../../../../framework/fixtures/FixtureUtils';
+import {
+ buildPermissions,
+ AnvilPort,
+} from '../../../../framework/fixtures/FixtureUtils';
import RowComponents from '../../../../pages/Browser/Confirmations/RowComponents';
import TokenApproveConfirmation from '../../../../pages/Confirmation/TokenApproveConfirmation';
import { SIMULATION_ENABLED_NETWORKS_MOCK } from '../../../../api-mocking/mock-responses/simulations';
@@ -17,6 +20,8 @@ import { setupMockRequest } from '../../../../api-mocking/helpers/mockHelpers';
import { Mockttp } from 'mockttp';
import { setupRemoteFeatureFlagsMock } from '../../../../api-mocking/helpers/remoteFeatureFlagsHelper';
import { confirmationsRedesignedFeatureFlags } from '../../../../api-mocking/mock-responses/feature-flags-mocks';
+import { LocalNode } from '../../../../framework/types';
+import { AnvilManager } from '../../../../seeder/anvil-manager';
describe(SmokeConfirmationsRedesigned('Token Approve - approve method'), () => {
const ERC_20_CONTRACT = SMART_CONTRACTS.HST;
@@ -43,12 +48,28 @@ describe(SmokeConfirmationsRedesigned('Token Approve - approve method'), () => {
dappVariant: DappVariants.TEST_DAPP,
},
],
- fixture: new FixtureBuilder()
- .withGanacheNetwork()
- .withPermissionControllerConnectedToTestDapp(
- buildPermissions(['0x539']),
- )
- .build(),
+ fixture: ({ localNodes }: { localNodes?: LocalNode[] }) => {
+ const node = localNodes?.[0] as unknown as AnvilManager;
+ const rpcPort =
+ node instanceof AnvilManager
+ ? (node.getPort() ?? AnvilPort())
+ : undefined;
+
+ return new FixtureBuilder()
+ .withNetworkController({
+ providerConfig: {
+ chainId: '0x539',
+ rpcUrl: `http://localhost:${rpcPort ?? AnvilPort()}`,
+ type: 'custom',
+ nickname: 'Local RPC',
+ ticker: 'ETH',
+ },
+ })
+ .withPermissionControllerConnectedToTestDapp(
+ buildPermissions(['0x539']),
+ )
+ .build();
+ },
restartDevice: true,
testSpecificMock,
smartContracts: [ERC_20_CONTRACT],
@@ -116,12 +137,28 @@ describe(SmokeConfirmationsRedesigned('Token Approve - approve method'), () => {
dappVariant: DappVariants.TEST_DAPP,
},
],
- fixture: new FixtureBuilder()
- .withGanacheNetwork()
- .withPermissionControllerConnectedToTestDapp(
- buildPermissions(['0x539']),
- )
- .build(),
+ fixture: ({ localNodes }: { localNodes?: LocalNode[] }) => {
+ const node = localNodes?.[0] as unknown as AnvilManager;
+ const rpcPort =
+ node instanceof AnvilManager
+ ? (node.getPort() ?? AnvilPort())
+ : undefined;
+
+ return new FixtureBuilder()
+ .withNetworkController({
+ providerConfig: {
+ chainId: '0x539',
+ rpcUrl: `http://localhost:${rpcPort ?? AnvilPort()}`,
+ type: 'custom',
+ nickname: 'Local RPC',
+ ticker: 'ETH',
+ },
+ })
+ .withPermissionControllerConnectedToTestDapp(
+ buildPermissions(['0x539']),
+ )
+ .build();
+ },
restartDevice: true,
testSpecificMock,
smartContracts: [ERC_721_CONTRACT],
diff --git a/e2e/specs/confirmations-redesigned/transactions/token-approve/increase-allowance.spec.ts b/e2e/specs/confirmations-redesigned/transactions/token-approve/increase-allowance.spec.ts
index 393237a3898..0dcbb29c1ff 100644
--- a/e2e/specs/confirmations-redesigned/transactions/token-approve/increase-allowance.spec.ts
+++ b/e2e/specs/confirmations-redesigned/transactions/token-approve/increase-allowance.spec.ts
@@ -7,7 +7,10 @@ import ConfirmationUITypes from '../../../../pages/Browser/Confirmations/Confirm
import FooterActions from '../../../../pages/Browser/Confirmations/FooterActions';
import Assertions from '../../../../framework/Assertions';
import { withFixtures } from '../../../../framework/fixtures/FixtureHelper';
-import { buildPermissions } from '../../../../framework/fixtures/FixtureUtils';
+import {
+ AnvilPort,
+ buildPermissions,
+} from '../../../../framework/fixtures/FixtureUtils';
import RowComponents from '../../../../pages/Browser/Confirmations/RowComponents';
import TokenApproveConfirmation from '../../../../pages/Confirmation/TokenApproveConfirmation';
import { SIMULATION_ENABLED_NETWORKS_MOCK } from '../../../../api-mocking/mock-responses/simulations';
@@ -17,6 +20,8 @@ import { Mockttp } from 'mockttp';
import { setupMockRequest } from '../../../../api-mocking/helpers/mockHelpers';
import { confirmationsRedesignedFeatureFlags } from '../../../../api-mocking/mock-responses/feature-flags-mocks';
import { setupRemoteFeatureFlagsMock } from '../../../../api-mocking/helpers/remoteFeatureFlagsHelper';
+import { LocalNode } from '../../../../framework/types';
+import { AnvilManager } from '../../../../seeder/anvil-manager';
describe(
SmokeConfirmationsRedesigned('Token Approve - increaseAllowance method'),
@@ -44,12 +49,28 @@ describe(
dappVariant: DappVariants.TEST_DAPP,
},
],
- fixture: new FixtureBuilder()
- .withGanacheNetwork()
- .withPermissionControllerConnectedToTestDapp(
- buildPermissions(['0x539']),
- )
- .build(),
+ fixture: ({ localNodes }: { localNodes?: LocalNode[] }) => {
+ const node = localNodes?.[0] as unknown as AnvilManager;
+ const rpcPort =
+ node instanceof AnvilManager
+ ? (node.getPort() ?? AnvilPort())
+ : undefined;
+
+ return new FixtureBuilder()
+ .withNetworkController({
+ providerConfig: {
+ chainId: '0x539',
+ rpcUrl: `http://localhost:${rpcPort ?? AnvilPort()}`,
+ type: 'custom',
+ nickname: 'Local RPC',
+ ticker: 'ETH',
+ },
+ })
+ .withPermissionControllerConnectedToTestDapp(
+ buildPermissions(['0x539']),
+ )
+ .build();
+ },
restartDevice: true,
testSpecificMock,
smartContracts: [ERC_20_CONTRACT],
diff --git a/e2e/specs/confirmations-redesigned/transactions/token-approve/set-approval-for-all.spec.ts b/e2e/specs/confirmations-redesigned/transactions/token-approve/set-approval-for-all.spec.ts
index 298a02d8df6..686bf9e519c 100644
--- a/e2e/specs/confirmations-redesigned/transactions/token-approve/set-approval-for-all.spec.ts
+++ b/e2e/specs/confirmations-redesigned/transactions/token-approve/set-approval-for-all.spec.ts
@@ -7,7 +7,10 @@ import ConfirmationUITypes from '../../../../pages/Browser/Confirmations/Confirm
import FooterActions from '../../../../pages/Browser/Confirmations/FooterActions';
import Assertions from '../../../../framework/Assertions';
import { withFixtures } from '../../../../framework/fixtures/FixtureHelper';
-import { buildPermissions } from '../../../../framework/fixtures/FixtureUtils';
+import {
+ AnvilPort,
+ buildPermissions,
+} from '../../../../framework/fixtures/FixtureUtils';
import RowComponents from '../../../../pages/Browser/Confirmations/RowComponents';
import TokenApproveConfirmation from '../../../../pages/Confirmation/TokenApproveConfirmation';
import { SIMULATION_ENABLED_NETWORKS_MOCK } from '../../../../api-mocking/mock-responses/simulations';
@@ -17,6 +20,8 @@ import { setupMockRequest } from '../../../../api-mocking/helpers/mockHelpers';
import { Mockttp } from 'mockttp';
import { setupRemoteFeatureFlagsMock } from '../../../../api-mocking/helpers/remoteFeatureFlagsHelper';
import { confirmationsRedesignedFeatureFlags } from '../../../../api-mocking/mock-responses/feature-flags-mocks';
+import { LocalNode } from '../../../../framework/types';
+import { AnvilManager } from '../../../../seeder/anvil-manager';
describe(
SmokeConfirmationsRedesigned('Token Approve - setApprovalForAll method'),
@@ -45,12 +50,28 @@ describe(
dappVariant: DappVariants.TEST_DAPP,
},
],
- fixture: new FixtureBuilder()
- .withGanacheNetwork()
- .withPermissionControllerConnectedToTestDapp(
- buildPermissions(['0x539']),
- )
- .build(),
+ fixture: ({ localNodes }: { localNodes?: LocalNode[] }) => {
+ const node = localNodes?.[0] as unknown as AnvilManager;
+ const rpcPort =
+ node instanceof AnvilManager
+ ? (node.getPort() ?? AnvilPort())
+ : undefined;
+
+ return new FixtureBuilder()
+ .withNetworkController({
+ providerConfig: {
+ chainId: '0x539',
+ rpcUrl: `http://localhost:${rpcPort ?? AnvilPort()}`,
+ type: 'custom',
+ nickname: 'Local RPC',
+ ticker: 'ETH',
+ },
+ })
+ .withPermissionControllerConnectedToTestDapp(
+ buildPermissions(['0x539']),
+ )
+ .build();
+ },
restartDevice: true,
testSpecificMock,
smartContracts: [ERC_721_CONTRACT],
@@ -76,22 +97,39 @@ describe(
// Check all expected row components are visible
await Assertions.expectElementToBeVisible(
RowComponents.AccountNetwork,
+ {
+ description: 'Account Network',
+ },
);
- await Assertions.expectElementToBeVisible(RowComponents.ApproveRow);
+ await Assertions.expectElementToBeVisible(RowComponents.ApproveRow, {
+ description: 'Approve Row',
+ });
await Assertions.expectElementToBeVisible(
RowComponents.NetworkAndOrigin,
+ {
+ description: 'Network And Origin',
+ },
);
await Assertions.expectElementToBeVisible(
RowComponents.GasFeesDetails,
+ {
+ description: 'Gas Fees Details',
+ },
);
await Assertions.expectElementToBeVisible(
RowComponents.AdvancedDetails,
+ {
+ description: 'Advanced Details',
+ },
);
// Check spending cap is visible and has the correct value
await Assertions.expectElementToHaveText(
TokenApproveConfirmation.SpendingCapValue,
'All',
+ {
+ description: 'Spending Cap Value',
+ },
);
// Accept confirmation
@@ -113,12 +151,28 @@ describe(
dappVariant: DappVariants.TEST_DAPP,
},
],
- fixture: new FixtureBuilder()
- .withGanacheNetwork()
- .withPermissionControllerConnectedToTestDapp(
- buildPermissions(['0x539']),
- )
- .build(),
+ fixture: ({ localNodes }: { localNodes?: LocalNode[] }) => {
+ const node = localNodes?.[0] as unknown as AnvilManager;
+ const rpcPort =
+ node instanceof AnvilManager
+ ? (node.getPort() ?? AnvilPort())
+ : undefined;
+
+ return new FixtureBuilder()
+ .withNetworkController({
+ providerConfig: {
+ chainId: '0x539',
+ rpcUrl: `http://localhost:${rpcPort ?? AnvilPort()}`,
+ type: 'custom',
+ nickname: 'Local RPC',
+ ticker: 'ETH',
+ },
+ })
+ .withPermissionControllerConnectedToTestDapp(
+ buildPermissions(['0x539']),
+ )
+ .build();
+ },
restartDevice: true,
testSpecificMock,
smartContracts: [ERC_1155_CONTRACT],
@@ -167,12 +221,28 @@ describe(
dappVariant: DappVariants.TEST_DAPP,
},
],
- fixture: new FixtureBuilder()
- .withGanacheNetwork()
- .withPermissionControllerConnectedToTestDapp(
- buildPermissions(['0x539']),
- )
- .build(),
+ fixture: ({ localNodes }: { localNodes?: LocalNode[] }) => {
+ const node = localNodes?.[0] as unknown as AnvilManager;
+ const rpcPort =
+ node instanceof AnvilManager
+ ? (node.getPort() ?? AnvilPort())
+ : undefined;
+
+ return new FixtureBuilder()
+ .withNetworkController({
+ providerConfig: {
+ chainId: '0x539',
+ rpcUrl: `http://localhost:${rpcPort ?? AnvilPort()}`,
+ type: 'custom',
+ nickname: 'Local RPC',
+ ticker: 'ETH',
+ },
+ })
+ .withPermissionControllerConnectedToTestDapp(
+ buildPermissions(['0x539']),
+ )
+ .build();
+ },
restartDevice: true,
testSpecificMock,
smartContracts: [ERC_721_CONTRACT],
diff --git a/e2e/specs/confirmations-redesigned/transactions/wallet-initiated-transfer.spec.ts b/e2e/specs/confirmations-redesigned/transactions/wallet-initiated-transfer.spec.ts
index e82e74c9425..e26d54d472c 100644
--- a/e2e/specs/confirmations-redesigned/transactions/wallet-initiated-transfer.spec.ts
+++ b/e2e/specs/confirmations-redesigned/transactions/wallet-initiated-transfer.spec.ts
@@ -1,7 +1,10 @@
import { SmokeConfirmationsRedesigned } from '../../../tags';
import { loginToApp } from '../../../viewHelper';
import { withFixtures } from '../../../framework/fixtures/FixtureHelper';
-import { buildPermissions } from '../../../framework/fixtures/FixtureUtils';
+import {
+ AnvilPort,
+ buildPermissions,
+} from '../../../framework/fixtures/FixtureUtils';
import {
SEND_ETH_SIMULATION_MOCK,
SIMULATION_ENABLED_NETWORKS_MOCK,
@@ -22,6 +25,8 @@ import {
} from '../../../api-mocking/helpers/mockHelpers';
import { setupRemoteFeatureFlagsMock } from '../../../api-mocking/helpers/remoteFeatureFlagsHelper';
import { confirmationsRedesignedFeatureFlags } from '../../../api-mocking/mock-responses/feature-flags-mocks';
+import { LocalNode } from '../../../framework/types';
+import { AnvilManager } from '../../../seeder/anvil-manager';
const RECIPIENT = '0x0c54fccd2e384b4bb6f2e405bf5cbc15a017aafb';
const AMOUNT = '1';
@@ -65,15 +70,31 @@ describe(SmokeConfirmationsRedesigned('Wallet Initiated Transfer'), () => {
it('sends native asset', async () => {
await withFixtures(
{
- fixture: new FixtureBuilder()
- .withGanacheNetwork()
- .withNetworkEnabledMap({
- eip155: { '0x539': true },
- })
- .withPermissionControllerConnectedToTestDapp(
- buildPermissions(['0x539']),
- )
- .build(),
+ fixture: ({ localNodes }: { localNodes?: LocalNode[] }) => {
+ const node = localNodes?.[0] as unknown as AnvilManager;
+ const rpcPort =
+ node instanceof AnvilManager
+ ? (node.getPort() ?? AnvilPort())
+ : undefined;
+
+ return new FixtureBuilder()
+ .withNetworkController({
+ providerConfig: {
+ chainId: '0x539',
+ rpcUrl: `http://localhost:${rpcPort ?? AnvilPort()}`,
+ type: 'custom',
+ nickname: 'Local RPC',
+ ticker: 'ETH',
+ },
+ })
+ .withNetworkEnabledMap({
+ eip155: { '0x539': true },
+ })
+ .withPermissionControllerConnectedToTestDapp(
+ buildPermissions(['0x539']),
+ )
+ .build();
+ },
restartDevice: true,
testSpecificMock,
},
diff --git a/e2e/specs/confirmations/advanced-gas-fees.mock.spec.ts b/e2e/specs/confirmations/advanced-gas-fees.mock.spec.ts
index efca3bbc4f7..79e4a5ae4be 100644
--- a/e2e/specs/confirmations/advanced-gas-fees.mock.spec.ts
+++ b/e2e/specs/confirmations/advanced-gas-fees.mock.spec.ts
@@ -11,6 +11,9 @@ import Assertions from '../../framework/Assertions';
import { Mockttp } from 'mockttp';
import { oldConfirmationsRemoteFeatureFlags } from '../../api-mocking/mock-responses/feature-flags-mocks';
import { setupRemoteFeatureFlagsMock } from '../../api-mocking/helpers/remoteFeatureFlagsHelper';
+import { AnvilPort } from '../../framework/fixtures/FixtureUtils';
+import { LocalNode } from '../../framework/types';
+import { AnvilManager } from '../../seeder/anvil-manager';
const VALID_ADDRESS = '0xebe6CcB6B55e1d094d9c58980Bc10Fed69932cAb';
const testSpecificMock = async (mockServer: Mockttp) => {
@@ -26,7 +29,25 @@ describe(
it('should edit priority gas settings and send ETH', async () => {
await withFixtures(
{
- fixture: new FixtureBuilder().withGanacheNetwork().build(),
+ fixture: ({ localNodes }: { localNodes?: LocalNode[] }) => {
+ const node = localNodes?.[0] as unknown as AnvilManager;
+ const rpcPort =
+ node instanceof AnvilManager
+ ? (node.getPort() ?? AnvilPort())
+ : undefined;
+
+ return new FixtureBuilder()
+ .withNetworkController({
+ providerConfig: {
+ chainId: '0x539',
+ rpcUrl: `http://localhost:${rpcPort ?? AnvilPort()}`,
+ type: 'custom',
+ nickname: 'Local RPC',
+ ticker: 'ETH',
+ },
+ })
+ .build();
+ },
restartDevice: true,
testSpecificMock,
},
diff --git a/e2e/specs/confirmations/approve-custom-erc20.spec.ts b/e2e/specs/confirmations/approve-custom-erc20.spec.ts
index eb4abbbec1e..23631c28425 100644
--- a/e2e/specs/confirmations/approve-custom-erc20.spec.ts
+++ b/e2e/specs/confirmations/approve-custom-erc20.spec.ts
@@ -9,11 +9,16 @@ import { SMART_CONTRACTS } from '../../../app/util/test/smart-contracts';
import ContractApprovalBottomSheet from '../../pages/Browser/ContractApprovalBottomSheet';
import Assertions from '../../framework/Assertions';
import { ActivitiesViewSelectorsText } from '../../selectors/Transactions/ActivitiesView.selectors';
-import { buildPermissions } from '../../framework/fixtures/FixtureUtils';
+import {
+ buildPermissions,
+ AnvilPort,
+} from '../../framework/fixtures/FixtureUtils';
import { DappVariants } from '../../framework/Constants';
import { Mockttp } from 'mockttp';
import { setupRemoteFeatureFlagsMock } from '../../api-mocking/helpers/remoteFeatureFlagsHelper';
import { oldConfirmationsRemoteFeatureFlags } from '../../api-mocking/mock-responses/feature-flags-mocks';
+import { LocalNode } from '../../framework/types';
+import { AnvilManager } from '../../seeder/anvil-manager';
const HST_CONTRACT = SMART_CONTRACTS.HST;
@@ -33,12 +38,28 @@ describe(RegressionConfirmations('ERC20 tokens'), () => {
dappVariant: DappVariants.TEST_DAPP,
},
],
- fixture: new FixtureBuilder()
- .withGanacheNetwork()
- .withPermissionControllerConnectedToTestDapp(
- buildPermissions(['0x539']),
- )
- .build(),
+ fixture: ({ localNodes }: { localNodes?: LocalNode[] }) => {
+ const node = localNodes?.[0] as unknown as AnvilManager;
+ const rpcPort =
+ node instanceof AnvilManager
+ ? (node.getPort() ?? AnvilPort())
+ : undefined;
+
+ return new FixtureBuilder()
+ .withNetworkController({
+ providerConfig: {
+ chainId: '0x539',
+ rpcUrl: `http://localhost:${rpcPort ?? AnvilPort()}`,
+ type: 'custom',
+ nickname: 'Local RPC',
+ ticker: 'ETH',
+ },
+ })
+ .withPermissionControllerConnectedToTestDapp(
+ buildPermissions(['0x539']),
+ )
+ .build();
+ },
restartDevice: true,
smartContracts: [HST_CONTRACT],
testSpecificMock,
diff --git a/e2e/specs/confirmations/approve-default-erc20.spec.ts b/e2e/specs/confirmations/approve-default-erc20.spec.ts
index b142df45986..2c53848a175 100644
--- a/e2e/specs/confirmations/approve-default-erc20.spec.ts
+++ b/e2e/specs/confirmations/approve-default-erc20.spec.ts
@@ -11,11 +11,16 @@ import ContractApprovalBottomSheet from '../../pages/Browser/ContractApprovalBot
import Assertions from '../../framework/Assertions';
import TabBarComponent from '../../pages/wallet/TabBarComponent';
import TestDApp from '../../pages/Browser/TestDApp';
-import { buildPermissions } from '../../framework/fixtures/FixtureUtils';
+import {
+ buildPermissions,
+ AnvilPort,
+} from '../../framework/fixtures/FixtureUtils';
import { DappVariants } from '../../framework/Constants';
import { Mockttp } from 'mockttp';
import { setupRemoteFeatureFlagsMock } from '../../api-mocking/helpers/remoteFeatureFlagsHelper';
import { oldConfirmationsRemoteFeatureFlags } from '../../api-mocking/mock-responses/feature-flags-mocks';
+import { LocalNode } from '../../framework/types';
+import { AnvilManager } from '../../seeder/anvil-manager';
const HST_CONTRACT = SMART_CONTRACTS.HST;
const EXPECTED_TOKEN_AMOUNT = '7';
@@ -31,12 +36,28 @@ describe(RegressionConfirmations('ERC20 tokens'), () => {
await withFixtures(
{
- fixture: new FixtureBuilder()
- .withGanacheNetwork()
- .withPermissionControllerConnectedToTestDapp(
- buildPermissions(['0x539']),
- )
- .build(),
+ fixture: ({ localNodes }: { localNodes?: LocalNode[] }) => {
+ const node = localNodes?.[0] as unknown as AnvilManager;
+ const rpcPort =
+ node instanceof AnvilManager
+ ? (node.getPort() ?? AnvilPort())
+ : undefined;
+
+ return new FixtureBuilder()
+ .withNetworkController({
+ providerConfig: {
+ chainId: '0x539',
+ rpcUrl: `http://localhost:${rpcPort ?? AnvilPort()}`,
+ type: 'custom',
+ nickname: 'Local RPC',
+ ticker: 'ETH',
+ },
+ })
+ .withPermissionControllerConnectedToTestDapp(
+ buildPermissions(['0x539']),
+ )
+ .build();
+ },
dapps: [
{
dappVariant: DappVariants.TEST_DAPP,
diff --git a/e2e/specs/confirmations/approve-erc721.spec.ts b/e2e/specs/confirmations/approve-erc721.spec.ts
index c32d6787462..8c77aea22db 100644
--- a/e2e/specs/confirmations/approve-erc721.spec.ts
+++ b/e2e/specs/confirmations/approve-erc721.spec.ts
@@ -8,10 +8,15 @@ import { DappVariants } from '../../framework/Constants';
import { SMART_CONTRACTS } from '../../../app/util/test/smart-contracts';
import { ActivitiesViewSelectorsText } from '../../selectors/Transactions/ActivitiesView.selectors';
import Assertions from '../../framework/Assertions';
-import { buildPermissions } from '../../framework/fixtures/FixtureUtils';
+import {
+ buildPermissions,
+ AnvilPort,
+} from '../../framework/fixtures/FixtureUtils';
import { Mockttp } from 'mockttp';
import { setupRemoteFeatureFlagsMock } from '../../api-mocking/helpers/remoteFeatureFlagsHelper';
import { oldConfirmationsRemoteFeatureFlags } from '../../api-mocking/mock-responses/feature-flags-mocks';
+import { LocalNode } from '../../framework/types';
+import { AnvilManager } from '../../seeder/anvil-manager';
describe(RegressionConfirmations('ERC721 tokens'), () => {
const NFT_CONTRACT = SMART_CONTRACTS.NFTS;
@@ -31,12 +36,28 @@ describe(RegressionConfirmations('ERC721 tokens'), () => {
dappVariant: DappVariants.TEST_DAPP,
},
],
- fixture: new FixtureBuilder()
- .withGanacheNetwork()
- .withPermissionControllerConnectedToTestDapp(
- buildPermissions(['0x539']),
- )
- .build(),
+ fixture: ({ localNodes }: { localNodes?: LocalNode[] }) => {
+ const node = localNodes?.[0] as unknown as AnvilManager;
+ const rpcPort =
+ node instanceof AnvilManager
+ ? (node.getPort() ?? AnvilPort())
+ : undefined;
+
+ return new FixtureBuilder()
+ .withNetworkController({
+ providerConfig: {
+ chainId: '0x539',
+ rpcUrl: `http://localhost:${rpcPort ?? AnvilPort()}`,
+ type: 'custom',
+ nickname: 'Local RPC',
+ ticker: 'ETH',
+ },
+ })
+ .withPermissionControllerConnectedToTestDapp(
+ buildPermissions(['0x539']),
+ )
+ .build();
+ },
restartDevice: true,
smartContracts: [NFT_CONTRACT],
testSpecificMock,
diff --git a/e2e/specs/confirmations/batch-transfer-erc1155.spec.ts b/e2e/specs/confirmations/batch-transfer-erc1155.spec.ts
index 8362fd3e78e..b177fc11b5b 100644
--- a/e2e/specs/confirmations/batch-transfer-erc1155.spec.ts
+++ b/e2e/specs/confirmations/batch-transfer-erc1155.spec.ts
@@ -10,12 +10,17 @@ import Assertions from '../../framework/Assertions';
import { ContractApprovalBottomSheetSelectorsText } from '../../selectors/Browser/ContractApprovalBottomSheet.selectors';
import ContractApprovalBottomSheet from '../../pages/Browser/ContractApprovalBottomSheet';
import { DappVariants } from '../../framework/Constants';
-import { buildPermissions } from '../../framework/fixtures/FixtureUtils';
+import {
+ buildPermissions,
+ AnvilPort,
+} from '../../framework/fixtures/FixtureUtils';
import { Mockttp } from 'mockttp';
import { setupRemoteFeatureFlagsMock } from '../../api-mocking/helpers/remoteFeatureFlagsHelper';
import { oldConfirmationsRemoteFeatureFlags } from '../../api-mocking/mock-responses/feature-flags-mocks';
import WalletView from '../../pages/wallet/WalletView';
import NetworkListModal from '../../pages/Network/NetworkListModal';
+import { LocalNode } from '../../framework/types';
+import { AnvilManager } from '../../seeder/anvil-manager';
describe(RegressionConfirmations('ERC1155 token'), () => {
const ERC1155_CONTRACT = SMART_CONTRACTS.ERC1155;
@@ -35,12 +40,28 @@ describe(RegressionConfirmations('ERC1155 token'), () => {
dappVariant: DappVariants.TEST_DAPP,
},
],
- fixture: new FixtureBuilder()
- .withGanacheNetwork()
- .withPermissionControllerConnectedToTestDapp(
- buildPermissions(['0x539']),
- )
- .build(),
+ fixture: ({ localNodes }: { localNodes?: LocalNode[] }) => {
+ const node = localNodes?.[0] as unknown as AnvilManager;
+ const rpcPort =
+ node instanceof AnvilManager
+ ? (node.getPort() ?? AnvilPort())
+ : undefined;
+
+ return new FixtureBuilder()
+ .withNetworkController({
+ providerConfig: {
+ chainId: '0x539',
+ rpcUrl: `http://localhost:${rpcPort ?? AnvilPort()}`,
+ type: 'custom',
+ nickname: 'Local RPC',
+ ticker: 'ETH',
+ },
+ })
+ .withPermissionControllerConnectedToTestDapp(
+ buildPermissions(['0x539']),
+ )
+ .build();
+ },
restartDevice: true,
smartContracts: [ERC1155_CONTRACT],
testSpecificMock,
diff --git a/e2e/specs/confirmations/increase-allowance-erc20.spec.ts b/e2e/specs/confirmations/increase-allowance-erc20.spec.ts
index df4d6f26eb5..3f152c1257a 100644
--- a/e2e/specs/confirmations/increase-allowance-erc20.spec.ts
+++ b/e2e/specs/confirmations/increase-allowance-erc20.spec.ts
@@ -8,13 +8,18 @@ import { SMART_CONTRACTS } from '../../../app/util/test/smart-contracts';
import ContractApprovalBottomSheet from '../../pages/Browser/ContractApprovalBottomSheet';
import Assertions from '../../framework/Assertions';
import { ActivitiesViewSelectorsText } from '../../selectors/Transactions/ActivitiesView.selectors';
-import { buildPermissions } from '../../framework/fixtures/FixtureUtils';
+import {
+ buildPermissions,
+ AnvilPort,
+} from '../../framework/fixtures/FixtureUtils';
import { DappVariants } from '../../framework/Constants';
import { Mockttp } from 'mockttp';
import { setupRemoteFeatureFlagsMock } from '../../api-mocking/helpers/remoteFeatureFlagsHelper';
import { oldConfirmationsRemoteFeatureFlags } from '../../api-mocking/mock-responses/feature-flags-mocks';
import NetworkListModal from '../../pages/Network/NetworkListModal';
import WalletView from '../../pages/wallet/WalletView';
+import { LocalNode } from '../../framework/types';
+import { AnvilManager } from '../../seeder/anvil-manager';
const HST_CONTRACT = SMART_CONTRACTS.HST;
@@ -34,12 +39,28 @@ describe(RegressionConfirmations('ERC20 - Increase Allowance'), () => {
dappVariant: DappVariants.TEST_DAPP,
},
],
- fixture: new FixtureBuilder()
- .withGanacheNetwork()
- .withPermissionControllerConnectedToTestDapp(
- buildPermissions(['0x539']),
- )
- .build(),
+ fixture: ({ localNodes }: { localNodes?: LocalNode[] }) => {
+ const node = localNodes?.[0] as unknown as AnvilManager;
+ const rpcPort =
+ node instanceof AnvilManager
+ ? (node.getPort() ?? AnvilPort())
+ : undefined;
+
+ return new FixtureBuilder()
+ .withNetworkController({
+ providerConfig: {
+ chainId: '0x539',
+ rpcUrl: `http://localhost:${rpcPort ?? AnvilPort()}`,
+ type: 'custom',
+ nickname: 'Local RPC',
+ ticker: 'ETH',
+ },
+ })
+ .withPermissionControllerConnectedToTestDapp(
+ buildPermissions(['0x539']),
+ )
+ .build();
+ },
restartDevice: true,
smartContracts: [HST_CONTRACT],
testSpecificMock,
diff --git a/e2e/specs/confirmations/send-erc20-with-dapp.spec.ts b/e2e/specs/confirmations/send-erc20-with-dapp.spec.ts
index ca8627c1f61..1e5899b1ddc 100644
--- a/e2e/specs/confirmations/send-erc20-with-dapp.spec.ts
+++ b/e2e/specs/confirmations/send-erc20-with-dapp.spec.ts
@@ -11,11 +11,16 @@ import { ActivitiesViewSelectorsText } from '../../selectors/Transactions/Activi
import TabBarComponent from '../../pages/wallet/TabBarComponent';
import TestDApp from '../../pages/Browser/TestDApp';
import Assertions from '../../framework/Assertions';
-import { buildPermissions } from '../../framework/fixtures/FixtureUtils';
+import {
+ buildPermissions,
+ AnvilPort,
+} from '../../framework/fixtures/FixtureUtils';
import { DappVariants } from '../../framework/Constants';
import { Mockttp } from 'mockttp';
import { setupRemoteFeatureFlagsMock } from '../../api-mocking/helpers/remoteFeatureFlagsHelper';
import { oldConfirmationsRemoteFeatureFlags } from '../../api-mocking/mock-responses/feature-flags-mocks';
+import { LocalNode } from '../../framework/types';
+import { AnvilManager } from '../../seeder/anvil-manager';
const HST_CONTRACT = SMART_CONTRACTS.HST;
@@ -35,15 +40,29 @@ describe(RegressionConfirmations('ERC20 tokens'), () => {
dappVariant: DappVariants.TEST_DAPP,
},
],
- fixture: new FixtureBuilder()
- .withGanacheNetwork()
- .withNetworkEnabledMap({
- eip155: { '0x539': true },
- })
- .withPermissionControllerConnectedToTestDapp(
- buildPermissions(['0x539']),
- )
- .build(),
+ fixture: ({ localNodes }: { localNodes?: LocalNode[] }) => {
+ const node = localNodes?.[0] as unknown as AnvilManager;
+ const rpcPort =
+ node instanceof AnvilManager
+ ? (node.getPort() ?? AnvilPort())
+ : undefined;
+
+ return new FixtureBuilder()
+ .withNetworkController({
+ providerConfig: {
+ chainId: '0x539',
+ rpcUrl: `http://localhost:${rpcPort ?? AnvilPort()}`,
+ type: 'custom',
+ nickname: 'Local RPC',
+ ticker: 'ETH',
+ },
+ })
+ .withNetworkEnabledMap({ eip155: { '0x539': true } })
+ .withPermissionControllerConnectedToTestDapp(
+ buildPermissions(['0x539']),
+ )
+ .build();
+ },
restartDevice: true,
smartContracts: [HST_CONTRACT],
testSpecificMock,
diff --git a/e2e/specs/confirmations/send-erc721.spec.ts b/e2e/specs/confirmations/send-erc721.spec.ts
index 45b69d9808b..6f748937c9e 100644
--- a/e2e/specs/confirmations/send-erc721.spec.ts
+++ b/e2e/specs/confirmations/send-erc721.spec.ts
@@ -7,13 +7,18 @@ import { withFixtures } from '../../framework/fixtures/FixtureHelper';
import { SMART_CONTRACTS } from '../../../app/util/test/smart-contracts';
import { ActivitiesViewSelectorsText } from '../../selectors/Transactions/ActivitiesView.selectors';
import Assertions from '../../framework/Assertions';
-import { buildPermissions } from '../../framework/fixtures/FixtureUtils';
+import {
+ AnvilPort,
+ buildPermissions,
+} from '../../framework/fixtures/FixtureUtils';
import { DappVariants } from '../../framework/Constants';
import { Mockttp } from 'mockttp';
import { setupRemoteFeatureFlagsMock } from '../../api-mocking/helpers/remoteFeatureFlagsHelper';
import { oldConfirmationsRemoteFeatureFlags } from '../../api-mocking/mock-responses/feature-flags-mocks';
import NetworkListModal from '../../pages/Network/NetworkListModal';
import WalletView from '../../pages/wallet/WalletView';
+import { LocalNode } from '../../framework/types';
+import { AnvilManager } from '../../seeder/anvil-manager';
describe(RegressionConfirmations('ERC721 tokens'), () => {
const NFT_CONTRACT = SMART_CONTRACTS.NFTS;
@@ -33,12 +38,28 @@ describe(RegressionConfirmations('ERC721 tokens'), () => {
dappVariant: DappVariants.TEST_DAPP,
},
],
- fixture: new FixtureBuilder()
- .withGanacheNetwork()
- .withPermissionControllerConnectedToTestDapp(
- buildPermissions(['0x539']),
- )
- .build(),
+ fixture: ({ localNodes }: { localNodes?: LocalNode[] }) => {
+ const node = localNodes?.[0] as unknown as AnvilManager;
+ const rpcPort =
+ node instanceof AnvilManager
+ ? (node.getPort() ?? AnvilPort())
+ : undefined;
+
+ return new FixtureBuilder()
+ .withNetworkController({
+ providerConfig: {
+ chainId: '0x539',
+ rpcUrl: `http://localhost:${rpcPort ?? AnvilPort()}`,
+ type: 'custom',
+ nickname: 'Local RPC',
+ ticker: 'ETH',
+ },
+ })
+ .withPermissionControllerConnectedToTestDapp(
+ buildPermissions(['0x539']),
+ )
+ .build();
+ },
restartDevice: true,
smartContracts: [NFT_CONTRACT],
testSpecificMock,
diff --git a/e2e/specs/confirmations/send-failing-contract.spec.ts b/e2e/specs/confirmations/send-failing-contract.spec.ts
index 9bfb6fbabe1..6cb29d313ef 100644
--- a/e2e/specs/confirmations/send-failing-contract.spec.ts
+++ b/e2e/specs/confirmations/send-failing-contract.spec.ts
@@ -7,11 +7,16 @@ import { withFixtures } from '../../framework/fixtures/FixtureHelper';
import { SMART_CONTRACTS } from '../../../app/util/test/smart-contracts';
import { ActivitiesViewSelectorsText } from '../../selectors/Transactions/ActivitiesView.selectors';
import Assertions from '../../framework/Assertions';
-import { buildPermissions } from '../../framework/fixtures/FixtureUtils';
+import {
+ buildPermissions,
+ AnvilPort,
+} from '../../framework/fixtures/FixtureUtils';
import { DappVariants } from '../../framework/Constants';
import { Mockttp } from 'mockttp';
import { setupRemoteFeatureFlagsMock } from '../../api-mocking/helpers/remoteFeatureFlagsHelper';
import { oldConfirmationsRemoteFeatureFlags } from '../../api-mocking/mock-responses/feature-flags-mocks';
+import { LocalNode } from '../../framework/types';
+import { AnvilManager } from '../../seeder/anvil-manager';
describe.skip(RegressionConfirmations('Failing contracts'), () => {
const FAILING_CONTRACT = SMART_CONTRACTS.FAILING;
@@ -31,12 +36,28 @@ describe.skip(RegressionConfirmations('Failing contracts'), () => {
dappVariant: DappVariants.TEST_DAPP,
},
],
- fixture: new FixtureBuilder()
- .withGanacheNetwork()
- .withPermissionControllerConnectedToTestDapp(
- buildPermissions(['0x539']),
- )
- .build(),
+ fixture: ({ localNodes }: { localNodes?: LocalNode[] }) => {
+ const node = localNodes?.[0] as unknown as AnvilManager;
+ const rpcPort =
+ node instanceof AnvilManager
+ ? (node.getPort() ?? AnvilPort())
+ : undefined;
+
+ return new FixtureBuilder()
+ .withNetworkController({
+ providerConfig: {
+ chainId: '0x539',
+ rpcUrl: `http://localhost:${rpcPort ?? AnvilPort()}`,
+ type: 'custom',
+ nickname: 'Local RPC',
+ ticker: 'ETH',
+ },
+ })
+ .withPermissionControllerConnectedToTestDapp(
+ buildPermissions(['0x539']),
+ )
+ .build();
+ },
restartDevice: true,
smartContracts: [FAILING_CONTRACT],
testSpecificMock,
diff --git a/e2e/specs/confirmations/send-to-contract-address.spec.ts b/e2e/specs/confirmations/send-to-contract-address.spec.ts
index 83b3a615c34..936c32f835f 100644
--- a/e2e/specs/confirmations/send-to-contract-address.spec.ts
+++ b/e2e/specs/confirmations/send-to-contract-address.spec.ts
@@ -14,6 +14,9 @@ import { DappVariants } from '../../framework/Constants';
import { Mockttp } from 'mockttp';
import { setupRemoteFeatureFlagsMock } from '../../api-mocking/helpers/remoteFeatureFlagsHelper';
import { oldConfirmationsRemoteFeatureFlags } from '../../api-mocking/mock-responses/feature-flags-mocks';
+import { LocalNode } from '../../framework/types';
+import { AnvilPort } from '../../framework/fixtures/FixtureUtils';
+import { AnvilManager } from '../../seeder/anvil-manager';
const HST_CONTRACT = SMART_CONTRACTS.HST;
@@ -35,7 +38,25 @@ describe.skip(RegressionConfirmations('Send to contract address'), () => {
dappVariant: DappVariants.TEST_DAPP,
},
],
- fixture: new FixtureBuilder().withGanacheNetwork().build(),
+ fixture: ({ localNodes }: { localNodes?: LocalNode[] }) => {
+ const node = localNodes?.[0] as unknown as AnvilManager;
+ const rpcPort =
+ node instanceof AnvilManager
+ ? (node.getPort() ?? AnvilPort())
+ : undefined;
+
+ return new FixtureBuilder()
+ .withNetworkController({
+ providerConfig: {
+ chainId: '0x539',
+ rpcUrl: `http://localhost:${rpcPort ?? AnvilPort()}`,
+ type: 'custom',
+ nickname: 'Local RPC',
+ ticker: 'ETH',
+ },
+ })
+ .build();
+ },
restartDevice: true,
smartContracts: [HST_CONTRACT],
testSpecificMock,
diff --git a/e2e/specs/confirmations/set-approval-for-all-erc1155.spec.ts b/e2e/specs/confirmations/set-approval-for-all-erc1155.spec.ts
index cac4d31a82a..afbff8fcf35 100644
--- a/e2e/specs/confirmations/set-approval-for-all-erc1155.spec.ts
+++ b/e2e/specs/confirmations/set-approval-for-all-erc1155.spec.ts
@@ -9,11 +9,16 @@ import { ActivitiesViewSelectorsText } from '../../selectors/Transactions/Activi
import Assertions from '../../framework/Assertions';
import { ContractApprovalBottomSheetSelectorsText } from '../../selectors/Browser/ContractApprovalBottomSheet.selectors';
import ContractApprovalBottomSheet from '../../pages/Browser/ContractApprovalBottomSheet';
-import { buildPermissions } from '../../framework/fixtures/FixtureUtils';
+import {
+ AnvilPort,
+ buildPermissions,
+} from '../../framework/fixtures/FixtureUtils';
import { DappVariants } from '../../framework/Constants';
import { Mockttp } from 'mockttp';
import { setupRemoteFeatureFlagsMock } from '../../api-mocking/helpers/remoteFeatureFlagsHelper';
import { oldConfirmationsRemoteFeatureFlags } from '../../api-mocking/mock-responses/feature-flags-mocks';
+import { LocalNode } from '../../framework/types';
+import { AnvilManager } from '../../seeder/anvil-manager';
describe.skip(RegressionConfirmations('ERC1155 token'), () => {
const ERC1155_CONTRACT = SMART_CONTRACTS.ERC1155;
@@ -33,12 +38,28 @@ describe.skip(RegressionConfirmations('ERC1155 token'), () => {
dappVariant: DappVariants.TEST_DAPP,
},
],
- fixture: new FixtureBuilder()
- .withGanacheNetwork()
- .withPermissionControllerConnectedToTestDapp(
- buildPermissions(['0x539']),
- )
- .build(),
+ fixture: ({ localNodes }: { localNodes?: LocalNode[] }) => {
+ const node = localNodes?.[0] as unknown as AnvilManager;
+ const rpcPort =
+ node instanceof AnvilManager
+ ? (node.getPort() ?? AnvilPort())
+ : undefined;
+
+ return new FixtureBuilder()
+ .withNetworkController({
+ providerConfig: {
+ chainId: '0x539',
+ rpcUrl: `http://localhost:${rpcPort ?? AnvilPort()}`,
+ type: 'custom',
+ nickname: 'Local RPC',
+ ticker: 'ETH',
+ },
+ })
+ .withPermissionControllerConnectedToTestDapp(
+ buildPermissions(['0x539']),
+ )
+ .build();
+ },
restartDevice: true,
smartContracts: [ERC1155_CONTRACT],
testSpecificMock,
diff --git a/e2e/specs/confirmations/set-approve-for-all-erc721.spec.ts b/e2e/specs/confirmations/set-approve-for-all-erc721.spec.ts
index 6546f787a25..a3bcf25540b 100644
--- a/e2e/specs/confirmations/set-approve-for-all-erc721.spec.ts
+++ b/e2e/specs/confirmations/set-approve-for-all-erc721.spec.ts
@@ -9,13 +9,18 @@ import { ActivitiesViewSelectorsText } from '../../selectors/Transactions/Activi
import Assertions from '../../framework/Assertions';
import { ContractApprovalBottomSheetSelectorsText } from '../../selectors/Browser/ContractApprovalBottomSheet.selectors';
import ContractApprovalBottomSheet from '../../pages/Browser/ContractApprovalBottomSheet';
-import { buildPermissions } from '../../framework/fixtures/FixtureUtils';
+import {
+ buildPermissions,
+ AnvilPort,
+} from '../../framework/fixtures/FixtureUtils';
import { DappVariants } from '../../framework/Constants';
import { Mockttp } from 'mockttp';
import { setupRemoteFeatureFlagsMock } from '../../api-mocking/helpers/remoteFeatureFlagsHelper';
import { oldConfirmationsRemoteFeatureFlags } from '../../api-mocking/mock-responses/feature-flags-mocks';
import NetworkListModal from '../../pages/Network/NetworkListModal';
import WalletView from '../../pages/wallet/WalletView';
+import { LocalNode } from '../../framework/types';
+import { AnvilManager } from '../../seeder/anvil-manager';
describe(RegressionConfirmations('ERC721 token'), () => {
const NFT_CONTRACT = SMART_CONTRACTS.NFTS;
@@ -35,12 +40,28 @@ describe(RegressionConfirmations('ERC721 token'), () => {
dappVariant: DappVariants.TEST_DAPP,
},
],
- fixture: new FixtureBuilder()
- .withGanacheNetwork()
- .withPermissionControllerConnectedToTestDapp(
- buildPermissions(['0x539']),
- )
- .build(),
+ fixture: ({ localNodes }: { localNodes?: LocalNode[] }) => {
+ const node = localNodes?.[0] as unknown as AnvilManager;
+ const rpcPort =
+ node instanceof AnvilManager
+ ? (node.getPort() ?? AnvilPort())
+ : undefined;
+
+ return new FixtureBuilder()
+ .withNetworkController({
+ providerConfig: {
+ chainId: '0x539',
+ rpcUrl: `http://localhost:${rpcPort ?? AnvilPort()}`,
+ type: 'custom',
+ nickname: 'Local RPC',
+ ticker: 'ETH',
+ },
+ })
+ .withPermissionControllerConnectedToTestDapp(
+ buildPermissions(['0x539']),
+ )
+ .build();
+ },
restartDevice: true,
smartContracts: [NFT_CONTRACT],
testSpecificMock,
diff --git a/e2e/specs/confirmations/signatures/ethereum-sign.spec.ts b/e2e/specs/confirmations/signatures/ethereum-sign.spec.ts
index cb6c9a2029d..670985e29cd 100644
--- a/e2e/specs/confirmations/signatures/ethereum-sign.spec.ts
+++ b/e2e/specs/confirmations/signatures/ethereum-sign.spec.ts
@@ -6,12 +6,17 @@ import TestDApp from '../../../pages/Browser/TestDApp';
import FixtureBuilder from '../../../framework/fixtures/FixtureBuilder';
import { withFixtures } from '../../../framework/fixtures/FixtureHelper';
import Assertions from '../../../framework/Assertions';
-import { buildPermissions } from '../../../framework/fixtures/FixtureUtils';
+import {
+ buildPermissions,
+ AnvilPort,
+} from '../../../framework/fixtures/FixtureUtils';
import { DappVariants } from '../../../framework/Constants';
import { RegressionConfirmations } from '../../../tags';
import { Mockttp } from 'mockttp';
import { setupRemoteFeatureFlagsMock } from '../../../api-mocking/helpers/remoteFeatureFlagsHelper';
import { oldConfirmationsRemoteFeatureFlags } from '../../../api-mocking/mock-responses/feature-flags-mocks';
+import { LocalNode } from '../../../framework/types';
+import { AnvilManager } from '../../../seeder/anvil-manager';
describe(RegressionConfirmations('Ethereum Sign'), () => {
it('Sign in with Ethereum', async () => {
@@ -29,12 +34,28 @@ describe(RegressionConfirmations('Ethereum Sign'), () => {
dappVariant: DappVariants.TEST_DAPP,
},
],
- fixture: new FixtureBuilder()
- .withGanacheNetwork()
- .withPermissionControllerConnectedToTestDapp(
- buildPermissions(['0x539']),
- )
- .build(),
+ fixture: ({ localNodes }: { localNodes?: LocalNode[] }) => {
+ const node = localNodes?.[0] as unknown as AnvilManager;
+ const rpcPort =
+ node instanceof AnvilManager
+ ? (node.getPort() ?? AnvilPort())
+ : undefined;
+
+ return new FixtureBuilder()
+ .withNetworkController({
+ providerConfig: {
+ chainId: '0x539',
+ rpcUrl: `http://localhost:${rpcPort ?? AnvilPort()}`,
+ type: 'custom',
+ nickname: 'Local RPC',
+ ticker: 'ETH',
+ },
+ })
+ .withPermissionControllerConnectedToTestDapp(
+ buildPermissions(['0x539']),
+ )
+ .build();
+ },
restartDevice: true,
testSpecificMock,
},
diff --git a/e2e/specs/confirmations/signatures/personal-sign.spec.ts b/e2e/specs/confirmations/signatures/personal-sign.spec.ts
index 5ecdd968edb..4be3ce72b22 100644
--- a/e2e/specs/confirmations/signatures/personal-sign.spec.ts
+++ b/e2e/specs/confirmations/signatures/personal-sign.spec.ts
@@ -6,12 +6,17 @@ import TestDApp from '../../../pages/Browser/TestDApp';
import FixtureBuilder from '../../../framework/fixtures/FixtureBuilder';
import { withFixtures } from '../../../framework/fixtures/FixtureHelper';
import Assertions from '../../../framework/Assertions';
-import { buildPermissions } from '../../../framework/fixtures/FixtureUtils';
+import {
+ buildPermissions,
+ AnvilPort,
+} from '../../../framework/fixtures/FixtureUtils';
import { DappVariants } from '../../../framework/Constants';
import { Mockttp } from 'mockttp';
import { setupRemoteFeatureFlagsMock } from '../../../api-mocking/helpers/remoteFeatureFlagsHelper';
import { oldConfirmationsRemoteFeatureFlags } from '../../../api-mocking/mock-responses/feature-flags-mocks';
import { RegressionConfirmations } from '../../../tags';
+import { LocalNode } from '../../../framework/types';
+import { AnvilManager } from '../../../seeder/anvil-manager';
describe(RegressionConfirmations('Personal Sign'), () => {
const testSpecificMock = async (mockServer: Mockttp) => {
@@ -33,12 +38,28 @@ describe(RegressionConfirmations('Personal Sign'), () => {
dappVariant: DappVariants.TEST_DAPP,
},
],
- fixture: new FixtureBuilder()
- .withGanacheNetwork()
- .withPermissionControllerConnectedToTestDapp(
- buildPermissions(['0x539']),
- )
- .build(),
+ fixture: ({ localNodes }: { localNodes?: LocalNode[] }) => {
+ const node = localNodes?.[0] as unknown as AnvilManager;
+ const rpcPort =
+ node instanceof AnvilManager
+ ? (node.getPort() ?? AnvilPort())
+ : undefined;
+
+ return new FixtureBuilder()
+ .withNetworkController({
+ providerConfig: {
+ chainId: '0x539',
+ rpcUrl: `http://localhost:${rpcPort ?? AnvilPort()}`,
+ type: 'custom',
+ nickname: 'Local RPC',
+ ticker: 'ETH',
+ },
+ })
+ .withPermissionControllerConnectedToTestDapp(
+ buildPermissions(['0x539']),
+ )
+ .build();
+ },
restartDevice: true,
testSpecificMock,
},
diff --git a/e2e/specs/confirmations/signatures/typed-sign-v3.spec.ts b/e2e/specs/confirmations/signatures/typed-sign-v3.spec.ts
index c1c80eaa6a3..f8eb22d6563 100644
--- a/e2e/specs/confirmations/signatures/typed-sign-v3.spec.ts
+++ b/e2e/specs/confirmations/signatures/typed-sign-v3.spec.ts
@@ -6,12 +6,17 @@ import TestDApp from '../../../pages/Browser/TestDApp';
import FixtureBuilder from '../../../framework/fixtures/FixtureBuilder';
import { withFixtures } from '../../../framework/fixtures/FixtureHelper';
import Assertions from '../../../framework/Assertions';
-import { buildPermissions } from '../../../framework/fixtures/FixtureUtils';
+import {
+ buildPermissions,
+ AnvilPort,
+} from '../../../framework/fixtures/FixtureUtils';
import { DappVariants } from '../../../framework/Constants';
import { Mockttp } from 'mockttp';
import { setupRemoteFeatureFlagsMock } from '../../../api-mocking/helpers/remoteFeatureFlagsHelper';
import { oldConfirmationsRemoteFeatureFlags } from '../../../api-mocking/mock-responses/feature-flags-mocks';
import { RegressionConfirmations } from '../../../tags';
+import { LocalNode } from '../../../framework/types';
+import { AnvilManager } from '../../../seeder/anvil-manager';
describe(RegressionConfirmations('Typed Sign V3'), () => {
const testSpecificMock = async (mockServer: Mockttp) => {
@@ -33,12 +38,28 @@ describe(RegressionConfirmations('Typed Sign V3'), () => {
dappVariant: DappVariants.TEST_DAPP,
},
],
- fixture: new FixtureBuilder()
- .withGanacheNetwork()
- .withPermissionControllerConnectedToTestDapp(
- buildPermissions(['0x539']),
- )
- .build(),
+ fixture: ({ localNodes }: { localNodes?: LocalNode[] }) => {
+ const node = localNodes?.[0] as unknown as AnvilManager;
+ const rpcPort =
+ node instanceof AnvilManager
+ ? (node.getPort() ?? AnvilPort())
+ : undefined;
+
+ return new FixtureBuilder()
+ .withNetworkController({
+ providerConfig: {
+ chainId: '0x539',
+ rpcUrl: `http://localhost:${rpcPort ?? AnvilPort()}`,
+ type: 'custom',
+ nickname: 'Local RPC',
+ ticker: 'ETH',
+ },
+ })
+ .withPermissionControllerConnectedToTestDapp(
+ buildPermissions(['0x539']),
+ )
+ .build();
+ },
restartDevice: true,
testSpecificMock,
},
diff --git a/e2e/specs/confirmations/signatures/typed-sign-v4.spec.ts b/e2e/specs/confirmations/signatures/typed-sign-v4.spec.ts
index f8ff058b5a6..d1a79a0c447 100644
--- a/e2e/specs/confirmations/signatures/typed-sign-v4.spec.ts
+++ b/e2e/specs/confirmations/signatures/typed-sign-v4.spec.ts
@@ -6,12 +6,17 @@ import TestDApp from '../../../pages/Browser/TestDApp';
import FixtureBuilder from '../../../framework/fixtures/FixtureBuilder';
import { withFixtures } from '../../../framework/fixtures/FixtureHelper';
import Assertions from '../../../framework/Assertions';
-import { buildPermissions } from '../../../framework/fixtures/FixtureUtils';
+import {
+ buildPermissions,
+ AnvilPort,
+} from '../../../framework/fixtures/FixtureUtils';
import { DappVariants } from '../../../framework/Constants';
import { Mockttp } from 'mockttp';
import { setupRemoteFeatureFlagsMock } from '../../../api-mocking/helpers/remoteFeatureFlagsHelper';
import { oldConfirmationsRemoteFeatureFlags } from '../../../api-mocking/mock-responses/feature-flags-mocks';
import { RegressionConfirmations } from '../../../tags';
+import { LocalNode } from '../../../framework/types';
+import { AnvilManager } from '../../../seeder/anvil-manager';
describe(RegressionConfirmations('Typed Sign V4'), () => {
const testSpecificMock = async (mockServer: Mockttp) => {
@@ -33,12 +38,28 @@ describe(RegressionConfirmations('Typed Sign V4'), () => {
dappVariant: DappVariants.TEST_DAPP,
},
],
- fixture: new FixtureBuilder()
- .withGanacheNetwork()
- .withPermissionControllerConnectedToTestDapp(
- buildPermissions(['0x539']),
- )
- .build(),
+ fixture: ({ localNodes }: { localNodes?: LocalNode[] }) => {
+ const node = localNodes?.[0] as unknown as AnvilManager;
+ const rpcPort =
+ node instanceof AnvilManager
+ ? (node.getPort() ?? AnvilPort())
+ : undefined;
+
+ return new FixtureBuilder()
+ .withNetworkController({
+ providerConfig: {
+ chainId: '0x539',
+ rpcUrl: `http://localhost:${rpcPort ?? AnvilPort()}`,
+ type: 'custom',
+ nickname: 'Local RPC',
+ ticker: 'ETH',
+ },
+ })
+ .withPermissionControllerConnectedToTestDapp(
+ buildPermissions(['0x539']),
+ )
+ .build();
+ },
restartDevice: true,
testSpecificMock,
},
diff --git a/e2e/specs/confirmations/signatures/typed-sign.spec.ts b/e2e/specs/confirmations/signatures/typed-sign.spec.ts
index c319f26cc97..b4f00588e38 100644
--- a/e2e/specs/confirmations/signatures/typed-sign.spec.ts
+++ b/e2e/specs/confirmations/signatures/typed-sign.spec.ts
@@ -6,12 +6,17 @@ import TestDApp from '../../../pages/Browser/TestDApp';
import FixtureBuilder from '../../../framework/fixtures/FixtureBuilder';
import { withFixtures } from '../../../framework/fixtures/FixtureHelper';
import Assertions from '../../../framework/Assertions';
-import { buildPermissions } from '../../../framework/fixtures/FixtureUtils';
+import {
+ buildPermissions,
+ AnvilPort,
+} from '../../../framework/fixtures/FixtureUtils';
import { DappVariants } from '../../../framework/Constants';
import { Mockttp } from 'mockttp';
import { setupRemoteFeatureFlagsMock } from '../../../api-mocking/helpers/remoteFeatureFlagsHelper';
import { oldConfirmationsRemoteFeatureFlags } from '../../../api-mocking/mock-responses/feature-flags-mocks';
import { RegressionConfirmations } from '../../../tags';
+import { LocalNode } from '../../../framework/types';
+import { AnvilManager } from '../../../seeder/anvil-manager';
describe(RegressionConfirmations('Typed Sign'), () => {
const testSpecificMock = async (mockServer: Mockttp) => {
@@ -33,12 +38,28 @@ describe(RegressionConfirmations('Typed Sign'), () => {
dappVariant: DappVariants.TEST_DAPP,
},
],
- fixture: new FixtureBuilder()
- .withGanacheNetwork()
- .withPermissionControllerConnectedToTestDapp(
- buildPermissions(['0x539']),
- )
- .build(),
+ fixture: ({ localNodes }: { localNodes?: LocalNode[] }) => {
+ const node = localNodes?.[0] as unknown as AnvilManager;
+ const rpcPort =
+ node instanceof AnvilManager
+ ? (node.getPort() ?? AnvilPort())
+ : undefined;
+
+ return new FixtureBuilder()
+ .withNetworkController({
+ providerConfig: {
+ chainId: '0x539',
+ rpcUrl: `http://localhost:${rpcPort ?? AnvilPort()}`,
+ type: 'custom',
+ nickname: 'Local RPC',
+ ticker: 'ETH',
+ },
+ })
+ .withPermissionControllerConnectedToTestDapp(
+ buildPermissions(['0x539']),
+ )
+ .build();
+ },
restartDevice: true,
testSpecificMock,
},
diff --git a/e2e/specs/quarantine/edit-recipient-address.failing.ts b/e2e/specs/quarantine/edit-recipient-address.failing.ts
index 9799e20b590..7467aca8386 100644
--- a/e2e/specs/quarantine/edit-recipient-address.failing.ts
+++ b/e2e/specs/quarantine/edit-recipient-address.failing.ts
@@ -11,6 +11,9 @@ import TabBarComponent from '../../pages/wallet/TabBarComponent';
import { withFixtures } from '../../framework/fixtures/FixtureHelper';
import FixtureBuilder from '../../framework/fixtures/FixtureBuilder';
import ActivitiesView from '../../pages/Transactions/ActivitiesView';
+import { LocalNode } from '../../framework/types';
+import { AnvilPort } from '../../framework/fixtures/FixtureUtils';
+import { AnvilManager } from '../../seeder/anvil-manager';
const INCORRECT_SEND_ADDRESS = '0xebe6CcB6B55e1d094d9c58980Bc10Fed69932cAb';
const CORRECT_SEND_ADDRESS = '0x37cc5ef6bfe753aeaf81f945efe88134b238face';
@@ -27,7 +30,25 @@ describe(
it('should display correct send address after edit', async () => {
await withFixtures(
{
- fixture: new FixtureBuilder().withGanacheNetwork().build(),
+ fixture: ({ localNodes }: { localNodes?: LocalNode[] }) => {
+ const node = localNodes?.[0] as unknown as AnvilManager;
+ const rpcPort =
+ node instanceof AnvilManager
+ ? (node.getPort() ?? AnvilPort())
+ : undefined;
+
+ return new FixtureBuilder()
+ .withNetworkController({
+ providerConfig: {
+ chainId: '0x539',
+ rpcUrl: `http://localhost:${rpcPort ?? AnvilPort()}`,
+ type: 'custom',
+ nickname: 'Local RPC',
+ ticker: 'ETH',
+ },
+ })
+ .build();
+ },
restartDevice: true,
},
async () => {
diff --git a/e2e/specs/quarantine/security-alert-send-eth.failing.ts b/e2e/specs/quarantine/security-alert-send-eth.failing.ts
index 3e3a8a07932..1fa6fb0d49c 100644
--- a/e2e/specs/quarantine/security-alert-send-eth.failing.ts
+++ b/e2e/specs/quarantine/security-alert-send-eth.failing.ts
@@ -19,6 +19,9 @@ import {
} from '../../api-mocking/mock-responses/security-alerts-mock';
import { setupRemoteFeatureFlagsMock } from '../../api-mocking/helpers/remoteFeatureFlagsHelper';
import { confirmationsRedesignedFeatureFlags } from '../../api-mocking/mock-responses/feature-flags-mocks';
+import { LocalNode } from '../../framework/types';
+import { AnvilPort } from '../../framework/fixtures/FixtureUtils';
+import { AnvilManager } from '../../seeder/anvil-manager';
const BENIGN_ADDRESS_MOCK = '0x50587E46C5B96a3F6f9792922EC647F13E6EFAE4';
@@ -29,7 +32,25 @@ describe(SmokeConfirmationsRedesigned('Security Alert API - Send flow'), () => {
) => {
await withFixtures(
{
- fixture: new FixtureBuilder().withGanacheNetwork().build(),
+ fixture: ({ localNodes }: { localNodes?: LocalNode[] }) => {
+ const node = localNodes?.[0] as unknown as AnvilManager;
+ const rpcPort =
+ node instanceof AnvilManager
+ ? (node.getPort() ?? AnvilPort())
+ : undefined;
+
+ return new FixtureBuilder()
+ .withNetworkController({
+ providerConfig: {
+ chainId: '0x539',
+ rpcUrl: `http://localhost:${rpcPort ?? AnvilPort()}`,
+ type: 'custom',
+ nickname: 'Local RPC',
+ ticker: 'ETH',
+ },
+ })
+ .build();
+ },
restartDevice: true,
testSpecificMock,
},
diff --git a/e2e/specs/quarantine/send-eth.failing.ts b/e2e/specs/quarantine/send-eth.failing.ts
index f7a32b99439..f726acaf218 100644
--- a/e2e/specs/quarantine/send-eth.failing.ts
+++ b/e2e/specs/quarantine/send-eth.failing.ts
@@ -12,6 +12,9 @@ import { withFixtures } from '../../framework/fixtures/FixtureHelper';
import { SMART_CONTRACTS } from '../../../app/util/test/smart-contracts';
import Assertions from '../../framework/Assertions';
import TokenOverview from '../../pages/wallet/TokenOverview';
+import { LocalNode } from '../../framework/types';
+import { AnvilPort } from '../../framework/fixtures/FixtureUtils';
+import { AnvilManager } from '../../seeder/anvil-manager';
// This test was migrated to the new framework but should be reworked to use withFixtures properly
describe(RegressionConfirmations('Send ETH'), () => {
@@ -27,7 +30,25 @@ describe(RegressionConfirmations('Send ETH'), () => {
const RECIPIENT = '0x1FDb169Ef12954F20A15852980e1F0C122BfC1D6';
await withFixtures(
{
- fixture: new FixtureBuilder().withGanacheNetwork().build(),
+ fixture: ({ localNodes }: { localNodes?: LocalNode[] }) => {
+ const node = localNodes?.[0] as unknown as AnvilManager;
+ const rpcPort =
+ node instanceof AnvilManager
+ ? (node.getPort() ?? AnvilPort())
+ : undefined;
+
+ return new FixtureBuilder()
+ .withNetworkController({
+ providerConfig: {
+ chainId: '0x539',
+ rpcUrl: `http://localhost:${rpcPort ?? AnvilPort()}`,
+ type: 'custom',
+ nickname: 'Local RPC',
+ ticker: 'ETH',
+ },
+ })
+ .build();
+ },
restartDevice: true,
},
async () => {
@@ -54,7 +75,25 @@ describe(RegressionConfirmations('Send ETH'), () => {
await withFixtures(
{
- fixture: new FixtureBuilder().withGanacheNetwork().build(),
+ fixture: ({ localNodes }: { localNodes?: LocalNode[] }) => {
+ const node = localNodes?.[0] as unknown as AnvilManager;
+ const rpcPort =
+ node instanceof AnvilManager
+ ? (node.getPort() ?? AnvilPort())
+ : undefined;
+
+ return new FixtureBuilder()
+ .withNetworkController({
+ providerConfig: {
+ chainId: '0x539',
+ rpcUrl: `http://localhost:${rpcPort ?? AnvilPort()}`,
+ type: 'custom',
+ nickname: 'Local RPC',
+ ticker: 'ETH',
+ },
+ })
+ .build();
+ },
restartDevice: true,
smartContracts: [MULTISIG_CONTRACT],
},
@@ -84,7 +123,25 @@ describe(RegressionConfirmations('Send ETH'), () => {
await withFixtures(
{
- fixture: new FixtureBuilder().withGanacheNetwork().build(),
+ fixture: ({ localNodes }: { localNodes?: LocalNode[] }) => {
+ const node = localNodes?.[0] as unknown as AnvilManager;
+ const rpcPort =
+ node instanceof AnvilManager
+ ? (node.getPort() ?? AnvilPort())
+ : undefined;
+
+ return new FixtureBuilder()
+ .withNetworkController({
+ providerConfig: {
+ chainId: '0x539',
+ rpcUrl: `http://localhost:${rpcPort ?? AnvilPort()}`,
+ type: 'custom',
+ nickname: 'Local RPC',
+ ticker: 'ETH',
+ },
+ })
+ .build();
+ },
restartDevice: true,
},
async () => {
diff --git a/e2e/specs/quarantine/swap-action-regression.failing.ts b/e2e/specs/quarantine/swap-action-regression.failing.ts
index fec5dace160..7ba186d9cf5 100644
--- a/e2e/specs/quarantine/swap-action-regression.failing.ts
+++ b/e2e/specs/quarantine/swap-action-regression.failing.ts
@@ -1,5 +1,5 @@
import { withFixtures } from '../../framework/fixtures/FixtureHelper';
-import { LocalNodeType } from '../../framework/types';
+import { LocalNode, LocalNodeType } from '../../framework/types';
import FixtureBuilder from '../../framework/fixtures/FixtureBuilder';
import TabBarComponent from '../../pages/wallet/TabBarComponent';
import WalletView from '../../pages/wallet/WalletView';
@@ -11,6 +11,8 @@ import {
import { loginToApp } from '../../viewHelper';
import { prepareSwapsTestEnvironment } from '../swaps/helpers/prepareSwapsTestEnvironment';
import { testSpecificMock } from '../swaps/helpers/swap-mocks';
+import { AnvilPort } from '../../framework/fixtures/FixtureUtils';
+import { AnvilManager } from '../../seeder/anvil-manager';
describe(RegressionTrade('Multiple Swaps from Actions'), (): void => {
beforeEach(async (): Promise => {
@@ -20,10 +22,26 @@ describe(RegressionTrade('Multiple Swaps from Actions'), (): void => {
it('should complete a USDC to DAI swap from the token chart', async (): Promise => {
await withFixtures(
{
- fixture: new FixtureBuilder()
- .withGanacheNetwork('0x1')
- .withDisabledSmartTransactions()
- .build(),
+ fixture: ({ localNodes }: { localNodes?: LocalNode[] }) => {
+ const node = localNodes?.[0] as unknown as AnvilManager;
+ const rpcPort =
+ node instanceof AnvilManager
+ ? (node.getPort() ?? AnvilPort())
+ : undefined;
+
+ return new FixtureBuilder()
+ .withNetworkController({
+ providerConfig: {
+ chainId: '0x539',
+ rpcUrl: `http://localhost:${rpcPort ?? AnvilPort()}`,
+ type: 'custom',
+ nickname: 'Local RPC',
+ ticker: 'ETH',
+ },
+ })
+ .withDisabledSmartTransactions()
+ .build();
+ },
localNodeOptions: [
{
type: LocalNodeType.anvil,
diff --git a/e2e/specs/send/send-erc20-token.spec.ts b/e2e/specs/send/send-erc20-token.spec.ts
index b21eaceed52..74688e9e23b 100644
--- a/e2e/specs/send/send-erc20-token.spec.ts
+++ b/e2e/specs/send/send-erc20-token.spec.ts
@@ -4,9 +4,10 @@ import SendView from '../../pages/Send/RedesignedSendView';
import WalletView from '../../pages/wallet/WalletView';
import { DappVariants } from '../../framework/Constants';
import { SmokeConfirmationsRedesigned } from '../../tags';
-import { buildPermissions } from '../../framework/fixtures/FixtureUtils';
+import { AnvilPort } from '../../framework/fixtures/FixtureUtils';
import { loginToApp } from '../../viewHelper';
import { withFixtures } from '../../framework/fixtures/FixtureHelper';
+import { AnvilManager } from '../../seeder/anvil-manager';
const RECIPIENT = '0x0c54fccd2e384b4bb6f2e405bf5cbc15a017aafb';
@@ -19,32 +20,45 @@ describe(SmokeConfirmationsRedesigned('Send ERC20 asset'), () => {
dappVariant: DappVariants.TEST_DAPP,
},
],
- fixture: new FixtureBuilder()
- .withGanacheNetwork()
- .withPermissionControllerConnectedToTestDapp(
- buildPermissions(['0x539']),
- )
- .withTokensForAllPopularNetworks([
- {
- address: '0x0000000000000000000000000000000000000000',
- symbol: 'ETH',
- decimals: 18,
- name: 'Ethereum',
- },
- {
- address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
- symbol: 'USDC',
- decimals: 6,
- name: 'USD Coin',
- },
- {
- address: '0x6B175474E89094C44Da98b954EedeAC495271d0F',
- symbol: 'DAI',
- decimals: 18,
- name: 'Dai Stablecoin',
- },
- ])
- .build(),
+ fixture: ({ localNodes }) => {
+ const node = localNodes?.[0] as unknown as AnvilManager;
+ const rpcPort =
+ node instanceof AnvilManager
+ ? (node.getPort() ?? AnvilPort())
+ : undefined;
+
+ return new FixtureBuilder()
+ .withNetworkController({
+ providerConfig: {
+ chainId: '0x539',
+ rpcUrl: `http://localhost:${rpcPort ?? AnvilPort()}`,
+ type: 'custom',
+ nickname: 'Local RPC',
+ ticker: 'ETH',
+ },
+ })
+ .withTokensForAllPopularNetworks([
+ {
+ address: '0x0000000000000000000000000000000000000000',
+ symbol: 'ETH',
+ decimals: 18,
+ name: 'Ethereum',
+ },
+ {
+ address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
+ symbol: 'USDC',
+ decimals: 6,
+ name: 'USD Coin',
+ },
+ {
+ address: '0x6B175474E89094C44Da98b954EedeAC495271d0F',
+ symbol: 'DAI',
+ decimals: 18,
+ name: 'Dai Stablecoin',
+ },
+ ])
+ .build();
+ },
restartDevice: true,
},
async () => {
@@ -71,32 +85,45 @@ describe(SmokeConfirmationsRedesigned('Send ERC20 asset'), () => {
dappVariant: DappVariants.TEST_DAPP,
},
],
- fixture: new FixtureBuilder()
- .withGanacheNetwork()
- .withPermissionControllerConnectedToTestDapp(
- buildPermissions(['0x539']),
- )
- .withTokensForAllPopularNetworks([
- {
- address: '0x0000000000000000000000000000000000000000',
- symbol: 'ETH',
- decimals: 18,
- name: 'Ethereum',
- },
- {
- address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
- symbol: 'USDC',
- decimals: 6,
- name: 'USD Coin',
- },
- {
- address: '0x6B175474E89094C44Da98b954EedeAC495271d0F',
- symbol: 'DAI',
- decimals: 18,
- name: 'Dai Stablecoin',
- },
- ])
- .build(),
+ fixture: ({ localNodes }) => {
+ const node = localNodes?.[0] as unknown as { getPort?: () => number };
+ const rpcPort =
+ node instanceof AnvilManager
+ ? (node.getPort() ?? AnvilPort())
+ : undefined;
+
+ return new FixtureBuilder()
+ .withNetworkController({
+ providerConfig: {
+ chainId: '0x539',
+ rpcUrl: `http://localhost:${rpcPort ?? AnvilPort()}`,
+ type: 'custom',
+ nickname: 'Local RPC',
+ ticker: 'ETH',
+ },
+ })
+ .withTokensForAllPopularNetworks([
+ {
+ address: '0x0000000000000000000000000000000000000000',
+ symbol: 'ETH',
+ decimals: 18,
+ name: 'Ethereum',
+ },
+ {
+ address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
+ symbol: 'USDC',
+ decimals: 6,
+ name: 'USD Coin',
+ },
+ {
+ address: '0x6B175474E89094C44Da98b954EedeAC495271d0F',
+ symbol: 'DAI',
+ decimals: 18,
+ name: 'Dai Stablecoin',
+ },
+ ])
+ .build();
+ },
restartDevice: true,
},
async () => {
@@ -123,32 +150,45 @@ describe(SmokeConfirmationsRedesigned('Send ERC20 asset'), () => {
dappVariant: DappVariants.TEST_DAPP,
},
],
- fixture: new FixtureBuilder()
- .withGanacheNetwork()
- .withPermissionControllerConnectedToTestDapp(
- buildPermissions(['0x539']),
- )
- .withTokensForAllPopularNetworks([
- {
- address: '0x0000000000000000000000000000000000000000',
- symbol: 'ETH',
- decimals: 18,
- name: 'Ethereum',
- },
- {
- address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
- symbol: 'USDC',
- decimals: 6,
- name: 'USD Coin',
- },
- {
- address: '0x6B175474E89094C44Da98b954EedeAC495271d0F',
- symbol: 'DAI',
- decimals: 18,
- name: 'Dai Stablecoin',
- },
- ])
- .build(),
+ fixture: ({ localNodes }) => {
+ const node = localNodes?.[0] as unknown as AnvilManager;
+ const rpcPort =
+ node instanceof AnvilManager
+ ? (node.getPort() ?? AnvilPort())
+ : undefined;
+
+ return new FixtureBuilder()
+ .withNetworkController({
+ providerConfig: {
+ chainId: '0x539',
+ rpcUrl: `http://localhost:${rpcPort ?? AnvilPort()}`,
+ type: 'custom',
+ nickname: 'Local RPC',
+ ticker: 'ETH',
+ },
+ })
+ .withTokensForAllPopularNetworks([
+ {
+ address: '0x0000000000000000000000000000000000000000',
+ symbol: 'ETH',
+ decimals: 18,
+ name: 'Ethereum',
+ },
+ {
+ address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
+ symbol: 'USDC',
+ decimals: 6,
+ name: 'USD Coin',
+ },
+ {
+ address: '0x6B175474E89094C44Da98b954EedeAC495271d0F',
+ symbol: 'DAI',
+ decimals: 18,
+ name: 'Dai Stablecoin',
+ },
+ ])
+ .build();
+ },
restartDevice: true,
},
async () => {
diff --git a/e2e/specs/send/send-native-token.spec.ts b/e2e/specs/send/send-native-token.spec.ts
index fb9be43761c..115c72ef581 100644
--- a/e2e/specs/send/send-native-token.spec.ts
+++ b/e2e/specs/send/send-native-token.spec.ts
@@ -6,9 +6,11 @@ import WalletView from '../../pages/wallet/WalletView';
import { Assertions } from '../../framework';
import { DappVariants } from '../../framework/Constants';
import { SmokeConfirmationsRedesigned } from '../../tags';
-import { buildPermissions } from '../../framework/fixtures/FixtureUtils';
+import { AnvilPort } from '../../framework/fixtures/FixtureUtils';
import { loginToApp } from '../../viewHelper';
import { withFixtures } from '../../framework/fixtures/FixtureHelper';
+import { LocalNode } from '../../framework/types';
+import { AnvilManager } from '../../seeder/anvil-manager';
const RECIPIENT = '0x0c54fccd2e384b4bb6f2e405bf5cbc15a017aafb';
@@ -21,12 +23,25 @@ describe(SmokeConfirmationsRedesigned('Send native asset'), () => {
dappVariant: DappVariants.TEST_DAPP,
},
],
- fixture: new FixtureBuilder()
- .withGanacheNetwork()
- .withPermissionControllerConnectedToTestDapp(
- buildPermissions(['0x539']),
- )
- .build(),
+ fixture: ({ localNodes }: { localNodes?: LocalNode[] }) => {
+ const node = localNodes?.[0] as unknown as AnvilManager;
+ const rpcPort =
+ node instanceof AnvilManager
+ ? (node.getPort() ?? AnvilPort())
+ : undefined;
+
+ return new FixtureBuilder()
+ .withNetworkController({
+ providerConfig: {
+ chainId: '0x539',
+ rpcUrl: `http://localhost:${rpcPort ?? AnvilPort()}`,
+ type: 'custom',
+ nickname: 'Local RPC',
+ ticker: 'ETH',
+ },
+ })
+ .build();
+ },
restartDevice: true,
},
async () => {
diff --git a/e2e/specs/settings/addressbook-send-add-contact.spec.ts b/e2e/specs/settings/addressbook-send-add-contact.spec.ts
index 48fd5d16c47..e0948bfb7c4 100644
--- a/e2e/specs/settings/addressbook-send-add-contact.spec.ts
+++ b/e2e/specs/settings/addressbook-send-add-contact.spec.ts
@@ -22,6 +22,9 @@ import {
import { confirmationsRedesignedFeatureFlags } from '../../api-mocking/mock-responses/feature-flags-mocks';
import AddContactView from '../../pages/Settings/Contacts/AddContactView';
import DeleteContactBottomSheet from '../../pages/Settings/Contacts/DeleteContactBottomSheet';
+import { LocalNode } from '../../framework/types';
+import { AnvilPort } from '../../framework/fixtures/FixtureUtils';
+import { AnvilManager } from '../../seeder/anvil-manager';
const INVALID_ADDRESS = '0xB8B4EE5B1b693971eB60bDa15211570df2dB221L';
const MYTH_ADDRESS = '0x1FDb169Ef12954F20A15852980e1F0C122BfC1D6';
@@ -79,13 +82,29 @@ describe(RegressionWalletPlatform('Addressbook Tests'), () => {
it('should add a contact via send flow and go to contacts view', async () => {
await withFixtures(
{
- fixture: new FixtureBuilder()
- .withGanacheNetwork()
- .withNetworkEnabledMap({
- eip155: { '0x539': true },
- })
- .withProfileSyncingDisabled()
- .build(),
+ fixture: ({ localNodes }: { localNodes?: LocalNode[] }) => {
+ const node = localNodes?.[0] as unknown as AnvilManager;
+ const rpcPort =
+ node instanceof AnvilManager
+ ? (node.getPort() ?? AnvilPort())
+ : undefined;
+
+ return new FixtureBuilder()
+ .withNetworkController({
+ providerConfig: {
+ chainId: '0x539',
+ rpcUrl: `http://localhost:${rpcPort ?? AnvilPort()}`,
+ type: 'custom',
+ nickname: 'Local RPC',
+ ticker: 'ETH',
+ },
+ })
+ .withNetworkEnabledMap({
+ eip155: { '0x539': true },
+ })
+ .withProfileSyncingDisabled()
+ .build();
+ },
restartDevice: true,
testSpecificMock,
},
@@ -118,13 +137,29 @@ describe(RegressionWalletPlatform('Addressbook Tests'), () => {
it('should show invalid address error message on send flow', async () => {
await withFixtures(
{
- fixture: new FixtureBuilder()
- .withGanacheNetwork()
- .withNetworkEnabledMap({
- eip155: { '0x539': true },
- })
- .withProfileSyncingDisabled()
- .build(),
+ fixture: ({ localNodes }: { localNodes?: LocalNode[] }) => {
+ const node = localNodes?.[0] as unknown as AnvilManager;
+ const rpcPort =
+ node instanceof AnvilManager
+ ? (node.getPort() ?? AnvilPort())
+ : undefined;
+
+ return new FixtureBuilder()
+ .withNetworkController({
+ providerConfig: {
+ chainId: '0x539',
+ rpcUrl: `http://localhost:${rpcPort ?? AnvilPort()}`,
+ type: 'custom',
+ nickname: 'Local RPC',
+ ticker: 'ETH',
+ },
+ })
+ .withNetworkEnabledMap({
+ eip155: { '0x539': true },
+ })
+ .withProfileSyncingDisabled()
+ .build();
+ },
restartDevice: true,
testSpecificMock,
},
@@ -146,13 +181,29 @@ describe(RegressionWalletPlatform('Addressbook Tests'), () => {
async () => {
await withFixtures(
{
- fixture: new FixtureBuilder()
- .withGanacheNetwork()
- .withNetworkEnabledMap({
- eip155: { '0x539': true },
- })
- .withProfileSyncingDisabled()
- .build(),
+ fixture: ({ localNodes }: { localNodes?: LocalNode[] }) => {
+ const node = localNodes?.[0] as unknown as AnvilManager;
+ const rpcPort =
+ node instanceof AnvilManager
+ ? (node.getPort() ?? AnvilPort())
+ : undefined;
+
+ return new FixtureBuilder()
+ .withNetworkController({
+ providerConfig: {
+ chainId: '0x539',
+ rpcUrl: `http://localhost:${rpcPort ?? AnvilPort()}`,
+ type: 'custom',
+ nickname: 'Local RPC',
+ ticker: 'ETH',
+ },
+ })
+ .withNetworkEnabledMap({
+ eip155: { '0x539': true },
+ })
+ .withProfileSyncingDisabled()
+ .build();
+ },
testSpecificMock,
restartDevice: true,
},
diff --git a/e2e/specs/settings/example-anvil-e2e.spec.ts b/e2e/specs/settings/example-anvil-e2e.spec.ts
index c2e1fe10c36..0d2e212c8ef 100644
--- a/e2e/specs/settings/example-anvil-e2e.spec.ts
+++ b/e2e/specs/settings/example-anvil-e2e.spec.ts
@@ -7,6 +7,9 @@ import QuoteView from '../../pages/swaps/QuoteView';
import SwapView from '../../pages/swaps/SwapView';
import WalletActionsBottomSheet from '../../pages/wallet/WalletActionsBottomSheet';
import ActivitiesView from '../../pages/Transactions/ActivitiesView';
+import { LocalNode } from '../../framework/types';
+import { AnvilPort } from '../../framework/fixtures/FixtureUtils';
+import { AnvilManager } from '../../seeder/anvil-manager';
const sourceTokenSymbol = 'ETH';
const destTokenSymbol = 'DAI';
@@ -17,7 +20,26 @@ describe.skip('NFT Details page', () => {
it('show nft details', async () => {
await withFixtures(
{
- fixture: new FixtureBuilder().withGanacheNetwork().build(),
+ fixture: ({ localNodes }: { localNodes?: LocalNode[] }) => {
+ const node = localNodes?.[0] as unknown as AnvilManager;
+ const rpcPort =
+ node instanceof AnvilManager
+ ? (node.getPort() ?? AnvilPort())
+ : undefined;
+
+ return new FixtureBuilder()
+ .withNetworkController({
+ providerConfig: {
+ chainId: '0x539',
+ rpcUrl: `http://localhost:${rpcPort ?? AnvilPort()}`,
+ type: 'custom',
+ nickname: 'Local RPC',
+ ticker: 'ETH',
+ },
+ })
+ .build();
+ },
+ restartDevice: true,
},
async () => {
// Launch app and login
diff --git a/e2e/specs/wallet/balance-privacy-toggle.spec.ts b/e2e/specs/wallet/balance-privacy-toggle.spec.ts
index 08e36bb8e1d..f4e9e4ca20b 100644
--- a/e2e/specs/wallet/balance-privacy-toggle.spec.ts
+++ b/e2e/specs/wallet/balance-privacy-toggle.spec.ts
@@ -5,6 +5,9 @@ import { loginToApp } from '../../viewHelper';
import WalletView from '../../pages/wallet/WalletView';
import TabBarComponent from '../../pages/wallet/TabBarComponent';
import Assertions from '../../framework/Assertions';
+import { LocalNode } from '../../framework/types';
+import { AnvilPort } from '../../framework/fixtures/FixtureUtils';
+import { AnvilManager } from '../../seeder/anvil-manager';
const EXPECTED_HIDDEN_BALANCE: string = '••••••••••••';
@@ -16,10 +19,26 @@ describe(RegressionWalletPlatform('Balance Privacy Toggle'), (): void => {
it('should toggle balance visibility when balance container is tapped', async (): Promise => {
await withFixtures(
{
- fixture: new FixtureBuilder()
- .withGanacheNetwork()
- .withETHAsPrimaryCurrency() // Set primary currency to ETH
- .build(),
+ fixture: ({ localNodes }: { localNodes?: LocalNode[] }) => {
+ const node = localNodes?.[0] as unknown as AnvilManager;
+ const rpcPort =
+ node instanceof AnvilManager
+ ? (node.getPort() ?? AnvilPort())
+ : undefined;
+
+ return new FixtureBuilder()
+ .withNetworkController({
+ providerConfig: {
+ chainId: '0x539',
+ rpcUrl: `http://localhost:${rpcPort ?? AnvilPort()}`,
+ type: 'custom',
+ nickname: 'Local RPC',
+ ticker: 'ETH',
+ },
+ })
+ .withETHAsPrimaryCurrency() // Set primary currency to ETH
+ .build();
+ },
restartDevice: true,
},
async (): Promise => {
diff --git a/e2e/specs/wallet/send-ERC-token.spec.ts b/e2e/specs/wallet/send-ERC-token.spec.ts
index 05dcd8d9f71..b68c4390261 100644
--- a/e2e/specs/wallet/send-ERC-token.spec.ts
+++ b/e2e/specs/wallet/send-ERC-token.spec.ts
@@ -16,6 +16,9 @@ import { setupRemoteFeatureFlagsMock } from '../../api-mocking/helpers/remoteFea
import { oldConfirmationsRemoteFeatureFlags } from '../../api-mocking/mock-responses/feature-flags-mocks';
import { SMART_CONTRACTS } from '../../../app/util/test/smart-contracts';
import { Mockttp } from 'mockttp';
+import { LocalNode } from '../../framework/types';
+import { AnvilPort } from '../../framework/fixtures/FixtureUtils';
+import { AnvilManager } from '../../seeder/anvil-manager';
const SEND_ADDRESS = '0xebe6CcB6B55e1d094d9c58980Bc10Fed69932cAb';
@@ -28,12 +31,28 @@ describe(RegressionWalletPlatform('Send ERC Token'), () => {
it('should send erc token successfully', async () => {
await withFixtures(
{
- fixture: new FixtureBuilder()
- .withGanacheNetwork()
- .withNetworkEnabledMap({
- eip155: { '0x539': true },
- })
- .build(),
+ fixture: ({ localNodes }: { localNodes?: LocalNode[] }) => {
+ const node = localNodes?.[0] as unknown as AnvilManager;
+ const rpcPort =
+ node instanceof AnvilManager
+ ? (node.getPort() ?? AnvilPort())
+ : undefined;
+
+ return new FixtureBuilder()
+ .withNetworkController({
+ providerConfig: {
+ chainId: '0x539',
+ rpcUrl: `http://localhost:${rpcPort ?? AnvilPort()}`,
+ type: 'custom',
+ nickname: 'Local RPC',
+ ticker: 'ETH',
+ },
+ })
+ .withNetworkEnabledMap({
+ eip155: { '0x539': true },
+ })
+ .build();
+ },
restartDevice: true,
smartContracts: [SMART_CONTRACTS.HST],
testSpecificMock: async (mockServer: Mockttp) => {