feat: make MoneyAccountUpgradeController own its bootstrap - #10072
feat: make MoneyAccountUpgradeController own its bootstrap#10072Jwhiles wants to merge 9 commits into
Conversation
|
@metamaskbot publish-preview |
1 similar comment
|
@metamaskbot publish-preview |
|
Preview builds have been published. Learn how to use preview builds in other projects. Expand for full list of packages and versions. |
|
Preview builds have been published. Learn how to use preview builds in other projects. Expand for full list of packages and versions. |
|
@metamaskbot publish-preview |
|
Preview builds have been published. Learn how to use preview builds in other projects. Expand for full list of packages and versions. |
MoneyAccountUpgradeController (core PR MetaMask/core#10072) now owns its bootstrap: it watches the feature-flag and keyring state, parses the vault config, and serializes its own re-inits. The extension supplies only the client-specific parts as hooks — the basic-functionality gate inside isEnabled, the fail-closed geo check as isEligible, the shared Money chain configurator, and Sentry reporting for a missing vault config — and MoneyAccountUpgradeService is deleted. Per review feedback, no messenger calls happen at construction: metamask-controller calls controller.init() once everything is built, alongside the other post-construction init() calls, so the init map no longer carries ordering constraints. Onboarding and preferences changes re-trigger the controller's gating through its public sync(). The package is pinned to a preview build until core releases @metamask/money-account-upgrade-controller@4.0.0 and @metamask/money-account-utils@1.2.0; the resolutions entries and the ^3.0.2 dependency swap to ^4.0.0 / ^1.2.0 then. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1ee21f9 to
4258cb1
Compare
4258cb1 to
81700ed
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 81700ed. Configure here.
|
@metamaskbot publish-preview |
2 similar comments
|
@metamaskbot publish-preview |
|
@metamaskbot publish-preview |
a302de5 to
6ec257f
Compare
|
@metamaskbot publish-preview |
The controller now subscribes to RemoteFeatureFlagController and KeyringController state, parses the moneyAccountVaultConfig flag, and runs its own serialized bootstrap, replacing the client-side services that previously drove init() externally. Client-specific concerns (version-gated enable flag, eligibility checks, adding the vault chain, error reporting) are supplied as constructor hooks. - BREAKING: init() is now the no-argument lifecycle entry point (subscribe and sync); the config-arming routine is internal and driven by the moneyAccountVaultConfig remote feature flag - BREAKING: the constructor requires a hooks option with an isEnabled hook - upgradeAccount() now waits for an in-flight bootstrap instead of throwing, and refuses to run against a stale config after the feature is disabled - Add vault-config parsing and comparison helpers to @metamask/money-account-utils, shared by the controller and both clients Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A disarm (isEnabled flipping off) or a newer scheduled config during the CHOMP service-details call now supersedes the in-flight run instead of the run arming a config the controller had already dropped. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Scheduling a bootstrap for a changed vault config now disarms the armed config, so upgradeAccount waits for the re-bootstrap (or refuses if it failed) instead of signing delegations against the old vault. The wait also follows runs chained onto the bootstrap while waiting, so a call that captured a superseded run no longer throws while its successor is still running. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Make `underlyingToken` optional in the shared vault-config parser so flags that predate the field still parse, matching the balance service - Re-check the armed config before every upgrade step and abort if it is disarmed or superseded mid-sequence - Refuse `upgradeAccount` while the wallet is locked - Contain a throwing `onBootstrapError` hook so the failed bootstrap is still retried and nothing escapes `init()` or `sync()` Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
ad38c64 to
ce71f7e
Compare
mcmire
left a comment
There was a problem hiding this comment.
I have one comment below, but everything else seems good to me.
| | ControllerStateChangedEvent<'KeyringController', KeyringControllerState> | ||
| | ControllerStateChangedEvent< | ||
| 'RemoteFeatureFlagController', | ||
| RemoteFeatureFlagControllerState | ||
| >; |
There was a problem hiding this comment.
Types already exist for these events, is it possible to use those?
| | ControllerStateChangedEvent<'KeyringController', KeyringControllerState> | |
| | ControllerStateChangedEvent< | |
| 'RemoteFeatureFlagController', | |
| RemoteFeatureFlagControllerState | |
| >; | |
| type AllowedEvents = | |
| | KeyringControllerStateChangeEvent | |
| | RemoteFeatureFlagControllerStateChangeEvent |
(You have may encountered a lint rule prohibiting use of :stateChange events but I plan on removing that.)

Explanation
This updates the money account upgrade controller to make it better able to handle its own initialisation. At the moment clients have to manage initialising the controller, leading to a lot of shared code. This PR tries to move that logic into the controller itself as much as possible
Key changes:
RemoteFeatureFlagController:stateChangedandKeyringController:stateChangedand uses this information to bootstrap itself.init()now takes no arguments, and merely sets up subscriptions to to some messenger events.hooksoption for the parts of the upgrade flow that can only be determined in the client.upgradeAccount()waits for an in-flight bootstrap instead of throwing (replacing mobile's module-levelwhenMoneyAccountUpgradeReadypromise), and is disarmed whenisEnabledflips off so it cannot run against a stale config.sync()lets clients re-trigger the bootstraps checks from client-only signals - for example onboarding being completed.@metamask/money-account-utils(getMoneyAccountVaultConfig,parseMoneyAccountVaultConfig,areMoneyAccountVaultConfigsEqual,MoneyAccountVaultConfig), shared by the controller and both clients.References
app/core/Engine/controllers/money-account-upgrade-controller-init.ts(follow-up PR required)Checklist
Note
High Risk
Breaking controller API and bootstrap timing affect wallet upgrade flows that sign EIP-7702 authorizations and delegations; incorrect gating or config supersession logic could block upgrades or sign against wrong vault parameters.
Overview
MoneyAccountUpgradeController now bootstraps itself instead of relying on clients to call
init({ chainId, boringVaultAddress }). After a no-arginit(), it listens to remote feature flags and keyring state, gates on clienthooks(isEnabled, optionalisEligible/ensureChainConfigured/onBootstrapError), parsesmoneyAccountVaultConfig, then fetches CHOMP service details to arm the upgrade config.Breaking API: constructor requires
hooks; messenger must allowRemoteFeatureFlagControllerandKeyringControllergetState + stateChange.upgradeAccount()waits for in-flight bootstrap, refuses when disarmed (feature off, failed bootstrap, locked wallet), and aborts mid-sequence if the vault config is superseded so nothing signs against a stale vault. Publicsync()re-runs gates for client-only signals (e.g. onboarding).Vault parsing moves to
@metamask/money-account-utils(getMoneyAccountVaultConfig,parseMoneyAccountVaultConfig,areMoneyAccountVaultConfigsEqual,MissingMoneyAccountVaultConfigErrorin the upgrade package). Tests and changelogs document serialization, re-bootstrap on config change, and error reporting.Reviewed by Cursor Bugbot for commit 5f5d356. Bugbot is set up for automated code reviews on this repo. Configure here.