diff --git a/packages/gator-permissions-controller/CHANGELOG.md b/packages/gator-permissions-controller/CHANGELOG.md index dcee094dd3a..a34a07a58f1 100644 --- a/packages/gator-permissions-controller/CHANGELOG.md +++ b/packages/gator-permissions-controller/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- **BREAKING:** Extends the `GatorPermissionsController` to enable attaching metadata(transaction hash) when submitting a permission revocation. ([#7503](https://github.com/MetaMask/core/pull/7503)) + - `submitRevocation` revocationParams object now requires a `revocationMetadata` property. + ### 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..5c1dada3fe3 100644 --- a/packages/gator-permissions-controller/src/GatorPermissionsController.test.ts +++ b/packages/gator-permissions-controller/src/GatorPermissionsController.test.ts @@ -847,6 +847,7 @@ describe('GatorPermissionsController', () => { const revocationParams: RevocationParams = { permissionContext: '0x1234567890abcdef1234567890abcdef12345678', + revocationMetadata: {}, }; await controller.submitRevocation(revocationParams); @@ -875,6 +876,7 @@ describe('GatorPermissionsController', () => { const revocationParams: RevocationParams = { permissionContext: '0x1234567890abcdef1234567890abcdef12345678', + revocationMetadata: {}, }; await expect( @@ -903,6 +905,7 @@ describe('GatorPermissionsController', () => { const revocationParams: RevocationParams = { permissionContext: '0x1234567890abcdef1234567890abcdef12345678', + revocationMetadata: {}, }; await expect( @@ -947,6 +950,7 @@ describe('GatorPermissionsController', () => { const revocationParams: RevocationParams = { permissionContext: '0x1234567890abcdef1234567890abcdef12345678', + revocationMetadata: {}, }; // Should throw GatorPermissionsFetchError (not GatorPermissionsProviderError) @@ -987,6 +991,7 @@ describe('GatorPermissionsController', () => { const revocationParams: RevocationParams = { permissionContext: '0x1234567890abcdef1234567890abcdef12345678', + revocationMetadata: {}, }; await controller.submitDirectRevocation(revocationParams); @@ -1028,6 +1033,7 @@ describe('GatorPermissionsController', () => { '0x1234567890abcdef1234567890abcdef12345678' as Hex; const revocationParams: RevocationParams = { permissionContext, + revocationMetadata: {}, }; // Spy on submitRevocation to check pending state before it's called @@ -1052,6 +1058,7 @@ describe('GatorPermissionsController', () => { const revocationParams: RevocationParams = { permissionContext: '0x1234567890abcdef1234567890abcdef12345678', + revocationMetadata: {}, }; await expect( @@ -1082,6 +1089,7 @@ describe('GatorPermissionsController', () => { '0x1234567890abcdef1234567890abcdef12345678' as Hex; const revocationParams: RevocationParams = { permissionContext, + revocationMetadata: {}, }; await expect( @@ -1218,7 +1226,72 @@ describe('GatorPermissionsController', () => { request: { jsonrpc: '2.0', method: 'permissionsProvider_submitRevocation', - params: { permissionContext }, + params: { permissionContext, revocationMetadata: {} }, + }, + }); + + // 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 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, + }, + }, }, }); diff --git a/packages/gator-permissions-controller/src/GatorPermissionsController.ts b/packages/gator-permissions-controller/src/GatorPermissionsController.ts index de0756c849d..36cd4abbaca 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,28 @@ 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 + const revocationMetadata: RevocationMetadata = {}; + 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; + } + + this.submitRevocation({ permissionContext, revocationMetadata }) .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..b7ee600a87f 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 | undefined; +}; + /** * 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; }; /**