- Assets
+ Tokens
Create and Manage tokens for your project
diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/ProjectFTUX/ProjectFTUX.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/ProjectFTUX/ProjectFTUX.tsx
index 1b70b3fd1e8..13bffaff6d6 100644
--- a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/ProjectFTUX/ProjectFTUX.tsx
+++ b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/ProjectFTUX/ProjectFTUX.tsx
@@ -9,15 +9,15 @@ import { TypeScriptIcon } from "components/icons/brand-icons/TypeScriptIcon";
import { UnityIcon } from "components/icons/brand-icons/UnityIcon";
import { UnrealIcon } from "components/icons/brand-icons/UnrealIcon";
import {
+ ArrowLeftRightIcon,
ChevronRightIcon,
CircleAlertIcon,
ExternalLinkIcon,
} from "lucide-react";
import Link from "next/link";
import { ContractIcon } from "../../../../../../(dashboard)/(chain)/components/server/icons/ContractIcon";
-import { EngineIcon } from "../../../../../../(dashboard)/(chain)/components/server/icons/EngineIcon";
import { InsightIcon } from "../../../../../../(dashboard)/(chain)/components/server/icons/InsightIcon";
-import { NebulaIcon } from "../../../../../../(dashboard)/(chain)/components/server/icons/NebulaIcon";
+import { PayIcon } from "../../../../../../(dashboard)/(chain)/components/server/icons/PayIcon";
import { ClientIDSection } from "./ClientIDSection";
import { IntegrateAPIKeyCodeTabs } from "./IntegrateAPIKeyCodeTabs";
import { SecretKeySection } from "./SecretKeySection";
@@ -233,15 +233,13 @@ function ProductsSection(props: {
description: string;
href: string;
icon: React.FC<{ className?: string }>;
- trackingLabel: string;
}> = [
{
- title: "Engine",
+ title: "Transactions",
description:
"Scale your application with a backend server to read, write, and deploy contracts at production-grade.",
- href: `/team/${props.teamSlug}/${props.projectSlug}/engine`,
- icon: EngineIcon,
- trackingLabel: "engine",
+ href: `/team/${props.teamSlug}/${props.projectSlug}/transactions`,
+ icon: ArrowLeftRightIcon,
},
{
title: "Contracts",
@@ -249,7 +247,6 @@ function ProductsSection(props: {
"Deploy your own contracts or leverage existing solutions for onchain implementation",
href: `/team/${props.teamSlug}/${props.projectSlug}/contracts`,
icon: ContractIcon,
- trackingLabel: "contracts",
},
{
title: "Insight",
@@ -257,15 +254,13 @@ function ProductsSection(props: {
"Add indexing capabilities to retrieve real-time onchain data",
href: `/team/${props.teamSlug}/${props.projectSlug}/insight`,
icon: InsightIcon,
- trackingLabel: "insight",
},
{
- title: "Nebula",
+ title: "Universal Bridge",
description:
- "Integrate a blockchain AI model to improve your users insight into your application and the blockchain",
- href: `/team/${props.teamSlug}/${props.projectSlug}/nebula`,
- icon: NebulaIcon,
- trackingLabel: "nebula",
+ "Bridge, swap, and purchase cryptocurrencies with any fiat options or tokens via cross-chain routing",
+ href: `/team/${props.teamSlug}/${props.projectSlug}/universal-bridge`,
+ icon: PayIcon,
},
];
@@ -290,7 +285,6 @@ function ProductsSection(props: {
description={product.description}
href={product.href}
icon={product.icon}
- trackingLabel={product.trackingLabel}
/>
))}
@@ -303,7 +297,6 @@ function ProductCard(props: {
description: string;
href: string;
icon: React.FC<{ className?: string }>;
- trackingLabel: string;
}) {
return (
diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/ProjectSidebarLayout.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/ProjectSidebarLayout.tsx
index 5d996d59355..d7102cb9df9 100644
--- a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/ProjectSidebarLayout.tsx
+++ b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/ProjectSidebarLayout.tsx
@@ -2,18 +2,18 @@
import { FullWidthSidebarLayout } from "@/components/blocks/SidebarLayout";
import { Badge } from "@/components/ui/badge";
import {
+ ArrowLeftRightIcon,
BellIcon,
BookTextIcon,
BoxIcon,
CoinsIcon,
HomeIcon,
+ LockIcon,
SettingsIcon,
WalletIcon,
} from "lucide-react";
import { ContractIcon } from "../../../../../(dashboard)/(chain)/components/server/icons/ContractIcon";
-import { EngineIcon } from "../../../../../(dashboard)/(chain)/components/server/icons/EngineIcon";
import { InsightIcon } from "../../../../../(dashboard)/(chain)/components/server/icons/InsightIcon";
-import { NebulaIcon } from "../../../../../(dashboard)/(chain)/components/server/icons/NebulaIcon";
import { PayIcon } from "../../../../../(dashboard)/(chain)/components/server/icons/PayIcon";
import { SmartAccountIcon } from "../../../../../(dashboard)/(chain)/components/server/icons/SmartAccountIcon";
@@ -33,7 +33,7 @@ export function ProjectSidebarLayout(props: {
icon: HomeIcon,
},
{
- label: "In-App Wallets",
+ label: "Wallets",
href: `${layoutPath}/connect/in-app-wallets`,
icon: WalletIcon,
},
@@ -53,18 +53,18 @@ export function ProjectSidebarLayout(props: {
icon: ContractIcon,
},
{
- href: `${layoutPath}/assets`,
+ href: `${layoutPath}/tokens`,
label: (
- Assets New
+ Tokens New
),
icon: CoinsIcon,
},
{
href: `${layoutPath}/engine`,
- label: "Engine",
- icon: EngineIcon,
+ label: "Transactions",
+ icon: ArrowLeftRightIcon,
},
{
href: `${layoutPath}/insight`,
@@ -72,10 +72,12 @@ export function ProjectSidebarLayout(props: {
icon: InsightIcon,
},
{
- href: `${layoutPath}/nebula`,
- label: "Nebula",
- icon: NebulaIcon,
+ href: `${layoutPath}/vault`,
+ label: "Vault",
+ icon: LockIcon,
},
+ ]}
+ footerSidebarLinks={[
{
href: `${layoutPath}/webhooks`,
label: (
@@ -85,8 +87,6 @@ export function ProjectSidebarLayout(props: {
),
icon: BellIcon,
},
- ]}
- footerSidebarLinks={[
{
href: `${layoutPath}/settings`,
label: "Project Settings",
diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/connect/in-app-wallets/_components/header.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/connect/in-app-wallets/_components/header.tsx
index 30e106dd9fa..dc61c459979 100644
--- a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/connect/in-app-wallets/_components/header.tsx
+++ b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/connect/in-app-wallets/_components/header.tsx
@@ -4,7 +4,7 @@ export async function InAppWalletsHeader() {
return (
- In-App Wallets
+ Wallets
A wallet infrastructure that enables apps to create, manage, and control
diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/connect/in-app-wallets/_constants.ts b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/connect/in-app-wallets/_constants.ts
deleted file mode 100644
index f50b20dd636..00000000000
--- a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/connect/in-app-wallets/_constants.ts
+++ /dev/null
@@ -1 +0,0 @@
-export const TRACKING_CATEGORY = "team/in-app-wallets";
diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/connect/in-app-wallets/users/page.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/connect/in-app-wallets/users/page.tsx
index 093558b4a71..a1f0ce5ed5b 100644
--- a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/connect/in-app-wallets/users/page.tsx
+++ b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/connect/in-app-wallets/users/page.tsx
@@ -4,7 +4,6 @@ import { InAppWalletUsersPageContent } from "components/embedded-wallets/Users";
import { redirect } from "next/navigation";
import { getAuthToken } from "../../../../../../../api/lib/getAuthToken";
import { loginRedirect } from "../../../../../../../login/loginRedirect";
-import { TRACKING_CATEGORY } from "../_constants";
export default async function Page(props: {
params: Promise<{ team_slug: string; project_slug: string }>;
@@ -33,7 +32,6 @@ export default async function Page(props: {
return (
diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/cloud/analytics/ftux.client.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/cloud/analytics/ftux.client.tsx
index 345b95f6a1c..79cd708767e 100644
--- a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/cloud/analytics/ftux.client.tsx
+++ b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/cloud/analytics/ftux.client.tsx
@@ -5,9 +5,9 @@ import { type Step, StepsCard } from "components/dashboard/StepsCard";
import Link from "next/link";
import { useMemo, useState } from "react";
import type { ThirdwebClient } from "thirdweb";
+import { CreateVaultAccountButton } from "../../../vault/components/create-vault-account.client";
import CreateServerWallet from "../server-wallets/components/create-server-wallet.client";
import type { Wallet } from "../server-wallets/wallet-table/types";
-import CreateVaultAccountButton from "../vault/components/create-vault-account.client";
import { SendTestTransaction } from "./send-test-tx.client";
import { deleteUserAccessToken } from "./utils";
diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/cloud/analytics/tx-chart/tx-chart-ui.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/cloud/analytics/tx-chart/tx-chart-ui.tsx
index b97c3f44382..3bb82113d15 100644
--- a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/cloud/analytics/tx-chart/tx-chart-ui.tsx
+++ b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/cloud/analytics/tx-chart/tx-chart-ui.tsx
@@ -185,7 +185,7 @@ function EmptyChartContent(props: {
variant="primary"
onClick={() => {
router.push(
- `/team/${props.teamSlug}/${props.project.slug}/engine/cloud/vault`,
+ `/team/${props.teamSlug}/${props.project.slug}/vault`,
);
}}
>
diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/cloud/layout.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/cloud/layout.tsx
index 8246256d7a9..13be81a2cd5 100644
--- a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/cloud/layout.tsx
+++ b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/cloud/layout.tsx
@@ -35,12 +35,12 @@ function TransactionsLayout(props: {
- Engine{" "}
+ Transactions{" "}
- Cloud
+ Engine Cloud
diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/cloud/server-wallets/components/create-server-wallet.client.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/cloud/server-wallets/components/create-server-wallet.client.tsx
index 2ec6fd295a3..841d4440923 100644
--- a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/cloud/server-wallets/components/create-server-wallet.client.tsx
+++ b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/cloud/server-wallets/components/create-server-wallet.client.tsx
@@ -86,9 +86,7 @@ export default function CreateServerWallet(props: {
const handleCreateServerWallet = async () => {
if (!props.managementAccessToken) {
- router.push(
- `/team/${props.teamSlug}/${props.project.slug}/engine/cloud/vault`,
- );
+ router.push(`/team/${props.teamSlug}/${props.project.slug}/vault`);
} else {
await createEoaMutation.mutateAsync({
managementAccessToken: props.managementAccessToken,
@@ -106,9 +104,7 @@ export default function CreateServerWallet(props: {
onClick={() =>
props.managementAccessToken
? setModalOpen(true)
- : router.push(
- `/team/${props.teamSlug}/${props.project.slug}/engine/cloud/vault`,
- )
+ : router.push(`/team/${props.teamSlug}/${props.project.slug}/vault`)
}
className="flex flex-row items-center gap-2"
>
diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/cloud/vault/components/key-management.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/cloud/vault/components/key-management.tsx
deleted file mode 100644
index 7f05b0a3da3..00000000000
--- a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/cloud/vault/components/key-management.tsx
+++ /dev/null
@@ -1,104 +0,0 @@
-import type { Project } from "@/api/projects";
-import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
-import { InfoIcon } from "lucide-react";
-import Link from "next/link";
-import CreateVaultAccountButton from "./create-vault-account.client";
-import ListAccessTokens from "./list-access-tokens.client";
-import RotateAdminKeyButton from "./rotate-admin-key.client";
-
-export function KeyManagement({
- maskedAdminKey,
- project,
-}: { maskedAdminKey?: string; project: Project }) {
- return (
-
-
-
-
Vault
-
- Secure, non-custodial key management system for your server wallets.{" "}
-
- Learn more.
-
-
-
- {!maskedAdminKey ? (
-
- ) : (
-
- )}
-
- {maskedAdminKey ? (
- <>
-
-
-
- Admin Key
-
-
- This key is used to create new server wallets and access tokens.
- We do not store this key. If you lose it, you can rotate
- it to create a new one. Doing so will invalidate all existing
- access tokens.
-
-
-
-
-
-
- >
- ) : null}
-
- );
-}
-
-async function CreateVaultAccountAlert(props: {
- project: Project;
-}) {
- return (
-
-
-
-
-
- What is Vault?
-
-
- Vault is thirdweb's non-custodial key management system for your
- server wallets that allows you to:
-
- Create multiple server wallets.
- Create Vault access tokens.
- Sign transactions using a Vault access token.
-
- Your keys are stored in a hardware enclave, and all requests are
- end-to-end encrypted.{" "}
-
- Learn more about Vault security model.
-
-
- Creating server wallets and access tokens requires a Vault admin
- account. Create one below to get started.
-
-
-
-
-
-
-
-
- );
-}
diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/dedicated/(general)/layout.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/dedicated/(general)/layout.tsx
index b8c10047be1..950cfa42a6f 100644
--- a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/dedicated/(general)/layout.tsx
+++ b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/dedicated/(general)/layout.tsx
@@ -25,12 +25,12 @@ export default async function Layout(props: {
- Engine{" "}
+ Transactions{" "}
- Dedicated
+ Dedicated Engine
diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/nebula/components/analytics/fetch-nebula-analytics.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/nebula/components/analytics/fetch-nebula-analytics.tsx
deleted file mode 100644
index 2b5f5c34b2f..00000000000
--- a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/nebula/components/analytics/fetch-nebula-analytics.tsx
+++ /dev/null
@@ -1,55 +0,0 @@
-import "server-only";
-import { ANALYTICS_SERVICE_URL } from "@/constants/server-envs";
-import { unstable_cache } from "next/cache";
-
-export type NebulaAnalyticsDataItem = {
- date: string;
- totalPromptTokens: number;
- totalCompletionTokens: number;
- totalSessions: number;
- totalRequests: number;
-};
-
-export const fetchNebulaAnalytics = unstable_cache(
- async (params: {
- teamId: string;
- projectId: string;
- authToken: string;
- from: string;
- to: string;
- period: "day" | "week" | "month" | "year" | "all";
- }) => {
- const analyticsEndpoint = ANALYTICS_SERVICE_URL;
- const url = new URL(`${analyticsEndpoint}/v2/nebula/usage`);
- url.searchParams.set("teamId", params.teamId);
- url.searchParams.set("projectId", params.projectId);
- url.searchParams.set("from", params.from);
- url.searchParams.set("to", params.to);
- url.searchParams.set("period", params.period);
-
- const res = await fetch(url, {
- headers: {
- Authorization: `Bearer ${params.authToken}`,
- },
- });
-
- if (!res.ok) {
- const error = await res.text();
- return {
- ok: false as const,
- error: error,
- };
- }
-
- const resData = await res.json();
-
- return {
- ok: true as const,
- data: resData.data as NebulaAnalyticsDataItem[],
- };
- },
- ["nebula-analytics"],
- {
- revalidate: 60 * 60, // 1 hour
- },
-);
diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/nebula/components/analytics/nebula-analytics-filter.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/nebula/components/analytics/nebula-analytics-filter.tsx
deleted file mode 100644
index 76a2d83ad8f..00000000000
--- a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/nebula/components/analytics/nebula-analytics-filter.tsx
+++ /dev/null
@@ -1,51 +0,0 @@
-"use client";
-
-import { normalizeTimeISOString } from "@/lib/time";
-import { DateRangeSelector } from "components/analytics/date-range-selector";
-import { IntervalSelector } from "components/analytics/interval-selector";
-import { getNebulaFiltersFromSearchParams } from "lib/time";
-import {
- useResponsiveSearchParams,
- useSetResponsiveSearchParams,
-} from "responsive-rsc";
-
-export function NebulaAnalyticsFilter() {
- const responsiveSearchParams = useResponsiveSearchParams();
- const setResponsiveSearchParams = useSetResponsiveSearchParams();
-
- const { range, interval } = getNebulaFiltersFromSearchParams({
- from: responsiveSearchParams.from,
- to: responsiveSearchParams.to,
- interval: responsiveSearchParams.interval,
- });
-
- return (
-
- {
- setResponsiveSearchParams((v) => {
- return {
- ...v,
- from: normalizeTimeISOString(newRange.from),
- to: normalizeTimeISOString(newRange.to),
- };
- });
- }}
- />
-
- {
- setResponsiveSearchParams((v) => {
- return {
- ...v,
- interval: newInterval,
- };
- });
- }}
- />
-
- );
-}
diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/nebula/components/analytics/nebula-analytics-page.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/nebula/components/analytics/nebula-analytics-page.tsx
deleted file mode 100644
index 6c3e69f3c59..00000000000
--- a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/nebula/components/analytics/nebula-analytics-page.tsx
+++ /dev/null
@@ -1,115 +0,0 @@
-import { Button } from "@/components/ui/button";
-import { normalizeTimeISOString } from "@/lib/time";
-import { FileCode2Icon, MessageSquareQuoteIcon } from "lucide-react";
-import Link from "next/link";
-import {
- ResponsiveSearchParamsProvider,
- ResponsiveSuspense,
-} from "responsive-rsc";
-import { fetchNebulaAnalytics } from "./fetch-nebula-analytics";
-import { NebulaAnalyticsFilter } from "./nebula-analytics-filter";
-import { NebulaAnalyticsDashboardUI } from "./nebula-analytics-ui";
-import { getNebulaAnalyticsRangeFromSearchParams } from "./utils";
-
-export function NebulaAnalyticsPage(props: {
- searchParams: {
- from: string | undefined | string[];
- to: string | undefined | string[];
- interval: string | undefined | string[];
- };
- teamId: string;
- authToken: string;
- projectId: string;
-}) {
- return (
-
-
-
-
-
-
Analytics
-
-
-
}
- >
-
-
-
-
- );
-}
-
-async function NebulaAnalyticDashboard(props: {
- teamId: string;
- authToken: string;
- projectId: string;
- searchParams: {
- from: string | undefined | string[];
- to: string | undefined | string[];
- interval: string | undefined | string[];
- };
-}) {
- const { range, interval } = getNebulaAnalyticsRangeFromSearchParams(
- props.searchParams,
- );
-
- const res = await fetchNebulaAnalytics({
- teamId: props.teamId,
- authToken: props.authToken,
- projectId: props.projectId,
- from: normalizeTimeISOString(range.from),
- to: normalizeTimeISOString(range.to),
- // internally renamed
- period: interval,
- });
-
- if (!res.ok) {
- return (
-
-
-
- Failed to fetch Nebula analytics
-
-
{res.error}
-
-
- );
- }
-
- return
;
-}
diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/nebula/components/analytics/nebula-analytics-ui.stories.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/nebula/components/analytics/nebula-analytics-ui.stories.tsx
deleted file mode 100644
index 45902917035..00000000000
--- a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/nebula/components/analytics/nebula-analytics-ui.stories.tsx
+++ /dev/null
@@ -1,68 +0,0 @@
-import type { Meta, StoryObj } from "@storybook/nextjs";
-import { subDays } from "date-fns";
-import type { NebulaAnalyticsDataItem } from "./fetch-nebula-analytics";
-import { NebulaAnalyticsDashboardUI } from "./nebula-analytics-ui";
-
-const meta = {
- title: "Nebula/Analytics",
- component: NebulaAnalyticsDashboardUI,
- parameters: {
- nextjs: {
- appDirectory: true,
- },
- },
- decorators: [
- (Story) => (
-
-
-
- ),
- ],
-} satisfies Meta
;
-
-export default meta;
-type Story = StoryObj;
-
-export const SixtyDays: Story = {
- args: {
- data: generateRandomNebulaAnalyticsData(60),
- isPending: false,
- },
-};
-
-export const ThirtyDays: Story = {
- args: {
- data: generateRandomNebulaAnalyticsData(30),
- isPending: false,
- },
-};
-
-export const SevenDays: Story = {
- args: {
- data: generateRandomNebulaAnalyticsData(7),
- isPending: false,
- },
-};
-
-export const Pending: Story = {
- args: {
- data: [],
- isPending: true,
- },
-};
-
-function generateRandomNebulaAnalyticsData(
- days: number,
-): NebulaAnalyticsDataItem[] {
- return Array.from({ length: days }, (_, i) => ({
- date: subDays(new Date(), i).toISOString(),
- totalPromptTokens: randomInt(500, 700 + i * 100),
- totalCompletionTokens: randomInt(1000, 2000 + i * 100),
- totalSessions: randomInt(400, 1000 + i * 100),
- totalRequests: randomInt(4000, 5000 + i * 100),
- }));
-}
-
-function randomInt(min: number, max: number) {
- return Math.floor(Math.random() * (max - min + 1)) + min;
-}
diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/nebula/components/analytics/nebula-analytics-ui.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/nebula/components/analytics/nebula-analytics-ui.tsx
deleted file mode 100644
index 24120f9165f..00000000000
--- a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/nebula/components/analytics/nebula-analytics-ui.tsx
+++ /dev/null
@@ -1,203 +0,0 @@
-"use client";
-
-import { ThirdwebAreaChart } from "@/components/blocks/charts/area-chart";
-import { SkeletonContainer } from "@/components/ui/skeleton";
-import { format } from "date-fns";
-import {
- ActivityIcon,
- MessageCircleQuestionIcon,
- MessageSquareIcon,
- MessageSquareQuoteIcon,
-} from "lucide-react";
-import { useMemo } from "react";
-import type { NebulaAnalyticsDataItem } from "./fetch-nebula-analytics";
-
-type ChartData = {
- time: Date;
- totalPromptTokens: number;
- totalCompletionTokens: number;
- totalSessions: number;
- totalRequests: number;
-};
-
-type AnalyticsChartProps = {
- data: ChartData[];
- isPending: boolean;
- title: string;
- description: string;
- dataKey: keyof ChartData;
- color: string;
-};
-
-function AnalyticsChart({
- data,
- isPending,
- title,
- description,
- dataKey,
- color,
-}: AnalyticsChartProps) {
- return (
- ({
- ...item,
- time: item.time.getTime(),
- }))}
- isPending={isPending}
- header={{
- title,
- description,
- titleClassName: "text-xl mb-1",
- }}
- chartClassName="aspect-[1.5] lg:aspect-[2.5]"
- hideLabel={false}
- toolTipLabelFormatter={toolTipLabelFormatter}
- config={{
- [dataKey]: {
- label: title,
- color,
- },
- }}
- />
- );
-}
-
-export function NebulaAnalyticsDashboardUI(props: {
- data: NebulaAnalyticsDataItem[];
- isPending: boolean;
-}) {
- const data = useMemo(() => {
- const val: {
- totalPromptTokens: number;
- totalCompletionTokens: number;
- totalSessions: number;
- totalRequests: number;
- chartData: ChartData[];
- } = {
- totalPromptTokens: 0,
- totalCompletionTokens: 0,
- totalSessions: 0,
- totalRequests: 0,
- chartData: [],
- };
-
- for (const item of props.data) {
- val.totalPromptTokens += item.totalPromptTokens;
- val.totalCompletionTokens += item.totalCompletionTokens;
- val.totalSessions += item.totalSessions;
- val.totalRequests += item.totalRequests;
- val.chartData.push({
- totalPromptTokens: item.totalPromptTokens,
- totalCompletionTokens: item.totalCompletionTokens,
- totalSessions: item.totalSessions,
- totalRequests: item.totalRequests,
- time: new Date(item.date),
- });
- }
-
- return val;
- }, [props.data]);
-
- return (
-
- );
-}
-
-function toolTipLabelFormatter(_v: string, item: unknown) {
- if (Array.isArray(item)) {
- const time = item[0].payload.time as number;
- return format(new Date(time), "MMM d, yyyy");
- }
- return undefined;
-}
-
-function StatCard(props: {
- title: string;
- value: number;
- icon: React.FC<{ className?: string }>;
- isPending: boolean;
-}) {
- return (
-
- );
-}
diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/nebula/components/analytics/utils.ts b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/nebula/components/analytics/utils.ts
deleted file mode 100644
index a361b20ae65..00000000000
--- a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/nebula/components/analytics/utils.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import { getNebulaFiltersFromSearchParams } from "lib/time";
-
-export function getNebulaAnalyticsRangeFromSearchParams(searchParams: {
- from: string | undefined | string[];
- to: string | undefined | string[];
- interval: string | undefined | string[];
-}) {
- return getNebulaFiltersFromSearchParams({
- from: searchParams.from,
- to: searchParams.to,
- interval: searchParams.interval,
- });
-}
diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/nebula/layout.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/nebula/layout.tsx
deleted file mode 100644
index f7d5c5aff05..00000000000
--- a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/nebula/layout.tsx
+++ /dev/null
@@ -1,71 +0,0 @@
-import { FooterLinksSection } from "../components/footer/FooterLinksSection";
-
-export default function Layout(props: {
- children: React.ReactNode;
-}) {
- return (
-
- );
-}
-
-function NebulaFooter() {
- return (
-
- );
-}
diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/nebula/nebula-ftux.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/nebula/nebula-ftux.tsx
deleted file mode 100644
index 7eb8b8e2efe..00000000000
--- a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/nebula/nebula-ftux.tsx
+++ /dev/null
@@ -1,102 +0,0 @@
-import { CodeServer } from "@/components/ui/code/code.server";
-import { SecretKeySection } from "../components/ProjectFTUX/SecretKeySection";
-import { WaitingForIntegrationCard } from "../components/WaitingForIntegrationCard/WaitingForIntegrationCard";
-
-export function NebulaFTUX(props: {
- secretKeyMasked: string;
- teamId: string;
- projectId: string;
-}) {
- return (
-
- ),
- },
- {
- label: "Python",
- code: (
-
- ),
- },
- {
- label: "Curl",
- code: (
-
- ),
- },
- ]}
- ctas={[
- {
- label: "Try on Playground",
- href: "https://nebula.thirdweb.com/",
- },
- {
- label: "View Docs",
- href: "https://portal.thirdweb.com/nebula",
- },
- ]}
- >
-
-
-
- );
-}
-
-const jsCode = `\
-// Example: Send message to Nebula
-// Replace PROJECT_SECRET_KEY with your project's full secret key
-
-const res = await fetch("https://nebula-api.thirdweb.com/chat", {
- method: "POST",
- headers: {
- "x-secret-key": "PROJECT_SECRET_KEY",
- },
- body: {
- message: "Hello",
- stream: false,
- },
-});
-
-const data = await res.json();
-`;
-
-const curlCode = `\
-# Example: Send message to Nebula
-# Replace PROJECT_SECRET_KEY with your project's full secret key
-
-curl -X POST https://nebula-api.thirdweb.com/chat \
--H "x-secret-key:PROJECT_SECRET_KEY" \
--d '{
- "message": "Hello",
- "stream": false,
-}'
-`;
-
-const pythonCode = `\
-# Example: Send message to Nebula
-# Replace PROJECT_SECRET_KEY with your project's full secret key
-
-import requests
-
-response = requests.post("https://nebula-api.thirdweb.com/chat", headers={
- "x-secret-key": "PROJECT_SECRET_KEY"
-}, json={
- "message": "Hello",
- "stream": False,
-})
-
-data = response.json()
-`;
diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/nebula/page.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/nebula/page.tsx
deleted file mode 100644
index 05643837434..00000000000
--- a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/nebula/page.tsx
+++ /dev/null
@@ -1,79 +0,0 @@
-import { isProjectActive } from "@/api/analytics";
-import { getProject } from "@/api/projects";
-import { getTeamBySlug } from "@/api/team";
-import { redirect } from "next/navigation";
-import { getAuthToken } from "../../../../../api/lib/getAuthToken";
-import { loginRedirect } from "../../../../../login/loginRedirect";
-import { NebulaAnalyticsPage } from "./components/analytics/nebula-analytics-page";
-import { NebulaFTUX } from "./nebula-ftux";
-
-export default async function Page(props: {
- params: Promise<{
- team_slug: string;
- project_slug: string;
- }>;
- searchParams: Promise<{
- from: string | undefined | string[];
- to: string | undefined | string[];
- interval: string | undefined | string[];
- }>;
-}) {
- const [params, searchParams] = await Promise.all([
- props.params,
- props.searchParams,
- ]);
-
- const [authToken, team, project] = await Promise.all([
- getAuthToken(),
- getTeamBySlug(params.team_slug),
- getProject(params.team_slug, params.project_slug),
- ]);
-
- if (!team) {
- redirect("/team");
- }
-
- if (!project) {
- redirect(`/team/${params.team_slug}`);
- }
-
- if (!authToken) {
- loginRedirect(`/team/${params.team_slug}/${params.project_slug}/nebula`);
- }
-
- const activeResponse = await isProjectActive({
- teamId: team.id,
- projectId: project.id,
- });
-
- const showFTUX = !activeResponse.nebula;
-
- if (showFTUX) {
- return (
-
- );
- }
-
- return (
-
- );
-}
diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/settings/ProjectGeneralSettingsPage.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/settings/ProjectGeneralSettingsPage.tsx
index a18c8253202..90c5f8bb2e8 100644
--- a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/settings/ProjectGeneralSettingsPage.tsx
+++ b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/settings/ProjectGeneralSettingsPage.tsx
@@ -761,7 +761,7 @@ function EnabledServicesSetting(props: {
return (
{/* Left */}
@@ -780,7 +780,7 @@ function EnabledServicesSetting(props: {
asChild
size="sm"
variant="outline"
- className="min-w-32 justify-between gap-2"
+ className="h-auto justify-between gap-2 rounded-full bg-background py-1"
>
Configure
diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/cloud/vault/components/create-vault-account.client.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/vault/components/create-vault-account.client.tsx
similarity index 97%
rename from apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/cloud/vault/components/create-vault-account.client.tsx
rename to apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/vault/components/create-vault-account.client.tsx
index b129944158c..5dcc541c7cc 100644
--- a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/cloud/vault/components/create-vault-account.client.tsx
+++ b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/vault/components/create-vault-account.client.tsx
@@ -15,18 +15,23 @@ import { useDashboardRouter } from "@/lib/DashboardRouter";
import { cn } from "@/lib/utils";
import { useMutation } from "@tanstack/react-query";
import { createServiceAccount } from "@thirdweb-dev/vault-sdk";
-import { CheckIcon, DownloadIcon, Loader2Icon, LockIcon } from "lucide-react";
+import {
+ CheckIcon,
+ DownloadIcon,
+ Loader2Icon,
+ UserLockIcon,
+} from "lucide-react";
import { useState } from "react";
import { toast } from "sonner";
-import { storeUserAccessToken } from "../../analytics/utils";
+import { storeUserAccessToken } from "../../engine/cloud/analytics/utils";
import {
createManagementAccessToken,
createWalletAccessToken,
initVaultClient,
maskSecret,
-} from "../../lib/vault.client";
+} from "../../engine/cloud/lib/vault.client";
-export default function CreateVaultAccountButton(props: {
+export function CreateVaultAccountButton(props: {
project: Project;
onUserAccessTokenCreated?: (userAccessToken: string) => void;
}) {
@@ -151,7 +156,7 @@ export default function CreateVaultAccountButton(props: {
{isLoading ? (
) : (
-
+
)}
{"Create Vault Admin Account"}
diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/vault/components/key-management.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/vault/components/key-management.tsx
new file mode 100644
index 00000000000..5bf8aa21931
--- /dev/null
+++ b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/vault/components/key-management.tsx
@@ -0,0 +1,86 @@
+import type { Project } from "@/api/projects";
+import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
+import { InfoIcon } from "lucide-react";
+import Link from "next/link";
+import { CreateVaultAccountButton } from "./create-vault-account.client";
+import ListAccessTokens from "./list-access-tokens.client";
+import RotateAdminKeyButton from "./rotate-admin-key.client";
+
+export function KeyManagement({
+ maskedAdminKey,
+ project,
+}: { maskedAdminKey?: string; project: Project }) {
+ return (
+
+ {!maskedAdminKey &&
}
+
+ {maskedAdminKey && (
+ <>
+
+
+
+ Admin Key
+
+
+ This key is used to create new server wallets and access tokens.
+ We do not store this key. If you lose it, you can rotate
+ it to create a new one. Doing so will invalidate all existing
+ access tokens.
+
+
+
+
+
+
+ >
+ )}
+
+ );
+}
+
+async function CreateVaultAccountAlert(props: {
+ project: Project;
+}) {
+ return (
+
+
+
+
+ What is Vault?
+
+
+ Vault is thirdweb's non-custodial key management system for your
+ server wallets that allows you to:
+
+ Create multiple server wallets.
+ Create Vault access tokens.
+ Sign transactions using a Vault access token.
+
+ Your keys are stored in a hardware enclave, and all requests are
+ end-to-end encrypted.{" "}
+
+ Learn more about Vault security model.
+
+
+ Creating server wallets and access tokens requires a Vault admin
+ account. Create one below to get started.
+
+
+
+
+
+
+ );
+}
diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/cloud/vault/components/list-access-tokens.client.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/vault/components/list-access-tokens.client.tsx
similarity index 99%
rename from apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/cloud/vault/components/list-access-tokens.client.tsx
rename to apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/vault/components/list-access-tokens.client.tsx
index d551f3ecb9b..175fd32010f 100644
--- a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/cloud/vault/components/list-access-tokens.client.tsx
+++ b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/vault/components/list-access-tokens.client.tsx
@@ -20,7 +20,7 @@ import {
SERVER_WALLET_MANAGEMENT_ACCESS_TOKEN_PURPOSE,
createWalletAccessToken,
initVaultClient,
-} from "../../lib/vault.client";
+} from "../../engine/cloud/lib/vault.client";
export default function ListAccessTokens(props: {
project: Project;
diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/cloud/vault/components/rotate-admin-key.client.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/vault/components/rotate-admin-key.client.tsx
similarity index 99%
rename from apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/cloud/vault/components/rotate-admin-key.client.tsx
rename to apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/vault/components/rotate-admin-key.client.tsx
index 6c3d62473b2..dae5b3fd31f 100644
--- a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/cloud/vault/components/rotate-admin-key.client.tsx
+++ b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/vault/components/rotate-admin-key.client.tsx
@@ -31,7 +31,7 @@ import {
createWalletAccessToken,
initVaultClient,
maskSecret,
-} from "../../lib/vault.client";
+} from "../../engine/cloud/lib/vault.client";
export default function RotateAdminKeyButton(props: { project: Project }) {
const [modalOpen, setModalOpen] = useState(false);
diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/vault/layout.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/vault/layout.tsx
new file mode 100644
index 00000000000..c7dda5e1f69
--- /dev/null
+++ b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/vault/layout.tsx
@@ -0,0 +1,33 @@
+import Link from "next/link";
+
+export default function VaultLayout(props: {
+ children: React.ReactNode;
+}) {
+ return (
+
+
+
+
Vault
+
+ Secure, non-custodial key management system for your server wallets.{" "}
+
+ Learn more.
+
+
+
+
+
+
+
+ {props.children}
+
+
+
+
+ );
+}
diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/cloud/vault/page.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/vault/page.tsx
similarity index 92%
rename from apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/cloud/vault/page.tsx
rename to apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/vault/page.tsx
index f3c3d783165..2e4b27fc815 100644
--- a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/cloud/vault/page.tsx
+++ b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/vault/page.tsx
@@ -1,6 +1,6 @@
import { getProject } from "@/api/projects";
+import { getAuthToken } from "@app/api/lib/getAuthToken";
import { notFound } from "next/navigation";
-import { getAuthToken } from "../../../../../../../api/lib/getAuthToken";
import { KeyManagement } from "./components/key-management";
export default async function VaultPage(props: {
diff --git a/apps/dashboard/src/components/embedded-wallets/Users/index.tsx b/apps/dashboard/src/components/embedded-wallets/Users/index.tsx
index f0dba99f450..8f223185227 100644
--- a/apps/dashboard/src/components/embedded-wallets/Users/index.tsx
+++ b/apps/dashboard/src/components/embedded-wallets/Users/index.tsx
@@ -36,7 +36,6 @@ const getUserIdentifier = (accounts: WalletUser["linkedAccounts"]) => {
const columnHelper = createColumnHelper
();
export function InAppWalletUsersPageContent(props: {
- trackingCategory: string;
authToken: string;
projectClientId: string;
client: ThirdwebClient;
diff --git a/apps/dashboard/src/lib/time.ts b/apps/dashboard/src/lib/time.ts
index 6b06f0e1395..95652750079 100644
--- a/apps/dashboard/src/lib/time.ts
+++ b/apps/dashboard/src/lib/time.ts
@@ -1,19 +1,6 @@
import { getFiltersFromSearchParams } from "@/lib/time";
import type { DurationId } from "../components/analytics/date-range-selector";
-export function getNebulaFiltersFromSearchParams(params: {
- from: string | undefined | string[];
- to: string | undefined | string[];
- interval: string | undefined | string[];
-}) {
- return getFiltersFromSearchParams({
- from: params.from,
- to: params.to,
- interval: params.interval,
- defaultRange: "last-30",
- });
-}
-
export function getUniversalBridgeFiltersFromSearchParams(params: {
from: string | undefined | string[];
to: string | undefined | string[];
diff --git a/packages/service-utils/src/core/services.ts b/packages/service-utils/src/core/services.ts
index 928a59731c8..8212e2dbce0 100644
--- a/packages/service-utils/src/core/services.ts
+++ b/packages/service-utils/src/core/services.ts
@@ -39,7 +39,7 @@ export const SERVICE_DEFINITIONS = {
},
embeddedWallets: {
name: "embeddedWallets",
- title: "In-App Wallets",
+ title: "Wallets",
description: "E-mail and social login wallets for easy web3 onboarding",
// all actions allowed
actions: [],
@@ -76,7 +76,7 @@ export const SERVICE_DEFINITIONS = {
},
engineCloud: {
name: "engineCloud",
- title: "Engine Cloud",
+ title: "Transactions",
description:
"Transaction API and Server wallets with high transaction throughput and low latency",
// all actions allowed