Commit 8bab3bf
authored
feat(card): add CardController shell to Engine (MetaMask#27020)
<!--
Please submit this PR as a draft initially.
Do not mark it as "Ready for review" until the template has been
completely filled out, and PR status checks have passed at least once.
-->
## **Description**
Introduces the `CardController` shell into the Engine as the first step
toward the multi-provider Card architecture.
**Why**: The Card feature is tightly coupled to Baanx with business
logic scattered across hooks, views, and a 2,600-line SDK monolith. To
support multiple card providers and clean up the architecture, we need a
proper Engine controller that owns the Card feature's persistent state.
This PR lays the foundation — an inert controller that holds state and
syncs it to Redux via the standard Engine batcher. No user-facing
changes.
**What changed**:
New files:
- **`app/core/Engine/controllers/card-controller/types.ts`**: Defines
`CardControllerState` (5 fields: `selectedCountry`, `activeProviderId`,
`isAuthenticated`, `cardholderAccounts`, `providerData`), default state
factory, and action/event/messenger types.
- **`app/core/Engine/controllers/card-controller/CardController.ts`**:
Bare-bones controller extending `BaseController`. All state is persisted
and marked `usedInUi: true`. No business logic yet — provider delegation
comes in subsequent PRs.
- **`app/core/Engine/controllers/card-controller/index.ts`**: Init
function following the standard Engine pattern — reads persisted state,
creates controller instance.
- **`app/core/Engine/messengers/card-controller-messenger/index.ts`**:
Simple messenger with no delegated actions/events (those will be added
when the controller needs to talk to `KeyringController`,
`TransactionController`, etc.).
- **`app/selectors/cardController.ts`**: Four selectors:
`selectCardSelectedCountry`, `selectCardActiveProviderId`,
`selectIsCardAuthenticated`, `selectCardholderAccounts`.
Modified files:
- **`app/core/Engine/types.ts`**: Added `CardController` to
`Controllers`, `EngineState`, `GlobalActions`, `GlobalEvents`, and
`ControllersToInitialize`.
- **`app/core/Engine/Engine.ts`**: Registered init function,
destructured from `controllersByName`, added to `this.context` and
`state` getter.
- **`app/core/Engine/messengers/index.ts`**: Added `CardController`
entry to `CONTROLLER_MESSENGERS`.
- **`app/core/Engine/constants.ts`**: Added
`'CardController:stateChange'` to `BACKGROUND_STATE_CHANGE_EVENT_NAMES`.
- **`app/util/test/initial-background-state.json`**: Added
`CardController` default state to the test fixture.
- **`.github/CODEOWNERS`**: Assigned
`app/core/Engine/controllers/card-controller`,
`app/core/Engine/messengers/card-controller-messenger`, and
`app/selectors/cardController.ts` to `@MetaMask/card`.
Test files:
- **`CardController.test.ts`**: Tests controller construction with
default state, partial state, and full persisted state.
- **`index.test.ts`**: Tests the init function returns a controller
instance, uses default state when none is persisted, and uses persisted
state when provided.
- **`cardController.test.ts`**: Tests all four selectors with populated
state, empty state, and undefined `CardController` state (fallback
values).
## **Changelog**
CHANGELOG entry: null
## **Related issues**
Fixes:
## **Manual testing steps**
```gherkin
Feature: CardController initialization
Scenario: Controller initializes with default state on fresh install
Given the app is freshly installed
When the Engine initializes
Then state.engine.backgroundState.CardController exists
And selectedCountry is null
And activeProviderId is null
And isAuthenticated is false
And cardholderAccounts is an empty array
And providerData is an empty object
Scenario: Controller state persists across app restarts
Given the app has been launched before
And CardController state was persisted
When the Engine initializes again
Then the persisted CardController state is restored
Scenario: No user-facing changes
Given the user is on any screen in the app
When the app loads
Then nothing visually changes
And the Card feature continues to work as before
```
## **Screenshots/Recordings**
No UI changes — this is an infrastructure-only PR.
### **Before**
No `CardController` in Engine. Card state managed entirely by Redux
slice.
### **After**
`CardController` exists in Engine with default state. Syncs to
`state.engine.backgroundState.CardController`. Existing Card feature
behavior is unchanged.
## **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.
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> **Medium Risk**
> Medium risk because it updates core Engine initialization/types and
introduces new persisted background state, which could affect startup or
state serialization despite having no business logic yet.
>
> **Overview**
> Introduces a new `CardController` (BaseController) that persists Card
feature state (`selectedCountry`, `activeProviderId`, `isAuthenticated`,
`cardholderAccounts`, `providerData`) and exposes it via the standard
Engine background state batching.
>
> Wires the controller into Engine initialization and messaging
(`Engine.ts`, `messengers/index.ts`, `types.ts`, `constants.ts`) so
`CardController` state is included in `Engine.state`, state-change
subscriptions, and debug/state log fixtures.
>
> Adds initial selectors in `selectors/cardController.ts`, plus unit
tests for controller construction/init and selector fallbacks, and
updates `CODEOWNERS` for new Card Engine paths.
>
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
8363bb1. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->1 parent 19722d7 commit 8bab3bf
15 files changed
Lines changed: 506 additions & 2 deletions
File tree
- .github
- app
- core/Engine
- controllers/card-controller
- messengers
- card-controller-messenger
- selectors
- util
- logs/__snapshots__
- test
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
70 | 70 | | |
71 | 71 | | |
72 | 72 | | |
73 | | - | |
74 | | - | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
75 | 78 | | |
76 | 79 | | |
77 | 80 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
177 | 177 | | |
178 | 178 | | |
179 | 179 | | |
| 180 | + | |
180 | 181 | | |
181 | 182 | | |
182 | 183 | | |
| |||
371 | 372 | | |
372 | 373 | | |
373 | 374 | | |
| 375 | + | |
374 | 376 | | |
375 | 377 | | |
376 | 378 | | |
| |||
411 | 413 | | |
412 | 414 | | |
413 | 415 | | |
| 416 | + | |
414 | 417 | | |
415 | 418 | | |
416 | 419 | | |
| |||
569 | 572 | | |
570 | 573 | | |
571 | 574 | | |
| 575 | + | |
572 | 576 | | |
573 | 577 | | |
574 | 578 | | |
| |||
1291 | 1295 | | |
1292 | 1296 | | |
1293 | 1297 | | |
| 1298 | + | |
1294 | 1299 | | |
1295 | 1300 | | |
1296 | 1301 | | |
| |||
1394 | 1399 | | |
1395 | 1400 | | |
1396 | 1401 | | |
| 1402 | + | |
1397 | 1403 | | |
1398 | 1404 | | |
1399 | 1405 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
83 | 83 | | |
84 | 84 | | |
85 | 85 | | |
| 86 | + | |
86 | 87 | | |
87 | 88 | | |
88 | 89 | | |
| |||
Lines changed: 77 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
Lines changed: 79 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
Lines changed: 70 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
Lines changed: 27 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
0 commit comments