Skip to content

Commit 0787a87

Browse files
authored
Merge pull request elizaOS#1418 from iankm/thirdweb
add thirdweb plugin
2 parents cae46f5 + ce30c18 commit 0787a87

12 files changed

+358
-2
lines changed

.env.example

+3
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,9 @@ CHARITY_ADDRESS_ETH=0x750EF1D7a0b4Ab1c97B7A623D7917CcEb5ea779C
268268
CHARITY_ADDRESS_ARB=0x1234567890123456789012345678901234567890
269269
CHARITY_ADDRESS_POL=0x1234567890123456789012345678901234567890
270270

271+
# thirdweb
272+
THIRDWEB_SECRET_KEY= # Create key on thirdweb developer dashboard: https://thirdweb.com/
273+
271274
# Conflux Configuration
272275
CONFLUX_CORE_PRIVATE_KEY=
273276
CONFLUX_CORE_SPACE_RPC_URL=

agent/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@
6969
"@elizaos/plugin-fuel": "workspace:*",
7070
"@elizaos/plugin-avalanche": "workspace:*",
7171
"@elizaos/plugin-web-search": "workspace:*",
72+
"@elizaos/plugin-thirdweb": "workspace:*",
7273
"@elizaos/plugin-genlayer": "workspace:*",
7374
"@elizaos/plugin-open-weather": "workspace:*",
7475
"@elizaos/plugin-obsidian": "workspace:*",

agent/src/index.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -70,14 +70,14 @@ import { TEEMode, teePlugin } from "@elizaos/plugin-tee";
7070
import { teeMarlinPlugin } from "@elizaos/plugin-tee-marlin";
7171
import { tonPlugin } from "@elizaos/plugin-ton";
7272
import { webSearchPlugin } from "@elizaos/plugin-web-search";
73+
import { echoChamberPlugin } from "@elizaos/plugin-echochambers";
74+
import { thirdwebPlugin } from "@elizaos/plugin-thirdweb";
7375
import { zksyncEraPlugin } from "@elizaos/plugin-zksync-era";
7476
import { availPlugin } from "@elizaos/plugin-avail";
7577
import { openWeatherPlugin } from "@elizaos/plugin-open-weather";
76-
7778
import { artheraPlugin } from "@elizaos/plugin-arthera";
7879
import { stargazePlugin } from "@elizaos/plugin-stargaze";
7980
import { obsidianPlugin } from "@elizaos/plugin-obsidian";
80-
8181
import Database from "better-sqlite3";
8282
import fs from "fs";
8383
import net from "net";
@@ -644,6 +644,7 @@ export async function createAgent(
644644
: null,
645645
getSecret(character, "TEE_MARLIN") ? teeMarlinPlugin : null,
646646
getSecret(character, "TON_PRIVATE_KEY") ? tonPlugin : null,
647+
getSecret(character, "THIRDWEB_SECRET_KEY") ? thirdwebPlugin : null,
647648
getSecret(character, "SUI_PRIVATE_KEY") ? suiPlugin : null,
648649
getSecret(character, "STORY_PRIVATE_KEY") ? storyPlugin : null,
649650
getSecret(character, "FUEL_PRIVATE_KEY") ? fuelPlugin : null,

packages/plugin-thirdweb/.npmignore

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
*
2+
3+
!dist/**
4+
!package.json
5+
!readme.md
6+
!tsup.config.ts

packages/plugin-thirdweb/README.md

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# `ai16z/plugin-thirdweb`
2+
3+
This plugin provides access to thirdweb's Nebula AI interface: [https://portal.thirdweb.com/nebula](https://portal.thirdweb.com/nebula).
4+
5+
## Configuration
6+
7+
### Default Setup
8+
9+
By default, \*_thirdweb plugin_ is enabled. To use it, simply add your secret key to the `.env` file:
10+
11+
```env
12+
THIRDWEB_SECRET_KEY=your-thirdweb-secret-key-here
13+
```
14+
15+
---
16+
17+
## Actions
18+
19+
### Chat
20+
21+
Interact with the thirdweb Nebula natural language interface to perform any of the following:
22+
23+
- Analyze any smart contract's functionality and features
24+
- Explain contract interfaces and supported standards
25+
- Read contract data and state
26+
- Help you understand function behaviors and parameters
27+
- Decode complex contract interactions
28+
- Retrieve detailed contract metadata and source code analysis
29+
- Provide real-time network status and gas prices
30+
- Explain block and transaction details
31+
- Help you understand blockchain network specifications
32+
- Offer insights about different blockchain networks
33+
- Track transaction status and history
34+
- Access detailed chain metadata including RPC endpoints
35+
- Look up token information across different networks
36+
- Track token prices and market data
37+
- Explain token standards and implementations
38+
- Help you understand token bridges and cross-chain aspects
39+
- Monitor trading pairs and liquidity
40+
- Fetch token metadata and current exchange rates
41+
- Retrieve detailed transaction information using transaction hashes
42+
- Provide wallet balance and transaction history
43+
44+
**Example usage:**
45+
46+
```env
47+
What is the ETH balance for 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045
48+
```
49+
50+
```env
51+
What is the total NFT supply for 0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D?
52+
```
53+
54+
```env
55+
Does 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 hold USDC on Base?
56+
```
57+
58+
```env
59+
What is the address of USDC on Ethereum?
60+
```
61+
62+
---
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import eslintGlobalConfig from "../../eslint.config.mjs";
2+
3+
export default [...eslintGlobalConfig];

packages/plugin-thirdweb/package.json

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"name": "@elizaos/plugin-thirdweb",
3+
"version": "0.1.7-alpha.2",
4+
"main": "dist/index.js",
5+
"type": "module",
6+
"types": "dist/index.d.ts",
7+
"dependencies": {
8+
"@elizaos/core": "workspace:*",
9+
"thirdweb": "^5.80.0",
10+
"tsup": "8.3.5"
11+
},
12+
"scripts": {
13+
"build": "tsup --format esm --dts",
14+
"dev": "tsup --format esm --dts --watch",
15+
"lint": "eslint --fix --cache ."
16+
},
17+
"peerDependencies": {
18+
"whatwg-url": "7.1.0"
19+
}
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
import {
2+
elizaLogger,
3+
HandlerCallback,
4+
IAgentRuntime,
5+
Memory,
6+
State,
7+
type Action,
8+
} from "@elizaos/core";
9+
10+
const BASE_URL = "https://nebula-api.thirdweb.com";
11+
12+
// If chat is a stream, wait for stream to complete before returning response
13+
async function handleStreamResponse(
14+
response: Response
15+
): Promise<ReadableStream> {
16+
elizaLogger.log("Starting stream response handling");
17+
const reader = response.body?.getReader();
18+
if (!reader) {
19+
elizaLogger.error("No readable stream available");
20+
throw new Error("No readable stream available");
21+
}
22+
23+
return new ReadableStream({
24+
async start(controller) {
25+
try {
26+
while (true) {
27+
const { done, value } = await reader.read();
28+
if (done) {
29+
elizaLogger.log("Stream reading completed");
30+
break;
31+
}
32+
33+
const events = new TextDecoder()
34+
.decode(value)
35+
.split("\n\n");
36+
elizaLogger.debug(
37+
`Processing ${events.length} stream events`
38+
);
39+
for (const event of events) {
40+
if (!event.trim()) continue;
41+
controller.enqueue(event);
42+
}
43+
}
44+
} finally {
45+
reader.releaseLock();
46+
controller.close();
47+
elizaLogger.log("Stream controller closed");
48+
}
49+
},
50+
});
51+
}
52+
53+
// Process & return a response to the current message with thirdweb Nebula
54+
export const blockchainChatAction: Action = {
55+
name: "BLOCKCHAIN_CHAT",
56+
similes: [
57+
"QUERY_BLOCKCHAIN",
58+
"CHECK_BLOCKCHAIN",
59+
"BLOCKCHAIN_SEARCH",
60+
"CRYPTO_LOOKUP",
61+
"WEB3_SEARCH",
62+
"BLOCKCHAIN_HISTORY_EXPLORER",
63+
"UNIVERSAL_BLOCKCHAIN_TRANSALTOR",
64+
"BLOCKCHAIN_DATA_PROVIDER",
65+
"HISTORICAL_BLOCKCHAIN_DATA",
66+
"TRACK_BLOCKCHAIN_TRANSACTIONS",
67+
"BLOCKCHAIN_INTERPRETER",
68+
"BLOCKCHAIN_TRANSACTION_DETAILS",
69+
],
70+
validate: async (
71+
runtime: IAgentRuntime,
72+
_message: Memory
73+
): Promise<boolean> => {
74+
const secretKey =
75+
runtime.getSetting("THIRDWEB_SECRET_KEY") ??
76+
process.env.THIRDWEB_SECRET_KEY;
77+
return Boolean(secretKey);
78+
},
79+
description:
80+
"Query blockchain data and execute transactions through natural language interaction with the Nebula API",
81+
handler: async (
82+
runtime: IAgentRuntime,
83+
message: Memory,
84+
_state: State,
85+
_options: any,
86+
callback: HandlerCallback
87+
): Promise<any> => {
88+
try {
89+
elizaLogger.log("Starting blockchain chat handler");
90+
const secretKey =
91+
runtime.getSetting("THIRDWEB_SECRET_KEY") ??
92+
process.env.THIRDWEB_SECRET_KEY;
93+
94+
if (!secretKey) {
95+
elizaLogger.error("THIRDWEB_SECRET_KEY not configured");
96+
throw new Error("THIRDWEB_SECRET_KEY is not configured");
97+
}
98+
99+
const request = {
100+
message: message.content.text,
101+
stream: false,
102+
};
103+
104+
elizaLogger.log("NEBULA CHAT REQUEST: ", request);
105+
106+
elizaLogger.debug("Sending request to Nebula API");
107+
const response = await fetch(`${BASE_URL}/chat`, {
108+
method: "POST",
109+
headers: {
110+
"Content-Type": "application/json",
111+
"x-secret-key": secretKey,
112+
},
113+
body: JSON.stringify(request),
114+
});
115+
elizaLogger.debug("Received response from Nebula API");
116+
117+
if (!request.stream) {
118+
const text = await response.text();
119+
elizaLogger.debug("Raw response text:", text);
120+
121+
try {
122+
const cleanedText = text.trim().split("\n").pop() || text;
123+
const parsed = JSON.parse(cleanedText);
124+
elizaLogger.log("Successfully parsed response:", parsed);
125+
126+
console.log(parsed.message);
127+
128+
await callback({ text: parsed.message });
129+
130+
return parsed;
131+
} catch (parseError) {
132+
elizaLogger.error("Parse error details:", parseError);
133+
elizaLogger.error(
134+
"Failed to parse JSON response. Raw text:",
135+
text
136+
);
137+
return { text: text };
138+
}
139+
}
140+
141+
elizaLogger.log("Handling streaming response");
142+
return handleStreamResponse(response);
143+
} catch (error) {
144+
elizaLogger.error("Blockchain chat failed:", error);
145+
throw new Error(`Blockchain chat failed: ${error.message}`);
146+
}
147+
},
148+
examples: [
149+
[
150+
{
151+
user: "{{user1}}",
152+
content: {
153+
text: "What's the ETH balance of vitalik.eth?",
154+
action: "BLOCKCHAIN_CHAT",
155+
},
156+
},
157+
{
158+
user: "{{user2}}",
159+
content: {
160+
text: "The current ETH balance of vitalik.eth (0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045) is 1,123.45 ETH",
161+
action: "BLOCKCHAIN_CHAT",
162+
},
163+
},
164+
],
165+
[
166+
{
167+
user: "{{user1}}",
168+
content: {
169+
text: "send 0.1 ETH to 0x742d35Cc6634C0532925a3b844Bc454e4438f44e",
170+
action: "BLOCKCHAIN_CHAT",
171+
},
172+
},
173+
{
174+
user: "{{user2}}",
175+
content: {
176+
text: "I'll help you send 0.1 ETH. Please review and sign the transaction.",
177+
action: "BLOCKCHAIN_CHAT",
178+
},
179+
},
180+
],
181+
[
182+
{
183+
user: "{{user1}}",
184+
content: {
185+
text: "Show me the floor price of BAYC",
186+
action: "BLOCKCHAIN_CHAT",
187+
},
188+
},
189+
{
190+
user: "{{user2}}",
191+
content: {
192+
text: "The current floor price for BAYC is 32.5 ETH with 3 sales in the last 24h",
193+
action: "BLOCKCHAIN_CHAT",
194+
},
195+
},
196+
],
197+
[
198+
{
199+
user: "{{user1}}",
200+
content: {
201+
text: "Show me my recent transactions",
202+
action: "BLOCKCHAIN_CHAT",
203+
},
204+
},
205+
{
206+
user: "{{user2}}",
207+
content: {
208+
text: "Here are your recent transactions: 1. Sent 1.5 ETH 2. Swapped tokens on Uniswap 3. Received 0.5 ETH",
209+
action: "BLOCKCHAIN_CHAT",
210+
},
211+
},
212+
],
213+
],
214+
} as Action;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from "./chat.ts";

packages/plugin-thirdweb/src/index.ts

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { Plugin } from "@elizaos/core";
2+
import { blockchainChatAction } from "./actions/chat";
3+
export * as actions from "./actions/index.ts";
4+
5+
export const thirdwebPlugin: Plugin = {
6+
name: "PROVIDE_BLOCKCHAIN_DATA",
7+
description:
8+
"Search the blockchain with thirdweb Nebula for information about wallet addresses, token prices, token owners, transactions and their details.",
9+
actions: [blockchainChatAction],
10+
evaluators: [],
11+
providers: [],
12+
};
+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"extends": "../core/tsconfig.json",
3+
"compilerOptions": {
4+
"outDir": "dist",
5+
"rootDir": "src",
6+
"types": [
7+
"node"
8+
]
9+
},
10+
"include": [
11+
"src/**/*.ts"
12+
]
13+
}

0 commit comments

Comments
 (0)