@@ -210,7 +210,9 @@ const _sendUserOp = async (
210210 } ) ;
211211 accountFactoryAddress = getAddress ( onchainAccountFactoryAddress ) ;
212212 } catch ( error ) {
213- const errorMessage = `${ wrapError ( error , "RPC" ) . message } Failed to find factory address for account` ;
213+ const errorMessage = `${
214+ wrapError ( error , "RPC" ) . message
215+ } Failed to find factory address for account`;
214216 const erroredTransaction : ErroredTransaction = {
215217 ...queuedTransaction ,
216218 status : "errored" ,
@@ -233,7 +235,9 @@ const _sendUserOp = async (
233235 chain ,
234236 ) ;
235237 } catch ( error ) {
236- const errorMessage = `${ wrapError ( error , "RPC" ) . message } Failed to find entrypoint address for account factory` ;
238+ const errorMessage = `${
239+ wrapError ( error , "RPC" ) . message
240+ } Failed to find entrypoint address for account factory`;
237241 const erroredTransaction : ErroredTransaction = {
238242 ...queuedTransaction ,
239243 status : "errored" ,
@@ -300,18 +304,36 @@ const _sendUserOp = async (
300304 return erroredTransaction ;
301305 }
302306
307+ // Handle if `gasFeeCeiling` is overridden.
308+ // Delay the job if the estimated cost is higher than the gas fee ceiling.
309+ const gasFeeCeiling = overrides ?. gasFeeCeiling ;
310+ if ( typeof gasFeeCeiling !== "undefined" ) {
311+ const estimatedCost =
312+ unsignedUserOp . maxFeePerGas *
313+ ( unsignedUserOp . callGasLimit +
314+ unsignedUserOp . preVerificationGas +
315+ unsignedUserOp . verificationGasLimit ) ;
316+
317+ if ( estimatedCost > gasFeeCeiling ) {
318+ const retryAt = _minutesFromNow ( 5 ) ;
319+ job . log (
320+ `Override gas fee ceiling (${ gasFeeCeiling } ) is lower than onchain estimated cost (${ estimatedCost } ). Delaying job until ${ retryAt } . [callGasLimit: ${ unsignedUserOp . callGasLimit } , preVerificationGas: ${ unsignedUserOp . preVerificationGas } , verificationGasLimit: ${ unsignedUserOp . verificationGasLimit } , maxFeePerGas: ${ unsignedUserOp . maxFeePerGas } ]` ,
321+ ) ;
322+ // token is required to acquire lock for delaying currently processing job: https://docs.bullmq.io/patterns/process-step-jobs#delaying
323+ await job . moveToDelayed ( retryAt . getTime ( ) , token ) ;
324+ // throwing delayed error is required to notify bullmq worker not to complete or fail the job
325+ throw new DelayedError ( "Delaying job due to gas fee override" ) ;
326+ }
327+ }
328+
303329 // Handle if `maxFeePerGas` is overridden.
304330 // Set it if the transaction will be sent, otherwise delay the job.
305- if (
306- typeof overrides ?. maxFeePerGas !== "undefined" &&
307- unsignedUserOp . maxFeePerGas
308- ) {
309- if ( overrides . maxFeePerGas > unsignedUserOp . maxFeePerGas ) {
310- unsignedUserOp . maxFeePerGas = overrides . maxFeePerGas ;
311- } else {
331+ const overrideMaxFeePerGas = overrides ?. maxFeePerGas ;
332+ if ( typeof overrideMaxFeePerGas !== "undefined" ) {
333+ if ( unsignedUserOp . maxFeePerGas > overrideMaxFeePerGas ) {
312334 const retryAt = _minutesFromNow ( 5 ) ;
313335 job . log (
314- `Override gas fee (${ overrides . maxFeePerGas } ) is lower than onchain fee (${ unsignedUserOp . maxFeePerGas } ). Delaying job until ${ retryAt } .` ,
336+ `Override gas fee (${ overrideMaxFeePerGas } ) is lower than onchain fee (${ unsignedUserOp . maxFeePerGas } ). Delaying job until ${ retryAt } .` ,
315337 ) ;
316338 // token is required to acquire lock for delaying currently processing job: https://docs.bullmq.io/patterns/process-step-jobs#delaying
317339 await job . moveToDelayed ( retryAt . getTime ( ) , token ) ;
@@ -331,7 +353,9 @@ const _sendUserOp = async (
331353 userOp : unsignedUserOp ,
332354 } ) ;
333355 } catch ( error ) {
334- const errorMessage = `${ wrapError ( error , "Bundler" ) . message } Failed to sign prepared userop` ;
356+ const errorMessage = `${
357+ wrapError ( error , "Bundler" ) . message
358+ } Failed to sign prepared userop`;
335359 const erroredTransaction : ErroredTransaction = {
336360 ...queuedTransaction ,
337361 status : "errored" ,
@@ -356,7 +380,9 @@ const _sendUserOp = async (
356380 } ,
357381 } ) ;
358382 } catch ( error ) {
359- const errorMessage = `${ wrapError ( error , "Bundler" ) . message } Failed to bundle userop` ;
383+ const errorMessage = `${
384+ wrapError ( error , "Bundler" ) . message
385+ } Failed to bundle userop`;
360386 const erroredTransaction : ErroredTransaction = {
361387 ...queuedTransaction ,
362388 status : "errored" ,
@@ -478,6 +504,32 @@ const _sendTransaction = async (
478504 }
479505 }
480506
507+ // Handle if `gasFeeCeiling` is overridden.
508+ // Delay the job if the estimated cost is higher than the gas fee ceiling.
509+ const gasFeeCeiling = overrides ?. gasFeeCeiling ;
510+ if ( typeof gasFeeCeiling !== "undefined" ) {
511+ let estimatedCost = 0n ;
512+
513+ if ( populatedTransaction . maxFeePerGas ) {
514+ estimatedCost =
515+ populatedTransaction . maxFeePerGas * populatedTransaction . gas ;
516+ } else if ( populatedTransaction . gasPrice ) {
517+ estimatedCost = populatedTransaction . gas * populatedTransaction . gasPrice ;
518+ }
519+
520+ // in case neither of the estimations work, the estimatedCost will be 0n, so this check should not pass, and transaction remains unaffected
521+ if ( estimatedCost > gasFeeCeiling ) {
522+ const retryAt = _minutesFromNow ( 5 ) ;
523+ job . log (
524+ `Override gas fee ceiling (${ gasFeeCeiling } ) is lower than onchain estimated cost (${ estimatedCost } ). Delaying job until ${ retryAt } . [gas: ${ populatedTransaction . gas } , gasPrice: ${ populatedTransaction . gasPrice } , maxFeePerGas: ${ populatedTransaction . maxFeePerGas } ]` ,
525+ ) ;
526+ // token is required to acquire lock for delaying currently processing job: https://docs.bullmq.io/patterns/process-step-jobs#delaying
527+ await job . moveToDelayed ( retryAt . getTime ( ) , token ) ;
528+ // throwing delayed error is required to notify bullmq worker not to complete or fail the job
529+ throw new DelayedError ( "Delaying job due to gas fee override" ) ;
530+ }
531+ }
532+
481533 // Acquire an unused nonce for this transaction.
482534 const { nonce, isRecycledNonce } = await acquireNonce ( {
483535 queueId,
@@ -495,8 +547,9 @@ const _sendTransaction = async (
495547 // This call throws if the RPC rejects the transaction.
496548 let transactionHash : Hex ;
497549 try {
498- const sendTransactionResult =
499- await account . sendTransaction ( populatedTransaction ) ;
550+ const sendTransactionResult = await account . sendTransaction (
551+ populatedTransaction ,
552+ ) ;
500553 transactionHash = sendTransactionResult . transactionHash ;
501554 } catch ( error : unknown ) {
502555 // If the nonce is already seen onchain (nonce too low) or in mempool (replacement underpriced),
0 commit comments