Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ feat(example): add para plugin example #327

Merged
merged 6 commits into from
Mar 19, 2025
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
7 changes: 7 additions & 0 deletions examples/para-plugin-example/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
LANGCHAIN_CALLBACKS_BACKGROUND=false
OPENAI_API_KEY=
RPC_URL=
SOLANA_PRIVATE_KEY=
PARA_API_KEY=
PARA_ENV=
GROQ_API_KEY=
3 changes: 3 additions & 0 deletions examples/para-plugin-example/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": "next/core-web-vitals"
}
43 changes: 43 additions & 0 deletions examples/para-plugin-example/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js

# testing
/coverage

# next.js
/.next/
/out/

# production
/build

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# local env files
.env*.local

# vercel
.vercel

# typescript
*.tsbuildinfo
next-env.d.ts

.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/sdks
!.yarn/versions
.env
1 change: 1 addition & 0 deletions examples/para-plugin-example/.prettierrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
21 changes: 21 additions & 0 deletions examples/para-plugin-example/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2023 LangChain

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
115 changes: 115 additions & 0 deletions examples/para-plugin-example/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
# Para Plugin Example

This repository demonstrates how to integrate and use Solana Agent Kit v2 with Para plugins in your application.

## 🎥 Demo

Watch our implementation demo: [YouTube Video](https://www.youtube.com/watch?v=qItH-SnOcr8)

## 🚀 Features

- Integration of [solana-plugin-para](https://github.com/uratmangun/solana-plugin-para) for backend and frontend respectively
- Complete example of Para wallet management
- Real-world usage patterns and best practices

## 📦 Prerequisites

- Node.js 16.x or higher
- pnpm or bun package manager
- Solana development environment

## 🛠️ Installation

1. Clone the repository:
```bash
git clone <repository-url>
```

2. Install dependencies:
In order to install this dependency:

```
"@getpara/plugin-para-server": "file:../../../solana-plugin-para/packages/plugin-para-server",
"@getpara/plugin-para-web": "file:../../../solana-plugin-para/packages/plugin-para-web",
```

```
git clone https://github.com/uratmangun/solana-plugin-para/
cd solana-plugin-para
pnpm install
pnpm build
cd ..
cd <this-repository-folder>/examples/para-plugin-example
pnpm install
```

3. Copy the environment variables:
```bash

cp .env.example .env
```

4. Update the `.env` file with your credentials:
```env
LANGCHAIN_CALLBACKS_BACKGROUND=false
OPENAI_API_KEY=#optional
RPC_URL=
SOLANA_PRIVATE_KEY=
PARA_API_KEY=
PARA_ENV=BETA | PROD
GROQ_API_KEY=
NEXT_PUBLIC_PARA_ENV=BETA | PROD
NEXT_PUBLIC_PARA_API_KEY=
```

## 🏃‍♂️ Running the Example

1. Start the development server:
```bash
pnpm dev
```

2. Open [http://localhost:3000](http://localhost:3000) in your browser

## 📚 Implementation Details

### Server-Side Integration

```typescript
import { SolanaAgentKit } from "solana-agent-kit";
import ParaServerPlugin from "@getpara/plugin-para-server";

const solanaAgent = new SolanaAgentKit(/* config */);
export const solanaAgentWithPara = solanaAgent.use(ParaServerPlugin);
```

### Web Integration

```typescript
import ParaWebPlugin from "@getpara/plugin-para-web";
import { solanaAgent } from "./solana";

export const solanaAgentWithPara = solanaAgent.use(ParaWebPlugin);
export const para = solanaAgentWithPara.methods.getParaInstance();
```

## 🔑 Key Components

- `app/api/*` - API routes for Para operations
- `utils/*` - Utility functions and configurations
- `components/*` - React components for the UI
- `public/*` - Static assets

## 📖 Documentation

For more detailed information about the plugins, visit:
- [Para Documentation](https://docs.getpara.com/integration-guides/solana)
- [Solana Agent Kit v2 Documentation](https://github.com/sendaifun/solana-agent-kit/tree/v2)

## 🤝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

## 📄 License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
52 changes: 52 additions & 0 deletions examples/para-plugin-example/app/api/chat/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { NextRequest } from "next/server";
import { Message,LanguageModelV1,streamText,tool } from "ai";
import { createGroq } from "@ai-sdk/groq";
// import { createOpenAI } from "@ai-sdk/openai";
import { createVercelAITools } from "solana-agent-kit";
import { solanaAgentWithPara } from "@/utils/init_server";
import {listParaToolsWeb} from "@/utils/get_all_tools"
// Initialize Groq with the mixtral model
const groq = createGroq({
baseURL: "https://api.groq.com/openai/v1",
apiKey: process.env.GROQ_API_KEY,
});
// const openai = createOpenAI({
// baseURL: "https://generativelanguage.googleapis.com/v1beta/openai/",
// apiKey: process.env.OPENAI_API_KEY,
// });
export async function POST(req: NextRequest) {
try {
const { messages } = await req.json() as { messages: Message[] };


const vercelTools = createVercelAITools(solanaAgentWithPara, solanaAgentWithPara.actions);
const webTools = listParaToolsWeb()

const tools = {...vercelTools,
...webTools
}
const result = await streamText({
model: groq("deepseek-r1-distill-llama-70b") as LanguageModelV1,
tools:tools as any,
system: `
You are a helpful agent that can interact onchain using the Solana Agent Kit. You are
empowered to interact onchain using your tools. 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. If there is a 5XX
(internal) HTTP error code, ask the user to try again later. 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
themselves using the Solana Agent Kit, recommend they go to https://www.solanaagentkit.xyz for more information. Be
concise and helpful with your responses. Refrain from restating your tools' descriptions unless it is explicitly requested.
`,
messages
});


return result.toDataStreamResponse();
} catch (error: any) {
console.error("Error in chat route:", error);
return new Response(JSON.stringify({ error: error.message }), {
status: error.status || 500,
headers: { 'Content-Type': 'application/json' },
});
}
}
6 changes: 6 additions & 0 deletions examples/para-plugin-example/app/api/usershare/db.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import Datastore from 'nedb';

// Create an in-memory database instance
const db = new Datastore({ inMemoryOnly: true });

export default db;
118 changes: 118 additions & 0 deletions examples/para-plugin-example/app/api/usershare/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import { NextResponse } from 'next/server';
import db from './db';

// POST /api/usershare - Save user share data
export async function POST(request: Request) {
try {
const body = await request.json();
const { email, userShare } = body;

if (!email || !userShare) {
return NextResponse.json(
{ error: 'Email and userShare are required' },
{ status: 400 }
);
}

// Insert the data into the database
db.insert({ email, userShare }, (err, newDoc) => {
if (err) {
return NextResponse.json(
{ error: 'Failed to save data' },
{ status: 500 }
);
}
});

return NextResponse.json({ message: 'Data saved successfully' });
} catch (error) {
return NextResponse.json(
{ error: 'Internal server error' },
{ status: 500 }
);
}
}

// GET /api/usershare?email=xxx - Get user share data by email
export async function GET(request: Request) {
try {
const { searchParams } = new URL(request.url);
const email = searchParams.get('email');

if (!email) {
return NextResponse.json(
{ error: 'Email parameter is required' },
{ status: 400 }
);
}

return new Promise((resolve) => {
db.findOne({ email }, (err, doc) => {
if (err) {
resolve(NextResponse.json(
{ error: 'Failed to retrieve data' },
{ status: 500 }
));
return;
}

if (!doc) {
resolve(NextResponse.json(
{ error: 'No data found for this email' },
{ status: 404 }
));
return;
}

resolve(NextResponse.json(doc));
});
});
} catch (error) {
return NextResponse.json(
{ error: 'Internal server error' },
{ status: 500 }
);
}
}

// DELETE /api/usershare?email=xxx - Delete user share data by email
export async function DELETE(request: Request) {
try {
const { searchParams } = new URL(request.url);
const email = searchParams.get('email');

if (!email) {
return NextResponse.json(
{ error: 'Email parameter is required' },
{ status: 400 }
);
}

return new Promise((resolve) => {
db.remove({ email }, {}, (err, numRemoved) => {
if (err) {
resolve(NextResponse.json(
{ error: 'Failed to delete data' },
{ status: 500 }
));
return;
}

if (numRemoved === 0) {
resolve(NextResponse.json(
{ error: 'No data found for this email' },
{ status: 404 }
));
return;
}

resolve(NextResponse.json({ message: 'Data deleted successfully' }));
});
});
} catch (error) {
return NextResponse.json(
{ error: 'Internal server error' },
{ status: 500 }
);
}
}
Loading