-
-
Notifications
You must be signed in to change notification settings - Fork 255
Description
References
- Follow-up from Fix
getRestrictedmethod: aligns runtime and type-level handling of omitted or empty inputs #4013
Description
The issue is observed when calling the ControllerMessenger class method getRestricted.
Repro steps
- The allowlists are omitted in both the
getRestrictedgeneric and function params. - The returned restricted controller-messenger is passed in directly as the
messengeroption in the controller constructor.
Following these steps prevents the Allowed{Action,Event} generic defaults (never) from being applied. Allowed{Action,Event} are inferred as their generic constraints instead (NotNamespacedBy<...>).
Currently known fixes
- Explicitly passing in the allowlists either as generic or function params.
- [unexpected behavior] Assigning the restricted messenger to a variable first before passing it into the controller constructor.
Objective
- Find a fix that is internal to
getRestrictedorRestrictedControllerMessenger, and doesn't require the consumer to take actions at the call site. - As a fallback option, add an entry to changelog that describes this issue and conveys in detail the steps consumers must take to use
getRestrictedwithout experiencing unexpected behavior.
Details
Error
Example A
Error: Type 'RestrictedControllerMessenger<"NetworkController", NetworkControllerGetStateAction | NetworkControllerGetProviderConfigAction | ... 19 more ... | TransactionControllerGetStateAction, NetworkControllerStateChangeEvent | ... 20 more ... | TransactionControllerUnapprovedTransactionAddedEvent, "ApprovalController:getSta...' is not assignable to type 'NetworkControllerMessenger'.
Types of property '#allowedActions' are incompatible.
Type '("ApprovalController:getState" | "ApprovalController:clearRequests" | "ApprovalController:addRequest" | "ApprovalController:hasRequest" | "ApprovalController:acceptRequest" | ... 7 more ... | "TransactionController:getState")[]' is not assignable to type 'never[]'.
Type 'string' is not assignable to type 'never'.ts(2322)
NotNamespacedBy,NarrowToAllowedare working correctly.- There doesn't seem to be anything wrong with the generic params of
getRestrictedorRestrictedControllerMessengerconstructor.
const unrestrictedMessenger: UnrestrictedControllerMessenger =
new ControllerMessenger();
const networkController = new NetworkController({
messenger: unrestrictedMessenger.getRestricted({
name: 'NetworkController',
}),Example B
No error, but full allowlist is inferred despite being omitted from generic + function arguments.
In this case, the code is relying on this bug to implement a SelectedNetworkControllerMessenger while conveniently sidestepping the need to supply allowlists.
This is dangerous behavior (especially at runtime) that should not be possible. The code should be breaking from the lack of explicitly supplied function-param allowlists.
- From
SelectedNetworkMiddleware.test.ts:
🚫 (method) ControllerMessenger<SelectedNetworkControllerActions | AllowedActions, SelectedNetworkControllerStateChangeEvent | AllowedEvents>.getRestricted<"SelectedNetworkController", "NetworkController:getNetworkClientById" | "NetworkController:getState" | "PermissionController:hasPermissions" | "PermissionController:getSubjectNames", "NetworkController:stateChange" | "PermissionController:stateChange">({ name, allowedActions, allowedEvents, }: {
...;
}): RestrictedControllerMessenger<...>
const buildMessenger = () => {
return new ControllerMessenger<
SelectedNetworkControllerActions | AllowedActions,
SelectedNetworkControllerEvents | AllowedEvents
>();
};
...
const messenger = buildMessenger();
const middleware = createSelectedNetworkMiddleware(
messenger.getRestricted({
name: 'SelectedNetworkController',
}),
);✅ const restrictedMessenger: RestrictedControllerMessenger<"SelectedNetworkController", SelectedNetworkControllerGetSelectedNetworkStateAction | SelectedNetworkControllerGetNetworkClientIdForDomainAction | SelectedNetworkControllerSetNetworkClientIdForDomainAction, SelectedNetworkControllerStateChangeEvent, never, never>
...
const restrictedMessenger = messenger.getRestricted({
name: 'SelectedNetworkController',
});
const middleware = createSelectedNetworkMiddleware(restrictedMessenger);Example C
Same behavior as Example B once this diff is applied:
diff --git a/packages/gas-fee-controller/src/GasFeeController.test.ts b/packages/gas-fee-controller/src/GasFeeController.test.ts
index d198f4b98..62a692751 100644
--- a/packages/gas-fee-controller/src/GasFeeController.test.ts
+++ b/packages/gas-fee-controller/src/GasFeeController.test.ts
@@ -66,14 +66,10 @@ const setupNetworkController = async ({
state: Partial<NetworkState>;
clock: sinon.SinonFakeTimers;
}) => {
- const restrictedMessenger = unrestrictedMessenger.getRestricted({
- name: 'NetworkController',
- allowedActions: [],
- allowedEvents: [],
- });
-
const networkController = new NetworkController({
- messenger: restrictedMessenger,
+ messenger: unrestrictedMessenger.getRestricted({
+ name: 'NetworkController',
+ }),
state,
infuraProjectId: '123',
trackMetaMetricsEvent: jest.fn(),Fixes
- Explicitly pass
neveras generic params
const unrestrictedMessenger: UnrestrictedControllerMessenger =
new ControllerMessenger();
- const networkControllerMessenger = unrestrictedMessenger.getRestricted({
- name: 'NetworkController',
- });
const networkController = new NetworkController({
- messenger: networkControllerMessenger,
+ messenger: unrestrictedMessenger.getRestricted<
+ 'NetworkController',
+ never,
+ never
+ >({
+ name: 'NetworkController',
+ }),- Explicitly pass empty array as function params
const unrestrictedMessenger: UnrestrictedControllerMessenger =
new ControllerMessenger();
- const networkControllerMessenger = unrestrictedMessenger.getRestricted({
- name: 'NetworkController',
- });
const networkController = new NetworkController({
- messenger: networkControllerMessenger,
+ messenger: unrestrictedMessenger.getRestricted({
+ name: 'NetworkController',
+ allowedActions: [],
+ allowedEvents: [],
+ }),- Assign restricted messenger to its own variable first before passing into controller constructor
const unrestrictedMessenger: UnrestrictedControllerMessenger =
new ControllerMessenger();
+ const networkControllerMessenger = unrestrictedMessenger.getRestricted({
+ name: 'NetworkController',
+ });
const networkController = new NetworkController({
- messenger: unrestrictedMessenger.getRestricted({
- name: 'NetworkController',
- }),
+ messenger: networkControllerMessenger,