{"version":3,"file":"MultichainNetworkController.cjs","sourceRoot":"","sources":["../../src/MultichainNetworkController/MultichainNetworkController.ts"],"names":[],"mappings":"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","sourcesContent":["import { BaseController } from '@metamask/base-controller';\nimport { isEvmAccountType } from '@metamask/keyring-api';\nimport type { InternalAccount } from '@metamask/keyring-internal-api';\nimport type { NetworkClientId } from '@metamask/network-controller';\nimport { type CaipChainId, isCaipChainId } from '@metamask/utils';\n\nimport {\n  type ActiveNetworksByAddress,\n  toAllowedCaipAccountIds,\n  toActiveNetworksByAddress,\n} from '../api/accounts-api';\nimport {\n  AVAILABLE_MULTICHAIN_NETWORK_CONFIGURATIONS,\n  MULTICHAIN_NETWORK_CONTROLLER_METADATA,\n  getDefaultMultichainNetworkControllerState,\n} from '../constants';\nimport type { AbstractMultichainNetworkService } from '../MultichainNetworkService/AbstractMultichainNetworkService';\nimport {\n  MULTICHAIN_NETWORK_CONTROLLER_NAME,\n  type MultichainNetworkControllerState,\n  type MultichainNetworkControllerMessenger,\n  type SupportedCaipChainId,\n} from '../types';\nimport {\n  checkIfSupportedCaipChainId,\n  getChainIdForNonEvmAddress,\n  convertEvmCaipToHexChainId,\n  isEvmCaipChainId,\n} from '../utils';\n\n/**\n * The MultichainNetworkController is responsible for fetching and caching account\n * balances.\n */\nexport class MultichainNetworkController extends BaseController<\n  typeof MULTICHAIN_NETWORK_CONTROLLER_NAME,\n  MultichainNetworkControllerState,\n  MultichainNetworkControllerMessenger\n> {\n  readonly #networkService: AbstractMultichainNetworkService;\n\n  constructor({\n    messenger,\n    state,\n    networkService,\n  }: {\n    messenger: MultichainNetworkControllerMessenger;\n    state?: Omit<\n      Partial<MultichainNetworkControllerState>,\n      'multichainNetworkConfigurationsByChainId'\n    >;\n    networkService: AbstractMultichainNetworkService;\n  }) {\n    super({\n      messenger,\n      name: MULTICHAIN_NETWORK_CONTROLLER_NAME,\n      metadata: MULTICHAIN_NETWORK_CONTROLLER_METADATA,\n      state: {\n        ...getDefaultMultichainNetworkControllerState(),\n        ...state,\n        // We can keep the current network as a hardcoded value\n        // since it is not expected to add/remove networks yet.\n        multichainNetworkConfigurationsByChainId:\n          AVAILABLE_MULTICHAIN_NETWORK_CONFIGURATIONS,\n      },\n    });\n\n    this.#networkService = networkService;\n    this.#subscribeToMessageEvents();\n    this.#registerMessageHandlers();\n  }\n\n  /**\n   * Sets the active EVM network.\n   *\n   * @param id - The client ID of the EVM network to set active.\n   */\n  async #setActiveEvmNetwork(id: NetworkClientId): Promise<void> {\n    const { selectedNetworkClientId } = this.messagingSystem.call(\n      'NetworkController:getState',\n    );\n\n    const shouldSetEvmActive = !this.state.isEvmSelected;\n    const shouldNotifyNetworkChange = id !== selectedNetworkClientId;\n\n    // No changes needed if EVM is active and network is already selected\n    if (!shouldSetEvmActive && !shouldNotifyNetworkChange) {\n      return;\n    }\n\n    // Update EVM selection state if needed\n    if (shouldSetEvmActive) {\n      this.update((state) => {\n        state.isEvmSelected = true;\n      });\n    }\n\n    // Only notify the network controller if the selected evm network is different\n    if (shouldNotifyNetworkChange) {\n      await this.messagingSystem.call('NetworkController:setActiveNetwork', id);\n    }\n\n    // Only publish the networkDidChange event if either the EVM network is different or we're switching between EVM and non-EVM networks\n    if (shouldSetEvmActive || shouldNotifyNetworkChange) {\n      this.messagingSystem.publish(\n        'MultichainNetworkController:networkDidChange',\n        id,\n      );\n    }\n  }\n\n  /**\n   * Sets the active non-EVM network.\n   *\n   * @param id - The chain ID of the non-EVM network to set active.\n   */\n  #setActiveNonEvmNetwork(id: SupportedCaipChainId): void {\n    if (\n      id === this.state.selectedMultichainNetworkChainId &&\n      !this.state.isEvmSelected\n    ) {\n      // Same non-EVM network is already selected, no need to update\n      return;\n    }\n\n    this.update((state) => {\n      state.selectedMultichainNetworkChainId = id;\n      state.isEvmSelected = false;\n    });\n\n    // Notify listeners that the network changed\n    this.messagingSystem.publish(\n      'MultichainNetworkController:networkDidChange',\n      id,\n    );\n  }\n\n  /**\n   * Sets the active network.\n   *\n   * @param id - The non-EVM Caip chain ID or EVM client ID of the network to set active.\n   * @returns - A promise that resolves when the network is set active.\n   */\n  async setActiveNetwork(\n    id: SupportedCaipChainId | NetworkClientId,\n  ): Promise<void> {\n    if (isCaipChainId(id)) {\n      const isSupportedCaipChainId = checkIfSupportedCaipChainId(id);\n      if (!isSupportedCaipChainId) {\n        throw new Error(`Unsupported Caip chain ID: ${String(id)}`);\n      }\n      return this.#setActiveNonEvmNetwork(id);\n    }\n\n    return await this.#setActiveEvmNetwork(id);\n  }\n\n  /**\n   * Returns the active networks for the available EVM addresses (non-EVM networks will be supported in the future).\n   * Fetches the data from the API and caches it in state.\n   *\n   * @returns A promise that resolves to the active networks for the available addresses\n   */\n  async getNetworksWithTransactionActivityByAccounts(): Promise<ActiveNetworksByAddress> {\n    const accounts = this.messagingSystem.call(\n      'AccountsController:listMultichainAccounts',\n    );\n    if (!accounts || accounts.length === 0) {\n      return this.state.networksWithTransactionActivity;\n    }\n\n    const formattedAccounts = accounts\n      .map((account: InternalAccount) => toAllowedCaipAccountIds(account))\n      .flat();\n\n    const activeNetworks =\n      await this.#networkService.fetchNetworkActivity(formattedAccounts);\n    const formattedNetworks = toActiveNetworksByAddress(activeNetworks);\n\n    this.update((state) => {\n      state.networksWithTransactionActivity = formattedNetworks;\n    });\n\n    return this.state.networksWithTransactionActivity;\n  }\n\n  /**\n   * Removes an EVM network from the list of networks.\n   * This method re-directs the request to the network-controller.\n   *\n   * @param chainId - The chain ID of the network to remove.\n   * @returns - A promise that resolves when the network is removed.\n   */\n  async #removeEvmNetwork(chainId: CaipChainId): Promise<void> {\n    const hexChainId = convertEvmCaipToHexChainId(chainId);\n    const selectedChainId = this.messagingSystem.call(\n      'NetworkController:getSelectedChainId',\n    );\n\n    if (selectedChainId === hexChainId) {\n      // We prevent removing the currently selected network.\n      if (this.state.isEvmSelected) {\n        throw new Error('Cannot remove the currently selected network');\n      }\n\n      // If a non-EVM network is selected, we can delete the currently EVM selected network, but\n      // we automatically switch to EVM mainnet.\n      const ethereumMainnetHexChainId = '0x1'; // TODO: Should probably be a constant.\n      const clientId = this.messagingSystem.call(\n        'NetworkController:findNetworkClientIdByChainId',\n        ethereumMainnetHexChainId,\n      );\n\n      await this.messagingSystem.call(\n        'NetworkController:setActiveNetwork',\n        clientId,\n      );\n    }\n\n    this.messagingSystem.call('NetworkController:removeNetwork', hexChainId);\n  }\n\n  /**\n   * Removes a non-EVM network from the list of networks.\n   * This method is not supported and throws an error.\n   *\n   * @param _chainId - The chain ID of the network to remove.\n   * @throws - An error indicating that removal of non-EVM networks is not supported.\n   */\n  #removeNonEvmNetwork(_chainId: CaipChainId): void {\n    throw new Error('Removal of non-EVM networks is not supported');\n  }\n\n  /**\n   * Removes a network from the list of networks.\n   * It only supports EVM networks.\n   *\n   * @param chainId - The chain ID of the network to remove.\n   * @returns - A promise that resolves when the network is removed.\n   */\n  async removeNetwork(chainId: CaipChainId): Promise<void> {\n    if (isEvmCaipChainId(chainId)) {\n      return await this.#removeEvmNetwork(chainId);\n    }\n\n    return this.#removeNonEvmNetwork(chainId);\n  }\n\n  /**\n   * Handles switching between EVM and non-EVM networks when an account is changed\n   *\n   * @param account - The account that was changed\n   */\n  #handleOnSelectedAccountChange(account: InternalAccount) {\n    const { type: accountType, address: accountAddress, scopes } = account;\n    const isEvmAccount = isEvmAccountType(accountType);\n\n    // Handle switching to EVM network\n    if (isEvmAccount) {\n      if (this.state.isEvmSelected) {\n        // No need to update if already on evm network\n        return;\n      }\n\n      // Make EVM network active\n      this.update((state) => {\n        state.isEvmSelected = true;\n      });\n\n      return;\n    }\n\n    // Handle switching to non-EVM network\n    if (scopes.includes(this.state.selectedMultichainNetworkChainId)) {\n      // No need to update if the account's scope includes the active network\n      this.update((state) => {\n        state.isEvmSelected = false;\n      });\n      return;\n    }\n\n    const nonEvmChainId = getChainIdForNonEvmAddress(accountAddress);\n    this.update((state) => {\n      state.selectedMultichainNetworkChainId = nonEvmChainId;\n      state.isEvmSelected = false;\n    });\n\n    // No need to publish NetworkController:setActiveNetwork because EVM accounts falls back to use the last selected EVM network\n    // DO NOT publish MultichainNetworkController:networkDidChange to prevent circular listener loops\n  }\n\n  /**\n   * Subscribes to message events.\n   */\n  #subscribeToMessageEvents() {\n    // Handle network switch when account is changed\n    this.messagingSystem.subscribe(\n      'AccountsController:selectedAccountChange',\n      (account) => this.#handleOnSelectedAccountChange(account),\n    );\n  }\n\n  /**\n   * Registers message handlers.\n   */\n  #registerMessageHandlers() {\n    this.messagingSystem.registerActionHandler(\n      'MultichainNetworkController:setActiveNetwork',\n      this.setActiveNetwork.bind(this),\n    );\n    this.messagingSystem.registerActionHandler(\n      'MultichainNetworkController:getNetworksWithTransactionActivityByAccounts',\n      this.getNetworksWithTransactionActivityByAccounts.bind(this),\n    );\n  }\n}\n"]}