|
1 | 1 | import type * as ox__TypedData from "ox/TypedData";
|
2 | 2 | import { serializeErc6492Signature } from "../../../auth/serialize-erc6492-signature.js";
|
3 |
| -import { verifyHash } from "../../../auth/verify-hash.js"; |
| 3 | +import { |
| 4 | + verifyEip1271Signature, |
| 5 | + verifyHash, |
| 6 | +} from "../../../auth/verify-hash.js"; |
| 7 | +import type { Chain } from "../../../chains/types.js"; |
| 8 | +import type { ThirdwebClient } from "../../../client/client.js"; |
4 | 9 | import {
|
5 | 10 | type ThirdwebContract,
|
6 | 11 | getContract,
|
7 | 12 | } from "../../../contract/contract.js";
|
8 | 13 | import { encode } from "../../../transaction/actions/encode.js";
|
9 | 14 | import { readContract } from "../../../transaction/read-contract.js";
|
10 | 15 | import { encodeAbiParameters } from "../../../utils/abi/encodeAbiParameters.js";
|
| 16 | +import { isContractDeployed } from "../../../utils/bytecode/is-contract-deployed.js"; |
11 | 17 | import type { Hex } from "../../../utils/encoding/hex.js";
|
12 | 18 | import { hashMessage } from "../../../utils/hashing/hashMessage.js";
|
13 | 19 | import { hashTypedData } from "../../../utils/hashing/hashTypedData.js";
|
14 | 20 | import type { SignableMessage } from "../../../utils/types.js";
|
| 21 | +import type { Account } from "../../../wallets/interfaces/wallet.js"; |
15 | 22 | import type { SmartAccountOptions } from "../types.js";
|
16 | 23 | import { prepareCreateAccount } from "./calls.js";
|
17 | 24 |
|
18 |
| -export async function deployAndSignMessage({ |
| 25 | +/** |
| 26 | + * If the account is already deployed, generate an ERC-1271 signature. |
| 27 | + * If the account is not deployed, generate an ERC-6492 signature unless otherwise specified. |
| 28 | + * |
| 29 | + * @internal |
| 30 | + */ |
| 31 | +export async function smartAccountSignMessage({ |
19 | 32 | accountContract,
|
20 | 33 | factoryContract,
|
21 | 34 | options,
|
@@ -55,40 +68,51 @@ export async function deployAndSignMessage({
|
55 | 68 | sig = await options.personalAccount.signMessage({ message });
|
56 | 69 | }
|
57 | 70 |
|
58 |
| - const deployTx = prepareCreateAccount({ |
59 |
| - factoryContract, |
60 |
| - adminAddress: options.personalAccount.address, |
61 |
| - accountSalt: options.overrides?.accountSalt, |
62 |
| - createAccountOverride: options.overrides?.createAccount, |
63 |
| - }); |
64 |
| - if (!deployTx) { |
65 |
| - throw new Error("Create account override not provided"); |
66 |
| - } |
67 |
| - const initCode = await encode(deployTx); |
68 |
| - const erc6492Sig = serializeErc6492Signature({ |
69 |
| - address: factoryContract.address, |
70 |
| - data: initCode, |
71 |
| - signature: sig, |
72 |
| - }); |
| 71 | + const isDeployed = await isContractDeployed(accountContract); |
| 72 | + if (isDeployed) { |
| 73 | + const isValid = await verifyEip1271Signature({ |
| 74 | + hash: originalMsgHash, |
| 75 | + signature: sig, |
| 76 | + contract: accountContract, |
| 77 | + }); |
| 78 | + if (isValid) { |
| 79 | + return sig; |
| 80 | + } |
| 81 | + throw new Error("Failed to verify signature"); |
| 82 | + } else { |
| 83 | + const deployTx = prepareCreateAccount({ |
| 84 | + factoryContract, |
| 85 | + adminAddress: options.personalAccount.address, |
| 86 | + accountSalt: options.overrides?.accountSalt, |
| 87 | + createAccountOverride: options.overrides?.createAccount, |
| 88 | + }); |
| 89 | + if (!deployTx) { |
| 90 | + throw new Error("Create account override not provided"); |
| 91 | + } |
| 92 | + const initCode = await encode(deployTx); |
| 93 | + const erc6492Sig = serializeErc6492Signature({ |
| 94 | + address: factoryContract.address, |
| 95 | + data: initCode, |
| 96 | + signature: sig, |
| 97 | + }); |
73 | 98 |
|
74 |
| - // check if the signature is valid |
75 |
| - const isValid = await verifyHash({ |
76 |
| - hash: originalMsgHash, |
77 |
| - signature: erc6492Sig, |
78 |
| - address: accountContract.address, |
79 |
| - chain: accountContract.chain, |
80 |
| - client: accountContract.client, |
81 |
| - }); |
| 99 | + // check if the signature is valid |
| 100 | + const isValid = await verifyHash({ |
| 101 | + hash: originalMsgHash, |
| 102 | + signature: erc6492Sig, |
| 103 | + address: accountContract.address, |
| 104 | + chain: accountContract.chain, |
| 105 | + client: accountContract.client, |
| 106 | + }); |
82 | 107 |
|
83 |
| - if (isValid) { |
84 |
| - return erc6492Sig; |
| 108 | + if (isValid) { |
| 109 | + return erc6492Sig; |
| 110 | + } |
| 111 | + throw new Error("Unable to verify ERC-6492 signature after signing."); |
85 | 112 | }
|
86 |
| - throw new Error( |
87 |
| - "Unable to verify signature on smart account, please make sure the admin wallet has permissions and the signature is valid.", |
88 |
| - ); |
89 | 113 | }
|
90 | 114 |
|
91 |
| -export async function deployAndSignTypedData< |
| 115 | +export async function smartAccountSignTypedData< |
92 | 116 | const typedData extends ox__TypedData.TypedData | Record<string, unknown>,
|
93 | 117 | primaryType extends keyof typedData | "EIP712Domain" = keyof typedData,
|
94 | 118 | >({
|
@@ -142,37 +166,50 @@ export async function deployAndSignTypedData<
|
142 | 166 | sig = await options.personalAccount.signTypedData(typedData);
|
143 | 167 | }
|
144 | 168 |
|
145 |
| - const deployTx = prepareCreateAccount({ |
146 |
| - factoryContract, |
147 |
| - adminAddress: options.personalAccount.address, |
148 |
| - accountSalt: options.overrides?.accountSalt, |
149 |
| - createAccountOverride: options.overrides?.createAccount, |
150 |
| - }); |
151 |
| - if (!deployTx) { |
152 |
| - throw new Error("Create account override not provided"); |
153 |
| - } |
154 |
| - const initCode = await encode(deployTx); |
155 |
| - const erc6492Sig = serializeErc6492Signature({ |
156 |
| - address: factoryContract.address, |
157 |
| - data: initCode, |
158 |
| - signature: sig, |
159 |
| - }); |
| 169 | + const isDeployed = await isContractDeployed(accountContract); |
| 170 | + if (isDeployed) { |
| 171 | + const isValid = await verifyEip1271Signature({ |
| 172 | + hash: originalMsgHash, |
| 173 | + signature: sig, |
| 174 | + contract: accountContract, |
| 175 | + }); |
| 176 | + if (isValid) { |
| 177 | + return sig; |
| 178 | + } |
| 179 | + throw new Error("Failed to verify signature"); |
| 180 | + } else { |
| 181 | + const deployTx = prepareCreateAccount({ |
| 182 | + factoryContract, |
| 183 | + adminAddress: options.personalAccount.address, |
| 184 | + accountSalt: options.overrides?.accountSalt, |
| 185 | + createAccountOverride: options.overrides?.createAccount, |
| 186 | + }); |
| 187 | + if (!deployTx) { |
| 188 | + throw new Error("Create account override not provided"); |
| 189 | + } |
| 190 | + const initCode = await encode(deployTx); |
| 191 | + const erc6492Sig = serializeErc6492Signature({ |
| 192 | + address: factoryContract.address, |
| 193 | + data: initCode, |
| 194 | + signature: sig, |
| 195 | + }); |
160 | 196 |
|
161 |
| - // check if the signature is valid |
162 |
| - const isValid = await verifyHash({ |
163 |
| - hash: originalMsgHash, |
164 |
| - signature: erc6492Sig, |
165 |
| - address: accountContract.address, |
166 |
| - chain: accountContract.chain, |
167 |
| - client: accountContract.client, |
168 |
| - }); |
| 197 | + // check if the signature is valid |
| 198 | + const isValid = await verifyHash({ |
| 199 | + hash: originalMsgHash, |
| 200 | + signature: erc6492Sig, |
| 201 | + address: accountContract.address, |
| 202 | + chain: accountContract.chain, |
| 203 | + client: accountContract.client, |
| 204 | + }); |
169 | 205 |
|
170 |
| - if (isValid) { |
171 |
| - return erc6492Sig; |
| 206 | + if (isValid) { |
| 207 | + return erc6492Sig; |
| 208 | + } |
| 209 | + throw new Error( |
| 210 | + "Unable to verify signature on smart account, please make sure the admin wallet has permissions and the signature is valid.", |
| 211 | + ); |
172 | 212 | }
|
173 |
| - throw new Error( |
174 |
| - "Unable to verify signature on smart account, please make sure the admin wallet has permissions and the signature is valid.", |
175 |
| - ); |
176 | 213 | }
|
177 | 214 |
|
178 | 215 | export async function confirmContractDeployment(args: {
|
@@ -229,3 +266,61 @@ async function checkFor712Factory({
|
229 | 266 | return false;
|
230 | 267 | }
|
231 | 268 | }
|
| 269 | + |
| 270 | +/** |
| 271 | + * Deployes a smart account via a dummy transaction. |
| 272 | + * |
| 273 | + * @param args - Arguments for the deployment. |
| 274 | + * @param args.smartAccount - The smart account to deploy. |
| 275 | + * @param args.chain - The chain to deploy on. |
| 276 | + * @param args.client - The client to use for the deployment. |
| 277 | + * @param args.accountContract - The account contract to deploy. |
| 278 | + * |
| 279 | + * @example |
| 280 | + * ```ts |
| 281 | + * import { deploySmartAccount } from "thirdweb"; |
| 282 | + * |
| 283 | + * const account = await deploySmartAccount({ |
| 284 | + * smartAccount, |
| 285 | + * chain, |
| 286 | + * client, |
| 287 | + * accountContract, |
| 288 | + * }); |
| 289 | + * ``` |
| 290 | + * |
| 291 | + * @wallets |
| 292 | + */ |
| 293 | +export async function deploySmartAccount(args: { |
| 294 | + smartAccount: Account; |
| 295 | + chain: Chain; |
| 296 | + client: ThirdwebClient; |
| 297 | + accountContract: ThirdwebContract; |
| 298 | +}) { |
| 299 | + const { chain, client, smartAccount, accountContract } = args; |
| 300 | + const isDeployed = await isContractDeployed(accountContract); |
| 301 | + if (isDeployed) { |
| 302 | + return; |
| 303 | + } |
| 304 | + |
| 305 | + const [{ sendTransaction }, { prepareTransaction }] = await Promise.all([ |
| 306 | + import("../../../transaction/actions/send-transaction.js"), |
| 307 | + import("../../../transaction/prepare-transaction.js"), |
| 308 | + ]); |
| 309 | + const dummyTx = prepareTransaction({ |
| 310 | + client: client, |
| 311 | + chain: chain, |
| 312 | + to: accountContract.address, |
| 313 | + value: 0n, |
| 314 | + gas: 50000n, // force gas to avoid simulation error |
| 315 | + }); |
| 316 | + const deployResult = await sendTransaction({ |
| 317 | + transaction: dummyTx, |
| 318 | + account: smartAccount, |
| 319 | + }); |
| 320 | + |
| 321 | + await confirmContractDeployment({ |
| 322 | + accountContract, |
| 323 | + }); |
| 324 | + |
| 325 | + return deployResult; |
| 326 | +} |
0 commit comments