@@ -6,8 +6,10 @@ import {
66 getContract ,
77 readContract ,
88 toSerializableTransaction ,
9+ toTokens ,
910 type Hex ,
1011} from "thirdweb" ;
12+ import { getChainMetadata } from "thirdweb/chains" ;
1113import { stringify } from "thirdweb/utils" ;
1214import type { Account } from "thirdweb/wallets" ;
1315import {
@@ -32,10 +34,10 @@ import { getChain } from "../../utils/chain";
3234import { msSince } from "../../utils/date" ;
3335import { env } from "../../utils/env" ;
3436import {
37+ isInsufficientFundsError ,
3538 isNonceAlreadyUsedError ,
3639 isReplacementGasFeeTooLow ,
37- prettifyError ,
38- prettifyTransactionError ,
40+ wrapError ,
3941} from "../../utils/error" ;
4042import { getChecksumAddress } from "../../utils/primitiveTypes" ;
4143import { recordMetrics } from "../../utils/prometheus" ;
@@ -243,16 +245,12 @@ const _sendUserOp = async (
243245 // we don't want this behavior in the engine context
244246 waitForDeployment : false ,
245247 } ) ) as UserOperation ; // TODO support entrypoint v0.7 accounts
246- } catch ( e ) {
247- const erroredTransaction : ErroredTransaction = {
248+ } catch ( error ) {
249+ return {
248250 ...queuedTransaction ,
249251 status : "errored" ,
250- errorMessage : prettifyError ( e ) ,
251- } ;
252- job . log (
253- `Failed to populate transaction: ${ erroredTransaction . errorMessage } ` ,
254- ) ;
255- return erroredTransaction ;
252+ errorMessage : wrapError ( error , "Bundler" ) . message ,
253+ } satisfies ErroredTransaction ;
256254 }
257255
258256 job . log ( `Populated userOp: ${ stringify ( signedUserOp ) } ` ) ;
@@ -325,15 +323,11 @@ const _sendTransaction = async (
325323 } ,
326324 } ) ;
327325 } catch ( e : unknown ) {
328- const erroredTransaction : ErroredTransaction = {
326+ return {
329327 ...queuedTransaction ,
330328 status : "errored" ,
331- errorMessage : prettifyError ( e ) ,
332- } ;
333- job . log (
334- `Failed to populate transaction: ${ erroredTransaction . errorMessage } ` ,
335- ) ;
336- return erroredTransaction ;
329+ errorMessage : wrapError ( e , "RPC" ) . message ,
330+ } satisfies ErroredTransaction ;
337331 }
338332
339333 // Handle if `maxFeePerGas` is overridden.
@@ -380,7 +374,28 @@ const _sendTransaction = async (
380374 job . log ( `Recycling nonce: ${ nonce } ` ) ;
381375 await recycleNonce ( chainId , from , nonce ) ;
382376 }
383- throw error ;
377+
378+ // Do not retry errors that are expected to be rejected by RPC again.
379+ if ( isInsufficientFundsError ( error ) ) {
380+ const { name, nativeCurrency } = await getChainMetadata ( chain ) ;
381+ const { gas, value = 0n } = populatedTransaction ;
382+ const gasPrice =
383+ populatedTransaction . gasPrice ?? populatedTransaction . maxFeePerGas ;
384+
385+ const minGasTokens = gasPrice
386+ ? toTokens ( gas * gasPrice + value , 18 )
387+ : null ;
388+ const errorMessage = minGasTokens
389+ ? `Insufficient funds in ${ account . address } on ${ name } . Transaction requires > ${ minGasTokens } ${ nativeCurrency . symbol } .`
390+ : `Insufficient funds in ${ account . address } on ${ name } . Transaction requires more ${ nativeCurrency . symbol } .` ;
391+ return {
392+ ...queuedTransaction ,
393+ status : "errored" ,
394+ errorMessage,
395+ } satisfies ErroredTransaction ;
396+ }
397+
398+ throw wrapError ( error , "RPC" ) ;
384399 }
385400
386401 await addSentNonce ( chainId , from , nonce ) ;
@@ -466,7 +481,7 @@ const _resendTransaction = async (
466481 job . log ( "A pending transaction exists with >= gas fees. Do not resend." ) ;
467482 return null ;
468483 }
469- throw error ;
484+ throw wrapError ( error , "RPC" ) ;
470485 }
471486
472487 return {
@@ -572,7 +587,7 @@ export const initSendTransactionWorker = () => {
572587 const erroredTransaction : ErroredTransaction = {
573588 ...transaction ,
574589 status : "errored" ,
575- errorMessage : await prettifyTransactionError ( transaction , error ) ,
590+ errorMessage : error . message ,
576591 } ;
577592 job . log ( `Transaction errored: ${ stringify ( erroredTransaction ) } ` ) ;
578593
0 commit comments