Skip to content

Commit 86a6316

Browse files
authored
Wormhole Agent Next JS Demo App (#321)
# Pull Request Description ## Related Issue Fixes #123 (UI improvements for cross-chain transfers) ## Changes Made This PR adds the following changes: - Added UI support for CCTP transfers with a distinct purple-themed interface - Fixed token transfer UI display for better user experience - Replaced img elements with Next.js Image components for better performance - Fixed TypeScript linting issues throughout the codebase - Updated README with comprehensive project documentation ## Implementation Details - Created a new CCTPTransferResult interface to properly type CCTP transfer responses - Implemented a dedicated renderCCTPTransfer function with a purple-themed UI to distinguish from regular token transfers - Enhanced the tool call rendering to support both token transfers and CCTP transfers with consistent styling - Fixed TypeScript linting issues by replacing 'any' types with more specific types - Updated the avatar component to use Next.js Image component for better performance and optimization ## Transaction executed by agent Example transaction: CCTP transfer of 1 USDC from Solana to BaseSepolia - Source Transaction: 5PspBSgHY7wZvJzvGai85eQgXXVE9FN8oxYLGrHNsFapU7Fg8rRKUHRPvZwuHPQfuEDUwNYRheNe9hvwfXR9Txhe - Attestation Hash: 0x542065291bd4754270f8ddeb66bc039052aa7d125505539d280c2e8bf6070535 - Destination Transaction: 0x57f753d222916e48b0bef3d4f488f3a1f05e094969155b4c2fe7241b220a4dbe ## Prompt Used transfer 0.01 sol to sui send 1 USDC from Solana to BaseSepolia using CCTP You can refer the demo video here [Wormhole Demo](https://screen.studio/share/7ibFKDyy) ## Additional Notes The CCTP transfer UI now matches the style of the token transfer UI but uses a purple color theme to distinguish it. This provides a consistent user experience while making it clear which type of transfer is being performed. ## Checklist - [x] I have tested these changes locally - [x] I have updated the documentation - [x] I have added a transaction link - [x] I have added the prompt used to test it
2 parents 7f83da4 + 165bcf2 commit 86a6316

30 files changed

Lines changed: 43484 additions & 0 deletions
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
2+
RPC_URL=https://api.devnet.solana.com
3+
OPENAI_API_KEY=sk-proj--
4+
SOLANA_PRIVATE_KEY=
5+
6+
7+
8+
# wormhole
9+
10+
11+
ETH_PRIVATE_KEY=
12+
BASE_PRIVATE_KEY=
13+
SUI_MNEMONIC=""
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2+
3+
# dependencies
4+
/node_modules
5+
/.pnp
6+
.pnp.*
7+
.yarn/*
8+
!.yarn/patches
9+
!.yarn/plugins
10+
!.yarn/releases
11+
!.yarn/versions
12+
13+
# testing
14+
/coverage
15+
16+
# next.js
17+
/.next/
18+
/out/
19+
20+
# production
21+
/build
22+
23+
# misc
24+
.DS_Store
25+
*.pem
26+
27+
# debug
28+
npm-debug.log*
29+
yarn-debug.log*
30+
yarn-error.log*
31+
.pnpm-debug.log*
32+
33+
# env files (can opt-in for committing if needed)
34+
.env*
35+
36+
# vercel
37+
.vercel
38+
39+
# typescript
40+
*.tsbuildinfo
41+
next-env.d.ts
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
# Wormhole NextJS Agent
2+
3+
This is a Next.js application that demonstrates the use of the Solana Agent Kit to create a Wormhole agent for cross-chain operations. The agent provides a chat interface for interacting with Wormhole's cross-chain messaging protocol, allowing users to perform token transfers and CCTP (Cross-Chain Transfer Protocol) operations seamlessly.
4+
5+
## Features
6+
7+
- **Interactive Chat Interface**: User-friendly chat UI for interacting with the Wormhole agent
8+
- **Cross-Chain Operations**:
9+
- Token transfers between supported blockchains
10+
- CCTP transfers for USDC between supported chains
11+
- Chain information retrieval
12+
- **Real-Time Transaction Tracking**: Visual feedback for transaction status and completion
13+
- **Transaction History**: View details of completed transactions including source and destination transactions
14+
- **Responsive Design**: Works on desktop and mobile devices with dark mode support
15+
- **Real-Time Logs**: Debug panel showing agent operations in real-time
16+
17+
## Cross-Chain Operations Supported
18+
19+
The agent supports the following operations:
20+
21+
1. **Token Transfers**: Transfer tokens between different blockchains using Wormhole's token bridge
22+
2. **CCTP Transfers**: Transfer USDC between chains using Circle's Cross-Chain Transfer Protocol
23+
3. **Chain Information**: Get information about supported chains and their addresses
24+
25+
## UI Components
26+
27+
The application includes custom UI components for displaying different types of operations:
28+
29+
- **Token Transfer UI**: Green-themed interface showing token transfer details
30+
- **CCTP Transfer UI**: Purple-themed interface showing CCTP transfer details
31+
- **Supported Chains UI**: Grid display of supported chains grouped by network
32+
33+
## Getting Started
34+
35+
First, set up your environment variables:
36+
37+
1. Copy `.example.env` to `.env.local`
38+
2. Fill in the required API keys and configuration values
39+
40+
Then, run the development server:
41+
42+
```bash
43+
npm run dev
44+
# or
45+
yarn dev
46+
# or
47+
pnpm dev
48+
# or
49+
bun dev
50+
```
51+
52+
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
53+
54+
## Usage Examples
55+
56+
Here are some example commands you can try with the agent:
57+
58+
- "What chains do you support?"
59+
- "Transfer 0.01 SOL from Solana to Sui"
60+
- "Send 1 USDC from Solana to Base Sepolia using CCTP"
61+
- "What is Wormhole?"
62+
63+
## Technologies Used
64+
65+
- **Next.js**: React framework for the frontend
66+
- **React**: UI library
67+
- **TypeScript**: Type-safe JavaScript
68+
- **Tailwind CSS**: Utility-first CSS framework
69+
- **Solana Agent Kit**: Framework for building blockchain agents
70+
- **Wormhole SDK**: For cross-chain operations
71+
72+
## Brand Guidelines
73+
74+
This project follows the Wormhole brand guidelines with the following color palette:
75+
76+
- **Black**: #000000 (CMYK: 0/0/0/100, RGB: 0/0/0)
77+
- **White**: #FFFFFF (CMYK: 0/0/0/0, RGB: 255/255/255)
78+
- **Plum**: #C1BBF6 (CMYK: 22/24/0/4, RGB: 193/187/246)
79+
- **Yellow**: #DDE95A (CMYK: 5/0/61/9, RGB: 221/233/90)
80+
- **Coral**: #FD8058 (CMYK: 0/62/67/0, RGB: 253/128/88)
81+
82+
## Project Structure
83+
84+
- `/app`: Next.js app router pages and API routes
85+
- `/components`: React components including:
86+
- `MessageBubble.tsx`: Handles rendering of different message types
87+
- `ChatInput.tsx`: User input component
88+
- `LogsPanel.tsx`: Debug logs display
89+
- `/public`: Static assets including brand icons
90+
91+
## License
92+
93+
This project is licensed under the MIT License.
94+
95+
## Learn More
96+
97+
- [Wormhole Documentation](https://docs.wormhole.com/wormhole/) - Learn about Wormhole's cross-chain messaging protocol
98+
- [Circle CCTP Documentation](https://developers.circle.com/stablecoins/docs/cctp-getting-started) - Learn about Circle's Cross-Chain Transfer Protocol
99+
- [Next.js Documentation](https://nextjs.org/docs) - Learn about Next.js features and API
100+
- [Solana Agent Kit](https://github.com/solana-labs/solana-agent-kit) - Framework for building blockchain agents
101+
102+
## Deploy on Vercel
103+
104+
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new) from the creators of Next.js.
105+
106+
Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
import { NextRequest, NextResponse } from "next/server";
2+
import { ChatOpenAI } from "@langchain/openai";
3+
import { createReactAgent } from "@langchain/langgraph/prebuilt";
4+
import { SolanaAgentKit } from "solana-agent-kit";
5+
import {
6+
CreateWrappedTokenTool,
7+
CctpTransferTool,
8+
GetWormholeSupportedChainsTool,
9+
TokenTransferTool,
10+
} from "solana-agent-kit/dist/langchain";
11+
12+
const llm = new ChatOpenAI({
13+
temperature: 0.7,
14+
model: "gpt-4o-mini",
15+
cache: false,
16+
});
17+
18+
const solanaAgent = new SolanaAgentKit(
19+
process.env.SOLANA_PRIVATE_KEY!,
20+
process.env.RPC_URL!,
21+
process.env.OPENAI_API_KEY!,
22+
);
23+
24+
const tools = [
25+
new GetWormholeSupportedChainsTool(solanaAgent),
26+
new TokenTransferTool(solanaAgent),
27+
new CctpTransferTool(solanaAgent),
28+
new CreateWrappedTokenTool(solanaAgent),
29+
];
30+
31+
const agent = createReactAgent({
32+
llm,
33+
tools,
34+
messageModifier: `
35+
You are a helpful agent that specializes in cross-chain operations using Wormhole Protocol on Solana.
36+
You can help users transfer tokens between different blockchains, create wrapped tokens, and perform CCTP transfers.
37+
If you ever need funds, you can request them from the faucet. If not, you can provide your wallet details and request funds from the user.
38+
If there is a 5XX (internal) HTTP error code, ask the user to try again later.
39+
If someone asks you to do something you can't do with your currently available tools, you must say so, and encourage them to implement it
40+
themselves using the Solana Agent Kit, recommend they go to https://www.solanaagentkit.xyz for more information.
41+
Be concise and helpful with your responses. Refrain from restating your tools' descriptions unless it is explicitly requested.
42+
IMPORTANT: Never repeat the user's question in your response.
43+
`,
44+
});
45+
46+
export async function POST(req: NextRequest) {
47+
const { messages } = await req.json();
48+
49+
console.log("messages", messages);
50+
51+
// Create a streaming response
52+
const encoder = new TextEncoder();
53+
const stream = new ReadableStream({
54+
async start(controller) {
55+
try {
56+
const agentStream = await agent.stream(
57+
{ messages },
58+
{
59+
streamMode: "values",
60+
},
61+
);
62+
63+
for await (const { messages: agentMessages } of agentStream) {
64+
const latestMsg = agentMessages[agentMessages.length - 1];
65+
let chunk;
66+
67+
if (latestMsg?.content) {
68+
// Remove any instances where the agent repeats the user's last question
69+
const lastUserMessage = messages[messages.length - 1].content;
70+
let content = latestMsg.content;
71+
72+
// Check if the response starts with the user's question
73+
if (content.startsWith(lastUserMessage)) {
74+
content = content.substring(lastUserMessage.length).trim();
75+
}
76+
77+
// Check if the response contains "what is wormhole?" followed by the answer
78+
const questionPattern = new RegExp(`^${lastUserMessage}\\s*`, "i");
79+
content = content.replace(questionPattern, "");
80+
81+
chunk = content;
82+
} else if (latestMsg?.tool_calls?.length > 0) {
83+
chunk = JSON.stringify(latestMsg.tool_calls);
84+
} else {
85+
chunk = JSON.stringify(latestMsg);
86+
}
87+
88+
controller.enqueue(
89+
encoder.encode(`data: ${JSON.stringify({ chunk })}\n\n`),
90+
);
91+
}
92+
93+
controller.enqueue(encoder.encode("data: [DONE]\n\n"));
94+
controller.close();
95+
} catch (error) {
96+
controller.error(error);
97+
}
98+
},
99+
});
100+
101+
return new NextResponse(stream, {
102+
headers: {
103+
"Content-Type": "text/event-stream",
104+
"Cache-Control": "no-cache",
105+
Connection: "keep-alive",
106+
},
107+
});
108+
}
25.3 KB
Binary file not shown.
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
@import "tailwindcss";
2+
3+
:root {
4+
/* Brand Colors */
5+
--black: #000000;
6+
--white: #FFFFFF;
7+
--plum: #C1BBF6;
8+
--yellow: #DDE95A;
9+
--coral: #FD8058;
10+
11+
/* Theme Variables */
12+
--background: var(--white);
13+
--foreground: var(--black);
14+
--primary: var(--plum);
15+
--secondary: var(--yellow);
16+
--accent: var(--coral);
17+
}
18+
19+
@theme inline {
20+
--color-background: var(--background);
21+
--color-foreground: var(--foreground);
22+
--color-primary: var(--primary);
23+
--color-secondary: var(--secondary);
24+
--color-accent: var(--accent);
25+
--font-sans: var(--font-geist-sans);
26+
--font-mono: var(--font-geist-mono);
27+
}
28+
29+
@media (prefers-color-scheme: dark) {
30+
:root {
31+
--background: var(--black);
32+
--foreground: var(--white);
33+
}
34+
}
35+
36+
body {
37+
background: var(--background);
38+
color: var(--foreground);
39+
font-family: var(--font-sans, Arial, Helvetica, sans-serif);
40+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import type { Metadata } from "next";
2+
import { Geist, Geist_Mono } from "next/font/google";
3+
import "./globals.css";
4+
5+
const geistSans = Geist({
6+
variable: "--font-geist-sans",
7+
subsets: ["latin"],
8+
});
9+
10+
const geistMono = Geist_Mono({
11+
variable: "--font-geist-mono",
12+
subsets: ["latin"],
13+
});
14+
15+
export const metadata: Metadata = {
16+
title: "Wormhole Agent - Cross-Chain Operations",
17+
description: "A Wormhole agent for cross-chain operations powered by Solana Agent Kit",
18+
};
19+
20+
export default function RootLayout({
21+
children,
22+
}: Readonly<{
23+
children: React.ReactNode;
24+
}>) {
25+
return (
26+
<html lang="en">
27+
<body
28+
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
29+
>
30+
{children}
31+
</body>
32+
</html>
33+
);
34+
}

0 commit comments

Comments
 (0)