Skip to content

Commit cadb36f

Browse files
committed
feat(sdk): makes chain optional in smart wallet config
1 parent eded4ff commit cadb36f

File tree

8 files changed

+53
-14
lines changed

8 files changed

+53
-14
lines changed

.changeset/six-baboons-begin.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
"thirdweb": minor
3+
---
4+
5+
Feature: Chain is no longer required for smart accounts
6+
7+
```ts
8+
import { smartWallet } from "thirdweb";
9+
10+
const wallet = smartWallet({
11+
sponsorGas: true, // enable sponsored transactions
12+
});
13+
```

packages/thirdweb/src/react/core/hooks/connection/ConnectButtonProps.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -948,8 +948,6 @@ export type ConnectButtonProps = {
948948
* ```tsx
949949
* <ConnectButton
950950
* accountAbstraction={{
951-
* factoryAddress: "0x123...",
952-
* chain: sepolia,
953951
* gasless: true;
954952
* }}
955953
* />

packages/thirdweb/src/react/web/ui/ConnectWallet/Modal/SmartWalletConnectUI.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,9 @@ function SmartWalletConnecting(props: {
129129
};
130130
}, [personalWallet]);
131131

132-
const wrongNetwork = personalWalletChainId !== smartWalletChain.id;
132+
const wrongNetwork =
133+
typeof smartWalletChain !== "undefined" &&
134+
personalWalletChainId !== smartWalletChain.id;
133135

134136
const [smartWalletConnectionStatus, setSmartWalletConnectionStatus] =
135137
useState<"connecting" | "connect-error" | "idle">("idle");

packages/thirdweb/src/wallets/smart/index.ts

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ import type {
5353
SmartWalletConnectionOptions,
5454
SmartWalletOptions,
5555
TokenPaymasterConfig,
56+
UserOpOptions,
5657
UserOperationV06,
5758
UserOperationV07,
5859
} from "./types.js";
@@ -92,7 +93,8 @@ export async function connectSmartAccount(
9293
}
9394

9495
const options = creationOptions;
95-
const chain = connectChain ?? options.chain;
96+
// Fallback to mainnet if no chain is provided (we only need this for pre-deploy signatures since transactions and deployments must define their own chain)
97+
const chain = connectChain ?? options.chain ?? getCachedChain(1);
9698

9799
// if factory is passed, but no entrypoint, try to resolve entrypoint from factory
98100
if (options.factoryAddress && !options.overrides?.entrypointAddress) {
@@ -265,16 +267,29 @@ async function createSmartAccount(
265267
});
266268
},
267269
async sendBatchTransaction(transactions: SendTransactionOption[]) {
270+
// The latter half of this OR is purely to satisfy TS
271+
if (transactions.length === 0 || typeof transactions[0] === "undefined") {
272+
throw new Error("You must provide at least one transaction in a batch");
273+
}
274+
275+
const chain = getCachedChain(transactions[0].chainId);
276+
if (transactions.some((tx) => tx.chainId !== chain.id)) {
277+
throw new Error(
278+
"All transactions in a batch must have the same chain ID",
279+
);
280+
}
281+
268282
const executeTx = prepareBatchExecute({
269283
accountContract,
270284
transactions,
271285
executeBatchOverride: options.overrides?.executeBatch,
272286
});
287+
273288
return _sendUserOp({
274289
executeTx,
275290
options: {
276291
...options,
277-
chain: getCachedChain(transactions[0]?.chainId ?? options.chain.id),
292+
chain,
278293
accountContract,
279294
},
280295
});
@@ -365,6 +380,7 @@ async function approveERC20(args: {
365380
executeTx,
366381
options: {
367382
...options,
383+
chain: getCachedChain(transaction.chainId),
368384
overrides: {
369385
...options.overrides,
370386
tokenPaymaster: undefined,
@@ -460,7 +476,7 @@ function createZkSyncAccount(args: {
460476

461477
async function _sendUserOp(args: {
462478
executeTx: PreparedTransaction;
463-
options: SmartAccountOptions;
479+
options: UserOpOptions;
464480
}): Promise<WaitForReceiptOptions> {
465481
const { executeTx, options } = args;
466482
try {

packages/thirdweb/src/wallets/smart/lib/signing.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ export async function smartAccountSignMessage({
5757
domain: {
5858
name: "Account",
5959
version: "1",
60-
chainId: options.chain.id,
60+
chainId: options.chain?.id ?? 1,
6161
verifyingContract: accountContract.address,
6262
},
6363
primaryType: "AccountMessage",
@@ -155,7 +155,7 @@ export async function smartAccountSignTypedData<
155155
domain: {
156156
name: "Account",
157157
version: "1",
158-
chainId: options.chain.id,
158+
chainId: options.chain?.id ?? 1,
159159
verifyingContract: accountContract.address,
160160
},
161161
primaryType: "AccountMessage",

packages/thirdweb/src/wallets/smart/lib/userop.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -697,7 +697,7 @@ export async function createAndSignUserOp(options: {
697697
transactions: PreparedTransaction[];
698698
adminAccount: Account;
699699
client: ThirdwebClient;
700-
smartWalletOptions: SmartWalletOptions;
700+
smartWalletOptions: SmartWalletOptions & { chain: Chain };
701701
waitForDeployment?: boolean;
702702
}) {
703703
const config = options.smartWalletOptions;

packages/thirdweb/src/wallets/smart/smart-wallet.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ import type { SmartWalletOptions } from "./types.js";
4343
* import { sendTransaction } from "thirdweb";
4444
*
4545
* const wallet = smartWallet({
46-
* chain: sepolia,
4746
* sponsorGas: true, // enable sponsored transactions
4847
* });
4948
*
@@ -77,7 +76,7 @@ import type { SmartWalletOptions } from "./types.js";
7776
* import { sepolia } from "thirdweb/chains";
7877
*
7978
* const wallet = smartWallet({
80-
* chain: sepolia,
79+
* chain: sepolia, // specify a chain if your factory only exists on one chain
8180
* sponsorGas: true, // enable sponsored transactions
8281
* factoryAddress: "0x...", // custom factory address
8382
* });
@@ -94,7 +93,6 @@ import type { SmartWalletOptions } from "./types.js";
9493
* import { sepolia } from "thirdweb/chains";
9594
*
9695
* const wallet = smartWallet({
97-
* chain: sepolia,
9896
* sponsorGas: true, // enable sponsored transactions
9997
* factoryAddress: DEFAULT_ACCOUNT_FACTORY_V0_7, // 0.7 factory address
10098
* });

packages/thirdweb/src/wallets/smart/types.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ export type TokenPaymasterConfig = {
1919

2020
export type SmartWalletOptions = Prettify<
2121
{
22-
chain: Chain; // TODO consider making default chain optional
22+
chain?: Chain;
2323
factoryAddress?: string;
2424
overrides?: {
2525
bundlerUrl?: string;
@@ -81,7 +81,7 @@ export type SmartWalletOptions = Prettify<
8181
// internal type
8282
export type SmartAccountOptions = Prettify<
8383
Omit<SmartWalletOptions, "chain" | "gasless" | "sponsorGas"> & {
84-
chain: Chain;
84+
chain?: Chain;
8585
sponsorGas: boolean;
8686
personalAccount: Account;
8787
factoryContract: ThirdwebContract;
@@ -90,6 +90,18 @@ export type SmartAccountOptions = Prettify<
9090
}
9191
>;
9292

93+
export type UserOpOptions = Omit<
94+
SmartWalletOptions,
95+
"chain" | "gasless" | "sponsorGas"
96+
> & {
97+
chain: Chain;
98+
sponsorGas: boolean;
99+
personalAccount: Account;
100+
factoryContract: ThirdwebContract;
101+
accountContract: ThirdwebContract;
102+
client: ThirdwebClient;
103+
};
104+
93105
export type BundlerOptions = {
94106
bundlerUrl?: string;
95107
entrypointAddress?: string;

0 commit comments

Comments
 (0)