diff --git a/packages/gator-permissions-controller/CHANGELOG.md b/packages/gator-permissions-controller/CHANGELOG.md index dcee094dd3a..95a8d927fde 100644 --- a/packages/gator-permissions-controller/CHANGELOG.md +++ b/packages/gator-permissions-controller/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- Extends the `GatorPermissionsController` to enable attaching metadata(transaction hash, block timestamp) when submitting a permission revocation. ([#7503](https://github.com/MetaMask/core/pull/7503)) + ### Changed - Bump `@metamask/transaction-controller` from `^62.5.0` to `^62.7.0` ([#7430](https://github.com/MetaMask/core/pull/7430), [#7494](https://github.com/MetaMask/core/pull/7494)) diff --git a/packages/gator-permissions-controller/src/GatorPermissionsController.test.ts b/packages/gator-permissions-controller/src/GatorPermissionsController.test.ts index a46ca7879c7..02e8ed02089 100644 --- a/packages/gator-permissions-controller/src/GatorPermissionsController.test.ts +++ b/packages/gator-permissions-controller/src/GatorPermissionsController.test.ts @@ -1235,6 +1235,71 @@ describe('GatorPermissionsController', () => { }); }); + it('should submit revocation metadata when transaction is confirmed', async () => { + const mockHandleRequestHandler = jest.fn().mockResolvedValue(undefined); + const rootMessenger = getRootMessenger({ + snapControllerHandleRequestActionHandler: mockHandleRequestHandler, + }); + const messenger = getMessenger(rootMessenger); + + const controller = new GatorPermissionsController({ + messenger, + state: { + isGatorPermissionsEnabled: true, + gatorPermissionsProviderSnapId: + MOCK_GATOR_PERMISSIONS_PROVIDER_SNAP_ID, + }, + }); + + const txId = 'test-tx-id'; + const permissionContext = '0x1234567890abcdef1234567890abcdef12345678'; + const hash = '0x-mock-hash'; + + await controller.addPendingRevocation({ txId, permissionContext }); + + // Emit transaction approved event (user confirms) + rootMessenger.publish('TransactionController:transactionApproved', { + transactionMeta: { id: txId } as TransactionMeta, + }); + + // Emit transaction confirmed event + rootMessenger.publish('TransactionController:transactionConfirmed', { + id: txId, + hash, + } as TransactionMeta); + + await flushPromises(); + + // Verify submitRevocation was called + expect(mockHandleRequestHandler).toHaveBeenCalledWith({ + snapId: MOCK_GATOR_PERMISSIONS_PROVIDER_SNAP_ID, + origin: 'metamask', + handler: 'onRpcRequest', + request: { + jsonrpc: '2.0', + method: 'permissionsProvider_submitRevocation', + params: { + permissionContext, + revocationMetadata: { + txHash: hash, + }, + }, + }, + }); + + // Verify that permissions are refreshed after revocation (getGrantedPermissions is called) + expect(mockHandleRequestHandler).toHaveBeenCalledWith({ + snapId: MOCK_GATOR_PERMISSIONS_PROVIDER_SNAP_ID, + origin: 'metamask', + handler: 'onRpcRequest', + request: { + jsonrpc: '2.0', + method: 'permissionsProvider_getGrantedPermissions', + params: { isRevoked: false }, + }, + }); + }); + it('should cleanup without adding to state when transaction is rejected by user', async () => { const mockHandleRequestHandler = jest.fn().mockResolvedValue(undefined); const rootMessenger = getRootMessenger({ diff --git a/packages/gator-permissions-controller/src/GatorPermissionsController.ts b/packages/gator-permissions-controller/src/GatorPermissionsController.ts index de0756c849d..76a94f41e2f 100644 --- a/packages/gator-permissions-controller/src/GatorPermissionsController.ts +++ b/packages/gator-permissions-controller/src/GatorPermissionsController.ts @@ -35,7 +35,10 @@ import { } from './errors'; import { controllerLog } from './logger'; import { GatorPermissionsSnapRpcMethod } from './types'; -import type { StoredGatorPermissionSanitized } from './types'; +import type { + RevocationMetadata, + StoredGatorPermissionSanitized, +} from './types'; import type { GatorPermissionsMap, PermissionTypesWithCustom, @@ -948,9 +951,33 @@ export default class GatorPermissionsController extends BaseController< controllerLog('Transaction confirmed, submitting revocation', { txId, permissionContext, + txHash: transactionMeta.hash, }); - this.submitRevocation({ permissionContext }) + // Attach metadata by parsing the confirmed transactionMeta + let revocationMetadata: RevocationMetadata | undefined; + const { hash } = transactionMeta; + if (hash === undefined) { + controllerLog( + 'Failed to attach transaction hash after revocation transaction confirmed', + { + txId, + permissionContext, + error: new Error( + 'Confirmed transaction is missing transaction hash', + ), + }, + ); + } else { + revocationMetadata = { + txHash: hash as Hex, + }; + } + + const revocationParams = revocationMetadata + ? { permissionContext, revocationMetadata } + : { permissionContext }; + this.submitRevocation(revocationParams) .catch((error) => { controllerLog( 'Failed to submit revocation after transaction confirmed', diff --git a/packages/gator-permissions-controller/src/types.ts b/packages/gator-permissions-controller/src/types.ts index b1f57419e26..f71f67cd033 100644 --- a/packages/gator-permissions-controller/src/types.ts +++ b/packages/gator-permissions-controller/src/types.ts @@ -237,6 +237,13 @@ export type DelegationDetails = Pick< 'caveats' | 'delegator' | 'delegate' | 'authority' >; +/** + * Represents the metadata for confirmed transaction revocation. + */ +export type RevocationMetadata = { + txHash: Hex; +}; + /** * Represents the parameters for submitting a revocation. */ @@ -245,6 +252,11 @@ export type RevocationParams = { * The permission context as a hex string that identifies the permission to revoke. */ permissionContext: Hex; + + /** + * The metadata associated with the permission revocation transaction. + */ + revocationMetadata?: RevocationMetadata; }; /**