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

add thirdweb plugin #1418

Merged
merged 3 commits into from
Jan 8, 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
3 changes: 3 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,9 @@ CHARITY_ADDRESS_ETH=0x750EF1D7a0b4Ab1c97B7A623D7917CcEb5ea779C
CHARITY_ADDRESS_ARB=0x1234567890123456789012345678901234567890
CHARITY_ADDRESS_POL=0x1234567890123456789012345678901234567890

# thirdweb
THIRDWEB_SECRET_KEY= # Create key on thirdweb developer dashboard: https://thirdweb.com/

# Conflux Configuration
CONFLUX_CORE_PRIVATE_KEY=
CONFLUX_CORE_SPACE_RPC_URL=
Expand Down
1 change: 1 addition & 0 deletions agent/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
"@elizaos/plugin-fuel": "workspace:*",
"@elizaos/plugin-avalanche": "workspace:*",
"@elizaos/plugin-web-search": "workspace:*",
"@elizaos/plugin-thirdweb": "workspace:*",
"@elizaos/plugin-genlayer": "workspace:*",
"@elizaos/plugin-open-weather": "workspace:*",
"@elizaos/plugin-obsidian": "workspace:*",
Expand Down
5 changes: 3 additions & 2 deletions agent/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,14 +70,14 @@ import { TEEMode, teePlugin } from "@elizaos/plugin-tee";
import { teeMarlinPlugin } from "@elizaos/plugin-tee-marlin";
import { tonPlugin } from "@elizaos/plugin-ton";
import { webSearchPlugin } from "@elizaos/plugin-web-search";
import { echoChamberPlugin } from "@elizaos/plugin-echochambers";
import { thirdwebPlugin } from "@elizaos/plugin-thirdweb";
import { zksyncEraPlugin } from "@elizaos/plugin-zksync-era";
import { availPlugin } from "@elizaos/plugin-avail";
import { openWeatherPlugin } from "@elizaos/plugin-open-weather";

import { artheraPlugin } from "@elizaos/plugin-arthera";
import { stargazePlugin } from "@elizaos/plugin-stargaze";
import { obsidianPlugin } from "@elizaos/plugin-obsidian";

import Database from "better-sqlite3";
import fs from "fs";
import net from "net";
Expand Down Expand Up @@ -644,6 +644,7 @@ export async function createAgent(
: null,
getSecret(character, "TEE_MARLIN") ? teeMarlinPlugin : null,
getSecret(character, "TON_PRIVATE_KEY") ? tonPlugin : null,
getSecret(character, "THIRDWEB_SECRET_KEY") ? thirdwebPlugin : null,
getSecret(character, "SUI_PRIVATE_KEY") ? suiPlugin : null,
getSecret(character, "STORY_PRIVATE_KEY") ? storyPlugin : null,
getSecret(character, "FUEL_PRIVATE_KEY") ? fuelPlugin : null,
Expand Down
6 changes: 6 additions & 0 deletions packages/plugin-thirdweb/.npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
*

!dist/**
!package.json
!readme.md
!tsup.config.ts
62 changes: 62 additions & 0 deletions packages/plugin-thirdweb/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# `ai16z/plugin-thirdweb`

This plugin provides access to thirdweb's Nebula AI interface: [https://portal.thirdweb.com/nebula](https://portal.thirdweb.com/nebula).

## Configuration

### Default Setup

By default, \*_thirdweb plugin_ is enabled. To use it, simply add your secret key to the `.env` file:

```env
THIRDWEB_SECRET_KEY=your-thirdweb-secret-key-here
```

---

## Actions

### Chat

Interact with the thirdweb Nebula natural language interface to perform any of the following:

- Analyze any smart contract's functionality and features
- Explain contract interfaces and supported standards
- Read contract data and state
- Help you understand function behaviors and parameters
- Decode complex contract interactions
- Retrieve detailed contract metadata and source code analysis
- Provide real-time network status and gas prices
- Explain block and transaction details
- Help you understand blockchain network specifications
- Offer insights about different blockchain networks
- Track transaction status and history
- Access detailed chain metadata including RPC endpoints
- Look up token information across different networks
- Track token prices and market data
- Explain token standards and implementations
- Help you understand token bridges and cross-chain aspects
- Monitor trading pairs and liquidity
- Fetch token metadata and current exchange rates
- Retrieve detailed transaction information using transaction hashes
- Provide wallet balance and transaction history

**Example usage:**

```env
What is the ETH balance for 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045
```

```env
What is the total NFT supply for 0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D?
```

```env
Does 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 hold USDC on Base?
```

```env
What is the address of USDC on Ethereum?
```

---
3 changes: 3 additions & 0 deletions packages/plugin-thirdweb/eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import eslintGlobalConfig from "../../eslint.config.mjs";

export default [...eslintGlobalConfig];
20 changes: 20 additions & 0 deletions packages/plugin-thirdweb/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"name": "@elizaos/plugin-thirdweb",
"version": "0.1.7-alpha.2",
"main": "dist/index.js",
"type": "module",
"types": "dist/index.d.ts",
"dependencies": {
"@elizaos/core": "workspace:*",
"thirdweb": "^5.80.0",
"tsup": "8.3.5"
},
"scripts": {
"build": "tsup --format esm --dts",
"dev": "tsup --format esm --dts --watch",
"lint": "eslint --fix --cache ."
},
"peerDependencies": {
"whatwg-url": "7.1.0"
}
}
214 changes: 214 additions & 0 deletions packages/plugin-thirdweb/src/actions/chat.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
import {
elizaLogger,
HandlerCallback,
IAgentRuntime,
Memory,
State,
type Action,
} from "@elizaos/core";

const BASE_URL = "https://nebula-api.thirdweb.com";

// If chat is a stream, wait for stream to complete before returning response
async function handleStreamResponse(
response: Response
): Promise<ReadableStream> {
elizaLogger.log("Starting stream response handling");
const reader = response.body?.getReader();
if (!reader) {
elizaLogger.error("No readable stream available");
throw new Error("No readable stream available");
}

return new ReadableStream({
async start(controller) {
try {
while (true) {
const { done, value } = await reader.read();
if (done) {
elizaLogger.log("Stream reading completed");
break;
}

const events = new TextDecoder()
.decode(value)
.split("\n\n");
elizaLogger.debug(
`Processing ${events.length} stream events`
);
for (const event of events) {
if (!event.trim()) continue;
controller.enqueue(event);
}
}
} finally {
reader.releaseLock();
controller.close();
elizaLogger.log("Stream controller closed");
}
},
});
}

// Process & return a response to the current message with thirdweb Nebula
export const blockchainChatAction: Action = {
name: "BLOCKCHAIN_CHAT",
similes: [
"QUERY_BLOCKCHAIN",
"CHECK_BLOCKCHAIN",
"BLOCKCHAIN_SEARCH",
"CRYPTO_LOOKUP",
"WEB3_SEARCH",
"BLOCKCHAIN_HISTORY_EXPLORER",
"UNIVERSAL_BLOCKCHAIN_TRANSALTOR",
"BLOCKCHAIN_DATA_PROVIDER",
"HISTORICAL_BLOCKCHAIN_DATA",
"TRACK_BLOCKCHAIN_TRANSACTIONS",
"BLOCKCHAIN_INTERPRETER",
"BLOCKCHAIN_TRANSACTION_DETAILS",
],
validate: async (
runtime: IAgentRuntime,
_message: Memory
): Promise<boolean> => {
const secretKey =
runtime.getSetting("THIRDWEB_SECRET_KEY") ??
process.env.THIRDWEB_SECRET_KEY;
return Boolean(secretKey);
},
description:
"Query blockchain data and execute transactions through natural language interaction with the Nebula API",
handler: async (
runtime: IAgentRuntime,
message: Memory,
_state: State,
_options: any,
callback: HandlerCallback
): Promise<any> => {
try {
elizaLogger.log("Starting blockchain chat handler");
const secretKey =
runtime.getSetting("THIRDWEB_SECRET_KEY") ??
process.env.THIRDWEB_SECRET_KEY;

if (!secretKey) {
elizaLogger.error("THIRDWEB_SECRET_KEY not configured");
throw new Error("THIRDWEB_SECRET_KEY is not configured");
}

const request = {
message: message.content.text,
stream: false,
};

elizaLogger.log("NEBULA CHAT REQUEST: ", request);

elizaLogger.debug("Sending request to Nebula API");
const response = await fetch(`${BASE_URL}/chat`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"x-secret-key": secretKey,
},
body: JSON.stringify(request),
});
elizaLogger.debug("Received response from Nebula API");

if (!request.stream) {
const text = await response.text();
elizaLogger.debug("Raw response text:", text);

try {
const cleanedText = text.trim().split("\n").pop() || text;
const parsed = JSON.parse(cleanedText);
elizaLogger.log("Successfully parsed response:", parsed);

console.log(parsed.message);

await callback({ text: parsed.message });

return parsed;
} catch (parseError) {
elizaLogger.error("Parse error details:", parseError);
elizaLogger.error(
"Failed to parse JSON response. Raw text:",
text
);
return { text: text };
}
}

elizaLogger.log("Handling streaming response");
return handleStreamResponse(response);
} catch (error) {
elizaLogger.error("Blockchain chat failed:", error);
throw new Error(`Blockchain chat failed: ${error.message}`);
}
},
examples: [
[
{
user: "{{user1}}",
content: {
text: "What's the ETH balance of vitalik.eth?",
action: "BLOCKCHAIN_CHAT",
},
},
{
user: "{{user2}}",
content: {
text: "The current ETH balance of vitalik.eth (0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045) is 1,123.45 ETH",
action: "BLOCKCHAIN_CHAT",
},
},
],
[
{
user: "{{user1}}",
content: {
text: "send 0.1 ETH to 0x742d35Cc6634C0532925a3b844Bc454e4438f44e",
action: "BLOCKCHAIN_CHAT",
},
},
{
user: "{{user2}}",
content: {
text: "I'll help you send 0.1 ETH. Please review and sign the transaction.",
action: "BLOCKCHAIN_CHAT",
},
},
],
[
{
user: "{{user1}}",
content: {
text: "Show me the floor price of BAYC",
action: "BLOCKCHAIN_CHAT",
},
},
{
user: "{{user2}}",
content: {
text: "The current floor price for BAYC is 32.5 ETH with 3 sales in the last 24h",
action: "BLOCKCHAIN_CHAT",
},
},
],
[
{
user: "{{user1}}",
content: {
text: "Show me my recent transactions",
action: "BLOCKCHAIN_CHAT",
},
},
{
user: "{{user2}}",
content: {
text: "Here are your recent transactions: 1. Sent 1.5 ETH 2. Swapped tokens on Uniswap 3. Received 0.5 ETH",
action: "BLOCKCHAIN_CHAT",
},
},
],
],
} as Action;
1 change: 1 addition & 0 deletions packages/plugin-thirdweb/src/actions/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./chat.ts";
12 changes: 12 additions & 0 deletions packages/plugin-thirdweb/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Plugin } from "@elizaos/core";
import { blockchainChatAction } from "./actions/chat";
export * as actions from "./actions/index.ts";

export const thirdwebPlugin: Plugin = {
name: "PROVIDE_BLOCKCHAIN_DATA",
description:
"Search the blockchain with thirdweb Nebula for information about wallet addresses, token prices, token owners, transactions and their details.",
actions: [blockchainChatAction],
evaluators: [],
providers: [],
};
13 changes: 13 additions & 0 deletions packages/plugin-thirdweb/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"extends": "../core/tsconfig.json",
"compilerOptions": {
"outDir": "dist",
"rootDir": "src",
"types": [
"node"
]
},
"include": [
"src/**/*.ts"
]
}
Loading
Loading