Skip to content

feat(wallet)!: wire GasFeeController into default initialization#9527

Open
sirtimid wants to merge 2 commits into
mainfrom
sirtimid/wire-gas-fee-controller
Open

feat(wallet)!: wire GasFeeController into default initialization#9527
sirtimid wants to merge 2 commits into
mainfrom
sirtimid/wire-gas-fee-controller

Conversation

@sirtimid

@sirtimid sirtimid commented Jul 15, 2026

Copy link
Copy Markdown
Member

Explanation

Wires GasFeeController into @metamask/wallet's default initialization set as its own InitializationConfiguration under src/initialization/instances/gas-fee-controller/, mirroring the most recently merged instances (transaction-controller, network-controller).

TransactionController is already wired and its messenger delegates GasFeeController:fetchGasFeeEstimates. Delegation registers a lazy handler, so the wallet boots fine today — but the first transaction flow that needs gas estimates throws A handler for GasFeeController:fetchGasFeeEstimates has not been registered. Wiring GasFeeController is the missing piece that lets a wired TransactionController actually estimate gas.

@metamask/wallet is the shared controller-integration layer that metamask-extension, metamask-mobile, and @metamask/wallet-cli all adopt, so every value that differs between clients is an injectable instanceOptions.gasFeeController slot with a platform-agnostic default — nothing is baked to one client.

Constructor callbacks

GasFeeController takes direct callbacks rather than pure messenger delegation. The instance builds them from the wired NetworkController:

  • getProviderNetworkController:getState (selectedNetworkClientId) then NetworkController:getNetworkClientById(id).provider
  • getCurrentNetworkEIP1559CompatibilityNetworkController:getEIP1559Compatibility (coerced to a defined boolean)

onNetworkDidChange / getChainId are omitted — the constructor already has a messenger-based network-tracking fallback when both are absent (subscribes NetworkController:networkDidChange).

Injectable options + per-environment values

Option Extension Mobile Default (wallet-cli / package)
EIP1559APIEndpoint .../networks/<chain_id>/suggestedGasFees (dev override → gas.uat-api.cx.metamask.io) same prod URL prod URL; injectable
legacyAPIEndpoint ${GAS_API}/networks/<chain_id>/gasPrices same prod URL prod URL; injectable
clientId 'extension' 'mobile' 'cli' (sent as X-Client-Id); injectable
interval 10_000 15_000 controller default 15_000; injectable
getCurrentNetworkLegacyGasAPICompatibility chainId === BSC mainnet || BSC || POLYGON () => false; injectable
getCurrentAccountEIP1559Compatibility () => true omitted () => true; injectable

Initialization ordering

GasFeeController's constructor eagerly reads NetworkController state (to build its provider), so NetworkController must be constructed first. The default set was previously constructed in the alphabetical order of Object.values(defaultConfigurations), which happened to satisfy every existing dependency (e.g. network < transaction) — but gas < network breaks that, causing a A handler for NetworkController:getState has not been registered throw at construction.

To fix this generally, this PR adds an optional dependencies field to InitializationConfiguration and a stable topological sort (orderByDependencies) in initialize. gasFeeController declares dependencies: ['NetworkController']. Instances without an unmet dependency keep their relative order, and dependencies outside the set are treated as already satisfied.

fetch decision

GasFeeController fetches gas estimates via the global fetch inside @metamask/gas-fee-controller (it has no injectable fetch option). This PR takes path (a): accept the global fetch — no upstream change; works on Node 18+ (wallet-cli daemon), modern browsers, and React Native. The global-fetch usage lives inside the controller package, not inside @metamask/wallet code, and both clients already rely on this today. Adding an injectable fetch option to @metamask/gas-fee-controller is tracked as a possible follow-up if the convention is to be enforced strictly.

Client adoption PRs

References

Related

Checklist

  • I've read the contributing guidelines.
  • I've completed the PR template to the best of my ability.
  • I've included tests if applicable (100% coverage; colocated instance test + orderByDependencies unit test).
  • I've documented my code using JSDoc format if applicable.
  • I've applied the right labels on the PR (see labeling guidelines). Not required for external contributors.

🤖 Generated with Claude Code


Note

Medium Risk
Touches transaction/gas estimation boot order and is a breaking wallet release; duplicate client-side GasFeeController wiring could collide on messenger registration.

Overview
Breaking: @metamask/wallet now constructs GasFeeController in its default initialization set, so GasFeeController:fetchGasFeeEstimates is registered on the shared messenger instead of failing when transaction flows need gas estimates.

A new gas-fee-controller initialization module wires the controller to NetworkController (provider and EIP-1559 compatibility via messenger delegation) and exposes optional instanceOptions.gasFeeController overrides (gas API URLs, clientId, poll interval, compatibility callbacks) with wallet-cli-oriented defaults.

Initialization no longer relies on incidental object key order: InitializationConfiguration gains optional dependencies, and orderByDependencies topologically sorts configs before construction—GasFeeController declares NetworkController as a dependency because its constructor reads network state eagerly.

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

sirtimid and others added 2 commits July 15, 2026 20:07
Wire `GasFeeController` into the wallet's default set as its own
`InitializationConfiguration`, with all client-varying values injectable
via `instanceOptions.gasFeeController` and platform-agnostic defaults.

This resolves the lazy `GasFeeController:fetchGasFeeEstimates` delegation
already registered by the wired `TransactionController`.

Because `GasFeeController`'s constructor eagerly reads `NetworkController`
state, add an optional `dependencies` field to `InitializationConfiguration`
and a stable topological sort in `initialize` so dependencies are always
constructed first (the default set was otherwise ordered alphabetically).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@sirtimid sirtimid requested a review from a team July 15, 2026 18:13
@sirtimid

Copy link
Copy Markdown
Member Author

@metamaskbot publish-previews

@github-actions

Copy link
Copy Markdown
Contributor

Preview builds have been published. Learn how to use preview builds in other projects.

Expand for full list of packages and versions.
@metamask-previews/account-tree-controller@7.5.5-preview-7ed2770ae
@metamask-previews/accounts-controller@39.0.5-preview-7ed2770ae
@metamask-previews/address-book-controller@7.1.2-preview-7ed2770ae
@metamask-previews/ai-controllers@0.8.0-preview-7ed2770ae
@metamask-previews/analytics-controller@1.2.1-preview-7ed2770ae
@metamask-previews/analytics-data-regulation-controller@0.0.0-preview-7ed2770ae
@metamask-previews/announcement-controller@8.1.0-preview-7ed2770ae
@metamask-previews/app-metadata-controller@2.0.1-preview-7ed2770ae
@metamask-previews/approval-controller@9.0.2-preview-7ed2770ae
@metamask-previews/assets-controller@11.0.0-preview-7ed2770ae
@metamask-previews/assets-controllers@109.4.1-preview-7ed2770ae
@metamask-previews/authenticated-user-storage@3.0.1-preview-7ed2770ae
@metamask-previews/base-controller@9.1.0-preview-7ed2770ae
@metamask-previews/base-data-service@0.1.3-preview-7ed2770ae
@metamask-previews/bitcoin-regtest-up@1.0.0-preview-7ed2770ae
@metamask-previews/bridge-controller@77.5.0-preview-7ed2770ae
@metamask-previews/bridge-status-controller@74.2.0-preview-7ed2770ae
@metamask-previews/build-utils@3.0.4-preview-7ed2770ae
@metamask-previews/chain-agnostic-permission@1.7.0-preview-7ed2770ae
@metamask-previews/chomp-api-service@3.1.0-preview-7ed2770ae
@metamask-previews/claims-controller@0.5.3-preview-7ed2770ae
@metamask-previews/client-controller@1.0.1-preview-7ed2770ae
@metamask-previews/client-utils@1.1.0-preview-7ed2770ae
@metamask-previews/compliance-controller@2.1.0-preview-7ed2770ae
@metamask-previews/composable-controller@12.0.1-preview-7ed2770ae
@metamask-previews/config-registry-controller@0.4.1-preview-7ed2770ae
@metamask-previews/connectivity-controller@0.3.0-preview-7ed2770ae
@metamask-previews/controller-utils@12.3.0-preview-7ed2770ae
@metamask-previews/core-backend@6.5.0-preview-7ed2770ae
@metamask-previews/delegation-controller@3.0.2-preview-7ed2770ae
@metamask-previews/earn-controller@12.2.2-preview-7ed2770ae
@metamask-previews/eip-5792-middleware@3.0.5-preview-7ed2770ae
@metamask-previews/eip-7702-internal-rpc-middleware@0.1.1-preview-7ed2770ae
@metamask-previews/eip1193-permission-middleware@2.0.1-preview-7ed2770ae
@metamask-previews/ens-controller@19.1.5-preview-7ed2770ae
@metamask-previews/eth-block-tracker@15.0.1-preview-7ed2770ae
@metamask-previews/eth-json-rpc-middleware@23.1.3-preview-7ed2770ae
@metamask-previews/eth-json-rpc-provider@6.0.1-preview-7ed2770ae
@metamask-previews/foundryup@1.0.1-preview-7ed2770ae
@metamask-previews/gas-fee-controller@26.2.4-preview-7ed2770ae
@metamask-previews/gator-permissions-controller@4.2.3-preview-7ed2770ae
@metamask-previews/geolocation-controller@0.1.3-preview-7ed2770ae
@metamask-previews/java-tron-up@1.0.0-preview-7ed2770ae
@metamask-previews/json-rpc-engine@10.5.0-preview-7ed2770ae
@metamask-previews/json-rpc-middleware-stream@8.0.8-preview-7ed2770ae
@metamask-previews/keyring-controller@27.1.0-preview-7ed2770ae
@metamask-previews/local-node-utils@1.0.0-preview-7ed2770ae
@metamask-previews/logging-controller@8.0.2-preview-7ed2770ae
@metamask-previews/message-manager@14.1.2-preview-7ed2770ae
@metamask-previews/messenger@2.0.0-preview-7ed2770ae
@metamask-previews/messenger-cli@0.2.0-preview-7ed2770ae
@metamask-previews/money-account-api-data-service@0.1.0-preview-7ed2770ae
@metamask-previews/money-account-balance-service@2.2.0-preview-7ed2770ae
@metamask-previews/money-account-controller@0.3.3-preview-7ed2770ae
@metamask-previews/money-account-upgrade-controller@2.2.1-preview-7ed2770ae
@metamask-previews/multichain-account-service@13.0.0-preview-7ed2770ae
@metamask-previews/multichain-api-middleware@4.0.1-preview-7ed2770ae
@metamask-previews/multichain-network-controller@3.2.1-preview-7ed2770ae
@metamask-previews/multichain-transactions-controller@7.1.1-preview-7ed2770ae
@metamask-previews/name-controller@9.1.2-preview-7ed2770ae
@metamask-previews/network-connection-banner-controller@0.1.0-preview-7ed2770ae
@metamask-previews/network-controller@34.0.0-preview-7ed2770ae
@metamask-previews/network-enablement-controller@5.6.0-preview-7ed2770ae
@metamask-previews/notification-services-controller@25.0.0-preview-7ed2770ae
@metamask-previews/passkey-controller@2.1.0-preview-7ed2770ae
@metamask-previews/permission-controller@13.1.1-preview-7ed2770ae
@metamask-previews/permission-log-controller@5.1.0-preview-7ed2770ae
@metamask-previews/perps-controller@9.2.1-preview-7ed2770ae
@metamask-previews/phishing-controller@17.2.1-preview-7ed2770ae
@metamask-previews/platform-api-docs@0.0.0-preview-7ed2770ae
@metamask-previews/polling-controller@16.0.8-preview-7ed2770ae
@metamask-previews/preferences-controller@23.1.0-preview-7ed2770ae
@metamask-previews/profile-metrics-controller@4.0.2-preview-7ed2770ae
@metamask-previews/profile-sync-controller@28.3.0-preview-7ed2770ae
@metamask-previews/ramps-controller@17.0.0-preview-7ed2770ae
@metamask-previews/rate-limit-controller@7.0.1-preview-7ed2770ae
@metamask-previews/react-data-query@0.2.1-preview-7ed2770ae
@metamask-previews/remote-feature-flag-controller@4.2.2-preview-7ed2770ae
@metamask-previews/sample-controllers@5.0.3-preview-7ed2770ae
@metamask-previews/seedless-onboarding-controller@10.0.3-preview-7ed2770ae
@metamask-previews/selected-network-controller@26.1.5-preview-7ed2770ae
@metamask-previews/sentinel-api-service@1.0.0-preview-7ed2770ae
@metamask-previews/shield-controller@5.1.3-preview-7ed2770ae
@metamask-previews/signature-controller@39.2.7-preview-7ed2770ae
@metamask-previews/smart-transactions-controller@25.0.1-preview-7ed2770ae
@metamask-previews/snap-account-service@2.0.0-preview-7ed2770ae
@metamask-previews/social-controllers@2.5.0-preview-7ed2770ae
@metamask-previews/solana-test-validator-up@1.0.0-preview-7ed2770ae
@metamask-previews/stellar-quickstart-up@0.0.0-preview-7ed2770ae
@metamask-previews/storage-service@1.0.2-preview-7ed2770ae
@metamask-previews/subscription-controller@6.2.1-preview-7ed2770ae
@metamask-previews/transaction-controller@69.0.0-preview-7ed2770ae
@metamask-previews/transaction-pay-controller@25.0.0-preview-7ed2770ae
@metamask-previews/user-operation-controller@41.2.7-preview-7ed2770ae
@metamask-previews/wallet@7.0.1-preview-7ed2770ae
@metamask-previews/wallet-cli@0.0.0-preview-7ed2770ae

sirtimid added a commit to MetaMask/metamask-extension that referenced this pull request Jul 16, 2026
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>
@sirtimid sirtimid marked this pull request as ready for review July 16, 2026 15:14
@sirtimid sirtimid requested a review from a team as a code owner July 16, 2026 15:14
@sirtimid sirtimid temporarily deployed to default-branch July 16, 2026 15:18 — with GitHub Actions Inactive
sirtimid added a commit to MetaMask/metamask-extension that referenced this pull request Jul 16, 2026
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Wire GasFeeController into @metamask/wallet default initialization

1 participant