
Security News
TypeScript 6.0 Released: The Final JavaScript-Based Version
TypeScript 6.0 introduces new standard APIs, modern default settings, and deprecations as it prepares projects for the upcoming TypeScript 7.0 release.
@metamask/core-backend
Advanced tools
@metamask/core-backendCore backend services for MetaMask, serving as the data layer between Backend services (REST APIs, WebSocket services) and Frontend applications (Extension, Mobile). Provides authenticated real-time data delivery including account activity monitoring, price updates, and WebSocket connection management with type-safe controller integration.
@metamask/core-backend
yarn add @metamask/core-backend
or
npm install @metamask/core-backend
import {
BackendWebSocketService,
AccountActivityService,
} from '@metamask/core-backend';
// Initialize Backend WebSocket service
const backendWebSocketService = new BackendWebSocketService({
messenger: backendWebSocketServiceMessenger,
url: 'wss://api.metamask.io/ws',
timeout: 15000,
requestTimeout: 20000,
});
// Initialize Account Activity service
const accountActivityService = new AccountActivityService({
messenger: accountActivityMessenger,
});
// Connect and subscribe to account activity
await backendWebSocketService.connect();
await accountActivityService.subscribe({
address: 'eip155:0:0x742d35cc6634c0532925a3b8d40c4e0e2c6e4e6',
});
// Listen for real-time updates
messenger.subscribe('AccountActivityService:transactionUpdated', (tx) => {
console.log('New transaction:', tx);
});
messenger.subscribe(
'AccountActivityService:balanceUpdated',
({ address, updates }) => {
console.log(`Balance updated for ${address}:`, updates);
},
);
// Coordinate with TokenBalancesController for fallback polling
messenger.subscribe(
'BackendWebSocketService:connectionStateChanged',
(info) => {
if (info.state === 'CONNECTED') {
// Reduce polling when WebSocket is active
messenger.call(
'TokenBalancesController:updateChainPollingConfigs',
{ '0x1': { interval: 600000 } }, // 10 min backup polling
{ immediateUpdate: false },
);
} else {
// Increase polling when WebSocket is down
const defaultInterval = messenger.call(
'TokenBalancesController:getDefaultPollingInterval',
);
messenger.call(
'TokenBalancesController:updateChainPollingConfigs',
{ '0x1': { interval: defaultInterval } },
{ immediateUpdate: true },
);
}
},
);
// Listen for account changes and manage subscriptions
messenger.subscribe(
'AccountsController:selectedAccountChange',
async (selectedAccount) => {
if (selectedAccount) {
await accountActivityService.subscribe({
address: selectedAccount.address,
});
}
},
);
graph TD
subgraph "FRONTEND"
subgraph "Presentation Layer"
FE[Frontend Applications<br/>MetaMask Extension, Mobile, etc.]
end
subgraph "Integration Layer"
IL[Controllers, State Management, UI]
end
subgraph "Data layer (core-backend)"
subgraph "Domain Services"
AAS[AccountActivityService]
PUS[PriceUpdateService<br/>future]
CS[Custom Services...]
end
subgraph "Transport Layer"
WSS[WebSocketService<br/>• Connection management<br/>• Automatic reconnection<br/>• Message routing<br/>• Subscription management]
HTTP[HTTP Service<br/>• REST API calls<br/>• Request/response handling<br/>• Error handling<br/>future]
end
end
end
subgraph "BACKEND"
BS[Backend Services<br/>REST APIs, WebSocket Services, etc.]
end
%% Flow connections
FE --> IL
IL --> AAS
IL --> PUS
IL --> CS
AAS --> WSS
AAS --> HTTP
PUS --> WSS
PUS --> HTTP
CS --> WSS
CS --> HTTP
WSS <--> BS
HTTP <--> BS
%% Styling
classDef frontend fill:#e1f5fe
classDef backend fill:#f3e5f5
classDef service fill:#e8f5e8
classDef transport fill:#fff3e0
class FE,IL frontend
class BS backend
class AAS,PUS,CS service
class WSS,HTTP transport
graph BT
%% External Controllers
AC["AccountsController<br/>(Auto-generated types)"]
AuthC["AuthenticationController<br/>(Auto-generated types)"]
TBC["TokenBalancesController<br/>(External Integration)"]
%% Core Services
AA["AccountActivityService"]
WS["BackendWebSocketService"]
%% Dependencies & Type Imports
AC -.->|"Import types<br/>(DRY)" | AA
AuthC -.->|"Import types<br/>(DRY)" | WS
WS -->|"Messenger calls"| AA
AA -.->|"Event publishing"| TBC
%% Styling
classDef core fill:#f3e5f5
classDef integration fill:#fff3e0
classDef controller fill:#e8f5e8
class WS,AA core
class TBC integration
class AC,AuthC controller
sequenceDiagram
participant TBC as TokenBalancesController
participant AA as AccountActivityService
participant WS as BackendWebSocketService
participant HTTP as HTTP Services<br/>(APIs & RPC)
participant Backend as WebSocket Endpoint<br/>(Backend)
Note over TBC,Backend: Initial Setup
TBC->>HTTP: Initial balance fetch via HTTP<br/>(first request for current state)
WS->>Backend: WebSocket connection request
Backend->>WS: Connection established
WS->>AA: WebSocket connection status notification<br/>(BackendWebSocketService:connectionStateChanged)<br/>{state: 'CONNECTED'}
AA->>AA: call('AccountsController:getSelectedAccount')
AA->>WS: subscribe({channels, callback})
WS->>Backend: {event: 'subscribe', channels: ['account-activity.v1.eip155:0:0x123...']}
Backend->>WS: {event: 'subscribe-response', subscriptionId: 'sub-456'}
Note over WS,Backend: System notification sent automatically upon subscription
Backend->>WS: {event: 'system-notification', data: {chainIds: ['eip155:1', 'eip155:137', ...], status: 'up'}}
WS->>AA: System notification received
AA->>AA: Track chains as 'up' internally
AA->>TBC: Chain availability notification<br/>(AccountActivityService:statusChanged)<br/>{chainIds: ['0x1', '0x89', ...], status: 'up'}
TBC->>TBC: Increase polling interval from 20s to 10min<br/>(.updateChainPollingConfigs({0x89: 600000}))
Note over TBC,Backend: User Account Change
par StatusChanged Event
TBC->>HTTP: Fetch balances for new account<br/>(fill transition gap)
and Account Subscription
AA->>AA: User switched to different account<br/>(AccountsController:selectedAccountChange)
AA->>WS: subscribe (new account)
WS->>Backend: {event: 'subscribe', channels: ['account-activity.v1.eip155:0:0x456...']}
Backend->>WS: {event: 'subscribe-response', subscriptionId: 'sub-789'}
AA->>WS: unsubscribe (previous account)
WS->>Backend: {event: 'unsubscribe', subscriptionId: 'sub-456'}
Backend->>WS: {event: 'unsubscribe-response'}
end
Note over TBC,Backend: Real-time Data Flow
Backend->>WS: {event: 'notification', channel: 'account-activity.v1.eip155:0:0x123...',<br/>data: {address, tx, updates}}
WS->>AA: Direct callback routing
AA->>AA: Validate & process AccountActivityMessage
par Balance Update
AA->>TBC: Real-time balance change notification<br/>(AccountActivityService:balanceUpdated)<br/>{address, chain, updates}
TBC->>TBC: Update balance state directly<br/>(or fallback poll if error)
and Transaction and Activity Update (Not yet implemented)
AA->>AA: Process transaction data<br/>(AccountActivityService:transactionUpdated)<br/>{tx: Transaction}
Note right of AA: Future: Forward to TransactionController<br/>for transaction state management<br/>(pending → confirmed → finalized)
end
Note over TBC,Backend: System Notifications
Backend->>WS: {event: 'system-notification', data: {chainIds: ['eip155:137'], status: 'down'}}
WS->>AA: System notification received
AA->>AA: Process chain status change
AA->>TBC: Chain status notification<br/>(AccountActivityService:statusChanged)<br/>{chainIds: ['eip155:137'], status: 'down'}
TBC->>TBC: Decrease polling interval from 10min to 20s<br/>(.updateChainPollingConfigs({0x89: 20000}))
TBC->>HTTP: Fetch balances immediately
Backend->>WS: {event: 'system-notification', data: {chainIds: ['eip155:137'], status: 'up'}}
WS->>AA: System notification received
AA->>AA: Process chain status change
AA->>TBC: Chain status notification<br/>(AccountActivityService:statusChanged)<br/>{chainIds: ['eip155:137'], status: 'up'}
TBC->>TBC: Increase polling interval from 20s to 10min<br/>(.updateChainPollingConfigs({0x89: 600000}))
Note over TBC,Backend: Connection Health Management
Backend-->>WS: Connection lost
WS->>AA: WebSocket connection status notification<br/>(BackendWebSocketService:connectionStateChanged)<br/>{state: 'DISCONNECTED'}
AA->>AA: Mark all tracked chains as 'down'<br/>(flush internal tracking set)
AA->>TBC: Chain status notification for all tracked chains<br/>(AccountActivityService:statusChanged)<br/>{chainIds: ['0x1', '0x89', ...], status: 'down'}
TBC->>TBC: Decrease polling interval from 10min to 20s<br/>(.updateChainPollingConfigs({0x89: 20000}))
TBC->>HTTP: Fetch balances immediately
WS->>WS: Automatic reconnection<br/>with exponential backoff
WS->>Backend: Reconnection successful
Note over AA,Backend: Restart initial setup - resubscribe and get fresh chain status
AA->>WS: subscribe (same account, new subscription)
WS->>Backend: {event: 'subscribe', channels: ['account-activity.v1.eip155:0:0x123...']}
Backend->>WS: {event: 'subscribe-response', subscriptionId: 'sub-999'}
Backend->>WS: {event: 'system-notification', data: {chainIds: [...], status: 'up'}}
WS->>AA: System notification received
AA->>AA: Track chains as 'up' again
AA->>TBC: Chain availability notification<br/>(AccountActivityService:statusChanged)<br/>{chainIds: [...], status: 'up'}
TBC->>TBC: Increase polling interval back to 10min
The WebSocket connects when ALL 3 conditions are true:
isEnabled() callback returns true (feature flag)AuthenticationController.isSignedIn = trueKeyringController.isUnlocked = truePlus: Platform code must call connect() when app opens/foregrounds and disconnect() when app closes/backgrounds.
Idempotent connect():
Auto-Reconnect:
The core WebSocket client providing connection management, authentication, and message routing.
interface BackendWebSocketServiceOptions {
messenger: BackendWebSocketServiceMessenger;
url: string;
timeout?: number;
reconnectDelay?: number;
maxReconnectDelay?: number;
requestTimeout?: number;
enableAuthentication?: boolean;
enabledCallback?: () => boolean;
}
connect(): Promise<void> - Establish authenticated WebSocket connectiondisconnect(): Promise<void> - Close WebSocket connectionsubscribe(options: SubscriptionOptions): Promise<SubscriptionResult> - Subscribe to channelssendRequest(message: ClientRequestMessage): Promise<ServerResponseMessage> - Send request/response messageschannelHasSubscription(channel: string): boolean - Check subscription statusfindSubscriptionsByChannelPrefix(prefix: string): SubscriptionInfo[] - Find subscriptions by prefixgetConnectionInfo(): WebSocketConnectionInfo - Get detailed connection stateHigh-level service for monitoring account activity using WebSocket data.
interface AccountActivityServiceOptions {
messenger: AccountActivityServiceMessenger;
subscriptionNamespace?: string;
}
subscribe(subscription: SubscriptionOptions): Promise<void> - Subscribe to account activityunsubscribe(subscription: SubscriptionOptions): Promise<void> - Unsubscribe from account activityAccountActivityService:balanceUpdated - Real-time balance changesAccountActivityService:transactionUpdated - Transaction status updatesAccountActivityService:statusChanged - Chain/service status changesFAQs
Core backend services for MetaMask
The npm package @metamask/core-backend receives a total of 19,090 weekly downloads. As such, @metamask/core-backend popularity was classified as popular.
We found that @metamask/core-backend demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 4 open source maintainers collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
TypeScript 6.0 introduces new standard APIs, modern default settings, and deprecations as it prepares projects for the upcoming TypeScript 7.0 release.

Security News
/Research
Newly published Trivy Docker images (0.69.4, 0.69.5, and 0.69.6) were found to contain infostealer IOCs and were pushed to Docker Hub without corresponding GitHub releases.

Research
/Security News
The worm-enabled campaign hit @emilgroup and @teale.io, then used an ICP canister to deliver follow-on payloads.