From 6b523c0ed5d4cfd02a725b514d97cd189ff56069 Mon Sep 17 00:00:00 2001 From: Prithvish Baidya Date: Thu, 27 Feb 2025 18:48:12 +0530 Subject: [PATCH 1/5] allow forcing deploy/skip_deploy behaviour --- packages/thirdweb/src/wallets/smart/lib/userop.ts | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/packages/thirdweb/src/wallets/smart/lib/userop.ts b/packages/thirdweb/src/wallets/smart/lib/userop.ts index db00983de90..a936246ec8e 100644 --- a/packages/thirdweb/src/wallets/smart/lib/userop.ts +++ b/packages/thirdweb/src/wallets/smart/lib/userop.ts @@ -136,6 +136,7 @@ export async function createUnsignedUserOp(args: { adminAddress: string; sponsorGas: boolean; waitForDeployment?: boolean; + forceDeployment?: "force_deploy" | "force_skip_deploy"; overrides?: SmartWalletOptions["overrides"]; }): Promise { const { @@ -146,6 +147,7 @@ export async function createUnsignedUserOp(args: { overrides, sponsorGas, waitForDeployment = true, + forceDeployment, } = args; const chain = executeTx.chain; const client = executeTx.client; @@ -161,9 +163,18 @@ export async function createUnsignedUserOp(args: { args.overrides?.entrypointAddress || ENTRYPOINT_ADDRESS_v0_6, ); + const isDeployedOverride = forceDeployment + ? { + force_deploy: true, + force_skip_deploy: false, + }[forceDeployment] + : undefined; + const [isDeployed, callData, callGasLimit, gasFees, nonce] = await Promise.all([ - isContractDeployed(accountContract), + typeof isDeployedOverride === "boolean" + ? isDeployedOverride + : isContractDeployed(accountContract), encode(executeTx), resolvePromisedValue(executeTx.gas), getGasFees({ @@ -699,6 +710,7 @@ export async function createAndSignUserOp(options: { client: ThirdwebClient; smartWalletOptions: SmartWalletOptions; waitForDeployment?: boolean; + forceDeployment?: "force_deploy" | "force_skip_deploy"; }) { const config = options.smartWalletOptions; const factoryContract = getContract({ @@ -757,6 +769,7 @@ export async function createAndSignUserOp(options: { sponsorGas: "sponsorGas" in config ? config.sponsorGas : config.gasless, overrides: config.overrides, waitForDeployment: options.waitForDeployment, + forceDeployment: options.forceDeployment, }); const signedUserOp = await signUserOp({ client: options.client, From 98587818d2c76d83d1767e4037ff4df20a34935f Mon Sep 17 00:00:00 2001 From: Prithvish Baidya Date: Fri, 28 Feb 2025 03:36:36 +0530 Subject: [PATCH 2/5] cleaup + change interface --- .../thirdweb/src/wallets/smart/lib/userop.ts | 22 +++++++------------ 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/packages/thirdweb/src/wallets/smart/lib/userop.ts b/packages/thirdweb/src/wallets/smart/lib/userop.ts index a936246ec8e..f490a933945 100644 --- a/packages/thirdweb/src/wallets/smart/lib/userop.ts +++ b/packages/thirdweb/src/wallets/smart/lib/userop.ts @@ -136,7 +136,7 @@ export async function createUnsignedUserOp(args: { adminAddress: string; sponsorGas: boolean; waitForDeployment?: boolean; - forceDeployment?: "force_deploy" | "force_skip_deploy"; + isDeployedOverride?: boolean; overrides?: SmartWalletOptions["overrides"]; }): Promise { const { @@ -147,7 +147,7 @@ export async function createUnsignedUserOp(args: { overrides, sponsorGas, waitForDeployment = true, - forceDeployment, + isDeployedOverride, } = args; const chain = executeTx.chain; const client = executeTx.client; @@ -163,18 +163,12 @@ export async function createUnsignedUserOp(args: { args.overrides?.entrypointAddress || ENTRYPOINT_ADDRESS_v0_6, ); - const isDeployedOverride = forceDeployment - ? { - force_deploy: true, - force_skip_deploy: false, - }[forceDeployment] - : undefined; - const [isDeployed, callData, callGasLimit, gasFees, nonce] = await Promise.all([ typeof isDeployedOverride === "boolean" ? isDeployedOverride - : isContractDeployed(accountContract), + : isContractDeployed(accountContract) || + isAccountDeploying(accountContract), encode(executeTx), resolvePromisedValue(executeTx.gas), getGasFees({ @@ -310,7 +304,7 @@ async function populateUserOp_v0_7(args: { let factory: string | undefined; let factoryData: Hex; - if (isDeployed || isAccountDeploying(accountContract)) { + if (isDeployed) { factoryData = "0x"; if (waitForDeployment) { // lock until account is deployed if needed to avoid 'sender already created' errors when sending multiple transactions in parallel @@ -473,7 +467,7 @@ async function populateUserOp_v0_6(args: { const { chain, client } = bundlerOptions; let initCode: Hex; - if (isDeployed || isAccountDeploying(accountContract)) { + if (isDeployed) { initCode = "0x"; if (waitForDeployment) { // lock until account is deployed if needed to avoid 'sender already created' errors when sending multiple transactions in parallel @@ -710,7 +704,7 @@ export async function createAndSignUserOp(options: { client: ThirdwebClient; smartWalletOptions: SmartWalletOptions; waitForDeployment?: boolean; - forceDeployment?: "force_deploy" | "force_skip_deploy"; + isDeployedOverride?: boolean; }) { const config = options.smartWalletOptions; const factoryContract = getContract({ @@ -769,7 +763,7 @@ export async function createAndSignUserOp(options: { sponsorGas: "sponsorGas" in config ? config.sponsorGas : config.gasless, overrides: config.overrides, waitForDeployment: options.waitForDeployment, - forceDeployment: options.forceDeployment, + isDeployedOverride: options.isDeployedOverride, }); const signedUserOp = await signUserOp({ client: options.client, From a37e16b056ef45bae0f52a6cb3a2968e32cbb696 Mon Sep 17 00:00:00 2001 From: Joaquim Verges Date: Fri, 28 Feb 2025 11:58:26 +1300 Subject: [PATCH 3/5] fix isAccountDeploying logic --- .../thirdweb/src/wallets/smart/lib/calls.ts | 63 +++++++++++-------- .../thirdweb/src/wallets/smart/lib/userop.ts | 5 +- 2 files changed, 39 insertions(+), 29 deletions(-) diff --git a/packages/thirdweb/src/wallets/smart/lib/calls.ts b/packages/thirdweb/src/wallets/smart/lib/calls.ts index f90c5582e43..8dcb6a3fc2b 100644 --- a/packages/thirdweb/src/wallets/smart/lib/calls.ts +++ b/packages/thirdweb/src/wallets/smart/lib/calls.ts @@ -8,6 +8,7 @@ import { prepareContractCall } from "../../../transaction/prepare-contract-call. import type { PreparedTransaction } from "../../../transaction/prepare-transaction.js"; import { readContract } from "../../../transaction/read-contract.js"; import { isHex, stringToHex } from "../../../utils/encoding/hex.js"; +import { withCache } from "../../../utils/promise/withCache.js"; import type { SendTransactionOption } from "../../interfaces/wallet.js"; import { DEFAULT_ACCOUNT_FACTORY_V0_6 } from "./constants.js"; @@ -72,33 +73,41 @@ export async function predictAddress(args: { accountSalt?: string; accountAddress?: string; }): Promise { - const { - factoryContract, - predictAddressOverride: predictAddress, - adminAddress, - accountSalt, - accountAddress, - } = args; - if (predictAddress) { - return predictAddress(factoryContract, adminAddress); - } - if (accountAddress) { - return accountAddress; - } - if (!adminAddress) { - throw new Error( - "Account address is required to predict the smart wallet address.", - ); - } - const saltHex = - accountSalt && isHex(accountSalt) - ? accountSalt - : stringToHex(accountSalt ?? ""); - return readContract({ - contract: factoryContract, - method: "function getAddress(address, bytes) returns (address)", - params: [adminAddress, saltHex], - }); + return withCache( + async () => { + const { + factoryContract, + predictAddressOverride: predictAddress, + adminAddress, + accountSalt, + accountAddress, + } = args; + if (predictAddress) { + return predictAddress(factoryContract, adminAddress); + } + if (accountAddress) { + return accountAddress; + } + if (!adminAddress) { + throw new Error( + "Account address is required to predict the smart wallet address.", + ); + } + const saltHex = + accountSalt && isHex(accountSalt) + ? accountSalt + : stringToHex(accountSalt ?? ""); + return readContract({ + contract: factoryContract, + method: "function getAddress(address, bytes) returns (address)", + params: [adminAddress, saltHex], + }); + }, + { + cacheKey: `${args.factoryContract.address}-${args.adminAddress}-${args.accountSalt}`, + cacheTime: 1000 * 60 * 60 * 24, // 1 day + }, + ); } /** diff --git a/packages/thirdweb/src/wallets/smart/lib/userop.ts b/packages/thirdweb/src/wallets/smart/lib/userop.ts index f490a933945..77f11c98825 100644 --- a/packages/thirdweb/src/wallets/smart/lib/userop.ts +++ b/packages/thirdweb/src/wallets/smart/lib/userop.ts @@ -167,8 +167,9 @@ export async function createUnsignedUserOp(args: { await Promise.all([ typeof isDeployedOverride === "boolean" ? isDeployedOverride - : isContractDeployed(accountContract) || - isAccountDeploying(accountContract), + : isContractDeployed(accountContract).then( + (isDeployed) => isDeployed || isAccountDeploying(accountContract), + ), encode(executeTx), resolvePromisedValue(executeTx.gas), getGasFees({ From 9e3fc9a9138529092da28f80293c3de5ad7ce19b Mon Sep 17 00:00:00 2001 From: Joaquim Verges Date: Fri, 28 Feb 2025 12:01:57 +1300 Subject: [PATCH 4/5] cache predict address --- .../thirdweb/src/wallets/smart/lib/calls.ts | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/thirdweb/src/wallets/smart/lib/calls.ts b/packages/thirdweb/src/wallets/smart/lib/calls.ts index 8dcb6a3fc2b..3ba03763aec 100644 --- a/packages/thirdweb/src/wallets/smart/lib/calls.ts +++ b/packages/thirdweb/src/wallets/smart/lib/calls.ts @@ -73,18 +73,18 @@ export async function predictAddress(args: { accountSalt?: string; accountAddress?: string; }): Promise { + const { + factoryContract, + predictAddressOverride: predictAddress, + adminAddress, + accountSalt, + accountAddress, + } = args; + if (predictAddress) { + return predictAddress(factoryContract, adminAddress); + } return withCache( async () => { - const { - factoryContract, - predictAddressOverride: predictAddress, - adminAddress, - accountSalt, - accountAddress, - } = args; - if (predictAddress) { - return predictAddress(factoryContract, adminAddress); - } if (accountAddress) { return accountAddress; } @@ -104,7 +104,7 @@ export async function predictAddress(args: { }); }, { - cacheKey: `${args.factoryContract.address}-${args.adminAddress}-${args.accountSalt}`, + cacheKey: `${args.factoryContract.chain.id}-${args.factoryContract.address}-${args.adminAddress}-${args.accountSalt}`, cacheTime: 1000 * 60 * 60 * 24, // 1 day }, ); From 5fc1d4e238fb88adfd1df0b786bb8fb023fd4734 Mon Sep 17 00:00:00 2001 From: Joaquim Verges Date: Fri, 28 Feb 2025 12:02:59 +1300 Subject: [PATCH 5/5] shortcut --- packages/thirdweb/src/wallets/smart/lib/calls.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/thirdweb/src/wallets/smart/lib/calls.ts b/packages/thirdweb/src/wallets/smart/lib/calls.ts index 3ba03763aec..b8730bd33cd 100644 --- a/packages/thirdweb/src/wallets/smart/lib/calls.ts +++ b/packages/thirdweb/src/wallets/smart/lib/calls.ts @@ -83,16 +83,16 @@ export async function predictAddress(args: { if (predictAddress) { return predictAddress(factoryContract, adminAddress); } + if (accountAddress) { + return accountAddress; + } + if (!adminAddress) { + throw new Error( + "Account address is required to predict the smart wallet address.", + ); + } return withCache( async () => { - if (accountAddress) { - return accountAddress; - } - if (!adminAddress) { - throw new Error( - "Account address is required to predict the smart wallet address.", - ); - } const saltHex = accountSalt && isHex(accountSalt) ? accountSalt