Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
c3ab29b
feat(network-controller): add getIsRpcFailoverForced selector
cryptodev-2s Jun 17, 2026
003b255
test(network-controller): clarify non-boolean selector test name
cryptodev-2s Jun 17, 2026
e51b625
feat(network-controller): force Infura traffic to failover when forced
cryptodev-2s Jun 17, 2026
4792d92
test(network-controller): cover disableRpcFailoverForced and document…
cryptodev-2s Jun 17, 2026
6a4e3c1
feat(network-controller): react to wallet-framework-rpc-failover-forc…
cryptodev-2s Jun 17, 2026
9beb97d
fix(network-controller): export forced failover action types
cryptodev-2s Jun 17, 2026
938e1bd
docs(network-controller): changelog for forced RPC failover
cryptodev-2s Jun 17, 2026
ba7ad24
docs(network-controller): point changelog at the real PR number
cryptodev-2s Jun 17, 2026
51ec735
docs(network-controller): note positional primary under forced failover
cryptodev-2s Jun 17, 2026
4bc51fe
fix: run lint:misc
cryptodev-2s Jun 17, 2026
18ef4dc
fix: messenger types
cryptodev-2s Jun 17, 2026
fe94fab
fix: remove no-restricted-syntax
cryptodev-2s Jun 17, 2026
0bd5566
refactor(network-controller): rename force-failover flag to core-plat…
cryptodev-2s Jun 17, 2026
8760483
Merge origin/main into feat/force-rpc-failover
cryptodev-2s Jun 18, 2026
eca6f20
Merge branch 'main' into feat/force-rpc-failover
cryptodev-2s Jun 19, 2026
89ac401
Merge remote-tracking branch 'origin/main' into feat/force-rpc-failover
cryptodev-2s Jun 22, 2026
bc8dce7
fix: changelog
cryptodev-2s Jun 22, 2026
2a256af
fix: lint:misc
cryptodev-2s Jun 22, 2026
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
6 changes: 6 additions & 0 deletions packages/network-controller/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- Add forced RPC failover for Infura endpoints, driven by the `core-platform-rpc-failover-force-enabled` remote feature flag ([#9175](https://github.com/MetaMask/core/pull/9175))
- When enabled, Infura endpoints configured with failover URLs route all traffic to those failover URLs, bypassing Infura entirely. Infura endpoints without failover URLs continue to use Infura, and custom RPC endpoints are unaffected.
- Adds the `NetworkController.enableRpcFailoverForced` and `NetworkController.disableRpcFailoverForced` methods, along with the `NetworkControllerEnableRpcFailoverForcedAction` and `NetworkControllerDisableRpcFailoverForcedAction` messenger actions.

## [33.0.0]

### Added
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,26 @@ export type NetworkControllerDisableRpcFailoverAction = {
handler: NetworkController['disableRpcFailover'];
};

/**
* Forces RPC failover for Infura endpoints. When enabled, any Infura endpoint
* configured with failover URLs will route all traffic to those failover URLs,
* bypassing Infura entirely. Infura endpoints without failover URLs continue to
* use Infura. Custom endpoints are unaffected.
*/
export type NetworkControllerEnableRpcFailoverForcedAction = {
type: `NetworkController:enableRpcFailoverForced`;
handler: NetworkController['enableRpcFailoverForced'];
};

/**
* Stops forcing RPC failover for Infura endpoints, restoring the normal
* automatic-failover behavior governed by {@link enableRpcFailover}.
*/
export type NetworkControllerDisableRpcFailoverForcedAction = {
type: `NetworkController:disableRpcFailoverForced`;
handler: NetworkController['disableRpcFailoverForced'];
};

/**
* Accesses the provider and block tracker for the currently selected network.
*
Expand Down Expand Up @@ -311,6 +331,8 @@ export type NetworkControllerMethodActions =
| NetworkControllerGetEthQueryAction
| NetworkControllerEnableRpcFailoverAction
| NetworkControllerDisableRpcFailoverAction
| NetworkControllerEnableRpcFailoverForcedAction
| NetworkControllerDisableRpcFailoverForcedAction
| NetworkControllerGetProviderAndBlockTrackerAction
| NetworkControllerGetSelectedNetworkClientAction
| NetworkControllerGetSelectedChainIdAction
Expand Down
81 changes: 78 additions & 3 deletions packages/network-controller/src/NetworkController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ import type { DegradedEventType, RetryReason } from './create-network-client';
import { projectLogger, createModuleLogger } from './logger';
import type { NetworkControllerMethodActions } from './NetworkController-method-action-types';
import type { RpcServiceOptionsWithDefaults } from './rpc-service/rpc-service';
import { getIsRpcFailoverEnabled } from './selectors';
import { getIsRpcFailoverEnabled, getIsRpcFailoverForced } from './selectors';
import { NetworkClientType } from './types';
import type {
BlockTracker,
Expand Down Expand Up @@ -669,7 +669,9 @@ type AllowedEvents = RemoteFeatureFlagControllerStateChangeEvent;
const MESSENGER_EXPOSED_METHODS = [
'addNetwork',
'disableRpcFailover',
'disableRpcFailoverForced',
'enableRpcFailover',
'enableRpcFailoverForced',
'findNetworkClientIdByChainId',
'get1559CompatibilityWithNetworkClientId',
'getEIP1559Compatibility',
Expand Down Expand Up @@ -1275,6 +1277,8 @@ export class NetworkController extends BaseController<

#isRpcFailoverEnabled = false;

#isRpcFailoverForced = false;

/**
* Constructs a NetworkController.
*
Expand Down Expand Up @@ -1378,6 +1382,15 @@ export class NetworkController extends BaseController<
},
getIsRpcFailoverEnabled,
);

this.messenger.subscribe(
// eslint-disable-next-line no-restricted-syntax
'RemoteFeatureFlagController:stateChange',
(isRpcFailoverForced) => {
this.#updateRpcFailoverForced(isRpcFailoverForced);
},
getIsRpcFailoverForced,
);
}

/**
Expand Down Expand Up @@ -1408,6 +1421,24 @@ export class NetworkController extends BaseController<
this.#updateRpcFailoverEnabled(false);
}

/**
* Forces RPC failover for Infura endpoints. When enabled, any Infura endpoint
* configured with failover URLs will route all traffic to those failover URLs,
* bypassing Infura entirely. Infura endpoints without failover URLs continue to
* use Infura. Custom endpoints are unaffected.
*/
enableRpcFailoverForced(): void {
this.#updateRpcFailoverForced(true);
}

/**
* Stops forcing RPC failover for Infura endpoints, restoring the normal
* automatic-failover behavior governed by {@link enableRpcFailover}.
*/
disableRpcFailoverForced(): void {
this.#updateRpcFailoverForced(false);
}

/**
* Enables or disables the RPC failover functionality, depending on the
* boolean given. This is done by reconstructing all network clients that were
Expand Down Expand Up @@ -1449,6 +1480,44 @@ export class NetworkController extends BaseController<
this.#isRpcFailoverEnabled = newIsRpcFailoverEnabled;
}

/**
* Enables or disables forced RPC failover, depending on the boolean given.
* This reconstructs all network clients that were configured with failover
* URLs so the new value takes effect. Network client IDs are preserved.
*
* @param newIsRpcFailoverForced - Whether or not to force RPC failover.
*/
#updateRpcFailoverForced(newIsRpcFailoverForced: boolean): void {
if (this.#isRpcFailoverForced === newIsRpcFailoverForced) {
return;
}

const autoManagedNetworkClientRegistry =
this.#ensureAutoManagedNetworkClientRegistryPopulated();

for (const networkClientsById of Object.values(
autoManagedNetworkClientRegistry,
)) {
for (const networkClientId of Object.keys(networkClientsById)) {
// Type assertion: We can assume that `networkClientId` is valid here.
const networkClient =
networkClientsById[
networkClientId as keyof typeof networkClientsById
];
if (
networkClient.configuration.failoverRpcUrls &&
networkClient.configuration.failoverRpcUrls.length > 0
) {
newIsRpcFailoverForced
? networkClient.enableRpcFailoverForced()
: networkClient.disableRpcFailoverForced();
}
}
}

this.#isRpcFailoverForced = newIsRpcFailoverForced;
}

/**
* Accesses the provider and block tracker for the currently selected network.
*
Expand Down Expand Up @@ -1610,12 +1679,14 @@ export class NetworkController extends BaseController<
}

/**
* Initialize the NetworkController, updating the RPC failover feature flag
* and applying the network selection.
* Initialize the NetworkController, updating the RPC failover feature flags
* (`isRpcFailoverEnabled` and `isRpcFailoverForced`) and applying the network
* selection.
*/
init(): void {
const state = this.messenger.call('RemoteFeatureFlagController:getState');
this.#updateRpcFailoverEnabled(getIsRpcFailoverEnabled(state));
this.#updateRpcFailoverForced(getIsRpcFailoverForced(state));

this.#applyNetworkSelection(this.state.selectedNetworkClientId);
}
Expand Down Expand Up @@ -2860,6 +2931,7 @@ export class NetworkController extends BaseController<
getBlockTrackerOptions: this.#getBlockTrackerOptions,
messenger: this.messenger,
isRpcFailoverEnabled: this.#isRpcFailoverEnabled,
isRpcFailoverForced: this.#isRpcFailoverForced,
logger: this.#log,
});
} else {
Expand All @@ -2879,6 +2951,7 @@ export class NetworkController extends BaseController<
getBlockTrackerOptions: this.#getBlockTrackerOptions,
messenger: this.messenger,
isRpcFailoverEnabled: this.#isRpcFailoverEnabled,
isRpcFailoverForced: this.#isRpcFailoverForced,
logger: this.#log,
});
}
Expand Down Expand Up @@ -3045,6 +3118,7 @@ export class NetworkController extends BaseController<
getBlockTrackerOptions: this.#getBlockTrackerOptions,
messenger: this.messenger,
isRpcFailoverEnabled: this.#isRpcFailoverEnabled,
isRpcFailoverForced: this.#isRpcFailoverForced,
logger: this.#log,
}),
] as const;
Expand All @@ -3064,6 +3138,7 @@ export class NetworkController extends BaseController<
getBlockTrackerOptions: this.#getBlockTrackerOptions,
messenger: this.messenger,
isRpcFailoverEnabled: this.#isRpcFailoverEnabled,
isRpcFailoverForced: this.#isRpcFailoverForced,
logger: this.#log,
}),
] as const;
Expand Down
Loading
Loading