Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
"use client";

import { useDashboardRouter } from "@/lib/DashboardRouter";
import type { ApiKey } from "@3rdweb-sdk/react/hooks/useApi";
import { ApiKeysMenu } from "../../../../../components/settings/ApiKeys/Menu";

export function AccountAbstractionAPIKeysMenu(props: {
apiKeys: Pick<ApiKey, "name" | "key">[];
selectedAPIKey: Pick<ApiKey, "name" | "key">;
}) {
const router = useDashboardRouter();
return (
<ApiKeysMenu
apiKeys={props.apiKeys}
selectedKey={props.selectedAPIKey}
onSelect={(key) => {
router.push(`/dashboard/connect/account-abstraction/${key.key}`);
}}
/>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
"use client";

import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
import { CircleAlertIcon } from "lucide-react";
import { useActiveWalletChain } from "thirdweb/react";
import { isOpChainId } from "../../../../team/[team_slug]/[project_slug]/connect/account-abstraction/isOpChain";

export function OpChainAlert() {
const chain = useActiveWalletChain();
const isOpChain = chain?.id ? isOpChainId(chain.id) : false;

if (!isOpChain) {
return null;
}

return (
<Alert variant="info">
<CircleAlertIcon className="size-4" />
<AlertTitle>Using the gas credits for OP chain paymaster</AlertTitle>
<AlertDescription>
Credits will automatically be applied to cover gas fees for any onchain
activity across thirdweb services. <br />
Eligible chains: OP Mainnet, Base, Zora, Frax, Mode.
</AlertDescription>
</Alert>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { TrackedLinkTW } from "@/components/ui/tracked-link";

export function PageHeader() {
return (
<div className="flex flex-col gap-1.5">
<h1 className="font-semibold text-2xl tracking-tight lg:text-3xl">
Account Abstraction
</h1>

<p className="text-muted-foreground text-sm">
Easily integrate Account abstraction (ERC-4337) compliant smart accounts
into your apps.{" "}
<TrackedLinkTW
target="_blank"
label="docs-wallets"
category="smart-wallet"
href="https://portal.thirdweb.com/wallets/smart-wallet"
className="text-link-foreground hover:text-foreground"
>
View Documentation
</TrackedLinkTW>
</p>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { AccountStatus } from "@3rdweb-sdk/react/hooks/useApi";
import { SmartWalletsBillingAlert } from "components/settings/ApiKeys/Alerts";
import { ConnectSDKCard } from "components/shared/ConnectSDKCard";
import { SmartWallets } from "components/smart-wallets";
import { redirect } from "next/navigation";
import { getAccount } from "../../../../../account/settings/getAccount";
import { AccountAbstractionAPIKeysMenu } from "../AccountAbstractionAPIKeysMenu";
import { OpChainAlert } from "../OpChainAlert";
import { PageHeader } from "../PageHeader";
import { getAASupportedAPIKeys } from "../getAASupportedAPIKeys";

export default async function Page(props: {
params: {
clientId: string;
};
searchParams: {
tab?: string;
};
}) {
const { clientId } = props.params;
const dashboardAccount = await getAccount();

if (!dashboardAccount) {
redirect(
`/login?next=${encodeURIComponent(
`/dashboard/connect/account-abstraction/${clientId}`,
)}`,
);
}

const apiKeys = await getAASupportedAPIKeys();
const apiKey = apiKeys.find((key) => key.key === clientId);

if (!apiKey) {
redirect("/dashboard/connect/account-abstraction");
}

const hasSmartWalletsWithoutBilling = apiKeys.find((k) =>
k.services?.find(
(s) =>
dashboardAccount.status !== AccountStatus.ValidPayment &&
s.name === "bundler",
),
);

return (
<div className="flex flex-col gap-10">
<div className="flex flex-col content-start justify-between gap-4 lg:flex-row">
<PageHeader />

<div>
<AccountAbstractionAPIKeysMenu
apiKeys={apiKeys.map((x) => ({
name: x.name,
key: x.key,
}))}
selectedAPIKey={apiKey}
/>
</div>
</div>

{hasSmartWalletsWithoutBilling ? (
<SmartWalletsBillingAlert />
) : (
<OpChainAlert />
)}

<SmartWallets
apiKeyServices={apiKey.services || []}
trackingCategory="smart-wallet"
defaultTab={props.searchParams.tab === "1" ? 1 : 0}
/>

<ConnectSDKCard
title="Get Started"
description="Add account abstraction to your app with the Connect SDK."
/>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { getApiKeys } from "../../../../api/lib/getAPIKeys";

export async function getAASupportedAPIKeys() {
return (await getApiKeys()).filter((key) => {
return !!(key.services || []).find((srv) => srv.name === "bundler");
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { ChakraProviderSetup } from "@/components/ChakraProviderSetup";
import type { Metadata } from "next";
import { getAbsoluteUrl } from "../../../../../lib/vercel-utils";

export default function Layout(props: { children: React.ReactNode }) {
return <ChakraProviderSetup>{props.children}</ChakraProviderSetup>;
}

const seo = {
title: "The Complete Account Abstraction Toolkit | thirdweb",
desc: "Add account abstraction to your web3 app & unlock powerful features for seamless onboarding, customizable transactions, & maximum security. Get started.",
};

export const metadata: Metadata = {
title: seo.title,
description: seo.desc,
openGraph: {
title: seo.title,
description: seo.desc,
images: [
{
url: `${getAbsoluteUrl()}/assets/og-image/dashboard-wallets-smart-wallet.png`,
width: 1200,
height: 630,
alt: seo.title,
},
],
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { redirect } from "next/navigation";
import { NoApiKeys } from "../../../../../components/settings/ApiKeys/NoApiKeys";
import { ConnectSDKCard } from "../../../../../components/shared/ConnectSDKCard";
import { getAuthToken } from "../../../../api/lib/getAuthToken";
import { PageHeader } from "./PageHeader";
import { getAASupportedAPIKeys } from "./getAASupportedAPIKeys";

export default async function Page() {
const authToken = getAuthToken();

if (!authToken) {
redirect(
`/login?next=${encodeURIComponent("/dashboard/connect/account-abstraction")}`,
);
}

const apiKeys = await getAASupportedAPIKeys();
const firstKey = apiKeys[0];

if (firstKey) {
redirect(`/dashboard/connect/account-abstraction/${firstKey.key}`);
}

return (
<div>
<PageHeader />
<div className="h-8" />
<NoApiKeys service="Account Abstraction" />
<div className="h-10" />
<ConnectSDKCard
title="Get Started"
description="Add account abstraction to your app with the Connect SDK."
/>
</div>
);
}
2 changes: 2 additions & 0 deletions apps/dashboard/src/components/settings/ApiKeys/Alerts.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
"use client";

import {
Alert,
AlertDescription,
Expand Down
9 changes: 4 additions & 5 deletions apps/dashboard/src/components/smart-wallets/index.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,24 @@
"use state";
"use client";

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

interface SmartWalletsProps {
apiKeyServices: ApiKeyService[];
trackingCategory: string;
defaultTab: 0 | 1;
}

export const SmartWallets: React.FC<SmartWalletsProps> = ({
apiKeyServices,
trackingCategory,
defaultTab,
}) => {
const searchParams = useSearchParams();
const defaultTabIndex = Number.parseInt(searchParams?.get("tab") || "1");
const [selectedTab, setSelectedTab] = useState<"factories" | "config">(
defaultTabIndex === 1 ? "config" : "factories",
defaultTab === 0 ? "config" : "factories",
);

return (
Expand Down
2 changes: 2 additions & 0 deletions apps/dashboard/src/hooks/useLocalStorage.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
"use client";

import { useEffect, useState } from "react";
import { isBrowser } from "utils/isBrowser";

Expand Down
2 changes: 0 additions & 2 deletions apps/dashboard/src/page-id.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,6 @@ export enum PageId {
// thirdweb.com/dashboard/infrastructure/storage
DashboardSettingsStorage = "dashboard-storage",

// thirdweb.com/dashboard/connect/smart-wallet
DashboardConnectAccountAbstraction = "dashboard-wallets-smart-wallet",
// thirdweb.com/dashboard/contracts/build
DashboardContractsBuild = "dashboard-contracts-build",

Expand Down
Loading
Loading