feat: Enhance Solana tools with action-based architecture

- Introduced action system for Solana tools, allowing for better modularity and maintainability.
- Updated SolanaBalanceTool, SolanaTransferTool, SolanaDeployTokenTool, SolanaDeployCollectionTool, SolanaMintNFTTool, SolanaTradeTool, and SolanaRequestFundsTool to utilize action handlers.
- Added new action exports in index.ts for better organization and accessibility.
This commit is contained in:
Fahri Bilici
2024-12-26 21:54:55 +01:00
parent 8d299244fc
commit 79cada2cbd
13 changed files with 825 additions and 165 deletions

59
src/actions/balance.ts Normal file
View File

@@ -0,0 +1,59 @@
import { PublicKey } from "@solana/web3.js";
import { Action } from "../types/action";
import { SolanaAgentKit } from "../agent";
import { z } from "zod";
const balanceAction: Action = {
name: "solana_balance",
similes: [
"check balance",
"get wallet balance",
"view balance",
"show balance",
"check token balance"
],
description: `Get the balance of a Solana wallet or token account.
If you want to get the balance of your wallet, you don't need to provide the tokenAddress.
If no tokenAddress is provided, the balance will be in SOL.`,
examples: [
[
{
input: {},
output: {
status: "success",
balance: "100",
token: "SOL"
},
explanation: "Get SOL balance of the wallet"
}
],
[
{
input: {
tokenAddress: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
},
output: {
status: "success",
balance: "1000",
token: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
},
explanation: "Get USDC token balance"
}
]
],
schema: z.object({
tokenAddress: z.string().optional()
}),
handler: async (agent: SolanaAgentKit, input: Record<string, any>) => {
const tokenAddress = input.tokenAddress ? new PublicKey(input.tokenAddress) : undefined;
const balance = await agent.getBalance(tokenAddress);
return {
status: "success",
balance: balance,
token: input.tokenAddress || "SOL"
};
}
};
export default balanceAction;

View File

@@ -0,0 +1,78 @@
import { PublicKey } from "@solana/web3.js";
import { Action } from "../types/action";
import { SolanaAgentKit } from "../agent";
import { z } from "zod";
interface CollectionOptions {
name: string;
uri: string;
royaltyBasisPoints?: number;
}
const deployCollectionAction: Action = {
name: "solana_deploy_collection",
similes: [
"create collection",
"launch collection",
"deploy nft collection",
"create nft collection",
"mint collection"
],
description: `Deploy a new NFT collection on Solana blockchain.`,
examples: [
[
{
input: {
name: "My Collection",
uri: "https://example.com/collection.json",
royaltyBasisPoints: 500
},
output: {
status: "success",
message: "Collection deployed successfully",
collectionAddress: "7nE9GvcwsqzYxmJLSrYmSB1V1YoJWVK1KWzAcWAzjXkN",
name: "My Collection"
},
explanation: "Deploy an NFT collection with 5% royalty"
}
],
[
{
input: {
name: "Basic Collection",
uri: "https://example.com/basic.json"
},
output: {
status: "success",
message: "Collection deployed successfully",
collectionAddress: "8nE9GvcwsqzYxmJLSrYmSB1V1YoJWVK1KWzAcWAzjXkM",
name: "Basic Collection"
},
explanation: "Deploy a basic NFT collection without royalties"
}
]
],
schema: z.object({
name: z.string().min(1, "Name is required"),
uri: z.string().url("URI must be a valid URL"),
royaltyBasisPoints: z.number().min(0).max(10000).optional()
}),
handler: async (agent: SolanaAgentKit, input: Record<string, any>) => {
const options: CollectionOptions = {
name: input.name,
uri: input.uri,
royaltyBasisPoints: input.royaltyBasisPoints
};
const result = await agent.deployCollection(options);
return {
status: "success",
message: "Collection deployed successfully",
collectionAddress: result.collectionAddress.toString(),
name: input.name
};
}
};
export default deployCollectionAction;

View File

@@ -0,0 +1,74 @@
import { PublicKey } from "@solana/web3.js";
import { Action, ActionExample } from "../types/action";
import { SolanaAgentKit } from "../agent";
import { z } from "zod";
const deployTokenAction: Action = {
name: "deploy_token",
similes: [
"create token",
"launch token",
"deploy new token",
"create new token",
"mint token",
],
description: "Deploy a new SPL token on the Solana blockchain with specified parameters",
examples: [
[
{
input: {
name: "My Token",
uri: "https://example.com/token.json",
symbol: "MTK",
decimals: 9,
initialSupply: 1000000
},
output: {
mint: "7nE9GvcwsqzYxmJLSrYmSB1V1YoJWVK1KWzAcWAzjXkN",
status: "success",
message: "Token deployed successfully"
},
explanation: "Deploy a token with initial supply and metadata"
}
],
[
{
input: {
name: "Basic Token",
uri: "https://example.com/basic.json",
symbol: "BASIC"
},
output: {
mint: "8nE9GvcwsqzYxmJLSrYmSB1V1YoJWVK1KWzAcWAzjXkM",
status: "success",
message: "Token deployed successfully"
},
explanation: "Deploy a basic token with minimal parameters"
}
]
],
schema: z.object({
name: z.string().min(1, "Name is required"),
uri: z.string().url("URI must be a valid URL"),
symbol: z.string().min(1, "Symbol is required"),
decimals: z.number().optional(),
initialSupply: z.number().optional()
}),
handler: async (agent: SolanaAgentKit, input: Record<string, any>) => {
const result = await agent.deployToken(
input.name,
input.uri,
input.symbol,
input.decimals,
input.initialSupply
);
return {
mint: result.mint.toString(),
status: "success",
message: "Token deployed successfully"
};
}
}
export default deployTokenAction;

20
src/actions/index.ts Normal file
View File

@@ -0,0 +1,20 @@
import deployTokenAction from "./deployToken";
import balanceAction from "./balance";
import transferAction from "./transfer";
import deployCollectionAction from "./deployCollection";
import mintNFTAction from "./mintNFT";
import tradeAction from "./trade";
import requestFundsAction from "./requestFunds";
export const actions = [
deployTokenAction,
balanceAction,
transferAction,
deployCollectionAction,
mintNFTAction,
tradeAction,
requestFundsAction,
// Add more actions here as they are implemented
];
export type { Action, ActionExample, Handler } from "../types/action";

88
src/actions/mintNFT.ts Normal file
View File

@@ -0,0 +1,88 @@
import { PublicKey } from "@solana/web3.js";
import { Action } from "../types/action";
import { SolanaAgentKit } from "../agent";
import { z } from "zod";
const mintNFTAction: Action = {
name: "solana_mint_nft",
similes: [
"mint nft",
"create nft",
"mint token",
"create token",
"add nft to collection"
],
description: `Mint a new NFT in a collection on Solana blockchain.`,
examples: [
[
{
input: {
collectionMint: "J1S9H3QjnRtBbbuD4HjPV6RpRhwuk4zKbxsnCHuTgh9w",
name: "My NFT",
uri: "https://example.com/nft.json"
},
output: {
status: "success",
message: "NFT minted successfully",
mintAddress: "7nE9GvcwsqzYxmJLSrYmSB1V1YoJWVK1KWzAcWAzjXkN",
metadata: {
name: "My NFT",
uri: "https://example.com/nft.json"
},
recipient: "7nE9GvcwsqzYxmJLSrYmSB1V1YoJWVK1KWzAcWAzjXkN"
},
explanation: "Mint an NFT to the default wallet"
}
],
[
{
input: {
collectionMint: "J1S9H3QjnRtBbbuD4HjPV6RpRhwuk4zKbxsnCHuTgh9w",
name: "Gift NFT",
uri: "https://example.com/gift.json",
recipient: "9aUn5swQzUTRanaaTwmszxiv89cvFwUCjEBv1vZCoT1u"
},
output: {
status: "success",
message: "NFT minted successfully",
mintAddress: "8nE9GvcwsqzYxmJLSrYmSB1V1YoJWVK1KWzAcWAzjXkM",
metadata: {
name: "Gift NFT",
uri: "https://example.com/gift.json"
},
recipient: "9aUn5swQzUTRanaaTwmszxiv89cvFwUCjEBv1vZCoT1u"
},
explanation: "Mint an NFT to a specific recipient"
}
]
],
schema: z.object({
collectionMint: z.string().min(32, "Invalid collection mint address"),
name: z.string().min(1, "Name is required"),
uri: z.string().url("URI must be a valid URL"),
recipient: z.string().min(32, "Invalid recipient address").optional()
}),
handler: async (agent: SolanaAgentKit, input: Record<string, any>) => {
const result = await agent.mintNFT(
new PublicKey(input.collectionMint),
{
name: input.name,
uri: input.uri,
},
input.recipient ? new PublicKey(input.recipient) : agent.wallet_address
);
return {
status: "success",
message: "NFT minted successfully",
mintAddress: result.mint.toString(),
metadata: {
name: input.name,
uri: input.uri
},
recipient: input.recipient || result.mint.toString()
};
}
};
export default mintNFTAction;

View File

@@ -0,0 +1,40 @@
import { Action } from "../types/action";
import { SolanaAgentKit } from "../agent";
import { z } from "zod";
const requestFundsAction: Action = {
name: "solana_request_funds",
similes: [
"request sol",
"get test sol",
"use faucet",
"request test tokens",
"get devnet sol"
],
description: "Request SOL from Solana faucet (devnet/testnet only)",
examples: [
[
{
input: {},
output: {
status: "success",
message: "Successfully requested faucet funds",
network: "devnet.solana.com"
},
explanation: "Request SOL from the devnet faucet"
}
]
],
schema: z.object({}), // No input parameters required
handler: async (agent: SolanaAgentKit, _input: Record<string, any>) => {
await agent.requestFaucetFunds();
return {
status: "success",
message: "Successfully requested faucet funds",
network: agent.connection.rpcEndpoint.split("/")[2]
};
}
};
export default requestFundsAction;

81
src/actions/trade.ts Normal file
View File

@@ -0,0 +1,81 @@
import { PublicKey } from "@solana/web3.js";
import { Action } from "../types/action";
import { SolanaAgentKit } from "../agent";
import { z } from "zod";
const tradeAction: Action = {
name: "solana_trade",
similes: [
"swap tokens",
"exchange tokens",
"trade tokens",
"convert tokens",
"swap sol"
],
description: `This tool can be used to swap tokens to another token (It uses Jupiter Exchange).`,
examples: [
[
{
input: {
outputMint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
inputAmount: 1
},
output: {
status: "success",
message: "Trade executed successfully",
transaction: "5UfgJ5vVZxUxefDGqzqkVLHzHxVTyYH9StYyHKgvHYmXJgqJKxEqy9k4Rz9LpXrHF9kUZB7",
inputAmount: 1,
inputToken: "SOL",
outputToken: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
},
explanation: "Swap 1 SOL for USDC"
}
],
[
{
input: {
outputMint: "So11111111111111111111111111111111111111112",
inputAmount: 100,
inputMint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
slippageBps: 100
},
output: {
status: "success",
message: "Trade executed successfully",
transaction: "4VfgJ5vVZxUxefDGqzqkVLHzHxVTyYH9StYyHKgvHYmXJgqJKxEqy9k4Rz9LpXrHF9kUZB7",
inputAmount: 100,
inputToken: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
outputToken: "So11111111111111111111111111111111111111112"
},
explanation: "Swap 100 USDC for SOL with 1% slippage"
}
]
],
schema: z.object({
outputMint: z.string().min(32, "Invalid output mint address"),
inputAmount: z.number().positive("Input amount must be positive"),
inputMint: z.string().min(32, "Invalid input mint address").optional(),
slippageBps: z.number().min(0).max(10000).optional()
}),
handler: async (agent: SolanaAgentKit, input: Record<string, any>) => {
const tx = await agent.trade(
new PublicKey(input.outputMint),
input.inputAmount,
input.inputMint
? new PublicKey(input.inputMint)
: new PublicKey("So11111111111111111111111111111111111111112"),
input.slippageBps
);
return {
status: "success",
message: "Trade executed successfully",
transaction: tx,
inputAmount: input.inputAmount,
inputToken: input.inputMint || "SOL",
outputToken: input.outputMint
};
}
};
export default tradeAction;

75
src/actions/transfer.ts Normal file
View File

@@ -0,0 +1,75 @@
import { PublicKey } from "@solana/web3.js";
import { Action } from "../types/action";
import { SolanaAgentKit } from "../agent";
import { z } from "zod";
const transferAction: Action = {
name: "solana_transfer",
similes: [
"send tokens",
"transfer funds",
"send money",
"send sol",
"transfer tokens"
],
description: `Transfer tokens or SOL to another address (also called as wallet address).`,
examples: [
[
{
input: {
to: "8x2dR8Mpzuz2YqyZyZjUbYWKSWesBo5jMx2Q9Y86udVk",
amount: 1
},
output: {
status: "success",
message: "Transfer completed successfully",
amount: 1,
recipient: "8x2dR8Mpzuz2YqyZyZjUbYWKSWesBo5jMx2Q9Y86udVk",
token: "SOL",
transaction: "5UfgJ5vVZxUxefDGqzqkVLHzHxVTyYH9StYyHKgvHYmXJgqJKxEqy9k4Rz9LpXrHF9kUZB7"
},
explanation: "Transfer 1 SOL to the recipient address"
}
],
[
{
input: {
to: "8x2dR8Mpzuz2YqyZyZjUbYWKSWesBo5jMx2Q9Y86udVk",
amount: 100,
mint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
},
output: {
status: "success",
message: "Transfer completed successfully",
amount: 100,
recipient: "8x2dR8Mpzuz2YqyZyZjUbYWKSWesBo5jMx2Q9Y86udVk",
token: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
transaction: "4VfgJ5vVZxUxefDGqzqkVLHzHxVTyYH9StYyHKgvHYmXJgqJKxEqy9k4Rz9LpXrHF9kUZB7"
},
explanation: "Transfer 100 USDC tokens to the recipient address"
}
]
],
schema: z.object({
to: z.string().min(32, "Invalid Solana address"),
amount: z.number().positive("Amount must be positive"),
mint: z.string().optional()
}),
handler: async (agent: SolanaAgentKit, input: Record<string, any>) => {
const recipient = new PublicKey(input.to);
const mintAddress = input.mint ? new PublicKey(input.mint) : undefined;
const tx = await agent.transfer(recipient, input.amount, mintAddress);
return {
status: "success",
message: "Transfer completed successfully",
amount: input.amount,
recipient: input.to,
token: input.mint || "SOL",
transaction: tx
};
}
};
export default transferAction;