1. 概述
NetworkController 是 MetaMask 中负责管理多链网络连接的核心控制器,它提供了完整的网络管理功能,包括网络切换、RPC 端点管理、网络状态监控等。
源码位置 : github.com/MetaMask/co...
2. 核心架构
2.1 主要组件
NetworkController 继承自 BaseController,主要包含以下核心组件:
typescript
class NetworkController extends BaseController {
// 网络客户端注册表 - 管理所有网络客户端实例
#networkClientRegistry: AutoManagedNetworkClientRegistry;
// 当前选中的网络客户端ID
#selectedNetworkClientId: NetworkClientId;
// 网络配置映射 - 按网络客户端ID索引配置
#networkConfigurationsByNetworkClientId: Map<NetworkClientId, NetworkConfiguration>;
// RPC故障转移开关
#isRpcFailoverEnabled: boolean;
}
2.2 状态结构
网络控制器的状态包含三个主要部分:
typescript
type NetworkState = {
// 当前选中的网络客户端ID
selectedNetworkClientId: NetworkClientId;
// 按链ID索引的网络配置
networkConfigurationsByChainId: Record<Hex, NetworkConfiguration>;
// 网络元数据信息(EIP支持、网络状态等)
networksMetadata: NetworksMetadata;
};
3. 网络类型和配置
3.1 网络类型
NetworkController 支持两种主要的网络类型:
typescript
// RPC端点类型
enum RpcEndpointType {
Custom = 'custom', // 自定义RPC
Infura = 'infura', // Infura RPC
}
// 网络客户端类型
enum NetworkClientType {
Custom = 'custom', // 自定义网络
Infura = 'infura', // Infura网络
}
3.2 网络配置结构
每个网络配置包含以下信息:
typescript
type NetworkConfiguration = {
chainId: Hex; // 链ID
name: string; // 网络名称
nativeCurrency: string; // 原生代币
blockExplorerUrls: string[]; // 区块浏览器URL
rpcEndpoints: RpcEndpoint[]; // RPC端点列表
defaultRpcEndpointIndex: number; // 默认RPC端点索引
};
3.3 RPC端点类型
Infura RPC端点
typescript
type InfuraRpcEndpoint = {
type: RpcEndpointType.Infura;
networkClientId: BuiltInNetworkClientId;
url: `https://${InfuraNetworkType}.infura.io/v3/{infuraProjectId}`;
failoverUrls?: string[]; // 备用URL
name?: string; // 端点名称
};
自定义RPC端点
typescript
type CustomRpcEndpoint = {
type: RpcEndpointType.Custom;
networkClientId: CustomNetworkClientId;
url: string; // 自定义URL
failoverUrls?: string[]; // 备用URL
name?: string; // 端点名称
};
4. 核心功能实现
4.1 网络切换机制
设置网络类型
当用户选择切换到 Infura 网络时:
typescript
async setProviderType(type: InfuraNetworkType) {
// 验证网络类型有效性
if (type === NetworkType.rpc) {
throw new Error('不能使用setProviderType设置自定义网络');
}
// 验证是否为有效的Infura网络类型
if (!isInfuraNetworkType(type)) {
throw new Error(`未知的Infura网络类型: ${type}`);
}
// 调用setActiveNetwork进行实际切换
await this.setActiveNetwork(type);
}
设置活跃网络
网络切换的核心逻辑:
typescript
async setActiveNetwork(networkClientId: string, options = {}) {
// 保存之前选中的网络ID
this.#previouslySelectedNetworkClientId = this.state.selectedNetworkClientId;
// 刷新网络连接
await this.#refreshNetwork(networkClientId, options);
}
4.2 网络客户端管理
获取网络客户端
typescript
getNetworkClientById(networkClientId: NetworkClientId) {
// 确定网络客户端类型
const networkClientType = this.#getNetworkClientType(networkClientId);
// 从对应注册表获取客户端
const registry = this.#networkClientRegistry[networkClientType];
const networkClient = registry[networkClientId];
// 验证客户端是否存在
if (!networkClient) {
throw new Error(`网络客户端 ${networkClientId} 不存在`);
}
return networkClient;
}
获取选中的网络客户端
typescript
getSelectedNetworkClient() {
// 获取当前选中的网络客户端
const networkClient = this.#networkClientRegistry[
this.#getNetworkClientType(this.state.selectedNetworkClientId)
][this.state.selectedNetworkClientId];
if (!networkClient) {
return undefined;
}
// 返回Provider和BlockTracker代理
return {
provider: networkClient.provider,
blockTracker: networkClient.blockTracker,
};
}
4.3 网络配置管理
添加网络
添加新网络的完整流程:
typescript
addNetwork(fields: AddNetworkFields) {
const { chainId, rpcEndpoints, ...otherFields } = fields;
// 验证链ID安全性
if (!isSafeChainId(chainId)) {
throw new Error(`无效的链ID: ${chainId}`);
}
// 检查网络是否已存在
if (this.state.networkConfigurationsByChainId[chainId]) {
throw new Error(`链ID ${chainId} 的网络已存在`);
}
// 处理RPC端点,为自定义端点生成ID
const processedRpcEndpoints = rpcEndpoints.map((endpoint, index) => {
if (endpoint.type === RpcEndpointType.Infura) {
return endpoint; // Infura端点保持原样
}
// 为自定义端点生成唯一ID
const networkClientId = uuidV4();
return { ...endpoint, networkClientId };
});
// 创建网络配置
const networkConfiguration = {
chainId,
rpcEndpoints: processedRpcEndpoints,
...otherFields,
};
// 更新状态
this.update((state) => {
state.networkConfigurationsByChainId[chainId] = networkConfiguration;
});
// 发布网络添加事件
this.messagingSystem.publish('NetworkController:networkAdded', networkConfiguration);
return networkConfiguration;
}
更新网络
更新网络配置的处理逻辑:
typescript
async updateNetwork(chainId: Hex, fields: UpdateNetworkFields) {
// 获取现有配置
const existingConfiguration = this.state.networkConfigurationsByChainId[chainId];
if (!existingConfiguration) {
throw new Error(`链ID ${chainId} 的网络不存在`);
}
// 确定网络客户端操作(添加、删除、替换)
const operations = this.#determineNetworkClientOperations(
existingConfiguration.rpcEndpoints,
fields.rpcEndpoints,
);
// 执行网络客户端操作
await this.#executeNetworkClientOperations(operations);
// 处理RPC端点更新
const updatedRpcEndpoints = fields.rpcEndpoints.map((endpoint, index) => {
if (endpoint.type === RpcEndpointType.Infura) {
return endpoint;
}
// 保持现有ID或生成新ID
const existingEndpoint = existingConfiguration.rpcEndpoints[index];
const networkClientId = existingEndpoint?.networkClientId || uuidV4();
return { ...endpoint, networkClientId };
});
// 更新配置
const updatedConfiguration = {
...existingConfiguration,
...fields,
rpcEndpoints: updatedRpcEndpoints,
};
// 更新状态
this.update((state) => {
state.networkConfigurationsByChainId[chainId] = updatedConfiguration;
});
return updatedConfiguration;
}
删除网络
删除网络的安全检查:
typescript
removeNetwork(chainId: Hex) {
// 获取网络配置
const networkConfiguration = this.state.networkConfigurationsByChainId[chainId];
if (!networkConfiguration) {
throw new Error(`链ID ${chainId} 的网络不存在`);
}
// 检查是否为当前选中的网络
const networkClientId = this.findNetworkClientIdByChainId(chainId);
if (networkClientId === this.state.selectedNetworkClientId) {
throw new Error(`不能删除当前选中的网络 ${chainId}`);
}
// 清理网络客户端资源
networkConfiguration.rpcEndpoints.forEach((rpcEndpoint) => {
if (rpcEndpoint.type === RpcEndpointType.Custom) {
const networkClient = this.#networkClientRegistry[NetworkClientType.Custom][rpcEndpoint.networkClientId];
if (networkClient) {
networkClient.destroy(); // 销毁客户端
delete this.#networkClientRegistry[NetworkClientType.Custom][rpcEndpoint.networkClientId];
}
}
});
// 更新状态
this.update((state) => {
delete state.networkConfigurationsByChainId[chainId];
});
// 发布删除事件
this.messagingSystem.publish('NetworkController:networkRemoved', networkConfiguration);
}
4.4 EIP-1559 兼容性检测
控制器通过检查最新区块的 baseFeePerGas
属性来判断网络是否支持 EIP-1559:
typescript
async getEIP1559Compatibility(networkClientId?: NetworkClientId) {
// 如果指定了网络客户端ID,直接检测
if (networkClientId) {
return this.get1559CompatibilityWithNetworkClientId(networkClientId);
}
// 检查缓存
const { EIPS } = this.state.networksMetadata[this.state.selectedNetworkClientId];
if (EIPS[1559] !== undefined) {
return EIPS[1559]; // 返回缓存结果
}
// 动态检测EIP-1559兼容性
const isEIP1559Compatible = await this.#determineEIP1559Compatibility(
this.state.selectedNetworkClientId,
);
// 更新缓存
this.update((state) => {
if (isEIP1559Compatible !== undefined) {
state.networksMetadata[state.selectedNetworkClientId].EIPS[1559] = isEIP1559Compatible;
}
});
return isEIP1559Compatible;
}
// 检测EIP-1559兼容性的具体实现
async #determineEIP1559Compatibility(networkClientId: NetworkClientId) {
try {
// 获取最新区块
const block = await this.#getLatestBlock(networkClientId);
// 检查是否有baseFeePerGas属性
return block.baseFeePerGas !== undefined;
} catch (error) {
debugLog('检测EIP-1559兼容性时出错', error);
return undefined;
}
}
flowchart TD
Start[开始检测] --> CheckCache{检查缓存}
CheckCache -->|有缓存| ReturnCache[返回缓存结果]
CheckCache -->|无缓存| GetBlock[获取最新区块]
GetBlock --> CheckBaseFee{检查baseFeePerGas}
CheckBaseFee -->|存在| Compatible[支持EIP-1559]
CheckBaseFee -->|不存在| NotCompatible[不支持EIP-1559]
CheckBaseFee -->|错误| Unknown[未知]
Compatible --> UpdateCache[更新缓存]
NotCompatible --> UpdateCache
Unknown --> UpdateCache
UpdateCache --> ReturnResult[返回结果]
ReturnCache --> End[结束]
ReturnResult --> End
5. 网络状态管理
5.1 网络状态枚举
typescript
enum NetworkStatus {
Unknown = 'unknown', // 未知状态
Available = 'available', // 可用
Unavailable = 'unavailable', // 不可用
Blocked = 'blocked', // 被阻止(仅Infura)
}
5.2 网络元数据
typescript
type NetworkMetadata = {
// 支持的EIP列表
EIPS: {
[eipNumber: number]: boolean;
};
// 网络状态
status: NetworkStatus;
};
6. 事件系统
6.1 网络事件类型
typescript
type NetworkControllerEvents =
| NetworkControllerStateChangeEvent // 状态变更
| NetworkControllerNetworkWillChangeEvent // 网络即将切换
| NetworkControllerNetworkDidChangeEvent // 网络已切换
| NetworkControllerInfuraIsBlockedEvent // Infura被阻止
| NetworkControllerInfuraIsUnblockedEvent // Infura解除阻止
| NetworkControllerNetworkAddedEvent // 网络已添加
| NetworkControllerNetworkRemovedEvent // 网络已删除
| NetworkControllerRpcEndpointUnavailableEvent // RPC端点不可用
| NetworkControllerRpcEndpointDegradedEvent // RPC端点降级
| NetworkControllerRpcEndpointRequestRetriedEvent; // RPC请求重试
6.2 事件发布示例
typescript
// 网络切换前发布事件
this.messagingSystem.publish('NetworkController:networkWillChange', this.state);
// 网络切换后发布事件
this.messagingSystem.publish('NetworkController:networkDidChange', this.state);
// 网络添加事件
this.messagingSystem.publish('NetworkController:networkAdded', networkConfiguration);
// RPC端点不可用事件
this.messagingSystem.publish('NetworkController:rpcEndpointUnavailable', {
chainId,
endpointUrl,
failoverEndpointUrl,
error,
});
7. RPC故障转移机制
7.1 故障转移配置
typescript
// 启用RPC故障转移
enableRpcFailover() {
this.#isRpcFailoverEnabled = true;
}
// 禁用RPC故障转移
disableRpcFailover() {
this.#isRpcFailoverEnabled = false;
}
7.2 故障转移事件
typescript
// RPC端点不可用事件
type NetworkControllerRpcEndpointUnavailableEvent = {
type: 'NetworkController:rpcEndpointUnavailable';
payload: [{
chainId: Hex;
endpointUrl: string;
failoverEndpointUrl?: string;
error: unknown;
}];
};
// RPC端点降级事件
type NetworkControllerRpcEndpointDegradedEvent = {
type: 'NetworkController:rpcEndpointDegraded';
payload: [{
chainId: Hex;
endpointUrl: string;
}];
};
7.2 图示
graph TD
subgraph "RPC端点配置"
Primary[主RPC端点
https://mainnet.infura.io/v3/xxx] Failover1[备用端点1
https://backup1.example.com] Failover2[备用端点2
https://backup2.example.com] Failover3[备用端点3
https://backup3.example.com] end subgraph "故障转移状态" Healthy[健康状态
响应时间 < 1000ms] Degraded[降级状态
响应时间 1000-5000ms] Unavailable[不可用状态
响应时间 > 5000ms 或错误] Blocked[被阻止状态
地理限制或IP封禁] end subgraph "故障转移流程" Start[开始RPC请求] --> CheckPrimary{检查主端点} CheckPrimary -->|健康| Success[请求成功] CheckPrimary -->|降级| DegradedEvent[发布降级事件
NetworkController:rpcEndpointDegraded] CheckPrimary -->|不可用| UnavailableEvent[发布不可用事件
NetworkController:rpcEndpointUnavailable] CheckPrimary -->|被阻止| BlockedEvent[发布阻止事件
NetworkController:infuraIsBlocked] DegradedEvent --> ContinuePrimary[继续使用主端点
但标记为降级] UnavailableEvent --> TryFailover1{尝试备用端点1} BlockedEvent --> TryFailover1 TryFailover1 -->|可用| SwitchToFailover1[切换到备用端点1] TryFailover1 -->|不可用| TryFailover2{尝试备用端点2} TryFailover2 -->|可用| SwitchToFailover2[切换到备用端点2] TryFailover2 -->|不可用| TryFailover3{尝试备用端点3} TryFailover3 -->|可用| SwitchToFailover3[切换到备用端点3] TryFailover3 -->|不可用| AllFailed[所有端点不可用] subgraph "重试机制" Retry1[第1次重试
延迟: 1s] Retry2[第2次重试
延迟: 2s] Retry3[第3次重试
延迟: 4s] MaxRetries[最大重试次数: 3] end AllFailed --> Retry1 Retry1 --> RetryEvent1[发布重试事件
NetworkController:rpcEndpointRequestRetried] Retry1 -->|失败| Retry2 Retry2 --> RetryEvent2[发布重试事件
NetworkController:rpcEndpointRequestRetried] Retry2 -->|失败| Retry3 Retry3 --> RetryEvent3[发布重试事件
NetworkController:rpcEndpointRequestRetried] Retry3 -->|失败| MaxRetries MaxRetries --> FinalError[最终错误
所有端点都不可用] end subgraph "状态恢复监控" MonitorPrimary[监控主端点恢复] MonitorFailover1[监控备用端点1恢复] MonitorFailover2[监控备用端点2恢复] MonitorFailover3[监控备用端点3恢复] SwitchToFailover1 --> MonitorPrimary SwitchToFailover2 --> MonitorPrimary SwitchToFailover3 --> MonitorPrimary MonitorPrimary -->|恢复| SwitchBack[切换回主端点] MonitorFailover1 -->|恢复| UpdateStatus1[更新端点1状态] MonitorFailover2 -->|恢复| UpdateStatus2[更新端点2状态] MonitorFailover3 -->|恢复| UpdateStatus3[更新端点3状态] end subgraph "事件发布" Events[事件系统] DegradedEvent --> Events UnavailableEvent --> Events BlockedEvent --> Events RetryEvent1 --> Events RetryEvent2 --> Events RetryEvent3 --> Events SwitchBack --> Events end subgraph "配置选项" EnableFailover[启用故障转移
isRpcFailoverEnabled: true] DisableFailover[禁用故障转移
isRpcFailoverEnabled: false] TimeoutConfig[超时配置
timeout: 30000ms] RetryConfig[重试配置
maxRetries: 3] end EnableFailover --> Start DisableFailover --> Primary TimeoutConfig --> CheckPrimary RetryConfig --> Retry1
https://mainnet.infura.io/v3/xxx] Failover1[备用端点1
https://backup1.example.com] Failover2[备用端点2
https://backup2.example.com] Failover3[备用端点3
https://backup3.example.com] end subgraph "故障转移状态" Healthy[健康状态
响应时间 < 1000ms] Degraded[降级状态
响应时间 1000-5000ms] Unavailable[不可用状态
响应时间 > 5000ms 或错误] Blocked[被阻止状态
地理限制或IP封禁] end subgraph "故障转移流程" Start[开始RPC请求] --> CheckPrimary{检查主端点} CheckPrimary -->|健康| Success[请求成功] CheckPrimary -->|降级| DegradedEvent[发布降级事件
NetworkController:rpcEndpointDegraded] CheckPrimary -->|不可用| UnavailableEvent[发布不可用事件
NetworkController:rpcEndpointUnavailable] CheckPrimary -->|被阻止| BlockedEvent[发布阻止事件
NetworkController:infuraIsBlocked] DegradedEvent --> ContinuePrimary[继续使用主端点
但标记为降级] UnavailableEvent --> TryFailover1{尝试备用端点1} BlockedEvent --> TryFailover1 TryFailover1 -->|可用| SwitchToFailover1[切换到备用端点1] TryFailover1 -->|不可用| TryFailover2{尝试备用端点2} TryFailover2 -->|可用| SwitchToFailover2[切换到备用端点2] TryFailover2 -->|不可用| TryFailover3{尝试备用端点3} TryFailover3 -->|可用| SwitchToFailover3[切换到备用端点3] TryFailover3 -->|不可用| AllFailed[所有端点不可用] subgraph "重试机制" Retry1[第1次重试
延迟: 1s] Retry2[第2次重试
延迟: 2s] Retry3[第3次重试
延迟: 4s] MaxRetries[最大重试次数: 3] end AllFailed --> Retry1 Retry1 --> RetryEvent1[发布重试事件
NetworkController:rpcEndpointRequestRetried] Retry1 -->|失败| Retry2 Retry2 --> RetryEvent2[发布重试事件
NetworkController:rpcEndpointRequestRetried] Retry2 -->|失败| Retry3 Retry3 --> RetryEvent3[发布重试事件
NetworkController:rpcEndpointRequestRetried] Retry3 -->|失败| MaxRetries MaxRetries --> FinalError[最终错误
所有端点都不可用] end subgraph "状态恢复监控" MonitorPrimary[监控主端点恢复] MonitorFailover1[监控备用端点1恢复] MonitorFailover2[监控备用端点2恢复] MonitorFailover3[监控备用端点3恢复] SwitchToFailover1 --> MonitorPrimary SwitchToFailover2 --> MonitorPrimary SwitchToFailover3 --> MonitorPrimary MonitorPrimary -->|恢复| SwitchBack[切换回主端点] MonitorFailover1 -->|恢复| UpdateStatus1[更新端点1状态] MonitorFailover2 -->|恢复| UpdateStatus2[更新端点2状态] MonitorFailover3 -->|恢复| UpdateStatus3[更新端点3状态] end subgraph "事件发布" Events[事件系统] DegradedEvent --> Events UnavailableEvent --> Events BlockedEvent --> Events RetryEvent1 --> Events RetryEvent2 --> Events RetryEvent3 --> Events SwitchBack --> Events end subgraph "配置选项" EnableFailover[启用故障转移
isRpcFailoverEnabled: true] DisableFailover[禁用故障转移
isRpcFailoverEnabled: false] TimeoutConfig[超时配置
timeout: 30000ms] RetryConfig[重试配置
maxRetries: 3] end EnableFailover --> Start DisableFailover --> Primary TimeoutConfig --> CheckPrimary RetryConfig --> Retry1
8. 使用示例
8.1 基本网络切换
typescript
// 切换到以太坊主网
await networkController.setProviderType('mainnet');
// 获取当前链ID
const chainId = networkController.getSelectedChainId();
// 检查EIP-1559兼容性
const isEIP1559Compatible = await networkController.getEIP1559Compatibility();
8.2 添加自定义网络
typescript
// 添加Polygon网络
const polygonNetwork = networkController.addNetwork({
chainId: '0x89',
name: 'Polygon',
nativeCurrency: 'MATIC',
blockExplorerUrls: ['https://polygonscan.com'],
rpcEndpoints: [{
type: RpcEndpointType.Custom,
url: 'https://polygon-rpc.com',
name: 'Polygon RPC',
}],
defaultRpcEndpointIndex: 0,
});
// 切换到新添加的网络
await networkController.setActiveNetwork(polygonNetwork.rpcEndpoints[0].networkClientId);
8.3 网络状态监控
typescript
// 获取网络客户端
const networkClient = networkController.getSelectedNetworkClient();
const provider = networkClient.provider;
const blockTracker = networkClient.blockTracker;
// 监听网络状态变化
networkController.messagingSystem.subscribe('NetworkController:networkDidChange', (state) => {
console.log('网络已切换到:', state.selectedNetworkClientId);
});
9. 总结
NetworkController 是 MetaMask 多链支持的核心组件,它提供了:
- 多网络管理: 支持 Infura 和自定义 RPC 端点
- 网络切换: 无缝切换不同网络
- 状态监控: 实时监控网络状态和可用性
- 故障转移: 自动故障转移到备用 RPC 端点
- EIP 兼容性: 动态检测网络支持的 EIP
- 事件系统: 完整的事件通知机制
这个控制器为 MetaMask 提供了强大的多链网络管理能力,是构建现代多链钱包应用的基础。通过其灵活的配置和强大的功能,用户可以轻松管理多个区块链网络,享受无缝的多链体验。
学习交流请添加vx: gh313061
下期预告:构建密钥管理控制器(KeyringController)