Skip to content

Commit fb18953

Browse files
committed
Updated UserOp Mine Flow to handle errors
1 parent bfd7400 commit fb18953

File tree

2 files changed

+126
-103
lines changed

2 files changed

+126
-103
lines changed

src/utils/date.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,5 @@
66
*/
77
export const msSince = (from: Date) => {
88
const ms = new Date().getTime() - from.getTime();
9-
return Math.min(ms, 0);
9+
return Math.max(ms, 0);
1010
};

src/worker/tasks/updateMinedUserOps.ts

Lines changed: 125 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { ERC4337EthersSigner } from "@thirdweb-dev/wallets/dist/declarations/src/evm/connectors/smart-wallet/lib/erc4337-signer";
2+
import { providers } from "ethers";
23
import {
34
defineChain,
45
eth_getBlockByNumber,
@@ -39,118 +40,140 @@ export const updateMinedUserOps = async () => {
3940
}
4041

4142
const promises = userOps.map(async (userOp) => {
42-
if (
43-
!userOp.sentAt ||
44-
!userOp.signerAddress ||
45-
!userOp.accountAddress ||
46-
!userOp.userOpHash
47-
) {
48-
return;
49-
}
50-
51-
const sdk = await getSdk({
52-
chainId: parseInt(userOp.chainId),
53-
walletAddress: userOp.signerAddress,
54-
accountAddress: userOp.accountAddress,
55-
});
56-
const signer = sdk.getSigner() as ERC4337EthersSigner;
43+
try {
44+
if (
45+
!userOp.sentAt ||
46+
!userOp.signerAddress ||
47+
!userOp.accountAddress ||
48+
!userOp.userOpHash
49+
) {
50+
return;
51+
}
5752

58-
// Get userOp receipt.
59-
// If no receipt, try again later (or cancel userOps after 1 hour).
60-
// Else the transaction call was submitted to mempool.
61-
const userOpReceipt = await signer.smartAccountAPI.getUserOpReceipt(
62-
signer.httpRpcClient,
63-
userOp.userOpHash,
64-
3_000, // 3 seconds
65-
);
66-
if (!userOpReceipt) {
67-
if (msSince(userOp.sentAt) > CANCEL_DEADLINE_MS) {
68-
await updateTx({
69-
pgtx,
53+
const sdk = await getSdk({
54+
chainId: parseInt(userOp.chainId),
55+
walletAddress: userOp.signerAddress,
56+
accountAddress: userOp.accountAddress,
57+
});
58+
const signer = sdk.getSigner() as ERC4337EthersSigner;
59+
let userOpReceipt: providers.TransactionReceipt | undefined;
60+
try {
61+
// Get userOp receipt.
62+
// If no receipt, try again later (or cancel userOps after 1 hour).
63+
// Else the transaction call was submitted to mempool.
64+
userOpReceipt = await signer.smartAccountAPI.getUserOpReceipt(
65+
signer.httpRpcClient,
66+
userOp.userOpHash,
67+
3_000, // 3 seconds
68+
);
69+
} catch (error) {
70+
// do-nothing
71+
logger({
72+
service: "worker",
73+
level: "error",
7074
queueId: userOp.id,
71-
data: {
72-
status: TransactionStatus.Errored,
73-
errorMessage: "Transaction timed out.",
74-
},
75+
message: "Failed to get receipt for UserOp",
76+
error,
7577
});
7678
}
77-
return;
78-
}
7979

80-
const chain = defineChain(parseInt(userOp.chainId));
81-
const rpcRequest = getRpcClient({
82-
client: thirdwebClient,
83-
chain,
84-
});
80+
if (!userOpReceipt) {
81+
if (msSince(userOp.sentAt) > CANCEL_DEADLINE_MS) {
82+
await updateTx({
83+
pgtx,
84+
queueId: userOp.id,
85+
data: {
86+
status: TransactionStatus.Errored,
87+
errorMessage: "Transaction timed out.",
88+
},
89+
});
90+
}
91+
return;
92+
}
93+
94+
const chain = defineChain(parseInt(userOp.chainId));
95+
const rpcRequest = getRpcClient({
96+
client: thirdwebClient,
97+
chain,
98+
});
8599

86-
// Get the transaction receipt.
87-
// If no receipt, try again later.
88-
// Else the transaction call was confirmed onchain.
89-
const transaction = await eth_getTransactionByHash(rpcRequest, {
90-
hash: userOpReceipt.transactionHash as `0x${string}`,
91-
});
92-
const transactionReceipt = await eth_getTransactionReceipt(
93-
rpcRequest,
94-
{ hash: transaction.hash },
95-
);
96-
if (!transactionReceipt) {
100+
// Get the transaction receipt.
97101
// If no receipt, try again later.
98-
return;
99-
}
102+
// Else the transaction call was confirmed onchain.
103+
const transaction = await eth_getTransactionByHash(rpcRequest, {
104+
hash: userOpReceipt.transactionHash as `0x${string}`,
105+
});
106+
const transactionReceipt = await eth_getTransactionReceipt(
107+
rpcRequest,
108+
{ hash: transaction.hash },
109+
);
110+
if (!transactionReceipt) {
111+
// If no receipt, try again later.
112+
return;
113+
}
100114

101-
let minedAt = new Date();
102-
try {
103-
const block = await eth_getBlockByNumber(rpcRequest, {
104-
blockNumber: transactionReceipt.blockNumber,
105-
includeTransactions: false,
115+
let minedAt = new Date();
116+
try {
117+
const block = await eth_getBlockByNumber(rpcRequest, {
118+
blockNumber: transactionReceipt.blockNumber,
119+
includeTransactions: false,
120+
});
121+
minedAt = new Date(Number(block.timestamp) * 1000);
122+
} catch (e) {}
123+
124+
// Update the userOp transaction as mined.
125+
await updateTx({
126+
pgtx,
127+
queueId: userOp.id,
128+
data: {
129+
status: TransactionStatus.Mined,
130+
minedAt,
131+
blockNumber: Number(transactionReceipt.blockNumber),
132+
onChainTxStatus: toTransactionStatus(transactionReceipt.status),
133+
transactionHash: transactionReceipt.transactionHash,
134+
transactionType: toTransactionType(transaction.type),
135+
gasLimit: userOp.gasLimit ?? undefined,
136+
maxFeePerGas: transaction.maxFeePerGas?.toString(),
137+
maxPriorityFeePerGas:
138+
transaction.maxPriorityFeePerGas?.toString(),
139+
gasPrice: transaction.gasPrice?.toString(),
140+
},
106141
});
107-
minedAt = new Date(Number(block.timestamp) * 1000);
108-
} catch (e) {}
109142

110-
// Update the userOp transaction as mined.
111-
await updateTx({
112-
pgtx,
113-
queueId: userOp.id,
114-
data: {
143+
logger({
144+
service: "worker",
145+
level: "info",
146+
queueId: userOp.id,
147+
message: "Updated with receipt",
148+
});
149+
sendWebhookForQueueIds.push({
150+
queueId: userOp.id,
115151
status: TransactionStatus.Mined,
116-
minedAt,
117-
blockNumber: Number(transactionReceipt.blockNumber),
118-
onChainTxStatus: toTransactionStatus(transactionReceipt.status),
119-
transactionHash: transactionReceipt.transactionHash,
120-
transactionType: toTransactionType(transaction.type),
121-
gasLimit: userOp.gasLimit ?? undefined,
122-
maxFeePerGas: transaction.maxFeePerGas?.toString(),
123-
maxPriorityFeePerGas:
124-
transaction.maxPriorityFeePerGas?.toString(),
125-
gasPrice: transaction.gasPrice?.toString(),
126-
},
127-
});
128-
129-
logger({
130-
service: "worker",
131-
level: "info",
132-
queueId: userOp.id,
133-
message: "Updated with receipt",
134-
});
135-
sendWebhookForQueueIds.push({
136-
queueId: userOp.id,
137-
status: TransactionStatus.Mined,
138-
});
139-
reportUsageForQueueIds.push({
140-
input: {
141-
fromAddress: userOp.fromAddress ?? undefined,
142-
toAddress: userOp.toAddress ?? undefined,
143-
value: userOp.value ?? undefined,
144-
chainId: userOp.chainId,
145-
userOpHash: userOp.userOpHash ?? undefined,
146-
onChainTxStatus: toTransactionStatus(transactionReceipt.status),
147-
functionName: userOp.functionName ?? undefined,
148-
extension: userOp.extension ?? undefined,
149-
provider: signer.httpRpcClient.bundlerUrl,
150-
msSinceSend: msSince(userOp.sentAt!),
151-
},
152-
action: UsageEventTxActionEnum.MineTx,
153-
});
152+
});
153+
reportUsageForQueueIds.push({
154+
input: {
155+
fromAddress: userOp.fromAddress ?? undefined,
156+
toAddress: userOp.toAddress ?? undefined,
157+
value: userOp.value ?? undefined,
158+
chainId: userOp.chainId,
159+
userOpHash: userOp.userOpHash ?? undefined,
160+
onChainTxStatus: toTransactionStatus(transactionReceipt.status),
161+
functionName: userOp.functionName ?? undefined,
162+
extension: userOp.extension ?? undefined,
163+
provider: signer.httpRpcClient.bundlerUrl,
164+
msSinceSend: msSince(userOp.sentAt!),
165+
},
166+
action: UsageEventTxActionEnum.MineTx,
167+
});
168+
} catch (err) {
169+
logger({
170+
service: "worker",
171+
level: "error",
172+
queueId: userOp.id,
173+
message: "Failed to update receipt for UserOp ",
174+
error: err,
175+
});
176+
}
154177
});
155178

156179
await Promise.all(promises);
@@ -166,7 +189,7 @@ export const updateMinedUserOps = async () => {
166189
logger({
167190
service: "worker",
168191
level: "error",
169-
message: "Failed to update receipts",
192+
message: "Failed to batch update receipts",
170193
error: err,
171194
});
172195
}

0 commit comments

Comments
 (0)