diff --git a/packages/network-controller/CHANGELOG.md b/packages/network-controller/CHANGELOG.md index 25869ad7ba..988daeb093 100644 --- a/packages/network-controller/CHANGELOG.md +++ b/packages/network-controller/CHANGELOG.md @@ -7,6 +7,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- **BREAKING:** Drive RPC failover from the single `corePlatformRpcFailoverMode` remote feature flag ([#9175](https://github.com/MetaMask/core/pull/9175)) + - The flag is a string with three values: `disabled` (failover off), `enabled` (divert to failover URLs when the primary endpoint is unavailable), and `forced` (Infura endpoints that have failover URLs route all traffic to those URLs, bypassing Infura entirely). Custom endpoints are unaffected, and the value defaults to `disabled` when the flag is absent or unrecognized. + - `NetworkController` no longer reads the `walletFrameworkRpcFailoverEnabled` flag; the `enabled` mode replaces it. Update your remote feature flag configuration to set `corePlatformRpcFailoverMode`. + +### Removed + +- **BREAKING:** Remove the `NetworkController.enableRpcFailover` and `NetworkController.disableRpcFailover` methods, their `NetworkController:enableRpcFailover` / `NetworkController:disableRpcFailover` messenger actions, and the `NetworkControllerEnableRpcFailoverAction` / `NetworkControllerDisableRpcFailoverAction` types ([#9175](https://github.com/MetaMask/core/pull/9175)) + - RPC failover is now driven entirely by the `corePlatformRpcFailoverMode` remote feature flag, so there is no longer an imperative toggle. + ## [33.0.0] ### Added diff --git a/packages/network-controller/src/NetworkController-method-action-types.ts b/packages/network-controller/src/NetworkController-method-action-types.ts index c07de322ba..019b6b225c 100644 --- a/packages/network-controller/src/NetworkController-method-action-types.ts +++ b/packages/network-controller/src/NetworkController-method-action-types.ts @@ -16,26 +16,6 @@ export type NetworkControllerGetEthQueryAction = { handler: NetworkController['getEthQuery']; }; -/** - * Enables the RPC failover functionality. That is, if any RPC endpoints are - * configured with failover URLs, then traffic will automatically be diverted - * to them if those RPC endpoints are unavailable. - */ -export type NetworkControllerEnableRpcFailoverAction = { - type: `NetworkController:enableRpcFailover`; - handler: NetworkController['enableRpcFailover']; -}; - -/** - * Disables the RPC failover functionality. That is, even if any RPC endpoints - * are configured with failover URLs, then traffic will not automatically be - * diverted to them if those RPC endpoints are unavailable. - */ -export type NetworkControllerDisableRpcFailoverAction = { - type: `NetworkController:disableRpcFailover`; - handler: NetworkController['disableRpcFailover']; -}; - /** * Accesses the provider and block tracker for the currently selected network. * @@ -309,8 +289,6 @@ export type NetworkControllerFindNetworkClientIdByChainIdAction = { */ export type NetworkControllerMethodActions = | NetworkControllerGetEthQueryAction - | NetworkControllerEnableRpcFailoverAction - | NetworkControllerDisableRpcFailoverAction | NetworkControllerGetProviderAndBlockTrackerAction | NetworkControllerGetSelectedNetworkClientAction | NetworkControllerGetSelectedChainIdAction diff --git a/packages/network-controller/src/NetworkController.ts b/packages/network-controller/src/NetworkController.ts index 09c727ada4..5ad7b5443c 100644 --- a/packages/network-controller/src/NetworkController.ts +++ b/packages/network-controller/src/NetworkController.ts @@ -56,7 +56,8 @@ 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 { getRpcFailoverMode } from './selectors'; +import type { RpcFailoverMode } from './selectors'; import { NetworkClientType } from './types'; import type { BlockTracker, @@ -668,8 +669,6 @@ type AllowedEvents = RemoteFeatureFlagControllerStateChangeEvent; const MESSENGER_EXPOSED_METHODS = [ 'addNetwork', - 'disableRpcFailover', - 'enableRpcFailover', 'findNetworkClientIdByChainId', 'get1559CompatibilityWithNetworkClientId', 'getEIP1559Compatibility', @@ -1273,7 +1272,7 @@ export class NetworkController extends BaseController< NetworkConfiguration >; - #isRpcFailoverEnabled = false; + #rpcFailoverMode: RpcFailoverMode = 'disabled'; /** * Constructs a NetworkController. @@ -1373,10 +1372,10 @@ export class NetworkController extends BaseController< this.messenger.subscribe( // eslint-disable-next-line no-restricted-syntax 'RemoteFeatureFlagController:stateChange', - (isRpcFailoverEnabled) => { - this.#updateRpcFailoverEnabled(isRpcFailoverEnabled); + (rpcFailoverMode) => { + this.#updateRpcFailover(rpcFailoverMode); }, - getIsRpcFailoverEnabled, + getRpcFailoverMode, ); } @@ -1391,35 +1390,15 @@ export class NetworkController extends BaseController< } /** - * Enables the RPC failover functionality. That is, if any RPC endpoints are - * configured with failover URLs, then traffic will automatically be diverted - * to them if those RPC endpoints are unavailable. - */ - enableRpcFailover(): void { - this.#updateRpcFailoverEnabled(true); - } - - /** - * Disables the RPC failover functionality. That is, even if any RPC endpoints - * are configured with failover URLs, then traffic will not automatically be - * diverted to them if those RPC endpoints are unavailable. - */ - disableRpcFailover(): void { - this.#updateRpcFailoverEnabled(false); - } - - /** - * Enables or disables the RPC failover functionality, depending on the - * boolean given. This is done by reconstructing all network clients that were - * originally configured with failover URLs so that those URLs are either - * honored or ignored. Network client IDs will be preserved so as not to - * invalidate state in other controllers. + * Applies the given RPC failover mode by reconstructing all network clients + * that were configured with failover URLs so that the new mode takes effect. + * Network client IDs are preserved so as not to invalidate state in other + * controllers. * - * @param newIsRpcFailoverEnabled - Whether or not to enable or disable the - * RPC failover functionality. + * @param newMode - The RPC failover mode to apply. */ - #updateRpcFailoverEnabled(newIsRpcFailoverEnabled: boolean): void { - if (this.#isRpcFailoverEnabled === newIsRpcFailoverEnabled) { + #updateRpcFailover(newMode: RpcFailoverMode): void { + if (this.#rpcFailoverMode === newMode) { return; } @@ -1439,14 +1418,12 @@ export class NetworkController extends BaseController< networkClient.configuration.failoverRpcUrls && networkClient.configuration.failoverRpcUrls.length > 0 ) { - newIsRpcFailoverEnabled - ? networkClient.enableRpcFailover() - : networkClient.disableRpcFailover(); + networkClient.setRpcFailoverMode(newMode); } } } - this.#isRpcFailoverEnabled = newIsRpcFailoverEnabled; + this.#rpcFailoverMode = newMode; } /** @@ -1610,12 +1587,13 @@ export class NetworkController extends BaseController< } /** - * Initialize the NetworkController, updating the RPC failover feature flag - * and applying the network selection. + * Initialize the NetworkController, applying the RPC failover mode from the + * `corePlatformRpcFailoverMode` remote feature flag and applying the network + * selection. */ init(): void { const state = this.messenger.call('RemoteFeatureFlagController:getState'); - this.#updateRpcFailoverEnabled(getIsRpcFailoverEnabled(state)); + this.#updateRpcFailover(getRpcFailoverMode(state)); this.#applyNetworkSelection(this.state.selectedNetworkClientId); } @@ -2859,7 +2837,7 @@ export class NetworkController extends BaseController< getRpcServiceOptions: this.#getRpcServiceOptions, getBlockTrackerOptions: this.#getBlockTrackerOptions, messenger: this.messenger, - isRpcFailoverEnabled: this.#isRpcFailoverEnabled, + rpcFailoverMode: this.#rpcFailoverMode, logger: this.#log, }); } else { @@ -2878,7 +2856,7 @@ export class NetworkController extends BaseController< getRpcServiceOptions: this.#getRpcServiceOptions, getBlockTrackerOptions: this.#getBlockTrackerOptions, messenger: this.messenger, - isRpcFailoverEnabled: this.#isRpcFailoverEnabled, + rpcFailoverMode: this.#rpcFailoverMode, logger: this.#log, }); } @@ -3044,7 +3022,7 @@ export class NetworkController extends BaseController< getRpcServiceOptions: this.#getRpcServiceOptions, getBlockTrackerOptions: this.#getBlockTrackerOptions, messenger: this.messenger, - isRpcFailoverEnabled: this.#isRpcFailoverEnabled, + rpcFailoverMode: this.#rpcFailoverMode, logger: this.#log, }), ] as const; @@ -3063,7 +3041,7 @@ export class NetworkController extends BaseController< getRpcServiceOptions: this.#getRpcServiceOptions, getBlockTrackerOptions: this.#getBlockTrackerOptions, messenger: this.messenger, - isRpcFailoverEnabled: this.#isRpcFailoverEnabled, + rpcFailoverMode: this.#rpcFailoverMode, logger: this.#log, }), ] as const; diff --git a/packages/network-controller/src/create-auto-managed-network-client.test.ts b/packages/network-controller/src/create-auto-managed-network-client.test.ts index eea5e94d8f..d7e3c16def 100644 --- a/packages/network-controller/src/create-auto-managed-network-client.test.ts +++ b/packages/network-controller/src/create-auto-managed-network-client.test.ts @@ -45,7 +45,7 @@ describe('createAutoManagedNetworkClient', () => { isOffline: (): boolean => false, }), messenger: buildNetworkControllerMessenger(), - isRpcFailoverEnabled: false, + rpcFailoverMode: 'disabled', }); expect(configuration).toStrictEqual(networkClientConfiguration); @@ -63,7 +63,7 @@ describe('createAutoManagedNetworkClient', () => { isOffline: (): boolean => false, }), messenger: buildNetworkControllerMessenger(), - isRpcFailoverEnabled: false, + rpcFailoverMode: 'disabled', }); }).not.toThrow(); }); @@ -78,7 +78,7 @@ describe('createAutoManagedNetworkClient', () => { isOffline: (): boolean => false, }), messenger: buildNetworkControllerMessenger(), - isRpcFailoverEnabled: false, + rpcFailoverMode: 'disabled', }); // This also tests the `has` trap in the proxy @@ -113,7 +113,7 @@ describe('createAutoManagedNetworkClient', () => { isOffline: (): boolean => false, }), messenger: buildNetworkControllerMessenger(), - isRpcFailoverEnabled: false, + rpcFailoverMode: 'disabled', }); const result = await provider.request({ @@ -164,7 +164,7 @@ describe('createAutoManagedNetworkClient', () => { getRpcServiceOptions, getBlockTrackerOptions, messenger, - isRpcFailoverEnabled: true, + rpcFailoverMode: 'enabled', }); await provider.request({ @@ -186,11 +186,11 @@ describe('createAutoManagedNetworkClient', () => { getRpcServiceOptions, getBlockTrackerOptions, messenger, - isRpcFailoverEnabled: true, + rpcFailoverMode: 'enabled', }); }); - it('allows for enabling the RPC failover behavior, even after having already accessed the provider', async () => { + it('allows setting the RPC failover mode to enabled, even after having already accessed the provider', async () => { mockNetwork({ networkClientConfiguration, mocks: [ @@ -229,7 +229,7 @@ describe('createAutoManagedNetworkClient', () => { getRpcServiceOptions, getBlockTrackerOptions, messenger, - isRpcFailoverEnabled: false, + rpcFailoverMode: 'disabled', }); const { provider } = autoManagedNetworkClient; @@ -239,7 +239,7 @@ describe('createAutoManagedNetworkClient', () => { method: 'test_method', params: [], }); - autoManagedNetworkClient.enableRpcFailover(); + autoManagedNetworkClient.setRpcFailoverMode('enabled'); await provider.request({ id: 1, jsonrpc: '2.0', @@ -253,7 +253,7 @@ describe('createAutoManagedNetworkClient', () => { getRpcServiceOptions, getBlockTrackerOptions, messenger, - isRpcFailoverEnabled: false, + rpcFailoverMode: 'disabled', }); expect(createNetworkClientMock).toHaveBeenNthCalledWith(2, { id: 'some-network-client-id', @@ -261,11 +261,11 @@ describe('createAutoManagedNetworkClient', () => { getRpcServiceOptions, getBlockTrackerOptions, messenger, - isRpcFailoverEnabled: true, + rpcFailoverMode: 'enabled', }); }); - it('allows for disabling the RPC failover behavior, even after having accessed the provider', async () => { + it('allows setting the RPC failover mode to disabled, even after having accessed the provider', async () => { mockNetwork({ networkClientConfiguration, mocks: [ @@ -304,7 +304,7 @@ describe('createAutoManagedNetworkClient', () => { getRpcServiceOptions, getBlockTrackerOptions, messenger, - isRpcFailoverEnabled: true, + rpcFailoverMode: 'enabled', }); const { provider } = autoManagedNetworkClient; @@ -314,7 +314,7 @@ describe('createAutoManagedNetworkClient', () => { method: 'test_method', params: [], }); - autoManagedNetworkClient.disableRpcFailover(); + autoManagedNetworkClient.setRpcFailoverMode('disabled'); await provider.request({ id: 1, jsonrpc: '2.0', @@ -328,7 +328,7 @@ describe('createAutoManagedNetworkClient', () => { getRpcServiceOptions, getBlockTrackerOptions, messenger, - isRpcFailoverEnabled: true, + rpcFailoverMode: 'enabled', }); expect(createNetworkClientMock).toHaveBeenNthCalledWith(2, { id: 'some-network-client-id', @@ -336,7 +336,7 @@ describe('createAutoManagedNetworkClient', () => { getRpcServiceOptions, getBlockTrackerOptions, messenger, - isRpcFailoverEnabled: false, + rpcFailoverMode: 'disabled', }); }); }); @@ -351,7 +351,7 @@ describe('createAutoManagedNetworkClient', () => { isOffline: (): boolean => false, }), messenger: buildNetworkControllerMessenger(), - isRpcFailoverEnabled: false, + rpcFailoverMode: 'disabled', }); // This also tests the `has` trap in the proxy @@ -412,7 +412,7 @@ describe('createAutoManagedNetworkClient', () => { isOffline: (): boolean => false, }), messenger: buildNetworkControllerMessenger(), - isRpcFailoverEnabled: false, + rpcFailoverMode: 'disabled', }); const blockNumberViaLatest = await new Promise((resolve) => { @@ -486,7 +486,7 @@ describe('createAutoManagedNetworkClient', () => { getRpcServiceOptions, getBlockTrackerOptions, messenger, - isRpcFailoverEnabled: true, + rpcFailoverMode: 'enabled', }); await new Promise((resolve) => { @@ -504,11 +504,11 @@ describe('createAutoManagedNetworkClient', () => { getRpcServiceOptions, getBlockTrackerOptions, messenger, - isRpcFailoverEnabled: true, + rpcFailoverMode: 'enabled', }); }); - it('allows for enabling the RPC failover behavior, even after having already accessed the provider', async () => { + it('allows setting the RPC failover mode to enabled, even after having already accessed the block tracker', async () => { mockNetwork({ networkClientConfiguration, mocks: [ @@ -547,14 +547,14 @@ describe('createAutoManagedNetworkClient', () => { getRpcServiceOptions, getBlockTrackerOptions, messenger, - isRpcFailoverEnabled: false, + rpcFailoverMode: 'disabled', }); const { blockTracker } = autoManagedNetworkClient; await new Promise((resolve) => { blockTracker.once('latest', resolve); }); - autoManagedNetworkClient.enableRpcFailover(); + autoManagedNetworkClient.setRpcFailoverMode('enabled'); await new Promise((resolve) => { blockTracker.once('latest', resolve); }); @@ -565,7 +565,7 @@ describe('createAutoManagedNetworkClient', () => { getRpcServiceOptions, getBlockTrackerOptions, messenger, - isRpcFailoverEnabled: false, + rpcFailoverMode: 'disabled', }); expect(createNetworkClientMock).toHaveBeenNthCalledWith(2, { id: 'some-network-client-id', @@ -573,11 +573,11 @@ describe('createAutoManagedNetworkClient', () => { getRpcServiceOptions, getBlockTrackerOptions, messenger, - isRpcFailoverEnabled: true, + rpcFailoverMode: 'enabled', }); }); - it('allows for disabling the RPC failover behavior, even after having already accessed the provider', async () => { + it('allows setting the RPC failover mode to disabled, even after having already accessed the block tracker', async () => { mockNetwork({ networkClientConfiguration, mocks: [ @@ -616,14 +616,14 @@ describe('createAutoManagedNetworkClient', () => { getRpcServiceOptions, getBlockTrackerOptions, messenger, - isRpcFailoverEnabled: true, + rpcFailoverMode: 'enabled', }); const { blockTracker } = autoManagedNetworkClient; await new Promise((resolve) => { blockTracker.once('latest', resolve); }); - autoManagedNetworkClient.disableRpcFailover(); + autoManagedNetworkClient.setRpcFailoverMode('disabled'); await new Promise((resolve) => { blockTracker.once('latest', resolve); }); @@ -634,7 +634,7 @@ describe('createAutoManagedNetworkClient', () => { getRpcServiceOptions, getBlockTrackerOptions, messenger, - isRpcFailoverEnabled: true, + rpcFailoverMode: 'enabled', }); expect(createNetworkClientMock).toHaveBeenNthCalledWith(2, { id: 'some-network-client-id', @@ -642,12 +642,162 @@ describe('createAutoManagedNetworkClient', () => { getRpcServiceOptions, getBlockTrackerOptions, messenger, - isRpcFailoverEnabled: false, + rpcFailoverMode: 'disabled', }); }); }); }); + it('allows setting the RPC failover mode to forced, even after having already accessed the provider', async () => { + mockNetwork({ + networkClientConfiguration, + mocks: [ + { + request: { + method: 'test_method', + params: [], + }, + response: { + result: 'test response', + }, + discardAfterMatching: false, + }, + ], + }); + const createNetworkClientMock = jest.spyOn( + createNetworkClientModule, + 'createNetworkClient', + ); + const getRpcServiceOptions = (): Omit< + RpcServiceOptions, + 'failoverService' | 'endpointUrl' + > => ({ + btoa, + fetch, + isOffline: (): boolean => false, + }); + const getBlockTrackerOptions = (): PollingBlockTrackerOptions => ({ + pollingInterval: 5000, + }); + const messenger = buildNetworkControllerMessenger(); + + const autoManagedNetworkClient = createAutoManagedNetworkClient({ + networkClientId: 'some-network-client-id', + networkClientConfiguration, + getRpcServiceOptions, + getBlockTrackerOptions, + messenger, + rpcFailoverMode: 'disabled', + }); + const { provider } = autoManagedNetworkClient; + + await provider.request({ + id: 1, + jsonrpc: '2.0', + method: 'test_method', + params: [], + }); + autoManagedNetworkClient.setRpcFailoverMode('forced'); + await provider.request({ + id: 1, + jsonrpc: '2.0', + method: 'test_method', + params: [], + }); + + expect(createNetworkClientMock).toHaveBeenNthCalledWith(1, { + id: 'some-network-client-id', + configuration: networkClientConfiguration, + getRpcServiceOptions, + getBlockTrackerOptions, + messenger, + rpcFailoverMode: 'disabled', + }); + expect(createNetworkClientMock).toHaveBeenNthCalledWith(2, { + id: 'some-network-client-id', + configuration: networkClientConfiguration, + getRpcServiceOptions, + getBlockTrackerOptions, + messenger, + rpcFailoverMode: 'forced', + }); + }); + + it('allows setting the RPC failover mode from forced back to disabled, even after having accessed the provider', async () => { + mockNetwork({ + networkClientConfiguration, + mocks: [ + { + request: { + method: 'test_method', + params: [], + }, + response: { + result: 'test response', + }, + discardAfterMatching: false, + }, + ], + }); + const createNetworkClientMock = jest.spyOn( + createNetworkClientModule, + 'createNetworkClient', + ); + const getRpcServiceOptions = (): Omit< + RpcServiceOptions, + 'failoverService' | 'endpointUrl' + > => ({ + btoa, + fetch, + isOffline: (): boolean => false, + }); + const getBlockTrackerOptions = (): PollingBlockTrackerOptions => ({ + pollingInterval: 5000, + }); + const messenger = buildNetworkControllerMessenger(); + + const autoManagedNetworkClient = createAutoManagedNetworkClient({ + networkClientId: 'some-network-client-id', + networkClientConfiguration, + getRpcServiceOptions, + getBlockTrackerOptions, + messenger, + rpcFailoverMode: 'forced', + }); + const { provider } = autoManagedNetworkClient; + + await provider.request({ + id: 1, + jsonrpc: '2.0', + method: 'test_method', + params: [], + }); + autoManagedNetworkClient.setRpcFailoverMode('disabled'); + await provider.request({ + id: 1, + jsonrpc: '2.0', + method: 'test_method', + params: [], + }); + + expect(createNetworkClientMock).toHaveBeenNthCalledWith(1, { + id: 'some-network-client-id', + configuration: networkClientConfiguration, + getRpcServiceOptions, + getBlockTrackerOptions, + messenger, + rpcFailoverMode: 'forced', + }); + expect(createNetworkClientMock).toHaveBeenNthCalledWith(2, { + id: 'some-network-client-id', + configuration: networkClientConfiguration, + getRpcServiceOptions, + getBlockTrackerOptions, + messenger, + rpcFailoverMode: 'disabled', + }); + }); + it('destroys the block tracker when destroyed', () => { mockNetwork({ networkClientConfiguration, @@ -672,7 +822,7 @@ describe('createAutoManagedNetworkClient', () => { isOffline: (): boolean => false, }), messenger: buildNetworkControllerMessenger(), - isRpcFailoverEnabled: false, + rpcFailoverMode: 'disabled', }); // Start the block tracker blockTracker.on('latest', () => { diff --git a/packages/network-controller/src/create-auto-managed-network-client.ts b/packages/network-controller/src/create-auto-managed-network-client.ts index 3259700d5f..fe826186cf 100644 --- a/packages/network-controller/src/create-auto-managed-network-client.ts +++ b/packages/network-controller/src/create-auto-managed-network-client.ts @@ -9,6 +9,7 @@ import type { NetworkControllerMessenger, } from './NetworkController'; import type { RpcServiceOptionsWithDefaults } from './rpc-service/rpc-service'; +import type { RpcFailoverMode } from './selectors'; import type { BlockTracker, NetworkClientConfiguration, @@ -47,8 +48,7 @@ export type AutoManagedNetworkClient< provider: ProxyWithAccessibleTarget; blockTracker: ProxyWithAccessibleTarget; destroy: () => void; - enableRpcFailover: () => void; - disableRpcFailover: () => void; + setRpcFailoverMode: (rpcFailoverMode: RpcFailoverMode) => void; }; /** @@ -78,9 +78,10 @@ const UNINITIALIZED_TARGET = { __UNINITIALIZED__: true }; * @param args.getBlockTrackerOptions - Factory for constructing block tracker * options. See {@link NetworkControllerOptions.getBlockTrackerOptions}. * @param args.messenger - The network controller messenger. - * @param args.isRpcFailoverEnabled - Whether or not requests sent to the - * primary RPC endpoint for this network should be automatically diverted to - * provided failover endpoints if the primary is unavailable. + * @param args.rpcFailoverMode - The RPC failover mode to apply: `disabled`, + * `enabled` (divert to the failover URLs when the primary is unavailable), or + * `forced` (route all traffic for Infura endpoints with failover URLs to those + * URLs, bypassing Infura). * @param args.logger - A `loglevel` logger. * @returns The auto-managed network client. */ @@ -95,7 +96,7 @@ export function createAutoManagedNetworkClient< 'provider' > => ({}), messenger, - isRpcFailoverEnabled: givenIsRpcFailoverEnabled, + rpcFailoverMode: givenRpcFailoverMode, logger, }: { networkClientId: NetworkClientId; @@ -107,10 +108,10 @@ export function createAutoManagedNetworkClient< rpcEndpointUrl: string, ) => Omit; messenger: NetworkControllerMessenger; - isRpcFailoverEnabled: boolean; + rpcFailoverMode: RpcFailoverMode; logger?: Logger; }): AutoManagedNetworkClient { - let isRpcFailoverEnabled = givenIsRpcFailoverEnabled; + let rpcFailoverMode = givenRpcFailoverMode; let networkClient: NetworkClient | undefined; const ensureNetworkClientCreated = (): NetworkClient => { @@ -120,7 +121,7 @@ export function createAutoManagedNetworkClient< getRpcServiceOptions, getBlockTrackerOptions, messenger, - isRpcFailoverEnabled, + rpcFailoverMode, logger, }); @@ -234,14 +235,8 @@ export function createAutoManagedNetworkClient< networkClient?.destroy(); }; - const enableRpcFailover = (): void => { - isRpcFailoverEnabled = true; - destroy(); - networkClient = undefined; - }; - - const disableRpcFailover = (): void => { - isRpcFailoverEnabled = false; + const setRpcFailoverMode = (newRpcFailoverMode: RpcFailoverMode): void => { + rpcFailoverMode = newRpcFailoverMode; destroy(); networkClient = undefined; }; @@ -251,7 +246,6 @@ export function createAutoManagedNetworkClient< provider: providerProxy, blockTracker: blockTrackerProxy, destroy, - enableRpcFailover, - disableRpcFailover, + setRpcFailoverMode, }; } diff --git a/packages/network-controller/src/create-network-client-tests/rpc-endpoint-events.test.ts b/packages/network-controller/src/create-network-client-tests/rpc-endpoint-events.test.ts index fa1c5587f7..3c50a39f39 100644 --- a/packages/network-controller/src/create-network-client-tests/rpc-endpoint-events.test.ts +++ b/packages/network-controller/src/create-network-client-tests/rpc-endpoint-events.test.ts @@ -75,7 +75,7 @@ describe('createNetworkClient - RPC endpoint events', () => { { networkClientId: 'AAAA-AAAA-AAAA-AAAA', providerType: networkClientType, - isRpcFailoverEnabled: true, + rpcFailoverMode: 'enabled', failoverRpcUrls: [failoverEndpointUrl], messenger, getRpcServiceOptions: () => ({ @@ -192,7 +192,7 @@ describe('createNetworkClient - RPC endpoint events', () => { { providerType: networkClientType, networkClientId: 'AAAA-AAAA-AAAA-AAAA', - isRpcFailoverEnabled: true, + rpcFailoverMode: 'enabled', failoverRpcUrls: [failoverEndpointUrl], messenger, getRpcServiceOptions: () => ({ @@ -310,7 +310,7 @@ describe('createNetworkClient - RPC endpoint events', () => { { providerType: networkClientType, networkClientId: 'AAAA-AAAA-AAAA-AAAA', - isRpcFailoverEnabled: true, + rpcFailoverMode: 'enabled', failoverRpcUrls: [failoverEndpointUrl], messenger: rootMessenger, getRpcServiceOptions: () => ({ @@ -393,7 +393,7 @@ describe('createNetworkClient - RPC endpoint events', () => { { providerType: networkClientType, networkClientId: 'AAAA-AAAA-AAAA-AAAA', - isRpcFailoverEnabled: true, + rpcFailoverMode: 'enabled', failoverRpcUrls: [failoverEndpointUrl], messenger: rootMessenger, getRpcServiceOptions: () => ({ @@ -466,7 +466,7 @@ describe('createNetworkClient - RPC endpoint events', () => { { providerType: networkClientType, networkClientId: 'AAAA-AAAA-AAAA-AAAA', - isRpcFailoverEnabled: true, + rpcFailoverMode: 'enabled', failoverRpcUrls: [failoverEndpointUrl], messenger, getRpcServiceOptions: () => ({ @@ -579,7 +579,7 @@ describe('createNetworkClient - RPC endpoint events', () => { { providerType: networkClientType, networkClientId: 'AAAA-AAAA-AAAA-AAAA', - isRpcFailoverEnabled: true, + rpcFailoverMode: 'enabled', failoverRpcUrls: [failoverEndpointUrl], messenger, getRpcServiceOptions: () => ({ @@ -704,7 +704,7 @@ describe('createNetworkClient - RPC endpoint events', () => { { providerType: networkClientType, networkClientId: 'AAAA-AAAA-AAAA-AAAA', - isRpcFailoverEnabled: true, + rpcFailoverMode: 'enabled', failoverRpcUrls: [failoverEndpointUrl], messenger, getRpcServiceOptions: () => ({ @@ -847,7 +847,7 @@ describe('createNetworkClient - RPC endpoint events', () => { { providerType: networkClientType, networkClientId: 'AAAA-AAAA-AAAA-AAAA', - isRpcFailoverEnabled: true, + rpcFailoverMode: 'enabled', failoverRpcUrls: [failoverEndpointUrl], messenger, getRpcServiceOptions: () => ({ @@ -1042,7 +1042,7 @@ describe('createNetworkClient - RPC endpoint events', () => { { providerType: networkClientType, networkClientId: 'AAAA-AAAA-AAAA-AAAA', - isRpcFailoverEnabled: true, + rpcFailoverMode: 'enabled', failoverRpcUrls: [failoverEndpointUrl], messenger, getRpcServiceOptions: () => ({ diff --git a/packages/network-controller/src/create-network-client-tests/rpc-endpoint-failover.test.ts b/packages/network-controller/src/create-network-client-tests/rpc-endpoint-failover.test.ts new file mode 100644 index 0000000000..865f78f93a --- /dev/null +++ b/packages/network-controller/src/create-network-client-tests/rpc-endpoint-failover.test.ts @@ -0,0 +1,135 @@ +import { buildRootMessenger } from '../../tests/helpers'; +import { + withMockedCommunications, + withNetworkClient, +} from '../../tests/network-client/helpers'; + +describe('createNetworkClient - RPC endpoint failover (forced)', () => { + describe('when rpcFailoverMode is forced and providerType is infura', () => { + it('routes requests to the failover endpoint instead of Infura when failover URLs are provided', async () => { + const failoverUrl = 'https://failover.example.com'; + + // Only mock the failover URL — if Infura is hit, nock will throw because + // there is no matching mock for it. + // eth_gasPrice is not served by local middleware so it actually reaches + // the RPC endpoint, letting us confirm which host received the request. + await withMockedCommunications( + { + providerType: 'custom', + customRpcUrl: failoverUrl, + }, + async (failoverComms) => { + failoverComms.mockNextBlockTrackerRequest({ blockNumber: '0x1' }); + failoverComms.mockRpcCall({ + request: { method: 'eth_gasPrice', params: [] }, + response: { result: '0xabc' }, + }); + + const messenger = buildRootMessenger(); + + const result = await withNetworkClient( + { + providerType: 'infura', + failoverRpcUrls: [failoverUrl], + rpcFailoverMode: 'forced', + messenger, + getRpcServiceOptions: () => ({ + fetch, + btoa, + isOffline: (): boolean => false, + }), + }, + async ({ makeRpcCall }) => { + return await makeRpcCall({ method: 'eth_gasPrice', params: [] }); + }, + ); + + expect(result).toBe('0xabc'); + }, + ); + }); + + it('falls back to Infura when no failover URLs are provided', async () => { + // Only mock Infura — if any failover were hit, nock would throw. + await withMockedCommunications( + { + providerType: 'infura', + }, + async (infuraComms) => { + infuraComms.mockNextBlockTrackerRequest({ blockNumber: '0x1' }); + infuraComms.mockRpcCall({ + request: { method: 'eth_gasPrice', params: [] }, + response: { result: '0xdef' }, + }); + + const messenger = buildRootMessenger(); + + const result = await withNetworkClient( + { + providerType: 'infura', + failoverRpcUrls: [], + rpcFailoverMode: 'forced', + messenger, + getRpcServiceOptions: () => ({ + fetch, + btoa, + isOffline: (): boolean => false, + }), + }, + async ({ makeRpcCall }) => { + return await makeRpcCall({ method: 'eth_gasPrice', params: [] }); + }, + ); + + expect(result).toBe('0xdef'); + }, + ); + }); + }); + + describe('when rpcFailoverMode is forced and providerType is custom', () => { + it('still routes requests to the custom primary endpoint, not the failover', async () => { + const customRpcUrl = 'https://custom.example.com'; + const failoverUrl = 'https://failover.example.com'; + + // Only mock the custom URL — if failover is hit, nock will throw. + // eth_gasPrice is not served by local middleware so it actually reaches + // the RPC endpoint, letting us confirm which host received the request. + await withMockedCommunications( + { + providerType: 'custom', + customRpcUrl, + }, + async (customComms) => { + customComms.mockNextBlockTrackerRequest({ blockNumber: '0x1' }); + customComms.mockRpcCall({ + request: { method: 'eth_gasPrice', params: [] }, + response: { result: '0xabc' }, + }); + + const messenger = buildRootMessenger(); + + const result = await withNetworkClient( + { + providerType: 'custom', + customRpcUrl, + failoverRpcUrls: [failoverUrl], + rpcFailoverMode: 'forced', + messenger, + getRpcServiceOptions: () => ({ + fetch, + btoa, + isOffline: (): boolean => false, + }), + }, + async ({ makeRpcCall }) => { + return await makeRpcCall({ method: 'eth_gasPrice', params: [] }); + }, + ); + + expect(result).toBe('0xabc'); + }, + ); + }); + }); +}); diff --git a/packages/network-controller/src/create-network-client.ts b/packages/network-controller/src/create-network-client.ts index 8fc02a6f92..f79ef7b2ca 100644 --- a/packages/network-controller/src/create-network-client.ts +++ b/packages/network-controller/src/create-network-client.ts @@ -48,6 +48,7 @@ import { isTimeoutError, } from './rpc-service/rpc-service'; import { RpcServiceChain } from './rpc-service/rpc-service-chain'; +import type { RpcFailoverMode } from './selectors'; import type { BlockTracker, NetworkClientConfiguration, @@ -130,11 +131,10 @@ type RpcApiMiddleware = JsonRpcMiddleware< * @param args.getBlockTrackerOptions - Factory for constructing block tracker * options. See {@link NetworkControllerOptions.getBlockTrackerOptions}. * @param args.messenger - The network controller messenger. - * @param args.isRpcFailoverEnabled - Whether or not requests sent to the - * primary RPC endpoint for this network should be automatically diverted to - * provided failover endpoints if the primary is unavailable. This effectively - * causes the `failoverRpcUrls` property of the network client configuration - * to be honored or ignored. + * @param args.rpcFailoverMode - The RPC failover mode to apply: `disabled` + * (failover off), `enabled` (divert to the configured failover URLs when the + * primary endpoint is unavailable), or `forced` (Infura endpoints that have + * failover URLs route all traffic to those URLs, bypassing Infura entirely). * @param args.logger - A `loglevel` logger. * @returns The network client. */ @@ -144,7 +144,7 @@ export function createNetworkClient({ getRpcServiceOptions, getBlockTrackerOptions, messenger, - isRpcFailoverEnabled, + rpcFailoverMode, logger, }: { id: NetworkClientId; @@ -156,7 +156,7 @@ export function createNetworkClient({ rpcEndpointUrl: string, ) => Omit; messenger: NetworkControllerMessenger; - isRpcFailoverEnabled: boolean; + rpcFailoverMode: RpcFailoverMode; logger?: Logger; }): NetworkClient { const primaryEndpointUrl = @@ -169,7 +169,7 @@ export function createNetworkClient({ configuration, getRpcServiceOptions, messenger, - isRpcFailoverEnabled, + rpcFailoverMode, logger, }); @@ -225,6 +225,56 @@ export function createNetworkClient({ return { configuration, provider, blockTracker, destroy }; } +/** + * Determines the ordered list of endpoints that make up the RPC service chain + * for a network, honoring the RPC failover flags. + * + * @param args - The arguments. + * @param args.primaryEndpointUrl - The primary endpoint URL for the network. + * @param args.failoverRpcUrls - The configured failover URLs, if any. + * @param args.rpcFailoverMode - The RPC failover mode to apply: `disabled`, + * `enabled` (divert to the failover URLs when the primary is unavailable), or + * `forced` (route all traffic for Infura endpoints with failover URLs to those + * URLs, bypassing Infura). + * @returns The endpoints to use, each flagged as primary or failover. + */ +function getAvailableEndpoints({ + primaryEndpointUrl, + failoverRpcUrls, + rpcFailoverMode, +}: { + primaryEndpointUrl: string; + failoverRpcUrls: string[] | undefined; + rpcFailoverMode: RpcFailoverMode; +}): { url: string; isFailover: boolean }[] { + const failoverEndpoints = (failoverRpcUrls ?? []).map((url) => ({ + url, + isFailover: true, + })); + // We explicitly check the URL since some networks have been added with invalid configuration types in the past. + const isInfura = new URL(primaryEndpointUrl).hostname.endsWith('.infura.io'); + + if ( + rpcFailoverMode === 'forced' && + isInfura && + failoverEndpoints.length > 0 + ) { + // Forced mode for an Infura endpoint with failovers: bypass Infura entirely + // and route all traffic (including block polling) to failovers. The first + // failover becomes the positional primary of the chain, so + // availability/degraded events will report that failover URL as the primary + // endpoint (there is no Infura primary in this mode). + return failoverEndpoints; + } + if (rpcFailoverMode === 'enabled' && isInfura) { + return [ + { url: primaryEndpointUrl, isFailover: false }, + ...failoverEndpoints, + ]; + } + return [{ url: primaryEndpointUrl, isFailover: false }]; +} + /** * Creates an RPC service chain, which represents the primary endpoint URL for * the network as well as its failover URLs. @@ -237,11 +287,10 @@ export function createNetworkClient({ * @param args.getRpcServiceOptions - Factory for constructing RPC service * options. See {@link NetworkControllerOptions.getRpcServiceOptions}. * @param args.messenger - The network controller messenger. - * @param args.isRpcFailoverEnabled - Whether or not requests sent to the - * primary RPC endpoint for this network should be automatically diverted to - * provided failover endpoints if the primary is unavailable. This effectively - * causes the `failoverRpcUrls` property of the network client configuration - * to be honored or ignored. + * @param args.rpcFailoverMode - The RPC failover mode to apply: `disabled` + * (failover off), `enabled` (divert to the configured failover URLs when the + * primary endpoint is unavailable), or `forced` (Infura endpoints that have + * failover URLs route all traffic to those URLs, bypassing Infura entirely). * @param args.logger - A `loglevel` logger. * @returns The RPC service chain. */ @@ -251,7 +300,7 @@ function createRpcServiceChain({ configuration, getRpcServiceOptions, messenger, - isRpcFailoverEnabled, + rpcFailoverMode, logger, }: { id: NetworkClientId; @@ -261,22 +310,14 @@ function createRpcServiceChain({ rpcEndpointUrl: string, ) => RpcServiceOptionsWithDefaults; messenger: NetworkControllerMessenger; - isRpcFailoverEnabled: boolean; + rpcFailoverMode: RpcFailoverMode; logger?: Logger; }): RpcServiceChain { - // We explicitly check the URL since some networks have been added with invalid configuration types in the past. - const isInfura = new URL(primaryEndpointUrl).hostname.endsWith('.infura.io'); - - const availableEndpoints = - isRpcFailoverEnabled && isInfura - ? [ - { url: primaryEndpointUrl, isFailover: false }, - ...(configuration.failoverRpcUrls ?? []).map((url) => ({ - url, - isFailover: true, - })), - ] - : [{ url: primaryEndpointUrl, isFailover: false }]; + const availableEndpoints = getAvailableEndpoints({ + primaryEndpointUrl, + failoverRpcUrls: configuration.failoverRpcUrls, + rpcFailoverMode, + }); const isOffline = (): boolean => { const connectivityState = messenger.call('ConnectivityController:getState'); diff --git a/packages/network-controller/src/index.ts b/packages/network-controller/src/index.ts index 85d940d7c9..f1f3a4ecc1 100644 --- a/packages/network-controller/src/index.ts +++ b/packages/network-controller/src/index.ts @@ -68,8 +68,6 @@ export type { NetworkControllerAddNetworkAction, NetworkControllerRemoveNetworkAction, NetworkControllerUpdateNetworkAction, - NetworkControllerEnableRpcFailoverAction, - NetworkControllerDisableRpcFailoverAction, NetworkControllerGetProviderAndBlockTrackerAction, NetworkControllerGetNetworkClientRegistryAction, NetworkControllerLookupNetworkAction, diff --git a/packages/network-controller/src/selectors.test.ts b/packages/network-controller/src/selectors.test.ts new file mode 100644 index 0000000000..9e934fdc9e --- /dev/null +++ b/packages/network-controller/src/selectors.test.ts @@ -0,0 +1,42 @@ +import { getRpcFailoverMode } from './selectors'; + +/** + * Builds a remote feature flag controller state with the given failover mode. + * + * @param mode - The value to set for `corePlatformRpcFailoverMode`, if any. + * @returns The state object. + */ +function buildState(mode?: unknown): { + remoteFeatureFlags: Record; + cacheTimestamp: number; +} { + return { + remoteFeatureFlags: + mode === undefined ? {} : { corePlatformRpcFailoverMode: mode }, + cacheTimestamp: 0, + }; +} + +describe('getRpcFailoverMode', () => { + it('returns "enabled" when the flag is "enabled"', () => { + expect(getRpcFailoverMode(buildState('enabled') as never)).toBe('enabled'); + }); + + it('returns "forced" when the flag is "forced"', () => { + expect(getRpcFailoverMode(buildState('forced') as never)).toBe('forced'); + }); + + it('returns "disabled" when the flag is "disabled"', () => { + expect(getRpcFailoverMode(buildState('disabled') as never)).toBe( + 'disabled', + ); + }); + + it('returns "disabled" when the flag is absent', () => { + expect(getRpcFailoverMode(buildState() as never)).toBe('disabled'); + }); + + it('returns "disabled" when the flag is an unrecognized value', () => { + expect(getRpcFailoverMode(buildState('yes') as never)).toBe('disabled'); + }); +}); diff --git a/packages/network-controller/src/selectors.ts b/packages/network-controller/src/selectors.ts index 016c8a66b0..60406079d8 100644 --- a/packages/network-controller/src/selectors.ts +++ b/packages/network-controller/src/selectors.ts @@ -1,9 +1,28 @@ import { RemoteFeatureFlagControllerState } from '@metamask/remote-feature-flag-controller'; -export function getIsRpcFailoverEnabled( +/** + * The RPC failover behavior for Infura networks, controlled by the + * `corePlatformRpcFailoverMode` remote feature flag. + * + * - `disabled`: failover URLs are ignored; traffic stays on the primary + * endpoint. + * - `enabled`: traffic automatically diverts to failover URLs when the primary + * endpoint is unavailable. + * - `forced`: Infura endpoints that have failover URLs route all traffic to + * those failover URLs, bypassing Infura entirely. + */ +export type RpcFailoverMode = 'disabled' | 'enabled' | 'forced'; + +/** + * Reads the RPC failover mode from the remote feature flags, defaulting to + * `disabled` when the flag is absent or not a recognized value. + * + * @param state - The remote feature flag controller state. + * @returns The RPC failover mode. + */ +export function getRpcFailoverMode( state: RemoteFeatureFlagControllerState, -): boolean { - const walletFrameworkRpcFailoverEnabled = state.remoteFeatureFlags - .walletFrameworkRpcFailoverEnabled as boolean | undefined; - return walletFrameworkRpcFailoverEnabled ?? false; +): RpcFailoverMode { + const mode = state.remoteFeatureFlags.corePlatformRpcFailoverMode; + return mode === 'enabled' || mode === 'forced' ? mode : 'disabled'; } diff --git a/packages/network-controller/tests/NetworkController.provider.test.ts b/packages/network-controller/tests/NetworkController.provider.test.ts index b315a00dad..00e360b2e2 100644 --- a/packages/network-controller/tests/NetworkController.provider.test.ts +++ b/packages/network-controller/tests/NetworkController.provider.test.ts @@ -54,7 +54,7 @@ describe('NetworkController provider tests', () => { await withController( { - isRpcFailoverEnabled: true, + rpcFailoverMode: 'enabled', state: { networkConfigurationsByChainId: { '0x1337': buildCustomNetworkConfiguration({ @@ -137,7 +137,7 @@ describe('NetworkController provider tests', () => { await withController( { - isRpcFailoverEnabled: true, + rpcFailoverMode: 'enabled', state: { networkConfigurationsByChainId: { '0x1337': buildCustomNetworkConfiguration({ @@ -194,7 +194,7 @@ describe('NetworkController provider tests', () => { await withController( { - isRpcFailoverEnabled: true, + rpcFailoverMode: 'enabled', state: { networkConfigurationsByChainId: { '0x1337': buildCustomNetworkConfiguration({ @@ -281,7 +281,7 @@ describe('NetworkController provider tests', () => { await withController( { - isRpcFailoverEnabled: true, + rpcFailoverMode: 'enabled', state: { networkConfigurationsByChainId: { '0x1': buildInfuraNetworkConfiguration(InfuraNetworkType.mainnet, { @@ -381,7 +381,7 @@ describe('NetworkController provider tests', () => { await withController( { - isRpcFailoverEnabled: true, + rpcFailoverMode: 'enabled', state: { networkConfigurationsByChainId: { '0x1': buildInfuraNetworkConfiguration(InfuraNetworkType.mainnet, { @@ -488,7 +488,7 @@ describe('NetworkController provider tests', () => { await withController( { - isRpcFailoverEnabled: true, + rpcFailoverMode: 'enabled', state: { networkConfigurationsByChainId: { '0x1': buildInfuraNetworkConfiguration(InfuraNetworkType.mainnet, { @@ -568,7 +568,7 @@ describe('NetworkController provider tests', () => { await withController( { - isRpcFailoverEnabled: true, + rpcFailoverMode: 'enabled', state: { networkConfigurationsByChainId: { '0x1': buildInfuraNetworkConfiguration(InfuraNetworkType.mainnet, { @@ -671,7 +671,7 @@ describe('NetworkController provider tests', () => { await withController( { - isRpcFailoverEnabled: true, + rpcFailoverMode: 'enabled', state: { networkConfigurationsByChainId: { // The network offers both an Infura and a custom endpoint, with the @@ -764,7 +764,7 @@ describe('NetworkController provider tests', () => { await withController( { - isRpcFailoverEnabled: true, + rpcFailoverMode: 'enabled', state: { networkConfigurationsByChainId: { '0x1337': buildCustomNetworkConfiguration({ @@ -893,7 +893,7 @@ describe('NetworkController provider tests', () => { await withController( { - isRpcFailoverEnabled: true, + rpcFailoverMode: 'enabled', state: { networkConfigurationsByChainId: { '0x1337': buildCustomNetworkConfiguration({ diff --git a/packages/network-controller/tests/NetworkController.test.ts b/packages/network-controller/tests/NetworkController.test.ts index 65ee190f87..3888b27e2a 100644 --- a/packages/network-controller/tests/NetworkController.test.ts +++ b/packages/network-controller/tests/NetworkController.test.ts @@ -805,293 +805,194 @@ describe('NetworkController', () => { }); }); - describe('enableRpcFailover', () => { - describe('if the controller was initialized with isRpcFailoverEnabled = false', () => { - it('calls enableRpcFailover on only the network clients whose RPC endpoints have configured failover URLs', async () => { - const originalCreateAutoManagedNetworkClient = - createAutoManagedNetworkClientModule.createAutoManagedNetworkClient; - const autoManagedNetworkClients: AutoManagedNetworkClient[] = - []; - jest - .spyOn( - createAutoManagedNetworkClientModule, - 'createAutoManagedNetworkClient', - ) - .mockImplementation((...args) => { - const autoManagedNetworkClient = - originalCreateAutoManagedNetworkClient(...args); - jest.spyOn(autoManagedNetworkClient, 'enableRpcFailover'); - autoManagedNetworkClients.push(autoManagedNetworkClient); - return autoManagedNetworkClient; - }); + describe('RemoteFeatureFlagController:stateChange (rpcFailoverMode forced)', () => { + it('calls setRpcFailoverMode on clients with failover URLs when the flag turns forced', async () => { + const originalCreateAutoManagedNetworkClient = + createAutoManagedNetworkClientModule.createAutoManagedNetworkClient; + const autoManagedNetworkClients: AutoManagedNetworkClient[] = + []; + jest + .spyOn( + createAutoManagedNetworkClientModule, + 'createAutoManagedNetworkClient', + ) + .mockImplementation((...args) => { + const autoManagedNetworkClient = + originalCreateAutoManagedNetworkClient(...args); + jest.spyOn(autoManagedNetworkClient, 'setRpcFailoverMode'); + autoManagedNetworkClients.push(autoManagedNetworkClient); + return autoManagedNetworkClient; + }); - await withController( - { - isRpcFailoverEnabled: false, - state: { - selectedNetworkClientId: 'AAAA-AAAA-AAAA-AAAA', - networkConfigurationsByChainId: { - [ChainId.mainnet]: buildInfuraNetworkConfiguration( - InfuraNetworkType.mainnet, - { - rpcEndpoints: [ - buildInfuraRpcEndpoint(InfuraNetworkType.mainnet, { - failoverUrls: [], - }), - ], - }, - ), - '0x200': buildCustomNetworkConfiguration({ - chainId: '0x200', - rpcEndpoints: [ - buildCustomRpcEndpoint({ - networkClientId: 'AAAA-AAAA-AAAA-AAAA', - url: 'https://test.network/1', - failoverUrls: ['https://failover.endpoint/1'], - }), - ], - }), - '0x300': buildCustomNetworkConfiguration({ - chainId: '0x300', + await withController( + { + rpcFailoverMode: 'disabled', + state: { + selectedNetworkClientId: 'AAAA-AAAA-AAAA-AAAA', + networkConfigurationsByChainId: { + [ChainId.mainnet]: buildInfuraNetworkConfiguration( + InfuraNetworkType.mainnet, + { rpcEndpoints: [ - buildCustomRpcEndpoint({ - networkClientId: 'BBBB-BBBB-BBBB-BBBB', - url: 'https://test.network/2', - failoverUrls: ['https://failover.endpoint/2'], + buildInfuraRpcEndpoint(InfuraNetworkType.mainnet, { + failoverUrls: [], }), ], - }), - }, + }, + ), + '0x200': buildCustomNetworkConfiguration({ + chainId: '0x200', + rpcEndpoints: [ + buildCustomRpcEndpoint({ + networkClientId: 'AAAA-AAAA-AAAA-AAAA', + url: 'https://test.network/1', + failoverUrls: ['https://failover.endpoint/1'], + }), + ], + }), }, }, - async ({ controller }) => { - controller.enableRpcFailover(); - - expect(autoManagedNetworkClients).toHaveLength(3); - expect( - autoManagedNetworkClients[0].enableRpcFailover, - ).not.toHaveBeenCalled(); - expect( - autoManagedNetworkClients[1].enableRpcFailover, - ).toHaveBeenCalled(); - expect( - autoManagedNetworkClients[2].enableRpcFailover, - ).toHaveBeenCalled(); - }, - ); - }); - }); - - describe('if the controller was initialized with isRpcFailoverEnabled = true', () => { - it('does not call createAutoManagedNetworkClient at all', async () => { - await withController( - { - isRpcFailoverEnabled: true, - state: { - selectedNetworkClientId: 'AAAA-AAAA-AAAA-AAAA', - networkConfigurationsByChainId: { - [ChainId.mainnet]: buildInfuraNetworkConfiguration( - InfuraNetworkType.mainnet, - { - rpcEndpoints: [ - buildInfuraRpcEndpoint(InfuraNetworkType.mainnet, { - failoverUrls: [], - }), - ], - }, - ), - '0x200': buildCustomNetworkConfiguration({ - chainId: '0x200', - rpcEndpoints: [ - buildCustomRpcEndpoint({ - networkClientId: 'AAAA-AAAA-AAAA-AAAA', - url: 'https://test.network/1', - failoverUrls: ['https://failover.endpoint/1'], - }), - ], - }), - '0x300': buildCustomNetworkConfiguration({ - chainId: '0x300', - rpcEndpoints: [ - buildCustomRpcEndpoint({ - networkClientId: 'BBBB-BBBB-BBBB-BBBB', - url: 'https://test.network/2', - failoverUrls: ['https://failover.endpoint/2'], - }), - ], - }), + }, + async ({ messenger }) => { + messenger.publish( + 'RemoteFeatureFlagController:stateChange', + { + remoteFeatureFlags: { + corePlatformRpcFailoverMode: 'forced', }, + cacheTimestamp: 0, }, - }, - async ({ controller }) => { - const originalCreateAutoManagedNetworkClient = - createAutoManagedNetworkClientModule.createAutoManagedNetworkClient; - const autoManagedNetworkClients: AutoManagedNetworkClient[] = - []; - jest - .spyOn( - createAutoManagedNetworkClientModule, - 'createAutoManagedNetworkClient', - ) - .mockImplementation((...args) => { - const autoManagedNetworkClient = - originalCreateAutoManagedNetworkClient(...args); - jest.spyOn(autoManagedNetworkClient, 'enableRpcFailover'); - autoManagedNetworkClients.push(autoManagedNetworkClient); - return autoManagedNetworkClient; - }); - - controller.enableRpcFailover(); + [], + ); - expect(autoManagedNetworkClients).toHaveLength(0); - }, - ); - }); + expect(autoManagedNetworkClients).toHaveLength(2); + expect( + autoManagedNetworkClients[0].setRpcFailoverMode, + ).not.toHaveBeenCalledWith('forced'); + expect( + autoManagedNetworkClients[1].setRpcFailoverMode, + ).toHaveBeenCalledWith('forced'); + }, + ); }); - }); - describe('disableRpcFailover', () => { - describe('if the controller was initialized with isRpcFailoverEnabled = true', () => { - it('calls disableRpcFailover on only the network clients whose RPC endpoints have configured failover URLs', async () => { - const originalCreateAutoManagedNetworkClient = - createAutoManagedNetworkClientModule.createAutoManagedNetworkClient; - const autoManagedNetworkClients: AutoManagedNetworkClient[] = - []; - jest - .spyOn( - createAutoManagedNetworkClientModule, - 'createAutoManagedNetworkClient', - ) - .mockImplementation((...args) => { - const autoManagedNetworkClient = - originalCreateAutoManagedNetworkClient(...args); - jest.spyOn(autoManagedNetworkClient, 'disableRpcFailover'); - autoManagedNetworkClients.push(autoManagedNetworkClient); - return autoManagedNetworkClient; - }); + it('picks up the initial forced value during init()', async () => { + const originalCreateAutoManagedNetworkClient = + createAutoManagedNetworkClientModule.createAutoManagedNetworkClient; + const autoManagedNetworkClients: AutoManagedNetworkClient[] = + []; + jest + .spyOn( + createAutoManagedNetworkClientModule, + 'createAutoManagedNetworkClient', + ) + .mockImplementation((...args) => { + const autoManagedNetworkClient = + originalCreateAutoManagedNetworkClient(...args); + jest.spyOn(autoManagedNetworkClient, 'setRpcFailoverMode'); + autoManagedNetworkClients.push(autoManagedNetworkClient); + return autoManagedNetworkClient; + }); - await withController( - { - isRpcFailoverEnabled: true, - state: { - selectedNetworkClientId: 'AAAA-AAAA-AAAA-AAAA', - networkConfigurationsByChainId: { - [ChainId.mainnet]: buildInfuraNetworkConfiguration( - InfuraNetworkType.mainnet, - { - rpcEndpoints: [ - buildInfuraRpcEndpoint(InfuraNetworkType.mainnet, { - failoverUrls: [], - }), - ], - }, - ), - '0x200': buildCustomNetworkConfiguration({ - chainId: '0x200', + await withController( + { + rpcFailoverMode: 'forced', + state: { + selectedNetworkClientId: 'AAAA-AAAA-AAAA-AAAA', + networkConfigurationsByChainId: { + [ChainId.mainnet]: buildInfuraNetworkConfiguration( + InfuraNetworkType.mainnet, + { rpcEndpoints: [ - buildCustomRpcEndpoint({ - networkClientId: 'AAAA-AAAA-AAAA-AAAA', - url: 'https://test.network/1', + buildInfuraRpcEndpoint(InfuraNetworkType.mainnet, { failoverUrls: ['https://failover.endpoint/1'], }), ], - }), - '0x300': buildCustomNetworkConfiguration({ - chainId: '0x300', - rpcEndpoints: [ - buildCustomRpcEndpoint({ - networkClientId: 'BBBB-BBBB-BBBB-BBBB', - url: 'https://test.network/2', - failoverUrls: ['https://failover.endpoint/2'], - }), - ], - }), - }, + }, + ), + '0x200': buildCustomNetworkConfiguration({ + chainId: '0x200', + rpcEndpoints: [ + buildCustomRpcEndpoint({ + networkClientId: 'AAAA-AAAA-AAAA-AAAA', + url: 'https://test.network/1', + failoverUrls: [], + }), + ], + }), }, }, - async ({ controller }) => { - controller.disableRpcFailover(); - - expect(autoManagedNetworkClients).toHaveLength(3); - expect( - autoManagedNetworkClients[0].disableRpcFailover, - ).not.toHaveBeenCalled(); - expect( - autoManagedNetworkClients[1].disableRpcFailover, - ).toHaveBeenCalled(); - expect( - autoManagedNetworkClients[2].disableRpcFailover, - ).toHaveBeenCalled(); - }, - ); - }); + }, + async () => { + expect(autoManagedNetworkClients).toHaveLength(2); + expect( + autoManagedNetworkClients[0].setRpcFailoverMode, + ).toHaveBeenCalledWith('forced'); + expect( + autoManagedNetworkClients[1].setRpcFailoverMode, + ).not.toHaveBeenCalledWith('forced'); + }, + ); }); - describe('if the controller was initialized with isRpcFailoverEnabled = false', () => { - it('does not call createAutoManagedNetworkClient at all', async () => { - await withController( - { - isRpcFailoverEnabled: false, - state: { - selectedNetworkClientId: 'AAAA-AAAA-AAAA-AAAA', - networkConfigurationsByChainId: { - [ChainId.mainnet]: buildInfuraNetworkConfiguration( - InfuraNetworkType.mainnet, - { - rpcEndpoints: [ - buildInfuraRpcEndpoint(InfuraNetworkType.mainnet, { - failoverUrls: [], - }), - ], - }, - ), - '0x200': buildCustomNetworkConfiguration({ - chainId: '0x200', - rpcEndpoints: [ - buildCustomRpcEndpoint({ - networkClientId: 'AAAA-AAAA-AAAA-AAAA', - url: 'https://test.network/1', - failoverUrls: ['https://failover.endpoint/1'], - }), - ], - }), - '0x300': buildCustomNetworkConfiguration({ - chainId: '0x300', - rpcEndpoints: [ - buildCustomRpcEndpoint({ - networkClientId: 'BBBB-BBBB-BBBB-BBBB', - url: 'https://test.network/2', - failoverUrls: ['https://failover.endpoint/2'], - }), - ], - }), - }, + it('calls setRpcFailoverMode with forced but not enabled when only the forced mode is set', async () => { + const originalCreateAutoManagedNetworkClient = + createAutoManagedNetworkClientModule.createAutoManagedNetworkClient; + const autoManagedNetworkClients: AutoManagedNetworkClient[] = + []; + jest + .spyOn( + createAutoManagedNetworkClientModule, + 'createAutoManagedNetworkClient', + ) + .mockImplementation((...args) => { + const autoManagedNetworkClient = + originalCreateAutoManagedNetworkClient(...args); + jest.spyOn(autoManagedNetworkClient, 'setRpcFailoverMode'); + autoManagedNetworkClients.push(autoManagedNetworkClient); + return autoManagedNetworkClient; + }); + + await withController( + { + rpcFailoverMode: 'disabled', + state: { + selectedNetworkClientId: 'AAAA-AAAA-AAAA-AAAA', + networkConfigurationsByChainId: { + '0x200': buildCustomNetworkConfiguration({ + chainId: '0x200', + rpcEndpoints: [ + buildCustomRpcEndpoint({ + networkClientId: 'AAAA-AAAA-AAAA-AAAA', + url: 'https://test.network/1', + failoverUrls: ['https://failover.endpoint/1'], + }), + ], + }), }, }, - async ({ controller }) => { - const originalCreateAutoManagedNetworkClient = - createAutoManagedNetworkClientModule.createAutoManagedNetworkClient; - const autoManagedNetworkClients: AutoManagedNetworkClient[] = - []; - jest - .spyOn( - createAutoManagedNetworkClientModule, - 'createAutoManagedNetworkClient', - ) - .mockImplementation((...args) => { - const autoManagedNetworkClient = - originalCreateAutoManagedNetworkClient(...args); - jest.spyOn(autoManagedNetworkClient, 'disableRpcFailover'); - autoManagedNetworkClients.push(autoManagedNetworkClient); - return autoManagedNetworkClient; - }); - - controller.disableRpcFailover(); + }, + async ({ messenger }) => { + messenger.publish( + 'RemoteFeatureFlagController:stateChange', + { + remoteFeatureFlags: { + corePlatformRpcFailoverMode: 'forced', + }, + cacheTimestamp: 0, + }, + [], + ); - expect(autoManagedNetworkClients).toHaveLength(0); - }, - ); - }); + expect(autoManagedNetworkClients).toHaveLength(1); + expect( + autoManagedNetworkClients[0].setRpcFailoverMode, + ).toHaveBeenCalledWith('forced'); + expect( + autoManagedNetworkClients[0].setRpcFailoverMode, + ).not.toHaveBeenCalledWith('enabled'); + }, + ); }); }); @@ -1657,8 +1558,7 @@ describe('NetworkController', () => { }, provider: expect.anything(), destroy: expect.any(Function), - enableRpcFailover: expect.any(Function), - disableRpcFailover: expect.any(Function), + setRpcFailoverMode: expect.any(Function), }, 'base-mainnet': { blockTracker: expect.anything(), @@ -1672,8 +1572,7 @@ describe('NetworkController', () => { }, provider: expect.anything(), destroy: expect.any(Function), - enableRpcFailover: expect.any(Function), - disableRpcFailover: expect.any(Function), + setRpcFailoverMode: expect.any(Function), }, 'bsc-mainnet': { blockTracker: expect.anything(), @@ -1687,8 +1586,7 @@ describe('NetworkController', () => { }, provider: expect.anything(), destroy: expect.any(Function), - enableRpcFailover: expect.any(Function), - disableRpcFailover: expect.any(Function), + setRpcFailoverMode: expect.any(Function), }, 'linea-mainnet': { blockTracker: expect.anything(), @@ -1702,8 +1600,7 @@ describe('NetworkController', () => { }, provider: expect.anything(), destroy: expect.any(Function), - enableRpcFailover: expect.any(Function), - disableRpcFailover: expect.any(Function), + setRpcFailoverMode: expect.any(Function), }, 'linea-sepolia': { blockTracker: expect.anything(), @@ -1717,8 +1614,7 @@ describe('NetworkController', () => { }, provider: expect.anything(), destroy: expect.any(Function), - enableRpcFailover: expect.any(Function), - disableRpcFailover: expect.any(Function), + setRpcFailoverMode: expect.any(Function), }, mainnet: { blockTracker: expect.anything(), @@ -1732,8 +1628,7 @@ describe('NetworkController', () => { }, provider: expect.anything(), destroy: expect.any(Function), - enableRpcFailover: expect.any(Function), - disableRpcFailover: expect.any(Function), + setRpcFailoverMode: expect.any(Function), }, 'megaeth-testnet-v2': { blockTracker: expect.anything(), @@ -1746,8 +1641,7 @@ describe('NetworkController', () => { }, provider: expect.anything(), destroy: expect.any(Function), - enableRpcFailover: expect.any(Function), - disableRpcFailover: expect.any(Function), + setRpcFailoverMode: expect.any(Function), }, 'monad-mainnet': { blockTracker: expect.anything(), @@ -1761,8 +1655,7 @@ describe('NetworkController', () => { }, provider: expect.anything(), destroy: expect.any(Function), - enableRpcFailover: expect.any(Function), - disableRpcFailover: expect.any(Function), + setRpcFailoverMode: expect.any(Function), }, 'monad-testnet': { blockTracker: expect.anything(), @@ -1775,8 +1668,7 @@ describe('NetworkController', () => { }, provider: expect.anything(), destroy: expect.any(Function), - enableRpcFailover: expect.any(Function), - disableRpcFailover: expect.any(Function), + setRpcFailoverMode: expect.any(Function), }, 'optimism-mainnet': { blockTracker: expect.anything(), @@ -1790,8 +1682,7 @@ describe('NetworkController', () => { }, provider: expect.anything(), destroy: expect.any(Function), - enableRpcFailover: expect.any(Function), - disableRpcFailover: expect.any(Function), + setRpcFailoverMode: expect.any(Function), }, 'polygon-mainnet': { blockTracker: expect.anything(), @@ -1805,8 +1696,7 @@ describe('NetworkController', () => { }, provider: expect.anything(), destroy: expect.any(Function), - enableRpcFailover: expect.any(Function), - disableRpcFailover: expect.any(Function), + setRpcFailoverMode: expect.any(Function), }, sepolia: { blockTracker: expect.anything(), @@ -1820,8 +1710,7 @@ describe('NetworkController', () => { }, provider: expect.anything(), destroy: expect.any(Function), - enableRpcFailover: expect.any(Function), - disableRpcFailover: expect.any(Function), + setRpcFailoverMode: expect.any(Function), }, }); }, @@ -1876,8 +1765,7 @@ describe('NetworkController', () => { }, provider: expect.anything(), destroy: expect.any(Function), - enableRpcFailover: expect.any(Function), - disableRpcFailover: expect.any(Function), + setRpcFailoverMode: expect.any(Function), }, 'BBBB-BBBB-BBBB-BBBB': { blockTracker: expect.anything(), @@ -1890,8 +1778,7 @@ describe('NetworkController', () => { }, provider: expect.anything(), destroy: expect.any(Function), - enableRpcFailover: expect.any(Function), - disableRpcFailover: expect.any(Function), + setRpcFailoverMode: expect.any(Function), }, }); }, @@ -4485,7 +4372,7 @@ describe('NetworkController', () => { infuraProjectId, getRpcServiceOptions, getBlockTrackerOptions, - isRpcFailoverEnabled: true, + rpcFailoverMode: 'enabled', }, ({ controller, networkControllerMessenger }) => { const defaultRpcEndpoint: InfuraRpcEndpoint = { @@ -4535,7 +4422,8 @@ describe('NetworkController', () => { getRpcServiceOptions, getBlockTrackerOptions, messenger: networkControllerMessenger, - isRpcFailoverEnabled: true, + rpcFailoverMode: 'enabled', + logger: undefined, }, ); expect(createAutoManagedNetworkClientSpy).toHaveBeenNthCalledWith( @@ -4552,7 +4440,8 @@ describe('NetworkController', () => { getRpcServiceOptions, getBlockTrackerOptions, messenger: networkControllerMessenger, - isRpcFailoverEnabled: true, + rpcFailoverMode: 'enabled', + logger: undefined, }, ); expect(createAutoManagedNetworkClientSpy).toHaveBeenNthCalledWith( @@ -4569,7 +4458,8 @@ describe('NetworkController', () => { getRpcServiceOptions, getBlockTrackerOptions, messenger: networkControllerMessenger, - isRpcFailoverEnabled: true, + rpcFailoverMode: 'enabled', + logger: undefined, }, ); const networkConfigurationsByNetworkClientId = @@ -4638,7 +4528,7 @@ describe('NetworkController', () => { failoverUrls: { [infuraChainId]: ['https://chain.failover'], }, - isRpcFailoverEnabled: true, + rpcFailoverMode: 'enabled', }, ({ controller }) => { const defaultRpcEndpoint: InfuraRpcEndpoint = { @@ -6002,7 +5892,7 @@ describe('NetworkController', () => { infuraProjectId, getRpcServiceOptions, getBlockTrackerOptions, - isRpcFailoverEnabled: true, + rpcFailoverMode: 'enabled', }, async ({ controller, networkControllerMessenger }) => { const infuraRpcEndpoint: InfuraRpcEndpoint = { @@ -6036,7 +5926,8 @@ describe('NetworkController', () => { getRpcServiceOptions, getBlockTrackerOptions, messenger: networkControllerMessenger, - isRpcFailoverEnabled: true, + rpcFailoverMode: 'enabled', + logger: undefined, }); const networkConfigurationsByNetworkClientId = @@ -6095,7 +5986,7 @@ describe('NetworkController', () => { failoverUrls: { [infuraChainId]: ['https://chain.failover'], }, - isRpcFailoverEnabled: true, + rpcFailoverMode: 'enabled', }, async ({ controller }) => { const infuraRpcEndpoint: InfuraRpcEndpoint = { @@ -6316,7 +6207,7 @@ describe('NetworkController', () => { infuraProjectId: 'some-infura-project-id', getRpcServiceOptions, getBlockTrackerOptions, - isRpcFailoverEnabled: true, + rpcFailoverMode: 'enabled', }, async ({ controller, networkControllerMessenger }) => { const [rpcEndpoint1, rpcEndpoint2] = [ @@ -6352,7 +6243,8 @@ describe('NetworkController', () => { getRpcServiceOptions, getBlockTrackerOptions, messenger: networkControllerMessenger, - isRpcFailoverEnabled: true, + rpcFailoverMode: 'enabled', + logger: undefined, }); expect( createAutoManagedNetworkClientSpy, @@ -6368,7 +6260,8 @@ describe('NetworkController', () => { getRpcServiceOptions, getBlockTrackerOptions, messenger: networkControllerMessenger, - isRpcFailoverEnabled: true, + rpcFailoverMode: 'enabled', + logger: undefined, }); const networkConfigurationsByNetworkClientId = @@ -7315,7 +7208,7 @@ describe('NetworkController', () => { }, getRpcServiceOptions, getBlockTrackerOptions, - isRpcFailoverEnabled: true, + rpcFailoverMode: 'enabled', }, async ({ controller, networkControllerMessenger }) => { createNetworkClientMock.mockReturnValue(buildFakeClient()); @@ -7345,7 +7238,8 @@ describe('NetworkController', () => { getRpcServiceOptions, getBlockTrackerOptions, messenger: networkControllerMessenger, - isRpcFailoverEnabled: true, + rpcFailoverMode: 'enabled', + logger: undefined, }); const networkConfigurationsByNetworkClientId = getNetworkConfigurationsByNetworkClientId( @@ -8185,7 +8079,7 @@ describe('NetworkController', () => { }, getRpcServiceOptions, getBlockTrackerOptions, - isRpcFailoverEnabled: true, + rpcFailoverMode: 'enabled', }, async ({ controller, networkControllerMessenger }) => { await controller.updateNetwork('0x1337', { @@ -8220,7 +8114,8 @@ describe('NetworkController', () => { getRpcServiceOptions, getBlockTrackerOptions, messenger: networkControllerMessenger, - isRpcFailoverEnabled: true, + rpcFailoverMode: 'enabled', + logger: undefined, }, ); expect(createAutoManagedNetworkClientSpy).toHaveBeenNthCalledWith( @@ -8237,7 +8132,8 @@ describe('NetworkController', () => { getRpcServiceOptions, getBlockTrackerOptions, messenger: networkControllerMessenger, - isRpcFailoverEnabled: true, + rpcFailoverMode: 'enabled', + logger: undefined, }, ); @@ -9184,7 +9080,7 @@ describe('NetworkController', () => { }, getRpcServiceOptions, getBlockTrackerOptions, - isRpcFailoverEnabled: true, + rpcFailoverMode: 'enabled', }, async ({ controller, networkControllerMessenger }) => { createNetworkClientMock.mockImplementation( @@ -9227,7 +9123,8 @@ describe('NetworkController', () => { getRpcServiceOptions, getBlockTrackerOptions, messenger: networkControllerMessenger, - isRpcFailoverEnabled: true, + rpcFailoverMode: 'enabled', + logger: undefined, }); expect( getNetworkConfigurationsByNetworkClientId( @@ -10350,7 +10247,7 @@ describe('NetworkController', () => { }, getRpcServiceOptions, getBlockTrackerOptions, - isRpcFailoverEnabled: true, + rpcFailoverMode: 'enabled', }, async ({ controller, networkControllerMessenger }) => { createNetworkClientMock.mockImplementation( @@ -10388,7 +10285,8 @@ describe('NetworkController', () => { getRpcServiceOptions, getBlockTrackerOptions, messenger: networkControllerMessenger, - isRpcFailoverEnabled: true, + rpcFailoverMode: 'enabled', + logger: undefined, }); expect( createAutoManagedNetworkClientSpy, @@ -10404,7 +10302,8 @@ describe('NetworkController', () => { getRpcServiceOptions, getBlockTrackerOptions, messenger: networkControllerMessenger, - isRpcFailoverEnabled: true, + rpcFailoverMode: 'enabled', + logger: undefined, }); const networkConfigurationsByNetworkClientId = @@ -11067,7 +10966,7 @@ describe('NetworkController', () => { }, getRpcServiceOptions, getBlockTrackerOptions, - isRpcFailoverEnabled: true, + rpcFailoverMode: 'enabled', }, async ({ controller, networkControllerMessenger }) => { createNetworkClientMock.mockImplementation( @@ -11110,7 +11009,8 @@ describe('NetworkController', () => { getRpcServiceOptions, getBlockTrackerOptions, messenger: networkControllerMessenger, - isRpcFailoverEnabled: true, + rpcFailoverMode: 'enabled', + logger: undefined, }); expect(createAutoManagedNetworkClientSpy).toHaveBeenCalledWith({ networkClientId: 'DDDD-DDDD-DDDD-DDDD', @@ -11124,7 +11024,8 @@ describe('NetworkController', () => { getRpcServiceOptions, getBlockTrackerOptions, messenger: networkControllerMessenger, - isRpcFailoverEnabled: true, + rpcFailoverMode: 'enabled', + logger: undefined, }); expect( @@ -11803,7 +11704,7 @@ describe('NetworkController', () => { infuraProjectId: 'some-infura-project-id', getRpcServiceOptions, getBlockTrackerOptions, - isRpcFailoverEnabled: true, + rpcFailoverMode: 'enabled', }, async ({ controller, networkControllerMessenger }) => { createNetworkClientMock.mockImplementation( @@ -11845,7 +11746,8 @@ describe('NetworkController', () => { getRpcServiceOptions, getBlockTrackerOptions, messenger: networkControllerMessenger, - isRpcFailoverEnabled: true, + rpcFailoverMode: 'enabled', + logger: undefined, }); expect( createAutoManagedNetworkClientSpy, @@ -11861,7 +11763,8 @@ describe('NetworkController', () => { getRpcServiceOptions, getBlockTrackerOptions, messenger: networkControllerMessenger, - isRpcFailoverEnabled: true, + rpcFailoverMode: 'enabled', + logger: undefined, }); const networkConfigurationsByChainId = @@ -12512,7 +12415,7 @@ describe('NetworkController', () => { }, getRpcServiceOptions, getBlockTrackerOptions, - isRpcFailoverEnabled: true, + rpcFailoverMode: 'enabled', }, async ({ controller, networkControllerMessenger }) => { createNetworkClientMock.mockImplementation(({ configuration }) => { @@ -12548,7 +12451,8 @@ describe('NetworkController', () => { getRpcServiceOptions, getBlockTrackerOptions, messenger: networkControllerMessenger, - isRpcFailoverEnabled: true, + rpcFailoverMode: 'enabled', + logger: undefined, }, ); expect(createAutoManagedNetworkClientSpy).toHaveBeenNthCalledWith( @@ -12565,7 +12469,8 @@ describe('NetworkController', () => { getRpcServiceOptions, getBlockTrackerOptions, messenger: networkControllerMessenger, - isRpcFailoverEnabled: true, + rpcFailoverMode: 'enabled', + logger: undefined, }, ); diff --git a/packages/network-controller/tests/helpers.ts b/packages/network-controller/tests/helpers.ts index adfa80aeb9..a46c7cdf78 100644 --- a/packages/network-controller/tests/helpers.ts +++ b/packages/network-controller/tests/helpers.ts @@ -42,6 +42,7 @@ import type { } from '../src/NetworkController'; import { RpcEndpointType } from '../src/NetworkController'; import { RpcServiceOptions } from '../src/rpc-service/rpc-service'; +import type { RpcFailoverMode } from '../src/selectors'; import type { CustomNetworkClientConfiguration, InfuraNetworkClientConfiguration, @@ -89,15 +90,15 @@ export const TESTNET = { * @param options - Optional configuration. * @param options.connectivityStatus - The connectivity status to return by default. * If not provided, defaults to Online. - * @param options.isRpcFailoverEnabled - The RPC failover feature flag to return, defaults to false. + * @param options.rpcFailoverMode - The RPC failover mode to return, defaults to `disabled`. * @returns The messenger. */ export function buildRootMessenger({ connectivityStatus = CONNECTIVITY_STATUSES.Online, - isRpcFailoverEnabled = false, + rpcFailoverMode = 'disabled', }: { connectivityStatus?: ConnectivityStatus; - isRpcFailoverEnabled?: boolean; + rpcFailoverMode?: RpcFailoverMode; } = {}): RootMessenger { const rootMessenger = new Messenger< MockAnyNamespace, @@ -116,7 +117,7 @@ export function buildRootMessenger({ 'RemoteFeatureFlagController:getState', () => ({ remoteFeatureFlags: { - walletFrameworkRpcFailoverEnabled: isRpcFailoverEnabled, + corePlatformRpcFailoverMode: rpcFailoverMode, }, cacheTimestamp: 0, }), @@ -631,7 +632,7 @@ type WithControllerCallback = ({ }) => Promise | ReturnValue; type WithControllerOptions = Partial & { - isRpcFailoverEnabled?: boolean; + rpcFailoverMode?: RpcFailoverMode; initializeController?: boolean; }; @@ -654,11 +655,13 @@ export async function withController( ): Promise { const [{ ...rest }, fn] = args.length === 2 ? args : [{}, args[0]]; const { - isRpcFailoverEnabled, + rpcFailoverMode, initializeController = true, ...controllerOptions } = rest; - const messenger = buildRootMessenger({ isRpcFailoverEnabled }); + const messenger = buildRootMessenger({ + rpcFailoverMode, + }); const networkControllerMessenger = buildNetworkControllerMessenger(messenger); const controller = new NetworkController({ messenger: networkControllerMessenger, diff --git a/packages/network-controller/tests/network-client/helpers.ts b/packages/network-controller/tests/network-client/helpers.ts index c5e6de192e..83bff9e147 100644 --- a/packages/network-controller/tests/network-client/helpers.ts +++ b/packages/network-controller/tests/network-client/helpers.ts @@ -16,6 +16,7 @@ import type { NetworkControllerOptions, } from '../../src/NetworkController'; import type { RpcServiceOptions } from '../../src/rpc-service/rpc-service'; +import type { RpcFailoverMode } from '../../src/selectors'; import type { NetworkClientConfiguration, Provider } from '../../src/types'; import { NetworkClientType } from '../../src/types'; import type { RootMessenger } from '../helpers'; @@ -333,7 +334,7 @@ export type MockOptions = { expectedHeaders?: Record; messenger?: RootMessenger; networkClientId?: NetworkClientId; - isRpcFailoverEnabled?: boolean; + rpcFailoverMode?: RpcFailoverMode; }; export type MockCommunications = { @@ -480,8 +481,8 @@ export async function waitForPromiseToBeFulfilledAfterRunningAllTimers( * @param options.getBlockTrackerOptions - Block tracker options factory. * @param options.messenger - The root messenger to use in tests. * @param options.networkClientId - The ID of the new network client. - * @param options.isRpcFailoverEnabled - Whether or not the RPC failover - * functionality is enabled. + * @param options.rpcFailoverMode - The RPC failover mode to apply, defaults to + * `disabled`. * @param fn - A function which will be called with an object that allows * interaction with the network client. * @returns The return value of the given function. @@ -501,7 +502,7 @@ export async function withNetworkClient( getBlockTrackerOptions = (): PollingBlockTrackerOptions => ({}), messenger = buildRootMessenger(), networkClientId = 'some-network-client-id', - isRpcFailoverEnabled = false, + rpcFailoverMode = 'disabled', }: MockOptions, fn: (client: MockNetworkClient) => Promise, ): Promise { @@ -553,7 +554,7 @@ export async function withNetworkClient( getRpcServiceOptions, getBlockTrackerOptions, messenger: networkControllerMessenger, - isRpcFailoverEnabled, + rpcFailoverMode, }); /* eslint-disable-next-line n/no-process-env */ process.env.IN_TEST = inTest; diff --git a/packages/network-controller/tests/network-client/rpc-failover.ts b/packages/network-controller/tests/network-client/rpc-failover.ts index a587b1e24f..ad4b9dd83b 100644 --- a/packages/network-controller/tests/network-client/rpc-failover.ts +++ b/packages/network-controller/tests/network-client/rpc-failover.ts @@ -86,7 +86,7 @@ export function testsForRpcFailoverBehavior({ const result = await withNetworkClient( { providerType, - isRpcFailoverEnabled: true, + rpcFailoverMode: 'enabled', failoverRpcUrls: ['https://failover.endpoint'], messenger, getRpcServiceOptions: () => ({ @@ -178,7 +178,7 @@ export function testsForRpcFailoverBehavior({ const result = await withNetworkClient( { providerType, - isRpcFailoverEnabled: true, + rpcFailoverMode: 'enabled', failoverRpcUrls: ['https://failover.endpoint'], messenger, getRpcServiceOptions: (rpcEndpointUrl) => { @@ -266,7 +266,7 @@ export function testsForRpcFailoverBehavior({ await withNetworkClient( { providerType, - isRpcFailoverEnabled: false, + rpcFailoverMode: 'disabled', failoverRpcUrls: ['https://failover.endpoint'], messenger, getRpcServiceOptions: () => ({