11import { ApprovalType } from '@metamask/controller-utils' ;
2+ import { errorCodes } from '@metamask/rpc-errors' ;
23import {
34 determineTransactionType ,
45 TransactionType ,
@@ -9,6 +10,7 @@ import { EventEmitter } from 'stream';
910import { ADDRESS_ZERO , EMPTY_BYTES , VALUE_ZERO } from './constants' ;
1011import * as BundlerHelper from './helpers/Bundler' ;
1112import * as PendingUserOperationTrackerHelper from './helpers/PendingUserOperationTracker' ;
13+ import { SnapSmartContractAccount } from './helpers/SnapSmartContractAccount' ;
1214import type { UserOperationMetadata } from './types' ;
1315import {
1416 UserOperationStatus ,
@@ -39,6 +41,7 @@ jest.mock('./utils/gas-fees');
3941jest . mock ( './utils/validation' ) ;
4042jest . mock ( './helpers/Bundler' ) ;
4143jest . mock ( './helpers/PendingUserOperationTracker' ) ;
44+ jest . mock ( './helpers/SnapSmartContractAccount' ) ;
4245
4346const CHAIN_ID_MOCK = '0x5' ;
4447const USER_OPERATION_HASH_MOCK = '0x123' ;
@@ -78,15 +81,16 @@ const SIGN_USER_OPERATION_RESPONSE_MOCK: SignUserOperationResponse = {
7881 signature : '0xB' ,
7982} ;
8083
81- const ADD_USER_OPERATION_REQUEST_MOCK = {
84+ const ADD_USER_OPERATION_REQUEST_MOCK : AddUserOperationRequest = {
8285 data : '0x1' ,
86+ from : '0x12' ,
8387 to : '0x2' ,
8488 value : '0x3' ,
8589 maxFeePerGas : '0x4' ,
8690 maxPriorityFeePerGas : '0x5' ,
8791} ;
8892
89- const ADD_USER_OPERATION_OPTIONS_MOCK = {
93+ const ADD_USER_OPERATION_OPTIONS_MOCK : AddUserOperationOptions = {
9094 networkClientId : NETWORK_CLIENT_ID_MOCK ,
9195 origin : ORIGIN_MOCK ,
9296} ;
@@ -257,10 +261,10 @@ describe('UserOperationController', () => {
257261 updateGasFeesMock
258262 . mockImplementationOnce ( async ( { metadata } ) => {
259263 metadata . userOperation . maxFeePerGas =
260- ADD_USER_OPERATION_REQUEST_MOCK . maxFeePerGas ;
264+ ADD_USER_OPERATION_REQUEST_MOCK . maxFeePerGas as string ;
261265
262266 metadata . userOperation . maxPriorityFeePerGas =
263- ADD_USER_OPERATION_REQUEST_MOCK . maxPriorityFeePerGas ;
267+ ADD_USER_OPERATION_REQUEST_MOCK . maxPriorityFeePerGas as string ;
264268 } )
265269 . mockImplementationOnce ( async ( { metadata } ) => {
266270 metadata . userOperation . maxFeePerGas = '0x6' ;
@@ -589,6 +593,26 @@ describe('UserOperationController', () => {
589593 ) ;
590594 } ) ;
591595
596+ it ( 'deletes user operation if rejected' , async ( ) => {
597+ const controller = new UserOperationController ( optionsMock ) ;
598+
599+ const error = new Error ( ERROR_MESSAGE_MOCK ) ;
600+ ( error as unknown as Record < string , unknown > ) . code =
601+ errorCodes . provider . userRejectedRequest ;
602+
603+ approvalControllerAddRequestMock . mockClear ( ) ;
604+ approvalControllerAddRequestMock . mockRejectedValue ( error ) ;
605+
606+ const { hash } = await controller . addUserOperation (
607+ ADD_USER_OPERATION_REQUEST_MOCK ,
608+ { ...ADD_USER_OPERATION_OPTIONS_MOCK , smartContractAccount } ,
609+ ) ;
610+
611+ await expect ( hash ( ) ) . rejects . toThrow ( ERROR_MESSAGE_MOCK ) ;
612+
613+ expect ( Object . keys ( controller . state . userOperations ) ) . toHaveLength ( 0 ) ;
614+ } ) ;
615+
592616 // eslint-disable-next-line jest/expect-expect
593617 it ( 'does not throw if hash function not invoked' , async ( ) => {
594618 const controller = new UserOperationController ( optionsMock ) ;
@@ -782,6 +806,24 @@ describe('UserOperationController', () => {
782806 expect ( resultCallbackSuccessMock ) . not . toHaveBeenCalled ( ) ;
783807 } ) ;
784808
809+ it ( 'uses snap smart contract account if no smart contract account provided' , async ( ) => {
810+ const prepareMock = jest . spyOn (
811+ SnapSmartContractAccount . prototype ,
812+ 'prepareUserOperation' ,
813+ ) ;
814+
815+ const controller = new UserOperationController ( optionsMock ) ;
816+
817+ await addUserOperation ( controller , ADD_USER_OPERATION_REQUEST_MOCK , {
818+ ...ADD_USER_OPERATION_OPTIONS_MOCK ,
819+ smartContractAccount : undefined ,
820+ } ) ;
821+
822+ await flushPromises ( ) ;
823+
824+ expect ( prepareMock ) . toHaveBeenCalledTimes ( 1 ) ;
825+ } ) ;
826+
785827 describe ( 'if approval request resolved with updated transaction' , ( ) => {
786828 it ( 'updates gas fees without regeneration if paymaster data not set' , async ( ) => {
787829 const controller = new UserOperationController ( optionsMock ) ;
@@ -1078,27 +1120,25 @@ describe('UserOperationController', () => {
10781120 } ) ;
10791121 } ) ;
10801122
1081- if ( method === 'addUserOperation' ) {
1082- it ( 'validates arguments' , async ( ) => {
1083- const controller = new UserOperationController ( optionsMock ) ;
1123+ it ( 'validates arguments' , async ( ) => {
1124+ const controller = new UserOperationController ( optionsMock ) ;
10841125
1085- await addUserOperation ( controller , ADD_USER_OPERATION_REQUEST_MOCK , {
1086- ...ADD_USER_OPERATION_OPTIONS_MOCK ,
1087- smartContractAccount,
1088- } ) ;
1126+ await addUserOperation ( controller , ADD_USER_OPERATION_REQUEST_MOCK , {
1127+ ...ADD_USER_OPERATION_OPTIONS_MOCK ,
1128+ smartContractAccount,
1129+ } ) ;
10891130
1090- expect ( validateAddUserOperationRequestMock ) . toHaveBeenCalledTimes ( 1 ) ;
1091- expect ( validateAddUserOperationRequestMock ) . toHaveBeenCalledWith (
1092- ADD_USER_OPERATION_REQUEST_MOCK ,
1093- ) ;
1131+ expect ( validateAddUserOperationRequestMock ) . toHaveBeenCalledTimes ( 1 ) ;
1132+ expect ( validateAddUserOperationRequestMock ) . toHaveBeenCalledWith (
1133+ ADD_USER_OPERATION_REQUEST_MOCK ,
1134+ ) ;
10941135
1095- expect ( validateAddUserOperationOptionsMock ) . toHaveBeenCalledTimes ( 1 ) ;
1096- expect ( validateAddUserOperationOptionsMock ) . toHaveBeenCalledWith ( {
1097- ...ADD_USER_OPERATION_OPTIONS_MOCK ,
1098- smartContractAccount,
1099- } ) ;
1136+ expect ( validateAddUserOperationOptionsMock ) . toHaveBeenCalledTimes ( 1 ) ;
1137+ expect ( validateAddUserOperationOptionsMock ) . toHaveBeenCalledWith ( {
1138+ ...ADD_USER_OPERATION_OPTIONS_MOCK ,
1139+ smartContractAccount,
11001140 } ) ;
1101- }
1141+ } ) ;
11021142
11031143 if ( method === 'addUserOperationFromTransaction' ) {
11041144 it ( 'sets data as undefined if empty string' , async ( ) => {
0 commit comments