diff --git a/.changeset/small-moons-add.md b/.changeset/small-moons-add.md
new file mode 100644
index 00000000000..b4b4dfafe5c
--- /dev/null
+++ b/.changeset/small-moons-add.md
@@ -0,0 +1,5 @@
+---
+"thirdweb": patch
+---
+
+Support EIP7702 execution for ecosystem wallets
diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/ecosystem/[slug]/(active)/configuration/components/client/EcosystemPermissionsPage.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/ecosystem/[slug]/(active)/configuration/components/client/EcosystemPermissionsPage.tsx
index 776dc75e019..cfd9566b951 100644
--- a/apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/ecosystem/[slug]/(active)/configuration/components/client/EcosystemPermissionsPage.tsx
+++ b/apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/ecosystem/[slug]/(active)/configuration/components/client/EcosystemPermissionsPage.tsx
@@ -1,4 +1,5 @@
"use client";
+import { getClientThirdwebClient } from "@/constants/thirdweb-client.client";
import { useEcosystem } from "../../../hooks/use-ecosystem";
import { AuthOptionsSection } from "../server/auth-options-section";
import { EcosystemPartnersSection } from "../server/ecosystem-partners-section";
@@ -18,6 +19,8 @@ export function EcosystemPermissionsPage({
teamIdOrSlug: params.team_slug,
});
+ const client = getClientThirdwebClient({ jwt: authToken, teamId });
+
return (
{ecosystem?.permission === "PARTNER_WHITELIST" && (
({
defaultValues: {
authOptions: ecosystem.authOptions || [],
@@ -71,6 +80,7 @@ export function AuthOptionsForm({
DEFAULT_ACCOUNT_FACTORY_V0_6
? "v0.6"
: "custom",
+ executionMode: ecosystem.smartAccountOptions?.executionMode || "EIP4337",
customAccountFactoryAddress:
ecosystem.smartAccountOptions?.accountFactoryAddress || "",
},
@@ -97,6 +107,7 @@ export function AuthOptionsForm({
.optional(),
accountFactoryType: z.enum(["v0.6", "v0.7", "custom"]),
customAccountFactoryAddress: z.string().optional(),
+ executionMode: z.enum(["EIP4337", "EIP7702"]),
})
.refine(
(data) => {
@@ -203,6 +214,7 @@ export function AuthOptionsForm({
defaultChainId: data.defaultChainId,
sponsorGas: data.sponsorGas,
accountFactoryAddress,
+ executionMode: data.executionMode,
};
}
@@ -437,26 +449,71 @@ export function AuthOptionsForm({
/>
{form.watch("useSmartAccount") && (
)}
diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/ecosystem/[slug]/(active)/configuration/components/server/auth-options-section.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/ecosystem/[slug]/(active)/configuration/components/server/auth-options-section.tsx
index f4c0fa03931..672790a78d2 100644
--- a/apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/ecosystem/[slug]/(active)/configuration/components/server/auth-options-section.tsx
+++ b/apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/ecosystem/[slug]/(active)/configuration/components/server/auth-options-section.tsx
@@ -1,3 +1,4 @@
+import type { ThirdwebClient } from "thirdweb";
import type { Ecosystem } from "../../../../../types";
import {
AuthOptionsForm,
@@ -8,7 +9,13 @@ export function AuthOptionsSection({
ecosystem,
authToken,
teamId,
-}: { ecosystem?: Ecosystem; authToken: string; teamId: string }) {
+ client,
+}: {
+ ecosystem?: Ecosystem;
+ authToken: string;
+ teamId: string;
+ client: ThirdwebClient;
+}) {
return (
{ecosystem ? (
@@ -16,6 +23,7 @@ export function AuthOptionsSection({
ecosystem={ecosystem}
authToken={authToken}
teamId={teamId}
+ client={client}
/>
) : (
diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/ecosystem/types.ts b/apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/ecosystem/types.ts
index f2d0f66d3be..5db9b603617 100644
--- a/apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/ecosystem/types.ts
+++ b/apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/ecosystem/types.ts
@@ -38,7 +38,8 @@ export type Ecosystem = {
smartAccountOptions?: {
defaultChainId: number;
sponsorGas: boolean;
- accountFactoryAddress: string;
+ accountFactoryAddress?: string;
+ executionMode?: "EIP4337" | "EIP7702";
} | null;
url: string;
status: "active" | "requested" | "paymentFailed";
diff --git a/apps/playground-web/src/components/in-app-wallet/ecosystem.tsx b/apps/playground-web/src/components/in-app-wallet/ecosystem.tsx
index cecf6c3d4ff..9324c5d0a56 100644
--- a/apps/playground-web/src/components/in-app-wallet/ecosystem.tsx
+++ b/apps/playground-web/src/components/in-app-wallet/ecosystem.tsx
@@ -8,7 +8,7 @@ const getEcosystemWallet = () => {
process.env.NEXT_PUBLIC_IN_APP_WALLET_URL?.endsWith(".thirdweb-dev.com")
) {
// dev ecosystem
- return ecosystemWallet("ecosystem.catlovers");
+ return ecosystemWallet("ecosystem.catfans");
}
// prod ecosystem
return ecosystemWallet("ecosystem.thirdweb-engs", {
diff --git a/packages/thirdweb/src/wallets/ecosystem/get-ecosystem-wallet-auth-options.ts b/packages/thirdweb/src/wallets/ecosystem/get-ecosystem-wallet-auth-options.ts
index ac8ab8c37cb..8ea7d776dae 100644
--- a/packages/thirdweb/src/wallets/ecosystem/get-ecosystem-wallet-auth-options.ts
+++ b/packages/thirdweb/src/wallets/ecosystem/get-ecosystem-wallet-auth-options.ts
@@ -15,7 +15,8 @@ type EcosystemOptions = {
type SmartAccountOptions = {
defaultChainId: number;
sponsorGas: boolean;
- accountFactoryAddress: string;
+ accountFactoryAddress?: string;
+ executionMode?: "EIP4337" | "EIP7702";
};
/**
diff --git a/packages/thirdweb/src/wallets/in-app/core/wallet/in-app-core.ts b/packages/thirdweb/src/wallets/in-app/core/wallet/in-app-core.ts
index 209fb344f8f..9c3332f8a5e 100644
--- a/packages/thirdweb/src/wallets/in-app/core/wallet/in-app-core.ts
+++ b/packages/thirdweb/src/wallets/in-app/core/wallet/in-app-core.ts
@@ -57,6 +57,47 @@ export function createInAppWallet(args: {
let client: ThirdwebClient | undefined;
let authToken: string | null = null;
+ const resolveSmartAccountOptionsFromEcosystem = async (options: {
+ chain?: Chain;
+ }) => {
+ if (ecosystem) {
+ const ecosystemOptions = await getEcosystemInfo(ecosystem.id);
+ const smartAccountOptions = ecosystemOptions?.smartAccountOptions;
+ if (smartAccountOptions) {
+ const executionMode =
+ ecosystemOptions.smartAccountOptions.executionMode;
+ if (executionMode === "EIP7702") {
+ createOptions = {
+ ...createOptions,
+ executionMode: {
+ mode: "EIP7702",
+ sponsorGas: smartAccountOptions.sponsorGas,
+ },
+ };
+ } else {
+ // default to 4337
+ const { defaultChainId } = ecosystemOptions.smartAccountOptions;
+ const preferredChain =
+ options.chain ??
+ (defaultChainId ? getCachedChain(defaultChainId) : undefined);
+ if (!preferredChain) {
+ throw new Error(
+ `A chain must be provided either via 'chain' in connect options or 'defaultChainId' in ecosystem configuration. Please pass it via connect() or update the ecosystem configuration.`,
+ );
+ }
+ createOptions = {
+ ...createOptions,
+ smartAccount: {
+ chain: preferredChain,
+ sponsorGas: smartAccountOptions.sponsorGas,
+ factoryAddress: smartAccountOptions.accountFactoryAddress,
+ },
+ };
+ }
+ }
+ }
+ };
+
return {
id: walletId,
getAuthToken: () => authToken,
@@ -80,30 +121,7 @@ export function createInAppWallet(args: {
ecosystem,
);
- if (ecosystem) {
- const ecosystemOptions = await getEcosystemInfo(ecosystem.id);
- const smartAccountOptions = ecosystemOptions?.smartAccountOptions;
- if (smartAccountOptions) {
- const { defaultChainId } = ecosystemOptions.smartAccountOptions;
- const preferredChain =
- options.chain ??
- (defaultChainId ? getCachedChain(defaultChainId) : undefined);
- if (!preferredChain) {
- throw new Error(
- `A chain must be provided either via 'chain' in connect options or 'defaultChainId' in ecosystem configuration. Please pass it via connect() or update the ecosystem configuration.`,
- );
- }
-
- createOptions = {
- ...createOptions,
- smartAccount: {
- chain: preferredChain,
- sponsorGas: smartAccountOptions.sponsorGas,
- factoryAddress: smartAccountOptions.accountFactoryAddress,
- },
- };
- }
- }
+ await resolveSmartAccountOptionsFromEcosystem(options);
const {
account: connectedAccount,
@@ -140,30 +158,7 @@ export function createInAppWallet(args: {
ecosystem,
);
- if (ecosystem) {
- const ecosystemOptions = await getEcosystemInfo(ecosystem.id);
- const smartAccountOptions = ecosystemOptions?.smartAccountOptions;
- if (smartAccountOptions) {
- const { defaultChainId } = ecosystemOptions.smartAccountOptions;
- const preferredChain =
- options.chain ??
- (defaultChainId ? getCachedChain(defaultChainId) : undefined);
- if (!preferredChain) {
- throw new Error(
- `A chain must be provided either via 'chain' in connect options or 'defaultChainId' in ecosystem configuration. Please pass it via connect() or update the ecosystem configuration.`,
- );
- }
-
- createOptions = {
- ...createOptions,
- smartAccount: {
- chain: preferredChain,
- sponsorGas: smartAccountOptions.sponsorGas,
- factoryAddress: smartAccountOptions.accountFactoryAddress,
- },
- };
- }
- }
+ await resolveSmartAccountOptionsFromEcosystem(options);
const {
account: connectedAccount,
@@ -212,7 +207,12 @@ export function createInAppWallet(args: {
emitter.emit("disconnect", undefined);
},
switchChain: async (newChain) => {
- if (createOptions?.smartAccount && client && account) {
+ if (
+ (createOptions?.smartAccount ||
+ createOptions?.executionMode?.mode === "EIP4337") &&
+ client &&
+ account
+ ) {
// if account abstraction is enabled, reconnect to smart account on the new chain
const { autoConnectInAppWallet } = await import("./index.js");
const connector = await getOrCreateInAppWalletConnector(
@@ -221,20 +221,7 @@ export function createInAppWallet(args: {
ecosystem,
);
- if (ecosystem) {
- const ecosystemOptions = await getEcosystemInfo(ecosystem.id);
- const smartAccountOptions = ecosystemOptions?.smartAccountOptions;
- if (smartAccountOptions) {
- createOptions = {
- ...createOptions,
- smartAccount: {
- chain: newChain,
- sponsorGas: smartAccountOptions.sponsorGas,
- factoryAddress: smartAccountOptions.accountFactoryAddress,
- },
- };
- }
- }
+ await resolveSmartAccountOptionsFromEcosystem({ chain: newChain });
const {
account: connectedAccount,