diff --git a/package.json b/package.json index 7f0769fd..770d4db6 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,8 @@ "form-data": "^4.0.1", "langchain": "^0.3.8", "openai": "^4.77.0", - "typedoc": "^0.27.6" + "typedoc": "^0.27.6", + "zod": "^3.24.1" }, "devDependencies": { "@types/bn.js": "^5.1.6", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5047863f..ac4bb302 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -107,6 +107,9 @@ importers: typedoc: specifier: ^0.27.6 version: 0.27.6(typescript@5.7.2) + zod: + specifier: ^3.24.1 + version: 3.24.1 devDependencies: '@types/bn.js': specifier: ^5.1.6 @@ -588,15 +591,9 @@ packages: '@shikijs/engine-javascript@1.24.4': resolution: {integrity: sha512-TClaQOLvo9WEMJv6GoUsykQ6QdynuKszuORFWCke8qvi6PeLm7FcD9+7y45UenysxEWYpDL5KJaVXTngTE+2BA==} - '@shikijs/engine-oniguruma@1.24.3': - resolution: {integrity: sha512-iNnx950gs/5Nk+zrp1LuF+S+L7SKEhn8k9eXgFYPGhVshKppsYwRmW8tpmAMvILIMSDfrgqZ0w+3xWVQB//1Xw==} - '@shikijs/engine-oniguruma@1.24.4': resolution: {integrity: sha512-Do2ry6flp2HWdvpj2XOwwa0ljZBRy15HKZITzPcNIBOGSeprnA8gOooA/bLsSPuy8aJBa+Q/r34dMmC3KNL/zw==} - '@shikijs/types@1.24.3': - resolution: {integrity: sha512-FPMrJ69MNxhRtldRk69CghvaGlbbN3pKRuvko0zvbfa2dXp4pAngByToqS5OY5jvN8D7LKR4RJE8UvzlCOuViw==} - '@shikijs/types@1.24.4': resolution: {integrity: sha512-0r0XU7Eaow0PuDxuWC1bVqmWCgm3XqizIaT7SM42K03vc69LGooT0U8ccSR44xP/hGlNx4FKhtYpV+BU6aaKAA==} @@ -2724,7 +2721,7 @@ snapshots: '@gerrit0/mini-shiki@1.24.4': dependencies: - '@shikijs/engine-oniguruma': 1.24.3 + '@shikijs/engine-oniguruma': 1.24.4 '@shikijs/types': 1.24.4 '@shikijs/vscode-textmate': 9.3.1 @@ -3273,21 +3270,11 @@ snapshots: '@shikijs/vscode-textmate': 9.3.1 oniguruma-to-es: 0.8.1 - '@shikijs/engine-oniguruma@1.24.3': - dependencies: - '@shikijs/types': 1.24.3 - '@shikijs/vscode-textmate': 9.3.1 - '@shikijs/engine-oniguruma@1.24.4': dependencies: '@shikijs/types': 1.24.4 '@shikijs/vscode-textmate': 9.3.1 - '@shikijs/types@1.24.3': - dependencies: - '@shikijs/vscode-textmate': 9.3.1 - '@types/hast': 3.0.4 - '@shikijs/types@1.24.4': dependencies: '@shikijs/vscode-textmate': 9.3.1 diff --git a/src/actions/balance.ts b/src/actions/balance.ts new file mode 100644 index 00000000..107b7095 --- /dev/null +++ b/src/actions/balance.ts @@ -0,0 +1,62 @@ +import { PublicKey } from "@solana/web3.js"; +import { Action } from "../types/action"; +import { SolanaAgentKit } from "../agent"; +import { z } from "zod"; +import { get_balance } from "../tools"; + +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) => { + const balance = await get_balance( + agent, + input.tokenAddress && new PublicKey(input.tokenAddress), + ); + + return { + status: "success", + balance: balance, + token: input.tokenAddress || "SOL", + }; + }, +}; + +export default balanceAction; diff --git a/src/actions/compressedAirdrop.ts b/src/actions/compressedAirdrop.ts new file mode 100644 index 00000000..4be2fb43 --- /dev/null +++ b/src/actions/compressedAirdrop.ts @@ -0,0 +1,104 @@ +import { Action } from "../types/action"; +import { SolanaAgentKit } from "../agent"; +import { z } from "zod"; +import { sendCompressedAirdrop } from "../tools"; + +const compressedAirdropAction: Action = { + name: "solana_compressed_airdrop", + similes: [ + "ZK Compressed airdrop", + "Airdrop tokens with compression", + "Send compressed SPL airdrop", + "Airdrop to multiple recipients", + ], + description: + "Airdrop SPL tokens with ZK Compression (also known as airdropping tokens) to multiple recipients", + examples: [ + [ + { + input: { + mintAddress: "JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN", + amount: 42, + decimals: 6, + recipients: [ + "1nc1nerator11111111111111111111111111111111", + "BrFndAe111111111111111111111111111111111", + ], + priorityFeeInLamports: 30000, + shouldLog: true, + }, + output: { + status: "success", + message: "Airdropped 42 tokens to 2 recipients.", + transactionHashes: ["4uyfBN...", "9XsF2N..."], + }, + explanation: + "Airdrops 42 tokens (with 6 decimals) to 2 recipients, optionally logging progress to stdout.", + }, + ], + ], + // Validate inputs with zod + schema: z.object({ + mintAddress: z + .string() + .min(1) + .describe("Mint address of the token, e.g., 'JUPy...'"), + amount: z + .number() + .positive() + .describe("Number of tokens to airdrop per recipient, e.g., 42"), + decimals: z + .number() + .nonnegative() + .int() + .describe("Decimals of the token, e.g., 6"), + recipients: z + .array(z.string()) + .nonempty() + .describe("Array of recipient addresses, e.g., ['1nc1n...']"), + priorityFeeInLamports: z + .number() + .optional() + .describe("Priority fee in lamports (default is 30_000)"), + shouldLog: z + .boolean() + .optional() + .describe("Whether to log progress to stdout (default is false)"), + }), + handler: async (agent: SolanaAgentKit, input: Record) => { + try { + const { + mintAddress, + amount, + decimals, + recipients, + priorityFeeInLamports, + shouldLog, + } = input; + + // Call your airdrop method on the SolanaAgentKit + const txs = await sendCompressedAirdrop( + mintAddress, + amount, + decimals, + recipients, + priorityFeeInLamports || 30_000, + shouldLog || false, + ); + + return { + status: "success", + message: `Airdropped ${amount} tokens to ${recipients.length} recipients.`, + transactionHashes: txs, + }; + } catch (error: any) { + return { + status: "error", + message: `Failed to airdrop tokens: ${error.message}`, + code: error.code || "UNKNOWN_ERROR", + }; + } + }, +}; + +export default compressedAirdropAction; diff --git a/src/actions/createGibworkTask.ts b/src/actions/createGibworkTask.ts new file mode 100644 index 00000000..3ff8b12c --- /dev/null +++ b/src/actions/createGibworkTask.ts @@ -0,0 +1,86 @@ +import { Action } from "../types/action"; +import { SolanaAgentKit } from "../agent"; +import { z } from "zod"; +import { PublicKey } from "@solana/web3.js"; +import { create_gibwork_task } from "../tools"; + +const createGibworkTaskAction: Action = { + name: "solana_create_gibwork_task", + similes: [ + "create task", + "post job", + "create gig", + "post task", + "create work", + "new task on gibwork", + ], + description: + "Create a new task on the Gibwork platform with payment in SPL tokens", + examples: [ + [ + { + input: { + title: "Build a Solana dApp", + content: "Create a simple Solana dApp with React frontend", + requirements: "Experience with Rust and React", + tags: ["solana", "rust", "react"], + tokenMintAddress: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", + tokenAmount: 100, + }, + output: { + status: "success", + taskId: "task_123", + signature: "3YKpM1...", + message: "Successfully created task: Build a Solana dApp", + }, + explanation: "Create a new task on Gibwork with 100 USDC payment", + }, + ], + ], + schema: z.object({ + title: z.string().min(1).describe("Title of the task"), + content: z.string().min(1).describe("Description of the task"), + requirements: z + .string() + .min(1) + .describe("Requirements to complete the task"), + tags: z + .array(z.string()) + .min(1) + .describe("List of tags associated with the task"), + tokenMintAddress: z.string().describe("Token mint address for payment"), + tokenAmount: z.number().positive().describe("Payment amount for the task"), + payer: z + .string() + .optional() + .describe("Optional payer address (defaults to wallet address)"), + }), + handler: async (agent: SolanaAgentKit, input: Record) => { + try { + const responseData = await create_gibwork_task( + agent, + input.title, + input.content, + input.requirements, + input.tags, + new PublicKey(input.tokenMintAddress), + input.tokenAmount, + input.payer ? new PublicKey(input.payer) : undefined, + ); + + return { + status: "success", + taskId: responseData.taskId, + signature: responseData.signature, + message: `Successfully created task: ${input.title}`, + }; + } catch (error: any) { + return { + status: "error", + message: `Failed to create task: ${error.message}`, + }; + } + }, +}; + +export default createGibworkTaskAction; diff --git a/src/actions/createImage.ts b/src/actions/createImage.ts new file mode 100644 index 00000000..895fefb7 --- /dev/null +++ b/src/actions/createImage.ts @@ -0,0 +1,101 @@ +import { Action } from "../types/action"; +import { SolanaAgentKit } from "../agent"; +import { z } from "zod"; +import { create_image } from "../tools/create_image"; + +const createImageAction: Action = { + name: "solana_create_image", + similes: [ + "generate image", + "create artwork", + "make image", + "generate artwork", + "create picture", + "generate picture", + ], + description: + "Create an AI-generated image based on a text prompt using OpenAI's DALL-E models", + examples: [ + [ + { + input: { + prompt: "A beautiful sunset over a mountain landscape", + model: "dall-e-3", + size: "1024x1024", + quality: "standard", + style: "natural", + }, + output: { + status: "success", + imageUrl: "https://example.com/image.png", + message: "Successfully generated image", + }, + explanation: "Generate an image of a sunset landscape using DALL-E 3", + }, + ], + ], + schema: z.object({ + prompt: z + .string() + .min(1) + .max(1000) + .describe("The text description of the image to generate"), + model: z + .enum(["dall-e-3"]) + .default("dall-e-3") + .describe("The AI model to use for generation"), + size: z + .enum(["256x256", "512x512", "1024x1024", "1792x1024", "1024x1792"]) + .default("1024x1024") + .describe("The size of the generated image"), + quality: z + .enum(["standard", "hd"]) + .default("standard") + .describe("The quality level of the generated image"), + style: z + .enum(["natural", "vivid"]) + .default("natural") + .describe("The style of the generated image"), + }), + handler: async (agent: SolanaAgentKit, input: Record) => { + try { + if (!agent.config.OPENAI_API_KEY) { + return { + status: "error", + message: "OpenAI API key not found in agent configuration", + }; + } + + const { prompt, model, size } = input; + const response = await create_image(agent, prompt, model, size); + + return { + status: "success", + imageUrl: response.images[0].url, + message: "Successfully generated image", + }; + } catch (error: any) { + // Handle specific OpenAI error types + if (error.response) { + const { status, data } = error.response; + if (status === 429) { + return { + status: "error", + message: "Rate limit exceeded. Please try again later.", + }; + } + return { + status: "error", + message: `OpenAI API error: ${data.error?.message || error.message}`, + }; + } + + return { + status: "error", + message: `Failed to generate image: ${error.message}`, + }; + } + }, +}; + +export default createImageAction; diff --git a/src/actions/createOpenbookMarket.ts b/src/actions/createOpenbookMarket.ts new file mode 100644 index 00000000..dafb5092 --- /dev/null +++ b/src/actions/createOpenbookMarket.ts @@ -0,0 +1,80 @@ +import { Action } from "../types/action"; +import { SolanaAgentKit } from "../agent"; +import { z } from "zod"; +import { PublicKey } from "@solana/web3.js"; +import { openbookCreateMarket } from "../tools"; + +const createOpenbookMarketAction: Action = { + name: "solana_create_openbook_market", + similes: [ + "create openbook market", + "setup trading market", + "new openbook market", + "create trading pair", + "setup dex market", + "new trading market", + ], + description: "Create a new trading market on Openbook DEX", + examples: [ + [ + { + input: { + baseMint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", // USDC + quoteMint: "So11111111111111111111111111111111111111112", // SOL + lotSize: 1, + tickSize: 0.01, + }, + output: { + status: "success", + signatures: ["2ZE7Rz...", "3YKpM1..."], + message: "Successfully created Openbook market", + }, + explanation: + "Create a new USDC/SOL market on Openbook with default lot and tick sizes", + }, + ], + ], + schema: z.object({ + baseMint: z.string().min(1).describe("The base token's mint address"), + quoteMint: z.string().min(1).describe("The quote token's mint address"), + lotSize: z + .number() + .positive() + .default(1) + .describe("The minimum order size (lot size)"), + tickSize: z + .number() + .positive() + .default(0.01) + .describe("The minimum price increment (tick size)"), + }), + handler: async (agent: SolanaAgentKit, input: Record) => { + try { + const baseMint = new PublicKey(input.baseMint); + const quoteMint = new PublicKey(input.quoteMint); + const lotSize = input.lotSize || 1; + const tickSize = input.tickSize || 0.01; + + const signatures = await openbookCreateMarket( + agent, + baseMint, + quoteMint, + lotSize, + tickSize, + ); + + return { + status: "success", + signatures, + message: "Successfully created Openbook market", + }; + } catch (error: any) { + return { + status: "error", + message: `Failed to create Openbook market: ${error.message}`, + }; + } + }, +}; + +export default createOpenbookMarketAction; diff --git a/src/actions/createOrcaSingleSidedWhirlpool.ts b/src/actions/createOrcaSingleSidedWhirlpool.ts new file mode 100644 index 00000000..9b95cdfd --- /dev/null +++ b/src/actions/createOrcaSingleSidedWhirlpool.ts @@ -0,0 +1,117 @@ +import { Action } from "../types/action"; +import { SolanaAgentKit } from "../agent"; +import { z } from "zod"; +import { PublicKey } from "@solana/web3.js"; +import { BN } from "@coral-xyz/anchor"; +import { Decimal } from "decimal.js"; +import { orcaCreateSingleSidedLiquidityPool } from "../tools"; + +// Fee tiers mapping from the original tool +const FEE_TIERS = { + 0.01: 1, + 0.02: 2, + 0.04: 4, + 0.05: 8, + 0.16: 16, + 0.3: 64, + 0.65: 96, + 1.0: 128, + 2.0: 256, +} as const; + +const createOrcaSingleSidedWhirlpoolAction: Action = { + name: "solana_create_orca_single_sided_whirlpool", + similes: [ + "create orca whirlpool", + "setup orca single sided pool", + "initialize orca whirlpool", + "create orca concentrated pool", + "setup orca concentrated liquidity", + "create orca trading pair", + ], + description: + "Create a new single-sided whirlpool on Orca with concentrated liquidity", + examples: [ + [ + { + input: { + depositTokenAmount: "1000000000000", // 1 million tokens with 6 decimals + depositTokenMint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", // USDC + otherTokenMint: "So11111111111111111111111111111111111111112", // SOL + initialPrice: "0.001", + maxPrice: "5.0", + feeTier: 0.3, + }, + output: { + status: "success", + signature: "2ZE7Rz...", + message: "Successfully created Orca single-sided whirlpool", + }, + explanation: + "Create a USDC/SOL whirlpool with 1M USDC initial liquidity", + }, + ], + ], + schema: z.object({ + depositTokenAmount: z + .string() + .min(1) + .describe( + "The amount of deposit token to provide as liquidity (including decimals)", + ), + depositTokenMint: z + .string() + .min(1) + .describe("The mint address of the token being deposited"), + otherTokenMint: z + .string() + .min(1) + .describe("The mint address of the other token in the pool"), + initialPrice: z + .string() + .min(1) + .describe("Initial price of deposit token in terms of the other token"), + maxPrice: z + .string() + .min(1) + .describe("Maximum price at which liquidity is added"), + feeTier: z + .number() + .refine((val) => val in FEE_TIERS, "Invalid fee tier") + .describe("Fee tier percentage for the pool (e.g., 0.3 for 0.3%)"), + }), + handler: async (agent: SolanaAgentKit, input: Record) => { + try { + const depositTokenAmount = Number(input.depositTokenAmount); + const depositTokenMint = new PublicKey(input.depositTokenMint); + const otherTokenMint = new PublicKey(input.otherTokenMint); + const initialPrice = new Decimal(input.initialPrice); + const maxPrice = new Decimal(input.maxPrice); + const feeTier = input.feeTier + + // Create the whirlpool + const signature = await orcaCreateSingleSidedLiquidityPool( + agent, + depositTokenAmount, + depositTokenMint, + otherTokenMint, + initialPrice, + maxPrice, + feeTier, + ); + + return { + status: "success", + signature, + message: "Successfully created Orca single-sided whirlpool", + }; + } catch (error: any) { + return { + status: "error", + message: `Failed to create whirlpool: ${error.message}`, + }; + } + }, +}; + +export default createOrcaSingleSidedWhirlpoolAction; diff --git a/src/actions/deployCollection.ts b/src/actions/deployCollection.ts new file mode 100644 index 00000000..0ac8e6cf --- /dev/null +++ b/src/actions/deployCollection.ts @@ -0,0 +1,78 @@ +import { Action } from "../types/action"; +import { SolanaAgentKit } from "../agent"; +import { z } from "zod"; +import { deploy_collection } from "../tools"; + +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) => { + const options: CollectionOptions = { + name: input.name, + uri: input.uri, + royaltyBasisPoints: input.royaltyBasisPoints, + }; + + const result = await deploy_collection(agent, options); + + return { + status: "success", + message: "Collection deployed successfully", + collectionAddress: result.collectionAddress.toString(), + name: input.name, + }; + }, +}; + +export default deployCollectionAction; diff --git a/src/actions/deployToken.ts b/src/actions/deployToken.ts new file mode 100644 index 00000000..d3650542 --- /dev/null +++ b/src/actions/deployToken.ts @@ -0,0 +1,83 @@ +import { Action } from "../types/action"; +import { SolanaAgentKit } from "../agent"; +import { z } from "zod"; +import { deploy_token } from "../tools"; + +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) => { + try { + const result = await deploy_token( + agent, + input.name, + input.uri, + input.symbol, + input.decimals, + input.initialSupply, + ); + + return { + mint: result.mint.toString(), + status: "success", + message: "Token deployed successfully", + }; + } catch (error: any) { + return { + status: "error", + message: `Token deployment failed: ${error.message}`, + }; + } + }, +}; + +export default deployTokenAction; diff --git a/src/actions/fetchPrice.ts b/src/actions/fetchPrice.ts new file mode 100644 index 00000000..0c453919 --- /dev/null +++ b/src/actions/fetchPrice.ts @@ -0,0 +1,57 @@ +import { Action } from "../types/action"; +import { SolanaAgentKit } from "../agent"; +import { z } from "zod"; +import { PublicKey } from "@solana/web3.js"; +import { fetchPrice } from "../tools"; + +const fetchPriceAction: Action = { + name: "solana_fetch_price", + similes: [ + "get token price", + "check price", + "token value", + "price check", + "get price in usd", + ], + description: + "Fetch the current price of a Solana token in USDC using Jupiter API", + examples: [ + [ + { + input: { + tokenAddress: "So11111111111111111111111111111111111111112", + }, + output: { + status: "success", + price: "23.45", + message: "Current price: $23.45 USDC", + }, + explanation: "Get the current price of SOL token in USDC", + }, + ], + ], + schema: z.object({ + tokenAddress: z + .string() + .describe("The mint address of the token to fetch the price for"), + }), + handler: async (agent: SolanaAgentKit, input: Record) => { + try { + const tokenId = new PublicKey(input.tokenAddress); + const price = await fetchPrice(tokenId); + + return { + status: "success", + price, + message: `Current price: $${price} USDC`, + }; + } catch (error: any) { + return { + status: "error", + message: `Failed to fetch price: ${error.message}`, + }; + } + }, +}; + +export default fetchPriceAction; diff --git a/src/actions/getAllDomainsTLDs.ts b/src/actions/getAllDomainsTLDs.ts new file mode 100644 index 00000000..1eae1aa9 --- /dev/null +++ b/src/actions/getAllDomainsTLDs.ts @@ -0,0 +1,52 @@ +import { Action } from "../types/action"; +import { SolanaAgentKit } from "../agent"; +import { z } from "zod"; +import { getAllDomainsTLDs } from "../tools"; + +const getAllDomainsTLDsAction: Action = { + name: "solana_get_all_tlds", + similes: [ + "list domain tlds", + "get domain extensions", + "fetch domain tlds", + "get top level domains", + "list available tlds", + "get domain suffixes", + ], + description: + "Get a list of all available top-level domains (TLDs) for Solana domains", + examples: [ + [ + { + input: {}, + output: { + status: "success", + tlds: [".sol", ".abc", ".backpack", ".bonk"], + message: "Successfully retrieved all domain TLDs", + }, + explanation: + "Get a list of all available TLDs that can be used for Solana domains", + }, + ], + ], + schema: z.object({}), + handler: async (agent: SolanaAgentKit) => { + try { + // Get all domain TLDs + const tlds = await getAllDomainsTLDs(agent); + + return { + status: "success", + tlds, + message: "Successfully retrieved all domain TLDs", + }; + } catch (error: any) { + return { + status: "error", + message: `Failed to get domain TLDs: ${error.message}`, + }; + } + }, +}; + +export default getAllDomainsTLDsAction; diff --git a/src/actions/getAllRegisteredAllDomains.ts b/src/actions/getAllRegisteredAllDomains.ts new file mode 100644 index 00000000..6ed91406 --- /dev/null +++ b/src/actions/getAllRegisteredAllDomains.ts @@ -0,0 +1,70 @@ +import { Action } from "../types/action"; +import { SolanaAgentKit } from "../agent"; +import { z } from "zod"; +import { getAllRegisteredAllDomains } from "../tools"; + +const getAllRegisteredAllDomainsAction: Action = { + name: "solana_get_all_registered_all_domains", + similes: [ + "list registered domains", + "get all domains", + "fetch registered domains", + "get domain list", + "list active domains", + "get registered names", + ], + description: "Get a list of all registered domains across all TLDs", + examples: [ + [ + { + input: { + limit: 100, + offset: 0, + }, + output: { + status: "success", + domains: ["solana.sol", "bonk.abc", "wallet.backpack"], + total: 3, + message: "Successfully retrieved registered domains", + }, + explanation: "Get the first 100 registered domains across all TLDs", + }, + ], + ], + schema: z.object({ + limit: z + .number() + .positive() + .max(1000) + .default(100) + .describe("Maximum number of domains to return"), + offset: z + .number() + .nonnegative() + .default(0) + .describe("Number of domains to skip"), + }), + handler: async (agent: SolanaAgentKit, input: Record) => { + try { + const limit = input.limit || 100; + const offset = input.offset || 0; + + // Get all registered domains + const domains = await getAllRegisteredAllDomains(agent); + + return { + status: "success", + domains: domains.slice(offset, offset + limit), + total: domains.length, + message: "Successfully retrieved registered domains", + }; + } catch (error: any) { + return { + status: "error", + message: `Failed to get registered domains: ${error.message}`, + }; + } + }, +}; + +export default getAllRegisteredAllDomainsAction; diff --git a/src/actions/getMainAllDomainsDomain.ts b/src/actions/getMainAllDomainsDomain.ts new file mode 100644 index 00000000..de35ee22 --- /dev/null +++ b/src/actions/getMainAllDomainsDomain.ts @@ -0,0 +1,67 @@ +import { Action } from "../types/action"; +import { SolanaAgentKit } from "../agent"; +import { z } from "zod"; +import { PublicKey } from "@solana/web3.js"; +import { getMainAllDomainsDomain } from "../tools"; + +const getMainAllDomainsDomainAction: Action = { + name: "solana_get_main_domain", + similes: [ + "get main domain", + "fetch primary domain", + "get default domain", + "get main address name", + "get primary name", + "get main domain name", + ], + description: "Get the main domain associated with a wallet address", + examples: [ + [ + { + input: { + address: "7nxQB...", + }, + output: { + status: "success", + domain: "solana.sol", + message: "Successfully retrieved main domain", + }, + explanation: "Get the main domain name for a given wallet address", + }, + ], + ], + schema: z.object({ + address: z + .string() + .min(1) + .describe("The wallet address to get the main domain for"), + }), + handler: async (agent: SolanaAgentKit, input: Record) => { + try { + const mainDomain = await getMainAllDomainsDomain( + agent, + new PublicKey(input.address), + ); + + if (!mainDomain) { + return { + status: "error", + message: "No main domain found for this address", + }; + } + + return { + status: "success", + domain: mainDomain, + message: "Successfully retrieved main domain", + }; + } catch (error: any) { + return { + status: "error", + message: `Failed to get main domain: ${error.message}`, + }; + } + }, +}; + +export default getMainAllDomainsDomainAction; diff --git a/src/actions/getOwnedAllDomains.ts b/src/actions/getOwnedAllDomains.ts new file mode 100644 index 00000000..359bf389 --- /dev/null +++ b/src/actions/getOwnedAllDomains.ts @@ -0,0 +1,63 @@ +import { Action } from "../types/action"; +import { SolanaAgentKit } from "../agent"; +import { z } from "zod"; +import { PublicKey } from "@solana/web3.js"; +import { getOwnedAllDomains } from "../tools"; + +const getOwnedAllDomainsAction: Action = { + name: "solana_get_owned_all_domains", + similes: [ + "list owned domains", + "get my domains", + "fetch wallet domains", + "get owned names", + "list my domains", + "get address domains", + ], + description: + "Get all domains owned by a specific wallet address across all TLDs", + examples: [ + [ + { + input: { + address: "7nxQB...", + }, + output: { + status: "success", + domains: ["solana.sol", "wallet.abc", "user.backpack"], + total: 3, + message: "Successfully retrieved owned domains", + }, + explanation: "Get all domain names owned by a specific wallet address", + }, + ], + ], + schema: z.object({ + address: z + .string() + .min(1) + .describe("The wallet address to get owned domains for"), + }), + handler: async (agent: SolanaAgentKit, input: Record) => { + try { + const address = new PublicKey(input.address); + + // Get owned domains + const domains = await getOwnedAllDomains(agent, address); + + return { + status: "success", + domains, + total: domains.length, + message: `Successfully retrieved ${domains.length} owned domain${domains.length === 1 ? "" : "s"}`, + }; + } catch (error: any) { + return { + status: "error", + message: `Failed to get owned domains: ${error.message}`, + }; + } + }, +}; + +export default getOwnedAllDomainsAction; diff --git a/src/actions/getOwnedDomainsForTLD.ts b/src/actions/getOwnedDomainsForTLD.ts new file mode 100644 index 00000000..1c0b4be9 --- /dev/null +++ b/src/actions/getOwnedDomainsForTLD.ts @@ -0,0 +1,63 @@ +import { Action } from "../types/action"; +import { SolanaAgentKit } from "../agent"; +import { z } from "zod"; +import { getOwnedDomainsForTLD } from "../tools"; + +const getOwnedDomainsForTLDAction: Action = { + name: "solana_get_owned_tld_domains", + similes: [ + "list owned domains for tld", + "get my domains for extension", + "fetch wallet domains by tld", + "get owned names by extension", + "list my domains by tld", + "get address domains for tld", + ], + description: + "Get all domains owned by a specific wallet address for a given top-level domain (TLD)", + examples: [ + [ + { + input: { + tld: "sol", + }, + output: { + status: "success", + domains: ["solana.sol", "wallet.sol", "user.sol"], + total: 3, + message: "Successfully retrieved owned domains for .sol", + }, + explanation: + "Get all .sol domain names owned by a specific wallet address", + }, + ], + ], + schema: z.object({ + tld: z + .string() + .min(1) + .describe("The top-level domain to filter by (e.g., 'sol', 'abc')"), + }), + handler: async (agent: SolanaAgentKit, input: Record) => { + try { + const tld = input.tld.toLowerCase(); + + // Get owned domains for TLD + const domains = await getOwnedDomainsForTLD(agent, tld); + + return { + status: "success", + domains, + total: domains.length, + message: `Successfully retrieved ${domains.length} owned domain${domains.length === 1 ? "" : "s"} for .${tld}`, + }; + } catch (error: any) { + return { + status: "error", + message: `Failed to get owned domains: ${error.message}`, + }; + } + }, +}; + +export default getOwnedDomainsForTLDAction; diff --git a/src/actions/getPrimaryDomain.ts b/src/actions/getPrimaryDomain.ts new file mode 100644 index 00000000..f678a31d --- /dev/null +++ b/src/actions/getPrimaryDomain.ts @@ -0,0 +1,57 @@ +import { Action } from "../types/action"; +import { SolanaAgentKit } from "../agent"; +import { z } from "zod"; +import { PublicKey } from "@solana/web3.js"; +import { getPrimaryDomain } from "../tools"; + +const getPrimaryDomainAction: Action = { + name: "solana_get_domain", + similes: [ + "get primary domain", + "lookup primary domain", + "check primary domain", + "find primary domain", + "get main domain", + "primary sol domain", + ], + description: + "Get the primary .sol domain associated with a Solana wallet address", + examples: [ + [ + { + input: { + account: "7nxQB...", + }, + output: { + status: "success", + domain: "vitalik.sol", + message: "Primary domain: vitalik.sol", + }, + explanation: "Get the primary .sol domain for a wallet address", + }, + ], + ], + schema: z.object({ + account: z.string().min(1).describe("The Solana wallet address to lookup"), + }), + handler: async (agent: SolanaAgentKit, input: Record) => { + try { + const account = new PublicKey(input.account); + + const response = await getPrimaryDomain(agent, account); + + return { + status: "success", + domain: response, + message: `Primary domain: ${response}`, + }; + } catch (error: any) { + return { + status: "error", + message: `Failed to get primary domain: ${error.message}`, + }; + } + }, +}; + +export default getPrimaryDomainAction; diff --git a/src/actions/getTPS.ts b/src/actions/getTPS.ts new file mode 100644 index 00000000..b5c09791 --- /dev/null +++ b/src/actions/getTPS.ts @@ -0,0 +1,48 @@ +import { Action } from "../types/action"; +import { SolanaAgentKit } from "../agent"; +import { z } from "zod"; +import { getTPS } from "../tools"; + +const getTPSAction: Action = { + name: "solana_get_tps", + similes: [ + "get transactions per second", + "check network speed", + "network performance", + "transaction throughput", + "network tps", + ], + description: + "Get the current transactions per second (TPS) of the Solana network", + examples: [ + [ + { + input: {}, + output: { + status: "success", + tps: 3500, + message: "Current network TPS: 3500", + }, + explanation: "Get the current TPS of the Solana network", + }, + ], + ], + schema: z.object({}), // No input parameters required + handler: async (agent: SolanaAgentKit, _input: Record) => { + try { + const response = await getTPS(agent); + return { + status: "success", + response, + message: `Current network TPS: ${response}`, + }; + } catch (error: any) { + return { + status: "error", + message: `Failed to get TPS: ${error.message}`, + }; + } + }, +}; + +export default getTPSAction; diff --git a/src/actions/getTokenData.ts b/src/actions/getTokenData.ts new file mode 100644 index 00000000..f416a0c0 --- /dev/null +++ b/src/actions/getTokenData.ts @@ -0,0 +1,98 @@ +import { Action } from "../types/action"; +import { SolanaAgentKit } from "../agent"; +import { z } from "zod"; +import { PublicKey } from "@solana/web3.js"; +import { JupiterTokenData } from "../types"; +import { getTokenAddressFromTicker, getTokenDataByAddress } from "../tools"; + +const getTokenDataAction: Action = { + name: "solana_token_data", + similes: [ + "get token info", + "token details", + "lookup token", + "find token", + "token data", + ], + description: "Get token data from either a token address or ticker symbol", + examples: [ + [ + { + input: { + address: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", + }, + output: { + status: "success", + token: { + name: "USD Coin", + symbol: "USDC", + address: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", + decimals: 6, + }, + }, + explanation: "Get token data using the token's address", + }, + ], + [ + { + input: { + ticker: "SOL", + }, + output: { + status: "success", + token: { + name: "Wrapped SOL", + symbol: "SOL", + address: "So11111111111111111111111111111111111111112", + decimals: 9, + }, + }, + explanation: "Get token data using the token's ticker symbol", + }, + ], + ], + schema: z + .object({ + address: z.string().optional().describe("The token's mint address"), + ticker: z.string().optional().describe("The token's ticker symbol"), + }) + .refine((data) => data.address || data.ticker, { + message: "Either address or ticker must be provided", + }), + handler: async (agent: SolanaAgentKit, input: Record) => { + try { + let tokenData: JupiterTokenData | undefined; + if (input.address) { + tokenData = await getTokenDataByAddress(new PublicKey(input.address)); + } else if (input.ticker) { + const address = await getTokenAddressFromTicker(input.ticker); + if (address) { + tokenData = await getTokenDataByAddress(new PublicKey(address)); + } + } + if (!tokenData) { + return { + status: "error", + message: "Token not found or not verified", + }; + } + return { + status: "success", + token: { + name: tokenData.name, + symbol: tokenData.symbol, + address: tokenData.address, + decimals: tokenData.decimals, + logoURI: tokenData.logoURI, + }, + }; + } catch (error: any) { + return { + status: "error", + message: `Failed to get token data: ${error.message}`, + }; + } + }, +}; + +export default getTokenDataAction; diff --git a/src/actions/index.ts b/src/actions/index.ts new file mode 100644 index 00000000..cb81ce1c --- /dev/null +++ b/src/actions/index.ts @@ -0,0 +1,61 @@ +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"; +import resolveDomainAction from "./resolveDomain"; +import getTokenDataAction from "./getTokenData"; +import getTPSAction from "./getTPS"; +import fetchPriceAction from "./fetchPrice"; +import stakeWithJupAction from "./stakeWithJup"; +import registerDomainAction from "./registerDomain"; +import lendAssetAction from "./lendAsset"; +import createGibworkTaskAction from "./createGibworkTask"; +import resolveSolDomainAction from "./resolveSolDomain"; +import pythFetchPriceAction from "./pythFetchPrice"; +import getOwnedDomainsForTLDAction from "./getOwnedDomainsForTLD"; +import getPrimaryDomainAction from "./getPrimaryDomain"; +import getAllDomainsTLDsAction from "./getAllDomainsTLDs"; +import getOwnedAllDomainsAction from "./getOwnedAllDomains"; +import createImageAction from "./createImage"; +import getMainAllDomainsDomainAction from "./getMainAllDomainsDomain"; +import getAllRegisteredAllDomainsAction from "./getAllRegisteredAllDomains"; +import raydiumCreateCpmmAction from "./raydiumCreateCpmm"; +import raydiumCreateAmmV4Action from "./raydiumCreateAmmV4"; +import createOrcaSingleSidedWhirlpoolAction from "./createOrcaSingleSidedWhirlpool"; +import launchPumpfunTokenAction from "./launchPumpfunToken"; + +export const actions = [ + deployTokenAction, + balanceAction, + transferAction, + deployCollectionAction, + mintNFTAction, + tradeAction, + requestFundsAction, + resolveDomainAction, + getTokenDataAction, + getTPSAction, + fetchPriceAction, + stakeWithJupAction, + registerDomainAction, + lendAssetAction, + createGibworkTaskAction, + resolveSolDomainAction, + pythFetchPriceAction, + getOwnedDomainsForTLDAction, + getPrimaryDomainAction, + getAllDomainsTLDsAction, + getOwnedAllDomainsAction, + createImageAction, + getMainAllDomainsDomainAction, + getAllRegisteredAllDomainsAction, + raydiumCreateCpmmAction, + raydiumCreateAmmV4Action, + createOrcaSingleSidedWhirlpoolAction, + launchPumpfunTokenAction, +]; + +export type { Action, ActionExample, Handler } from "../types/action"; diff --git a/src/actions/launchPumpfunToken.ts b/src/actions/launchPumpfunToken.ts new file mode 100644 index 00000000..601bb47f --- /dev/null +++ b/src/actions/launchPumpfunToken.ts @@ -0,0 +1,106 @@ +import { Action } from "../types/action"; +import { SolanaAgentKit } from "../agent"; +import { z } from "zod"; +import { launchPumpFunToken } from "../tools"; + +const launchPumpfunTokenAction: Action = { + name: "solana_launch_pumpfun_token", + similes: [ + "create pumpfun token", + "launch token on pumpfun", + "deploy pumpfun token", + "create meme token", + "launch memecoin", + "create pump token", + ], + description: + "Launch a new token on Pump.fun with customizable metadata and initial liquidity", + examples: [ + [ + { + input: { + tokenName: "Sample Token", + tokenTicker: "SMPL", + description: "A sample token for demonstration", + imageUrl: "https://example.com/token.png", + twitter: "@sampletoken", + telegram: "t.me/sampletoken", + website: "https://sampletoken.com", + initialLiquiditySOL: 0.1, + slippageBps: 10, + priorityFee: 0.0001, + }, + output: { + status: "success", + signature: "2ZE7Rz...", + mint: "7nxQB...", + metadataUri: "https://arweave.net/...", + message: "Successfully launched token on Pump.fun", + }, + explanation: + "Launch a new token with custom metadata and 0.1 SOL initial liquidity", + }, + ], + ], + schema: z.object({ + tokenName: z.string().min(1).max(32).describe("Name of the token"), + tokenTicker: z + .string() + .min(2) + .max(10) + .describe("Ticker symbol of the token"), + description: z + .string() + .min(1) + .max(1000) + .describe("Description of the token"), + imageUrl: z.string().url().describe("URL of the token image"), + twitter: z.string().optional().describe("Twitter handle (optional)"), + telegram: z.string().optional().describe("Telegram group link (optional)"), + website: z.string().url().optional().describe("Website URL (optional)"), + initialLiquiditySOL: z + .number() + .min(0.0001) + .default(0.0001) + .describe("Initial liquidity in SOL"), + slippageBps: z + .number() + .min(1) + .max(1000) + .default(5) + .describe("Slippage tolerance in basis points"), + priorityFee: z + .number() + .min(0.00001) + .default(0.00005) + .describe("Priority fee in SOL"), + }), + handler: async (agent: SolanaAgentKit, input: Record) => { + try { + const { tokenName, tokenTicker, description, imageUrl } = input; + const result = await launchPumpFunToken( + agent, + tokenName, + tokenTicker, + description, + imageUrl, + input, + ); + + return { + status: "success", + signature: result.signature, + mint: result.mint, + metadataUri: result.metadataUri, + message: "Successfully launched token on Pump.fun", + }; + } catch (error: any) { + return { + status: "error", + message: `Failed to launch token: ${error.message}`, + }; + } + }, +}; + +export default launchPumpfunTokenAction; diff --git a/src/actions/lendAsset.ts b/src/actions/lendAsset.ts new file mode 100644 index 00000000..1072acec --- /dev/null +++ b/src/actions/lendAsset.ts @@ -0,0 +1,55 @@ +import { Action } from "../types/action"; +import { SolanaAgentKit } from "../agent"; +import { z } from "zod"; +import { lendAsset } from "../tools"; + +const lendAssetAction: Action = { + name: "solana_lend_asset", + similes: [ + "lend usdc", + "deposit for yield", + "earn yield", + "lend with lulo", + "deposit usdc", + "lending", + ], + description: "Lend USDC tokens to earn yield using Lulo protocol", + examples: [ + [ + { + input: { + amount: 100, + }, + output: { + status: "success", + signature: "4xKpN2...", + message: "Successfully lent 100 USDC", + }, + explanation: "Lend 100 USDC to earn yield on Lulo", + }, + ], + ], + schema: z.object({ + amount: z.number().positive().describe("Amount of USDC to lend"), + }), + handler: async (agent: SolanaAgentKit, input: Record) => { + try { + const amount = input.amount as number; + + const response = await lendAsset(agent, amount); + + return { + status: "success", + signature: response, + message: `Successfully lent ${amount} USDC`, + }; + } catch (error: any) { + return { + status: "error", + message: `Lending failed: ${error.message}`, + }; + } + }, +}; + +export default lendAssetAction; diff --git a/src/actions/mintNFT.ts b/src/actions/mintNFT.ts new file mode 100644 index 00000000..5c3d691b --- /dev/null +++ b/src/actions/mintNFT.ts @@ -0,0 +1,90 @@ +import { PublicKey } from "@solana/web3.js"; +import { Action } from "../types/action"; +import { SolanaAgentKit } from "../agent"; +import { z } from "zod"; +import { mintCollectionNFT } from "../tools"; + +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"), + }), + handler: async (agent: SolanaAgentKit, input: Record) => { + const result = await mintCollectionNFT( + agent, + new PublicKey(input.collectionMint), + { + name: input.name, + uri: input.uri, + }, + input.recipient ? new PublicKey(input.recipient) : undefined, + ); + + 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; diff --git a/src/actions/pythFetchPrice.ts b/src/actions/pythFetchPrice.ts new file mode 100644 index 00000000..ed3d5be4 --- /dev/null +++ b/src/actions/pythFetchPrice.ts @@ -0,0 +1,56 @@ +import { Action } from "../types/action"; +import { SolanaAgentKit } from "../agent"; +import { z } from "zod"; +import { pythFetchPrice } from "../tools"; + +const pythFetchPriceAction: Action = { + name: "solana_pyth_fetch_price", + similes: [ + "get pyth price", + "check pyth price", + "pyth oracle price", + "fetch from pyth", + "pyth price feed", + "oracle price", + ], + description: "Fetch the current price from a Pyth oracle price feed", + examples: [ + [ + { + input: { + priceFeedId: "Gnt27xtC473ZT2Mw5u8wZ68Z3gULkSTb5DuxJy7eJotD", // SOL/USD price feed + }, + output: { + status: "success", + price: "23.45", + message: "Current price: $23.45", + }, + explanation: "Get the current SOL/USD price from Pyth oracle", + }, + ], + ], + schema: z.object({ + priceFeedId: z + .string() + .min(1) + .describe("The Pyth price feed ID to fetch the price from"), + }), + handler: async (_agent: SolanaAgentKit, input: Record) => { + try { + const priceFeedId = input.tokenId as string; + const priceStr = await pythFetchPrice(priceFeedId); + return { + status: "success", + price: priceStr, + message: `Current price: $${priceStr}`, + }; + } catch (error: any) { + return { + status: "error", + message: `Failed to fetch price from Pyth: ${error.message}`, + }; + } + }, +}; + +export default pythFetchPriceAction; diff --git a/src/actions/raydiumCreateAmmV4.ts b/src/actions/raydiumCreateAmmV4.ts new file mode 100644 index 00000000..7d4e37b0 --- /dev/null +++ b/src/actions/raydiumCreateAmmV4.ts @@ -0,0 +1,91 @@ +import { Action } from "../types/action"; +import { SolanaAgentKit } from "../agent"; +import { z } from "zod"; +import { PublicKey } from "@solana/web3.js"; +import BN from "bn.js"; +import { raydiumCreateAmmV4 } from "../tools"; + +const raydiumCreateAmmV4Action: Action = { + name: "raydium_create_ammV4", + similes: [ + "create raydium v4 pool", + "setup raydium v4 liquidity pool", + "initialize raydium v4 amm", + "create raydium v4 market maker", + "setup raydium v4 pool", + "create raydium v4 trading pair", + ], + description: + "Create a new AMM V4 pool on Raydium with advanced features and improved efficiency", + examples: [ + [ + { + input: { + baseMint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", // USDC + quoteMint: "So11111111111111111111111111111111111111112", // SOL + baseAmount: 1000, + quoteAmount: 10, + startPrice: 100, // 1 SOL = 100 USDC + openTime: 1672531200, // Unix timestamp + }, + output: { + status: "success", + signature: "2ZE7Rz...", + poolId: "7nxQB...", + message: "Successfully created Raydium AMM V4 pool", + }, + explanation: + "Create a USDC-SOL V4 pool with initial liquidity and price", + }, + ], + ], + schema: z.object({ + baseMint: z.string().min(1).describe("The base token mint address"), + quoteMint: z.string().min(1).describe("The quote token mint address"), + baseAmount: z + .number() + .positive() + .describe("Initial base token amount to provide as liquidity"), + quoteAmount: z + .number() + .positive() + .describe("Initial quote token amount to provide as liquidity"), + startPrice: z + .number() + .positive() + .describe("Initial price of quote token in base token units"), + openTime: z + .number() + .positive() + .describe("Unix timestamp when trading should start"), + }), + handler: async (agent: SolanaAgentKit, input: Record) => { + try { + const marketId = new PublicKey(input.marketId); + const baseAmount = new BN(input.baseAmount); + const quoteAmount = new BN(input.quoteAmount); + const startTime = new BN(input.startTime); + + const txId = await raydiumCreateAmmV4( + agent, + marketId, + baseAmount, + quoteAmount, + startTime, + ); + + return { + status: "success", + signature: txId, + message: "Successfully created Raydium AMM V4 pool", + }; + } catch (error: any) { + return { + status: "error", + message: `Failed to create AMM V4 pool: ${error.message}`, + }; + } + }, +}; + +export default raydiumCreateAmmV4Action; diff --git a/src/actions/raydiumCreateClmm.ts b/src/actions/raydiumCreateClmm.ts new file mode 100644 index 00000000..277cc06b --- /dev/null +++ b/src/actions/raydiumCreateClmm.ts @@ -0,0 +1,76 @@ +import { Action } from "../types/action"; +import { SolanaAgentKit } from "../agent"; +import { z } from "zod"; +import { PublicKey } from "@solana/web3.js"; +import { BN } from "@coral-xyz/anchor"; +import Decimal from "decimal.js"; +import { raydiumCreateClmm } from "../tools"; + +const raydiumCreateClmmAction: Action = { + name: "raydium_create_clmm", + similes: [ + "create clmm pool", + "create concentrated liquidity pool", + "raydium clmm setup", + "launch concentrated liquidity market maker", + ], + description: `Create a Raydium Concentrated Liquidity Market Maker (CLMM) pool with custom ranges, providing increased capital efficiency`, + examples: [ + [ + { + input: { + mint1: "9xU1vzz456... (PublicKey)", + mint2: "EfrsBcG98... (PublicKey)", + configId: "D6yTTr... (Config PublicKey)", + initialPrice: 123.12, + startTime: 0, // or current UNIX timestamp + }, + output: { + status: "success", + message: "Create raydium clmm pool successfully", + transaction: "3skCN8... (transaction signature)", + }, + explanation: + "Creates a CLMM pool between mint1 and mint2 at an initial price of 123.12 and start time of 0.", + }, + ], + ], + // Validate tool inputs using zod + schema: z.object({ + mint1: z.string().min(1).describe("First token mint address (public key)"), + mint2: z.string().min(1).describe("Second token mint address (public key)"), + configId: z.string().min(1).describe("Raydium configId (public key)"), + initialPrice: z.number().describe("Initial price for the CLMM pool"), + startTime: z + .number() + .describe("Start time in seconds (UNIX timestamp or zero)"), + }), + handler: async (agent: SolanaAgentKit, input: Record) => { + try { + const { mint1, mint2, configId, initialPrice, startTime } = input; + + const tx = await raydiumCreateClmm( + agent, + new PublicKey(mint1), + new PublicKey(mint2), + new PublicKey(configId), + new Decimal(initialPrice), + new BN(startTime), + ); + + return { + status: "success", + message: "Create raydium clmm pool successfully", + transaction: tx, + }; + } catch (error: any) { + return { + status: "error", + message: `Failed to create CLMM pool: ${error.message}`, + code: error.code || "UNKNOWN_ERROR", + }; + } + }, +}; + +export default raydiumCreateClmmAction; diff --git a/src/actions/raydiumCreateCpmm.ts b/src/actions/raydiumCreateCpmm.ts new file mode 100644 index 00000000..28fe82a3 --- /dev/null +++ b/src/actions/raydiumCreateCpmm.ts @@ -0,0 +1,90 @@ +import { Action } from "../types/action"; +import { SolanaAgentKit } from "../agent"; +import { z } from "zod"; +import { PublicKey } from "@solana/web3.js"; +import BN from "bn.js"; +import { raydiumCreateCpmm } from "../tools"; + +const raydiumCreateCpmmAction: Action = { + name: "solana_raydium_create_cpmm", + similes: [ + "create raydium pool", + "setup raydium liquidity pool", + "initialize raydium amm", + "create constant product market maker", + "setup raydium cpmm", + "create raydium trading pair", + ], + description: + "Create a new Constant Product Market Maker (CPMM) pool on Raydium", + examples: [ + [ + { + input: { + baseMint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", // USDC + quoteMint: "So11111111111111111111111111111111111111112", // SOL + baseAmount: 1000, + quoteAmount: 10, + startTime: 1672531200, // Unix timestamp + }, + output: { + status: "success", + signature: "2ZE7Rz...", + poolId: "7nxQB...", + message: "Successfully created Raydium CPMM pool", + }, + explanation: + "Create a USDC-SOL pool with initial liquidity of 1000 USDC and 10 SOL", + }, + ], + ], + schema: z.object({ + baseMint: z.string().min(1).describe("The base token mint address"), + quoteMint: z.string().min(1).describe("The quote token mint address"), + baseAmount: z + .number() + .positive() + .describe("Initial base token amount to provide as liquidity"), + quoteAmount: z + .number() + .positive() + .describe("Initial quote token amount to provide as liquidity"), + startTime: z + .number() + .positive() + .describe("Unix timestamp when trading should start"), + }), + handler: async (agent: SolanaAgentKit, input: Record) => { + try { + const mintA = new PublicKey(input.baseMint); + const mintB = new PublicKey(input.quoteMint); + const configId = new PublicKey(input.configId); + const mintAAmount = new BN(input.baseAmount); + const mintBAmount = new BN(input.quoteAmount); + const startTime = new BN(input.startTime); + + const txId = await raydiumCreateCpmm( + agent, + mintA, + mintB, + configId, + mintAAmount, + mintBAmount, + startTime, + ); + + return { + status: "success", + signature: txId, + message: "Successfully created Raydium CPMM pool", + }; + } catch (error: any) { + return { + status: "error", + message: `Failed to create CPMM pool: ${error.message}`, + }; + } + }, +}; + +export default raydiumCreateCpmmAction; diff --git a/src/actions/registerDomain.ts b/src/actions/registerDomain.ts new file mode 100644 index 00000000..b4a6d8d9 --- /dev/null +++ b/src/actions/registerDomain.ts @@ -0,0 +1,63 @@ +import { Action } from "../types/action"; +import { SolanaAgentKit } from "../agent"; +import { z } from "zod"; +import { registerDomain } from "../tools"; + +const registerDomainAction: Action = { + name: "solana_register_domain", + similes: [ + "register domain", + "buy domain", + "get domain name", + "register .sol", + "purchase domain", + "domain registration", + ], + description: "Register a .sol domain name using Bonfida Name Service", + examples: [ + [ + { + input: { + name: "mydomain", + spaceKB: 1, + }, + output: { + status: "success", + signature: "2ZE7Rz...", + message: "Successfully registered mydomain.sol", + }, + explanation: "Register a new .sol domain with 1KB storage space", + }, + ], + ], + schema: z.object({ + name: z.string().min(1).describe("Domain name to register (without .sol)"), + spaceKB: z + .number() + .min(1) + .max(10) + .default(1) + .describe("Space allocation in KB (max 10KB)"), + }), + handler: async (agent: SolanaAgentKit, input: Record) => { + try { + const name = input.name as string; + const spaceKB = (input.spaceKB as number) || 1; + + const signature = await registerDomain(agent, name, spaceKB); + + return { + status: "success", + signature, + message: `Successfully registered ${name}.sol`, + }; + } catch (error: any) { + return { + status: "error", + message: `Domain registration failed: ${error.message}`, + }; + } + }, +}; + +export default registerDomainAction; diff --git a/src/actions/requestFunds.ts b/src/actions/requestFunds.ts new file mode 100644 index 00000000..116c15e7 --- /dev/null +++ b/src/actions/requestFunds.ts @@ -0,0 +1,41 @@ +import { Action } from "../types/action"; +import { SolanaAgentKit } from "../agent"; +import { z } from "zod"; +import { request_faucet_funds } from "../tools"; + +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) => { + await request_faucet_funds(agent); + + return { + status: "success", + message: "Successfully requested faucet funds", + network: agent.connection.rpcEndpoint.split("/")[2], + }; + }, +}; + +export default requestFundsAction; diff --git a/src/actions/resolveDomain.ts b/src/actions/resolveDomain.ts new file mode 100644 index 00000000..d159141c --- /dev/null +++ b/src/actions/resolveDomain.ts @@ -0,0 +1,51 @@ +import { Action } from "../types/action"; +import { SolanaAgentKit } from "../agent"; +import { z } from "zod"; +import { resolveAllDomains } from "../tools"; + +const resolveDomainAction: Action = { + name: "solana_resolve_all_domains", + similes: [ + "resolve domain", + "lookup domain", + "get domain owner", + "check domain", + "find domain owner", + ], + description: "Resolve a Solana domain name to get its owner's public key", + examples: [ + [ + { + input: { + domain: "example.sol", + }, + output: { + status: "success", + owner: "7nxQB...", + }, + explanation: "Resolve a .sol domain name to get the owner's public key", + }, + ], + ], + schema: z.object({ + domain: z.string().min(1).describe("The domain name to resolve"), + }), + handler: async (agent: SolanaAgentKit, input: Record) => { + try { + const domain = input.domain as string; + const tld = await resolveAllDomains(agent, domain); + return { + status: "success", + owner: tld, + message: `Successfully resolved domain ${domain}`, + }; + } catch (error: any) { + return { + status: "error", + message: `Failed to resolve domain: ${error.message}`, + }; + } + }, +}; + +export default resolveDomainAction; diff --git a/src/actions/resolveSolDomain.ts b/src/actions/resolveSolDomain.ts new file mode 100644 index 00000000..26ebae04 --- /dev/null +++ b/src/actions/resolveSolDomain.ts @@ -0,0 +1,59 @@ +import { Action } from "../types/action"; +import { SolanaAgentKit } from "../agent"; +import { z } from "zod"; +import { resolveSolDomain } from "../tools"; + +const resolveSolDomainAction: Action = { + name: "solana_resolve_sol_domain", + similes: [ + "resolve sol domain", + "lookup sol domain", + "get sol domain owner", + "check sol domain", + "find sol domain owner", + "resolve .sol", + ], + description: + "Resolve a .sol domain to its corresponding Solana wallet address using Bonfida Name Service", + examples: [ + [ + { + input: { + domain: "vitalik.sol", + }, + output: { + status: "success", + owner: "7nxQB...", + message: "Successfully resolved vitalik.sol", + }, + explanation: "Resolve a .sol domain to get the owner's wallet address", + }, + ], + ], + schema: z.object({ + domain: z + .string() + .min(1) + .describe("The .sol domain to resolve (with or without .sol suffix)"), + }), + handler: async (agent: SolanaAgentKit, input: Record) => { + try { + const domain = input.domain as string; + + const res = await resolveSolDomain(agent, domain); + + return { + status: "success", + owner: res.toString(), + message: `Successfully resolved ${res}`, + }; + } catch (error: any) { + return { + status: "error", + message: `Failed to resolve domain: ${error.message}`, + }; + } + }, +}; + +export default resolveSolDomainAction; diff --git a/src/actions/stakeWithJup.ts b/src/actions/stakeWithJup.ts new file mode 100644 index 00000000..630f0249 --- /dev/null +++ b/src/actions/stakeWithJup.ts @@ -0,0 +1,55 @@ +import { Action } from "../types/action"; +import { SolanaAgentKit } from "../agent"; +import { z } from "zod"; +import { stakeWithJup } from "../tools"; + +const stakeWithJupAction: Action = { + name: "solana_stake_with_jup", + similes: [ + "stake sol", + "stake with jupiter", + "jup staking", + "stake with jup", + "liquid staking", + "get jupsol", + ], + description: + "Stake SOL tokens with Jupiter's liquid staking protocol to receive jupSOL", + examples: [ + [ + { + input: { + amount: 1.5, + }, + output: { + status: "success", + signature: "5KtPn3...", + message: "Successfully staked 1.5 SOL for jupSOL", + }, + explanation: "Stake 1.5 SOL to receive jupSOL tokens", + }, + ], + ], + schema: z.object({ + amount: z.number().positive().describe("Amount of SOL to stake"), + }), + handler: async (agent: SolanaAgentKit, input: Record) => { + try { + const amount = input.amount as number; + + const res = await stakeWithJup(agent, amount); + return { + status: "success", + res, + message: `Successfully staked ${amount} SOL for jupSOL`, + }; + } catch (error: any) { + return { + status: "error", + message: `jupSOL staking failed: ${error.message}`, + }; + } + }, +}; + +export default stakeWithJupAction; diff --git a/src/actions/tokenDataByTicker.ts b/src/actions/tokenDataByTicker.ts new file mode 100644 index 00000000..3ab3f127 --- /dev/null +++ b/src/actions/tokenDataByTicker.ts @@ -0,0 +1,60 @@ +import { Action } from "../types/action"; +import { SolanaAgentKit } from "../agent"; +import { z } from "zod"; +import { getTokenDataByTicker } from "../tools"; + +const tokenDataByTickerAction: Action = { + name: "solana_token_data_by_ticker", + similes: [ + "token data by ticker", + "fetch token info by ticker", + "lookup token ticker info", + "get token info by ticker", + ], + description: "Get the token data for a given token ticker", + examples: [ + [ + { + input: { + ticker: "USDC", + }, + output: { + status: "success", + tokenData: { + // Some placeholder example data + symbol: "USDC", + name: "USD Coin", + decimals: 6, + mintAddress: "FhRg...", + }, + }, + explanation: "Fetches metadata for the USDC token by its ticker.", + }, + ], + ], + schema: z.object({ + ticker: z.string().min(1).describe("Ticker of the token, e.g. 'USDC'"), + }), + handler: async (agent: SolanaAgentKit, input: Record) => { + try { + const ticker = input.ticker as string; + + // Use agent’s method to get token data by ticker + const tokenData = await getTokenDataByTicker(ticker); + + return { + status: "success", + tokenData: tokenData, + message: `Successfully fetched token data for ticker: ${ticker}`, + }; + } catch (error: any) { + return { + status: "error", + message: `Failed to fetch token data for ticker: ${input.ticker || ""}. ${error.message}`, + code: error.code || "UNKNOWN_ERROR", + }; + } + }, +}; + +export default tokenDataByTickerAction; diff --git a/src/actions/trade.ts b/src/actions/trade.ts new file mode 100644 index 00000000..397f0ca9 --- /dev/null +++ b/src/actions/trade.ts @@ -0,0 +1,85 @@ +import { PublicKey } from "@solana/web3.js"; +import { Action } from "../types/action"; +import { SolanaAgentKit } from "../agent"; +import { z } from "zod"; +import { trade } from "../tools"; + +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) => { + const tx = await trade( + agent, + 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; diff --git a/src/actions/transfer.ts b/src/actions/transfer.ts new file mode 100644 index 00000000..cc8a6480 --- /dev/null +++ b/src/actions/transfer.ts @@ -0,0 +1,78 @@ +import { PublicKey } from "@solana/web3.js"; +import { Action } from "../types/action"; +import { SolanaAgentKit } from "../agent"; +import { z } from "zod"; +import { transfer } from "../tools"; + +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) => { + const recipient = new PublicKey(input.to); + const mintAddress = input.mint ? new PublicKey(input.mint) : undefined; + + const tx = await transfer(agent, 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; diff --git a/src/index.ts b/src/index.ts index 1b4116f8..4ae9056b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,3 +5,8 @@ export { SolanaAgentKit, createSolanaTools }; // Optional: Export types that users might need export * from "./types"; + +// Export action system +export * from "./actions"; +export * from "./types/action"; +export * from "./utils/actionExecutor"; diff --git a/src/types/action.ts b/src/types/action.ts new file mode 100644 index 00000000..df4c0beb --- /dev/null +++ b/src/types/action.ts @@ -0,0 +1,56 @@ +import { SolanaAgentKit } from "../agent"; +import { z } from "zod"; + +/** + * Example of an action with input and output + */ +export interface ActionExample { + input: Record; + output: Record; + explanation: string; +} + +/** + * Handler function type for executing the action + */ +export type Handler = ( + agent: SolanaAgentKit, + input: Record, +) => Promise>; + +/** + * Main Action interface inspired by ELIZA + * This interface makes it easier to implement actions across different frameworks + */ +export interface Action { + /** + * Unique name of the action + */ + name: string; + + /** + * Alternative names/phrases that can trigger this action + */ + similes: string[]; + + /** + * Detailed description of what the action does + */ + description: string; + + /** + * Array of example inputs and outputs for the action + * Each inner array represents a group of related examples + */ + examples: ActionExample[][]; + + /** + * Zod schema for input validation + */ + schema: z.ZodType; + + /** + * Function that executes the action + */ + handler: Handler; +} diff --git a/src/types/index.ts b/src/types/index.ts index 54592bb9..5c7786cb 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -97,3 +97,4 @@ export interface GibworkCreateTaskReponse { taskId?: string | undefined; signature?: string | undefined; } + diff --git a/src/utils/actionExecutor.ts b/src/utils/actionExecutor.ts new file mode 100644 index 00000000..e152ecbe --- /dev/null +++ b/src/utils/actionExecutor.ts @@ -0,0 +1,68 @@ +import { Action } from "../types/action"; +import { SolanaAgentKit } from "../agent"; +import { actions } from "../actions"; + +/** + * Find an action by its name or one of its similes + */ +export function findAction(query: string): Action | undefined { + const normalizedQuery = query.toLowerCase().trim(); + return actions.find( + (action) => + action.name.toLowerCase() === normalizedQuery || + action.similes.some((simile) => simile.toLowerCase() === normalizedQuery), + ); +} + +/** + * Execute an action with the given input + */ +export async function executeAction( + action: Action, + agent: SolanaAgentKit, + input: Record, +): Promise> { + try { + // Validate input using Zod schema + const validatedInput = action.schema.parse(input); + + // Execute the action with validated input + const result = await action.handler(agent, validatedInput); + + return { + status: "success", + ...result, + }; + } catch (error: any) { + // Handle Zod validation errors specially + if (error.errors) { + return { + status: "error", + message: "Validation error", + details: error.errors, + code: "VALIDATION_ERROR", + }; + } + + return { + status: "error", + message: error.message, + code: error.code || "EXECUTION_ERROR", + }; + } +} + +/** + * Get examples for an action + */ +export function getActionExamples(action: Action): string { + return action.examples + .flat() + .map((example) => { + return `Input: ${JSON.stringify(example.input, null, 2)} +Output: ${JSON.stringify(example.output, null, 2)} +Explanation: ${example.explanation} +---`; + }) + .join("\n"); +} diff --git a/test/index.ts b/test/index.ts index b6e51e70..a9b115f6 100644 --- a/test/index.ts +++ b/test/index.ts @@ -59,6 +59,7 @@ async function initializeAgent() { ); const tools = createSolanaTools(solanaAgent); + const memory = new MemorySaver(); const config = { configurable: { thread_id: "Solana Agent Kit!" } };