{"version":3,"file":"AccountActivityService.cjs","sourceRoot":"","sources":["../../src/ws/AccountActivityService.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Account Activity Service for monitoring account transactions and balance changes\n *\n * This service subscribes to account activity and receives all transactions\n * and balance updates for those accounts via the comprehensive AccountActivityMessage format.\n */\n\nimport type {\n  AccountsControllerGetSelectedAccountAction,\n  AccountsControllerSelectedAccountChangeEvent,\n} from '@metamask/accounts-controller';\nimport type { TraceCallback } from '@metamask/controller-utils';\nimport type { InternalAccount } from '@metamask/keyring-internal-api';\nimport type { Messenger } from '@metamask/messenger';\n\nimport { projectLogger, createModuleLogger } from '../logger';\nimport type {\n  Transaction,\n  AccountActivityMessage,\n  BalanceUpdate,\n} from '../types';\nimport type { AccountActivityServiceMethodActions } from './AccountActivityService-method-action-types';\nimport type {\n  WebSocketConnectionInfo,\n  BackendWebSocketServiceConnectionStateChangedEvent,\n  ServerNotificationMessage,\n} from './BackendWebSocketService';\nimport { WebSocketState } from './BackendWebSocketService';\nimport type { BackendWebSocketServiceMethodActions } from './BackendWebSocketService-method-action-types';\n\n// =============================================================================\n// Types and Constants\n// =============================================================================\n\n/**\n * System notification data for chain status updates\n */\nexport type SystemNotificationData = {\n  /** Array of chain IDs affected (e.g., ['eip155:137', 'eip155:1']) */\n  chainIds: string[];\n  /** Status of the chains: 'down' or 'up' */\n  status: 'down' | 'up';\n  /** Timestamp of the notification */\n  timestamp?: number;\n};\n\nconst SERVICE_NAME = 'AccountActivityService';\n\nconst log = createModuleLogger(projectLogger, SERVICE_NAME);\n\nconst MESSENGER_EXPOSED_METHODS = ['subscribe', 'unsubscribe'] as const;\n\nconst SUBSCRIPTION_NAMESPACE = 'account-activity.v1';\n\n/**\n * Account subscription options\n */\nexport type SubscriptionOptions = {\n  address: string; // Should be in CAIP-10 format, e.g., \"eip155:0:0x1234...\" or \"solana:0:ABC123...\"\n};\n\n/**\n * Configuration options for the account activity service\n */\nexport type AccountActivityServiceOptions = {\n  /** Custom subscription namespace (default: 'account-activity.v1') */\n  subscriptionNamespace?: string;\n  /** Optional callback to trace performance of account activity operations (default: no-op) */\n  traceFn?: TraceCallback;\n};\n\n// =============================================================================\n// Action and Event Types\n// =============================================================================\n\n// Action types for the messaging system - using generated method actions\nexport type AccountActivityServiceActions = AccountActivityServiceMethodActions;\n\n// Allowed actions that AccountActivityService can call on other controllers\nexport const ACCOUNT_ACTIVITY_SERVICE_ALLOWED_ACTIONS = [\n  'AccountsController:getSelectedAccount',\n  'BackendWebSocketService:connect',\n  'BackendWebSocketService:forceReconnection',\n  'BackendWebSocketService:subscribe',\n  'BackendWebSocketService:getConnectionInfo',\n  'BackendWebSocketService:channelHasSubscription',\n  'BackendWebSocketService:getSubscriptionsByChannel',\n  'BackendWebSocketService:findSubscriptionsByChannelPrefix',\n  'BackendWebSocketService:addChannelCallback',\n  'BackendWebSocketService:removeChannelCallback',\n] as const;\n\n// Allowed events that AccountActivityService can listen to\nexport const ACCOUNT_ACTIVITY_SERVICE_ALLOWED_EVENTS = [\n  'AccountsController:selectedAccountChange',\n  'BackendWebSocketService:connectionStateChanged',\n] as const;\n\nexport type AllowedActions =\n  | AccountsControllerGetSelectedAccountAction\n  | BackendWebSocketServiceMethodActions;\n\n// Event types for the messaging system\n\nexport type AccountActivityServiceTransactionUpdatedEvent = {\n  type: `AccountActivityService:transactionUpdated`;\n  payload: [Transaction];\n};\n\nexport type AccountActivityServiceBalanceUpdatedEvent = {\n  type: `AccountActivityService:balanceUpdated`;\n  payload: [{ address: string; chain: string; updates: BalanceUpdate[] }];\n};\n\nexport type AccountActivityServiceSubscriptionErrorEvent = {\n  type: `AccountActivityService:subscriptionError`;\n  payload: [{ addresses: string[]; error: string; operation: string }];\n};\n\nexport type AccountActivityServiceStatusChangedEvent = {\n  type: `AccountActivityService:statusChanged`;\n  payload: [\n    {\n      chainIds: string[];\n      status: 'up' | 'down';\n      timestamp?: number;\n    },\n  ];\n};\n\nexport type AccountActivityServiceEvents =\n  | AccountActivityServiceTransactionUpdatedEvent\n  | AccountActivityServiceBalanceUpdatedEvent\n  | AccountActivityServiceSubscriptionErrorEvent\n  | AccountActivityServiceStatusChangedEvent;\n\nexport type AllowedEvents =\n  | AccountsControllerSelectedAccountChangeEvent\n  | BackendWebSocketServiceConnectionStateChangedEvent;\n\nexport type AccountActivityServiceMessenger = Messenger<\n  typeof SERVICE_NAME,\n  AccountActivityServiceActions | AllowedActions,\n  AccountActivityServiceEvents | AllowedEvents\n>;\n\n// =============================================================================\n// Main Service Class\n// =============================================================================\n\n/**\n * High-performance service for real-time account activity monitoring using optimized\n * WebSocket subscriptions with direct callback routing. Automatically subscribes to\n * the currently selected account and switches subscriptions when the selected account changes.\n * Receives transactions and balance updates using the comprehensive AccountActivityMessage format.\n *\n * Performance Features:\n * - Direct callback routing (no EventEmitter overhead)\n * - Minimal subscription tracking (no duplication with BackendWebSocketService)\n * - Optimized cleanup for mobile environments\n * - Single-account subscription (only selected account)\n * - Comprehensive balance updates with transfer tracking\n *\n * Architecture:\n * - Uses messenger pattern to communicate with BackendWebSocketService\n * - AccountActivityService tracks channel-to-subscriptionId mappings via messenger calls\n * - Automatically subscribes to selected account on initialization\n * - Switches subscriptions when selected account changes\n * - No direct dependency on BackendWebSocketService (uses messenger instead)\n *\n * @example\n * ```typescript\n * const service = new AccountActivityService({\n *   messenger: activityMessenger,\n * });\n *\n * // Service automatically subscribes to the currently selected account\n * // When user switches accounts, service automatically resubscribes\n *\n * // All transactions and balance updates are received via optimized\n * // WebSocket callbacks and processed with zero-allocation routing\n * // Balance updates include comprehensive transfer details and post-transaction balances\n * ```\n */\nexport class AccountActivityService {\n  /**\n   * The name of the service.\n   */\n  readonly name = SERVICE_NAME;\n\n  readonly #messenger: AccountActivityServiceMessenger;\n\n  readonly #options: Required<Omit<AccountActivityServiceOptions, 'traceFn'>>;\n\n  readonly #trace: TraceCallback;\n\n  // Track chains that are currently up (based on system notifications)\n  readonly #chainsUp: Set<string> = new Set();\n\n  // =============================================================================\n  // Constructor and Initialization\n  // =============================================================================\n\n  /**\n   * Creates a new Account Activity service instance\n   *\n   * @param options - Configuration options including messenger\n   */\n  constructor(\n    options: AccountActivityServiceOptions & {\n      messenger: AccountActivityServiceMessenger;\n    },\n  ) {\n    this.#messenger = options.messenger;\n\n    // Set configuration with defaults\n    this.#options = {\n      subscriptionNamespace:\n        options.subscriptionNamespace ?? SUBSCRIPTION_NAMESPACE,\n    };\n\n    // Default to no-op trace function to keep core platform-agnostic\n    this.#trace =\n      options.traceFn ??\n      // eslint-disable-next-line @typescript-eslint/no-explicit-any\n      (((_request: any, fn?: any) => fn?.()) as TraceCallback);\n\n    this.#messenger.registerMethodActionHandlers(\n      this,\n      MESSENGER_EXPOSED_METHODS,\n    );\n    this.#messenger.subscribe(\n      'AccountsController:selectedAccountChange',\n      // Promise result intentionally not awaited\n      // eslint-disable-next-line @typescript-eslint/no-misused-promises\n      async (account: InternalAccount) =>\n        await this.#handleSelectedAccountChange(account),\n    );\n    this.#messenger.subscribe(\n      'BackendWebSocketService:connectionStateChanged',\n      // Promise result intentionally not awaited\n      // eslint-disable-next-line @typescript-eslint/no-misused-promises\n      (connectionInfo: WebSocketConnectionInfo) =>\n        this.#handleWebSocketStateChange(connectionInfo),\n    );\n    this.#messenger.call('BackendWebSocketService:addChannelCallback', {\n      channelName: `system-notifications.v1.${this.#options.subscriptionNamespace}`,\n      callback: (notification: ServerNotificationMessage) =>\n        this.#handleSystemNotification(notification),\n    });\n  }\n\n  // =============================================================================\n  // Account Subscription Methods\n  // =============================================================================\n\n  /**\n   * Subscribe to account activity (transactions and balance updates)\n   * Address should be in CAIP-10 format (e.g., \"eip155:0:0x1234...\" or \"solana:0:ABC123...\")\n   *\n   * @param subscription - Account subscription configuration with address\n   */\n  async subscribe(subscription: SubscriptionOptions): Promise<void> {\n    try {\n      await this.#messenger.call('BackendWebSocketService:connect');\n\n      // Create channel name from address\n      const channel = `${this.#options.subscriptionNamespace}.${subscription.address}`;\n\n      // Check if already subscribed\n      if (\n        this.#messenger.call(\n          'BackendWebSocketService:channelHasSubscription',\n          channel,\n        )\n      ) {\n        return;\n      }\n\n      // Create subscription using the proper subscribe method (this will be stored in WebSocketService's internal tracking)\n      await this.#messenger.call('BackendWebSocketService:subscribe', {\n        channels: [channel],\n        channelType: this.#options.subscriptionNamespace, // e.g., 'account-activity.v1'\n        callback: (notification: ServerNotificationMessage) => {\n          this.#handleAccountActivityUpdate(\n            notification.data as AccountActivityMessage,\n          );\n        },\n      });\n    } catch (error) {\n      log('Subscription failed, forcing reconnection', { error });\n      await this.#forceReconnection();\n    }\n  }\n\n  /**\n   * Unsubscribe from account activity for specified address\n   * Address should be in CAIP-10 format (e.g., \"eip155:0:0x1234...\" or \"solana:0:ABC123...\")\n   *\n   * @param subscription - Account subscription configuration with address to unsubscribe\n   */\n  async unsubscribe(subscription: SubscriptionOptions): Promise<void> {\n    const { address } = subscription;\n    try {\n      // Find channel for the specified address\n      const channel = `${this.#options.subscriptionNamespace}.${address}`;\n      const subscriptions = this.#messenger.call(\n        'BackendWebSocketService:getSubscriptionsByChannel',\n        channel,\n      );\n\n      if (subscriptions.length === 0) {\n        return;\n      }\n\n      // Fast path: Direct unsubscribe using stored unsubscribe function\n      // Unsubscribe from all matching subscriptions\n      for (const subscriptionInfo of subscriptions) {\n        await subscriptionInfo.unsubscribe();\n      }\n    } catch (error) {\n      log('Unsubscription failed, forcing reconnection', { error });\n      await this.#forceReconnection();\n    }\n  }\n\n  // =============================================================================\n  // Private Methods - Event Handlers\n  // =============================================================================\n\n  /**\n   * Handle account activity updates (transactions + balance changes)\n   * Processes the comprehensive AccountActivityMessage format with detailed balance updates and transfers\n   *\n   * @param payload - The account activity message containing transaction and balance updates\n   * @example AccountActivityMessage format handling:\n   * Input: {\n   *   address: \"0xd14b52362b5b777ffa754c666ddec6722aaeee08\",\n   *   tx: { id: \"0x1cde...\", chain: \"eip155:8453\", status: \"confirmed\", timestamp: 1760099871, ... },\n   *   updates: [{\n   *     asset: { fungible: true, type: \"eip155:8453/erc20:0x833...\", unit: \"USDC\", decimals: 6 },\n   *     postBalance: { amount: \"0xc350\" },\n   *     transfers: [{ from: \"0x7b07...\", to: \"0xd14b...\", amount: \"0x2710\" }]\n   *   }]\n   * }\n   * Output: Transaction and balance updates published separately\n   */\n  #handleAccountActivityUpdate(payload: AccountActivityMessage): void {\n    const { address, tx, updates } = payload;\n\n    // Calculate time elapsed between transaction time and message receipt\n    const txTimestampMs = tx.timestamp * 1000; // Convert Unix timestamp (seconds) to milliseconds\n    const elapsedMs = Date.now() - txTimestampMs;\n\n    log('Handling account activity update', {\n      address,\n      updateCount: updates.length,\n      elapsedMs,\n    });\n\n    // Trace message receipt with latency from transaction time to now\n    // Promise result intentionally not awaited\n    // eslint-disable-next-line @typescript-eslint/no-floating-promises\n    this.#trace(\n      {\n        name: `${SERVICE_NAME} Transaction Message`,\n        data: {\n          chain: tx.chain,\n          status: tx.status,\n          elapsed_ms: elapsedMs,\n        },\n        tags: {\n          service: SERVICE_NAME,\n          notification_type: this.#options.subscriptionNamespace,\n        },\n      },\n      () => {\n        // Process transaction update\n        this.#messenger.publish(\n          `AccountActivityService:transactionUpdated`,\n          tx,\n        );\n\n        // Publish comprehensive balance updates with transfer details\n        this.#messenger.publish(`AccountActivityService:balanceUpdated`, {\n          address,\n          chain: tx.chain,\n          updates,\n        });\n      },\n    );\n  }\n\n  /**\n   * Handle selected account change event\n   *\n   * @param newAccount - The newly selected account\n   */\n  async #handleSelectedAccountChange(\n    newAccount: InternalAccount | null,\n  ): Promise<void> {\n    if (!newAccount?.address) {\n      return;\n    }\n\n    try {\n      // Convert new account to CAIP-10 format\n      const newAddress = this.#convertToCaip10Address(newAccount);\n\n      // First, unsubscribe from all current account activity subscriptions to avoid multiple subscriptions\n      await this.#unsubscribeFromAllAccountActivity();\n\n      // Then, subscribe to the new selected account\n      await this.subscribe({ address: newAddress });\n    } catch (error) {\n      log('Account change failed', { error });\n    }\n  }\n\n  /**\n   * Handle system notification for chain status changes\n   * Publishes only the status change (delta) for affected chains\n   *\n   * @param notification - Server notification message containing chain status updates and timestamp\n   */\n  #handleSystemNotification(notification: ServerNotificationMessage): void {\n    const data = notification.data as SystemNotificationData;\n    const { timestamp } = notification;\n\n    // Validate required fields\n    if (!data.chainIds || !Array.isArray(data.chainIds) || !data.status) {\n      throw new Error(\n        'Invalid system notification data: missing chainIds or status',\n      );\n    }\n\n    // Track chain status\n    if (data.status === 'up') {\n      for (const chainId of data.chainIds) {\n        this.#chainsUp.add(chainId);\n      }\n    } else {\n      for (const chainId of data.chainIds) {\n        this.#chainsUp.delete(chainId);\n      }\n    }\n\n    // Publish status change directly (delta update)\n    this.#messenger.publish(`AccountActivityService:statusChanged`, {\n      chainIds: data.chainIds,\n      status: data.status,\n      timestamp,\n    });\n\n    log(\n      `WebSocket status change - Published tracked chains as ${data.status}`,\n      {\n        count: data.chainIds.length,\n        chains: data.chainIds,\n        status: data.status,\n      },\n    );\n  }\n\n  /**\n   * Handle WebSocket connection state changes for fallback polling and resubscription\n   *\n   * @param connectionInfo - WebSocket connection state information\n   */\n  async #handleWebSocketStateChange(\n    connectionInfo: WebSocketConnectionInfo,\n  ): Promise<void> {\n    const { state } = connectionInfo;\n\n    if (state === WebSocketState.CONNECTED) {\n      // WebSocket connected - resubscribe to selected account\n      // The system notification will automatically provide the list of chains that are up\n      await this.#subscribeToSelectedAccount();\n    } else if (state === WebSocketState.DISCONNECTED) {\n      // On disconnect, flush all tracked chains as down\n      const chainsToMarkDown = Array.from(this.#chainsUp);\n\n      if (chainsToMarkDown.length > 0) {\n        this.#messenger.publish(`AccountActivityService:statusChanged`, {\n          chainIds: chainsToMarkDown,\n          status: 'down',\n          timestamp: Date.now(),\n        });\n\n        log('WebSocket disconnection - Published tracked chains as down', {\n          count: chainsToMarkDown.length,\n          chains: chainsToMarkDown,\n        });\n\n        // Clear the tracking set since all chains are now down\n        this.#chainsUp.clear();\n      }\n    }\n  }\n\n  // =============================================================================\n  // Private Methods - Subscription Management\n  // =============================================================================\n\n  /**\n   * Subscribe to the currently selected account only\n   */\n  async #subscribeToSelectedAccount(): Promise<void> {\n    const selectedAccount = this.#messenger.call(\n      'AccountsController:getSelectedAccount',\n    );\n\n    if (!selectedAccount?.address) {\n      return;\n    }\n\n    // Convert to CAIP-10 format and subscribe\n    const address = this.#convertToCaip10Address(selectedAccount);\n    await this.subscribe({ address });\n  }\n\n  /**\n   * Unsubscribe from all account activity subscriptions for this service\n   * Finds all channels matching the service's namespace and unsubscribes from them\n   */\n  async #unsubscribeFromAllAccountActivity(): Promise<void> {\n    const accountActivitySubscriptions = this.#messenger.call(\n      'BackendWebSocketService:findSubscriptionsByChannelPrefix',\n      this.#options.subscriptionNamespace,\n    );\n\n    // Unsubscribe from all matching subscriptions\n    for (const subscription of accountActivitySubscriptions) {\n      await subscription.unsubscribe();\n    }\n  }\n\n  // =============================================================================\n  // Private Methods - Utility Functions\n  // =============================================================================\n\n  /**\n   * Convert an InternalAccount address to CAIP-10 format or raw address\n   *\n   * @param account - The internal account to convert\n   * @returns The CAIP-10 formatted address or raw address\n   */\n  #convertToCaip10Address(account: InternalAccount): string {\n    // Check if account has EVM scopes\n    if (account.scopes.some((scope) => scope.startsWith('eip155:'))) {\n      // CAIP-10 format: eip155:0:address (subscribe to all EVM chains)\n      return `eip155:0:${account.address}`;\n    }\n\n    // Check if account has Solana scopes\n    if (account.scopes.some((scope) => scope.startsWith('solana:'))) {\n      // CAIP-10 format: solana:0:address (subscribe to all Solana chains)\n      return `solana:0:${account.address}`;\n    }\n\n    // For other chains or unknown scopes, return raw address\n    return account.address;\n  }\n\n  /**\n   * Force WebSocket reconnection to clean up subscription state\n   */\n  async #forceReconnection(): Promise<void> {\n    log('Forcing WebSocket reconnection to clean up subscription state');\n\n    // Use the dedicated forceReconnection method which performs a controlled\n    // disconnect-then-connect sequence to clean up subscription state\n    await this.#messenger.call('BackendWebSocketService:forceReconnection');\n  }\n\n  // =============================================================================\n  // Public Methods - Cleanup\n  // =============================================================================\n\n  /**\n   * Destroy the service and clean up all resources\n   * Optimized for fast cleanup during service destruction or mobile app termination\n   */\n  destroy(): void {\n    // Clean up system notification callback\n    this.#messenger.call(\n      'BackendWebSocketService:removeChannelCallback',\n      `system-notifications.v1.${this.#options.subscriptionNamespace}`,\n    );\n  }\n}\n"]}