Skip to content

Commit ee9c4ee

Browse files
authored
chore: Show min amount needed if out of funds (#760)
* chore: Show min amount needed if out of funds * dont retry if out of funds * wrap error * error * update custom message
1 parent 224bd8b commit ee9c4ee

File tree

3 files changed

+40
-52
lines changed

3 files changed

+40
-52
lines changed

src/utils/error.ts

Lines changed: 4 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,12 @@
11
import { ethers } from "ethers";
2-
import { getChainMetadata } from "thirdweb/chains";
32
import { stringify } from "thirdweb/utils";
4-
import { getChain } from "./chain";
53
import { isEthersErrorCode } from "./ethers";
6-
import { doSimulateTransaction } from "./transaction/simulateQueuedTransaction";
7-
import type { AnyTransaction } from "./transaction/types";
84

9-
export const prettifyError = (error: unknown): string => {
10-
if (error instanceof Error) {
11-
return error.message;
12-
}
13-
return stringify(error);
14-
};
15-
16-
export const prettifyTransactionError = async (
17-
transaction: AnyTransaction,
18-
error: Error,
19-
): Promise<string> => {
20-
if (!transaction.isUserOp) {
21-
if (isInsufficientFundsError(error)) {
22-
const chain = await getChain(transaction.chainId);
23-
const metadata = await getChainMetadata(chain);
24-
return `Insufficient ${metadata.nativeCurrency?.symbol} on ${metadata.name} in ${transaction.from}.`;
25-
}
5+
export const wrapError = (error: unknown, prefix: "RPC" | "Bundler") =>
6+
new Error(`[${prefix}] ${prettifyError(error)}`);
267

27-
if (isEthersErrorCode(error, ethers.errors.UNPREDICTABLE_GAS_LIMIT)) {
28-
const simulateError = await doSimulateTransaction(transaction);
29-
if (simulateError) {
30-
return simulateError;
31-
}
32-
}
33-
}
34-
35-
return error.message;
36-
};
8+
export const prettifyError = (error: unknown): string =>
9+
error instanceof Error ? error.message : stringify(error);
3710

3811
const _parseMessage = (error: unknown): string | null => {
3912
return error && typeof error === "object" && "message" in error

src/worker/queues/queues.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Job, JobsOptions, Worker } from "bullmq";
1+
import type { Job, JobsOptions, Worker } from "bullmq";
22
import { env } from "../../utils/env";
33
import { logger } from "../../utils/logger";
44

src/worker/tasks/sendTransactionWorker.ts

Lines changed: 35 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -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";
1113
import { stringify } from "thirdweb/utils";
1214
import type { Account } from "thirdweb/wallets";
1315
import {
@@ -32,10 +34,10 @@ import { getChain } from "../../utils/chain";
3234
import { msSince } from "../../utils/date";
3335
import { env } from "../../utils/env";
3436
import {
37+
isInsufficientFundsError,
3538
isNonceAlreadyUsedError,
3639
isReplacementGasFeeTooLow,
37-
prettifyError,
38-
prettifyTransactionError,
40+
wrapError,
3941
} from "../../utils/error";
4042
import { getChecksumAddress } from "../../utils/primitiveTypes";
4143
import { 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

Comments
 (0)