From f4678cd2b3626c93a1ddb058edf65c3596061d07 Mon Sep 17 00:00:00 2001 From: gregfromstl Date: Sun, 5 Jan 2025 19:35:18 -1000 Subject: [PATCH 1/3] fix: Adds 1271 signatures and force deployment function --- .changeset/polite-trains-kick.md | 16 ++ packages/thirdweb/src/auth/verify-hash.ts | 21 +- packages/thirdweb/src/exports/thirdweb.ts | 2 + packages/thirdweb/src/exports/wallets.ts | 2 + .../thirdweb/src/wallets/create-wallet.ts | 16 ++ packages/thirdweb/src/wallets/smart/index.ts | 8 +- .../thirdweb/src/wallets/smart/lib/signing.ts | 213 +++++++++++++----- .../smart-wallet-integration-v07.test.ts | 50 +++- .../smart/smart-wallet-integration.test.ts | 48 +++- 9 files changed, 302 insertions(+), 74 deletions(-) create mode 100644 .changeset/polite-trains-kick.md diff --git a/.changeset/polite-trains-kick.md b/.changeset/polite-trains-kick.md new file mode 100644 index 00000000000..7801761ecc4 --- /dev/null +++ b/.changeset/polite-trains-kick.md @@ -0,0 +1,16 @@ +--- +"thirdweb": minor +--- + +Feature: Adds `deploySmartAccount` function to force the deployment of a smart account. + +```ts +const account = await deploySmartAccount({ + smartAccount, + chain, + client, + accountContract, +}); +``` + +Fix: Uses 1271 signatures if the smart account is already deployed. diff --git a/packages/thirdweb/src/auth/verify-hash.ts b/packages/thirdweb/src/auth/verify-hash.ts index 51f20d5201d..da32a454065 100644 --- a/packages/thirdweb/src/auth/verify-hash.ts +++ b/packages/thirdweb/src/auth/verify-hash.ts @@ -129,8 +129,7 @@ export async function verifyHash({ try { const result = await eth_call(rpcRequest, verificationData); return hexToBool(result); - } catch (err) { - console.error("Error verifying ERC-6492 signature", err); + } catch { // Some chains do not support the eth_call simulation and will fail, so we fall back to regular EIP1271 validation const validEip1271 = await verifyEip1271Signature({ hash, @@ -154,7 +153,7 @@ export async function verifyHash({ } const EIP_1271_MAGIC_VALUE = "0x1626ba7e"; -async function verifyEip1271Signature({ +export async function verifyEip1271Signature({ hash, signature, contract, @@ -163,10 +162,14 @@ async function verifyEip1271Signature({ signature: Hex; contract: ThirdwebContract; }): Promise { - const result = await isValidSignature({ - hash, - signature, - contract, - }); - return result === EIP_1271_MAGIC_VALUE; + try { + const result = await isValidSignature({ + hash, + signature, + contract, + }); + return result === EIP_1271_MAGIC_VALUE; + } catch { + return false; + } } diff --git a/packages/thirdweb/src/exports/thirdweb.ts b/packages/thirdweb/src/exports/thirdweb.ts index ff6ce7e4e17..4a15bd62380 100644 --- a/packages/thirdweb/src/exports/thirdweb.ts +++ b/packages/thirdweb/src/exports/thirdweb.ts @@ -302,3 +302,5 @@ export { type VerifyTypedDataParams, verifyTypedData, } from "../auth/verify-typed-data.js"; + +export { deploySmartAccount } from "../wallets/smart/lib/signing.js"; diff --git a/packages/thirdweb/src/exports/wallets.ts b/packages/thirdweb/src/exports/wallets.ts index 4336ac5f1ba..e648e0f9873 100644 --- a/packages/thirdweb/src/exports/wallets.ts +++ b/packages/thirdweb/src/exports/wallets.ts @@ -160,3 +160,5 @@ export * as EIP1193 from "../adapters/eip1193/index.js"; export { injectedProvider } from "../wallets/injected/mipdStore.js"; export type { ConnectionManager } from "../wallets/manager/index.js"; + +export { deploySmartAccount } from "../wallets/smart/lib/signing.js"; diff --git a/packages/thirdweb/src/wallets/create-wallet.ts b/packages/thirdweb/src/wallets/create-wallet.ts index 132244b006e..2b290076c7a 100644 --- a/packages/thirdweb/src/wallets/create-wallet.ts +++ b/packages/thirdweb/src/wallets/create-wallet.ts @@ -116,6 +116,22 @@ import { createWalletEmitter } from "./wallet-emitter.js"; * * [View Coinbase wallet creation options](https://portal.thirdweb.com/references/typescript/v5/CoinbaseWalletCreationOptions) * + * ## Connecting with a smart wallet + * + * ```ts + * import { createWallet } from "thirdweb/wallets"; + * + * const wallet = createWallet("smart", { + * chain: sepolia, + * sponsorGas: true, + * }); + * + * const account = await wallet.connect({ + * client, + * personalAccount, // pass the admin account + * }); + * ``` + * * @wallet */ export function createWallet( diff --git a/packages/thirdweb/src/wallets/smart/index.ts b/packages/thirdweb/src/wallets/smart/index.ts index f97d8fb386c..09f83cc04bb 100644 --- a/packages/thirdweb/src/wallets/smart/index.ts +++ b/packages/thirdweb/src/wallets/smart/index.ts @@ -277,8 +277,8 @@ async function createSmartAccount( }); } - const { deployAndSignMessage } = await import("./lib/signing.js"); - return deployAndSignMessage({ + const { smartAccountSignMessage } = await import("./lib/signing.js"); + return smartAccountSignMessage({ accountContract, factoryContract: options.factoryContract, options, @@ -298,8 +298,8 @@ async function createSmartAccount( }); } - const { deployAndSignTypedData } = await import("./lib/signing.js"); - return deployAndSignTypedData({ + const { smartAccountSignTypedData } = await import("./lib/signing.js"); + return smartAccountSignTypedData({ accountContract, factoryContract: options.factoryContract, options, diff --git a/packages/thirdweb/src/wallets/smart/lib/signing.ts b/packages/thirdweb/src/wallets/smart/lib/signing.ts index e5f81c297af..5997da09b46 100644 --- a/packages/thirdweb/src/wallets/smart/lib/signing.ts +++ b/packages/thirdweb/src/wallets/smart/lib/signing.ts @@ -1,6 +1,11 @@ import type * as ox__TypedData from "ox/TypedData"; import { serializeErc6492Signature } from "../../../auth/serialize-erc6492-signature.js"; -import { verifyHash } from "../../../auth/verify-hash.js"; +import { + verifyEip1271Signature, + verifyHash, +} from "../../../auth/verify-hash.js"; +import type { Chain } from "../../../chains/types.js"; +import type { ThirdwebClient } from "../../../client/client.js"; import { type ThirdwebContract, getContract, @@ -8,14 +13,22 @@ import { import { encode } from "../../../transaction/actions/encode.js"; import { readContract } from "../../../transaction/read-contract.js"; import { encodeAbiParameters } from "../../../utils/abi/encodeAbiParameters.js"; +import { isContractDeployed } from "../../../utils/bytecode/is-contract-deployed.js"; import type { Hex } from "../../../utils/encoding/hex.js"; import { hashMessage } from "../../../utils/hashing/hashMessage.js"; import { hashTypedData } from "../../../utils/hashing/hashTypedData.js"; import type { SignableMessage } from "../../../utils/types.js"; +import type { Account } from "../../../wallets/interfaces/wallet.js"; import type { SmartAccountOptions } from "../types.js"; import { prepareCreateAccount } from "./calls.js"; -export async function deployAndSignMessage({ +/** + * If the account is already deployed, generate an ERC-1271 signature. + * If the account is not deployed, generate an ERC-6492 signature unless otherwise specified. + * + * @internal + */ +export async function smartAccountSignMessage({ accountContract, factoryContract, options, @@ -55,40 +68,51 @@ export async function deployAndSignMessage({ sig = await options.personalAccount.signMessage({ message }); } - const deployTx = prepareCreateAccount({ - factoryContract, - adminAddress: options.personalAccount.address, - accountSalt: options.overrides?.accountSalt, - createAccountOverride: options.overrides?.createAccount, - }); - if (!deployTx) { - throw new Error("Create account override not provided"); - } - const initCode = await encode(deployTx); - const erc6492Sig = serializeErc6492Signature({ - address: factoryContract.address, - data: initCode, - signature: sig, - }); + const isDeployed = await isContractDeployed(accountContract); + if (isDeployed) { + const isValid = await verifyEip1271Signature({ + hash: originalMsgHash, + signature: sig, + contract: accountContract, + }); + if (isValid) { + return sig; + } + throw new Error("Failed to verify signature"); + } else { + const deployTx = prepareCreateAccount({ + factoryContract, + adminAddress: options.personalAccount.address, + accountSalt: options.overrides?.accountSalt, + createAccountOverride: options.overrides?.createAccount, + }); + if (!deployTx) { + throw new Error("Create account override not provided"); + } + const initCode = await encode(deployTx); + const erc6492Sig = serializeErc6492Signature({ + address: factoryContract.address, + data: initCode, + signature: sig, + }); - // check if the signature is valid - const isValid = await verifyHash({ - hash: originalMsgHash, - signature: erc6492Sig, - address: accountContract.address, - chain: accountContract.chain, - client: accountContract.client, - }); + // check if the signature is valid + const isValid = await verifyHash({ + hash: originalMsgHash, + signature: erc6492Sig, + address: accountContract.address, + chain: accountContract.chain, + client: accountContract.client, + }); - if (isValid) { - return erc6492Sig; + if (isValid) { + return erc6492Sig; + } + throw new Error("Unable to verify ERC-6492 signature after signing."); } - throw new Error( - "Unable to verify signature on smart account, please make sure the admin wallet has permissions and the signature is valid.", - ); } -export async function deployAndSignTypedData< +export async function smartAccountSignTypedData< const typedData extends ox__TypedData.TypedData | Record, primaryType extends keyof typedData | "EIP712Domain" = keyof typedData, >({ @@ -142,37 +166,50 @@ export async function deployAndSignTypedData< sig = await options.personalAccount.signTypedData(typedData); } - const deployTx = prepareCreateAccount({ - factoryContract, - adminAddress: options.personalAccount.address, - accountSalt: options.overrides?.accountSalt, - createAccountOverride: options.overrides?.createAccount, - }); - if (!deployTx) { - throw new Error("Create account override not provided"); - } - const initCode = await encode(deployTx); - const erc6492Sig = serializeErc6492Signature({ - address: factoryContract.address, - data: initCode, - signature: sig, - }); + const isDeployed = await isContractDeployed(accountContract); + if (isDeployed) { + const isValid = await verifyEip1271Signature({ + hash: originalMsgHash, + signature: sig, + contract: accountContract, + }); + if (isValid) { + return sig; + } + throw new Error("Failed to verify signature"); + } else { + const deployTx = prepareCreateAccount({ + factoryContract, + adminAddress: options.personalAccount.address, + accountSalt: options.overrides?.accountSalt, + createAccountOverride: options.overrides?.createAccount, + }); + if (!deployTx) { + throw new Error("Create account override not provided"); + } + const initCode = await encode(deployTx); + const erc6492Sig = serializeErc6492Signature({ + address: factoryContract.address, + data: initCode, + signature: sig, + }); - // check if the signature is valid - const isValid = await verifyHash({ - hash: originalMsgHash, - signature: erc6492Sig, - address: accountContract.address, - chain: accountContract.chain, - client: accountContract.client, - }); + // check if the signature is valid + const isValid = await verifyHash({ + hash: originalMsgHash, + signature: erc6492Sig, + address: accountContract.address, + chain: accountContract.chain, + client: accountContract.client, + }); - if (isValid) { - return erc6492Sig; + if (isValid) { + return erc6492Sig; + } + throw new Error( + "Unable to verify signature on smart account, please make sure the admin wallet has permissions and the signature is valid.", + ); } - throw new Error( - "Unable to verify signature on smart account, please make sure the admin wallet has permissions and the signature is valid.", - ); } export async function confirmContractDeployment(args: { @@ -229,3 +266,61 @@ async function checkFor712Factory({ return false; } } + +/** + * Deployes a smart account via a dummy transaction. + * + * @param args - Arguments for the deployment. + * @param args.smartAccount - The smart account to deploy. + * @param args.chain - The chain to deploy on. + * @param args.client - The client to use for the deployment. + * @param args.accountContract - The account contract to deploy. + * + * @example + * ```ts + * import { deploySmartAccount } from "thirdweb"; + * + * const account = await deploySmartAccount({ + * smartAccount, + * chain, + * client, + * accountContract, + * }); + * ``` + * + * @wallets + */ +export async function deploySmartAccount(args: { + smartAccount: Account; + chain: Chain; + client: ThirdwebClient; + accountContract: ThirdwebContract; +}) { + const { chain, client, smartAccount, accountContract } = args; + const isDeployed = await isContractDeployed(accountContract); + if (isDeployed) { + return; + } + + const [{ sendTransaction }, { prepareTransaction }] = await Promise.all([ + import("../../../transaction/actions/send-transaction.js"), + import("../../../transaction/prepare-transaction.js"), + ]); + const dummyTx = prepareTransaction({ + client: client, + chain: chain, + to: accountContract.address, + value: 0n, + gas: 50000n, // force gas to avoid simulation error + }); + const deployResult = await sendTransaction({ + transaction: dummyTx, + account: smartAccount, + }); + + await confirmContractDeployment({ + accountContract, + }); + + return deployResult; +} diff --git a/packages/thirdweb/src/wallets/smart/smart-wallet-integration-v07.test.ts b/packages/thirdweb/src/wallets/smart/smart-wallet-integration-v07.test.ts index 015bb6cf8b2..5edc3c11ed5 100644 --- a/packages/thirdweb/src/wallets/smart/smart-wallet-integration-v07.test.ts +++ b/packages/thirdweb/src/wallets/smart/smart-wallet-integration-v07.test.ts @@ -5,6 +5,7 @@ import { verifySignature } from "../../auth/verify-signature.js"; import { type ThirdwebContract, getContract } from "../../contract/contract.js"; import { parseEventLogs } from "../../event/actions/parse-logs.js"; +import { verifyEip1271Signature } from "../../auth/verify-hash.js"; import { verifyTypedData } from "../../auth/verify-typed-data.js"; import { sepolia } from "../../chains/chain-definitions/sepolia.js"; import { @@ -20,12 +21,17 @@ import { sendBatchTransaction } from "../../transaction/actions/send-batch-trans import { waitForReceipt } from "../../transaction/actions/wait-for-tx-receipt.js"; import { getAddress } from "../../utils/address.js"; import { isContractDeployed } from "../../utils/bytecode/is-contract-deployed.js"; +import { hashMessage } from "../../utils/hashing/hashMessage.js"; +import { hashTypedData } from "../../utils/hashing/hashTypedData.js"; import { sleep } from "../../utils/sleep.js"; import type { Account, Wallet } from "../interfaces/wallet.js"; import { generateAccount } from "../utils/generateAccount.js"; import { predictSmartAccountAddress } from "./lib/calls.js"; import { DEFAULT_ACCOUNT_FACTORY_V0_7 } from "./lib/constants.js"; -import { confirmContractDeployment } from "./lib/signing.js"; +import { + confirmContractDeployment, + deploySmartAccount, +} from "./lib/signing.js"; import { smartWallet } from "./smart-wallet.js"; let wallet: Wallet; @@ -95,6 +101,27 @@ describe.runIf(process.env.TW_SECRET_KEY)( expect(isValid).toEqual(true); }); + it("should use ERC-1271 signatures after deployment", async () => { + await deploySmartAccount({ + chain, + client, + smartAccount, + accountContract, + }); + await new Promise((resolve) => setTimeout(resolve, 1000)); // pause for a second to prevent race condition + + const signature = await smartAccount.signMessage({ + message: "hello world", + }); + + const isValid = await verifyEip1271Signature({ + hash: hashMessage("hello world"), + signature, + contract: accountContract, + }); + expect(isValid).toEqual(true); + }); + it("can sign typed data", async () => { const signature = await smartAccount.signTypedData(typedData.basic); const isValid = await verifyTypedData({ @@ -107,6 +134,27 @@ describe.runIf(process.env.TW_SECRET_KEY)( expect(isValid).toEqual(true); }); + it("should use ERC-1271 typed data signatures after deployment", async () => { + await deploySmartAccount({ + chain, + client, + smartAccount, + accountContract, + }); + + await new Promise((resolve) => setTimeout(resolve, 1000)); // pause for a second to prevent race condition + + const signature = await smartAccount.signTypedData(typedData.basic); + + const messageHash = hashTypedData(typedData.basic); + const isValid = await verifyEip1271Signature({ + signature, + hash: messageHash, + contract: accountContract, + }); + expect(isValid).toEqual(true); + }); + it("should revert on unsuccessful transactions", async () => { const tx = sendAndConfirmTransaction({ transaction: setContractURI({ diff --git a/packages/thirdweb/src/wallets/smart/smart-wallet-integration.test.ts b/packages/thirdweb/src/wallets/smart/smart-wallet-integration.test.ts index 012b936d319..bba41d26185 100644 --- a/packages/thirdweb/src/wallets/smart/smart-wallet-integration.test.ts +++ b/packages/thirdweb/src/wallets/smart/smart-wallet-integration.test.ts @@ -1,6 +1,7 @@ import { beforeAll, describe, expect, it } from "vitest"; import { TEST_CLIENT } from "../../../test/src/test-clients.js"; import { typedData } from "../../../test/src/typed-data.js"; +import { verifyEip1271Signature } from "../../auth/verify-hash.js"; import { verifySignature } from "../../auth/verify-signature.js"; import { verifyTypedData } from "../../auth/verify-typed-data.js"; import { arbitrumSepolia } from "../../chains/chain-definitions/arbitrum-sepolia.js"; @@ -21,10 +22,13 @@ import { sendTransaction } from "../../transaction/actions/send-transaction.js"; import { waitForReceipt } from "../../transaction/actions/wait-for-tx-receipt.js"; import { prepareTransaction } from "../../transaction/prepare-transaction.js"; import { isContractDeployed } from "../../utils/bytecode/is-contract-deployed.js"; +import { hashMessage } from "../../utils/hashing/hashMessage.js"; +import { hashTypedData } from "../../utils/hashing/hashTypedData.js"; import { sleep } from "../../utils/sleep.js"; import type { Account, Wallet } from "../interfaces/wallet.js"; import { generateAccount } from "../utils/generateAccount.js"; import { predictSmartAccountAddress } from "./lib/calls.js"; +import { deploySmartAccount } from "./lib/signing.js"; import { smartWallet } from "./smart-wallet.js"; let wallet: Wallet; @@ -93,6 +97,27 @@ describe.runIf(process.env.TW_SECRET_KEY).sequential( expect(isValid).toEqual(true); }); + it("should use ERC-1271 signatures after deployment", async () => { + await deploySmartAccount({ + chain, + client, + smartAccount, + accountContract, + }); + await new Promise((resolve) => setTimeout(resolve, 1000)); // pause for a second to prevent race condition + + const signature = await smartAccount.signMessage({ + message: "hello world", + }); + + const isValid = await verifyEip1271Signature({ + hash: hashMessage("hello world"), + signature, + contract: accountContract, + }); + expect(isValid).toEqual(true); + }); + it("can sign typed data", async () => { const signature = await smartAccount.signTypedData(typedData.basic); const isValid = await verifyTypedData({ @@ -105,6 +130,27 @@ describe.runIf(process.env.TW_SECRET_KEY).sequential( expect(isValid).toEqual(true); }); + it("should use ERC-1271 typed data signatures after deployment", async () => { + await deploySmartAccount({ + chain, + client, + smartAccount, + accountContract, + }); + + await new Promise((resolve) => setTimeout(resolve, 1000)); // pause for a second to prevent race condition + + const signature = await smartAccount.signTypedData(typedData.basic); + + const messageHash = hashTypedData(typedData.basic); + const isValid = await verifyEip1271Signature({ + signature, + hash: messageHash, + contract: accountContract, + }); + expect(isValid).toEqual(true); + }); + it("should revert on unsuccessful transactions", async () => { const tx = sendAndConfirmTransaction({ transaction: setContractURI({ @@ -183,7 +229,7 @@ describe.runIf(process.env.TW_SECRET_KEY).sequential( expect(result.status).toEqual("success"); }); - it("can sign and verify 1271 with replay protection", async () => { + it("can sign and verify with replay protection", async () => { const message = "hello world"; const signature = await smartAccount.signMessage({ message }); const isValidV1 = await verifySignature({ From 7846be4a9acaa244c6389946c0c2bff841011376 Mon Sep 17 00:00:00 2001 From: gregfromstl Date: Mon, 6 Jan 2025 14:12:04 -1000 Subject: [PATCH 2/3] fix(sdk): ox cjs imports --- .../thirdweb/src/utils/abi/decodeError.ts | 9 ++++--- .../src/utils/abi/decodeFunctionData.ts | 12 +++++---- .../src/utils/abi/decodeFunctionResult.ts | 12 +++++---- .../thirdweb/src/utils/hashing/hashMessage.ts | 2 +- .../src/wallets/smart/presets/7579.ts | 25 ++++++++++--------- 5 files changed, 33 insertions(+), 27 deletions(-) diff --git a/packages/thirdweb/src/utils/abi/decodeError.ts b/packages/thirdweb/src/utils/abi/decodeError.ts index 697b3282399..21947945115 100644 --- a/packages/thirdweb/src/utils/abi/decodeError.ts +++ b/packages/thirdweb/src/utils/abi/decodeError.ts @@ -1,4 +1,5 @@ -import { type Abi, AbiError } from "ox"; +import type * as ox__Abi from "ox/Abi"; +import * as ox__AbiError from "ox/AbiError"; import { resolveContractAbi } from "../../contract/actions/resolve-abi.js"; import type { ThirdwebContract } from "../../contract/contract.js"; import type { Hex } from "../encoding/hex.js"; @@ -17,7 +18,7 @@ import type { Hex } from "../encoding/hex.js"; * * @utils */ -export async function decodeError(options: { +export async function decodeError(options: { contract: ThirdwebContract; data: Hex; }) { @@ -31,6 +32,6 @@ export async function decodeError(options: { `No ABI found for contract ${contract.address} on chain ${contract.chain.id}`, ); } - const abiError = AbiError.fromAbi(abi, data) as AbiError.AbiError; - return AbiError.decode(abiError, data); + const abiError = ox__AbiError.fromAbi(abi, data) as ox__AbiError.AbiError; + return ox__AbiError.decode(abiError, data); } diff --git a/packages/thirdweb/src/utils/abi/decodeFunctionData.ts b/packages/thirdweb/src/utils/abi/decodeFunctionData.ts index 100b6256470..b1d651edbe9 100644 --- a/packages/thirdweb/src/utils/abi/decodeFunctionData.ts +++ b/packages/thirdweb/src/utils/abi/decodeFunctionData.ts @@ -1,4 +1,6 @@ -import { type Abi, AbiFunction, type Hex } from "ox"; +import type * as ox__Abi from "ox/Abi"; +import * as ox__AbiFunction from "ox/AbiFunction"; +import type * as ox__Hex from "ox/Hex"; import { resolveContractAbi } from "../../contract/actions/resolve-abi.js"; import type { ThirdwebContract } from "../../contract/contract.js"; @@ -16,9 +18,9 @@ import type { ThirdwebContract } from "../../contract/contract.js"; * * @utils */ -export async function decodeFunctionData(options: { +export async function decodeFunctionData(options: { contract: ThirdwebContract; - data: Hex.Hex; + data: ox__Hex.Hex; }) { const { contract, data } = options; let abi = contract?.abi; @@ -30,6 +32,6 @@ export async function decodeFunctionData(options: { `No ABI found for contract ${contract.address} on chain ${contract.chain.id}`, ); } - const abiFunction = AbiFunction.fromAbi(abi, data); - return AbiFunction.decodeData(abiFunction, data); + const abiFunction = ox__AbiFunction.fromAbi(abi, data); + return ox__AbiFunction.decodeData(abiFunction, data); } diff --git a/packages/thirdweb/src/utils/abi/decodeFunctionResult.ts b/packages/thirdweb/src/utils/abi/decodeFunctionResult.ts index 6e40e553c6e..02057cff8bc 100644 --- a/packages/thirdweb/src/utils/abi/decodeFunctionResult.ts +++ b/packages/thirdweb/src/utils/abi/decodeFunctionResult.ts @@ -1,4 +1,6 @@ -import { type Abi, AbiFunction, type Hex } from "ox"; +import type * as ox__Abi from "ox/Abi"; +import * as ox__AbiFunction from "ox/AbiFunction"; +import type * as ox__Hex from "ox/Hex"; import { resolveContractAbi } from "../../contract/actions/resolve-abi.js"; import type { ThirdwebContract } from "../../contract/contract.js"; @@ -16,9 +18,9 @@ import type { ThirdwebContract } from "../../contract/contract.js"; * * @utils */ -export async function decodeFunctionResult(options: { +export async function decodeFunctionResult(options: { contract: ThirdwebContract; - data: Hex.Hex; + data: ox__Hex.Hex; }) { const { contract, ...rest } = options; let abi = contract?.abi; @@ -30,6 +32,6 @@ export async function decodeFunctionResult(options: { `No ABI found for contract ${contract.address} on chain ${contract.chain.id}`, ); } - const abiFunction = AbiFunction.fromAbi(abi, rest.data); - return AbiFunction.decodeResult(abiFunction, rest.data); + const abiFunction = ox__AbiFunction.fromAbi(abi, rest.data); + return ox__AbiFunction.decodeResult(abiFunction, rest.data); } diff --git a/packages/thirdweb/src/utils/hashing/hashMessage.ts b/packages/thirdweb/src/utils/hashing/hashMessage.ts index f5004dba050..5cde10c28e8 100644 --- a/packages/thirdweb/src/utils/hashing/hashMessage.ts +++ b/packages/thirdweb/src/utils/hashing/hashMessage.ts @@ -1,4 +1,4 @@ -import { Bytes as ox__Bytes } from "ox"; +import * as ox__Bytes from "ox/Bytes"; import type { Hex } from "../encoding/hex.js"; import { stringToBytes, toBytes } from "../encoding/to-bytes.js"; import type { SignableMessage } from "../types.js"; diff --git a/packages/thirdweb/src/wallets/smart/presets/7579.ts b/packages/thirdweb/src/wallets/smart/presets/7579.ts index 9c0916c29cf..93adc23aac2 100644 --- a/packages/thirdweb/src/wallets/smart/presets/7579.ts +++ b/packages/thirdweb/src/wallets/smart/presets/7579.ts @@ -1,4 +1,5 @@ -import { AbiParameters, Hex } from "ox"; +import * as ox__AbiParameters from "ox/AbiParameters"; +import * as ox__Hex from "ox/Hex"; import { serializeErc6492Signature } from "../../../auth/serialize-erc6492-signature.js"; import { verifyHash } from "../../../auth/verify-hash.js"; import { ZERO_ADDRESS } from "../../../constants/addresses.js"; @@ -56,9 +57,9 @@ export type ERC7579Config = SmartWalletOptions & { export function erc7579(options: ERC7579Config): SmartWalletOptions { const saltHex = options.overrides?.accountSalt && - Hex.validate(options.overrides.accountSalt) + ox__Hex.validate(options.overrides.accountSalt) ? options.overrides.accountSalt - : Hex.fromString(options.overrides?.accountSalt ?? ""); + : ox__Hex.fromString(options.overrides?.accountSalt ?? ""); const defaultValidator = getAddress(options.validatorAddress); const modularAccountOptions: SmartWalletOptions = { ...options, @@ -75,7 +76,7 @@ export function erc7579(options: ERC7579Config): SmartWalletOptions { { moduleTypeId: 1n, // validator type id module: defaultValidator, - initData: Hex.fromString(""), + initData: ox__Hex.fromString(""), }, ]; return { @@ -99,8 +100,8 @@ export function erc7579(options: ERC7579Config): SmartWalletOptions { contract: accountContract, async asyncParams() { return { - mode: Hex.padRight("0x00", 32), // single execution - executionCalldata: AbiParameters.encodePacked( + mode: ox__Hex.padRight("0x00", 32), // single execution + executionCalldata: ox__AbiParameters.encodePacked( ["address", "uint256", "bytes"], [ transaction.to || ZERO_ADDRESS, @@ -117,8 +118,8 @@ export function erc7579(options: ERC7579Config): SmartWalletOptions { contract: accountContract, async asyncParams() { return { - mode: Hex.padRight("0x01", 32), // batch execution - executionCalldata: AbiParameters.encode( + mode: ox__Hex.padRight("0x01", 32), // batch execution + executionCalldata: ox__AbiParameters.encode( [ { type: "tuple[]", @@ -152,10 +153,10 @@ export function erc7579(options: ERC7579Config): SmartWalletOptions { sender: accountContract.address, }); // TODO (msa) - could be different if validator for the deployed account is different - const withValidator = Hex.from( - `${defaultValidator}${Hex.fromNumber(entryPointNonce).slice(42)}`, + const withValidator = ox__Hex.from( + `${defaultValidator}${ox__Hex.fromNumber(entryPointNonce).slice(42)}`, ); - return Hex.toBigInt(withValidator); + return ox__Hex.toBigInt(withValidator); }, async signMessage(options) { const { accountContract, factoryContract, adminAccount, message } = @@ -201,7 +202,7 @@ async function generateSignature(options: { accountContract: ThirdwebContract; factoryContract: ThirdwebContract; adminAccount: Account; - originalMsgHash: Hex.Hex; + originalMsgHash: ox__Hex.Hex; defaultValidator: string; createAccount: ( factoryContract: ThirdwebContract, From 720f29031654b2b746de31eb661650953cd24f92 Mon Sep 17 00:00:00 2001 From: gregfromstl Date: Mon, 6 Jan 2025 14:43:45 -1000 Subject: [PATCH 3/3] fix(sdk): smart account transactions on non-original chain --- packages/thirdweb/src/wallets/smart/index.ts | 31 ++++++++++---------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/packages/thirdweb/src/wallets/smart/index.ts b/packages/thirdweb/src/wallets/smart/index.ts index 7afdbf793c4..becc9004050 100644 --- a/packages/thirdweb/src/wallets/smart/index.ts +++ b/packages/thirdweb/src/wallets/smart/index.ts @@ -214,7 +214,7 @@ async function createSmartAccount( } } - const { accountContract } = options; + let accountContract = options.accountContract; const account: Account = { address: getAddress(accountContract.address), async sendTransaction(transaction: SendTransactionOption) { @@ -241,21 +241,17 @@ async function createSmartAccount( paymasterOverride = options.overrides?.paymaster; } - const accountContractForTransaction = (() => { - // If this transaction is for a different chain than the initial one, get the account contract for that chain - if (transaction.chainId !== accountContract.chain.id) { - return getContract({ - address: account.address, - chain: getCachedChain(transaction.chainId), - client: options.client, - }); - } - // Default to the existing account contract - return accountContract; - })(); + // If this transaction is for a different chain than the initial one, get the account contract for that chain + if (transaction.chainId !== accountContract.chain.id) { + accountContract = getContract({ + address: account.address, + chain: getCachedChain(transaction.chainId), + client: options.client, + }); + } const executeTx = prepareExecute({ - accountContract: accountContractForTransaction, + accountContract: accountContract, transaction, executeOverride: options.overrides?.execute, }); @@ -264,6 +260,7 @@ async function createSmartAccount( options: { ...options, chain: getCachedChain(transaction.chainId), + accountContract, overrides: { ...options.overrides, paymaster: paymasterOverride, @@ -279,7 +276,11 @@ async function createSmartAccount( }); return _sendUserOp({ executeTx, - options, + options: { + ...options, + chain: getCachedChain(transactions[0]?.chainId ?? options.chain.id), + accountContract, + }, }); }, async signMessage({ message }: { message: SignableMessage }) {