Skip to content

[WIP] Add way to customize block tracker + Add flag for RPC failover #5679

New issue

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

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

Already on GitHub? Sign in to your account

Draft
wants to merge 20 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
12 changes: 12 additions & 0 deletions packages/network-controller/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- Add optional `getBlockTrackerOptions` argument to NetworkController constructor ([#5679](https://github.com/MetaMask/core/pull/5679))
- Add optional `rpcFailoverEnabled` option to NetworkController constructor (`false` by default) ([#5679](https://github.com/MetaMask/core/pull/5679))
- Add `enableRpcFailover` and `disableRpcFailover` methods to NetworkController ([#5679](https://github.com/MetaMask/core/pull/5679))

### Changed

- Disable the RPC failover behavior by default ([#5679](https://github.com/MetaMask/core/pull/5679))
- You are free to set the `failoverUrls` property on an RPC endpoint, but it won't have any effect
- To enable this behavior, either pass `rpcFailoverEnabled: true` to the constructor or call `enableRpcFailover` after initialization

## [23.2.0]

### Added
Expand Down
128 changes: 113 additions & 15 deletions packages/network-controller/src/NetworkController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
BUILT_IN_NETWORKS,
BuiltInNetworkName,
} from '@metamask/controller-utils';
import type { PollingBlockTrackerOptions } from '@metamask/eth-block-tracker';
import EthQuery from '@metamask/eth-query';
import { errorCodes } from '@metamask/rpc-errors';
import { createEventEmitterProxy } from '@metamask/swappable-obj-proxy';
Expand Down Expand Up @@ -621,20 +622,32 @@ export type NetworkControllerOptions = {
*/
log?: Logger;
/**
* A function that can be used to customize the options passed to a
* RPC service constructed for an RPC endpoint. The object that the function
* should return is the same as {@link RpcServiceOptions}, except that
* `failoverService` and `endpointUrl` are not accepted (as they are filled in
* automatically).
* A function that can be used to customize a RPC service constructed for an
* RPC endpoint. The function takes the URL of the endpoint and should return
* an object with type {@link RpcServiceOptions}, minus `failoverService`
* and `endpointUrl` (as they are filled in automatically).
*/
getRpcServiceOptions: (
rpcEndpointUrl: string,
) => Omit<RpcServiceOptions, 'failoverService' | 'endpointUrl'>;

/**
* A function that can be used to customize a block tracker constructed for an
* RPC endpoint. The function takes the URL of the endpoint and should return
* an object of type {@link PollingBlockTrackerOptions}, minus `provider` (as
* it is filled in automatically).
*/
getBlockTrackerOptions?: (
rpcEndpointUrl: string,
) => Omit<PollingBlockTrackerOptions, 'provider'>;
/**
* An array of Hex Chain IDs representing the additional networks to be included as default.
*/
additionalDefaultNetworks?: AdditionalDefaultNetwork[];
/**
* Whether or not requests sent to unavailable RPC endpoints should be
* automatically diverted to configured failover RPC endpoints.
*/
isRpcFailoverEnabled?: boolean;
};

/**
Expand Down Expand Up @@ -1080,11 +1093,18 @@ export class NetworkController extends BaseController<

readonly #getRpcServiceOptions: NetworkControllerOptions['getRpcServiceOptions'];

readonly #getBlockTrackerOptions: NetworkControllerOptions['getBlockTrackerOptions'];

#networkConfigurationsByNetworkClientId: Map<
NetworkClientId,
NetworkConfiguration
>;

#isRpcFailoverEnabled: Exclude<
NetworkControllerOptions['isRpcFailoverEnabled'],
undefined
>;

/**
* Constructs a NetworkController.
*
Expand All @@ -1097,7 +1117,9 @@ export class NetworkController extends BaseController<
infuraProjectId,
log,
getRpcServiceOptions,
getBlockTrackerOptions,
additionalDefaultNetworks,
isRpcFailoverEnabled = false,
} = options;
const initialState = {
...getDefaultNetworkControllerState(additionalDefaultNetworks),
Expand Down Expand Up @@ -1131,6 +1153,8 @@ export class NetworkController extends BaseController<
this.#infuraProjectId = infuraProjectId;
this.#log = log;
this.#getRpcServiceOptions = getRpcServiceOptions;
this.#getBlockTrackerOptions = getBlockTrackerOptions;
this.#isRpcFailoverEnabled = isRpcFailoverEnabled;

this.#previouslySelectedNetworkClientId =
this.state.selectedNetworkClientId;
Expand Down Expand Up @@ -1229,6 +1253,65 @@ 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() {
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() {
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.
*
* @param newIsRpcFailoverEnabled - Whether or not to enable or disable the
* RPC failover functionality.
*/
#updateRpcFailoverEnabled(newIsRpcFailoverEnabled: boolean) {
if (this.#isRpcFailoverEnabled === newIsRpcFailoverEnabled) {
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
) {
newIsRpcFailoverEnabled
? networkClient.enableRpcFailover()
: networkClient.disableRpcFailover();
}
}
}

this.#isRpcFailoverEnabled = newIsRpcFailoverEnabled;
}

/**
* Accesses the provider and block tracker for the currently selected network.
* @returns The proxy and block tracker proxies.
Expand Down Expand Up @@ -2638,7 +2721,9 @@ export class NetworkController extends BaseController<
ticker: networkFields.nativeCurrency,
},
getRpcServiceOptions: this.#getRpcServiceOptions,
getBlockTrackerOptions: this.#getBlockTrackerOptions,
messenger: this.messagingSystem,
isRpcFailoverEnabled: this.#isRpcFailoverEnabled,
});
} else {
autoManagedNetworkClientRegistry[NetworkClientType.Custom][
Expand All @@ -2652,7 +2737,9 @@ export class NetworkController extends BaseController<
ticker: networkFields.nativeCurrency,
},
getRpcServiceOptions: this.#getRpcServiceOptions,
getBlockTrackerOptions: this.#getBlockTrackerOptions,
messenger: this.messagingSystem,
isRpcFailoverEnabled: this.#isRpcFailoverEnabled,
});
}
}
Expand Down Expand Up @@ -2812,7 +2899,9 @@ export class NetworkController extends BaseController<
ticker: networkConfiguration.nativeCurrency,
},
getRpcServiceOptions: this.#getRpcServiceOptions,
getBlockTrackerOptions: this.#getBlockTrackerOptions,
messenger: this.messagingSystem,
isRpcFailoverEnabled: this.#isRpcFailoverEnabled,
}),
] as const;
}
Expand All @@ -2827,7 +2916,9 @@ export class NetworkController extends BaseController<
ticker: networkConfiguration.nativeCurrency,
},
getRpcServiceOptions: this.#getRpcServiceOptions,
getBlockTrackerOptions: this.#getBlockTrackerOptions,
messenger: this.messagingSystem,
isRpcFailoverEnabled: this.#isRpcFailoverEnabled,
}),
] as const;
});
Expand Down Expand Up @@ -2930,25 +3021,32 @@ export class NetworkController extends BaseController<
updateState?.(state);
});

const { providerProxy } = this.#setProxies(this.#autoManagedNetworkClient);

this.#ethQuery = new EthQuery(providerProxy);
}

#setProxies(
networkClient: AutoManagedNetworkClient<NetworkClientConfiguration>,
) {
if (this.#providerProxy) {
this.#providerProxy.setTarget(this.#autoManagedNetworkClient.provider);
this.#providerProxy.setTarget(networkClient.provider);
} else {
this.#providerProxy = createEventEmitterProxy(
this.#autoManagedNetworkClient.provider,
);
this.#providerProxy = createEventEmitterProxy(networkClient.provider);
}

if (this.#blockTrackerProxy) {
this.#blockTrackerProxy.setTarget(
this.#autoManagedNetworkClient.blockTracker,
);
this.#blockTrackerProxy.setTarget(networkClient.blockTracker);
} else {
this.#blockTrackerProxy = createEventEmitterProxy(
this.#autoManagedNetworkClient.blockTracker,
networkClient.blockTracker,
{ eventFilter: 'skipInternal' },
);
}

this.#ethQuery = new EthQuery(this.#providerProxy);
return {
providerProxy: this.#providerProxy,
blockTrackerProxy: this.#blockTrackerProxy,
};
}
}
Loading
Loading