@@ -279,6 +279,32 @@ describe('GatorPermissionsController', () => {
279279 } ) ;
280280 } ) ;
281281
282+ it ( 'fetches gator permissions with optional params' , async ( ) => {
283+ const mockHandleRequestHandler = jest
284+ . fn ( )
285+ . mockResolvedValue ( MOCK_GATOR_PERMISSIONS_STORAGE_ENTRIES ) ;
286+ const rootMessenger = getRootMessenger ( {
287+ snapControllerHandleRequestActionHandler : mockHandleRequestHandler ,
288+ } ) ;
289+
290+ const controller = new GatorPermissionsController ( {
291+ messenger : getMessenger ( rootMessenger ) ,
292+ } ) ;
293+
294+ await controller . enableGatorPermissions ( ) ;
295+
296+ const params = { origin : 'https://example.com' , chainId : '0x1' } ;
297+ await controller . fetchAndUpdateGatorPermissions ( params ) ;
298+
299+ expect ( mockHandleRequestHandler ) . toHaveBeenCalledWith (
300+ expect . objectContaining ( {
301+ request : expect . objectContaining ( {
302+ params,
303+ } ) ,
304+ } ) ,
305+ ) ;
306+ } ) ;
307+
282308 it ( 'handles error during fetch and update' , async ( ) => {
283309 const rootMessenger = getRootMessenger ( {
284310 snapControllerHandleRequestActionHandler : async ( ) => {
@@ -774,6 +800,240 @@ describe('GatorPermissionsController', () => {
774800 ) ;
775801 } ) ;
776802 } ) ;
803+
804+ describe ( 'addPendingRevocation' , ( ) => {
805+ beforeEach ( ( ) => {
806+ jest . useFakeTimers ( ) ;
807+ } ) ;
808+
809+ afterEach ( ( ) => {
810+ jest . useRealTimers ( ) ;
811+ } ) ;
812+
813+ it ( 'should submit revocation when transaction is confirmed' , async ( ) => {
814+ const mockHandleRequestHandler = jest . fn ( ) . mockResolvedValue ( undefined ) ;
815+ const rootMessenger = getRootMessenger ( {
816+ snapControllerHandleRequestActionHandler : mockHandleRequestHandler ,
817+ } ) ;
818+ const messenger = getMessenger ( rootMessenger ) ;
819+
820+ const controller = new GatorPermissionsController ( {
821+ messenger,
822+ state : {
823+ isGatorPermissionsEnabled : true ,
824+ gatorPermissionsProviderSnapId :
825+ MOCK_GATOR_PERMISSIONS_PROVIDER_SNAP_ID ,
826+ } ,
827+ } ) ;
828+
829+ const txId = 'test-tx-id' ;
830+ const permissionContext = '0x1234567890abcdef1234567890abcdef12345678' ;
831+
832+ await controller . addPendingRevocation ( txId , permissionContext ) ;
833+
834+ // Emit transaction confirmed event
835+ rootMessenger . publish ( 'TransactionController:transactionConfirmed' , {
836+ id : txId ,
837+ } ) ;
838+
839+ // Wait for async operations
840+ await Promise . resolve ( ) ;
841+
842+ expect ( mockHandleRequestHandler ) . toHaveBeenCalledWith ( {
843+ snapId : MOCK_GATOR_PERMISSIONS_PROVIDER_SNAP_ID ,
844+ origin : 'metamask' ,
845+ handler : 'onRpcRequest' ,
846+ request : {
847+ jsonrpc : '2.0' ,
848+ method : 'permissionsProvider_submitRevocation' ,
849+ params : { permissionContext } ,
850+ } ,
851+ } ) ;
852+ } ) ;
853+
854+ it ( 'should cleanup without submitting revocation when transaction fails' , async ( ) => {
855+ const mockHandleRequestHandler = jest . fn ( ) . mockResolvedValue ( undefined ) ;
856+ const rootMessenger = getRootMessenger ( {
857+ snapControllerHandleRequestActionHandler : mockHandleRequestHandler ,
858+ } ) ;
859+ const messenger = getMessenger ( rootMessenger ) ;
860+
861+ const controller = new GatorPermissionsController ( {
862+ messenger,
863+ state : {
864+ isGatorPermissionsEnabled : true ,
865+ gatorPermissionsProviderSnapId :
866+ MOCK_GATOR_PERMISSIONS_PROVIDER_SNAP_ID ,
867+ } ,
868+ } ) ;
869+
870+ const txId = 'test-tx-id' ;
871+ const permissionContext = '0x1234567890abcdef1234567890abcdef12345678' ;
872+
873+ await controller . addPendingRevocation ( txId , permissionContext ) ;
874+
875+ // Emit transaction failed event
876+ rootMessenger . publish ( 'TransactionController:transactionFailed' , {
877+ transactionMeta : { id : txId } ,
878+ error : 'Transaction failed' ,
879+ } ) ;
880+
881+ // Wait for async operations
882+ await Promise . resolve ( ) ;
883+
884+ // Should not call submitRevocation
885+ expect ( mockHandleRequestHandler ) . not . toHaveBeenCalled ( ) ;
886+ } ) ;
887+
888+ it ( 'should cleanup without submitting revocation when transaction is dropped' , async ( ) => {
889+ const mockHandleRequestHandler = jest . fn ( ) . mockResolvedValue ( undefined ) ;
890+ const rootMessenger = getRootMessenger ( {
891+ snapControllerHandleRequestActionHandler : mockHandleRequestHandler ,
892+ } ) ;
893+ const messenger = getMessenger ( rootMessenger ) ;
894+
895+ const controller = new GatorPermissionsController ( {
896+ messenger,
897+ state : {
898+ isGatorPermissionsEnabled : true ,
899+ gatorPermissionsProviderSnapId :
900+ MOCK_GATOR_PERMISSIONS_PROVIDER_SNAP_ID ,
901+ } ,
902+ } ) ;
903+
904+ const txId = 'test-tx-id' ;
905+ const permissionContext = '0x1234567890abcdef1234567890abcdef12345678' ;
906+
907+ await controller . addPendingRevocation ( txId , permissionContext ) ;
908+
909+ // Emit transaction dropped event
910+ rootMessenger . publish ( 'TransactionController:transactionDropped' , {
911+ id : txId ,
912+ } ) ;
913+
914+ // Wait for async operations
915+ await Promise . resolve ( ) ;
916+
917+ // Should not call submitRevocation
918+ expect ( mockHandleRequestHandler ) . not . toHaveBeenCalled ( ) ;
919+ } ) ;
920+
921+ it ( 'should cleanup without submitting revocation when timeout is reached' , async ( ) => {
922+ const mockHandleRequestHandler = jest . fn ( ) . mockResolvedValue ( undefined ) ;
923+ const rootMessenger = getRootMessenger ( {
924+ snapControllerHandleRequestActionHandler : mockHandleRequestHandler ,
925+ } ) ;
926+ const messenger = getMessenger ( rootMessenger ) ;
927+
928+ const controller = new GatorPermissionsController ( {
929+ messenger,
930+ state : {
931+ isGatorPermissionsEnabled : true ,
932+ gatorPermissionsProviderSnapId :
933+ MOCK_GATOR_PERMISSIONS_PROVIDER_SNAP_ID ,
934+ } ,
935+ } ) ;
936+
937+ const txId = 'test-tx-id' ;
938+ const permissionContext = '0x1234567890abcdef1234567890abcdef12345678' ;
939+
940+ await controller . addPendingRevocation ( txId , permissionContext ) ;
941+
942+ // Fast-forward time by 2 hours
943+ jest . advanceTimersByTime ( 2 * 60 * 60 * 1000 ) ;
944+
945+ // Wait for async operations
946+ await Promise . resolve ( ) ;
947+
948+ // Should not call submitRevocation
949+ expect ( mockHandleRequestHandler ) . not . toHaveBeenCalled ( ) ;
950+ } ) ;
951+
952+ it ( 'should not submit revocation for different transaction IDs' , async ( ) => {
953+ const mockHandleRequestHandler = jest . fn ( ) . mockResolvedValue ( undefined ) ;
954+ const rootMessenger = getRootMessenger ( {
955+ snapControllerHandleRequestActionHandler : mockHandleRequestHandler ,
956+ } ) ;
957+ const messenger = getMessenger ( rootMessenger ) ;
958+
959+ const controller = new GatorPermissionsController ( {
960+ messenger,
961+ state : {
962+ isGatorPermissionsEnabled : true ,
963+ gatorPermissionsProviderSnapId :
964+ MOCK_GATOR_PERMISSIONS_PROVIDER_SNAP_ID ,
965+ } ,
966+ } ) ;
967+
968+ const txId = 'test-tx-id' ;
969+ const permissionContext = '0x1234567890abcdef1234567890abcdef12345678' ;
970+
971+ await controller . addPendingRevocation ( txId , permissionContext ) ;
972+
973+ // Emit transaction confirmed event for different transaction
974+ rootMessenger . publish ( 'TransactionController:transactionConfirmed' , {
975+ id : 'different-tx-id' ,
976+ } ) ;
977+
978+ // Wait for async operations
979+ await Promise . resolve ( ) ;
980+
981+ // Should not call submitRevocation for different transaction
982+ expect ( mockHandleRequestHandler ) . not . toHaveBeenCalled ( ) ;
983+ } ) ;
984+
985+ it ( 'should handle revocation submission errors gracefully' , async ( ) => {
986+ const mockHandleRequestHandler = jest
987+ . fn ( )
988+ . mockRejectedValue ( new Error ( 'Revocation submission failed' ) ) ;
989+ const rootMessenger = getRootMessenger ( {
990+ snapControllerHandleRequestActionHandler : mockHandleRequestHandler ,
991+ } ) ;
992+ const messenger = getMessenger ( rootMessenger ) ;
993+
994+ const controller = new GatorPermissionsController ( {
995+ messenger,
996+ state : {
997+ isGatorPermissionsEnabled : true ,
998+ gatorPermissionsProviderSnapId :
999+ MOCK_GATOR_PERMISSIONS_PROVIDER_SNAP_ID ,
1000+ } ,
1001+ } ) ;
1002+
1003+ const txId = 'test-tx-id' ;
1004+ const permissionContext = '0x1234567890abcdef1234567890abcdef12345678' ;
1005+
1006+ await controller . addPendingRevocation ( txId , permissionContext ) ;
1007+
1008+ // Emit transaction confirmed event
1009+ rootMessenger . publish ( 'TransactionController:transactionConfirmed' , {
1010+ id : txId ,
1011+ } ) ;
1012+
1013+ // Wait for async operations
1014+ await Promise . resolve ( ) ;
1015+
1016+ // Should have attempted to call submitRevocation even though it failed
1017+ expect ( mockHandleRequestHandler ) . toHaveBeenCalled ( ) ;
1018+ } ) ;
1019+
1020+ it ( 'should throw GatorPermissionsNotEnabledError when gator permissions are disabled' , async ( ) => {
1021+ const messenger = getMessenger ( ) ;
1022+ const controller = new GatorPermissionsController ( {
1023+ messenger,
1024+ state : {
1025+ isGatorPermissionsEnabled : false ,
1026+ } ,
1027+ } ) ;
1028+
1029+ const txId = 'test-tx-id' ;
1030+ const permissionContext = '0x1234567890abcdef1234567890abcdef12345678' ;
1031+
1032+ await expect (
1033+ controller . addPendingRevocation ( txId , permissionContext ) ,
1034+ ) . rejects . toThrow ( 'Gator permissions are not enabled' ) ;
1035+ } ) ;
1036+ } ) ;
7771037} ) ;
7781038
7791039/**
@@ -838,6 +1098,10 @@ function getMessenger(
8381098 return rootMessenger . getRestricted ( {
8391099 name : 'GatorPermissionsController' ,
8401100 allowedActions : [ 'SnapController:handleRequest' , 'SnapController:has' ] ,
841- allowedEvents : [ ] ,
1101+ allowedEvents : [
1102+ 'TransactionController:transactionConfirmed' ,
1103+ 'TransactionController:transactionFailed' ,
1104+ 'TransactionController:transactionDropped' ,
1105+ ] ,
8421106 } ) ;
8431107}
0 commit comments