Skip to content

Conversation

Kriys94
Copy link
Contributor

@Kriys94 Kriys94 commented Oct 10, 2025

Explanation

Current State & Motivation

The core-backend package had several architectural issues that needed to be addressed:

  1. Inflexible Chain Management: AccountActivityService was proactively fetching supported chains from the Accounts API (/v2/supportedNetworks) with a hardcoded fallback list. This approach was:

    • Out of sync with backend capabilities (client-side caching vs. real-time backend state)
    • Unnecessarily complex with cache expiration logic
    • Made assumptions about supported chains rather than reacting to actual backend status
  2. Incomplete Connection Management: BackendWebSocketService only subscribed to AuthenticationController:stateChange events but didn't directly respond to wallet lock/unlock events from KeyringController. This created a dependency where connection management relied solely on authentication state changes.

  3. Reconnection Logic Based on Close Codes: The service used WebSocket close codes to determine reconnection behavior, which was fragile and didn't properly distinguish between manual disconnects (user action) and unexpected disconnects (network issues).

  4. Type Mismatches: The Transaction and Asset types didn't match the backend API contract, causing potential integration issues.

  5. No Performance Monitoring: There was no way to trace WebSocket operations for performance analysis in production environments.

Solution

1. System Notification-Driven Chain Tracking (AccountActivityService)

Before: Client proactively fetches and caches supported chains

async getSupportedChains(): Promise<string[]> {
  // Fetch from API with cache + fallback to hardcoded list
}

After: Client reacts to backend system notifications

#chainsUp: Set<string> = new Set();

// Backend automatically sends system notifications with chain status
// Service tracks chains dynamically based on real-time backend state

Benefits:

  • Real-time chain status that matches backend capabilities
  • No stale cache issues
  • Simpler code (removed ~100 lines)
  • Backend has single source of truth

Flow:

  1. Client subscribes to account activity
  2. Backend automatically sends system notification: {chainIds: ['eip155:1', 'eip155:137'], status: 'up'}
  3. Service tracks these chains internally
  4. On disconnect, service flushes all tracked chains as 'down'
  5. On reconnect, backend sends fresh system notification with current status

2. Simplified Reconnection Logic (BackendWebSocketService)

Before: Reconnection based on close codes

#shouldReconnectOnClose(code: number): boolean {
  return code !== 1000; // Only don't reconnect on normal closure
}

After: Reconnection based on manual disconnect flag

#manualDisconnect = false;

connect() {
  this.#manualDisconnect = false; // Reset flag on explicit connect
}

disconnect() {
  this.#manualDisconnect = true; // Set flag to prevent auto-reconnect
}

onclose() {
  if (!this.#manualDisconnect) {
    this.#scheduleReconnect(); // Auto-reconnect unless manually disconnected
  }
}

Benefits:

  • Clear intent: manual disconnects stay disconnected, unexpected disconnects auto-reconnect
  • No ambiguity with close codes
  • Simpler logic

3. KeyringController Event Integration

Added direct subscriptions to wallet lock state:

#subscribeEvents() {
  // Sign in/out
  this.#messenger.subscribe('AuthenticationController:stateChange', ...);
  
  // Wallet lock/unlock (NEW)
  this.#messenger.subscribe('KeyringController:unlock', () => this.connect());
  this.#messenger.subscribe('KeyringController:lock', () => this.disconnect());
}

Connection Requirements (all must be true):

  1. ✅ Feature enabled (isEnabled() callback)
  2. ✅ User signed in (AuthenticationController.isSignedIn)
  3. ✅ Wallet unlocked (KeyringController state)

4. Type Alignment with Backend API

  • Transaction.hashTransaction.id (matches backend field name)
  • Asset now requires decimals: number (needed for proper token formatting)

5. Performance Tracing Integration

Added optional traceFn parameter to enable performance monitoring:

new BackendWebSocketService({
  messenger,
  url: BACKEND_WS_URL,
  traceFn: trace as TraceCallback, // Inject platform-specific trace function
  // ...other options
});

Benefits:

  • Platform-agnostic tracing support (enables Sentry integration in extension/mobile)
  • Traces WebSocket operations (connect, disconnect methods)
  • Enables performance monitoring in production
  • Core package remains platform-agnostic with no-op default

Integration:

  • Extension: Uses trace from shared/lib/trace.ts (Sentry-backed)
  • Mobile: Uses trace from app/util/trace.ts (Sentry-backed)
  • Core: Defaults to no-op if not provided, keeping the package platform-agnostic

Example usage:

// Extension/Mobile init files inject their platform-specific trace function
const service = new BackendWebSocketService({
  messenger,
  url: BACKEND_WS_URL,
  traceFn: trace as TraceCallback,
  // When operations occur, they're automatically traced:
  // - "BackendWebSocketService:connect"
  // - "BackendWebSocketService:disconnect"
});

Breaking Changes Impact

For AccountActivityService consumers:

  • Removed: getSupportedChains() method - consumers should subscribe to AccountActivityService:statusChanged events instead
  • Chain status is now event-driven rather than request-response

For type definitions:

  • Transaction objects must use id instead of hash
  • Asset objects must include decimals field

Testing Updates

  • Removed nock dependency (no longer fetching from external API)
  • Updated all tests to use system notification-driven flow
  • Added tests for KeyringController event integration
  • Added tests for manual disconnect flag behavior
  • Increased test coverage for edge cases in reconnection logic

References

  • Part of ongoing core-backend stabilization effort
  • Aligns with backend system notification architecture

Checklist

  • I've updated the test suite for new or updated code as appropriate
  • I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate
  • I've communicated my changes to consumers by updating changelogs for packages I've changed, highlighting breaking changes as necessary
  • I've prepared draft pull requests for clients and consumer packages to resolve any breaking changes

Note

Revamps WebSocket lifecycle and reconnection with lock/unlock integration and tracing, replaces API-based chain support with system-notification tracking, and aligns types (Transaction.id, Asset.decimals).

  • Core-backend:
    • BackendWebSocketService:
      • Add optional traceFn for performance tracing; trace connection, disconnect, notifications.
      • Integrate KeyringController:lock/unlock events; idempotent connect()/disconnect().
      • Simplify reconnection via #manualDisconnect flag; always auto-reconnect on unexpected closes.
      • Include message timestamp handling; expose channelType; refine connectedAt/state.
    • AccountActivityService:
      • Replace API-based supported chains with system notification–driven tracking via internal #chainsUp set.
      • On WS disconnect/error, flush tracked chains as down; resubscribe on reconnect.
      • Publish statusChanged with optional timestamp; pass channelType on subscribe.
    • Types (BREAKING):
      • Transaction.hashTransaction.id.
      • Asset adds required decimals.
    • Docs/Changelog:
      • Update README with connection requirements/behavior, diagrams; changelog documents breaking changes.
    • Dependencies/Config:
      • Add @metamask/keyring-controller (dev/peer); TS references updated.
  • Assets-controllers tests:
    • Update TokenBalancesController.test.ts to include asset.decimals in balance updates.

Written by Cursor Bugbot for commit 0189b42. This will update automatically on new commits. Configure here.

@Kriys94 Kriys94 force-pushed the feature/updateCoreBackend branch 3 times, most recently from 61d7fa7 to 1a6bf0c Compare October 12, 2025 21:28
@Kriys94 Kriys94 marked this pull request as ready for review October 12, 2025 21:36
@Kriys94 Kriys94 requested review from a team as code owners October 12, 2025 21:36
cursor[bot]

This comment was marked as outdated.

@Kriys94 Kriys94 changed the title feat(core-backend): change behavior feat(core-backend): update websocket behavior Oct 13, 2025
bergarces
bergarces previously approved these changes Oct 13, 2025
Copy link
Contributor

@bergarces bergarces left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving for the assets codeowner part.

cursor[bot]

This comment was marked as outdated.

@Kriys94
Copy link
Contributor Author

Kriys94 commented Oct 13, 2025

@metamaskbot publish-preview

Copy link
Contributor

Preview builds have been published. See these instructions for more information about preview builds.

Expand for full list of packages and versions.
{
  "@metamask-previews/account-tree-controller": "1.4.2-preview-e7dac947",
  "@metamask-previews/accounts-controller": "33.1.1-preview-e7dac947",
  "@metamask-previews/address-book-controller": "6.2.0-preview-e7dac947",
  "@metamask-previews/announcement-controller": "7.1.0-preview-e7dac947",
  "@metamask-previews/app-metadata-controller": "1.1.0-preview-e7dac947",
  "@metamask-previews/approval-controller": "7.2.0-preview-e7dac947",
  "@metamask-previews/assets-controllers": "79.0.1-preview-e7dac947",
  "@metamask-previews/base-controller": "8.4.1-preview-e7dac947",
  "@metamask-previews/bridge-controller": "49.0.1-preview-e7dac947",
  "@metamask-previews/bridge-status-controller": "49.0.1-preview-e7dac947",
  "@metamask-previews/build-utils": "3.0.4-preview-e7dac947",
  "@metamask-previews/chain-agnostic-permission": "1.2.0-preview-e7dac947",
  "@metamask-previews/composable-controller": "11.1.0-preview-e7dac947",
  "@metamask-previews/controller-utils": "11.14.1-preview-e7dac947",
  "@metamask-previews/core-backend": "1.0.1-preview-e7dac947",
  "@metamask-previews/delegation-controller": "0.8.0-preview-e7dac947",
  "@metamask-previews/earn-controller": "8.0.1-preview-e7dac947",
  "@metamask-previews/eip-5792-middleware": "1.2.2-preview-e7dac947",
  "@metamask-previews/eip1193-permission-middleware": "1.0.1-preview-e7dac947",
  "@metamask-previews/ens-controller": "17.1.0-preview-e7dac947",
  "@metamask-previews/error-reporting-service": "2.2.1-preview-e7dac947",
  "@metamask-previews/eth-json-rpc-provider": "5.0.1-preview-e7dac947",
  "@metamask-previews/foundryup": "1.0.1-preview-e7dac947",
  "@metamask-previews/gas-fee-controller": "24.1.0-preview-e7dac947",
  "@metamask-previews/gator-permissions-controller": "0.2.1-preview-e7dac947",
  "@metamask-previews/json-rpc-engine": "10.1.1-preview-e7dac947",
  "@metamask-previews/json-rpc-middleware-stream": "8.0.8-preview-e7dac947",
  "@metamask-previews/keyring-controller": "23.1.1-preview-e7dac947",
  "@metamask-previews/logging-controller": "6.1.0-preview-e7dac947",
  "@metamask-previews/message-manager": "13.0.1-preview-e7dac947",
  "@metamask-previews/messenger": "0.3.0-preview-e7dac947",
  "@metamask-previews/multichain-account-service": "1.6.1-preview-e7dac947",
  "@metamask-previews/multichain-api-middleware": "1.2.1-preview-e7dac947",
  "@metamask-previews/multichain-network-controller": "1.0.1-preview-e7dac947",
  "@metamask-previews/multichain-transactions-controller": "5.1.0-preview-e7dac947",
  "@metamask-previews/name-controller": "8.1.0-preview-e7dac947",
  "@metamask-previews/network-controller": "24.2.1-preview-e7dac947",
  "@metamask-previews/network-enablement-controller": "2.1.1-preview-e7dac947",
  "@metamask-previews/notification-services-controller": "18.3.0-preview-e7dac947",
  "@metamask-previews/permission-controller": "11.1.0-preview-e7dac947",
  "@metamask-previews/permission-log-controller": "4.1.0-preview-e7dac947",
  "@metamask-previews/phishing-controller": "14.1.2-preview-e7dac947",
  "@metamask-previews/polling-controller": "14.0.1-preview-e7dac947",
  "@metamask-previews/preferences-controller": "20.0.2-preview-e7dac947",
  "@metamask-previews/profile-sync-controller": "25.1.1-preview-e7dac947",
  "@metamask-previews/rate-limit-controller": "6.1.0-preview-e7dac947",
  "@metamask-previews/remote-feature-flag-controller": "1.8.0-preview-e7dac947",
  "@metamask-previews/sample-controllers": "2.0.1-preview-e7dac947",
  "@metamask-previews/seedless-onboarding-controller": "4.1.0-preview-e7dac947",
  "@metamask-previews/selected-network-controller": "24.0.1-preview-e7dac947",
  "@metamask-previews/shield-controller": "0.3.1-preview-e7dac947",
  "@metamask-previews/signature-controller": "34.0.1-preview-e7dac947",
  "@metamask-previews/subscription-controller": "1.0.1-preview-e7dac947",
  "@metamask-previews/token-search-discovery-controller": "3.4.0-preview-e7dac947",
  "@metamask-previews/transaction-controller": "60.6.1-preview-e7dac947",
  "@metamask-previews/user-operation-controller": "39.1.0-preview-e7dac947"
}

@Kriys94 Kriys94 force-pushed the feature/updateCoreBackend branch from e7dac94 to 0189b42 Compare October 13, 2025 12:20
@Kriys94
Copy link
Contributor Author

Kriys94 commented Oct 13, 2025

@metamaskbot publish-preview

Copy link
Contributor

Preview builds have been published. See these instructions for more information about preview builds.

Expand for full list of packages and versions.
{
  "@metamask-previews/account-tree-controller": "1.4.2-preview-0189b42",
  "@metamask-previews/accounts-controller": "33.1.1-preview-0189b42",
  "@metamask-previews/address-book-controller": "6.2.0-preview-0189b42",
  "@metamask-previews/announcement-controller": "7.1.0-preview-0189b42",
  "@metamask-previews/app-metadata-controller": "1.1.0-preview-0189b42",
  "@metamask-previews/approval-controller": "7.2.0-preview-0189b42",
  "@metamask-previews/assets-controllers": "80.0.0-preview-0189b42",
  "@metamask-previews/base-controller": "8.4.1-preview-0189b42",
  "@metamask-previews/bridge-controller": "50.0.0-preview-0189b42",
  "@metamask-previews/bridge-status-controller": "50.0.0-preview-0189b42",
  "@metamask-previews/build-utils": "3.0.4-preview-0189b42",
  "@metamask-previews/chain-agnostic-permission": "1.2.0-preview-0189b42",
  "@metamask-previews/composable-controller": "11.1.0-preview-0189b42",
  "@metamask-previews/controller-utils": "11.14.1-preview-0189b42",
  "@metamask-previews/core-backend": "1.0.1-preview-0189b42",
  "@metamask-previews/delegation-controller": "0.8.0-preview-0189b42",
  "@metamask-previews/earn-controller": "8.0.1-preview-0189b42",
  "@metamask-previews/eip-5792-middleware": "1.2.2-preview-0189b42",
  "@metamask-previews/eip1193-permission-middleware": "1.0.1-preview-0189b42",
  "@metamask-previews/ens-controller": "17.1.0-preview-0189b42",
  "@metamask-previews/error-reporting-service": "2.2.1-preview-0189b42",
  "@metamask-previews/eth-json-rpc-provider": "5.0.1-preview-0189b42",
  "@metamask-previews/foundryup": "1.0.1-preview-0189b42",
  "@metamask-previews/gas-fee-controller": "24.1.0-preview-0189b42",
  "@metamask-previews/gator-permissions-controller": "0.2.1-preview-0189b42",
  "@metamask-previews/json-rpc-engine": "10.1.1-preview-0189b42",
  "@metamask-previews/json-rpc-middleware-stream": "8.0.8-preview-0189b42",
  "@metamask-previews/keyring-controller": "23.1.1-preview-0189b42",
  "@metamask-previews/logging-controller": "6.1.0-preview-0189b42",
  "@metamask-previews/message-manager": "13.0.1-preview-0189b42",
  "@metamask-previews/messenger": "0.3.0-preview-0189b42",
  "@metamask-previews/multichain-account-service": "1.6.1-preview-0189b42",
  "@metamask-previews/multichain-api-middleware": "1.2.1-preview-0189b42",
  "@metamask-previews/multichain-network-controller": "1.0.1-preview-0189b42",
  "@metamask-previews/multichain-transactions-controller": "5.1.0-preview-0189b42",
  "@metamask-previews/name-controller": "8.1.0-preview-0189b42",
  "@metamask-previews/network-controller": "24.2.1-preview-0189b42",
  "@metamask-previews/network-enablement-controller": "2.1.1-preview-0189b42",
  "@metamask-previews/notification-services-controller": "18.3.0-preview-0189b42",
  "@metamask-previews/permission-controller": "11.1.0-preview-0189b42",
  "@metamask-previews/permission-log-controller": "4.1.0-preview-0189b42",
  "@metamask-previews/phishing-controller": "14.1.2-preview-0189b42",
  "@metamask-previews/polling-controller": "14.0.1-preview-0189b42",
  "@metamask-previews/preferences-controller": "20.0.2-preview-0189b42",
  "@metamask-previews/profile-sync-controller": "25.1.1-preview-0189b42",
  "@metamask-previews/rate-limit-controller": "6.1.0-preview-0189b42",
  "@metamask-previews/remote-feature-flag-controller": "1.8.0-preview-0189b42",
  "@metamask-previews/sample-controllers": "2.0.1-preview-0189b42",
  "@metamask-previews/seedless-onboarding-controller": "4.1.0-preview-0189b42",
  "@metamask-previews/selected-network-controller": "24.0.1-preview-0189b42",
  "@metamask-previews/shield-controller": "0.3.2-preview-0189b42",
  "@metamask-previews/signature-controller": "34.0.1-preview-0189b42",
  "@metamask-previews/subscription-controller": "1.0.1-preview-0189b42",
  "@metamask-previews/token-search-discovery-controller": "3.4.0-preview-0189b42",
  "@metamask-previews/transaction-controller": "60.6.1-preview-0189b42",
  "@metamask-previews/user-operation-controller": "39.1.0-preview-0189b42"
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants