Skip to content

Commit 0f643c0

Browse files
authored
feat: Add features to the Solana Agent Kit (#2458)
* wip: solana-agent-kit * feat: swap * feat: transfer * feat: swap dao * broken lock file * lock file * feat: add lend/stake/bounty/token info action from agent kit * feat: agent kit pkg update * add actions * feat: Adds functions to the solana agent kit plugin * feat: rename solana-agentkit to solana-agent-kit
1 parent e28353c commit 0f643c0

25 files changed

+3389
-2049
lines changed

agent/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@
6363
"@elizaos/plugin-node": "workspace:*",
6464
"@elizaos/plugin-solana": "workspace:*",
6565
"@elizaos/plugin-injective": "workspace:*",
66-
"@elizaos/plugin-solana-agentkit": "workspace:*",
66+
"@elizaos/plugin-solana-agent-kit": "workspace:*",
6767
"@elizaos/plugin-squid-router": "workspace:*",
6868
"@elizaos/plugin-autonome": "workspace:*",
6969
"@elizaos/plugin-starknet": "workspace:*",

agent/src/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ import { openWeatherPlugin } from "@elizaos/plugin-open-weather";
8585
import { quaiPlugin } from "@elizaos/plugin-quai";
8686
import { sgxPlugin } from "@elizaos/plugin-sgx";
8787
import { solanaPlugin } from "@elizaos/plugin-solana";
88-
import { solanaAgentkitPlugin } from "@elizaos/plugin-solana-agentkit";
88+
import { solanaAgentkitPlugin } from "@elizaos/plugin-solana-agent-kit";
8989
import { squidRouterPlugin } from "@elizaos/plugin-squid-router";
9090
import { stargazePlugin } from "@elizaos/plugin-stargaze";
9191
import { storyPlugin } from "@elizaos/plugin-story";

packages/plugin-solana-agentkit/package.json packages/plugin-solana-agent-kit/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"name": "@elizaos/plugin-solana-agentkit",
2+
"name": "@elizaos/plugin-solana-agent-kit",
33
"version": "0.1.9-alpha.1",
44
"main": "dist/index.js",
55
"type": "module",

packages/plugin-solana-agentkit/src/actions/createToken.ts packages/plugin-solana-agent-kit/src/actions/createToken.ts

+2-11
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,7 @@ import {
1111
State,
1212
type Action,
1313
} from "@elizaos/core";
14-
15-
import { SolanaAgentKit } from "solana-agent-kit";
16-
14+
import { getSAK } from "../client";
1715
export interface CreateTokenContent extends Content {
1816
name: string;
1917
uri: string;
@@ -103,14 +101,7 @@ export default {
103101
}
104102

105103
elizaLogger.log("Init solana agent kit...");
106-
const solanaPrivatekey = runtime.getSetting("SOLANA_PRIVATE_KEY");
107-
const rpc = runtime.getSetting("SOLANA_RPC_URL");
108-
const openAIKey = runtime.getSetting("OPENAI_API_KEY");
109-
const solanaAgentKit = new SolanaAgentKit(
110-
solanaPrivatekey,
111-
rpc,
112-
openAIKey
113-
);
104+
const solanaAgentKit = await getSAK(runtime);
114105
try {
115106
const deployedAddress = await solanaAgentKit.deployToken(
116107
content.name,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
2+
import { elizaLogger, } from "@elizaos/core";
3+
import {
4+
ActionExample,
5+
Content,
6+
HandlerCallback,
7+
IAgentRuntime,
8+
Memory,
9+
ModelClass,
10+
State,
11+
type Action,
12+
} from "@elizaos/core";
13+
import { composeContext } from "@elizaos/core";
14+
import { generateObjectDeprecated } from "@elizaos/core";
15+
import { ACTIONS } from "solana-agent-kit";
16+
import { getSAK } from "../client";
17+
18+
const GET_TOKEN_INFO_ACTION = ACTIONS.GET_TOKEN_DATA_ACTION;
19+
20+
export interface GetTokenInfoContent extends Content {
21+
tokenAddress: string;
22+
}
23+
24+
function isGetTokenInfoContent(
25+
runtime: IAgentRuntime,
26+
content: any
27+
): content is GetTokenInfoContent {
28+
elizaLogger.log("Content for transfer", content);
29+
return (
30+
typeof content.tokenAddress === "string"
31+
);
32+
}
33+
34+
const getTokenInfoTemplate = `Respond with a JSON markdown block containing only the extracted values. Use null for any values that cannot be determined.
35+
36+
Example response:
37+
\`\`\`json
38+
{
39+
"tokenAddress": "SENDdRQtYMWaQrBroBrJ2Q53fgVuq95CV9UPGEvpCxa",
40+
}
41+
\`\`\`
42+
43+
{{recentMessages}}
44+
45+
Given the recent messages, extract the following information about the requested token:
46+
- Token contract address
47+
48+
Respond with a JSON markdown block containing only the extracted values.`;
49+
50+
export default {
51+
name: GET_TOKEN_INFO_ACTION.name,
52+
similes: GET_TOKEN_INFO_ACTION.similes,
53+
validate: async (runtime: IAgentRuntime, message: Memory) => {
54+
elizaLogger.log("Validating get token info from user:", message.userId);
55+
56+
return false;
57+
},
58+
description: GET_TOKEN_INFO_ACTION.description,
59+
handler: async (
60+
runtime: IAgentRuntime,
61+
message: Memory,
62+
state: State,
63+
_options: { [key: string]: unknown },
64+
callback?: HandlerCallback
65+
): Promise<boolean> => {
66+
elizaLogger.log("Starting GET_TOKEN_INFO handler...");
67+
const sak = await getSAK(runtime);
68+
69+
// Initialize or update state
70+
if (!state) {
71+
state = (await runtime.composeState(message)) as State;
72+
} else {
73+
state = await runtime.updateRecentMessageState(state);
74+
}
75+
76+
// Compose get token info context
77+
const getTokenInfoContext = composeContext({
78+
state,
79+
template: getTokenInfoTemplate,
80+
});
81+
82+
// Generate get token info content
83+
const content = await generateObjectDeprecated({
84+
runtime,
85+
context: getTokenInfoContext,
86+
modelClass: ModelClass.LARGE,
87+
});
88+
89+
// Validate get token info content
90+
if (!isGetTokenInfoContent(runtime, content)) {
91+
elizaLogger.error("Invalid content for GET_TOKEN_INFO action.");
92+
if (callback) {
93+
callback({
94+
text: "Unable to process get token info request. Invalid content provided.",
95+
content: { error: "Invalid get token info content" },
96+
});
97+
}
98+
return false;
99+
}
100+
101+
try {
102+
103+
const tokenData = await sak.getTokenDataByAddress(content.tokenAddress)
104+
105+
console.log("Token data:", tokenData);
106+
107+
if (callback) {
108+
callback({
109+
text: `Successfully retrieved token data for ${content.tokenAddress}`,
110+
content: {
111+
success: true,
112+
tokenData: tokenData,
113+
},
114+
});
115+
}
116+
117+
return true;
118+
} catch (error) {
119+
elizaLogger.error("Error during get token info:", error);
120+
if (callback) {
121+
callback({
122+
text: `Error getting token info: ${error.message}`,
123+
content: { error: error.message },
124+
});
125+
}
126+
return false;
127+
}
128+
},
129+
130+
examples: [
131+
[
132+
{
133+
user: "{{user1}}",
134+
content: {
135+
text: "Get token info for SENDdRQtYMWaQrBroBrJ2Q53fgVuq95CV9UPGEvpCxa",
136+
},
137+
},
138+
{
139+
user: "{{user2}}",
140+
content: {
141+
text: "Get token info for SENDdRQtYMWaQrBroBrJ2Q53fgVuq95CV9UPGEvpCxa",
142+
action: "GET_TOKEN_INFO",
143+
},
144+
},
145+
{
146+
user: "{{user2}}",
147+
content: {
148+
text: "Successfully retrieved token info for SENDdRQtYMWaQrBroBrJ2Q53fgVuq95CV9UPGEvpCxa",
149+
},
150+
},
151+
],
152+
] as ActionExample[][],
153+
} as Action;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
import { elizaLogger } from "@elizaos/core";
2+
import {
3+
ActionExample,
4+
Content,
5+
HandlerCallback,
6+
IAgentRuntime,
7+
Memory,
8+
ModelClass,
9+
State,
10+
type Action,
11+
} from "@elizaos/core";
12+
import { composeContext } from "@elizaos/core";
13+
import { generateObjectDeprecated } from "@elizaos/core";
14+
import { ACTIONS } from "solana-agent-kit";
15+
import { getSAK } from "../client";
16+
17+
const GIBWORK_ACTION = ACTIONS.CREATE_GIBWORK_TASK_ACTION;
18+
19+
export interface GibWorkContent extends Content {
20+
title: string;
21+
content: string;
22+
requirements: string;
23+
tags: string[];
24+
tokenMintAddress: string;
25+
tokenAmount: number;
26+
}
27+
28+
function isGibWorkContent(
29+
runtime: IAgentRuntime,
30+
content: any
31+
): content is GibWorkContent {
32+
elizaLogger.log("Content for gibwork", content);
33+
return (
34+
typeof content.title === "string" &&
35+
typeof content.content === "string" &&
36+
typeof content.requirements === "string" &&
37+
Array.isArray(content.tags) &&
38+
typeof content.tokenMintAddress === "string" &&
39+
typeof content.tokenAmount === "number"
40+
);
41+
}
42+
43+
const gibworkTemplate = `Respond with a JSON markdown block containing only the extracted values. Use null for any values that cannot be determined.
44+
45+
Example response:
46+
\`\`\`json
47+
{
48+
"title": "Build a Solana dApp",
49+
"content": "Create a simple Solana dApp with React frontend",
50+
"requirements": "Experience with Rust and React",
51+
"tags": ["solana", "rust", "react"],
52+
"tokenMintAddress": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
53+
"tokenAmount": 100
54+
}
55+
\`\`\`
56+
57+
{{recentMessages}}
58+
59+
Given the recent messages, extract the following information about the GibWork task:
60+
- Title of the task
61+
- Content/description of the task
62+
- Requirements for the task
63+
- Tags related to the task
64+
- Token mint address for payment
65+
- Token amount for payment
66+
67+
Respond with a JSON markdown block containing only the extracted values.`;
68+
69+
export default {
70+
name: GIBWORK_ACTION.name,
71+
similes: GIBWORK_ACTION.similes,
72+
validate: async (runtime: IAgentRuntime, message: Memory) => {
73+
elizaLogger.log("Validating gibwork task from user:", message.userId);
74+
return false;
75+
},
76+
description: GIBWORK_ACTION.description,
77+
handler: async (
78+
runtime: IAgentRuntime,
79+
message: Memory,
80+
state: State,
81+
_options: { [key: string]: unknown },
82+
callback?: HandlerCallback
83+
): Promise<boolean> => {
84+
elizaLogger.log("Starting CREATE_GIBWORK_TASK handler...");
85+
const sak = await getSAK(runtime);
86+
87+
// Initialize or update state
88+
if (!state) {
89+
state = (await runtime.composeState(message)) as State;
90+
} else {
91+
state = await runtime.updateRecentMessageState(state);
92+
}
93+
94+
// Compose gibwork context
95+
const gibworkContext = composeContext({
96+
state,
97+
template: gibworkTemplate,
98+
});
99+
100+
// Generate gibwork content
101+
const content = await generateObjectDeprecated({
102+
runtime,
103+
context: gibworkContext,
104+
modelClass: ModelClass.LARGE,
105+
});
106+
107+
// Validate gibwork content
108+
if (!isGibWorkContent(runtime, content)) {
109+
elizaLogger.error("Invalid content for CREATE_GIBWORK_TASK action.");
110+
if (callback) {
111+
callback({
112+
text: "Unable to process GibWork task creation. Invalid content provided.",
113+
content: { error: "Invalid gibwork content" },
114+
});
115+
}
116+
return false;
117+
}
118+
119+
try {
120+
const gibworkResult = await sak.createGibworkTask(
121+
content.title,
122+
content.content,
123+
content.requirements,
124+
content.tags,
125+
content.tokenMintAddress,
126+
content.tokenAmount
127+
);
128+
129+
console.log("GibWork task creation result:", gibworkResult);
130+
131+
if (callback) {
132+
callback({
133+
text: `Successfully created GibWork task: ${content.title}`,
134+
content: {
135+
success: true,
136+
gibworkResult: gibworkResult,
137+
},
138+
});
139+
}
140+
141+
return true;
142+
} catch (error) {
143+
elizaLogger.error("Error during GibWork task creation:", error);
144+
if (callback) {
145+
callback({
146+
text: `Error creating GibWork task: ${error.message}`,
147+
content: { error: error.message },
148+
});
149+
}
150+
return false;
151+
}
152+
},
153+
154+
examples: [
155+
[
156+
{
157+
user: "{{user1}}",
158+
content: {
159+
text: "Create a GibWork task for building a Solana dApp, offering 100 USDC",
160+
},
161+
},
162+
{
163+
user: "{{user2}}",
164+
content: {
165+
text: "Creating GibWork task",
166+
action: "CREATE_GIBWORK_TASK",
167+
},
168+
},
169+
{
170+
user: "{{user2}}",
171+
content: {
172+
text: "Successfully created GibWork task: Build a Solana dApp",
173+
},
174+
},
175+
],
176+
] as ActionExample[][],
177+
} as Action;

0 commit comments

Comments
 (0)