feat(wallet)!: wire GasFeeController into default initialization - #9527
Conversation
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
@metamaskbot publish-previews |
|
Preview builds have been published. Learn how to use preview builds in other projects. Expand for full list of packages and versions. |
Construct the extension's GasFeeController through @metamask/wallet instead of the local messenger-client-init wiring, integrating MetaMask/core#9527. The wallet now owns GasFeeController as an instanceOptions.gasFeeController slot: it builds getProvider and getCurrentNetworkEIP1559Compatibility from NetworkController itself and lets the controller default onNetworkDidChange and getChainId via its messenger subscription. The extension supplies only the client-specific overrides (interval, clientId, gas API endpoints, and the BSC legacy gas API compatibility check). - Add wallet-init/instance-options/gas-fee-controller.ts and a minimal wallet-init/messengers/gas-fee-controller-messenger.ts (delegates only NetworkController:getState + getNetworkClientById for the chain-id read). - Wire gasFeeController into wallet-init/initialization.ts. - Resolve this.gasFeeController via this.wallet.getInstance('GasFeeController') and drop the GasFeeControllerInit import + init-map entry. - Delete the local confirmations/gas-fee-controller-init and messengers/gas-fee-controller-messenger (+tests) and their entries in messenger-client-init/messengers/index.ts. GasFeeController stays in controller-list.ts. Preview-pin @metamask/wallet and @metamask/gas-fee-controller to the core#9527 preview builds (7ed2770ae) until a wallet release ships it. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Construct the extension's GasFeeController through @metamask/wallet instead of the local messenger-client-init wiring, integrating MetaMask/core#9527. The wallet now owns GasFeeController as an instanceOptions.gasFeeController slot: it builds getProvider and getCurrentNetworkEIP1559Compatibility from NetworkController itself and lets the controller default onNetworkDidChange and getChainId via its messenger subscription. The extension supplies only the client-specific overrides (interval, clientId, gas API endpoints, and the BSC legacy gas API compatibility check). - Add wallet-init/instance-options/gas-fee-controller.ts and a minimal wallet-init/messengers/gas-fee-controller-messenger.ts (delegates only NetworkController:getState + getNetworkClientById for the chain-id read). - Wire gasFeeController into wallet-init/initialization.ts. - Resolve this.gasFeeController via this.wallet.getInstance('GasFeeController') and drop the GasFeeControllerInit import + init-map entry. - Delete the local confirmations/gas-fee-controller-init and messengers/gas-fee-controller-messenger (+tests) and their entries in messenger-client-init/messengers/index.ts. GasFeeController stays in controller-list.ts. Preview-pin @metamask/wallet and @metamask/gas-fee-controller to the core#9527 preview builds (7ed2770ae) until a wallet release ships it. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
rekmarks
left a comment
There was a problem hiding this comment.
Looks good! Just a couple of things.
Construct the extension's GasFeeController through @metamask/wallet instead of the local messenger-client-init wiring, integrating MetaMask/core#9527. The wallet now owns GasFeeController as an instanceOptions.gasFeeController slot: it builds getProvider and getCurrentNetworkEIP1559Compatibility from NetworkController itself and lets the controller default onNetworkDidChange and getChainId via its messenger subscription. The extension supplies only the client-specific overrides (interval, clientId, gas API endpoints, and the BSC legacy gas API compatibility check). - Add wallet-init/instance-options/gas-fee-controller.ts and a minimal wallet-init/messengers/gas-fee-controller-messenger.ts (delegates only NetworkController:getState + getNetworkClientById for the chain-id read). - Wire gasFeeController into wallet-init/initialization.ts. - Resolve this.gasFeeController via this.wallet.getInstance('GasFeeController') and drop the GasFeeControllerInit import + init-map entry. - Delete the local confirmations/gas-fee-controller-init and messengers/gas-fee-controller-messenger (+tests) and their entries in messenger-client-init/messengers/index.ts. GasFeeController stays in controller-list.ts. Preview-pin @metamask/wallet and @metamask/gas-fee-controller to the core#9527 preview builds (7ed2770ae) until a wallet release ships it. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Address review feedback on #9527: - Make `instanceOptions.gasFeeController.clientId` required and drop the `'cli'` default, so every client (extension, mobile, wallet-cli) must identify itself to the gas API rather than silently inheriting a default. `gasFeeController` is now a required instance-options key (mirroring `networkController`); wallet-cli passes `clientId: 'cli'` explicitly. - Throw a descriptive error naming the members of the cycle when `orderByDependencies` cannot satisfy the declared dependencies, instead of silently deferring to a later "handler not registered" messenger error. Also re-add the `gas-fee-controller` instance rule via the new programmatic `codeowners.ts` generator (#9529) picked up in the rebase onto main. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
7ed2770 to
ec8eeec
Compare
| * @returns The configurations ordered so that dependencies come first. | ||
| * @throws If the configurations contain a dependency cycle. | ||
| */ | ||
| export function orderByDependencies( |
There was a problem hiding this comment.
This is technically very cool! But logistically terrifying 😅
Should we ensure we don't migrate packages until they are initialisation-order-agnostic?
We had to do this for TransactionController for example.
I'd hope these dependencies are very rare, as we usually rely on legacy callbacks or dynamic messenger calls, rather than hard dependencies at constructor / initialisation time?
There was a problem hiding this comment.
Should we ensure we don't migrate packages until they are initialisation-order-agnostic?
We'll look into what it'd take to do this, but are we not already doing the equivalent of this in the clients?
There was a problem hiding this comment.
@matthewwalsh0 here is the controller change #9569
My review was addressed, holding approval pending resolution of other feedback.
ec8eeec to
ee084b8
Compare
Wire `GasFeeController` into `@metamask/wallet`'s default initialization set as its own `InitializationConfiguration`, mirroring the `transaction-controller` / `network-controller` instances. A wired `TransactionController` delegates `GasFeeController:fetchGasFeeEstimates` lazily, so the wallet boots today but throws the first time a transaction flow needs gas estimates; wiring `GasFeeController` supplies that handler. Adds a required `instanceOptions.gasFeeController` option whose `clientId` (sent as `X-Client-Id` to the gas API) is required, so every client identifies itself; all other fields are optional and fall back to platform-agnostic defaults. wallet-cli passes `clientId: 'cli'`. The instance builds `getProvider` / `getCurrentNetworkEIP1559Compatibility` as lazy closures over `NetworkController`, and `GasFeeController` was made initialization-order-agnostic upstream (#9569), so no dependency-ordering machinery is needed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ee084b8 to
baa4472
Compare
…metamask/wallet (MetaMask#33430) ## **Description** Bumps `@metamask/wallet` to **v8.1.0** and adopts the two controllers it newly wires into the default `Wallet` initialization set, continuing the shared controller-integration migration ([`@metamask/wallet`](https://github.com/MetaMask/core/tree/main/packages/wallet) is the layer that extension, mobile, and wallet-cli all share). **GasFeeController** (core [MetaMask#9527](MetaMask/core#9527)): - Adds `wallet-init/instance-options/gas-fee-controller.ts` (+ test) supplying the mobile-specific options: `clientId`, EIP-1559/legacy gas API endpoints, and `getCurrentNetworkLegacyGasAPICompatibility` (mainnet ‖ BSC ‖ Polygon). The wallet builds `getProvider` and `getCurrentNetworkEIP1559Compatibility` from `NetworkController` itself. - Deletes the local `gas-fee-controller-init` and `gas-fee-controller-messenger` (+ tests + barrels). - Resolves the instance via `this.#wallet.getInstance('GasFeeController')` in `Engine.ts`. **SeedlessOnboardingController** (core [MetaMask#9533](MetaMask/core#9533)) — required in the same PR because wallet@8 wires both; leaving the local init would cause a double-registration boot crash: - Adds `wallet-init/instance-options/seedless-onboarding-controller.ts` (+ test) supplying the mobile-specific options: encryptor `cipher`↔`data` adapter, JWT handlers via `AuthTokenHandler`, `web3AuthNetwork`, and `passwordOutdatedCacheTTL`. - Deletes the local `seedless-onboarding-controller-messenger` factory entry from `MESSENGER_FACTORIES`. - Updates E2E Metro mock to intercept `@metamask/seedless-onboarding-controller` at the **package** level (routing to `tests/module-mocking/seedless/package.ts`) instead of the former local controller path. - Resolves the instance via `this.#wallet.getInstance('SeedlessOnboardingController')` in `Engine.ts`. Both controllers are dropped from `MessengerClientsToInitialize` only; they remain in `MessengerClients` / `EngineState` / actions / events / `BACKGROUND_STATE_CHANGE_EVENT_NAMES`, matching the already-migrated controllers. ## **Changelog** CHANGELOG entry: null ## **Related issues** Refs: MetaMask/core#9527 Refs: MetaMask/core#9533 ## **Manual testing steps** 1. Build and launch the app on iOS or Android and confirm it boots with no `A handler for GasFeeController:* has already been registered` or `SeedlessOnboardingController:*` error. 2. Open the send/confirmation flow on an EIP-1559 network (e.g. Ethereum mainnet) and confirm gas fee estimates (low / market / aggressive) load and update. 3. Repeat on a legacy-gas network (e.g. BSC or Polygon) and confirm gas prices load. 4. Submit a transaction and confirm gas estimation drives the fee correctly. 5. On a build with seedless onboarding enabled, complete a social login flow and confirm vault encryption/decryption works end-to-end. ## **Screenshots/Recordings** N/A — internal controller-wiring refactor; no UI changes. ### **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. #### Performance checks (if applicable) - [x] I've tested on Android - Ideally on a mid-range device; emulator is acceptable - [x] I've tested with a power user scenario - Use these [power-user SRPs](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/edit-v2/401401446401?draftShareId=9d77e1e1-4bdc-4be1-9ebb-ccd916988d93) to import wallets with many accounts and tokens - [x] I've instrumented key operations with Sentry traces for production performance metrics - See [`trace()`](/app/util/trace.ts) for usage and [`addToken`](/app/components/Views/AddAsset/components/AddCustomToken/AddCustomToken.tsx#L274) for an example For performance guidelines and tooling, see the [Performance Guide](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/400085549067/Performance+Guide+for+Engineers). ## **Pre-merge reviewer checklist** - [ ] 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. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **High Risk** > Touches gas estimation for sends/confirmations and seedless vault encryption/onboarding—both security- and funds-sensitive—and changes controller lifecycle in a way that can cause boot failures if registration diverges. > > **Overview** > Bumps **`@metamask/wallet`** to **v8.1.0** (with **`@metamask/gas-fee-controller`** and **`@metamask/seedless-onboarding-controller`** bumps) and moves **GasFeeController** and **SeedlessOnboardingController** off mobile’s Engine messenger-client init path into the shared Wallet bootstrap. > > **GasFeeController** and **SeedlessOnboardingController** are no longer registered via local `*-init` modules or messenger factories. Mobile passes **`gasFeeController`** and **`seedlessOnboardingController`** **`instanceOptions`** from new **`wallet-init/instance-options/*`** builders (gas API endpoints, legacy-gas compatibility, seedless encryptor adapter, JWT handlers, Web3Auth network). **`Engine`** resolves both with **`this.#wallet.getInstance(...)`** instead of **`messengerClientsByName`**, avoiding double registration at boot. > > Local **`gas-fee-controller-init`**, **`gas-fee-controller-messenger`**, and the old seedless init/messenger wiring are removed; tests move to the instance-option modules. **CODEOWNERS** and E2E **Metro** mocks shift to **`@metamask/seedless-onboarding-controller`** at the package level because Wallet now constructs that controller. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 82f1fe5. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: Erik Marks <25517051+rekmarks@users.noreply.github.com>
…metamask/wallet (#33430) ## **Description** Bumps `@metamask/wallet` to **v8.1.0** and adopts the two controllers it newly wires into the default `Wallet` initialization set, continuing the shared controller-integration migration ([`@metamask/wallet`](https://github.com/MetaMask/core/tree/main/packages/wallet) is the layer that extension, mobile, and wallet-cli all share). **GasFeeController** (core [#9527](MetaMask/core#9527)): - Adds `wallet-init/instance-options/gas-fee-controller.ts` (+ test) supplying the mobile-specific options: `clientId`, EIP-1559/legacy gas API endpoints, and `getCurrentNetworkLegacyGasAPICompatibility` (mainnet ‖ BSC ‖ Polygon). The wallet builds `getProvider` and `getCurrentNetworkEIP1559Compatibility` from `NetworkController` itself. - Deletes the local `gas-fee-controller-init` and `gas-fee-controller-messenger` (+ tests + barrels). - Resolves the instance via `this.#wallet.getInstance('GasFeeController')` in `Engine.ts`. **SeedlessOnboardingController** (core [#9533](MetaMask/core#9533)) — required in the same PR because wallet@8 wires both; leaving the local init would cause a double-registration boot crash: - Adds `wallet-init/instance-options/seedless-onboarding-controller.ts` (+ test) supplying the mobile-specific options: encryptor `cipher`↔`data` adapter, JWT handlers via `AuthTokenHandler`, `web3AuthNetwork`, and `passwordOutdatedCacheTTL`. - Deletes the local `seedless-onboarding-controller-messenger` factory entry from `MESSENGER_FACTORIES`. - Updates E2E Metro mock to intercept `@metamask/seedless-onboarding-controller` at the **package** level (routing to `tests/module-mocking/seedless/package.ts`) instead of the former local controller path. - Resolves the instance via `this.#wallet.getInstance('SeedlessOnboardingController')` in `Engine.ts`. Both controllers are dropped from `MessengerClientsToInitialize` only; they remain in `MessengerClients` / `EngineState` / actions / events / `BACKGROUND_STATE_CHANGE_EVENT_NAMES`, matching the already-migrated controllers. ## **Changelog** CHANGELOG entry: null ## **Related issues** Refs: MetaMask/core#9527 Refs: MetaMask/core#9533 ## **Manual testing steps** 1. Build and launch the app on iOS or Android and confirm it boots with no `A handler for GasFeeController:* has already been registered` or `SeedlessOnboardingController:*` error. 2. Open the send/confirmation flow on an EIP-1559 network (e.g. Ethereum mainnet) and confirm gas fee estimates (low / market / aggressive) load and update. 3. Repeat on a legacy-gas network (e.g. BSC or Polygon) and confirm gas prices load. 4. Submit a transaction and confirm gas estimation drives the fee correctly. 5. On a build with seedless onboarding enabled, complete a social login flow and confirm vault encryption/decryption works end-to-end. ## **Screenshots/Recordings** N/A — internal controller-wiring refactor; no UI changes. ### **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. #### Performance checks (if applicable) - [x] I've tested on Android - Ideally on a mid-range device; emulator is acceptable - [x] I've tested with a power user scenario - Use these [power-user SRPs](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/edit-v2/401401446401?draftShareId=9d77e1e1-4bdc-4be1-9ebb-ccd916988d93) to import wallets with many accounts and tokens - [x] I've instrumented key operations with Sentry traces for production performance metrics - See [`trace()`](/app/util/trace.ts) for usage and [`addToken`](/app/components/Views/AddAsset/components/AddCustomToken/AddCustomToken.tsx#L274) for an example For performance guidelines and tooling, see the [Performance Guide](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/400085549067/Performance+Guide+for+Engineers). ## **Pre-merge reviewer checklist** - [ ] 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. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **High Risk** > Touches gas estimation for sends/confirmations and seedless vault encryption/onboarding—both security- and funds-sensitive—and changes controller lifecycle in a way that can cause boot failures if registration diverges. > > **Overview** > Bumps **`@metamask/wallet`** to **v8.1.0** (with **`@metamask/gas-fee-controller`** and **`@metamask/seedless-onboarding-controller`** bumps) and moves **GasFeeController** and **SeedlessOnboardingController** off mobile’s Engine messenger-client init path into the shared Wallet bootstrap. > > **GasFeeController** and **SeedlessOnboardingController** are no longer registered via local `*-init` modules or messenger factories. Mobile passes **`gasFeeController`** and **`seedlessOnboardingController`** **`instanceOptions`** from new **`wallet-init/instance-options/*`** builders (gas API endpoints, legacy-gas compatibility, seedless encryptor adapter, JWT handlers, Web3Auth network). **`Engine`** resolves both with **`this.#wallet.getInstance(...)`** instead of **`messengerClientsByName`**, avoiding double registration at boot. > > Local **`gas-fee-controller-init`**, **`gas-fee-controller-messenger`**, and the old seedless init/messenger wiring are removed; tests move to the instance-option modules. **CODEOWNERS** and E2E **Metro** mocks shift to **`@metamask/seedless-onboarding-controller`** at the package level because Wallet now constructs that controller. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 82f1fe5. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: Erik Marks <25517051+rekmarks@users.noreply.github.com>
Rebasing #9300 onto `main` after five weeks of drift required these adjustments, none of which change the feature being wired: - `codeowners.ts` is now the source of truth for `.github/CODEOWNERS`, which is generated. `initializationPath` accepts an array so `permission-controller` can claim both of the instance directories it supplies, and rules are sorted by pattern so the generated section stays alphabetical. - `Wallet` tests pass the `gasFeeController` instance option, required since #9527. - Regenerate `README.md` and both `tsconfig`s via their fixers. - Repair the `[6.0.0]` changelog section, which a conflict resolution had left with duplicated content. - Spell out the client-facing consequences of the wiring in the changelog: the duplicate-registration collision for consumers passing their own messenger, and the fact that permission specifications with side effects (the Snaps specifications reach `SnapController:*`) need a wider delegation allowlist than the default configuration provides. - Cover `caveatSpecifications` injection, the one instance option without a behavioural assertion. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…eController from wallet (#45246) This PR continues the migration of controller construction into the shared `@metamask/wallet` integration layer. It upgrades `@metamask/wallet` from 7.0.1 to 8.1.0 and `@metamask/passkey-controller` from 2.0.1 to 3.0.0, then sources `PasskeyController` and `SeedlessOnboardingController` from the wallet instead of the extension's local messenger client initialization. The extension now supplies only its platform specific controller options: - Passkey relying party details, extension origin, browser platform, and onboarding state. - Seedless onboarding network, encryption, and OAuth token callbacks. - Gas fee API endpoints, polling interval, client ID, and legacy network compatibility. Passkey vault orchestration is delegated to `PasskeyController`, covering enrollment, unlock, removal, and vault key renewal on password change. The legacy background API exposes the required wallet owned controller actions while preserving existing UI call sites and error translation. This PR also incorporates the `GasFeeController` wallet migration. The extension's local GasFee initialization and messenger factories are removed, and consumers now resolve the wallet owned instance. Tests were added or updated for wallet option wiring, controller delegation, passkey errors, and legacy background API behavior. Upstream changes: - MetaMask/core#9533 - MetaMask/core#9527 CHANGELOG entry: null <!-- Fixes: --> 1. Configure a development build with `PASSKEY_ENABLED=true` and `SEEDLESS_ONBOARDING_ENABLED=true`, including the required OAuth development configuration. 2. Load the extension and complete the standard SRP onboarding flow. Lock and unlock the wallet with the password to verify the normal flow still works. 3. Open **Settings > Security & privacy**, set up a passkey, lock MetaMask, and verify that the wallet unlocks with the registered passkey. 4. With a passkey enrolled, change the wallet password and verify that the new password works and passkey unlock continues to work. 5. Remove the passkey using passkey verification, then verify that passkey unlock is no longer offered. 6. Reset the extension and create or import a wallet using Google or Apple social login. Lock and unlock the wallet and verify the social login wallet remains accessible. 7. Submit a transaction on a supported EVM network and verify that gas fee estimates load and the transaction can be confirmed. <!-- --> - [ ] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/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-extension/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. - [x] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [x] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **High Risk** > Touches passkey enrollment, vault unlock, password change, and seedless OAuth flows with a large refactor of how controllers are constructed and delegated. > > **Overview** > Upgrades **`@metamask/wallet`** to **8.1.0** and **`@metamask/passkey-controller`** to **3.0.0**, then moves **`PasskeyController`**, **`SeedlessOnboardingController`**, and **`GasFeeController`** out of extension messenger-client init and into wallet initialization via new **`wallet-init`** instance options (extension origin/RP, OAuth callbacks, gas API endpoints, BSC legacy compatibility). > > **`MetaMaskController`** resolves those controllers with **`wallet.getInstance()`**, drops the large inline passkey/vault helpers, and routes the public API through messenger actions. Unlock and passkey-gated password change go through **`LegacyBackgroundApiService`** so post-unlock account init and **`seedlessOperationMutex`** serialization stay extension-specific. > > Local gas/passkey/seedless init modules, messenger factories, and related tests are removed; UI/redux call sites adopt object-shaped params and **`PasskeyControllerErrorCode`** (replacing **`ExtensionPasskeyErrorCode`**). LavaMoat policies are updated for the expanded wallet dependency graph. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 4215648. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: lwin <lwin.kyaw@consensys.net> Co-authored-by: MetaMask Bot <metamaskbot@users.noreply.github.com>
…eController from wallet (MetaMask#45246) ## **Description** This PR continues the migration of controller construction into the shared `@metamask/wallet` integration layer. It upgrades `@metamask/wallet` from 7.0.1 to 8.1.0 and `@metamask/passkey-controller` from 2.0.1 to 3.0.0, then sources `PasskeyController` and `SeedlessOnboardingController` from the wallet instead of the extension's local messenger client initialization. The extension now supplies only its platform specific controller options: - Passkey relying party details, extension origin, browser platform, and onboarding state. - Seedless onboarding network, encryption, and OAuth token callbacks. - Gas fee API endpoints, polling interval, client ID, and legacy network compatibility. Passkey vault orchestration is delegated to `PasskeyController`, covering enrollment, unlock, removal, and vault key renewal on password change. The legacy background API exposes the required wallet owned controller actions while preserving existing UI call sites and error translation. This PR also incorporates the `GasFeeController` wallet migration. The extension's local GasFee initialization and messenger factories are removed, and consumers now resolve the wallet owned instance. Tests were added or updated for wallet option wiring, controller delegation, passkey errors, and legacy background API behavior. Upstream changes: - MetaMask/core#9533 - MetaMask/core#9527 ## **Changelog** CHANGELOG entry: null <!-- ## **Related issues** Fixes: --> ## **Manual testing steps** 1. Configure a development build with `PASSKEY_ENABLED=true` and `SEEDLESS_ONBOARDING_ENABLED=true`, including the required OAuth development configuration. 2. Load the extension and complete the standard SRP onboarding flow. Lock and unlock the wallet with the password to verify the normal flow still works. 3. Open **Settings > Security & privacy**, set up a passkey, lock MetaMask, and verify that the wallet unlocks with the registered passkey. 4. With a passkey enrolled, change the wallet password and verify that the new password works and passkey unlock continues to work. 5. Remove the passkey using passkey verification, then verify that passkey unlock is no longer offered. 6. Reset the extension and create or import a wallet using Google or Apple social login. Lock and unlock the wallet and verify the social login wallet remains accessible. 7. Submit a transaction on a supported EVM network and verify that gas fee estimates load and the transaction can be confirmed. <!-- ## **Screenshots/Recordings** ### **Before** ### **After** --> ## **Pre-merge author checklist** - [ ] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/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-extension/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. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **High Risk** > Touches passkey enrollment, vault unlock, password change, and seedless OAuth flows with a large refactor of how controllers are constructed and delegated. > > **Overview** > Upgrades **`@metamask/wallet`** to **8.1.0** and **`@metamask/passkey-controller`** to **3.0.0**, then moves **`PasskeyController`**, **`SeedlessOnboardingController`**, and **`GasFeeController`** out of extension messenger-client init and into wallet initialization via new **`wallet-init`** instance options (extension origin/RP, OAuth callbacks, gas API endpoints, BSC legacy compatibility). > > **`MetaMaskController`** resolves those controllers with **`wallet.getInstance()`**, drops the large inline passkey/vault helpers, and routes the public API through messenger actions. Unlock and passkey-gated password change go through **`LegacyBackgroundApiService`** so post-unlock account init and **`seedlessOperationMutex`** serialization stay extension-specific. > > Local gas/passkey/seedless init modules, messenger factories, and related tests are removed; UI/redux call sites adopt object-shaped params and **`PasskeyControllerErrorCode`** (replacing **`ExtensionPasskeyErrorCode`**). LavaMoat policies are updated for the expanded wallet dependency graph. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 4215648. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: lwin <lwin.kyaw@consensys.net> Co-authored-by: MetaMask Bot <metamaskbot@users.noreply.github.com>
Explanation
Wires
GasFeeControllerinto@metamask/wallet's default initialization set as its ownInitializationConfigurationundersrc/initialization/instances/gas-fee-controller/, mirroring the most recently merged instances (transaction-controller,network-controller).TransactionControlleris already wired and its messenger delegatesGasFeeController:fetchGasFeeEstimates. Delegation registers a lazy handler, so the wallet boots fine today — but the first transaction flow that needs gas estimates throwsA handler for GasFeeController:fetchGasFeeEstimates has not been registered. WiringGasFeeControlleris the missing piece that lets a wiredTransactionControlleractually estimate gas.@metamask/walletis the shared controller-integration layer thatmetamask-extension,metamask-mobile, and@metamask/wallet-cliall adopt, so every value that differs between clients is an injectableinstanceOptions.gasFeeControllerslot with a platform-agnostic default — nothing is baked to one client.Constructor callbacks
GasFeeControllertakes direct callbacks rather than pure messenger delegation. The instance builds them from the wiredNetworkController:getProvider→NetworkController:getState(selectedNetworkClientId) thenNetworkController:getNetworkClientById(id).providergetCurrentNetworkEIP1559Compatibility→NetworkController:getEIP1559Compatibility(coerced to a definedboolean)onNetworkDidChange/getChainIdare omitted — the constructor already has a messenger-based network-tracking fallback when both are absent (subscribesNetworkController:networkDidChange).Injectable options + per-environment values
EIP1559APIEndpoint.../networks/<chain_id>/suggestedGasFees(dev override →gas.uat-api.cx.metamask.io)legacyAPIEndpoint${GAS_API}/networks/<chain_id>/gasPricesclientId'extension''mobile''cli'(sent asX-Client-Id); injectableinterval10_00015_00015_000; injectablegetCurrentNetworkLegacyGasAPICompatibilitychainId === BSCmainnet || BSC || POLYGON() => false; injectablegetCurrentAccountEIP1559Compatibility() => true() => true; injectableInitialization ordering
No construction-ordering machinery is needed.
GasFeeControllerwas made initialization-order-agnostic upstream (#9569): the constructor no longer eagerly resolvesNetworkController(thegetProvider/getCurrentNetworkEIP1559Compatibilitycallbacks resolve it lazily at call time), so it can be constructed beforeNetworkController.initializetherefore constructs the default set in its natural order, and thedependenciesfield /orderByDependenciessort that an earlier revision of this PR added were dropped in favor of that upstream refactor.fetchdecisionGasFeeControllerfetches gas estimates via the globalfetchinside@metamask/gas-fee-controller(it has no injectablefetchoption). This PR takes path (a): accept the globalfetch— no upstream change; works on Node 18+ (wallet-cli daemon), modern browsers, and React Native. The global-fetchusage lives inside the controller package, not inside@metamask/walletcode, and both clients already rely on this today. Adding an injectablefetchoption to@metamask/gas-fee-controlleris tracked as a possible follow-up if the convention is to be enforced strictly.Client adoption PRs
References
packages/gas-fee-controller/src/GasFeeController.tspackages/wallet/src/initialization/instances/transaction-controller/app/scripts/messenger-client-init/confirmations/gas-fee-controller-init.tsapp/core/Engine/controllers/gas-fee-controller/gas-fee-controller-init.tsRelated
GasFeeControllerinto@metamask/walletdefault initialization #9510Checklist
🤖 Generated with Claude Code
Note
Medium Risk
Breaking API for all Wallet consumers and changes the transaction gas-estimation path, but the integration follows existing controller init patterns and includes dedicated tests.
Overview
Breaking:
@metamask/walletnow bootsGasFeeControlleras part of default initialization, so wiredTransactionControllerflows can callGasFeeController:fetchGasFeeEstimatesinstead of failing with a missing handler.Consumers must pass
instanceOptions.gasFeeControllerwith a requiredclientId(sent asX-Client-Idto the gas API); other gas settings stay optional with shared defaults (production API URLs, network callbacks built fromNetworkController).@metamask/wallet-clisetsclientId: 'cli'on the daemon wallet. Extension/mobile adoption is expected in separate PRs.Reviewed by Cursor Bugbot for commit baa4472. Bugbot is set up for automated code reviews on this repo. Configure here.