Skip to content

feat: add gator permissions controller #6033

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

Open
wants to merge 22 commits into
base: main
Choose a base branch
from

Conversation

V00D00-child
Copy link
Member

@V00D00-child V00D00-child commented Jun 25, 2025

Explanation

@metamask/gator-permissions-controller

Current State and Why Change is Needed

MetaMask clients currently lack a dedicated system for managing gator permissions that are stored to profile sync via the @metamask/gator-permissions-snap.

Solution and How It Works

This change introduces a new @metamask/gator-permissions-controller package that provides a comprehensive solution for managing gator permissions in MetaMask clients with gator-snap integration.

Changes That Might Not Be Obvious

  • Serialization Strategy: The controller state uses JSON serialization for storing permission data fetched from @metamask/gator-permissions-snap, which allows for efficient storage and retrieval while maintaining data integrity. The deserialize permission data is represented as a list of gator permissions filtered by permission type and chainId.
  • Default Permission Structure: The controller initializes with an empty structure for all three permission types, ensuring consistent state even when no permissions are configured

Package Dependencies and Integration

The new package depends on @metamask/snaps-controllers as a peer dependency, ensuring it can leverage sending RPC requests to an installed Metamask Snap. This integration allows the GatorPermissionsController to forward requests to @metamask/gator-permissions-snap to fetch users' Gator permissions that have been stored in the MetaMask Profile Sync service.

The @metamask/gator-permissions-snap will take on the responsibility of authenticating with MetaMask Profile Sync service using anSRP identifier via integration with @metamask/message-signing-snap.

No Dependency Upgrades Required

This is a new package that introduces new functionality without requiring changes to existing dependencies. The package uses the current stable versions of @metamask/base-controller, @metamask/utils ,@metamask/snaps-sdk, and @metamask/snaps-utils following the established patterns in the MetaMask codebase.

References

Related to(MM snap-7715-permissions): Persisting Granted Permissions with MM Profile Sync
Requires(MM snap-7715-permissions):Add new permissionsProvider_getGrantedPermissions RPC
Required by(MM Extension): MetaMask/metamask-extension#33996

Gator Permissions Data Flow

graph TD
    %% dApp flow for storing permissions
    A[dApp<br/>client side RPC] -->|RPC| GPS[gator-permissions-snap]
    C -->|WRITE| D[(permissions stored<br/>across all sites)]
    
    %% User flow for reading permissions
    E[user<br/>permissions page] -->|UI| F[MM client]
    F -->|submitRequestToBackground| G[GatorPermissionsController]
    G --> MSYS[messagingSystem]
    MSYS -->|handleRequest| SC[SnapController]
    SC -->|RPC| GPS
    C -->|READ| D

    %% SRP Auth
    GPS -->|OAuth 2.0 Auth| MS[message-signing-snap]
    MS -->|SRP identifier & signature| C[profile sync service]
    
    %% Styling
    classDef dappStyle fill:#e1f5fe,stroke:#01579b,stroke-width:2px
    classDef userStyle fill:#f3e5f5,stroke:#4a148c,stroke-width:2px
    classDef serviceStyle fill:#e8f5e8,stroke:#1b5e20,stroke-width:2px
    classDef dataStyle fill:#fff3e0,stroke:#e65100,stroke-width:2px
    classDef authStyle fill:#ffebee,stroke:#c62828,stroke-width:2px
    classDef systemStyle fill:#fce4ec,stroke:#ad1457,stroke-width:2px
    
    class A dappStyle
    class E,F userStyle
    class GPS,C serviceStyle
    class D dataStyle
    class MS authStyle
    class G,MSYS,SC systemStyle
Loading

Checklist

  • I've updated the test suite for new or updated code as appropriate
  • I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate
  • I've communicated my changes to consumers by updating changelogs for packages I've changed, highlighting breaking changes as necessary
  • I've prepared draft pull requests for clients and consumer packages to resolve any breaking changes

Copy link
Contributor

@jeffsmale90 jeffsmale90 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Had a high level glance through and this looks good 👍

Just one question regarding the long-term scope of this controller.

@V00D00-child V00D00-child changed the title Add GatorPermissionsController package feat: add gator permissions controller Jun 26, 2025
@V00D00-child V00D00-child added the team-delegation MetaMask Delegation Team label Jun 26, 2025
@V00D00-child V00D00-child force-pushed the feat/add-gator-permissions-controller branch from cbf1238 to 22ab42d Compare June 30, 2025 15:56
@V00D00-child V00D00-child marked this pull request as ready for review June 30, 2025 16:23
@V00D00-child V00D00-child requested review from a team as code owners June 30, 2025 16:23
@V00D00-child V00D00-child requested a review from jeffsmale90 June 30, 2025 16:24
cursor[bot]

This comment was marked as outdated.

mathieuartu
mathieuartu previously approved these changes Jul 4, 2025
Copy link
Contributor

@mathieuartu mathieuartu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Owned files LGTM! Nice usage of our controllers, I'm very happy to see this.

cursor[bot]

This comment was marked as outdated.

cursor[bot]

This comment was marked as outdated.

@V00D00-child V00D00-child requested a review from a team as a code owner July 10, 2025 16:11
cursor[bot]

This comment was marked as outdated.

cursor[bot]

This comment was marked as outdated.

cursor[bot]

This comment was marked as outdated.

cursor[bot]

This comment was marked as outdated.

const defaultGatorPermissionsList: GatorPermissionsList = {
'native-token-stream': {},
'native-token-periodic': {},
'erc20-token-stream': {},
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is erc20-token-periodic missing here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, need to add that type. Started building this controller before type was supported in gator-snap.

Erc20TokenStreamPermission
>,
);
break;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

erc20-token-periodic missing here as well and at other instances where needed

break;
default:
throw new Error(
`Unsupported permission type: ${permissionType as string}`,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will we always have to update the controller for a new permission type? I believe the plan is also to support developer creating custom permissions which will have type 'custom' and will then need to provide a name. I think it still makes sense to save those permissions and display to the user."

Tagging @jeffsmale90 who should have more info on this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Permission types defined by a third party would likely not be shown here.

It does feel like a reasonably large amount of overhead for surfacing permission types here for revocation.

A couple thoughts:

  • can we generalise this logic so that we're not having to duplicate it for every type?
  • do we want to have a case for permission types that don't fall into any of the "known" categories? It feels like there's a possibility that a user could grant a permission that the extension doesn't know how to format - we probably don't want to fail, or hide that permission. Maybe group them into "Other" permission types?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We will likely need to update this controller for any new permission type that we support in gator-snap to keep this controller in sync.

can we generalise this logic so that we're not having to duplicate it for every type?

Yeah, I can take a look at that.

do we want to have a case for permission types that don't fall into any of the "known" categories? It feels like there's a possibility that a user could grant a permission that the extension doesn't know how to format - we probably don't want to fail, or hide that permission. Maybe group them into "Other" permission types?

I intentionally left that out, but I can update the default statement to serve as a catch-all for all custom types. The key in GatorPermissionsList for the see custom permissions type can be "other" or "custom and will have a known type.

/**
* Boolean value that allows DApp to define whether the permission can be attenuated–adjusted to meet the user’s terms.
*/
isAdjustmentAllowed: boolean;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

possibly a parameter we don't need to save

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We stored the entire permission response, so if we want to remove this data, we will need to make the change on gator-snap first before making that change in this controller.

@github-project-automation github-project-automation bot moved this from Needs dev review to Needs more work from the author in PR review queue Jul 18, 2025
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: ERC20 Token Missing Periodic Permission Type

The Erc20TokenPeriodicPermission type definition is missing and not included in the PermissionTypes union. Consequently, the GatorPermissionsList type also lacks the 'erc20-token-periodic' key. This creates an inconsistency where native tokens support both stream and periodic permissions, but ERC20 tokens only support stream.

packages/gator-permissions-controller/src/types.ts#L56-L61

*/
export type PermissionTypes =
| NativeTokenStreamPermission
| NativeTokenPeriodicPermission
| Erc20TokenStreamPermission;

packages/gator-permissions-controller/src/types.ts#L178-L198

*/
export type GatorPermissionsList = {
'native-token-stream': {
[chainId: Hex]: StoredGatorPermission<
SignerParam,
NativeTokenStreamPermission
>[];
};
'native-token-periodic': {
[chainId: Hex]: StoredGatorPermission<
SignerParam,
NativeTokenPeriodicPermission
>[];
};
'erc20-token-stream': {
[chainId: Hex]: StoredGatorPermission<
SignerParam,
Erc20TokenStreamPermission
>[];
};
};

Fix in CursorFix in Web


Bug: Missing Permission Type Handling

The #categorizePermissionsDataByTypeAndChainId method in GatorPermissionsController.ts is missing a case for the 'erc20-token-periodic' permission type in its switch statement. This causes permissions of this type to fall into the default case, resulting in an "Unsupported permission type" error.

packages/gator-permissions-controller/src/GatorPermissionsController.ts#L334-L373

switch (permissionType) {
case 'native-token-stream':
if (!gatorPermissionsList['native-token-stream'][chainId]) {
gatorPermissionsList['native-token-stream'][chainId] = [];
}
gatorPermissionsList['native-token-stream'][chainId].push(
storedGatorPermission as StoredGatorPermission<
SignerParam,
NativeTokenStreamPermission
>,
);
break;
case 'native-token-periodic':
if (!gatorPermissionsList['native-token-periodic'][chainId]) {
gatorPermissionsList['native-token-periodic'][chainId] = [];
}
gatorPermissionsList['native-token-periodic'][chainId].push(
storedGatorPermission as StoredGatorPermission<
SignerParam,
NativeTokenPeriodicPermission
>,
);
break;
case 'erc20-token-stream':
if (!gatorPermissionsList['erc20-token-stream'][chainId]) {
gatorPermissionsList['erc20-token-stream'][chainId] = [];
}
gatorPermissionsList['erc20-token-stream'][chainId].push(
storedGatorPermission as StoredGatorPermission<
SignerParam,
Erc20TokenStreamPermission
>,
);
break;
default:
throw new Error(
`Unsupported permission type: ${permissionType as string}`,
);
}

Fix in CursorFix in Web


Was this report helpful? Give feedback by reacting with 👍 or 👎

};

/**
* Represents a list of gator permissions filtered by permission type.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* Represents a list of gator permissions filtered by permission type.
* Represents a list of gator permissions grouped by permission type.

break;
default:
throw new Error(
`Unsupported permission type: ${permissionType as string}`,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Permission types defined by a third party would likely not be shown here.

It does feel like a reasonably large amount of overhead for surfacing permission types here for revocation.

A couple thoughts:

  • can we generalise this logic so that we're not having to duplicate it for every type?
  • do we want to have a case for permission types that don't fall into any of the "known" categories? It feels like there's a possibility that a user could grant a permission that the extension doesn't know how to format - we probably don't want to fail, or hide that permission. Maybe group them into "Other" permission types?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
team-delegation MetaMask Delegation Team
Projects
Status: Needs more work from the author
Development

Successfully merging this pull request may close these issues.

4 participants