Skip to content

Commit a0fdd49

Browse files
committed
Merge remote-tracking branch 'origin/main' into cryptodev-2s/messenger/remote-feature-flag-controller
2 parents 779543d + ba91843 commit a0fdd49

File tree

198 files changed

+12243
-2477
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

198 files changed

+12243
-2477
lines changed

.github/CODEOWNERS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@
9292
/packages/permission-log-controller @MetaMask/wallet-integrations @MetaMask/core-platform
9393
/packages/remote-feature-flag-controller @MetaMask/extension-platform @MetaMask/mobile-platform
9494
/packages/foundryup @MetaMask/mobile-platform @MetaMask/extension-platform
95+
/packages/core-backend @MetaMask/core-platform @MetaMask/metamask-assets
9596

9697
## Package Release related
9798
/packages/account-tree-controller/package.json @MetaMask/accounts-engineers @MetaMask/core-platform
@@ -170,3 +171,5 @@
170171
/packages/network-enablement-controller/CHANGELOG.md @MetaMask/metamask-assets @MetaMask/core-platform
171172
/packages/subscription-controller/package.json @MetaMask/web3auth @MetaMask/core-platform
172173
/packages/subscription-controller/CHANGELOG.md @MetaMask/web3auth @MetaMask/core-platform
174+
/packages/core-backend/package.json @MetaMask/core-platform @MetaMask/metamask-assets
175+
/packages/core-backend/CHANGELOG.md @MetaMask/core-platform @MetaMask/metamask-assets

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ Each package in this repository has its own README where you can find installati
3434
- [`@metamask/chain-agnostic-permission`](packages/chain-agnostic-permission)
3535
- [`@metamask/composable-controller`](packages/composable-controller)
3636
- [`@metamask/controller-utils`](packages/controller-utils)
37+
- [`@metamask/core-backend`](packages/core-backend)
3738
- [`@metamask/delegation-controller`](packages/delegation-controller)
3839
- [`@metamask/earn-controller`](packages/earn-controller)
3940
- [`@metamask/eip-5792-middleware`](packages/eip-5792-middleware)
@@ -98,6 +99,7 @@ linkStyle default opacity:0.5
9899
chain_agnostic_permission(["@metamask/chain-agnostic-permission"]);
99100
composable_controller(["@metamask/composable-controller"]);
100101
controller_utils(["@metamask/controller-utils"]);
102+
core_backend(["@metamask/core-backend"]);
101103
delegation_controller(["@metamask/delegation-controller"]);
102104
earn_controller(["@metamask/earn-controller"]);
103105
eip_5792_middleware(["@metamask/eip-5792-middleware"]);
@@ -159,6 +161,7 @@ linkStyle default opacity:0.5
159161
assets_controllers --> account_tree_controller;
160162
assets_controllers --> accounts_controller;
161163
assets_controllers --> approval_controller;
164+
assets_controllers --> core_backend;
162165
assets_controllers --> keyring_controller;
163166
assets_controllers --> multichain_account_service;
164167
assets_controllers --> network_controller;
@@ -192,6 +195,11 @@ linkStyle default opacity:0.5
192195
chain_agnostic_permission --> permission_controller;
193196
composable_controller --> base_controller;
194197
composable_controller --> json_rpc_engine;
198+
core_backend --> base_controller;
199+
core_backend --> controller_utils;
200+
core_backend --> profile_sync_controller;
201+
core_backend --> accounts_controller;
202+
core_backend --> keyring_controller;
195203
delegation_controller --> base_controller;
196204
delegation_controller --> accounts_controller;
197205
delegation_controller --> keyring_controller;
@@ -264,6 +272,7 @@ linkStyle default opacity:0.5
264272
permission_log_controller --> json_rpc_engine;
265273
phishing_controller --> base_controller;
266274
phishing_controller --> controller_utils;
275+
phishing_controller --> transaction_controller;
267276
polling_controller --> base_controller;
268277
polling_controller --> controller_utils;
269278
polling_controller --> network_controller;
Lines changed: 253 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,253 @@
1+
# Real-Time Balance Updates and Status Management Flow
2+
3+
This document describes the architecture and flow for real-time balance updates and WebSocket status management in MetaMask Core, specifically focusing on the `AccountActivityService:balanceUpdated` and `AccountActivityService:statusChanged` events.
4+
5+
## Overview
6+
7+
The system provides real-time balance updates and intelligent polling management through a multi-layered architecture that combines WebSocket streaming with fallback HTTP polling. The key components work together to ensure users receive timely balance updates while optimizing network usage and battery consumption.
8+
9+
## Architecture Components
10+
11+
### 1. BackendWebSocketService
12+
13+
- **Purpose**: Low-level WebSocket connection management
14+
- **Responsibilities**:
15+
- Maintains WebSocket connection with automatic reconnection
16+
- Handles subscription management
17+
- Routes incoming messages to registered callbacks
18+
- Publishes connection state changes
19+
20+
### 2. AccountActivityService
21+
22+
- **Purpose**: High-level account activity monitoring
23+
- **Responsibilities**:
24+
- Subscribes to selected account activity
25+
- Processes transaction and balance updates
26+
- Emits `balanceUpdated` and `statusChanged` events
27+
- Manages chain status based on WebSocket connectivity and system notifications
28+
29+
### 3. TokenBalancesController
30+
31+
- **Purpose**: Token balance state management and intelligent polling
32+
- **Responsibilities**:
33+
- Maintains token balance state for all accounts
34+
- Implements per-chain configurable polling intervals
35+
- Responds to real-time balance updates from AccountActivityService
36+
- Dynamically adjusts polling based on WebSocket availability
37+
- Imports newly detected tokens via TokenDetectionController
38+
39+
## Event Flow
40+
41+
### Balance Update Flow
42+
43+
```
44+
┌─────────────────────────────────────────────────────────────────────────┐
45+
│ BALANCE UPDATE FLOW │
46+
└─────────────────────────────────────────────────────────────────────────┘
47+
48+
1. WebSocket receives account activity message
49+
50+
2. BackendWebSocketService routes message to registered callback
51+
52+
3. AccountActivityService processes AccountActivityMessage
53+
{
54+
address: "0x123...",
55+
tx: { hash: "0x...", chain: "eip155:1", status: "completed", ... },
56+
updates: [
57+
{
58+
asset: { fungible: true, type: "eip155:1/erc20:0x...", unit: "USDT" },
59+
postBalance: { amount: "1254.75" },
60+
transfers: [{ from: "0x...", to: "0x...", amount: "500.00" }]
61+
}
62+
]
63+
}
64+
65+
4. AccountActivityService publishes separate events:
66+
- AccountActivityService:transactionUpdated (transaction data)
67+
- AccountActivityService:balanceUpdated (balance updates)
68+
69+
5. TokenBalancesController receives balanceUpdated event
70+
71+
6. TokenBalancesController processes balance updates:
72+
a. Parses CAIP chain ID (e.g., "eip155:1" → "0x1")
73+
b. Parses asset types:
74+
- ERC20 tokens: "eip155:1/erc20:0x..." → token address
75+
- Native tokens: "eip155:1/slip44:60" → zero address
76+
c. Validates addresses and checksums them
77+
d. Checks if tokens are tracked (imported or detected)
78+
79+
7. For tracked tokens:
80+
- Updates tokenBalances state immediately
81+
- Updates AccountTrackerController for native balances
82+
83+
8. For untracked ERC20 tokens:
84+
- Queues tokens for import via TokenDetectionController
85+
- Triggers fallback polling to fetch newly imported token balances
86+
87+
9. On errors:
88+
- Falls back to HTTP polling for the affected chain
89+
```
90+
91+
### Status Change Flow
92+
93+
The system manages chain status through two primary mechanisms:
94+
95+
#### A. WebSocket Connection State Changes
96+
97+
```
98+
┌─────────────────────────────────────────────────────────────────────────┐
99+
│ WEBSOCKET CONNECTION STATUS FLOW │
100+
└─────────────────────────────────────────────────────────────────────────┘
101+
102+
1. BackendWebSocketService detects connection state change
103+
(CONNECTING → CONNECTED | DISCONNECTED | ERROR)
104+
105+
2. BackendWebSocketService publishes:
106+
BackendWebSocketService:connectionStateChanged
107+
108+
3. AccountActivityService receives connection state change
109+
110+
4. AccountActivityService determines affected chains:
111+
- Fetches list of supported chains from backend API
112+
- Example: ["eip155:1", "eip155:137", "eip155:56"]
113+
114+
5. AccountActivityService publishes status based on connection state:
115+
116+
IF state === CONNECTED:
117+
→ Publishes: statusChanged { chainIds: [...], status: 'up' }
118+
→ Triggers resubscription to selected account
119+
120+
IF state === DISCONNECTED || ERROR:
121+
→ Publishes: statusChanged { chainIds: [...], status: 'down' }
122+
123+
6. TokenBalancesController receives statusChanged event
124+
125+
7. TokenBalancesController applies debouncing (5 second window)
126+
- Accumulates status changes to prevent excessive updates
127+
- Latest status wins for each chain
128+
129+
8. After debounce period, processes accumulated changes:
130+
- Converts CAIP format to hex (e.g., "eip155:1" → "0x1")
131+
- Calculates new polling intervals:
132+
* status = 'down' → Uses default interval (30 seconds)
133+
* status = 'up' → Uses extended interval (5 minutes)
134+
135+
9. Adds jitter delay (0 to default interval)
136+
- Prevents synchronized requests across instances
137+
138+
10. Updates chain polling configurations
139+
- Triggers immediate balance fetch
140+
- Restarts polling with new intervals
141+
```
142+
143+
#### B. System Notifications (Per-Chain Status)
144+
145+
```
146+
┌─────────────────────────────────────────────────────────────────────────┐
147+
│ SYSTEM NOTIFICATION STATUS FLOW │
148+
└─────────────────────────────────────────────────────────────────────────┘
149+
150+
1. WebSocket receives system notification message
151+
{
152+
type: 'system',
153+
chainIds: ['eip155:1'], // Specific affected chains
154+
status: 'down' // or 'up'
155+
}
156+
157+
2. BackendWebSocketService routes to AccountActivityService
158+
159+
3. AccountActivityService validates notification:
160+
- Ensures chainIds array is present and valid
161+
- Ensures status is present
162+
163+
4. AccountActivityService publishes delta update:
164+
AccountActivityService:statusChanged
165+
{
166+
chainIds: ['eip155:1'], // Only affected chains
167+
status: 'down'
168+
}
169+
170+
5. TokenBalancesController processes (same as WebSocket flow above)
171+
```
172+
173+
#### Status Change Event Format
174+
175+
```typescript
176+
// Event published by AccountActivityService
177+
AccountActivityService:statusChanged
178+
Payload: {
179+
chainIds: string[]; // Array of CAIP chain IDs (e.g., ["eip155:1", "eip155:137"])
180+
status: 'up' | 'down'; // Connection status
181+
}
182+
```
183+
184+
## Polling Strategy
185+
186+
The TokenBalancesController implements intelligent polling that adapts based on WebSocket availability:
187+
188+
### Polling Intervals
189+
190+
| Scenario | Interval | Reason |
191+
| ----------------------------------------- | -------------------- | --------------------------------------------------- |
192+
| WebSocket Connected (`status: 'up'`) | 5 minutes | Real-time updates available, polling is backup only |
193+
| WebSocket Disconnected (`status: 'down'`) | 30 seconds (default) | Primary update mechanism, needs faster polling |
194+
| Per-chain custom configuration | Configurable | Allows fine-tuning per chain requirements |
195+
196+
### Debouncing Strategy
197+
198+
To prevent excessive HTTP calls during unstable connections:
199+
200+
1. **Accumulation Window**: 5 seconds
201+
202+
- All status changes within this window are accumulated
203+
- Latest status wins for each chain
204+
205+
2. **Jitter Addition**: Random delay (0 to default interval)
206+
207+
- Prevents synchronized requests across multiple instances
208+
- Reduces backend load spikes
209+
210+
3. **Batch Processing**: After debounce + jitter
211+
- All accumulated changes applied at once
212+
- Single polling configuration update
213+
- Immediate balance fetch triggered
214+
215+
### Per-Chain Polling Configuration
216+
217+
TokenBalancesController supports per-chain polling intervals:
218+
219+
```typescript
220+
// Configure custom intervals for specific chains
221+
tokenBalancesController.updateChainPollingConfigs({
222+
'0x1': { interval: 30000 }, // Ethereum: 30 seconds (default)
223+
'0x89': { interval: 15000 }, // Polygon: 15 seconds (faster)
224+
'0xa4b1': { interval: 60000 }, // Arbitrum: 1 minute (slower)
225+
});
226+
```
227+
228+
## Token Discovery Flow
229+
230+
When balance updates include previously unknown tokens:
231+
232+
```
233+
1. TokenBalancesController receives balance update for unknown token
234+
235+
2. Checks if token is tracked (in allTokens or allIgnoredTokens)
236+
237+
3. If NOT tracked:
238+
a. Queues token for import
239+
b. Calls TokenDetectionController:addDetectedTokensViaWs
240+
c. Token is added to detected tokens list
241+
242+
4. Triggers balance fetch for the chain
243+
244+
5. New token balance is fetched and state is updated
245+
```
246+
247+
## References
248+
249+
- [`TokenBalancesController.ts`](../packages/assets-controllers/src/TokenBalancesController.ts) - Main controller implementation
250+
- [`AccountActivityService.ts`](../packages/core-backend/src/AccountActivityService.ts) - Account activity monitoring
251+
- [`BackendWebSocketService.ts`](../packages/core-backend/src/BackendWebSocketService.ts) - WebSocket connection management
252+
- [`types.ts`](../packages/core-backend/src/types.ts) - Type definitions
253+
- [Core Backend README](../packages/core-backend/README.md) - Package overview

eslint-warning-thresholds.json

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
},
2828
"packages/assets-controllers/src/NftDetectionController.test.ts": {
2929
"import-x/namespace": 6,
30-
"import-x/order": 4
30+
"import-x/order": 3
3131
},
3232
"packages/assets-controllers/src/NftDetectionController.ts": {
3333
"jsdoc/check-tag-names": 34,
@@ -54,16 +54,6 @@
5454
"packages/assets-controllers/src/Standards/NftStandards/ERC721/ERC721Standard.ts": {
5555
"prettier/prettier": 1
5656
},
57-
"packages/assets-controllers/src/TokenDetectionController.test.ts": {
58-
"import-x/namespace": 11,
59-
"jsdoc/tag-lines": 1
60-
},
61-
"packages/assets-controllers/src/TokenDetectionController.ts": {
62-
"@typescript-eslint/prefer-readonly": 3,
63-
"jsdoc/check-tag-names": 8,
64-
"jsdoc/tag-lines": 6,
65-
"no-unused-private-class-members": 2
66-
},
6757
"packages/assets-controllers/src/TokenListController.test.ts": {
6858
"import-x/namespace": 7,
6959
"import-x/order": 3,

0 commit comments

Comments
 (0)