Skip to content

Commit f40c83d

Browse files
jakeloofarhanW3
andauthored
Cache smart wallet account factory address (#585)
* Cache smart wallet account factory address * Fix import path * Absolute import path * Clean up re: comments * fix types * updatde logic on processTx for UserOps to check for account created onchain and then only process --------- Co-authored-by: farhanW3 <farhan@thirdweb.com>
1 parent e5766fc commit f40c83d

File tree

4 files changed

+61
-11
lines changed

4 files changed

+61
-11
lines changed

src/server/routes/contract/extensions/accountFactory/write/createAccount.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
import { txOverridesWithValueSchema } from "../../../../../schemas/txOverrides";
1313
import { walletWithAAHeaderSchema } from "../../../../../schemas/wallet";
1414
import { getChainIdFromChain } from "../../../../../utils/chain";
15+
import { redis } from "utils/redis/redis";
1516

1617
const requestBodySchema = Type.Object({
1718
adminAddress: Type.String({
@@ -92,6 +93,10 @@ export const createAccount = async (fastify: FastifyInstance) => {
9293
txOverrides,
9394
});
9495

96+
// Note: This is a temporary solution to cache the deployed address's factory for 7 days.
97+
// This is needed due to a potential race condition of submitting a transaction immediately after creating an account that is not yet mined onchain
98+
await redis.set(`account-factory:${deployedAddress.toLowerCase()}`, contractAddress, 'EX', 7 * 24 * 60 * 60);
99+
95100
reply.status(StatusCodes.OK).send({
96101
result: {
97102
queueId,

src/server/utils/wallets/getSmartWallet.ts

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { EVMWallet, SmartWallet } from "@thirdweb-dev/wallets";
22
import { getContract } from "../../../utils/cache/getContract";
33
import { env } from "../../../utils/env";
4+
import { redis } from "utils/redis/redis";
45

56
interface GetSmartWalletParams {
67
chainId: number;
@@ -13,17 +14,27 @@ export const getSmartWallet = async ({
1314
backendWallet,
1415
accountAddress,
1516
}: GetSmartWalletParams) => {
16-
let factoryAddress: string;
17+
let factoryAddress: string = "";
18+
1719
try {
18-
const contract = await getContract({
19-
chainId,
20-
contractAddress: accountAddress,
21-
});
22-
factoryAddress = await contract.call("factory");
20+
// Note: This is a temporary solution to use cached deployed address's factory address from create-account
21+
// This is needed due to a potential race condition of submitting a transaction immediately after creating an account that is not yet mined onchain
22+
factoryAddress = (await redis.get(`account-factory:${accountAddress.toLowerCase()}`)) || "";
2323
} catch {
24-
throw new Error(
25-
`Failed to find factory address for account '${accountAddress}' on chain '${chainId}'`,
26-
);
24+
}
25+
26+
if (!factoryAddress) {
27+
try {
28+
const contract = await getContract({
29+
chainId,
30+
contractAddress: accountAddress,
31+
});
32+
factoryAddress = await contract.call("factory");
33+
} catch {
34+
throw new Error(
35+
`Failed to find factory address for account '${accountAddress}' on chain '${chainId}'`,
36+
);
37+
}
2738
}
2839

2940
const smartWallet = new SmartWallet({

src/worker/tasks/processTx.ts

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import {
99
import { ERC4337EthersSigner } from "@thirdweb-dev/wallets/dist/declarations/src/evm/connectors/smart-wallet/lib/erc4337-signer";
1010
import { ethers } from "ethers";
1111
import { BigNumber } from "ethers/lib/ethers";
12+
import { getContractAddress } from "ethers/lib/utils";
13+
import { redis } from "utils/redis/redis";
1214
import { RpcResponse } from "viem/_types/utils/rpc";
1315
import { prisma } from "../../db/client";
1416
import { getQueuedTxs } from "../../db/transactions/getQueuedTxs";
@@ -35,7 +37,6 @@ import {
3537
} from "../../utils/webhook";
3638
import { randomNonce } from "../utils/nonce";
3739
import { getWithdrawValue } from "../utils/withdraw";
38-
import { getContractAddress } from "ethers/lib/utils";
3940

4041
type RpcResponseData = {
4142
tx: Transactions;
@@ -381,6 +382,38 @@ export const processTx = async () => {
381382
walletAddress: tx.signerAddress!,
382383
accountAddress: tx.accountAddress!,
383384
});
385+
386+
// Check if the Smart Account is deployed onchain before sending the user op
387+
// check Redis cache for account deployed code
388+
const cachedAccountDeployedCode = await redis.get(
389+
`account-deployed:${tx.accountAddress!.toLowerCase()}`,
390+
);
391+
392+
// if not found in cache, fetch from chain and set in cache once
393+
if (!cachedAccountDeployedCode) {
394+
const accountDeployedCode = await sdk
395+
.getProvider()
396+
.getCode(tx.accountAddress!);
397+
398+
if (accountDeployedCode === "0x") {
399+
logger({
400+
service: "worker",
401+
level: "warn",
402+
queueId: tx.id,
403+
message: `Account not deployed. Skipping`,
404+
});
405+
406+
return;
407+
}
408+
409+
await redis.set(
410+
`account-deployed:${tx.accountAddress!.toLowerCase()}`,
411+
accountDeployedCode,
412+
"EX",
413+
60 * 60 * 24,
414+
);
415+
}
416+
384417
const signer = sdk.getSigner() as ERC4337EthersSigner;
385418

386419
const nonce = randomNonce();

tsconfig.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
"emitDecoratorMetadata": true,
1111
"skipLibCheck": true,
1212
"allowJs": true,
13-
"resolveJsonModule": true
13+
"resolveJsonModule": true,
14+
"baseUrl": "src"
1415
},
1516
"ts-node": {
1617
"esm": true,

0 commit comments

Comments
 (0)