Skip to content

chore: create network enablement controller #6028

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 37 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
77477b2
chore: create network enablement controller
salimtb Jun 24, 2025
2bf8d2f
fix: add changelog
salimtb Jun 24, 2025
0472fdb
fix: clean up
salimtb Jun 24, 2025
1fdf600
fix: fix changelog
salimtb Jun 24, 2025
5855bf1
fix: fix PR comments
salimtb Jun 26, 2025
da23ff3
fix: fix comments
salimtb Jun 26, 2025
7653511
fix: clean
salimtb Jun 26, 2025
d98194c
fix: fix prettier
salimtb Jun 26, 2025
e701107
fix: clean up
salimtb Jun 26, 2025
cb878b3
fix: clean up
salimtb Jun 27, 2025
ec4ff6c
fix: clean up
salimtb Jun 27, 2025
0670f59
fix: fix
salimtb Jun 27, 2025
39aa1af
fix: clean up
salimtb Jun 27, 2025
52333b0
fix: fix PR comments
salimtb Jun 30, 2025
70ed7c0
fix: fix comment
salimtb Jun 30, 2025
aeced8d
fix: fix PR comments
salimtb Jun 30, 2025
e75b6b2
fix: fix PR comments
salimtb Jun 30, 2025
c4588b3
fix: fix tests
salimtb Jun 30, 2025
3979af4
fix: clean up
salimtb Jun 30, 2025
c55fc62
fix: fix linter
salimtb Jun 30, 2025
5b8e81a
Merge branch 'main' into chore/network-enablement-controller
salimtb Jul 22, 2025
38f4ca9
fix: fix yarn lock
salimtb Jul 22, 2025
535952d
fix: fix formating
salimtb Jul 22, 2025
e7b723f
fix: increase test coverage
salimtb Jul 22, 2025
2eb5a88
fix: fix package
salimtb Jul 22, 2025
303ac2a
fix: fix build
salimtb Jul 22, 2025
e672af4
fix: fix PR comments
salimtb Jul 24, 2025
e08e494
fix: fix PR comments
salimtb Jul 24, 2025
66dafc3
fix: fix PR comments
salimtb Jul 24, 2025
aecb083
fix: fix resolution
salimtb Jul 24, 2025
0649edb
clean up
salimtb Jul 24, 2025
23aa99f
Merge branch 'main' into chore/network-enablement-controller
salimtb Jul 24, 2025
3d8813c
fix: clean up
salimtb Jul 24, 2025
e3424ee
fix: fix lick file
salimtb Jul 24, 2025
6d8897f
fix: bump multichain network controller
salimtb Jul 24, 2025
3bd1ba0
fix: clean up
salimtb Jul 24, 2025
9f10a92
fix: fix lint
salimtb Jul 24, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

## Assets Team
/packages/assets-controllers @MetaMask/metamask-assets
/packages/network-enablement-controller @MetaMask/metamask-assets

## Confirmations Team
/packages/address-book-controller @MetaMask/confirmations
Expand Down Expand Up @@ -157,3 +158,5 @@
/packages/foundryup/CHANGELOG.md @MetaMask/mobile-platform @MetaMask/extension-platform @MetaMask/core-platform
/packages/seedless-onboarding-controller/package.json @MetaMask/web3auth @MetaMask/core-platform
/packages/seedless-onboarding-controller/CHANGELOG.md @MetaMask/web3auth @MetaMask/core-platform
/packages/network-enablement-controller/package.json @MetaMask/metamask-assets @MetaMask/core-platform
/packages/network-enablement-controller/CHANGELOG.md @MetaMask/metamask-assets @MetaMask/core-platform
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ Each package in this repository has its own README where you can find installati
- [`@metamask/multichain-transactions-controller`](packages/multichain-transactions-controller)
- [`@metamask/name-controller`](packages/name-controller)
- [`@metamask/network-controller`](packages/network-controller)
- [`@metamask/network-enablement-controller`](packages/network-enablement-controller)
- [`@metamask/notification-services-controller`](packages/notification-services-controller)
- [`@metamask/permission-controller`](packages/permission-controller)
- [`@metamask/permission-log-controller`](packages/permission-log-controller)
Expand Down Expand Up @@ -113,6 +114,7 @@ linkStyle default opacity:0.5
multichain_transactions_controller(["@metamask/multichain-transactions-controller"]);
name_controller(["@metamask/name-controller"]);
network_controller(["@metamask/network-controller"]);
network_enablement_controller(["@metamask/network-enablement-controller"]);
notification_services_controller(["@metamask/notification-services-controller"]);
permission_controller(["@metamask/permission-controller"]);
permission_log_controller(["@metamask/permission-log-controller"]);
Expand Down Expand Up @@ -228,6 +230,10 @@ linkStyle default opacity:0.5
network_controller --> eth_json_rpc_provider;
network_controller --> json_rpc_engine;
network_controller --> error_reporting_service;
network_enablement_controller --> base_controller;
network_enablement_controller --> controller_utils;
network_enablement_controller --> multichain_network_controller;
network_enablement_controller --> network_controller;
notification_services_controller --> base_controller;
notification_services_controller --> controller_utils;
notification_services_controller --> keyring_controller;
Expand Down
14 changes: 14 additions & 0 deletions packages/network-enablement-controller/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Added

- Initial release ([#6028](https://github.com/MetaMask/core/pull/6028))

[Unreleased]: https://github.com/MetaMask/core/
20 changes: 20 additions & 0 deletions packages/network-enablement-controller/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
MIT License

Copyright (c) 2025 MetaMask

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
178 changes: 178 additions & 0 deletions packages/network-enablement-controller/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
# Network Enablement Controller

A MetaMask controller for managing network enablement state across different blockchain networks.

## Overview

The NetworkEnablementController tracks which networks are enabled/disabled for the user and provides methods to toggle network states. It supports both EVM (EIP-155) and non-EVM networks like Solana.

## Installation

```bash
npm install @metamask/network-enablement-controller
```

## Usage

### Basic Controller Usage

```typescript
import { NetworkEnablementController } from '@metamask/network-enablement-controller';

// Create controller instance
const controller = new NetworkEnablementController({
messenger,
state: {
enabledNetworkMap: {
eip155: {
'0x1': true, // Ethereum mainnet enabled
'0xa': false, // Optimism disabled
},
solana: {
'solana:mainnet': true,
},
},
},
});

// Enable a network
controller.setEnabledNetwork('0x1'); // Hex format for EVM
controller.setEnabledNetwork('eip155:1'); // CAIP-2 format for EVM
controller.setEnabledNetwork('solana:mainnet'); // CAIP-2 format for Solana

// Disable a network
controller.setDisabledNetwork('0xa');

// Check if network is enabled
const isEnabled = controller.isNetworkEnabled('0x1');

// Get all enabled networks for a namespace
const evmNetworks = controller.getEnabledNetworksForNamespace('eip155');

// Get all enabled networks across all namespaces
const allNetworks = controller.getAllEnabledNetworks();
```

### Using Selectors (Redux-style)

The controller also provides selectors that can be used in Redux contexts or any state management system:

```typescript
import {
selectIsNetworkEnabled,
selectAllEnabledNetworks,
selectEnabledNetworksForNamespace,
selectEnabledEvmNetworks,
selectEnabledSolanaNetworks,
} from '@metamask/network-enablement-controller';

// Get controller state
const state = controller.state;

// Check if a specific network is enabled
const isEthereumEnabled = selectIsNetworkEnabled('0x1')(state);
const isSolanaEnabled = selectIsNetworkEnabled('solana:mainnet')(state);

// Get all enabled networks across all namespaces
const allEnabledNetworks = selectAllEnabledNetworks(state);
// Returns: { eip155: ['0x1'], solana: ['solana:mainnet'] }

// Get enabled networks for a specific namespace
const evmNetworks = selectEnabledNetworksForNamespace('eip155')(state);
const solanaNetworks = selectEnabledNetworksForNamespace('solana')(state);

// Convenience selectors for specific network types
const enabledEvmNetworks = selectEnabledEvmNetworks(state);
const enabledSolanaNetworks = selectEnabledSolanaNetworks(state);

// Get total count of enabled networks
const totalEnabled = selectEnabledNetworksCount(state);

// Check if any networks are enabled for a namespace
const hasEvmNetworks = selectHasEnabledNetworksForNamespace('eip155')(state);
```

## API Reference

### Controller Methods

#### `setEnabledNetwork(chainId: Hex | CaipChainId): void`

Enables a network for the user. Accepts either Hex chain IDs (for EVM networks) or CAIP-2 chain IDs (for any blockchain network).

#### `setDisabledNetwork(chainId: Hex | CaipChainId): void`

Disables a network for the user. Prevents disabling the last remaining enabled network.

#### `isNetworkEnabled(chainId: Hex | CaipChainId): boolean`

Checks if a network is currently enabled. Returns false for unknown networks.

#### `getEnabledNetworksForNamespace(namespace: CaipNamespace): string[]`

Gets all enabled networks for a specific namespace.

#### `getAllEnabledNetworks(): Record<CaipNamespace, string[]>`

Gets all enabled networks across all namespaces.

### Selectors

#### `selectIsNetworkEnabled(chainId: Hex | CaipChainId)`

Returns a selector function that checks if a specific network is enabled.

#### `selectAllEnabledNetworks`

Returns a selector function that gets all enabled networks across all namespaces.

#### `selectEnabledNetworksForNamespace(namespace: CaipNamespace)`

Returns a selector function that gets enabled networks for a specific namespace.

#### `selectEnabledNetworksCount`

Returns a selector function that gets the total count of enabled networks.

#### `selectHasEnabledNetworksForNamespace(namespace: CaipNamespace)`

Returns a selector function that checks if any networks are enabled for a namespace.

#### `selectEnabledEvmNetworks`

Returns a selector function that gets all enabled EVM networks.

#### `selectEnabledSolanaNetworks`

Returns a selector function that gets all enabled Solana networks.

## Chain ID Formats

The controller supports two chain ID formats:

1. **Hex format**: Traditional EVM chain IDs (e.g., `'0x1'` for Ethereum mainnet)
2. **CAIP-2 format**: Chain Agnostic Improvement Proposal format (e.g., `'eip155:1'` for Ethereum mainnet, `'solana:mainnet'` for Solana)

## Network Types

### EVM Networks (eip155 namespace)

- Ethereum Mainnet: `'0x1'` or `'eip155:1'`
- Optimism: `'0xa'` or `'eip155:10'`
- Arbitrum One: `'0xa4b1'` or `'eip155:42161'`

### Solana Networks (solana namespace)

- Solana Mainnet: `'solana:mainnet'`
- Solana Testnet: `'solana:testnet'`

## State Persistence

The controller state is automatically persisted and restored between sessions. The `enabledNetworkMap` is stored anonymously to protect user privacy.

## Safety Features

- **At least one network enabled**: The controller ensures at least one network is always enabled
- **Unknown network protection**: Prevents enabling networks not configured in the system
- **Exclusive mode**: When enabling non-popular networks, all other networks are disabled
- **Last network protection**: Prevents disabling the last remaining enabled network
26 changes: 26 additions & 0 deletions packages/network-enablement-controller/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* For a detailed explanation regarding each configuration property and type check, visit:
* https://jestjs.io/docs/configuration
*/

const merge = require('deepmerge');
const path = require('path');

const baseConfig = require('../../jest.config.packages');

const displayName = path.basename(__dirname);

module.exports = merge(baseConfig, {
// The display name when running multiple projects
displayName,

// An object that configures minimum threshold enforcement for coverage results
coverageThreshold: {
global: {
branches: 88.47,
Copy link
Contributor

Choose a reason for hiding this comment

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

Any way that we can start this coverage off at 100%? Not doing this will make it difficult to identify drops in coverage for new PRs later (i.e. you'll probably need to sort through the coverage report to figure out which changes introduced the drops).

I've identified the missing tests below.

functions: 97.43,
lines: 94.47,
statements: 94.29,
},
},
});
79 changes: 79 additions & 0 deletions packages/network-enablement-controller/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
{
"name": "@metamask/network-enablement-controller",
"version": "0.0.0",
"description": "Provides an interface to the currently enabled network using a MetaMask-compatible provider object",
"keywords": [
"MetaMask",
"Ethereum"
],
"homepage": "https://github.com/MetaMask/core/tree/main/packages/network-enablement-controller#readme",
"bugs": {
"url": "https://github.com/MetaMask/core/issues"
},
"repository": {
"type": "git",
"url": "https://github.com/MetaMask/core.git"
},
"license": "MIT",
"sideEffects": false,
"exports": {
".": {
"import": {
"types": "./dist/index.d.mts",
"default": "./dist/index.mjs"
},
"require": {
"types": "./dist/index.d.cts",
"default": "./dist/index.cjs"
}
},
"./package.json": "./package.json"
},
"main": "./dist/index.cjs",
"types": "./dist/index.d.cts",
"files": [
"dist/"
],
"scripts": {
"build": "ts-bridge --project tsconfig.build.json --verbose --clean --no-references",
"build:docs": "typedoc",
"changelog:update": "../../scripts/update-changelog.sh @metamask/network-enablement-controller",
"changelog:validate": "../../scripts/validate-changelog.sh @metamask/network-enablement-controller",
"publish:preview": "yarn npm publish --tag preview",
"since-latest-release": "../../scripts/since-latest-release.sh",
"test": "NODE_OPTIONS=--experimental-vm-modules jest --reporters=jest-silent-reporter",
"test:clean": "NODE_OPTIONS=--experimental-vm-modules jest --clearCache",
"test:verbose": "NODE_OPTIONS=--experimental-vm-modules jest --verbose",
"test:watch": "NODE_OPTIONS=--experimental-vm-modules jest --watch"
},
"devDependencies": {
"@metamask/auto-changelog": "^3.4.4",
"@types/jest": "^27.4.1",
"deepmerge": "^4.2.2",
"jest": "^27.5.1",
"ts-jest": "^27.1.4",
"typedoc": "^0.24.8",
"typedoc-plugin-missing-exports": "^2.0.0",
"typescript": "~5.2.2"
},
"dependencies": {
"@metamask/base-controller": "^8.0.1",
"@metamask/controller-utils": "^11.10.0",
"@metamask/keyring-api": "^18.0.0",
"@metamask/multichain-network-controller": "^0.9.0",
"@metamask/network-controller": "^24.0.0",
"@metamask/utils": "^11.2.0",
"reselect": "^5.1.1"
},
"peerDependencies": {
"@metamask/multichain-network-controller": "^0.9.0",
"@metamask/network-controller": "^24.0.0"
},
"engines": {
"node": "^18.18 || >=20"
},
"publishConfig": {
"access": "public",
"registry": "https://registry.npmjs.org/"
}
}
Loading
Loading