Skip to content

Commit 25d4cf0

Browse files
committed
Move /connect/account-abstraction to app router
1 parent d8ff07e commit 25d4cf0

File tree

12 files changed

+233
-206
lines changed

12 files changed

+233
-206
lines changed
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
"use client";
2+
3+
import { useDashboardRouter } from "@/lib/DashboardRouter";
4+
import type { ApiKey } from "@3rdweb-sdk/react/hooks/useApi";
5+
import { ApiKeysMenu } from "../../../../../components/settings/ApiKeys/Menu";
6+
7+
export function AccountAbstractionAPIKeysMenu(props: {
8+
apiKeys: Pick<ApiKey, "name" | "key">[];
9+
selectedAPIKey: Pick<ApiKey, "name" | "key">;
10+
}) {
11+
const router = useDashboardRouter();
12+
return (
13+
<ApiKeysMenu
14+
apiKeys={props.apiKeys}
15+
selectedKey={props.selectedAPIKey}
16+
onSelect={(key) => {
17+
router.push(`/dashboard/connect/account-abstraction/${key.key}`);
18+
}}
19+
/>
20+
);
21+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
"use client";
2+
3+
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
4+
import { CircleAlertIcon } from "lucide-react";
5+
import { useActiveWalletChain } from "thirdweb/react";
6+
import { isOpChainId } from "../../../../team/[team_slug]/[project_slug]/connect/account-abstraction/isOpChain";
7+
8+
export function OpChainAlert() {
9+
const chain = useActiveWalletChain();
10+
const isOpChain = chain?.id ? isOpChainId(chain.id) : false;
11+
12+
if (!isOpChain) {
13+
return null;
14+
}
15+
16+
return (
17+
<Alert variant="info">
18+
<CircleAlertIcon className="size-4" />
19+
<AlertTitle>Using the gas credits for OP chain paymaster</AlertTitle>
20+
<AlertDescription>
21+
Credits will automatically be applied to cover gas fees for any onchain
22+
activity across thirdweb services. <br />
23+
Eligible chains: OP Mainnet, Base, Zora, Frax, Mode.
24+
</AlertDescription>
25+
</Alert>
26+
);
27+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { TrackedLinkTW } from "@/components/ui/tracked-link";
2+
3+
export function PageHeader() {
4+
return (
5+
<div className="flex flex-col gap-1.5">
6+
<h1 className="font-semibold text-2xl tracking-tight lg:text-3xl">
7+
Account Abstraction
8+
</h1>
9+
10+
<p className="text-muted-foreground text-sm">
11+
Easily integrate Account abstraction (ERC-4337) compliant smart accounts
12+
into your apps.{" "}
13+
<TrackedLinkTW
14+
target="_blank"
15+
label="docs-wallets"
16+
category="smart-wallet"
17+
href="https://portal.thirdweb.com/wallets/smart-wallet"
18+
className="text-link-foreground hover:text-foreground"
19+
>
20+
View Documentation
21+
</TrackedLinkTW>
22+
</p>
23+
</div>
24+
);
25+
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import { AccountStatus } from "@3rdweb-sdk/react/hooks/useApi";
2+
import { SmartWalletsBillingAlert } from "components/settings/ApiKeys/Alerts";
3+
import { ConnectSDKCard } from "components/shared/ConnectSDKCard";
4+
import { SmartWallets } from "components/smart-wallets";
5+
import { redirect } from "next/navigation";
6+
import { getAccount } from "../../../../../account/settings/getAccount";
7+
import { AccountAbstractionAPIKeysMenu } from "../AccountAbstractionAPIKeysMenu";
8+
import { OpChainAlert } from "../OpChainAlert";
9+
import { PageHeader } from "../PageHeader";
10+
import { getAASupportedAPIKeys } from "../getAASupportedAPIKeys";
11+
12+
export default async function Page(props: {
13+
params: {
14+
clientId: string;
15+
};
16+
searchParams: {
17+
tab?: string;
18+
};
19+
}) {
20+
const { clientId } = props.params;
21+
const dashboardAccount = await getAccount();
22+
23+
if (!dashboardAccount) {
24+
redirect(
25+
`/login?next=${encodeURIComponent(
26+
`/dashboard/connect/account-abstraction/${clientId}`,
27+
)}`,
28+
);
29+
}
30+
31+
const apiKeys = await getAASupportedAPIKeys();
32+
const apiKey = apiKeys.find((key) => key.key === clientId);
33+
34+
if (!apiKey) {
35+
redirect("/dashboard/connect/account-abstraction");
36+
}
37+
38+
const hasSmartWalletsWithoutBilling = apiKeys.find((k) =>
39+
k.services?.find(
40+
(s) =>
41+
dashboardAccount.status !== AccountStatus.ValidPayment &&
42+
s.name === "bundler",
43+
),
44+
);
45+
46+
return (
47+
<div className="flex flex-col gap-10">
48+
<div className="flex flex-col content-start justify-between gap-4 lg:flex-row">
49+
<PageHeader />
50+
51+
<div>
52+
<AccountAbstractionAPIKeysMenu
53+
apiKeys={apiKeys.map((x) => ({
54+
name: x.name,
55+
key: x.key,
56+
}))}
57+
selectedAPIKey={apiKey}
58+
/>
59+
</div>
60+
</div>
61+
62+
{hasSmartWalletsWithoutBilling ? (
63+
<SmartWalletsBillingAlert />
64+
) : (
65+
<OpChainAlert />
66+
)}
67+
68+
<SmartWallets
69+
apiKeyServices={apiKey.services || []}
70+
trackingCategory="smart-wallet"
71+
defaultTab={props.searchParams.tab === "1" ? 1 : 0}
72+
/>
73+
74+
<ConnectSDKCard
75+
title="Get Started"
76+
description="Add account abstraction to your app with the Connect SDK."
77+
/>
78+
</div>
79+
);
80+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { getApiKeys } from "../../../../api/lib/getAPIKeys";
2+
3+
export async function getAASupportedAPIKeys() {
4+
return (await getApiKeys()).filter((key) => {
5+
return !!(key.services || []).find((srv) => srv.name === "bundler");
6+
});
7+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { ChakraProviderSetup } from "@/components/ChakraProviderSetup";
2+
import type { Metadata } from "next";
3+
import { getAbsoluteUrl } from "../../../../../lib/vercel-utils";
4+
5+
export default function Layout(props: { children: React.ReactNode }) {
6+
return <ChakraProviderSetup>{props.children}</ChakraProviderSetup>;
7+
}
8+
9+
const seo = {
10+
title: "The Complete Account Abstraction Toolkit | thirdweb",
11+
desc: "Add account abstraction to your web3 app & unlock powerful features for seamless onboarding, customizable transactions, & maximum security. Get started.",
12+
};
13+
14+
export const metadata: Metadata = {
15+
title: seo.title,
16+
description: seo.desc,
17+
openGraph: {
18+
title: seo.title,
19+
description: seo.desc,
20+
images: [
21+
{
22+
url: `${getAbsoluteUrl()}/assets/og-image/dashboard-wallets-smart-wallet.png`,
23+
width: 1200,
24+
height: 630,
25+
alt: seo.title,
26+
},
27+
],
28+
},
29+
};
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { redirect } from "next/navigation";
2+
import { NoApiKeys } from "../../../../../components/settings/ApiKeys/NoApiKeys";
3+
import { ConnectSDKCard } from "../../../../../components/shared/ConnectSDKCard";
4+
import { getAuthToken } from "../../../../api/lib/getAuthToken";
5+
import { PageHeader } from "./PageHeader";
6+
import { getAASupportedAPIKeys } from "./getAASupportedAPIKeys";
7+
8+
export default async function Page() {
9+
const authToken = getAuthToken();
10+
11+
if (!authToken) {
12+
redirect(
13+
`/login?next=${encodeURIComponent("/dashboard/connect/account-abstraction")}`,
14+
);
15+
}
16+
17+
const apiKeys = await getAASupportedAPIKeys();
18+
const firstKey = apiKeys[0];
19+
20+
if (firstKey) {
21+
redirect(`/dashboard/connect/account-abstraction/${firstKey.key}`);
22+
}
23+
24+
return (
25+
<div>
26+
<PageHeader />
27+
<div className="h-8" />
28+
<NoApiKeys service="Account Abstraction" />
29+
<div className="h-10" />
30+
<ConnectSDKCard
31+
title="Get Started"
32+
description="Add account abstraction to your app with the Connect SDK."
33+
/>
34+
</div>
35+
);
36+
}

apps/dashboard/src/components/settings/ApiKeys/Alerts.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
"use client";
2+
13
import {
24
Alert,
35
AlertDescription,

apps/dashboard/src/components/smart-wallets/index.tsx

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,24 @@
1-
"use state";
1+
"use client";
22

33
import { TabButtons } from "@/components/ui/tabs";
44
import type { ApiKeyService } from "@3rdweb-sdk/react/hooks/useApi";
5-
import { useSearchParams } from "next/navigation";
65
import { useState } from "react";
76
import { AccountFactories } from "./AccountFactories";
87
import { AccountAbstractionSettingsPage } from "./SponsorshipPolicies";
98

109
interface SmartWalletsProps {
1110
apiKeyServices: ApiKeyService[];
1211
trackingCategory: string;
12+
defaultTab: 0 | 1;
1313
}
1414

1515
export const SmartWallets: React.FC<SmartWalletsProps> = ({
1616
apiKeyServices,
1717
trackingCategory,
18+
defaultTab,
1819
}) => {
19-
const searchParams = useSearchParams();
20-
const defaultTabIndex = Number.parseInt(searchParams?.get("tab") || "1");
2120
const [selectedTab, setSelectedTab] = useState<"factories" | "config">(
22-
defaultTabIndex === 1 ? "config" : "factories",
21+
defaultTab === 0 ? "config" : "factories",
2322
);
2423

2524
return (

apps/dashboard/src/hooks/useLocalStorage.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
"use client";
2+
13
import { useEffect, useState } from "react";
24
import { isBrowser } from "utils/isBrowser";
35

0 commit comments

Comments
 (0)