Skip to content

Commit 7a2dc25

Browse files
authored
[NEB-167] Add Nebula floating chat button in chain and contract pages (#6750)
1 parent 32568fd commit 7a2dc25

File tree

10 files changed

+792
-118
lines changed

10 files changed

+792
-118
lines changed

apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/(chainPage)/layout.tsx

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,18 @@ import {
1313
DropdownMenuItem,
1414
DropdownMenuTrigger,
1515
} from "@/components/ui/dropdown-menu";
16+
import { getThirdwebClient } from "@/constants/thirdweb.server";
1617
import { ChevronDownIcon, TicketCheckIcon } from "lucide-react";
1718
import type { Metadata } from "next";
1819
import Link from "next/link";
1920
import { redirect } from "next/navigation";
2021
import { mapV4ChainToV5Chain } from "../../../../../contexts/map-chains";
21-
import { getAuthToken } from "../../../../api/lib/getAuthToken";
22+
import { getRawAccount } from "../../../../account/settings/getAccount";
23+
import {
24+
getAuthToken,
25+
getAuthTokenWalletAddress,
26+
} from "../../../../api/lib/getAuthToken";
27+
import { NebulaFloatingChatButton } from "../../../../nebula-app/(app)/components/FloatingChat/FloatingChat";
2228
import { StarButton } from "../../components/client/star-button";
2329
import { getChain, getChainMetadata } from "../../utils";
2430
import { AddChainToWallet } from "./components/client/add-chain-to-wallet";
@@ -55,17 +61,58 @@ export default async function ChainPageLayout(props: {
5561
}) {
5662
const params = await props.params;
5763
const { children } = props;
58-
const chain = await getChain(params.chain_id);
59-
const authToken = await getAuthToken();
64+
const [chain, authToken, account, accountAddress] = await Promise.all([
65+
getChain(params.chain_id),
66+
getAuthToken(),
67+
getRawAccount(),
68+
getAuthTokenWalletAddress(),
69+
]);
6070

6171
if (params.chain_id !== chain.slug) {
6272
redirect(chain.slug);
6373
}
6474

6575
const chainMetadata = await getChainMetadata(chain.chainId);
76+
const client = getThirdwebClient(authToken ?? undefined);
77+
78+
const chainPromptPrefix = `\
79+
You are assisting users exploring the chain ${chain.name} (Chain ID: ${chain.chainId}). Provide concise insights into the types of applications and activities prevalent on this chain, such as DeFi protocols, NFT marketplaces, or gaming platforms. Highlight notable projects or trends without delving into technical details like consensus mechanisms or gas fees.
80+
Users may seek comparisons between ${chain.name} and other chains. Provide objective, succinct comparisons focusing on performance, fees, and ecosystem support. Refrain from transaction-specific advice unless requested.
81+
Provide users with an understanding of the unique use cases and functionalities that ${chain.name} supports. Discuss how developers leverage this chain for specific applications, such as scalable dApps, low-cost transactions, or specialized token standards, focusing on practical implementations.
82+
Users may be interested in utilizing thirdweb tools on ${chain.name}. Offer clear guidance on how thirdweb's SDKs, smart contract templates, and deployment tools integrate with this chain. Emphasize the functionalities enabled by thirdweb without discussing transaction execution unless prompted.
83+
Avoid transaction-related actions to be executed by the user unless inquired about.
84+
85+
The following is the user's message:
86+
`;
87+
88+
const examplePrompts: string[] = [
89+
"What are users doing on this chain?",
90+
"What are the most active contracts?",
91+
"Why would I use this chain over others?",
92+
"Can I deploy thirdweb contracts to this chain?",
93+
];
94+
95+
if (chain.chainId !== 1) {
96+
examplePrompts.push("Can I bridge assets from Ethereum to this chain?");
97+
}
6698

6799
return (
68100
<>
101+
<NebulaFloatingChatButton
102+
authToken={authToken ?? undefined}
103+
account={account}
104+
label="Ask AI about this chain"
105+
client={client}
106+
nebulaParams={{
107+
messagePrefix: chainPromptPrefix,
108+
chainIds: [chain.chainId],
109+
wallet: accountAddress ?? undefined,
110+
}}
111+
examplePrompts={examplePrompts.map((prompt) => ({
112+
title: prompt,
113+
message: prompt,
114+
}))}
115+
/>
69116
<div className="flex h-14 border-border border-b pl-7">
70117
<Breadcrumb className="my-auto">
71118
<BreadcrumbList>

apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/layout.tsx

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,12 @@ import { isAddress, isContractDeployed } from "thirdweb/utils";
99
import type { MinimalTeamsAndProjects } from "../../../../../components/contract-components/contract-deploy-form/add-to-project-card";
1010
import { resolveFunctionSelectors } from "../../../../../lib/selectors";
1111
import { shortenIfAddress } from "../../../../../utils/usedapp-external";
12+
import { getRawAccount } from "../../../../account/settings/getAccount";
13+
import {
14+
getAuthToken,
15+
getAuthTokenWalletAddress,
16+
} from "../../../../api/lib/getAuthToken";
17+
import { NebulaFloatingChatButton } from "../../../../nebula-app/(app)/components/FloatingChat/FloatingChat";
1218
import { ConfigureCustomChain } from "./_layout/ConfigureCustomChain";
1319
import { getContractMetadataHeaderData } from "./_layout/contract-metadata";
1420
import { ContractPageLayout } from "./_layout/contract-page-layout";
@@ -42,6 +48,12 @@ export default async function Layout(props: {
4248
notFound();
4349
}
4450

51+
const [authToken, account, accountAddress] = await Promise.all([
52+
getAuthToken(),
53+
getRawAccount(),
54+
getAuthTokenWalletAddress(),
55+
]);
56+
4557
const client = getThirdwebClient();
4658
const teamsAndProjects = await getTeamsAndProjectsIfLoggedIn();
4759

@@ -75,6 +87,25 @@ export default async function Layout(props: {
7587
const { contractMetadata, externalLinks } =
7688
await getContractMetadataHeaderData(contract);
7789

90+
const contractAddress = info.contract.address;
91+
const chainName = info.chainMetadata.name;
92+
const chainId = info.contract.chain.id;
93+
94+
const contractPromptPrefix = `A user is viewing the contract address ${contractAddress} on ${chainName} (Chain ID: ${chainId}). Provide a concise summary of this contract's functionalities, such as token minting, staking, or governance mechanisms. Focus on what the contract enables users to do, avoiding transaction execution details unless requested.
95+
Users may be interested in how to interact with the contract. Outline common interaction patterns, such as claiming rewards, participating in governance, or transferring assets. Emphasize the contract's capabilities without guiding through transaction processes unless asked.
96+
Provide insights into how the contract is being used. Share information on user engagement, transaction volumes, or integration with other dApps, focusing on the contract's role within the broader ecosystem.
97+
Users may be considering integrating the contract into their applications. Discuss how this contract's functionalities can be leveraged within different types of dApps, highlighting potential use cases and benefits.
98+
99+
The following is the user's message:`;
100+
101+
const examplePrompts: string[] = [
102+
"What does this contract do?",
103+
"What permissions or roles exist in this contract?",
104+
"Which functions are used the most?",
105+
"Has this contract been used recently?",
106+
"Who are the largest holders/users of this?",
107+
];
108+
78109
return (
79110
<ContractPageLayout
80111
chainMetadata={chainMetadata}
@@ -85,6 +116,21 @@ export default async function Layout(props: {
85116
teamsAndProjects={teamsAndProjects}
86117
client={client}
87118
>
119+
<NebulaFloatingChatButton
120+
authToken={authToken ?? undefined}
121+
account={account}
122+
label="Ask AI about this contract"
123+
client={client}
124+
nebulaParams={{
125+
messagePrefix: contractPromptPrefix,
126+
chainIds: [info.contract.chain.id],
127+
wallet: accountAddress ?? undefined,
128+
}}
129+
examplePrompts={examplePrompts.map((prompt) => ({
130+
title: prompt,
131+
message: prompt,
132+
}))}
133+
/>
88134
{props.children}
89135
</ContractPageLayout>
90136
);

apps/dashboard/src/app/nebula-app/(app)/components/ChatBar.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,17 @@ export function ChatBar(props: {
1111
isChatStreaming: boolean;
1212
abortChatStream: () => void;
1313
prefillMessage: string | undefined;
14+
className?: string;
1415
}) {
1516
const [message, setMessage] = useState(props.prefillMessage || "");
1617

1718
return (
18-
<div className="rounded-2xl border border-border bg-card p-2">
19+
<div
20+
className={cn(
21+
"rounded-2xl border border-border bg-card p-2",
22+
props.className,
23+
)}
24+
>
1925
<div className="max-h-[70vh] overflow-y-auto">
2026
<AutoResizeTextarea
2127
placeholder={"Ask Nebula"}

0 commit comments

Comments
 (0)