MetaMask / MetaMask/metamask-extension
[State Sync Redesign] Epic 2: Background cleanup
- Dominant language
- TypeScript
- Stars
- 13.2k
- Forks
- 5.6k
- Avg merge
- 2d 5h
- Merged PRs (30d)
- 451
Description
Modernize MetamaskController's state delivery path: replace `ComposableObservableStore` and the `@metamask/obs-store` dependency with direct messenger subscriptions. Completes the BaseController V2 migration on the background side for controller `stateChange` routing — these layers were necessary when controllers had no standardized state management but have been redundant since V2. `MetamaskController` retains its `EventEmitter` base for events emitted during early app load before controllers and their messengers are guaranteed to be initialized.
## Key Benefits
- **Completes BaseController V2 migration for controller `stateChange` routing**: removes 4 legacy V1-era broadcast layers
- **MetamaskController state-broadcast cleanup**: `updateStructure` x2, `ComposableObservableStore` x2, `PatchStore`, `publicConfigStore` removed
- **Zero-touch controller addition**: 6 manual steps to 2; `ControllerRegistry` becomes canonical registry
- **3 bug classes eliminated**: unbounded UI starvation (debounce with no `maxWait`), listener leaks (per-controller subscriptions without cleanup accounting), untyped event subscriptions (silent mismatches)
- **`forceUpdateMetamaskState` RPC elimination**: replace full-state fetch implementation with lightweight sync primitive that waits for next push update (~109 round-trips removed)
- **Remove `@metamask/obs-store` dependency** (~8KB bundle reduction)
## ADR Option
A1 (background cleanup)
## Phase
A1 (background cleanup) — together with Epic 1
## Dependencies
Epic 1 (transport must not go through memStore; per-controller messenger subscriptions in `setupControllerConnection` replace `memStore.subscribe(sendUpdate)`)
## Enables
Epic 3 (A2 phase — UI-controller subscriptions). Epic 3 depends on both Epic 1 and Epic 2 completing (A1 phase complete).
## Blocked by
Epic 3 only for Issue 2.7 (`@metamask/obs-store` removal requires no Redux consumers via obs-store)
## Issues
- [ ] 2.1: Migrate MetaMetrics to direct messenger subscriptions — #40052
- [ ] 2.2: Migrate `_onStateUpdate` to direct messenger subscriptions — #40053
- [ ] 2.3: Simplify `getState()` to iterate `ControllerRegistry` — #40054
- [ ] 2.4: Replace `this.memStore` with direct messenger subscriptions — #40055
- [ ] 2.5: Replace `this.store` with direct persistence subscriptions — #40056
- [ ] 2.7: Remove `@metamask/obs-store` dependency — #40058
- [ ] 2.8: Replace `forceUpdateMetamaskState` implementation with push-based sync primitive — TBD
## Critical path
2.1 → 2.4 → 2.7
2.8 depends on Epic 1 (reliable push pipeline)
## Validation criteria
- [ ] Persistence works across extension restarts (2.5 — E2E)
- [ ] Vault backup/restore works (2.5 — E2E)
- [ ] MetaMetrics traits still reported correctly (2.1)
- [ ] Dapp provider state notifications still work (2.2, `_notifyChainChange`)
- [ ] `@metamask/obs-store` not in `node_modules` (2.7)
- [ ] `forceUpdateMetamaskState` no longer makes background RPC call (2.8)
- [ ] No regression in E2E test suite
Contributor guide
Research direction
Start with MetamaskController, ControllerRegistry, and setupControllerConnection, then review the linked subtasks for _onStateUpdate, memStore, store, and forceUpdateMetamaskState. Done means the listed background cleanup is complete, persistence and state notifications still work, and the validation criteria—including removal of @metamask/obs-store and no regression in E2E tests—are satisfied.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- backend
- Issue type
- Refactor
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 25/100