From 79cada2cbd65d87ef0a6d97531dad3648a9dab43 Mon Sep 17 00:00:00 2001 From: Fahri Bilici <28020526+FahriBilici@users.noreply.github.com> Date: Thu, 26 Dec 2024 21:54:55 +0100 Subject: [PATCH 01/11] 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. --- src/actions/balance.ts | 59 ++++++++ src/actions/deployCollection.ts | 78 ++++++++++ src/actions/deployToken.ts | 74 ++++++++++ src/actions/index.ts | 20 +++ src/actions/mintNFT.ts | 88 +++++++++++ src/actions/requestFunds.ts | 40 +++++ src/actions/trade.ts | 81 ++++++++++ src/actions/transfer.ts | 75 ++++++++++ src/index.ts | 5 + src/langchain/index.ts | 254 +++++++++++--------------------- src/types/action.ts | 53 +++++++ src/utils/actionExecutor.ts | 67 +++++++++ src/utils/langchainWrapper.ts | 96 ++++++++++++ 13 files changed, 825 insertions(+), 165 deletions(-) create mode 100644 src/actions/balance.ts create mode 100644 src/actions/deployCollection.ts create mode 100644 src/actions/deployToken.ts create mode 100644 src/actions/index.ts create mode 100644 src/actions/mintNFT.ts create mode 100644 src/actions/requestFunds.ts create mode 100644 src/actions/trade.ts create mode 100644 src/actions/transfer.ts create mode 100644 src/types/action.ts create mode 100644 src/utils/actionExecutor.ts create mode 100644 src/utils/langchainWrapper.ts diff --git a/src/actions/balance.ts b/src/actions/balance.ts new file mode 100644 index 0000000..2a4d34d --- /dev/null +++ b/src/actions/balance.ts @@ -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) => { + 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; \ No newline at end of file diff --git a/src/actions/deployCollection.ts b/src/actions/deployCollection.ts new file mode 100644 index 0000000..0127b5c --- /dev/null +++ b/src/actions/deployCollection.ts @@ -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) => { + 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; \ No newline at end of file diff --git a/src/actions/deployToken.ts b/src/actions/deployToken.ts new file mode 100644 index 0000000..046c120 --- /dev/null +++ b/src/actions/deployToken.ts @@ -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) => { + 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; \ No newline at end of file diff --git a/src/actions/index.ts b/src/actions/index.ts new file mode 100644 index 0000000..bf72aca --- /dev/null +++ b/src/actions/index.ts @@ -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"; \ No newline at end of file diff --git a/src/actions/mintNFT.ts b/src/actions/mintNFT.ts new file mode 100644 index 0000000..4e9e6ae --- /dev/null +++ b/src/actions/mintNFT.ts @@ -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) => { + 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; \ No newline at end of file diff --git a/src/actions/requestFunds.ts b/src/actions/requestFunds.ts new file mode 100644 index 0000000..03159a7 --- /dev/null +++ b/src/actions/requestFunds.ts @@ -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) => { + await agent.requestFaucetFunds(); + + return { + status: "success", + message: "Successfully requested faucet funds", + network: agent.connection.rpcEndpoint.split("/")[2] + }; + } +}; + +export default requestFundsAction; \ No newline at end of file diff --git a/src/actions/trade.ts b/src/actions/trade.ts new file mode 100644 index 0000000..8a536b3 --- /dev/null +++ b/src/actions/trade.ts @@ -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) => { + 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; \ No newline at end of file diff --git a/src/actions/transfer.ts b/src/actions/transfer.ts new file mode 100644 index 0000000..426a071 --- /dev/null +++ b/src/actions/transfer.ts @@ -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) => { + 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; \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index 1b4116f..4ae9056 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/langchain/index.ts b/src/langchain/index.ts index f28b000..68cd157 100644 --- a/src/langchain/index.ts +++ b/src/langchain/index.ts @@ -1,6 +1,6 @@ import { PublicKey } from "@solana/web3.js"; import Decimal from "decimal.js"; -import { Tool } from "langchain/tools"; +import { Tool } from "@langchain/core/tools"; import { GibworkCreateTaskReponse, PythFetchPriceResponse, @@ -10,282 +10,203 @@ import { create_image } from "../tools/create_image"; import { BN } from "@coral-xyz/anchor"; import { FEE_TIERS } from "../tools"; import { toJSON } from "../utils/toJSON"; +import { wrapLangChainTool } from "../utils/langchainWrapper"; +import deployTokenAction from "../actions/deployToken"; +import balanceAction from "../actions/balance"; +import transferAction from "../actions/transfer"; +import deployCollectionAction from "../actions/deployCollection"; +import mintNFTAction from "../actions/mintNFT"; +import tradeAction from "../actions/trade"; +import requestFundsAction from "../actions/requestFunds"; export class SolanaBalanceTool extends Tool { - name = "solana_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. - - Inputs: - tokenAddress: string, eg "So11111111111111111111111111111111111111112" (optional)`; + private action = balanceAction; + name = this.action.name; + description = this.action.description; constructor(private solanaKit: SolanaAgentKit) { super(); } - protected async _call(input: string): Promise { + async _call(input: string): Promise { try { - const tokenAddress = input ? new PublicKey(input) : undefined; - const balance = await this.solanaKit.getBalance(tokenAddress); - - return JSON.stringify({ - status: "success", - balance: balance, - token: input || "SOL", - }); + // Parse input as JSON if provided, otherwise use empty object + const parsedInput = input ? JSON.parse(input) : {}; + + // Validate and execute using the action + const result = await this.action.handler(this.solanaKit, parsedInput); + + return JSON.stringify(result); } catch (error: any) { return JSON.stringify({ status: "error", message: error.message, - code: error.code || "UNKNOWN_ERROR", + code: error.code || "UNKNOWN_ERROR" }); } } } export class SolanaTransferTool extends Tool { - name = "solana_transfer"; - description = `Transfer tokens or SOL to another address ( also called as wallet address ). - - Inputs ( input is a JSON string ): - to: string, eg "8x2dR8Mpzuz2YqyZyZjUbYWKSWesBo5jMx2Q9Y86udVk" (required) - amount: number, eg 1 (required) - mint?: string, eg "So11111111111111111111111111111111111111112" or "SENDdRQtYMWaQrBroBrJ2Q53fgVuq95CV9UPGEvpCxa" (optional)`; + private action = transferAction; + name = this.action.name; + description = this.action.description; constructor(private solanaKit: SolanaAgentKit) { super(); } - protected async _call(input: string): Promise { + async _call(input: string): Promise { try { + // Parse input as JSON const parsedInput = JSON.parse(input); - - const recipient = new PublicKey(parsedInput.to); - const mintAddress = parsedInput.mint - ? new PublicKey(parsedInput.mint) - : undefined; - - const tx = await this.solanaKit.transfer( - recipient, - parsedInput.amount, - mintAddress, - ); - - return JSON.stringify({ - status: "success", - message: "Transfer completed successfully", - amount: parsedInput.amount, - recipient: parsedInput.to, - token: parsedInput.mint || "SOL", - transaction: tx, - }); + + // Validate and execute using the action + const result = await this.action.handler(this.solanaKit, parsedInput); + + return JSON.stringify(result); } catch (error: any) { return JSON.stringify({ status: "error", message: error.message, - code: error.code || "UNKNOWN_ERROR", + code: error.code || "UNKNOWN_ERROR" }); } } } export class SolanaDeployTokenTool extends Tool { - name = "solana_deploy_token"; - description = `Deploy a new token on Solana blockchain. - - Inputs (input is a JSON string): - name: string, eg "My Token" (required) - uri: string, eg "https://example.com/token.json" (required) - symbol: string, eg "MTK" (required) - decimals?: number, eg 9 (optional, defaults to 9) - initialSupply?: number, eg 1000000 (optional)`; + private action = deployTokenAction; + name = this.action.name; + description = this.action.description; constructor(private solanaKit: SolanaAgentKit) { super(); } - protected async _call(input: string): Promise { + async _call(input: string): Promise { try { + // Parse input as JSON const parsedInput = JSON.parse(input); - - const result = await this.solanaKit.deployToken( - parsedInput.name, - parsedInput.uri, - parsedInput.symbol, - parsedInput.decimals, - parsedInput.initialSupply, - ); - - return JSON.stringify({ - status: "success", - message: "Token deployed successfully", - mintAddress: result.mint.toString(), - decimals: parsedInput.decimals || 9, - }); + + // Validate and execute using the action + const result = await this.action.handler(this.solanaKit, parsedInput); + + return JSON.stringify(result); } catch (error: any) { return JSON.stringify({ status: "error", message: error.message, - code: error.code || "UNKNOWN_ERROR", + code: error.code || "UNKNOWN_ERROR" }); } } } export class SolanaDeployCollectionTool extends Tool { - name = "solana_deploy_collection"; - description = `Deploy a new NFT collection on Solana blockchain. - - Inputs (input is a JSON string): - name: string, eg "My Collection" (required) - uri: string, eg "https://example.com/collection.json" (required) - royaltyBasisPoints?: number, eg 500 for 5% (optional)`; + private action = deployCollectionAction; + name = this.action.name; + description = this.action.description; constructor(private solanaKit: SolanaAgentKit) { super(); } - protected async _call(input: string): Promise { + async _call(input: string): Promise { try { + // Parse input as JSON const parsedInput = JSON.parse(input); - - const result = await this.solanaKit.deployCollection(parsedInput); - - return JSON.stringify({ - status: "success", - message: "Collection deployed successfully", - collectionAddress: result.collectionAddress.toString(), - name: parsedInput.name, - }); + + // Validate and execute using the action + const result = await this.action.handler(this.solanaKit, parsedInput); + + return JSON.stringify(result); } catch (error: any) { return JSON.stringify({ status: "error", message: error.message, - code: error.code || "UNKNOWN_ERROR", + code: error.code || "UNKNOWN_ERROR" }); } } } export class SolanaMintNFTTool extends Tool { - name = "solana_mint_nft"; - description = `Mint a new NFT in a collection on Solana blockchain. - - Inputs (input is a JSON string): - collectionMint: string, eg "J1S9H3QjnRtBbbuD4HjPV6RpRhwuk4zKbxsnCHuTgh9w" (required) - The address of the collection to mint into - name: string, eg "My NFT" (required) - uri: string, eg "https://example.com/nft.json" (required) - recipient?: string, eg "9aUn5swQzUTRanaaTwmszxiv89cvFwUCjEBv1vZCoT1u" (optional) - The wallet to receive the NFT, defaults to agent's wallet which is ${this.solanaKit.wallet_address.toString()}`; + private action = mintNFTAction; + name = this.action.name; + description = this.action.description; constructor(private solanaKit: SolanaAgentKit) { super(); } - protected async _call(input: string): Promise { + async _call(input: string): Promise { try { + // Parse input as JSON const parsedInput = JSON.parse(input); - - const result = await this.solanaKit.mintNFT( - new PublicKey(parsedInput.collectionMint), - { - name: parsedInput.name, - uri: parsedInput.uri, - }, - parsedInput.recipient - ? new PublicKey(parsedInput.recipient) - : this.solanaKit.wallet_address, - ); - - return JSON.stringify({ - status: "success", - message: "NFT minted successfully", - mintAddress: result.mint.toString(), - metadata: { - name: parsedInput.name, - symbol: parsedInput.symbol, - uri: parsedInput.uri, - }, - recipient: parsedInput.recipient || result.mint.toString(), - }); + + // Validate and execute using the action + const result = await this.action.handler(this.solanaKit, parsedInput); + + return JSON.stringify(result); } catch (error: any) { return JSON.stringify({ status: "error", message: error.message, - code: error.code || "UNKNOWN_ERROR", + code: error.code || "UNKNOWN_ERROR" }); } } } export class SolanaTradeTool extends Tool { - name = "solana_trade"; - description = `This tool can be used to swap tokens to another token ( It uses Jupiter Exchange ). - - Inputs ( input is a JSON string ): - outputMint: string, eg "So11111111111111111111111111111111111111112" or "SENDdRQtYMWaQrBroBrJ2Q53fgVuq95CV9UPGEvpCxa" (required) - inputAmount: number, eg 1 or 0.01 (required) - inputMint?: string, eg "So11111111111111111111111111111111111111112" (optional) - slippageBps?: number, eg 100 (optional)`; + private action = tradeAction; + name = this.action.name; + description = this.action.description; constructor(private solanaKit: SolanaAgentKit) { super(); } - protected async _call(input: string): Promise { + async _call(input: string): Promise { try { + // Parse input as JSON const parsedInput = JSON.parse(input); - - const tx = await this.solanaKit.trade( - new PublicKey(parsedInput.outputMint), - parsedInput.inputAmount, - parsedInput.inputMint - ? new PublicKey(parsedInput.inputMint) - : new PublicKey("So11111111111111111111111111111111111111112"), - parsedInput.slippageBps, - ); - - return JSON.stringify({ - status: "success", - message: "Trade executed successfully", - transaction: tx, - inputAmount: parsedInput.inputAmount, - inputToken: parsedInput.inputMint || "SOL", - outputToken: parsedInput.outputMint, - }); + + // Validate and execute using the action + const result = await this.action.handler(this.solanaKit, parsedInput); + + return JSON.stringify(result); } catch (error: any) { return JSON.stringify({ status: "error", message: error.message, - code: error.code || "UNKNOWN_ERROR", + code: error.code || "UNKNOWN_ERROR" }); } } } export class SolanaRequestFundsTool extends Tool { - name = "solana_request_funds"; - description = "Request SOL from Solana faucet (devnet/testnet only)"; + private action = requestFundsAction; + name = this.action.name; + description = this.action.description; constructor(private solanaKit: SolanaAgentKit) { super(); } - protected async _call(_input: string): Promise { + async _call(_input: string): Promise { try { - await this.solanaKit.requestFaucetFunds(); - - return JSON.stringify({ - status: "success", - message: "Successfully requested faucet funds", - network: this.solanaKit.connection.rpcEndpoint.split("/")[2], - }); + // No input needed for this action + const result = await this.action.handler(this.solanaKit, {}); + + return JSON.stringify(result); } catch (error: any) { return JSON.stringify({ status: "error", message: error.message, - code: error.code || "UNKNOWN_ERROR", + code: error.code || "UNKNOWN_ERROR" }); } } @@ -1230,7 +1151,7 @@ export class SolanaCreateGibworkTask extends Tool { } export function createSolanaTools(solanaKit: SolanaAgentKit) { - return [ + const tools = [ new SolanaBalanceTool(solanaKit), new SolanaTransferTool(solanaKit), new SolanaDeployTokenTool(solanaKit), @@ -1264,4 +1185,7 @@ export function createSolanaTools(solanaKit: SolanaAgentKit) { new SolanaResolveAllDomainsTool(solanaKit), new SolanaCreateGibworkTask(solanaKit), ]; + + // Convert LangChain tools to our Action interface + return tools.map(tool => wrapLangChainTool(tool, solanaKit)); } diff --git a/src/types/action.ts b/src/types/action.ts new file mode 100644 index 0000000..d715177 --- /dev/null +++ b/src/types/action.ts @@ -0,0 +1,53 @@ +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; +} \ No newline at end of file diff --git a/src/utils/actionExecutor.ts b/src/utils/actionExecutor.ts new file mode 100644 index 0000000..0bb8f4b --- /dev/null +++ b/src/utils/actionExecutor.ts @@ -0,0 +1,67 @@ +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"); +} \ No newline at end of file diff --git a/src/utils/langchainWrapper.ts b/src/utils/langchainWrapper.ts new file mode 100644 index 0000000..c7bf500 --- /dev/null +++ b/src/utils/langchainWrapper.ts @@ -0,0 +1,96 @@ +import { Tool } from "langchain/tools"; +import { Action } from "../types/action"; +import { SolanaAgentKit } from "../agent"; +import { z } from "zod"; + +/** + * Convert a LangChain tool to our Action interface + */ +export function wrapLangChainTool(tool: Tool, agent: SolanaAgentKit): Action { + // Parse the description to extract input parameters + const inputParams = parseToolDescription(tool.description); + + return { + name: tool.name, + similes: [], // LangChain tools don't have similes + description: tool.description, + examples: [], // LangChain tools don't have examples + schema: createZodSchema(inputParams), + handler: async (agent: SolanaAgentKit, input: Record) => { + const result = await tool.call(JSON.stringify(input)); + try { + return JSON.parse(result); + } catch { + return { result }; + } + } + }; +} + +/** + * Parse tool description to extract input parameters + */ +function parseToolDescription(description: string): Array<{name: string, type: string, required: boolean}> { + const lines = description.split('\n'); + const params: Array<{name: string, type: string, required: boolean}> = []; + + let inInputsSection = false; + for (const line of lines) { + const trimmed = line.trim(); + + if (trimmed === 'Inputs:' || trimmed === 'Inputs (input is a JSON string):') { + inInputsSection = true; + continue; + } + + if (inInputsSection && trimmed) { + // Match patterns like: name: string, eg "value" (required) + const match = trimmed.match(/(\w+):\s*([\w\[\]]+)(?:,\s*eg[:\s]+"[^"]+")?(?:\s*\((required|optional)\))?/); + if (match) { + params.push({ + name: match[1], + type: match[2], + required: match[3] === 'required' + }); + } + } + } + + return params; +} + +/** + * Create a Zod schema from parsed parameters + */ +function createZodSchema(params: Array<{name: string, type: string, required: boolean}>): z.ZodType { + const schemaObj: Record> = {}; + + for (const param of params) { + let schema: z.ZodType; + + switch (param.type.toLowerCase()) { + case 'string': + schema = z.string(); + break; + case 'number': + schema = z.number(); + break; + case 'boolean': + schema = z.boolean(); + break; + case 'string[]': + schema = z.array(z.string()); + break; + default: + schema = z.any(); + } + + if (!param.required) { + schema = schema.optional(); + } + + schemaObj[param.name] = schema; + } + + return z.object(schemaObj); +} \ No newline at end of file From 9297d78838e30c7d5d925978fa87df13b4c6393e Mon Sep 17 00:00:00 2001 From: Fahri Bilici <28020526+FahriBilici@users.noreply.github.com> Date: Fri, 27 Dec 2024 18:32:14 +0100 Subject: [PATCH 02/11] feat: Implement Action to Tool conversion for enhanced Solana agent functionality - Added a new function to convert Action interfaces into LangChain Tools, improving the integration of action handlers within the Solana agent. - Updated the agent initialization process to utilize the new conversion, allowing for dynamic tool creation based on defined actions. - Enhanced error handling and input validation within the action execution flow. --- test/index.ts | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/test/index.ts b/test/index.ts index 7cbf8bc..10a2003 100644 --- a/test/index.ts +++ b/test/index.ts @@ -7,6 +7,8 @@ import { ChatOpenAI } from "@langchain/openai"; import * as dotenv from "dotenv"; import * as fs from "fs"; import * as readline from "readline"; +import { Tool } from "@langchain/core/tools"; +import { Action } from "../src/types/action"; dotenv.config(); @@ -33,6 +35,52 @@ validateEnvironment(); const WALLET_DATA_FILE = "wallet_data.txt"; +// Convert our Action interface to LangChain Tool +function convertActionToTool(action: Action, solanaAgent: SolanaAgentKit): Tool { + class ActionTool extends Tool { + name = action.name; + description = action.description; + + async _call(input: string): Promise { + try { + let parsedInput; + try { + // Try to parse as JSON first + parsedInput = input ? JSON.parse(input) : {}; + } catch { + // If JSON parsing fails, use the raw input string + parsedInput = { input }; + } + + // Validate input against schema if available + if (action.schema) { + try { + parsedInput = action.schema.parse(parsedInput); + } catch (validationError: any) { + return JSON.stringify({ + status: "error", + message: `Invalid input: ${validationError.message}`, + code: "VALIDATION_ERROR" + }); + } + } + + const result = await action.handler(solanaAgent, parsedInput); + return JSON.stringify(result); + } catch (error: any) { + console.error("Action execution error:", error); + return JSON.stringify({ + status: "error", + message: error.message, + code: error.code || "UNKNOWN_ERROR" + }); + } + } + } + + return new ActionTool(); +} + async function initializeAgent() { try { const llm = new ChatOpenAI({ @@ -56,7 +104,10 @@ async function initializeAgent() { process.env.OPENAI_API_KEY!, ); - const tools = createSolanaTools(solanaAgent); + const actions = createSolanaTools(solanaAgent); + // Convert our Actions to LangChain Tools + const tools = actions.map(action => convertActionToTool(action, solanaAgent)); + const memory = new MemorySaver(); const config = { configurable: { thread_id: "Solana Agent Kit!" } }; From 9326da25b185d34f8d029243c4d21e6a305b2ae2 Mon Sep 17 00:00:00 2001 From: Fahri Bilici <28020526+FahriBilici@users.noreply.github.com> Date: Fri, 27 Dec 2024 23:34:15 +0100 Subject: [PATCH 03/11] adding missing tools --- src/actions/createGibworkTask.ts | 135 ++++++++++++ src/actions/createImage.ts | 114 ++++++++++ src/actions/createOpenbookMarket.ts | 119 ++++++++++ src/actions/createOrcaSingleSidedWhirlpool.ts | 105 +++++++++ src/actions/fetchPrice.ts | 70 ++++++ src/actions/getAllDomainsTLDs.ts | 49 +++++ src/actions/getAllRegisteredAllDomains.ts | 70 ++++++ src/actions/getMainAllDomainsDomain.ts | 67 ++++++ src/actions/getOwnedAllDomains.ts | 60 ++++++ src/actions/getOwnedDomainsForTLD.ts | 60 ++++++ src/actions/getPrimaryDomain.ts | 68 ++++++ src/actions/getTPS.ts | 62 ++++++ src/actions/getTokenData.ts | 134 ++++++++++++ src/actions/index.ts | 49 ++++- src/actions/launchPumpfunToken.ts | 203 ++++++++++++++++++ src/actions/lendAsset.ts | 101 +++++++++ src/actions/pythFetchPrice.ts | 78 +++++++ src/actions/raydiumCreateAmmV4.ts | 154 +++++++++++++ src/actions/raydiumCreateCpmm.ts | 142 ++++++++++++ src/actions/registerDomain.ts | 110 ++++++++++ src/actions/resolveDomain.ts | 61 ++++++ src/actions/resolveSolDomain.ts | 69 ++++++ src/actions/stakeWithJup.ts | 101 +++++++++ 23 files changed, 2180 insertions(+), 1 deletion(-) create mode 100644 src/actions/createGibworkTask.ts create mode 100644 src/actions/createImage.ts create mode 100644 src/actions/createOpenbookMarket.ts create mode 100644 src/actions/createOrcaSingleSidedWhirlpool.ts create mode 100644 src/actions/fetchPrice.ts create mode 100644 src/actions/getAllDomainsTLDs.ts create mode 100644 src/actions/getAllRegisteredAllDomains.ts create mode 100644 src/actions/getMainAllDomainsDomain.ts create mode 100644 src/actions/getOwnedAllDomains.ts create mode 100644 src/actions/getOwnedDomainsForTLD.ts create mode 100644 src/actions/getPrimaryDomain.ts create mode 100644 src/actions/getTPS.ts create mode 100644 src/actions/getTokenData.ts create mode 100644 src/actions/launchPumpfunToken.ts create mode 100644 src/actions/lendAsset.ts create mode 100644 src/actions/pythFetchPrice.ts create mode 100644 src/actions/raydiumCreateAmmV4.ts create mode 100644 src/actions/raydiumCreateCpmm.ts create mode 100644 src/actions/registerDomain.ts create mode 100644 src/actions/resolveDomain.ts create mode 100644 src/actions/resolveSolDomain.ts create mode 100644 src/actions/stakeWithJup.ts diff --git a/src/actions/createGibworkTask.ts b/src/actions/createGibworkTask.ts new file mode 100644 index 0000000..8ae28ce --- /dev/null +++ b/src/actions/createGibworkTask.ts @@ -0,0 +1,135 @@ +import { Action } from "../types/action"; +import { SolanaAgentKit } from "../agent"; +import { z } from "zod"; +import { PublicKey, VersionedTransaction } from "@solana/web3.js"; + +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 tokenMintAddress = new PublicKey(input.tokenMintAddress); + const payer = input.payer ? new PublicKey(input.payer) : undefined; + + const apiResponse = await fetch( + "https://api2.gib.work/tasks/public/transaction", + { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + title: input.title, + content: input.content, + requirements: input.requirements, + tags: input.tags, + payer: payer?.toBase58() || agent.wallet.publicKey.toBase58(), + token: { + mintAddress: tokenMintAddress.toBase58(), + amount: input.tokenAmount, + }, + }), + } + ); + + if (!apiResponse.ok) { + return { + status: "error", + message: `Failed to create task: ${apiResponse.statusText}` + }; + } + + const responseData = await apiResponse.json(); + if (!responseData.taskId || !responseData.serializedTransaction) { + return { + status: "error", + message: responseData.message || "Invalid response from Gibwork API" + }; + } + + const serializedTransaction = Buffer.from( + responseData.serializedTransaction, + "base64" + ); + const tx = VersionedTransaction.deserialize(serializedTransaction); + + tx.sign([agent.wallet]); + const signature = await agent.connection.sendTransaction(tx, { + preflightCommitment: "confirmed", + maxRetries: 3, + }); + + const latestBlockhash = await agent.connection.getLatestBlockhash(); + await agent.connection.confirmTransaction({ + signature, + blockhash: latestBlockhash.blockhash, + lastValidBlockHeight: latestBlockhash.lastValidBlockHeight, + }); + + return { + status: "success", + taskId: responseData.taskId, + signature, + message: `Successfully created task: ${input.title}` + }; + } catch (error: any) { + return { + status: "error", + message: `Failed to create task: ${error.message}` + }; + } + } +}; + +export default createGibworkTaskAction; \ No newline at end of file diff --git a/src/actions/createImage.ts b/src/actions/createImage.ts new file mode 100644 index 0000000..4d16b9a --- /dev/null +++ b/src/actions/createImage.ts @@ -0,0 +1,114 @@ +import { Action } from "../types/action"; +import { SolanaAgentKit } from "../agent"; +import { z } from "zod"; +import OpenAI from "openai"; + +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.openai_api_key) { + return { + status: "error", + message: "OpenAI API key not found in agent configuration" + }; + } + + const { prompt, model, size, quality, style } = input; + + const openai = new OpenAI({ + apiKey: agent.openai_api_key + }); + + const response = await openai.images.generate({ + prompt, + model, + n: 1, + size, + quality, + style + }); + + if (!response.data || response.data.length === 0) { + return { + status: "error", + message: "No image was generated" + }; + } + + return { + status: "success", + imageUrl: response.data[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; \ No newline at end of file diff --git a/src/actions/createOpenbookMarket.ts b/src/actions/createOpenbookMarket.ts new file mode 100644 index 0000000..991ab97 --- /dev/null +++ b/src/actions/createOpenbookMarket.ts @@ -0,0 +1,119 @@ +import { Action } from "../types/action"; +import { SolanaAgentKit } from "../agent"; +import { z } from "zod"; +import { OPEN_BOOK_PROGRAM, Raydium, TxVersion } from "@raydium-io/raydium-sdk-v2"; +import { MintLayout, TOKEN_PROGRAM_ID } from "@solana/spl-token"; +import { PublicKey } from "@solana/web3.js"; + +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 raydium = await Raydium.load({ + owner: agent.wallet, + connection: agent.connection, + }); + + // Get mint info + const baseMintInfo = await agent.connection.getAccountInfo(baseMint); + const quoteMintInfo = await agent.connection.getAccountInfo(quoteMint); + + if (!baseMintInfo || !quoteMintInfo) { + return { + status: "error", + message: "Failed to fetch mint information" + }; + } + + // Verify token program + if ( + baseMintInfo.owner.toString() !== TOKEN_PROGRAM_ID.toBase58() || + quoteMintInfo.owner.toString() !== TOKEN_PROGRAM_ID.toBase58() + ) { + return { + status: "error", + message: "Openbook market only supports TOKEN_PROGRAM_ID mints. For token-2022, please use Raydium CPMM pool instead." + }; + } + + // Create market + const { execute } = await raydium.marketV2.create({ + baseInfo: { + mint: baseMint, + decimals: MintLayout.decode(baseMintInfo.data).decimals, + }, + quoteInfo: { + mint: quoteMint, + decimals: MintLayout.decode(quoteMintInfo.data).decimals, + }, + lotSize, + tickSize, + dexProgramId: OPEN_BOOK_PROGRAM, + txVersion: TxVersion.V0, + }); + + const { txIds } = await execute({ sequentially: true }); + + return { + status: "success", + signatures: txIds, + message: "Successfully created Openbook market" + }; + } catch (error: any) { + return { + status: "error", + message: `Failed to create Openbook market: ${error.message}` + }; + } + } +}; + +export default createOpenbookMarketAction; \ No newline at end of file diff --git a/src/actions/createOrcaSingleSidedWhirlpool.ts b/src/actions/createOrcaSingleSidedWhirlpool.ts new file mode 100644 index 0000000..11c8289 --- /dev/null +++ b/src/actions/createOrcaSingleSidedWhirlpool.ts @@ -0,0 +1,105 @@ +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"; + +// 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 = new BN(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 as keyof typeof FEE_TIERS; + + // Create the whirlpool + const signature = await agent.createOrcaSingleSidedWhirlpool( + 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; \ No newline at end of file diff --git a/src/actions/fetchPrice.ts b/src/actions/fetchPrice.ts new file mode 100644 index 0000000..40b9a1d --- /dev/null +++ b/src/actions/fetchPrice.ts @@ -0,0 +1,70 @@ +import { Action } from "../types/action"; +import { SolanaAgentKit } from "../agent"; +import { z } from "zod"; +import { PublicKey } from "@solana/web3.js"; + +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 response = await fetch(`https://api.jup.ag/price/v2?ids=${tokenId}`); + + if (!response.ok) { + return { + status: "error", + message: `Failed to fetch price: ${response.statusText}` + }; + } + + const data = await response.json(); + const price = data.data[tokenId.toBase58()]?.price; + + if (!price) { + return { + status: "error", + message: "Price data not available for the given token" + }; + } + + 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; \ No newline at end of file diff --git a/src/actions/getAllDomainsTLDs.ts b/src/actions/getAllDomainsTLDs.ts new file mode 100644 index 0000000..fbda0b8 --- /dev/null +++ b/src/actions/getAllDomainsTLDs.ts @@ -0,0 +1,49 @@ +import { Action } from "../types/action"; +import { SolanaAgentKit } from "../agent"; +import { z } from "zod"; + +const getAllDomainsTLDsAction: Action = { + name: "solana_get_all_domains_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, input: Record) => { + try { + // Get all domain TLDs + const tlds = await agent.getAllDomainsTLDs(); + + 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; \ No newline at end of file diff --git a/src/actions/getAllRegisteredAllDomains.ts b/src/actions/getAllRegisteredAllDomains.ts new file mode 100644 index 0000000..19a5542 --- /dev/null +++ b/src/actions/getAllRegisteredAllDomains.ts @@ -0,0 +1,70 @@ +import { Action } from "../types/action"; +import { SolanaAgentKit } from "../agent"; +import { z } from "zod"; + +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 agent.getAllRegisteredAllDomains(); + + // Apply pagination + const paginatedDomains = domains.slice(offset, offset + limit); + + return { + status: "success", + domains: paginatedDomains, + 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; \ No newline at end of file diff --git a/src/actions/getMainAllDomainsDomain.ts b/src/actions/getMainAllDomainsDomain.ts new file mode 100644 index 0000000..81f911d --- /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 { TldParser } from "@onsol/tldparser"; + +const getMainAllDomainsDomainAction: Action = { + name: "solana_get_main_all_domains_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 address = new PublicKey(input.address); + + // Get the main domain using TldParser + const parser = new TldParser(agent.connection); + const mainDomain = await parser.getMainDomain(address); + + if (!mainDomain) { + return { + status: "error", + message: "No main domain found for this address" + }; + } + + return { + status: "success", + domain: mainDomain.domain, + message: "Successfully retrieved main domain" + }; + } catch (error: any) { + return { + status: "error", + message: `Failed to get main domain: ${error.message}` + }; + } + } +}; + +export default getMainAllDomainsDomainAction; \ No newline at end of file diff --git a/src/actions/getOwnedAllDomains.ts b/src/actions/getOwnedAllDomains.ts new file mode 100644 index 0000000..7c41760 --- /dev/null +++ b/src/actions/getOwnedAllDomains.ts @@ -0,0 +1,60 @@ +import { Action } from "../types/action"; +import { SolanaAgentKit } from "../agent"; +import { z } from "zod"; +import { PublicKey } from "@solana/web3.js"; + +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 agent.getOwnedAllDomains(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; \ No newline at end of file diff --git a/src/actions/getOwnedDomainsForTLD.ts b/src/actions/getOwnedDomainsForTLD.ts new file mode 100644 index 0000000..36924e4 --- /dev/null +++ b/src/actions/getOwnedDomainsForTLD.ts @@ -0,0 +1,60 @@ +import { Action } from "../types/action"; +import { SolanaAgentKit } from "../agent"; +import { z } from "zod"; +import { PublicKey } from "@solana/web3.js"; + +const getOwnedDomainsForTLDAction: Action = { + name: "solana_get_owned_domains_for_tld", + 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 agent.getOwnedDomainsForTLD(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; \ No newline at end of file diff --git a/src/actions/getPrimaryDomain.ts b/src/actions/getPrimaryDomain.ts new file mode 100644 index 0000000..778fadb --- /dev/null +++ b/src/actions/getPrimaryDomain.ts @@ -0,0 +1,68 @@ +import { Action } from "../types/action"; +import { SolanaAgentKit } from "../agent"; +import { z } from "zod"; +import { getPrimaryDomain as _getPrimaryDomain } from "@bonfida/spl-name-service"; +import { PublicKey } from "@solana/web3.js"; + +const getPrimaryDomainAction: Action = { + name: "solana_get_primary_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 { reverse, stale } = await _getPrimaryDomain( + agent.connection, + account + ); + + if (stale) { + return { + status: "error", + message: `Primary domain is stale for account: ${account.toBase58()}` + }; + } + + return { + status: "success", + domain: reverse, + message: `Primary domain: ${reverse}` + }; + } catch (error: any) { + return { + status: "error", + message: `Failed to get primary domain: ${error.message}` + }; + } + } +}; + +export default getPrimaryDomainAction; \ No newline at end of file diff --git a/src/actions/getTPS.ts b/src/actions/getTPS.ts new file mode 100644 index 0000000..8baadff --- /dev/null +++ b/src/actions/getTPS.ts @@ -0,0 +1,62 @@ +import { Action } from "../types/action"; +import { SolanaAgentKit } from "../agent"; +import { z } from "zod"; + +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 perfSamples = await agent.connection.getRecentPerformanceSamples(); + + if ( + !perfSamples.length || + !perfSamples[0]?.numTransactions || + !perfSamples[0]?.samplePeriodSecs + ) { + return { + status: "error", + message: "No performance samples available" + }; + } + + const tps = Math.round( + perfSamples[0].numTransactions / perfSamples[0].samplePeriodSecs + ); + + return { + status: "success", + tps, + message: `Current network TPS: ${tps}` + }; + } catch (error: any) { + return { + status: "error", + message: `Failed to get TPS: ${error.message}` + }; + } + } +}; + +export default getTPSAction; \ No newline at end of file diff --git a/src/actions/getTokenData.ts b/src/actions/getTokenData.ts new file mode 100644 index 0000000..4b75cb9 --- /dev/null +++ b/src/actions/getTokenData.ts @@ -0,0 +1,134 @@ +import { Action } from "../types/action"; +import { SolanaAgentKit } from "../agent"; +import { z } from "zod"; +import { PublicKey } from "@solana/web3.js"; +import { JupiterTokenData } from "../types"; + +const getTokenDataAction: Action = { + name: "solana_get_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) { + const mint = new PublicKey(input.address); + const response = await fetch("https://tokens.jup.ag/tokens?tags=verified", { + method: "GET", + headers: { + "Content-Type": "application/json", + }, + }); + + const data = (await response.json()) as JupiterTokenData[]; + tokenData = data.find((token: JupiterTokenData) => token.address === mint.toBase58()); + } else if (input.ticker) { + const response = await fetch( + `https://api.dexscreener.com/latest/dex/search?q=${input.ticker}` + ); + const data = await response.json(); + + if (!data.pairs || data.pairs.length === 0) { + return { + status: "error", + message: `No token found for ticker: ${input.ticker}` + }; + } + + let solanaPairs = data.pairs + .filter((pair: any) => pair.chainId === "solana") + .sort((a: any, b: any) => (b.fdv || 0) - (a.fdv || 0)) + .filter( + (pair: any) => + pair.baseToken.symbol.toLowerCase() === input.ticker.toLowerCase() + ); + + if (solanaPairs.length === 0) { + return { + status: "error", + message: `No Solana token found for ticker: ${input.ticker}` + }; + } + + const address = solanaPairs[0].baseToken.address; + const jupResponse = await fetch("https://tokens.jup.ag/tokens?tags=verified"); + const jupData = (await jupResponse.json()) as JupiterTokenData[]; + tokenData = jupData.find((token: JupiterTokenData) => token.address === 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; \ No newline at end of file diff --git a/src/actions/index.ts b/src/actions/index.ts index bf72aca..1e15f58 100644 --- a/src/actions/index.ts +++ b/src/actions/index.ts @@ -5,6 +5,30 @@ 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 createRaydiumCLMMAction from "./createRaydiumCLMM"; +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 createRaydiumCPMMAction from "./createRaydiumCPMM"; +import sendCompressedAirdropAction from "./sendCompressedAirdrop"; +import raydiumCreateCpmmAction from "./raydiumCreateCpmm"; +import raydiumCreateAmmV4Action from "./raydiumCreateAmmV4"; +import createOrcaSingleSidedWhirlpoolAction from "./createOrcaSingleSidedWhirlpool"; +import launchPumpfunTokenAction from "./launchPumpfunToken"; export const actions = [ deployTokenAction, @@ -14,7 +38,30 @@ export const actions = [ mintNFTAction, tradeAction, requestFundsAction, - // Add more actions here as they are implemented + resolveDomainAction, + getTokenDataAction, + getTPSAction, + fetchPriceAction, + stakeWithJupAction, + registerDomainAction, + lendAssetAction, + createGibworkTaskAction, + resolveSolDomainAction, + pythFetchPriceAction, + getOwnedDomainsForTLDAction, + createRaydiumCLMMAction, + getPrimaryDomainAction, + getAllDomainsTLDsAction, + getOwnedAllDomainsAction, + createImageAction, + getMainAllDomainsDomainAction, + getAllRegisteredAllDomainsAction, + createRaydiumCPMMAction, + sendCompressedAirdropAction, + raydiumCreateCpmmAction, + raydiumCreateAmmV4Action, + createOrcaSingleSidedWhirlpoolAction, + launchPumpfunTokenAction, ]; export type { Action, ActionExample, Handler } from "../types/action"; \ No newline at end of file diff --git a/src/actions/launchPumpfunToken.ts b/src/actions/launchPumpfunToken.ts new file mode 100644 index 0000000..ea38076 --- /dev/null +++ b/src/actions/launchPumpfunToken.ts @@ -0,0 +1,203 @@ +import { Action } from "../types/action"; +import { SolanaAgentKit } from "../agent"; +import { z } from "zod"; +import { VersionedTransaction, Keypair } from "@solana/web3.js"; + +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 mintKeypair = Keypair.generate(); + + // Upload metadata + const formData = new URLSearchParams(); + formData.append("name", input.tokenName); + formData.append("symbol", input.tokenTicker); + formData.append("description", input.description); + formData.append("showName", "true"); + + if (input.twitter) { + formData.append("twitter", input.twitter); + } + if (input.telegram) { + formData.append("telegram", input.telegram); + } + if (input.website) { + formData.append("website", input.website); + } + + // Fetch and process image + const imageResponse = await fetch(input.imageUrl); + const imageBlob = await imageResponse.blob(); + const imageFile = new File([imageBlob], "token_image.png", { type: "image/png" }); + + // Create final form data + const finalFormData = new FormData(); + for (const [key, value] of formData.entries()) { + finalFormData.append(key, value); + } + finalFormData.append("file", imageFile); + + // Upload metadata to IPFS + const metadataResponse = await fetch("https://pump.fun/api/ipfs", { + method: "POST", + body: finalFormData, + }); + + if (!metadataResponse.ok) { + throw new Error(`Metadata upload failed: ${metadataResponse.statusText}`); + } + + const metadataResult = await metadataResponse.json(); + + // Create token transaction + const payload = { + publicKey: agent.wallet_address.toBase58(), + action: "create", + tokenMetadata: { + name: metadataResult.metadata.name, + symbol: metadataResult.metadata.symbol, + uri: metadataResult.metadataUri, + }, + mint: mintKeypair.publicKey.toBase58(), + denominatedInSol: "true", + amount: input.initialLiquiditySOL || 0.0001, + slippage: input.slippageBps || 5, + priorityFee: input.priorityFee || 0.00005, + pool: "pump", + }; + + const txResponse = await fetch("https://pumpportal.fun/api/trade-local", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(payload), + }); + + if (!txResponse.ok) { + const errorText = await txResponse.text(); + throw new Error(`Transaction creation failed: ${txResponse.status} - ${errorText}`); + } + + // Process and sign transaction + const transactionData = await txResponse.arrayBuffer(); + const tx = VersionedTransaction.deserialize(new Uint8Array(transactionData)); + + // Get latest blockhash + const { blockhash, lastValidBlockHeight } = await agent.connection.getLatestBlockhash(); + tx.message.recentBlockhash = blockhash; + + // Sign transaction + tx.sign([mintKeypair, agent.wallet]); + + // Send transaction + const signature = await agent.connection.sendTransaction(tx, { + skipPreflight: false, + preflightCommitment: "confirmed", + maxRetries: 5, + }); + + // Wait for confirmation + const confirmation = await agent.connection.confirmTransaction({ + signature, + blockhash, + lastValidBlockHeight, + }); + + if (confirmation.value.err) { + throw new Error(`Transaction failed: ${confirmation.value.err}`); + } + + return { + status: "success", + signature, + mint: mintKeypair.publicKey.toBase58(), + metadataUri: metadataResult.metadataUri, + message: "Successfully launched token on Pump.fun" + }; + } catch (error: any) { + return { + status: "error", + message: `Failed to launch token: ${error.message}` + }; + } + } +}; + +export default launchPumpfunTokenAction; \ No newline at end of file diff --git a/src/actions/lendAsset.ts b/src/actions/lendAsset.ts new file mode 100644 index 0000000..25e7258 --- /dev/null +++ b/src/actions/lendAsset.ts @@ -0,0 +1,101 @@ +import { Action } from "../types/action"; +import { SolanaAgentKit } from "../agent"; +import { z } from "zod"; +import { VersionedTransaction } from "@solana/web3.js"; + +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 fetch( + `https://blink.lulo.fi/actions?amount=${amount}&symbol=USDC`, + { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + account: agent.wallet.publicKey.toBase58(), + }), + } + ); + + if (!response.ok) { + return { + status: "error", + message: `Failed to get lending transaction: ${response.statusText}` + }; + } + + const data = await response.json(); + + // Deserialize the transaction + const luloTxn = VersionedTransaction.deserialize( + Buffer.from(data.transaction, "base64") + ); + + // Get a recent blockhash and set it + const { blockhash } = await agent.connection.getLatestBlockhash(); + luloTxn.message.recentBlockhash = blockhash; + + // Sign and send transaction + luloTxn.sign([agent.wallet]); + const signature = await agent.connection.sendTransaction(luloTxn, { + preflightCommitment: "confirmed", + maxRetries: 3, + }); + + // Wait for confirmation + const latestBlockhash = await agent.connection.getLatestBlockhash(); + await agent.connection.confirmTransaction({ + signature, + blockhash: latestBlockhash.blockhash, + lastValidBlockHeight: latestBlockhash.lastValidBlockHeight, + }); + + return { + status: "success", + signature, + message: `Successfully lent ${amount} USDC` + }; + } catch (error: any) { + return { + status: "error", + message: `Lending failed: ${error.message}` + }; + } + } +}; + +export default lendAssetAction; \ No newline at end of file diff --git a/src/actions/pythFetchPrice.ts b/src/actions/pythFetchPrice.ts new file mode 100644 index 0000000..acafeb0 --- /dev/null +++ b/src/actions/pythFetchPrice.ts @@ -0,0 +1,78 @@ +import { Action } from "../types/action"; +import { SolanaAgentKit } from "../agent"; +import { z } from "zod"; +import { PriceServiceConnection } from "@pythnetwork/price-service-client"; +import BN from "bn.js"; + +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.priceFeedId as string; + + // Connect to Hermes service + const stableHermesServiceUrl = "https://hermes.pyth.network"; + const connection = new PriceServiceConnection(stableHermesServiceUrl); + const feeds = [priceFeedId]; + + const currentPrice = await connection.getLatestPriceFeeds(feeds); + + if (!currentPrice || currentPrice.length === 0) { + return { + status: "error", + message: "Price data not available for the given feed ID" + }; + } + + // Get price and exponent from price feed + const price = new BN(currentPrice[0].getPriceUnchecked().price); + const exponent = new BN(currentPrice[0].getPriceUnchecked().expo); + + // Convert to scaled price + const scaledPrice = price.div(new BN(10).pow(exponent)); + const priceStr = scaledPrice.toString(); + + 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; \ No newline at end of file diff --git a/src/actions/raydiumCreateAmmV4.ts b/src/actions/raydiumCreateAmmV4.ts new file mode 100644 index 0000000..6c4c357 --- /dev/null +++ b/src/actions/raydiumCreateAmmV4.ts @@ -0,0 +1,154 @@ +import { Action } from "../types/action"; +import { SolanaAgentKit } from "../agent"; +import { z } from "zod"; +import { + AMM_V4, + FEE_DESTINATION_ID, + MARKET_STATE_LAYOUT_V3, + OPEN_BOOK_PROGRAM, + Raydium, + TxVersion, +} from "@raydium-io/raydium-sdk-v2"; +import { MintLayout, TOKEN_PROGRAM_ID } from "@solana/spl-token"; +import { PublicKey } from "@solana/web3.js"; +import BN from "bn.js"; + +const raydiumCreateAmmV4Action: Action = { + name: "solana_raydium_create_amm_v4", + 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 raydium = await Raydium.load({ + owner: agent.wallet, + connection: agent.connection, + }); + + const marketBufferInfo = await agent.connection.getAccountInfo( + new PublicKey(marketId), + ); + const { baseMint, quoteMint } = MARKET_STATE_LAYOUT_V3.decode( + marketBufferInfo!.data, + ); + + const baseMintInfo = await agent.connection.getAccountInfo(baseMint); + const quoteMintInfo = await agent.connection.getAccountInfo(quoteMint); + + if ( + baseMintInfo?.owner.toString() !== TOKEN_PROGRAM_ID.toBase58() || + quoteMintInfo?.owner.toString() !== TOKEN_PROGRAM_ID.toBase58() + ) { + throw new Error( + "amm pools with openbook market only support TOKEN_PROGRAM_ID mints, if you want to create pool with token-2022, please create cpmm pool instead", + ); + } + + if ( + baseAmount + .mul(quoteAmount) + .lte( + new BN(1) + .mul(new BN(10 ** MintLayout.decode(baseMintInfo.data).decimals)) + .pow(new BN(2)), + ) + ) { + throw new Error( + "initial liquidity too low, try adding more baseAmount/quoteAmount", + ); + } + + const { execute } = await raydium.liquidity.createPoolV4({ + programId: AMM_V4, + marketInfo: { + marketId, + programId: OPEN_BOOK_PROGRAM, + }, + baseMintInfo: { + mint: baseMint, + decimals: MintLayout.decode(baseMintInfo.data).decimals, + }, + quoteMintInfo: { + mint: quoteMint, + decimals: MintLayout.decode(quoteMintInfo.data).decimals, + }, + baseAmount, + quoteAmount, + startTime, + ownerInfo: { + useSOLBalance: true, + }, + associatedOnly: false, + txVersion: TxVersion.V0, + feeDestinationId: FEE_DESTINATION_ID, + }); + + const { txId } = await execute({ sendAndConfirm: true }); + + 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; \ No newline at end of file diff --git a/src/actions/raydiumCreateCpmm.ts b/src/actions/raydiumCreateCpmm.ts new file mode 100644 index 0000000..faea973 --- /dev/null +++ b/src/actions/raydiumCreateCpmm.ts @@ -0,0 +1,142 @@ +import { Action } from "../types/action"; +import { SolanaAgentKit } from "../agent"; +import { z } from "zod"; +import { + CREATE_CPMM_POOL_FEE_ACC, + CREATE_CPMM_POOL_PROGRAM, + Raydium, + TxVersion, +} from "@raydium-io/raydium-sdk-v2"; +import { MintLayout } from "@solana/spl-token"; +import { PublicKey } from "@solana/web3.js"; +import BN from "bn.js"; + +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 raydium = await Raydium.load({ + owner: agent.wallet, + connection: agent.connection, + }); + + const [mintInfoA, mintInfoB] = await agent.connection.getMultipleAccountsInfo( + [mintA, mintB], + ); + if (mintInfoA === null || mintInfoB === null) { + throw Error("fetch mint info error"); + } + + const mintDecodeInfoA = MintLayout.decode(mintInfoA.data); + const mintDecodeInfoB = MintLayout.decode(mintInfoB.data); + + const mintFormatInfoA = { + chainId: 101, + address: mintA.toString(), + programId: mintInfoA.owner.toString(), + logoURI: "", + symbol: "", + name: "", + decimals: mintDecodeInfoA.decimals, + tags: [], + extensions: {}, + }; + const mintFormatInfoB = { + chainId: 101, + address: mintB.toString(), + programId: mintInfoB.owner.toString(), + logoURI: "", + symbol: "", + name: "", + decimals: mintDecodeInfoB.decimals, + tags: [], + extensions: {}, + }; + + const { execute } = await raydium.cpmm.createPool({ + programId: CREATE_CPMM_POOL_PROGRAM, + poolFeeAccount: CREATE_CPMM_POOL_FEE_ACC, + mintA: mintFormatInfoA, + mintB: mintFormatInfoB, + mintAAmount, + mintBAmount, + startTime, + //@ts-expect-error sdk bug + feeConfig: { id: configId.toString() }, + associatedOnly: false, + ownerInfo: { + useSOLBalance: true, + }, + txVersion: TxVersion.V0, + }); + + const { txId } = await execute({ sendAndConfirm: true }); + + 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; \ No newline at end of file diff --git a/src/actions/registerDomain.ts b/src/actions/registerDomain.ts new file mode 100644 index 0000000..02b92d3 --- /dev/null +++ b/src/actions/registerDomain.ts @@ -0,0 +1,110 @@ +import { Action } from "../types/action"; +import { SolanaAgentKit } from "../agent"; +import { z } from "zod"; +import { Transaction } from "@solana/web3.js"; +import { registerDomainNameV2 } from "@bonfida/spl-name-service"; +import { getAssociatedTokenAddressSync } from "@solana/spl-token"; +import { TOKENS } from "../constants"; + +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; + + // Validate space size + if (spaceKB > 10) { + return { + status: "error", + message: "Maximum domain size is 10KB" + }; + } + + // Convert KB to bytes + const space = spaceKB * 1_000; + + const buyerTokenAccount = await getAssociatedTokenAddressSync( + agent.wallet_address, + TOKENS.USDC + ); + + // Create registration instruction + const instruction = await registerDomainNameV2( + agent.connection, + name, + space, + agent.wallet_address, + buyerTokenAccount + ); + + // Create and sign transaction + const transaction = new Transaction().add(...instruction); + transaction.recentBlockhash = ( + await agent.connection.getLatestBlockhash() + ).blockhash; + transaction.feePayer = agent.wallet_address; + + // Sign and send transaction + const signature = await agent.connection.sendTransaction(transaction, [ + agent.wallet + ]); + + // Wait for confirmation + const latestBlockhash = await agent.connection.getLatestBlockhash(); + await agent.connection.confirmTransaction({ + signature, + blockhash: latestBlockhash.blockhash, + lastValidBlockHeight: latestBlockhash.lastValidBlockHeight, + }); + + return { + status: "success", + signature, + message: `Successfully registered ${name}.sol` + }; + } catch (error: any) { + return { + status: "error", + message: `Domain registration failed: ${error.message}` + }; + } + } +}; + +export default registerDomainAction; \ No newline at end of file diff --git a/src/actions/resolveDomain.ts b/src/actions/resolveDomain.ts new file mode 100644 index 0000000..049c3df --- /dev/null +++ b/src/actions/resolveDomain.ts @@ -0,0 +1,61 @@ +import { Action } from "../types/action"; +import { SolanaAgentKit } from "../agent"; +import { z } from "zod"; +import { TldParser } from "@onsol/tldparser"; + +const resolveDomainAction: Action = { + name: "solana_resolve_domain", + 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 new TldParser(agent.connection).getOwnerFromDomainTld( + domain + ); + + if (!tld) { + return { + status: "error", + message: "Domain not found" + }; + } + + return { + status: "success", + owner: tld.toBase58(), + message: `Successfully resolved domain ${domain}` + }; + } catch (error: any) { + return { + status: "error", + message: `Failed to resolve domain: ${error.message}` + }; + } + } +}; + +export default resolveDomainAction; \ No newline at end of file diff --git a/src/actions/resolveSolDomain.ts b/src/actions/resolveSolDomain.ts new file mode 100644 index 0000000..481acef --- /dev/null +++ b/src/actions/resolveSolDomain.ts @@ -0,0 +1,69 @@ +import { Action } from "../types/action"; +import { SolanaAgentKit } from "../agent"; +import { z } from "zod"; +import { resolve } from "@bonfida/spl-name-service"; + +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; + + if (!domain || typeof domain !== "string") { + return { + status: "error", + message: "Invalid domain. Expected a non-empty string." + }; + } + + // Remove .sol suffix if present for consistent handling + const cleanDomain = domain.toLowerCase().endsWith(".sol") + ? domain.slice(0, -4) + : domain; + + const ownerAddress = await resolve(agent.connection, cleanDomain); + + return { + status: "success", + owner: ownerAddress.toBase58(), + message: `Successfully resolved ${cleanDomain}.sol` + }; + } catch (error: any) { + return { + status: "error", + message: `Failed to resolve domain: ${error.message}` + }; + } + } +}; + +export default resolveSolDomainAction; \ No newline at end of file diff --git a/src/actions/stakeWithJup.ts b/src/actions/stakeWithJup.ts new file mode 100644 index 0000000..271f902 --- /dev/null +++ b/src/actions/stakeWithJup.ts @@ -0,0 +1,101 @@ +import { Action } from "../types/action"; +import { SolanaAgentKit } from "../agent"; +import { z } from "zod"; +import { VersionedTransaction } from "@solana/web3.js"; + +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; + + // Get staking transaction from Jupiter + const res = await fetch( + `https://worker.jup.ag/blinks/swap/So11111111111111111111111111111111111111112/jupSoLaHXQiZZTSfEWMTRRgpnyFm8f6sZdosWBjx93v/${amount}`, + { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + account: agent.wallet.publicKey.toBase58(), + }), + } + ); + + if (!res.ok) { + return { + status: "error", + message: `Failed to get staking transaction: ${res.statusText}` + }; + } + + const data = await res.json(); + + // Deserialize and prepare transaction + const txn = VersionedTransaction.deserialize( + Buffer.from(data.transaction, "base64") + ); + + const { blockhash } = await agent.connection.getLatestBlockhash(); + txn.message.recentBlockhash = blockhash; + + // Sign and send transaction + txn.sign([agent.wallet]); + const signature = await agent.connection.sendTransaction(txn, { + preflightCommitment: "confirmed", + maxRetries: 3, + }); + + // Confirm transaction + const latestBlockhash = await agent.connection.getLatestBlockhash(); + await agent.connection.confirmTransaction({ + signature, + blockhash: latestBlockhash.blockhash, + lastValidBlockHeight: latestBlockhash.lastValidBlockHeight, + }); + + return { + status: "success", + signature, + message: `Successfully staked ${amount} SOL for jupSOL` + }; + } catch (error: any) { + return { + status: "error", + message: `jupSOL staking failed: ${error.message}` + }; + } + } +}; + +export default stakeWithJupAction; \ No newline at end of file From 378fea201d73f4caf630d3b57b6a39f87268ffa9 Mon Sep 17 00:00:00 2001 From: Fahri Bilici <28020526+FahriBilici@users.noreply.github.com> Date: Sat, 28 Dec 2024 16:24:57 +0100 Subject: [PATCH 04/11] feat: Refactor actions to use tool functions for improved code clarity and maintainability --- src/actions/balance.ts | 4 +- src/actions/createGibworkTask.ts | 65 ++--------- src/actions/createImage.ts | 22 +--- src/actions/createOpenbookMarket.ts | 51 ++------ src/actions/createOrcaSingleSidedWhirlpool.ts | 4 +- src/actions/deployCollection.ts | 3 +- src/actions/deployToken.ts | 33 ++++-- src/actions/fetchPrice.ts | 20 +--- src/actions/getAllDomainsTLDs.ts | 3 +- src/actions/getAllRegisteredAllDomains.ts | 7 +- src/actions/getMainAllDomainsDomain.ts | 9 +- src/actions/getOwnedAllDomains.ts | 3 +- src/actions/getOwnedDomainsForTLD.ts | 3 +- src/actions/getPrimaryDomain.ts | 15 +-- src/actions/getTPS.ts | 23 +--- src/actions/getTokenData.ts | 48 +------- src/actions/index.ts | 6 - src/actions/launchPumpfunToken.ts | 110 +----------------- src/actions/lendAsset.ts | 49 +------- src/actions/mintNFT.ts | 8 +- src/actions/pythFetchPrice.ts | 23 +--- src/actions/raydiumCreateAmmV4.ts | 66 +---------- src/actions/raydiumCreateCpmm.ts | 58 +-------- src/actions/registerDomain.ts | 46 +------- src/actions/requestFunds.ts | 3 +- src/actions/resolveDomain.ts | 15 +-- src/actions/resolveSolDomain.ts | 19 +-- src/actions/stakeWithJup.ts | 50 +------- src/actions/trade.ts | 3 +- src/actions/transfer.ts | 3 +- 30 files changed, 118 insertions(+), 654 deletions(-) diff --git a/src/actions/balance.ts b/src/actions/balance.ts index 2a4d34d..36d9304 100644 --- a/src/actions/balance.ts +++ b/src/actions/balance.ts @@ -2,6 +2,7 @@ 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", @@ -45,8 +46,7 @@ const balanceAction: Action = { tokenAddress: z.string().optional() }), handler: async (agent: SolanaAgentKit, input: Record) => { - const tokenAddress = input.tokenAddress ? new PublicKey(input.tokenAddress) : undefined; - const balance = await agent.getBalance(tokenAddress); + const balance = await get_balance(agent, input.tokenAddress && new PublicKey(input.tokenAddress)); return { status: "success", diff --git a/src/actions/createGibworkTask.ts b/src/actions/createGibworkTask.ts index 8ae28ce..419b7b5 100644 --- a/src/actions/createGibworkTask.ts +++ b/src/actions/createGibworkTask.ts @@ -2,6 +2,7 @@ import { Action } from "../types/action"; import { SolanaAgentKit } from "../agent"; import { z } from "zod"; import { PublicKey, VersionedTransaction } from "@solana/web3.js"; +import { create_gibwork_task } from "../tools"; const createGibworkTaskAction: Action = { name: "solana_create_gibwork_task", @@ -62,65 +63,21 @@ const createGibworkTaskAction: Action = { const tokenMintAddress = new PublicKey(input.tokenMintAddress); const payer = input.payer ? new PublicKey(input.payer) : undefined; - const apiResponse = await fetch( - "https://api2.gib.work/tasks/public/transaction", - { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - title: input.title, - content: input.content, - requirements: input.requirements, - tags: input.tags, - payer: payer?.toBase58() || agent.wallet.publicKey.toBase58(), - token: { - mintAddress: tokenMintAddress.toBase58(), - amount: input.tokenAmount, - }, - }), - } + 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 ); - if (!apiResponse.ok) { - return { - status: "error", - message: `Failed to create task: ${apiResponse.statusText}` - }; - } - - const responseData = await apiResponse.json(); - if (!responseData.taskId || !responseData.serializedTransaction) { - return { - status: "error", - message: responseData.message || "Invalid response from Gibwork API" - }; - } - - const serializedTransaction = Buffer.from( - responseData.serializedTransaction, - "base64" - ); - const tx = VersionedTransaction.deserialize(serializedTransaction); - - tx.sign([agent.wallet]); - const signature = await agent.connection.sendTransaction(tx, { - preflightCommitment: "confirmed", - maxRetries: 3, - }); - - const latestBlockhash = await agent.connection.getLatestBlockhash(); - await agent.connection.confirmTransaction({ - signature, - blockhash: latestBlockhash.blockhash, - lastValidBlockHeight: latestBlockhash.lastValidBlockHeight, - }); - return { status: "success", taskId: responseData.taskId, - signature, + signature: responseData.signature, message: `Successfully created task: ${input.title}` }; } catch (error: any) { diff --git a/src/actions/createImage.ts b/src/actions/createImage.ts index 4d16b9a..722a683 100644 --- a/src/actions/createImage.ts +++ b/src/actions/createImage.ts @@ -2,6 +2,8 @@ import { Action } from "../types/action"; import { SolanaAgentKit } from "../agent"; import { z } from "zod"; import OpenAI from "openai"; +import { create } from "domain"; +import { create_image } from "../tools/create_image"; const createImageAction: Action = { name: "solana_create_image", @@ -60,31 +62,17 @@ const createImageAction: Action = { }; } - const { prompt, model, size, quality, style } = input; + const { prompt, model, size } = input; const openai = new OpenAI({ apiKey: agent.openai_api_key }); - const response = await openai.images.generate({ - prompt, - model, - n: 1, - size, - quality, - style - }); - - if (!response.data || response.data.length === 0) { - return { - status: "error", - message: "No image was generated" - }; - } + const response = await create_image(agent, prompt, model, size); return { status: "success", - imageUrl: response.data[0].url, + imageUrl: response.images[0].url, message: "Successfully generated image" }; } catch (error: any) { diff --git a/src/actions/createOpenbookMarket.ts b/src/actions/createOpenbookMarket.ts index 991ab97..0e923c0 100644 --- a/src/actions/createOpenbookMarket.ts +++ b/src/actions/createOpenbookMarket.ts @@ -4,6 +4,7 @@ import { z } from "zod"; import { OPEN_BOOK_PROGRAM, Raydium, TxVersion } from "@raydium-io/raydium-sdk-v2"; import { MintLayout, TOKEN_PROGRAM_ID } from "@solana/spl-token"; import { PublicKey } from "@solana/web3.js"; +import { openbookCreateMarket } from "../tools"; const createOpenbookMarketAction: Action = { name: "solana_create_openbook_market", @@ -57,54 +58,18 @@ const createOpenbookMarketAction: Action = { const lotSize = input.lotSize || 1; const tickSize = input.tickSize || 0.01; - const raydium = await Raydium.load({ - owner: agent.wallet, - connection: agent.connection, - }); - // Get mint info - const baseMintInfo = await agent.connection.getAccountInfo(baseMint); - const quoteMintInfo = await agent.connection.getAccountInfo(quoteMint); - - if (!baseMintInfo || !quoteMintInfo) { - return { - status: "error", - message: "Failed to fetch mint information" - }; - } - - // Verify token program - if ( - baseMintInfo.owner.toString() !== TOKEN_PROGRAM_ID.toBase58() || - quoteMintInfo.owner.toString() !== TOKEN_PROGRAM_ID.toBase58() - ) { - return { - status: "error", - message: "Openbook market only supports TOKEN_PROGRAM_ID mints. For token-2022, please use Raydium CPMM pool instead." - }; - } - - // Create market - const { execute } = await raydium.marketV2.create({ - baseInfo: { - mint: baseMint, - decimals: MintLayout.decode(baseMintInfo.data).decimals, - }, - quoteInfo: { - mint: quoteMint, - decimals: MintLayout.decode(quoteMintInfo.data).decimals, - }, + const signatures = await openbookCreateMarket( + agent, + baseMint, + quoteMint, lotSize, - tickSize, - dexProgramId: OPEN_BOOK_PROGRAM, - txVersion: TxVersion.V0, - }); - - const { txIds } = await execute({ sequentially: true }); + tickSize + ); return { status: "success", - signatures: txIds, + signatures, message: "Successfully created Openbook market" }; } catch (error: any) { diff --git a/src/actions/createOrcaSingleSidedWhirlpool.ts b/src/actions/createOrcaSingleSidedWhirlpool.ts index 11c8289..f23389d 100644 --- a/src/actions/createOrcaSingleSidedWhirlpool.ts +++ b/src/actions/createOrcaSingleSidedWhirlpool.ts @@ -4,6 +4,7 @@ import { z } from "zod"; import { PublicKey } from "@solana/web3.js"; import { BN } from "@coral-xyz/anchor"; import { Decimal } from "decimal.js"; +import { createOrcaSingleSidedWhirlpool } from "../tools"; // Fee tiers mapping from the original tool const FEE_TIERS = { @@ -79,7 +80,8 @@ const createOrcaSingleSidedWhirlpoolAction: Action = { const feeTier = input.feeTier as keyof typeof FEE_TIERS; // Create the whirlpool - const signature = await agent.createOrcaSingleSidedWhirlpool( + const signature = await createOrcaSingleSidedWhirlpool( + agent, depositTokenAmount, depositTokenMint, otherTokenMint, diff --git a/src/actions/deployCollection.ts b/src/actions/deployCollection.ts index 0127b5c..77efc10 100644 --- a/src/actions/deployCollection.ts +++ b/src/actions/deployCollection.ts @@ -2,6 +2,7 @@ import { PublicKey } from "@solana/web3.js"; import { Action } from "../types/action"; import { SolanaAgentKit } from "../agent"; import { z } from "zod"; +import { deploy_collection } from "../tools"; interface CollectionOptions { name: string; @@ -64,7 +65,7 @@ const deployCollectionAction: Action = { royaltyBasisPoints: input.royaltyBasisPoints }; - const result = await agent.deployCollection(options); + const result = await deploy_collection(agent, options); return { status: "success", diff --git a/src/actions/deployToken.ts b/src/actions/deployToken.ts index 046c120..2a1793d 100644 --- a/src/actions/deployToken.ts +++ b/src/actions/deployToken.ts @@ -2,6 +2,7 @@ import { PublicKey } from "@solana/web3.js"; import { Action, ActionExample } from "../types/action"; import { SolanaAgentKit } from "../agent"; import { z } from "zod"; +import { deploy_token } from "../tools"; const deployTokenAction: Action = { name: "deploy_token", @@ -55,19 +56,27 @@ const deployTokenAction: Action = { initialSupply: z.number().optional() }), handler: async (agent: SolanaAgentKit, input: Record) => { - const result = await agent.deployToken( - input.name, - input.uri, - input.symbol, - input.decimals, - input.initialSupply - ); + 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" - }; + return { + mint: result.mint.toString(), + status: "success", + message: "Token deployed successfully" + }; + } catch (error: any) { + return { + status: "error", + message: `Token deployment failed: ${error.message}` + }; + } } } diff --git a/src/actions/fetchPrice.ts b/src/actions/fetchPrice.ts index 40b9a1d..9512e14 100644 --- a/src/actions/fetchPrice.ts +++ b/src/actions/fetchPrice.ts @@ -2,6 +2,7 @@ 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", @@ -34,24 +35,7 @@ const fetchPriceAction: Action = { handler: async (agent: SolanaAgentKit, input: Record) => { try { const tokenId = new PublicKey(input.tokenAddress); - const response = await fetch(`https://api.jup.ag/price/v2?ids=${tokenId}`); - - if (!response.ok) { - return { - status: "error", - message: `Failed to fetch price: ${response.statusText}` - }; - } - - const data = await response.json(); - const price = data.data[tokenId.toBase58()]?.price; - - if (!price) { - return { - status: "error", - message: "Price data not available for the given token" - }; - } + const price = await fetchPrice(tokenId); return { status: "success", diff --git a/src/actions/getAllDomainsTLDs.ts b/src/actions/getAllDomainsTLDs.ts index fbda0b8..60c05d2 100644 --- a/src/actions/getAllDomainsTLDs.ts +++ b/src/actions/getAllDomainsTLDs.ts @@ -1,6 +1,7 @@ import { Action } from "../types/action"; import { SolanaAgentKit } from "../agent"; import { z } from "zod"; +import { getAllDomainsTLDs } from "../tools"; const getAllDomainsTLDsAction: Action = { name: "solana_get_all_domains_tlds", @@ -30,7 +31,7 @@ const getAllDomainsTLDsAction: Action = { handler: async (agent: SolanaAgentKit, input: Record) => { try { // Get all domain TLDs - const tlds = await agent.getAllDomainsTLDs(); + const tlds = await getAllDomainsTLDs(agent); return { status: "success", diff --git a/src/actions/getAllRegisteredAllDomains.ts b/src/actions/getAllRegisteredAllDomains.ts index 19a5542..96fe879 100644 --- a/src/actions/getAllRegisteredAllDomains.ts +++ b/src/actions/getAllRegisteredAllDomains.ts @@ -1,6 +1,7 @@ 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", @@ -47,14 +48,12 @@ const getAllRegisteredAllDomainsAction: Action = { const offset = input.offset || 0; // Get all registered domains - const domains = await agent.getAllRegisteredAllDomains(); + const domains = await getAllRegisteredAllDomains(agent); - // Apply pagination - const paginatedDomains = domains.slice(offset, offset + limit); return { status: "success", - domains: paginatedDomains, + domains: domains.slice(offset, offset + limit), total: domains.length, message: "Successfully retrieved registered domains" }; diff --git a/src/actions/getMainAllDomainsDomain.ts b/src/actions/getMainAllDomainsDomain.ts index 81f911d..4f301f9 100644 --- a/src/actions/getMainAllDomainsDomain.ts +++ b/src/actions/getMainAllDomainsDomain.ts @@ -3,6 +3,7 @@ import { SolanaAgentKit } from "../agent"; import { z } from "zod"; import { PublicKey } from "@solana/web3.js"; import { TldParser } from "@onsol/tldparser"; +import { getMainAllDomainsDomain } from "../tools"; const getMainAllDomainsDomainAction: Action = { name: "solana_get_main_all_domains_domain", @@ -37,11 +38,7 @@ const getMainAllDomainsDomainAction: Action = { }), handler: async (agent: SolanaAgentKit, input: Record) => { try { - const address = new PublicKey(input.address); - - // Get the main domain using TldParser - const parser = new TldParser(agent.connection); - const mainDomain = await parser.getMainDomain(address); + const mainDomain = await getMainAllDomainsDomain(agent, new PublicKey(input.address)); if (!mainDomain) { return { @@ -52,7 +49,7 @@ const getMainAllDomainsDomainAction: Action = { return { status: "success", - domain: mainDomain.domain, + domain: mainDomain, message: "Successfully retrieved main domain" }; } catch (error: any) { diff --git a/src/actions/getOwnedAllDomains.ts b/src/actions/getOwnedAllDomains.ts index 7c41760..9cb279a 100644 --- a/src/actions/getOwnedAllDomains.ts +++ b/src/actions/getOwnedAllDomains.ts @@ -2,6 +2,7 @@ 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", @@ -40,7 +41,7 @@ const getOwnedAllDomainsAction: Action = { const address = new PublicKey(input.address); // Get owned domains - const domains = await agent.getOwnedAllDomains(address); + const domains = await getOwnedAllDomains(agent, address); return { status: "success", diff --git a/src/actions/getOwnedDomainsForTLD.ts b/src/actions/getOwnedDomainsForTLD.ts index 36924e4..3b2a06e 100644 --- a/src/actions/getOwnedDomainsForTLD.ts +++ b/src/actions/getOwnedDomainsForTLD.ts @@ -2,6 +2,7 @@ import { Action } from "../types/action"; import { SolanaAgentKit } from "../agent"; import { z } from "zod"; import { PublicKey } from "@solana/web3.js"; +import { getOwnedDomainsForTLD } from "../tools"; const getOwnedDomainsForTLDAction: Action = { name: "solana_get_owned_domains_for_tld", @@ -40,7 +41,7 @@ const getOwnedDomainsForTLDAction: Action = { const tld = input.tld.toLowerCase(); // Get owned domains for TLD - const domains = await agent.getOwnedDomainsForTLD(tld); + const domains = await getOwnedDomainsForTLD(agent, tld); return { status: "success", diff --git a/src/actions/getPrimaryDomain.ts b/src/actions/getPrimaryDomain.ts index 778fadb..f19b091 100644 --- a/src/actions/getPrimaryDomain.ts +++ b/src/actions/getPrimaryDomain.ts @@ -3,6 +3,7 @@ import { SolanaAgentKit } from "../agent"; import { z } from "zod"; import { getPrimaryDomain as _getPrimaryDomain } from "@bonfida/spl-name-service"; import { PublicKey } from "@solana/web3.js"; +import { getPrimaryDomain } from "../tools"; const getPrimaryDomainAction: Action = { name: "solana_get_primary_domain", @@ -39,22 +40,16 @@ const getPrimaryDomainAction: Action = { try { const account = new PublicKey(input.account); - const { reverse, stale } = await _getPrimaryDomain( - agent.connection, + const response = await getPrimaryDomain( + agent, account ); - if (stale) { - return { - status: "error", - message: `Primary domain is stale for account: ${account.toBase58()}` - }; - } return { status: "success", - domain: reverse, - message: `Primary domain: ${reverse}` + domain: response, + message: `Primary domain: ${response}` }; } catch (error: any) { return { diff --git a/src/actions/getTPS.ts b/src/actions/getTPS.ts index 8baadff..546da65 100644 --- a/src/actions/getTPS.ts +++ b/src/actions/getTPS.ts @@ -1,6 +1,7 @@ import { Action } from "../types/action"; import { SolanaAgentKit } from "../agent"; import { z } from "zod"; +import { getTPS } from "../tools"; const getTPSAction: Action = { name: "solana_get_tps", @@ -28,27 +29,11 @@ const getTPSAction: Action = { schema: z.object({}), // No input parameters required handler: async (agent: SolanaAgentKit, _input: Record) => { try { - const perfSamples = await agent.connection.getRecentPerformanceSamples(); - - if ( - !perfSamples.length || - !perfSamples[0]?.numTransactions || - !perfSamples[0]?.samplePeriodSecs - ) { - return { - status: "error", - message: "No performance samples available" - }; - } - - const tps = Math.round( - perfSamples[0].numTransactions / perfSamples[0].samplePeriodSecs - ); - + const response = await getTPS(agent); return { status: "success", - tps, - message: `Current network TPS: ${tps}` + response, + message: `Current network TPS: ${response}` }; } catch (error: any) { return { diff --git a/src/actions/getTokenData.ts b/src/actions/getTokenData.ts index 4b75cb9..8ac9721 100644 --- a/src/actions/getTokenData.ts +++ b/src/actions/getTokenData.ts @@ -3,6 +3,7 @@ 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_get_token_data", @@ -59,59 +60,20 @@ const getTokenDataAction: Action = { handler: async (agent: SolanaAgentKit, input: Record) => { try { let tokenData: JupiterTokenData | undefined; - if (input.address) { - const mint = new PublicKey(input.address); - const response = await fetch("https://tokens.jup.ag/tokens?tags=verified", { - method: "GET", - headers: { - "Content-Type": "application/json", - }, - }); - - const data = (await response.json()) as JupiterTokenData[]; - tokenData = data.find((token: JupiterTokenData) => token.address === mint.toBase58()); + tokenData = await getTokenDataByAddress(new PublicKey(input.address)); } else if (input.ticker) { - const response = await fetch( - `https://api.dexscreener.com/latest/dex/search?q=${input.ticker}` - ); - const data = await response.json(); - - if (!data.pairs || data.pairs.length === 0) { - return { - status: "error", - message: `No token found for ticker: ${input.ticker}` - }; + const address = await getTokenAddressFromTicker(input.ticker); + if (address) { + tokenData = await getTokenDataByAddress(new PublicKey(address)); } - - let solanaPairs = data.pairs - .filter((pair: any) => pair.chainId === "solana") - .sort((a: any, b: any) => (b.fdv || 0) - (a.fdv || 0)) - .filter( - (pair: any) => - pair.baseToken.symbol.toLowerCase() === input.ticker.toLowerCase() - ); - - if (solanaPairs.length === 0) { - return { - status: "error", - message: `No Solana token found for ticker: ${input.ticker}` - }; - } - - const address = solanaPairs[0].baseToken.address; - const jupResponse = await fetch("https://tokens.jup.ag/tokens?tags=verified"); - const jupData = (await jupResponse.json()) as JupiterTokenData[]; - tokenData = jupData.find((token: JupiterTokenData) => token.address === address); } - if (!tokenData) { return { status: "error", message: "Token not found or not verified" }; } - return { status: "success", token: { diff --git a/src/actions/index.ts b/src/actions/index.ts index 1e15f58..d3b1679 100644 --- a/src/actions/index.ts +++ b/src/actions/index.ts @@ -16,15 +16,12 @@ import createGibworkTaskAction from "./createGibworkTask"; import resolveSolDomainAction from "./resolveSolDomain"; import pythFetchPriceAction from "./pythFetchPrice"; import getOwnedDomainsForTLDAction from "./getOwnedDomainsForTLD"; -import createRaydiumCLMMAction from "./createRaydiumCLMM"; 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 createRaydiumCPMMAction from "./createRaydiumCPMM"; -import sendCompressedAirdropAction from "./sendCompressedAirdrop"; import raydiumCreateCpmmAction from "./raydiumCreateCpmm"; import raydiumCreateAmmV4Action from "./raydiumCreateAmmV4"; import createOrcaSingleSidedWhirlpoolAction from "./createOrcaSingleSidedWhirlpool"; @@ -49,15 +46,12 @@ export const actions = [ resolveSolDomainAction, pythFetchPriceAction, getOwnedDomainsForTLDAction, - createRaydiumCLMMAction, getPrimaryDomainAction, getAllDomainsTLDsAction, getOwnedAllDomainsAction, createImageAction, getMainAllDomainsDomainAction, getAllRegisteredAllDomainsAction, - createRaydiumCPMMAction, - sendCompressedAirdropAction, raydiumCreateCpmmAction, raydiumCreateAmmV4Action, createOrcaSingleSidedWhirlpoolAction, diff --git a/src/actions/launchPumpfunToken.ts b/src/actions/launchPumpfunToken.ts index ea38076..00f0add 100644 --- a/src/actions/launchPumpfunToken.ts +++ b/src/actions/launchPumpfunToken.ts @@ -2,6 +2,7 @@ import { Action } from "../types/action"; import { SolanaAgentKit } from "../agent"; import { z } from "zod"; import { VersionedTransaction, Keypair } from "@solana/web3.js"; +import { launchPumpFunToken } from "../tools"; const launchPumpfunTokenAction: Action = { name: "solana_launch_pumpfun_token", @@ -82,113 +83,14 @@ const launchPumpfunTokenAction: Action = { }), handler: async (agent: SolanaAgentKit, input: Record) => { try { - const mintKeypair = Keypair.generate(); - - // Upload metadata - const formData = new URLSearchParams(); - formData.append("name", input.tokenName); - formData.append("symbol", input.tokenTicker); - formData.append("description", input.description); - formData.append("showName", "true"); - - if (input.twitter) { - formData.append("twitter", input.twitter); - } - if (input.telegram) { - formData.append("telegram", input.telegram); - } - if (input.website) { - formData.append("website", input.website); - } - - // Fetch and process image - const imageResponse = await fetch(input.imageUrl); - const imageBlob = await imageResponse.blob(); - const imageFile = new File([imageBlob], "token_image.png", { type: "image/png" }); - - // Create final form data - const finalFormData = new FormData(); - for (const [key, value] of formData.entries()) { - finalFormData.append(key, value); - } - finalFormData.append("file", imageFile); - - // Upload metadata to IPFS - const metadataResponse = await fetch("https://pump.fun/api/ipfs", { - method: "POST", - body: finalFormData, - }); - - if (!metadataResponse.ok) { - throw new Error(`Metadata upload failed: ${metadataResponse.statusText}`); - } - - const metadataResult = await metadataResponse.json(); - - // Create token transaction - const payload = { - publicKey: agent.wallet_address.toBase58(), - action: "create", - tokenMetadata: { - name: metadataResult.metadata.name, - symbol: metadataResult.metadata.symbol, - uri: metadataResult.metadataUri, - }, - mint: mintKeypair.publicKey.toBase58(), - denominatedInSol: "true", - amount: input.initialLiquiditySOL || 0.0001, - slippage: input.slippageBps || 5, - priorityFee: input.priorityFee || 0.00005, - pool: "pump", - }; - - const txResponse = await fetch("https://pumpportal.fun/api/trade-local", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify(payload), - }); - - if (!txResponse.ok) { - const errorText = await txResponse.text(); - throw new Error(`Transaction creation failed: ${txResponse.status} - ${errorText}`); - } - - // Process and sign transaction - const transactionData = await txResponse.arrayBuffer(); - const tx = VersionedTransaction.deserialize(new Uint8Array(transactionData)); - - // Get latest blockhash - const { blockhash, lastValidBlockHeight } = await agent.connection.getLatestBlockhash(); - tx.message.recentBlockhash = blockhash; - - // Sign transaction - tx.sign([mintKeypair, agent.wallet]); - - // Send transaction - const signature = await agent.connection.sendTransaction(tx, { - skipPreflight: false, - preflightCommitment: "confirmed", - maxRetries: 5, - }); - - // Wait for confirmation - const confirmation = await agent.connection.confirmTransaction({ - signature, - blockhash, - lastValidBlockHeight, - }); - - if (confirmation.value.err) { - throw new Error(`Transaction failed: ${confirmation.value.err}`); - } + const { tokenName, tokenTicker, description, imageUrl } = input; + const result = await launchPumpFunToken(agent, tokenName, tokenTicker, description, imageUrl, input); return { status: "success", - signature, - mint: mintKeypair.publicKey.toBase58(), - metadataUri: metadataResult.metadataUri, + signature: result.signature, + mint: result.mint, + metadataUri: result.metadataUri, message: "Successfully launched token on Pump.fun" }; } catch (error: any) { diff --git a/src/actions/lendAsset.ts b/src/actions/lendAsset.ts index 25e7258..b4b0885 100644 --- a/src/actions/lendAsset.ts +++ b/src/actions/lendAsset.ts @@ -2,6 +2,7 @@ import { Action } from "../types/action"; import { SolanaAgentKit } from "../agent"; import { z } from "zod"; import { VersionedTransaction } from "@solana/web3.js"; +import { lendAsset } from "../tools"; const lendAssetAction: Action = { name: "solana_lend_asset", @@ -38,55 +39,11 @@ const lendAssetAction: Action = { try { const amount = input.amount as number; - const response = await fetch( - `https://blink.lulo.fi/actions?amount=${amount}&symbol=USDC`, - { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - account: agent.wallet.publicKey.toBase58(), - }), - } - ); - - if (!response.ok) { - return { - status: "error", - message: `Failed to get lending transaction: ${response.statusText}` - }; - } - - const data = await response.json(); - - // Deserialize the transaction - const luloTxn = VersionedTransaction.deserialize( - Buffer.from(data.transaction, "base64") - ); - - // Get a recent blockhash and set it - const { blockhash } = await agent.connection.getLatestBlockhash(); - luloTxn.message.recentBlockhash = blockhash; - - // Sign and send transaction - luloTxn.sign([agent.wallet]); - const signature = await agent.connection.sendTransaction(luloTxn, { - preflightCommitment: "confirmed", - maxRetries: 3, - }); - - // Wait for confirmation - const latestBlockhash = await agent.connection.getLatestBlockhash(); - await agent.connection.confirmTransaction({ - signature, - blockhash: latestBlockhash.blockhash, - lastValidBlockHeight: latestBlockhash.lastValidBlockHeight, - }); + const response = await lendAsset(agent, amount); return { status: "success", - signature, + signature: response, message: `Successfully lent ${amount} USDC` }; } catch (error: any) { diff --git a/src/actions/mintNFT.ts b/src/actions/mintNFT.ts index 4e9e6ae..ef26ec4 100644 --- a/src/actions/mintNFT.ts +++ b/src/actions/mintNFT.ts @@ -2,6 +2,7 @@ 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", @@ -63,13 +64,14 @@ const mintNFTAction: Action = { recipient: z.string().min(32, "Invalid recipient address").optional() }), handler: async (agent: SolanaAgentKit, input: Record) => { - const result = await agent.mintNFT( + const result = await mintCollectionNFT( + agent, new PublicKey(input.collectionMint), { name: input.name, - uri: input.uri, + uri: input.uri }, - input.recipient ? new PublicKey(input.recipient) : agent.wallet_address + input.recipient ? new PublicKey(input.recipient) : undefined ); return { diff --git a/src/actions/pythFetchPrice.ts b/src/actions/pythFetchPrice.ts index acafeb0..1c1f9e0 100644 --- a/src/actions/pythFetchPrice.ts +++ b/src/actions/pythFetchPrice.ts @@ -3,6 +3,7 @@ import { SolanaAgentKit } from "../agent"; import { z } from "zod"; import { PriceServiceConnection } from "@pythnetwork/price-service-client"; import BN from "bn.js"; +import { pythFetchPrice } from "../tools"; const pythFetchPriceAction: Action = { name: "solana_pyth_fetch_price", @@ -39,27 +40,7 @@ const pythFetchPriceAction: Action = { try { const priceFeedId = input.priceFeedId as string; - // Connect to Hermes service - const stableHermesServiceUrl = "https://hermes.pyth.network"; - const connection = new PriceServiceConnection(stableHermesServiceUrl); - const feeds = [priceFeedId]; - - const currentPrice = await connection.getLatestPriceFeeds(feeds); - - if (!currentPrice || currentPrice.length === 0) { - return { - status: "error", - message: "Price data not available for the given feed ID" - }; - } - - // Get price and exponent from price feed - const price = new BN(currentPrice[0].getPriceUnchecked().price); - const exponent = new BN(currentPrice[0].getPriceUnchecked().expo); - - // Convert to scaled price - const scaledPrice = price.div(new BN(10).pow(exponent)); - const priceStr = scaledPrice.toString(); + const priceStr = await pythFetchPrice(priceFeedId); return { status: "success", diff --git a/src/actions/raydiumCreateAmmV4.ts b/src/actions/raydiumCreateAmmV4.ts index 6c4c357..afda966 100644 --- a/src/actions/raydiumCreateAmmV4.ts +++ b/src/actions/raydiumCreateAmmV4.ts @@ -12,6 +12,7 @@ import { import { MintLayout, TOKEN_PROGRAM_ID } from "@solana/spl-token"; import { PublicKey } from "@solana/web3.js"; import BN from "bn.js"; +import { raydiumCreateAmmV4 } from "../tools"; const raydiumCreateAmmV4Action: Action = { name: "solana_raydium_create_amm_v4", @@ -72,70 +73,7 @@ const raydiumCreateAmmV4Action: Action = { const quoteAmount = new BN(input.quoteAmount); const startTime = new BN(input.startTime); - const raydium = await Raydium.load({ - owner: agent.wallet, - connection: agent.connection, - }); - - const marketBufferInfo = await agent.connection.getAccountInfo( - new PublicKey(marketId), - ); - const { baseMint, quoteMint } = MARKET_STATE_LAYOUT_V3.decode( - marketBufferInfo!.data, - ); - - const baseMintInfo = await agent.connection.getAccountInfo(baseMint); - const quoteMintInfo = await agent.connection.getAccountInfo(quoteMint); - - if ( - baseMintInfo?.owner.toString() !== TOKEN_PROGRAM_ID.toBase58() || - quoteMintInfo?.owner.toString() !== TOKEN_PROGRAM_ID.toBase58() - ) { - throw new Error( - "amm pools with openbook market only support TOKEN_PROGRAM_ID mints, if you want to create pool with token-2022, please create cpmm pool instead", - ); - } - - if ( - baseAmount - .mul(quoteAmount) - .lte( - new BN(1) - .mul(new BN(10 ** MintLayout.decode(baseMintInfo.data).decimals)) - .pow(new BN(2)), - ) - ) { - throw new Error( - "initial liquidity too low, try adding more baseAmount/quoteAmount", - ); - } - - const { execute } = await raydium.liquidity.createPoolV4({ - programId: AMM_V4, - marketInfo: { - marketId, - programId: OPEN_BOOK_PROGRAM, - }, - baseMintInfo: { - mint: baseMint, - decimals: MintLayout.decode(baseMintInfo.data).decimals, - }, - quoteMintInfo: { - mint: quoteMint, - decimals: MintLayout.decode(quoteMintInfo.data).decimals, - }, - baseAmount, - quoteAmount, - startTime, - ownerInfo: { - useSOLBalance: true, - }, - associatedOnly: false, - txVersion: TxVersion.V0, - feeDestinationId: FEE_DESTINATION_ID, - }); - - const { txId } = await execute({ sendAndConfirm: true }); + const txId = await raydiumCreateAmmV4(agent, marketId, baseAmount, quoteAmount, startTime); return { status: "success", diff --git a/src/actions/raydiumCreateCpmm.ts b/src/actions/raydiumCreateCpmm.ts index faea973..0e6795c 100644 --- a/src/actions/raydiumCreateCpmm.ts +++ b/src/actions/raydiumCreateCpmm.ts @@ -10,6 +10,7 @@ import { import { MintLayout } from "@solana/spl-token"; import { PublicKey } from "@solana/web3.js"; import BN from "bn.js"; +import { raydiumCreateCpmm } from "../tools"; const raydiumCreateCpmmAction: Action = { name: "solana_raydium_create_cpmm", @@ -68,62 +69,7 @@ const raydiumCreateCpmmAction: Action = { const mintBAmount = new BN(input.quoteAmount); const startTime = new BN(input.startTime); - const raydium = await Raydium.load({ - owner: agent.wallet, - connection: agent.connection, - }); - - const [mintInfoA, mintInfoB] = await agent.connection.getMultipleAccountsInfo( - [mintA, mintB], - ); - if (mintInfoA === null || mintInfoB === null) { - throw Error("fetch mint info error"); - } - - const mintDecodeInfoA = MintLayout.decode(mintInfoA.data); - const mintDecodeInfoB = MintLayout.decode(mintInfoB.data); - - const mintFormatInfoA = { - chainId: 101, - address: mintA.toString(), - programId: mintInfoA.owner.toString(), - logoURI: "", - symbol: "", - name: "", - decimals: mintDecodeInfoA.decimals, - tags: [], - extensions: {}, - }; - const mintFormatInfoB = { - chainId: 101, - address: mintB.toString(), - programId: mintInfoB.owner.toString(), - logoURI: "", - symbol: "", - name: "", - decimals: mintDecodeInfoB.decimals, - tags: [], - extensions: {}, - }; - - const { execute } = await raydium.cpmm.createPool({ - programId: CREATE_CPMM_POOL_PROGRAM, - poolFeeAccount: CREATE_CPMM_POOL_FEE_ACC, - mintA: mintFormatInfoA, - mintB: mintFormatInfoB, - mintAAmount, - mintBAmount, - startTime, - //@ts-expect-error sdk bug - feeConfig: { id: configId.toString() }, - associatedOnly: false, - ownerInfo: { - useSOLBalance: true, - }, - txVersion: TxVersion.V0, - }); - - const { txId } = await execute({ sendAndConfirm: true }); + const txId = await raydiumCreateCpmm(agent, mintA, mintB, configId, mintAAmount, mintBAmount, startTime); return { status: "success", diff --git a/src/actions/registerDomain.ts b/src/actions/registerDomain.ts index 02b92d3..50b398a 100644 --- a/src/actions/registerDomain.ts +++ b/src/actions/registerDomain.ts @@ -5,6 +5,7 @@ import { Transaction } from "@solana/web3.js"; import { registerDomainNameV2 } from "@bonfida/spl-name-service"; import { getAssociatedTokenAddressSync } from "@solana/spl-token"; import { TOKENS } from "../constants"; +import { registerDomain } from "../tools"; const registerDomainAction: Action = { name: "solana_register_domain", @@ -48,50 +49,7 @@ const registerDomainAction: Action = { const name = input.name as string; const spaceKB = (input.spaceKB as number) || 1; - // Validate space size - if (spaceKB > 10) { - return { - status: "error", - message: "Maximum domain size is 10KB" - }; - } - - // Convert KB to bytes - const space = spaceKB * 1_000; - - const buyerTokenAccount = await getAssociatedTokenAddressSync( - agent.wallet_address, - TOKENS.USDC - ); - - // Create registration instruction - const instruction = await registerDomainNameV2( - agent.connection, - name, - space, - agent.wallet_address, - buyerTokenAccount - ); - - // Create and sign transaction - const transaction = new Transaction().add(...instruction); - transaction.recentBlockhash = ( - await agent.connection.getLatestBlockhash() - ).blockhash; - transaction.feePayer = agent.wallet_address; - - // Sign and send transaction - const signature = await agent.connection.sendTransaction(transaction, [ - agent.wallet - ]); - - // Wait for confirmation - const latestBlockhash = await agent.connection.getLatestBlockhash(); - await agent.connection.confirmTransaction({ - signature, - blockhash: latestBlockhash.blockhash, - lastValidBlockHeight: latestBlockhash.lastValidBlockHeight, - }); + const signature = await registerDomain(agent, name, spaceKB); return { status: "success", diff --git a/src/actions/requestFunds.ts b/src/actions/requestFunds.ts index 03159a7..49e4c2b 100644 --- a/src/actions/requestFunds.ts +++ b/src/actions/requestFunds.ts @@ -1,6 +1,7 @@ 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", @@ -27,7 +28,7 @@ const requestFundsAction: Action = { ], schema: z.object({}), // No input parameters required handler: async (agent: SolanaAgentKit, _input: Record) => { - await agent.requestFaucetFunds(); + await request_faucet_funds(agent); return { status: "success", diff --git a/src/actions/resolveDomain.ts b/src/actions/resolveDomain.ts index 049c3df..1d6307a 100644 --- a/src/actions/resolveDomain.ts +++ b/src/actions/resolveDomain.ts @@ -2,6 +2,7 @@ import { Action } from "../types/action"; import { SolanaAgentKit } from "../agent"; import { z } from "zod"; import { TldParser } from "@onsol/tldparser"; +import { resolveAllDomains } from "../tools"; const resolveDomainAction: Action = { name: "solana_resolve_domain", @@ -33,20 +34,10 @@ const resolveDomainAction: Action = { handler: async (agent: SolanaAgentKit, input: Record) => { try { const domain = input.domain as string; - const tld = await new TldParser(agent.connection).getOwnerFromDomainTld( - domain - ); - - if (!tld) { - return { - status: "error", - message: "Domain not found" - }; - } - + const tld = await resolveAllDomains(agent, domain); return { status: "success", - owner: tld.toBase58(), + owner: tld, message: `Successfully resolved domain ${domain}` }; } catch (error: any) { diff --git a/src/actions/resolveSolDomain.ts b/src/actions/resolveSolDomain.ts index 481acef..ce13fe3 100644 --- a/src/actions/resolveSolDomain.ts +++ b/src/actions/resolveSolDomain.ts @@ -2,6 +2,7 @@ import { Action } from "../types/action"; import { SolanaAgentKit } from "../agent"; import { z } from "zod"; import { resolve } from "@bonfida/spl-name-service"; +import { resolveSolDomain } from "../tools"; const resolveSolDomainAction: Action = { name: "solana_resolve_sol_domain", @@ -38,24 +39,12 @@ const resolveSolDomainAction: Action = { try { const domain = input.domain as string; - if (!domain || typeof domain !== "string") { - return { - status: "error", - message: "Invalid domain. Expected a non-empty string." - }; - } - - // Remove .sol suffix if present for consistent handling - const cleanDomain = domain.toLowerCase().endsWith(".sol") - ? domain.slice(0, -4) - : domain; - - const ownerAddress = await resolve(agent.connection, cleanDomain); + const res = await resolveSolDomain(agent,domain) return { status: "success", - owner: ownerAddress.toBase58(), - message: `Successfully resolved ${cleanDomain}.sol` + owner: res.toString(), + message: `Successfully resolved ${res}` }; } catch (error: any) { return { diff --git a/src/actions/stakeWithJup.ts b/src/actions/stakeWithJup.ts index 271f902..7110a72 100644 --- a/src/actions/stakeWithJup.ts +++ b/src/actions/stakeWithJup.ts @@ -2,6 +2,7 @@ import { Action } from "../types/action"; import { SolanaAgentKit } from "../agent"; import { z } from "zod"; import { VersionedTransaction } from "@solana/web3.js"; +import { stakeWithJup, trade } from "../tools"; const stakeWithJupAction: Action = { name: "solana_stake_with_jup", @@ -38,55 +39,10 @@ const stakeWithJupAction: Action = { try { const amount = input.amount as number; - // Get staking transaction from Jupiter - const res = await fetch( - `https://worker.jup.ag/blinks/swap/So11111111111111111111111111111111111111112/jupSoLaHXQiZZTSfEWMTRRgpnyFm8f6sZdosWBjx93v/${amount}`, - { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - account: agent.wallet.publicKey.toBase58(), - }), - } - ); - - if (!res.ok) { - return { - status: "error", - message: `Failed to get staking transaction: ${res.statusText}` - }; - } - - const data = await res.json(); - - // Deserialize and prepare transaction - const txn = VersionedTransaction.deserialize( - Buffer.from(data.transaction, "base64") - ); - - const { blockhash } = await agent.connection.getLatestBlockhash(); - txn.message.recentBlockhash = blockhash; - - // Sign and send transaction - txn.sign([agent.wallet]); - const signature = await agent.connection.sendTransaction(txn, { - preflightCommitment: "confirmed", - maxRetries: 3, - }); - - // Confirm transaction - const latestBlockhash = await agent.connection.getLatestBlockhash(); - await agent.connection.confirmTransaction({ - signature, - blockhash: latestBlockhash.blockhash, - lastValidBlockHeight: latestBlockhash.lastValidBlockHeight, - }); - + const res = await stakeWithJup(agent,amount) return { status: "success", - signature, + res, message: `Successfully staked ${amount} SOL for jupSOL` }; } catch (error: any) { diff --git a/src/actions/trade.ts b/src/actions/trade.ts index 8a536b3..bb27f79 100644 --- a/src/actions/trade.ts +++ b/src/actions/trade.ts @@ -2,6 +2,7 @@ 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", @@ -58,7 +59,7 @@ const tradeAction: Action = { slippageBps: z.number().min(0).max(10000).optional() }), handler: async (agent: SolanaAgentKit, input: Record) => { - const tx = await agent.trade( + const tx = await trade(agent, new PublicKey(input.outputMint), input.inputAmount, input.inputMint diff --git a/src/actions/transfer.ts b/src/actions/transfer.ts index 426a071..3769481 100644 --- a/src/actions/transfer.ts +++ b/src/actions/transfer.ts @@ -2,6 +2,7 @@ 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", @@ -59,7 +60,7 @@ const transferAction: Action = { const recipient = new PublicKey(input.to); const mintAddress = input.mint ? new PublicKey(input.mint) : undefined; - const tx = await agent.transfer(recipient, input.amount, mintAddress); + const tx = await transfer(agent,recipient, input.amount, mintAddress); return { status: "success", From e48959fb1a963c8f481d9bc6f86781d319064c5a Mon Sep 17 00:00:00 2001 From: Fahri Bilici <28020526+FahriBilici@users.noreply.github.com> Date: Sat, 28 Dec 2024 16:48:10 +0100 Subject: [PATCH 05/11] fix: Make recipient address mandatory in mintNFT action validation --- src/actions/mintNFT.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/actions/mintNFT.ts b/src/actions/mintNFT.ts index ef26ec4..949514c 100644 --- a/src/actions/mintNFT.ts +++ b/src/actions/mintNFT.ts @@ -61,7 +61,7 @@ const mintNFTAction: Action = { 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() + recipient: z.string().min(32, "Invalid recipient address") }), handler: async (agent: SolanaAgentKit, input: Record) => { const result = await mintCollectionNFT( From 3b4fddd65a8a63cc22f6a6e055bbb82fa5f36084 Mon Sep 17 00:00:00 2001 From: Fahri Bilici <28020526+FahriBilici@users.noreply.github.com> Date: Mon, 30 Dec 2024 21:38:31 +0100 Subject: [PATCH 06/11] fixing tool calling bug --- src/langchain/index.ts | 31 ++++++++++++------------- test/index.ts | 52 ++---------------------------------------- 2 files changed, 16 insertions(+), 67 deletions(-) diff --git a/src/langchain/index.ts b/src/langchain/index.ts index 68cd157..4e64556 100644 --- a/src/langchain/index.ts +++ b/src/langchain/index.ts @@ -32,10 +32,10 @@ export class SolanaBalanceTool extends Tool { try { // Parse input as JSON if provided, otherwise use empty object const parsedInput = input ? JSON.parse(input) : {}; - + // Validate and execute using the action const result = await this.action.handler(this.solanaKit, parsedInput); - + return JSON.stringify(result); } catch (error: any) { return JSON.stringify({ @@ -60,10 +60,10 @@ export class SolanaTransferTool extends Tool { try { // Parse input as JSON const parsedInput = JSON.parse(input); - + // Validate and execute using the action const result = await this.action.handler(this.solanaKit, parsedInput); - + return JSON.stringify(result); } catch (error: any) { return JSON.stringify({ @@ -88,10 +88,10 @@ export class SolanaDeployTokenTool extends Tool { try { // Parse input as JSON const parsedInput = JSON.parse(input); - + // Validate and execute using the action const result = await this.action.handler(this.solanaKit, parsedInput); - + return JSON.stringify(result); } catch (error: any) { return JSON.stringify({ @@ -116,10 +116,10 @@ export class SolanaDeployCollectionTool extends Tool { try { // Parse input as JSON const parsedInput = JSON.parse(input); - + // Validate and execute using the action const result = await this.action.handler(this.solanaKit, parsedInput); - + return JSON.stringify(result); } catch (error: any) { return JSON.stringify({ @@ -144,10 +144,10 @@ export class SolanaMintNFTTool extends Tool { try { // Parse input as JSON const parsedInput = JSON.parse(input); - + // Validate and execute using the action const result = await this.action.handler(this.solanaKit, parsedInput); - + return JSON.stringify(result); } catch (error: any) { return JSON.stringify({ @@ -172,10 +172,10 @@ export class SolanaTradeTool extends Tool { try { // Parse input as JSON const parsedInput = JSON.parse(input); - + // Validate and execute using the action const result = await this.action.handler(this.solanaKit, parsedInput); - + return JSON.stringify(result); } catch (error: any) { return JSON.stringify({ @@ -200,7 +200,7 @@ export class SolanaRequestFundsTool extends Tool { try { // No input needed for this action const result = await this.action.handler(this.solanaKit, {}); - + return JSON.stringify(result); } catch (error: any) { return JSON.stringify({ @@ -1151,7 +1151,7 @@ export class SolanaCreateGibworkTask extends Tool { } export function createSolanaTools(solanaKit: SolanaAgentKit) { - const tools = [ + return [ new SolanaBalanceTool(solanaKit), new SolanaTransferTool(solanaKit), new SolanaDeployTokenTool(solanaKit), @@ -1185,7 +1185,4 @@ export function createSolanaTools(solanaKit: SolanaAgentKit) { new SolanaResolveAllDomainsTool(solanaKit), new SolanaCreateGibworkTask(solanaKit), ]; - - // Convert LangChain tools to our Action interface - return tools.map(tool => wrapLangChainTool(tool, solanaKit)); } diff --git a/test/index.ts b/test/index.ts index 10a2003..5aae92e 100644 --- a/test/index.ts +++ b/test/index.ts @@ -35,52 +35,6 @@ validateEnvironment(); const WALLET_DATA_FILE = "wallet_data.txt"; -// Convert our Action interface to LangChain Tool -function convertActionToTool(action: Action, solanaAgent: SolanaAgentKit): Tool { - class ActionTool extends Tool { - name = action.name; - description = action.description; - - async _call(input: string): Promise { - try { - let parsedInput; - try { - // Try to parse as JSON first - parsedInput = input ? JSON.parse(input) : {}; - } catch { - // If JSON parsing fails, use the raw input string - parsedInput = { input }; - } - - // Validate input against schema if available - if (action.schema) { - try { - parsedInput = action.schema.parse(parsedInput); - } catch (validationError: any) { - return JSON.stringify({ - status: "error", - message: `Invalid input: ${validationError.message}`, - code: "VALIDATION_ERROR" - }); - } - } - - const result = await action.handler(solanaAgent, parsedInput); - return JSON.stringify(result); - } catch (error: any) { - console.error("Action execution error:", error); - return JSON.stringify({ - status: "error", - message: error.message, - code: error.code || "UNKNOWN_ERROR" - }); - } - } - } - - return new ActionTool(); -} - async function initializeAgent() { try { const llm = new ChatOpenAI({ @@ -104,10 +58,8 @@ async function initializeAgent() { process.env.OPENAI_API_KEY!, ); - const actions = createSolanaTools(solanaAgent); - // Convert our Actions to LangChain Tools - const tools = actions.map(action => convertActionToTool(action, solanaAgent)); - + const tools = createSolanaTools(solanaAgent); + const memory = new MemorySaver(); const config = { configurable: { thread_id: "Solana Agent Kit!" } }; From c00515f3d26bdf06578b15a336e463eb5a08ebaa Mon Sep 17 00:00:00 2001 From: Fahri Bilici <28020526+FahriBilici@users.noreply.github.com> Date: Mon, 30 Dec 2024 22:31:15 +0100 Subject: [PATCH 07/11] Fix formatting issues and removing unusued libaries --- src/actions/balance.ts | 31 +++--- src/actions/createGibworkTask.ts | 56 +++++------ src/actions/createImage.ts | 53 +++++----- src/actions/createOpenbookMarket.ts | 46 ++++----- src/actions/createOrcaSingleSidedWhirlpool.ts | 54 ++++++----- src/actions/deployCollection.ts | 31 +++--- src/actions/deployToken.ts | 38 ++++---- src/actions/fetchPrice.ts | 27 +++--- src/actions/getAllDomainsTLDs.ts | 24 ++--- src/actions/getAllRegisteredAllDomains.ts | 29 +++--- src/actions/getMainAllDomainsDomain.ts | 33 ++++--- src/actions/getOwnedAllDomains.ts | 28 +++--- src/actions/getOwnedDomainsForTLD.ts | 30 +++--- src/actions/getPrimaryDomain.ts | 34 +++---- src/actions/getTPS.ts | 21 ++-- src/actions/getTokenData.ts | 50 +++++----- src/actions/index.ts | 2 +- src/actions/launchPumpfunToken.ts | 73 +++++++------- src/actions/lendAsset.ts | 25 +++-- src/actions/mintNFT.ts | 38 ++++---- src/actions/pythFetchPrice.ts | 27 +++--- src/actions/raydiumCreateAmmV4.ts | 69 +++++++------ src/actions/raydiumCreateCpmm.ts | 64 +++++++------ src/actions/registerDomain.ts | 33 +++---- src/actions/requestFunds.ts | 16 ++-- src/actions/resolveDomain.ts | 23 +++-- src/actions/resolveSolDomain.ts | 31 +++--- src/actions/stakeWithJup.ts | 34 ++++--- src/actions/trade.ts | 39 ++++---- src/actions/transfer.ts | 32 ++++--- src/langchain/index.ts | 15 ++- src/types/action.ts | 7 +- src/utils/actionExecutor.ts | 25 ++--- src/utils/langchainWrapper.ts | 96 ------------------- test/index.ts | 2 - 35 files changed, 574 insertions(+), 662 deletions(-) delete mode 100644 src/utils/langchainWrapper.ts diff --git a/src/actions/balance.ts b/src/actions/balance.ts index 36d9304..107b709 100644 --- a/src/actions/balance.ts +++ b/src/actions/balance.ts @@ -11,7 +11,7 @@ const balanceAction: Action = { "get wallet balance", "view balance", "show balance", - "check token 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. @@ -23,37 +23,40 @@ const balanceAction: Action = { output: { status: "success", balance: "100", - token: "SOL" + token: "SOL", }, - explanation: "Get SOL balance of the wallet" - } + explanation: "Get SOL balance of the wallet", + }, ], [ { input: { - tokenAddress: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v" + tokenAddress: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", }, output: { status: "success", balance: "1000", - token: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v" + token: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", }, - explanation: "Get USDC token balance" - } - ] + explanation: "Get USDC token balance", + }, + ], ], schema: z.object({ - tokenAddress: z.string().optional() + tokenAddress: z.string().optional(), }), handler: async (agent: SolanaAgentKit, input: Record) => { - const balance = await get_balance(agent, input.tokenAddress && new PublicKey(input.tokenAddress)); + const balance = await get_balance( + agent, + input.tokenAddress && new PublicKey(input.tokenAddress), + ); return { status: "success", balance: balance, - token: input.tokenAddress || "SOL" + token: input.tokenAddress || "SOL", }; - } + }, }; -export default balanceAction; \ No newline at end of file +export default balanceAction; diff --git a/src/actions/createGibworkTask.ts b/src/actions/createGibworkTask.ts index 419b7b5..3ff8b12 100644 --- a/src/actions/createGibworkTask.ts +++ b/src/actions/createGibworkTask.ts @@ -1,7 +1,7 @@ import { Action } from "../types/action"; import { SolanaAgentKit } from "../agent"; import { z } from "zod"; -import { PublicKey, VersionedTransaction } from "@solana/web3.js"; +import { PublicKey } from "@solana/web3.js"; import { create_gibwork_task } from "../tools"; const createGibworkTaskAction: Action = { @@ -12,9 +12,10 @@ const createGibworkTaskAction: Action = { "create gig", "post task", "create work", - "new task on gibwork" + "new task on gibwork", ], - description: "Create a new task on the Gibwork platform with payment in SPL tokens", + description: + "Create a new task on the Gibwork platform with payment in SPL tokens", examples: [ [ { @@ -24,45 +25,38 @@ const createGibworkTaskAction: Action = { requirements: "Experience with Rust and React", tags: ["solana", "rust", "react"], tokenMintAddress: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", - tokenAmount: 100 + tokenAmount: 100, }, output: { status: "success", taskId: "task_123", signature: "3YKpM1...", - message: "Successfully created task: Build a Solana dApp" + message: "Successfully created task: Build a Solana dApp", }, - explanation: "Create a new task on Gibwork with 100 USDC payment" - } - ] + 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() + 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()) + 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() + 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)") + .describe("Optional payer address (defaults to wallet address)"), }), handler: async (agent: SolanaAgentKit, input: Record) => { try { - const tokenMintAddress = new PublicKey(input.tokenMintAddress); - const payer = input.payer ? new PublicKey(input.payer) : undefined; - const responseData = await create_gibwork_task( agent, input.title, @@ -71,22 +65,22 @@ const createGibworkTaskAction: Action = { input.tags, new PublicKey(input.tokenMintAddress), input.tokenAmount, - input.payer ? new PublicKey(input.payer) : undefined + input.payer ? new PublicKey(input.payer) : undefined, ); return { status: "success", taskId: responseData.taskId, signature: responseData.signature, - message: `Successfully created task: ${input.title}` + message: `Successfully created task: ${input.title}`, }; } catch (error: any) { return { status: "error", - message: `Failed to create task: ${error.message}` + message: `Failed to create task: ${error.message}`, }; } - } + }, }; -export default createGibworkTaskAction; \ No newline at end of file +export default createGibworkTaskAction; diff --git a/src/actions/createImage.ts b/src/actions/createImage.ts index 722a683..8906844 100644 --- a/src/actions/createImage.ts +++ b/src/actions/createImage.ts @@ -1,8 +1,6 @@ import { Action } from "../types/action"; import { SolanaAgentKit } from "../agent"; import { z } from "zod"; -import OpenAI from "openai"; -import { create } from "domain"; import { create_image } from "../tools/create_image"; const createImageAction: Action = { @@ -13,9 +11,10 @@ const createImageAction: Action = { "make image", "generate artwork", "create picture", - "generate picture" + "generate picture", ], - description: "Create an AI-generated image based on a text prompt using OpenAI's DALL-E models", + description: + "Create an AI-generated image based on a text prompt using OpenAI's DALL-E models", examples: [ [ { @@ -24,56 +23,56 @@ const createImageAction: Action = { model: "dall-e-3", size: "1024x1024", quality: "standard", - style: "natural" + style: "natural", }, output: { status: "success", imageUrl: "https://example.com/image.png", - message: "Successfully generated image" + message: "Successfully generated image", }, - explanation: "Generate an image of a sunset landscape using DALL-E 3" - } - ] + explanation: "Generate an image of a sunset landscape using DALL-E 3", + }, + ], ], schema: z.object({ - prompt: z.string() + prompt: z + .string() .min(1) .max(1000) .describe("The text description of the image to generate"), - model: z.enum(["dall-e-3"]) + 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"]) + size: z + .enum(["256x256", "512x512", "1024x1024", "1792x1024", "1024x1792"]) .default("1024x1024") .describe("The size of the generated image"), - quality: z.enum(["standard", "hd"]) + quality: z + .enum(["standard", "hd"]) .default("standard") .describe("The quality level of the generated image"), - style: z.enum(["natural", "vivid"]) + style: z + .enum(["natural", "vivid"]) .default("natural") - .describe("The style of the generated image") + .describe("The style of the generated image"), }), handler: async (agent: SolanaAgentKit, input: Record) => { try { if (!agent.openai_api_key) { return { status: "error", - message: "OpenAI API key not found in agent configuration" + message: "OpenAI API key not found in agent configuration", }; } const { prompt, model, size } = input; - - const openai = new OpenAI({ - apiKey: agent.openai_api_key - }); - const response = await create_image(agent, prompt, model, size); return { status: "success", imageUrl: response.images[0].url, - message: "Successfully generated image" + message: "Successfully generated image", }; } catch (error: any) { // Handle specific OpenAI error types @@ -82,21 +81,21 @@ const createImageAction: Action = { if (status === 429) { return { status: "error", - message: "Rate limit exceeded. Please try again later." + message: "Rate limit exceeded. Please try again later.", }; } return { status: "error", - message: `OpenAI API error: ${data.error?.message || error.message}` + message: `OpenAI API error: ${data.error?.message || error.message}`, }; } return { status: "error", - message: `Failed to generate image: ${error.message}` + message: `Failed to generate image: ${error.message}`, }; } - } + }, }; -export default createImageAction; \ No newline at end of file +export default createImageAction; diff --git a/src/actions/createOpenbookMarket.ts b/src/actions/createOpenbookMarket.ts index 0e923c0..dafb509 100644 --- a/src/actions/createOpenbookMarket.ts +++ b/src/actions/createOpenbookMarket.ts @@ -1,8 +1,6 @@ import { Action } from "../types/action"; import { SolanaAgentKit } from "../agent"; import { z } from "zod"; -import { OPEN_BOOK_PROGRAM, Raydium, TxVersion } from "@raydium-io/raydium-sdk-v2"; -import { MintLayout, TOKEN_PROGRAM_ID } from "@solana/spl-token"; import { PublicKey } from "@solana/web3.js"; import { openbookCreateMarket } from "../tools"; @@ -14,42 +12,41 @@ const createOpenbookMarketAction: Action = { "new openbook market", "create trading pair", "setup dex market", - "new trading market" + "new trading market", ], description: "Create a new trading market on Openbook DEX", examples: [ [ { input: { - baseMint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", // USDC - quoteMint: "So11111111111111111111111111111111111111112", // SOL + baseMint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", // USDC + quoteMint: "So11111111111111111111111111111111111111112", // SOL lotSize: 1, - tickSize: 0.01 + tickSize: 0.01, }, output: { status: "success", signatures: ["2ZE7Rz...", "3YKpM1..."], - message: "Successfully created Openbook market" + message: "Successfully created Openbook market", }, - explanation: "Create a new USDC/SOL market on Openbook with default lot and tick sizes" - } - ] + 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() + 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() + tickSize: z + .number() .positive() .default(0.01) - .describe("The minimum price increment (tick size)") + .describe("The minimum price increment (tick size)"), }), handler: async (agent: SolanaAgentKit, input: Record) => { try { @@ -58,27 +55,26 @@ const createOpenbookMarketAction: Action = { const lotSize = input.lotSize || 1; const tickSize = input.tickSize || 0.01; - const signatures = await openbookCreateMarket( agent, baseMint, quoteMint, lotSize, - tickSize + tickSize, ); return { status: "success", signatures, - message: "Successfully created Openbook market" + message: "Successfully created Openbook market", }; } catch (error: any) { return { status: "error", - message: `Failed to create Openbook market: ${error.message}` + message: `Failed to create Openbook market: ${error.message}`, }; } - } + }, }; -export default createOpenbookMarketAction; \ No newline at end of file +export default createOpenbookMarketAction; diff --git a/src/actions/createOrcaSingleSidedWhirlpool.ts b/src/actions/createOrcaSingleSidedWhirlpool.ts index f23389d..dc82984 100644 --- a/src/actions/createOrcaSingleSidedWhirlpool.ts +++ b/src/actions/createOrcaSingleSidedWhirlpool.ts @@ -27,48 +27,58 @@ const createOrcaSingleSidedWhirlpoolAction: Action = { "initialize orca whirlpool", "create orca concentrated pool", "setup orca concentrated liquidity", - "create orca trading pair" + "create orca trading pair", ], - description: "Create a new single-sided whirlpool on Orca with concentrated liquidity", + 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 + depositTokenMint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", // USDC + otherTokenMint: "So11111111111111111111111111111111111111112", // SOL initialPrice: "0.001", maxPrice: "5.0", - feeTier: 0.3 + feeTier: 0.3, }, output: { status: "success", signature: "2ZE7Rz...", - message: "Successfully created Orca single-sided whirlpool" + message: "Successfully created Orca single-sided whirlpool", }, - explanation: "Create a USDC/SOL whirlpool with 1M USDC initial liquidity" - } - ] + explanation: + "Create a USDC/SOL whirlpool with 1M USDC initial liquidity", + }, + ], ], schema: z.object({ - depositTokenAmount: z.string() + depositTokenAmount: z + .string() .min(1) - .describe("The amount of deposit token to provide as liquidity (including decimals)"), - depositTokenMint: z.string() + .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() + otherTokenMint: z + .string() .min(1) .describe("The mint address of the other token in the pool"), - initialPrice: z.string() + initialPrice: z + .string() .min(1) .describe("Initial price of deposit token in terms of the other token"), - maxPrice: z.string() + maxPrice: z + .string() .min(1) .describe("Maximum price at which liquidity is added"), - feeTier: z.number() + 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%)") + .describe("Fee tier percentage for the pool (e.g., 0.3 for 0.3%)"), }), handler: async (agent: SolanaAgentKit, input: Record) => { try { @@ -87,21 +97,21 @@ const createOrcaSingleSidedWhirlpoolAction: Action = { otherTokenMint, initialPrice, maxPrice, - feeTier + feeTier, ); return { status: "success", signature, - message: "Successfully created Orca single-sided whirlpool" + message: "Successfully created Orca single-sided whirlpool", }; } catch (error: any) { return { status: "error", - message: `Failed to create whirlpool: ${error.message}` + message: `Failed to create whirlpool: ${error.message}`, }; } - } + }, }; -export default createOrcaSingleSidedWhirlpoolAction; \ No newline at end of file +export default createOrcaSingleSidedWhirlpoolAction; diff --git a/src/actions/deployCollection.ts b/src/actions/deployCollection.ts index 77efc10..0ac8e6c 100644 --- a/src/actions/deployCollection.ts +++ b/src/actions/deployCollection.ts @@ -1,4 +1,3 @@ -import { PublicKey } from "@solana/web3.js"; import { Action } from "../types/action"; import { SolanaAgentKit } from "../agent"; import { z } from "zod"; @@ -17,7 +16,7 @@ const deployCollectionAction: Action = { "launch collection", "deploy nft collection", "create nft collection", - "mint collection" + "mint collection", ], description: `Deploy a new NFT collection on Solana blockchain.`, examples: [ @@ -26,43 +25,43 @@ const deployCollectionAction: Action = { input: { name: "My Collection", uri: "https://example.com/collection.json", - royaltyBasisPoints: 500 + royaltyBasisPoints: 500, }, output: { status: "success", message: "Collection deployed successfully", collectionAddress: "7nE9GvcwsqzYxmJLSrYmSB1V1YoJWVK1KWzAcWAzjXkN", - name: "My Collection" + name: "My Collection", }, - explanation: "Deploy an NFT collection with 5% royalty" - } + explanation: "Deploy an NFT collection with 5% royalty", + }, ], [ { input: { name: "Basic Collection", - uri: "https://example.com/basic.json" + uri: "https://example.com/basic.json", }, output: { status: "success", message: "Collection deployed successfully", collectionAddress: "8nE9GvcwsqzYxmJLSrYmSB1V1YoJWVK1KWzAcWAzjXkM", - name: "Basic Collection" + name: "Basic Collection", }, - explanation: "Deploy a basic NFT collection without royalties" - } - ] + 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() + 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 + royaltyBasisPoints: input.royaltyBasisPoints, }; const result = await deploy_collection(agent, options); @@ -71,9 +70,9 @@ const deployCollectionAction: Action = { status: "success", message: "Collection deployed successfully", collectionAddress: result.collectionAddress.toString(), - name: input.name + name: input.name, }; - } + }, }; -export default deployCollectionAction; \ No newline at end of file +export default deployCollectionAction; diff --git a/src/actions/deployToken.ts b/src/actions/deployToken.ts index 2a1793d..d365054 100644 --- a/src/actions/deployToken.ts +++ b/src/actions/deployToken.ts @@ -1,5 +1,4 @@ -import { PublicKey } from "@solana/web3.js"; -import { Action, ActionExample } from "../types/action"; +import { Action } from "../types/action"; import { SolanaAgentKit } from "../agent"; import { z } from "zod"; import { deploy_token } from "../tools"; @@ -13,7 +12,8 @@ const deployTokenAction: Action = { "create new token", "mint token", ], - description: "Deploy a new SPL token on the Solana blockchain with specified parameters", + description: + "Deploy a new SPL token on the Solana blockchain with specified parameters", examples: [ [ { @@ -22,38 +22,38 @@ const deployTokenAction: Action = { uri: "https://example.com/token.json", symbol: "MTK", decimals: 9, - initialSupply: 1000000 + initialSupply: 1000000, }, output: { mint: "7nE9GvcwsqzYxmJLSrYmSB1V1YoJWVK1KWzAcWAzjXkN", status: "success", - message: "Token deployed successfully" + message: "Token deployed successfully", }, - explanation: "Deploy a token with initial supply and metadata" - } + explanation: "Deploy a token with initial supply and metadata", + }, ], [ { input: { name: "Basic Token", uri: "https://example.com/basic.json", - symbol: "BASIC" + symbol: "BASIC", }, output: { mint: "8nE9GvcwsqzYxmJLSrYmSB1V1YoJWVK1KWzAcWAzjXkM", status: "success", - message: "Token deployed successfully" + message: "Token deployed successfully", }, - explanation: "Deploy a basic token with minimal parameters" - } - ] + 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() + initialSupply: z.number().optional(), }), handler: async (agent: SolanaAgentKit, input: Record) => { try { @@ -63,21 +63,21 @@ const deployTokenAction: Action = { input.uri, input.symbol, input.decimals, - input.initialSupply + input.initialSupply, ); return { mint: result.mint.toString(), status: "success", - message: "Token deployed successfully" + message: "Token deployed successfully", }; } catch (error: any) { return { status: "error", - message: `Token deployment failed: ${error.message}` + message: `Token deployment failed: ${error.message}`, }; } - } -} + }, +}; -export default deployTokenAction; \ No newline at end of file +export default deployTokenAction; diff --git a/src/actions/fetchPrice.ts b/src/actions/fetchPrice.ts index 9512e14..0c45391 100644 --- a/src/actions/fetchPrice.ts +++ b/src/actions/fetchPrice.ts @@ -11,26 +11,29 @@ const fetchPriceAction: Action = { "check price", "token value", "price check", - "get price in usd" + "get price in usd", ], - description: "Fetch the current price of a Solana token in USDC using Jupiter API", + description: + "Fetch the current price of a Solana token in USDC using Jupiter API", examples: [ [ { input: { - tokenAddress: "So11111111111111111111111111111111111111112" + tokenAddress: "So11111111111111111111111111111111111111112", }, output: { status: "success", price: "23.45", - message: "Current price: $23.45 USDC" + message: "Current price: $23.45 USDC", }, - explanation: "Get the current price of SOL token in 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") + tokenAddress: z + .string() + .describe("The mint address of the token to fetch the price for"), }), handler: async (agent: SolanaAgentKit, input: Record) => { try { @@ -40,15 +43,15 @@ const fetchPriceAction: Action = { return { status: "success", price, - message: `Current price: $${price} USDC` + message: `Current price: $${price} USDC`, }; } catch (error: any) { return { status: "error", - message: `Failed to fetch price: ${error.message}` + message: `Failed to fetch price: ${error.message}`, }; } - } + }, }; -export default fetchPriceAction; \ No newline at end of file +export default fetchPriceAction; diff --git a/src/actions/getAllDomainsTLDs.ts b/src/actions/getAllDomainsTLDs.ts index 60c05d2..34dbd19 100644 --- a/src/actions/getAllDomainsTLDs.ts +++ b/src/actions/getAllDomainsTLDs.ts @@ -11,9 +11,10 @@ const getAllDomainsTLDsAction: Action = { "fetch domain tlds", "get top level domains", "list available tlds", - "get domain suffixes" + "get domain suffixes", ], - description: "Get a list of all available top-level domains (TLDs) for Solana domains", + description: + "Get a list of all available top-level domains (TLDs) for Solana domains", examples: [ [ { @@ -21,14 +22,15 @@ const getAllDomainsTLDsAction: Action = { output: { status: "success", tlds: [".sol", ".abc", ".backpack", ".bonk"], - message: "Successfully retrieved all domain TLDs" + message: "Successfully retrieved all domain TLDs", }, - explanation: "Get a list of all available TLDs that can be used for Solana domains" - } - ] + explanation: + "Get a list of all available TLDs that can be used for Solana domains", + }, + ], ], schema: z.object({}), - handler: async (agent: SolanaAgentKit, input: Record) => { + handler: async (agent: SolanaAgentKit) => { try { // Get all domain TLDs const tlds = await getAllDomainsTLDs(agent); @@ -36,15 +38,15 @@ const getAllDomainsTLDsAction: Action = { return { status: "success", tlds, - message: "Successfully retrieved all domain TLDs" + message: "Successfully retrieved all domain TLDs", }; } catch (error: any) { return { status: "error", - message: `Failed to get domain TLDs: ${error.message}` + message: `Failed to get domain TLDs: ${error.message}`, }; } - } + }, }; -export default getAllDomainsTLDsAction; \ No newline at end of file +export default getAllDomainsTLDsAction; diff --git a/src/actions/getAllRegisteredAllDomains.ts b/src/actions/getAllRegisteredAllDomains.ts index 96fe879..6ed9140 100644 --- a/src/actions/getAllRegisteredAllDomains.ts +++ b/src/actions/getAllRegisteredAllDomains.ts @@ -11,7 +11,7 @@ const getAllRegisteredAllDomainsAction: Action = { "fetch registered domains", "get domain list", "list active domains", - "get registered names" + "get registered names", ], description: "Get a list of all registered domains across all TLDs", examples: [ @@ -19,28 +19,30 @@ const getAllRegisteredAllDomainsAction: Action = { { input: { limit: 100, - offset: 0 + offset: 0, }, output: { status: "success", domains: ["solana.sol", "bonk.abc", "wallet.backpack"], total: 3, - message: "Successfully retrieved registered domains" + message: "Successfully retrieved registered domains", }, - explanation: "Get the first 100 registered domains across all TLDs" - } - ] + explanation: "Get the first 100 registered domains across all TLDs", + }, + ], ], schema: z.object({ - limit: z.number() + limit: z + .number() .positive() .max(1000) .default(100) .describe("Maximum number of domains to return"), - offset: z.number() + offset: z + .number() .nonnegative() .default(0) - .describe("Number of domains to skip") + .describe("Number of domains to skip"), }), handler: async (agent: SolanaAgentKit, input: Record) => { try { @@ -50,20 +52,19 @@ const getAllRegisteredAllDomainsAction: Action = { // 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" + message: "Successfully retrieved registered domains", }; } catch (error: any) { return { status: "error", - message: `Failed to get registered domains: ${error.message}` + message: `Failed to get registered domains: ${error.message}`, }; } - } + }, }; -export default getAllRegisteredAllDomainsAction; \ No newline at end of file +export default getAllRegisteredAllDomainsAction; diff --git a/src/actions/getMainAllDomainsDomain.ts b/src/actions/getMainAllDomainsDomain.ts index 4f301f9..ad57161 100644 --- a/src/actions/getMainAllDomainsDomain.ts +++ b/src/actions/getMainAllDomainsDomain.ts @@ -2,7 +2,6 @@ import { Action } from "../types/action"; import { SolanaAgentKit } from "../agent"; import { z } from "zod"; import { PublicKey } from "@solana/web3.js"; -import { TldParser } from "@onsol/tldparser"; import { getMainAllDomainsDomain } from "../tools"; const getMainAllDomainsDomainAction: Action = { @@ -13,52 +12,56 @@ const getMainAllDomainsDomainAction: Action = { "get default domain", "get main address name", "get primary name", - "get main domain name" + "get main domain name", ], description: "Get the main domain associated with a wallet address", examples: [ [ { input: { - address: "7nxQB..." + address: "7nxQB...", }, output: { status: "success", domain: "solana.sol", - message: "Successfully retrieved main domain" + message: "Successfully retrieved main domain", }, - explanation: "Get the main domain name for a given wallet address" - } - ] + explanation: "Get the main domain name for a given wallet address", + }, + ], ], schema: z.object({ - address: z.string() + address: z + .string() .min(1) - .describe("The wallet address to get the main domain for") + .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)); + const mainDomain = await getMainAllDomainsDomain( + agent, + new PublicKey(input.address), + ); if (!mainDomain) { return { status: "error", - message: "No main domain found for this address" + message: "No main domain found for this address", }; } return { status: "success", domain: mainDomain, - message: "Successfully retrieved main domain" + message: "Successfully retrieved main domain", }; } catch (error: any) { return { status: "error", - message: `Failed to get main domain: ${error.message}` + message: `Failed to get main domain: ${error.message}`, }; } - } + }, }; -export default getMainAllDomainsDomainAction; \ No newline at end of file +export default getMainAllDomainsDomainAction; diff --git a/src/actions/getOwnedAllDomains.ts b/src/actions/getOwnedAllDomains.ts index 9cb279a..359bf38 100644 --- a/src/actions/getOwnedAllDomains.ts +++ b/src/actions/getOwnedAllDomains.ts @@ -12,29 +12,31 @@ const getOwnedAllDomainsAction: Action = { "fetch wallet domains", "get owned names", "list my domains", - "get address domains" + "get address domains", ], - description: "Get all domains owned by a specific wallet address across all TLDs", + description: + "Get all domains owned by a specific wallet address across all TLDs", examples: [ [ { input: { - address: "7nxQB..." + address: "7nxQB...", }, output: { status: "success", domains: ["solana.sol", "wallet.abc", "user.backpack"], total: 3, - message: "Successfully retrieved owned domains" + message: "Successfully retrieved owned domains", }, - explanation: "Get all domain names owned by a specific wallet address" - } - ] + explanation: "Get all domain names owned by a specific wallet address", + }, + ], ], schema: z.object({ - address: z.string() + address: z + .string() .min(1) - .describe("The wallet address to get owned domains for") + .describe("The wallet address to get owned domains for"), }), handler: async (agent: SolanaAgentKit, input: Record) => { try { @@ -47,15 +49,15 @@ const getOwnedAllDomainsAction: Action = { status: "success", domains, total: domains.length, - message: `Successfully retrieved ${domains.length} owned domain${domains.length === 1 ? '' : 's'}` + 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}` + message: `Failed to get owned domains: ${error.message}`, }; } - } + }, }; -export default getOwnedAllDomainsAction; \ No newline at end of file +export default getOwnedAllDomainsAction; diff --git a/src/actions/getOwnedDomainsForTLD.ts b/src/actions/getOwnedDomainsForTLD.ts index 3b2a06e..b419e58 100644 --- a/src/actions/getOwnedDomainsForTLD.ts +++ b/src/actions/getOwnedDomainsForTLD.ts @@ -1,7 +1,6 @@ import { Action } from "../types/action"; import { SolanaAgentKit } from "../agent"; import { z } from "zod"; -import { PublicKey } from "@solana/web3.js"; import { getOwnedDomainsForTLD } from "../tools"; const getOwnedDomainsForTLDAction: Action = { @@ -12,29 +11,32 @@ const getOwnedDomainsForTLDAction: Action = { "fetch wallet domains by tld", "get owned names by extension", "list my domains by tld", - "get address domains for tld" + "get address domains for tld", ], - description: "Get all domains owned by a specific wallet address for a given top-level domain (TLD)", + description: + "Get all domains owned by a specific wallet address for a given top-level domain (TLD)", examples: [ [ { input: { - tld: "sol" + tld: "sol", }, output: { status: "success", domains: ["solana.sol", "wallet.sol", "user.sol"], total: 3, - message: "Successfully retrieved owned domains for .sol" + message: "Successfully retrieved owned domains for .sol", }, - explanation: "Get all .sol domain names owned by a specific wallet address" - } - ] + explanation: + "Get all .sol domain names owned by a specific wallet address", + }, + ], ], schema: z.object({ - tld: z.string() + tld: z + .string() .min(1) - .describe("The top-level domain to filter by (e.g., 'sol', 'abc')") + .describe("The top-level domain to filter by (e.g., 'sol', 'abc')"), }), handler: async (agent: SolanaAgentKit, input: Record) => { try { @@ -47,15 +49,15 @@ const getOwnedDomainsForTLDAction: Action = { status: "success", domains, total: domains.length, - message: `Successfully retrieved ${domains.length} owned domain${domains.length === 1 ? '' : 's'} for .${tld}` + 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}` + message: `Failed to get owned domains: ${error.message}`, }; } - } + }, }; -export default getOwnedDomainsForTLDAction; \ No newline at end of file +export default getOwnedDomainsForTLDAction; diff --git a/src/actions/getPrimaryDomain.ts b/src/actions/getPrimaryDomain.ts index f19b091..d903c04 100644 --- a/src/actions/getPrimaryDomain.ts +++ b/src/actions/getPrimaryDomain.ts @@ -1,7 +1,6 @@ import { Action } from "../types/action"; import { SolanaAgentKit } from "../agent"; import { z } from "zod"; -import { getPrimaryDomain as _getPrimaryDomain } from "@bonfida/spl-name-service"; import { PublicKey } from "@solana/web3.js"; import { getPrimaryDomain } from "../tools"; @@ -13,51 +12,46 @@ const getPrimaryDomainAction: Action = { "check primary domain", "find primary domain", "get main domain", - "primary sol domain" + "primary sol domain", ], - description: "Get the primary .sol domain associated with a Solana wallet address", + description: + "Get the primary .sol domain associated with a Solana wallet address", examples: [ [ { input: { - account: "7nxQB..." + account: "7nxQB...", }, output: { status: "success", domain: "vitalik.sol", - message: "Primary domain: vitalik.sol" + message: "Primary domain: vitalik.sol", }, - explanation: "Get the primary .sol domain for a wallet address" - } - ] + 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") + 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 - ); - + const response = await getPrimaryDomain(agent, account); return { status: "success", domain: response, - message: `Primary domain: ${response}` + message: `Primary domain: ${response}`, }; } catch (error: any) { return { status: "error", - message: `Failed to get primary domain: ${error.message}` + message: `Failed to get primary domain: ${error.message}`, }; } - } + }, }; -export default getPrimaryDomainAction; \ No newline at end of file +export default getPrimaryDomainAction; diff --git a/src/actions/getTPS.ts b/src/actions/getTPS.ts index 546da65..b5c0979 100644 --- a/src/actions/getTPS.ts +++ b/src/actions/getTPS.ts @@ -10,9 +10,10 @@ const getTPSAction: Action = { "check network speed", "network performance", "transaction throughput", - "network tps" + "network tps", ], - description: "Get the current transactions per second (TPS) of the Solana network", + description: + "Get the current transactions per second (TPS) of the Solana network", examples: [ [ { @@ -20,11 +21,11 @@ const getTPSAction: Action = { output: { status: "success", tps: 3500, - message: "Current network TPS: 3500" + message: "Current network TPS: 3500", }, - explanation: "Get the current TPS of the Solana network" - } - ] + explanation: "Get the current TPS of the Solana network", + }, + ], ], schema: z.object({}), // No input parameters required handler: async (agent: SolanaAgentKit, _input: Record) => { @@ -33,15 +34,15 @@ const getTPSAction: Action = { return { status: "success", response, - message: `Current network TPS: ${response}` + message: `Current network TPS: ${response}`, }; } catch (error: any) { return { status: "error", - message: `Failed to get TPS: ${error.message}` + message: `Failed to get TPS: ${error.message}`, }; } - } + }, }; -export default getTPSAction; \ No newline at end of file +export default getTPSAction; diff --git a/src/actions/getTokenData.ts b/src/actions/getTokenData.ts index 8ac9721..57b288b 100644 --- a/src/actions/getTokenData.ts +++ b/src/actions/getTokenData.ts @@ -12,14 +12,14 @@ const getTokenDataAction: Action = { "token details", "lookup token", "find token", - "token data" + "token data", ], description: "Get token data from either a token address or ticker symbol", examples: [ [ { input: { - address: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v" + address: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", }, output: { status: "success", @@ -27,16 +27,16 @@ const getTokenDataAction: Action = { name: "USD Coin", symbol: "USDC", address: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", - decimals: 6 - } + decimals: 6, + }, }, - explanation: "Get token data using the token's address" - } + explanation: "Get token data using the token's address", + }, ], [ { input: { - ticker: "SOL" + ticker: "SOL", }, output: { status: "success", @@ -44,19 +44,21 @@ const getTokenDataAction: Action = { name: "Wrapped SOL", symbol: "SOL", address: "So11111111111111111111111111111111111111112", - decimals: 9 - } + decimals: 9, + }, }, - explanation: "Get token data using the token's ticker symbol" - } - ] + 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" - }), + 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; @@ -71,7 +73,7 @@ const getTokenDataAction: Action = { if (!tokenData) { return { status: "error", - message: "Token not found or not verified" + message: "Token not found or not verified", }; } return { @@ -81,16 +83,16 @@ const getTokenDataAction: Action = { symbol: tokenData.symbol, address: tokenData.address, decimals: tokenData.decimals, - logoURI: tokenData.logoURI - } + logoURI: tokenData.logoURI, + }, }; } catch (error: any) { return { status: "error", - message: `Failed to get token data: ${error.message}` + message: `Failed to get token data: ${error.message}`, }; } - } + }, }; -export default getTokenDataAction; \ No newline at end of file +export default getTokenDataAction; diff --git a/src/actions/index.ts b/src/actions/index.ts index d3b1679..cb81ce1 100644 --- a/src/actions/index.ts +++ b/src/actions/index.ts @@ -58,4 +58,4 @@ export const actions = [ launchPumpfunTokenAction, ]; -export type { Action, ActionExample, Handler } from "../types/action"; \ No newline at end of file +export type { Action, ActionExample, Handler } from "../types/action"; diff --git a/src/actions/launchPumpfunToken.ts b/src/actions/launchPumpfunToken.ts index 00f0add..601bb47 100644 --- a/src/actions/launchPumpfunToken.ts +++ b/src/actions/launchPumpfunToken.ts @@ -1,7 +1,6 @@ import { Action } from "../types/action"; import { SolanaAgentKit } from "../agent"; import { z } from "zod"; -import { VersionedTransaction, Keypair } from "@solana/web3.js"; import { launchPumpFunToken } from "../tools"; const launchPumpfunTokenAction: Action = { @@ -12,9 +11,10 @@ const launchPumpfunTokenAction: Action = { "deploy pumpfun token", "create meme token", "launch memecoin", - "create pump token" + "create pump token", ], - description: "Launch a new token on Pump.fun with customizable metadata and initial liquidity", + description: + "Launch a new token on Pump.fun with customizable metadata and initial liquidity", examples: [ [ { @@ -28,78 +28,79 @@ const launchPumpfunTokenAction: Action = { website: "https://sampletoken.com", initialLiquiditySOL: 0.1, slippageBps: 10, - priorityFee: 0.0001 + priorityFee: 0.0001, }, output: { status: "success", signature: "2ZE7Rz...", mint: "7nxQB...", metadataUri: "https://arweave.net/...", - message: "Successfully launched token on Pump.fun" + message: "Successfully launched token on Pump.fun", }, - explanation: "Launch a new token with custom metadata and 0.1 SOL initial liquidity" - } - ] + 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() + 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() + 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() + 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() + slippageBps: z + .number() .min(1) .max(1000) .default(5) .describe("Slippage tolerance in basis points"), - priorityFee: z.number() + priorityFee: z + .number() .min(0.00001) .default(0.00005) - .describe("Priority fee in SOL") + .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); + 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" + message: "Successfully launched token on Pump.fun", }; } catch (error: any) { return { status: "error", - message: `Failed to launch token: ${error.message}` + message: `Failed to launch token: ${error.message}`, }; } - } + }, }; -export default launchPumpfunTokenAction; \ No newline at end of file +export default launchPumpfunTokenAction; diff --git a/src/actions/lendAsset.ts b/src/actions/lendAsset.ts index b4b0885..1072ace 100644 --- a/src/actions/lendAsset.ts +++ b/src/actions/lendAsset.ts @@ -1,7 +1,6 @@ import { Action } from "../types/action"; import { SolanaAgentKit } from "../agent"; import { z } from "zod"; -import { VersionedTransaction } from "@solana/web3.js"; import { lendAsset } from "../tools"; const lendAssetAction: Action = { @@ -12,28 +11,26 @@ const lendAssetAction: Action = { "earn yield", "lend with lulo", "deposit usdc", - "lending" + "lending", ], description: "Lend USDC tokens to earn yield using Lulo protocol", examples: [ [ { input: { - amount: 100 + amount: 100, }, output: { status: "success", signature: "4xKpN2...", - message: "Successfully lent 100 USDC" + message: "Successfully lent 100 USDC", }, - explanation: "Lend 100 USDC to earn yield on Lulo" - } - ] + explanation: "Lend 100 USDC to earn yield on Lulo", + }, + ], ], schema: z.object({ - amount: z.number() - .positive() - .describe("Amount of USDC to lend") + amount: z.number().positive().describe("Amount of USDC to lend"), }), handler: async (agent: SolanaAgentKit, input: Record) => { try { @@ -44,15 +41,15 @@ const lendAssetAction: Action = { return { status: "success", signature: response, - message: `Successfully lent ${amount} USDC` + message: `Successfully lent ${amount} USDC`, }; } catch (error: any) { return { status: "error", - message: `Lending failed: ${error.message}` + message: `Lending failed: ${error.message}`, }; } - } + }, }; -export default lendAssetAction; \ No newline at end of file +export default lendAssetAction; diff --git a/src/actions/mintNFT.ts b/src/actions/mintNFT.ts index 949514c..5c3d691 100644 --- a/src/actions/mintNFT.ts +++ b/src/actions/mintNFT.ts @@ -11,7 +11,7 @@ const mintNFTAction: Action = { "create nft", "mint token", "create token", - "add nft to collection" + "add nft to collection", ], description: `Mint a new NFT in a collection on Solana blockchain.`, examples: [ @@ -20,7 +20,7 @@ const mintNFTAction: Action = { input: { collectionMint: "J1S9H3QjnRtBbbuD4HjPV6RpRhwuk4zKbxsnCHuTgh9w", name: "My NFT", - uri: "https://example.com/nft.json" + uri: "https://example.com/nft.json", }, output: { status: "success", @@ -28,12 +28,12 @@ const mintNFTAction: Action = { mintAddress: "7nE9GvcwsqzYxmJLSrYmSB1V1YoJWVK1KWzAcWAzjXkN", metadata: { name: "My NFT", - uri: "https://example.com/nft.json" + uri: "https://example.com/nft.json", }, - recipient: "7nE9GvcwsqzYxmJLSrYmSB1V1YoJWVK1KWzAcWAzjXkN" + recipient: "7nE9GvcwsqzYxmJLSrYmSB1V1YoJWVK1KWzAcWAzjXkN", }, - explanation: "Mint an NFT to the default wallet" - } + explanation: "Mint an NFT to the default wallet", + }, ], [ { @@ -41,7 +41,7 @@ const mintNFTAction: Action = { collectionMint: "J1S9H3QjnRtBbbuD4HjPV6RpRhwuk4zKbxsnCHuTgh9w", name: "Gift NFT", uri: "https://example.com/gift.json", - recipient: "9aUn5swQzUTRanaaTwmszxiv89cvFwUCjEBv1vZCoT1u" + recipient: "9aUn5swQzUTRanaaTwmszxiv89cvFwUCjEBv1vZCoT1u", }, output: { status: "success", @@ -49,19 +49,19 @@ const mintNFTAction: Action = { mintAddress: "8nE9GvcwsqzYxmJLSrYmSB1V1YoJWVK1KWzAcWAzjXkM", metadata: { name: "Gift NFT", - uri: "https://example.com/gift.json" + uri: "https://example.com/gift.json", }, - recipient: "9aUn5swQzUTRanaaTwmszxiv89cvFwUCjEBv1vZCoT1u" + recipient: "9aUn5swQzUTRanaaTwmszxiv89cvFwUCjEBv1vZCoT1u", }, - explanation: "Mint an NFT to a specific recipient" - } - ] + 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") + recipient: z.string().min(32, "Invalid recipient address"), }), handler: async (agent: SolanaAgentKit, input: Record) => { const result = await mintCollectionNFT( @@ -69,9 +69,9 @@ const mintNFTAction: Action = { new PublicKey(input.collectionMint), { name: input.name, - uri: input.uri + uri: input.uri, }, - input.recipient ? new PublicKey(input.recipient) : undefined + input.recipient ? new PublicKey(input.recipient) : undefined, ); return { @@ -80,11 +80,11 @@ const mintNFTAction: Action = { mintAddress: result.mint.toString(), metadata: { name: input.name, - uri: input.uri + uri: input.uri, }, - recipient: input.recipient || result.mint.toString() + recipient: input.recipient || result.mint.toString(), }; - } + }, }; -export default mintNFTAction; \ No newline at end of file +export default mintNFTAction; diff --git a/src/actions/pythFetchPrice.ts b/src/actions/pythFetchPrice.ts index 1c1f9e0..02ff1d8 100644 --- a/src/actions/pythFetchPrice.ts +++ b/src/actions/pythFetchPrice.ts @@ -1,8 +1,6 @@ import { Action } from "../types/action"; import { SolanaAgentKit } from "../agent"; import { z } from "zod"; -import { PriceServiceConnection } from "@pythnetwork/price-service-client"; -import BN from "bn.js"; import { pythFetchPrice } from "../tools"; const pythFetchPriceAction: Action = { @@ -13,28 +11,29 @@ const pythFetchPriceAction: Action = { "pyth oracle price", "fetch from pyth", "pyth price feed", - "oracle price" + "oracle price", ], description: "Fetch the current price from a Pyth oracle price feed", examples: [ [ { input: { - priceFeedId: "Gnt27xtC473ZT2Mw5u8wZ68Z3gULkSTb5DuxJy7eJotD" // SOL/USD price feed + priceFeedId: "Gnt27xtC473ZT2Mw5u8wZ68Z3gULkSTb5DuxJy7eJotD", // SOL/USD price feed }, output: { status: "success", price: "23.45", - message: "Current price: $23.45" + message: "Current price: $23.45", }, - explanation: "Get the current SOL/USD price from Pyth oracle" - } - ] + explanation: "Get the current SOL/USD price from Pyth oracle", + }, + ], ], schema: z.object({ - priceFeedId: z.string() + priceFeedId: z + .string() .min(1) - .describe("The Pyth price feed ID to fetch the price from") + .describe("The Pyth price feed ID to fetch the price from"), }), handler: async (_agent: SolanaAgentKit, input: Record) => { try { @@ -45,15 +44,15 @@ const pythFetchPriceAction: Action = { return { status: "success", price: priceStr, - message: `Current price: $${priceStr}` + message: `Current price: $${priceStr}`, }; } catch (error: any) { return { status: "error", - message: `Failed to fetch price from Pyth: ${error.message}` + message: `Failed to fetch price from Pyth: ${error.message}`, }; } - } + }, }; -export default pythFetchPriceAction; \ No newline at end of file +export default pythFetchPriceAction; diff --git a/src/actions/raydiumCreateAmmV4.ts b/src/actions/raydiumCreateAmmV4.ts index afda966..412ef18 100644 --- a/src/actions/raydiumCreateAmmV4.ts +++ b/src/actions/raydiumCreateAmmV4.ts @@ -1,15 +1,6 @@ import { Action } from "../types/action"; import { SolanaAgentKit } from "../agent"; import { z } from "zod"; -import { - AMM_V4, - FEE_DESTINATION_ID, - MARKET_STATE_LAYOUT_V3, - OPEN_BOOK_PROGRAM, - Raydium, - TxVersion, -} from "@raydium-io/raydium-sdk-v2"; -import { MintLayout, TOKEN_PROGRAM_ID } from "@solana/spl-token"; import { PublicKey } from "@solana/web3.js"; import BN from "bn.js"; import { raydiumCreateAmmV4 } from "../tools"; @@ -22,49 +13,51 @@ const raydiumCreateAmmV4Action: Action = { "initialize raydium v4 amm", "create raydium v4 market maker", "setup raydium v4 pool", - "create raydium v4 trading pair" + "create raydium v4 trading pair", ], - description: "Create a new AMM V4 pool on Raydium with advanced features and improved efficiency", + description: + "Create a new AMM V4 pool on Raydium with advanced features and improved efficiency", examples: [ [ { input: { - baseMint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", // USDC - quoteMint: "So11111111111111111111111111111111111111112", // SOL + baseMint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", // USDC + quoteMint: "So11111111111111111111111111111111111111112", // SOL baseAmount: 1000, quoteAmount: 10, - startPrice: 100, // 1 SOL = 100 USDC - openTime: 1672531200 // Unix timestamp + startPrice: 100, // 1 SOL = 100 USDC + openTime: 1672531200, // Unix timestamp }, output: { status: "success", signature: "2ZE7Rz...", poolId: "7nxQB...", - message: "Successfully created Raydium AMM V4 pool" + message: "Successfully created Raydium AMM V4 pool", }, - explanation: "Create a USDC-SOL V4 pool with initial liquidity and price" - } - ] + 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() + 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() + quoteAmount: z + .number() .positive() .describe("Initial quote token amount to provide as liquidity"), - startPrice: z.number() + startPrice: z + .number() .positive() .describe("Initial price of quote token in base token units"), - openTime: z.number() + openTime: z + .number() .positive() - .describe("Unix timestamp when trading should start") + .describe("Unix timestamp when trading should start"), }), handler: async (agent: SolanaAgentKit, input: Record) => { try { @@ -73,20 +66,26 @@ const raydiumCreateAmmV4Action: Action = { const quoteAmount = new BN(input.quoteAmount); const startTime = new BN(input.startTime); - const txId = await raydiumCreateAmmV4(agent, marketId, baseAmount, quoteAmount, startTime); + const txId = await raydiumCreateAmmV4( + agent, + marketId, + baseAmount, + quoteAmount, + startTime, + ); return { status: "success", signature: txId, - message: "Successfully created Raydium AMM V4 pool" + message: "Successfully created Raydium AMM V4 pool", }; } catch (error: any) { return { status: "error", - message: `Failed to create AMM V4 pool: ${error.message}` + message: `Failed to create AMM V4 pool: ${error.message}`, }; } - } + }, }; -export default raydiumCreateAmmV4Action; \ No newline at end of file +export default raydiumCreateAmmV4Action; diff --git a/src/actions/raydiumCreateCpmm.ts b/src/actions/raydiumCreateCpmm.ts index 0e6795c..28fe82a 100644 --- a/src/actions/raydiumCreateCpmm.ts +++ b/src/actions/raydiumCreateCpmm.ts @@ -1,13 +1,6 @@ import { Action } from "../types/action"; import { SolanaAgentKit } from "../agent"; import { z } from "zod"; -import { - CREATE_CPMM_POOL_FEE_ACC, - CREATE_CPMM_POOL_PROGRAM, - Raydium, - TxVersion, -} from "@raydium-io/raydium-sdk-v2"; -import { MintLayout } from "@solana/spl-token"; import { PublicKey } from "@solana/web3.js"; import BN from "bn.js"; import { raydiumCreateCpmm } from "../tools"; @@ -20,45 +13,46 @@ const raydiumCreateCpmmAction: Action = { "initialize raydium amm", "create constant product market maker", "setup raydium cpmm", - "create raydium trading pair" + "create raydium trading pair", ], - description: "Create a new Constant Product Market Maker (CPMM) pool on Raydium", + description: + "Create a new Constant Product Market Maker (CPMM) pool on Raydium", examples: [ [ { input: { - baseMint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", // USDC - quoteMint: "So11111111111111111111111111111111111111112", // SOL + baseMint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", // USDC + quoteMint: "So11111111111111111111111111111111111111112", // SOL baseAmount: 1000, quoteAmount: 10, - startTime: 1672531200 // Unix timestamp + startTime: 1672531200, // Unix timestamp }, output: { status: "success", signature: "2ZE7Rz...", poolId: "7nxQB...", - message: "Successfully created Raydium CPMM pool" + message: "Successfully created Raydium CPMM pool", }, - explanation: "Create a USDC-SOL pool with initial liquidity of 1000 USDC and 10 SOL" - } - ] + 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() + 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() + quoteAmount: z + .number() .positive() .describe("Initial quote token amount to provide as liquidity"), - startTime: z.number() + startTime: z + .number() .positive() - .describe("Unix timestamp when trading should start") + .describe("Unix timestamp when trading should start"), }), handler: async (agent: SolanaAgentKit, input: Record) => { try { @@ -69,20 +63,28 @@ const raydiumCreateCpmmAction: Action = { const mintBAmount = new BN(input.quoteAmount); const startTime = new BN(input.startTime); - const txId = await raydiumCreateCpmm(agent, mintA, mintB, configId, mintAAmount, mintBAmount, startTime); + const txId = await raydiumCreateCpmm( + agent, + mintA, + mintB, + configId, + mintAAmount, + mintBAmount, + startTime, + ); return { status: "success", signature: txId, - message: "Successfully created Raydium CPMM pool" + message: "Successfully created Raydium CPMM pool", }; } catch (error: any) { return { status: "error", - message: `Failed to create CPMM pool: ${error.message}` + message: `Failed to create CPMM pool: ${error.message}`, }; } - } + }, }; -export default raydiumCreateCpmmAction; \ No newline at end of file +export default raydiumCreateCpmmAction; diff --git a/src/actions/registerDomain.ts b/src/actions/registerDomain.ts index 50b398a..b4a6d8d 100644 --- a/src/actions/registerDomain.ts +++ b/src/actions/registerDomain.ts @@ -1,10 +1,6 @@ import { Action } from "../types/action"; import { SolanaAgentKit } from "../agent"; import { z } from "zod"; -import { Transaction } from "@solana/web3.js"; -import { registerDomainNameV2 } from "@bonfida/spl-name-service"; -import { getAssociatedTokenAddressSync } from "@solana/spl-token"; -import { TOKENS } from "../constants"; import { registerDomain } from "../tools"; const registerDomainAction: Action = { @@ -15,7 +11,7 @@ const registerDomainAction: Action = { "get domain name", "register .sol", "purchase domain", - "domain registration" + "domain registration", ], description: "Register a .sol domain name using Bonfida Name Service", examples: [ @@ -23,26 +19,25 @@ const registerDomainAction: Action = { { input: { name: "mydomain", - spaceKB: 1 + spaceKB: 1, }, output: { status: "success", signature: "2ZE7Rz...", - message: "Successfully registered mydomain.sol" + message: "Successfully registered mydomain.sol", }, - explanation: "Register a new .sol domain with 1KB storage space" - } - ] + 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() + 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)") + .describe("Space allocation in KB (max 10KB)"), }), handler: async (agent: SolanaAgentKit, input: Record) => { try { @@ -54,15 +49,15 @@ const registerDomainAction: Action = { return { status: "success", signature, - message: `Successfully registered ${name}.sol` + message: `Successfully registered ${name}.sol`, }; } catch (error: any) { return { status: "error", - message: `Domain registration failed: ${error.message}` + message: `Domain registration failed: ${error.message}`, }; } - } + }, }; -export default registerDomainAction; \ No newline at end of file +export default registerDomainAction; diff --git a/src/actions/requestFunds.ts b/src/actions/requestFunds.ts index 49e4c2b..116c15e 100644 --- a/src/actions/requestFunds.ts +++ b/src/actions/requestFunds.ts @@ -10,7 +10,7 @@ const requestFundsAction: Action = { "get test sol", "use faucet", "request test tokens", - "get devnet sol" + "get devnet sol", ], description: "Request SOL from Solana faucet (devnet/testnet only)", examples: [ @@ -20,11 +20,11 @@ const requestFundsAction: Action = { output: { status: "success", message: "Successfully requested faucet funds", - network: "devnet.solana.com" + network: "devnet.solana.com", }, - explanation: "Request SOL from the devnet faucet" - } - ] + explanation: "Request SOL from the devnet faucet", + }, + ], ], schema: z.object({}), // No input parameters required handler: async (agent: SolanaAgentKit, _input: Record) => { @@ -33,9 +33,9 @@ const requestFundsAction: Action = { return { status: "success", message: "Successfully requested faucet funds", - network: agent.connection.rpcEndpoint.split("/")[2] + network: agent.connection.rpcEndpoint.split("/")[2], }; - } + }, }; -export default requestFundsAction; \ No newline at end of file +export default requestFundsAction; diff --git a/src/actions/resolveDomain.ts b/src/actions/resolveDomain.ts index 1d6307a..aca514e 100644 --- a/src/actions/resolveDomain.ts +++ b/src/actions/resolveDomain.ts @@ -1,7 +1,6 @@ import { Action } from "../types/action"; import { SolanaAgentKit } from "../agent"; import { z } from "zod"; -import { TldParser } from "@onsol/tldparser"; import { resolveAllDomains } from "../tools"; const resolveDomainAction: Action = { @@ -11,25 +10,25 @@ const resolveDomainAction: Action = { "lookup domain", "get domain owner", "check domain", - "find domain owner" + "find domain owner", ], description: "Resolve a Solana domain name to get its owner's public key", examples: [ [ { input: { - domain: "example.sol" + domain: "example.sol", }, output: { status: "success", - owner: "7nxQB..." + owner: "7nxQB...", }, - explanation: "Resolve a .sol domain name to get the owner's public key" - } - ] + 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") + domain: z.string().min(1).describe("The domain name to resolve"), }), handler: async (agent: SolanaAgentKit, input: Record) => { try { @@ -38,15 +37,15 @@ const resolveDomainAction: Action = { return { status: "success", owner: tld, - message: `Successfully resolved domain ${domain}` + message: `Successfully resolved domain ${domain}`, }; } catch (error: any) { return { status: "error", - message: `Failed to resolve domain: ${error.message}` + message: `Failed to resolve domain: ${error.message}`, }; } - } + }, }; -export default resolveDomainAction; \ No newline at end of file +export default resolveDomainAction; diff --git a/src/actions/resolveSolDomain.ts b/src/actions/resolveSolDomain.ts index ce13fe3..26ebae0 100644 --- a/src/actions/resolveSolDomain.ts +++ b/src/actions/resolveSolDomain.ts @@ -1,7 +1,6 @@ import { Action } from "../types/action"; import { SolanaAgentKit } from "../agent"; import { z } from "zod"; -import { resolve } from "@bonfida/spl-name-service"; import { resolveSolDomain } from "../tools"; const resolveSolDomainAction: Action = { @@ -12,47 +11,49 @@ const resolveSolDomainAction: Action = { "get sol domain owner", "check sol domain", "find sol domain owner", - "resolve .sol" + "resolve .sol", ], - description: "Resolve a .sol domain to its corresponding Solana wallet address using Bonfida Name Service", + description: + "Resolve a .sol domain to its corresponding Solana wallet address using Bonfida Name Service", examples: [ [ { input: { - domain: "vitalik.sol" + domain: "vitalik.sol", }, output: { status: "success", owner: "7nxQB...", - message: "Successfully resolved vitalik.sol" + message: "Successfully resolved vitalik.sol", }, - explanation: "Resolve a .sol domain to get the owner's wallet address" - } - ] + explanation: "Resolve a .sol domain to get the owner's wallet address", + }, + ], ], schema: z.object({ - domain: z.string() + domain: z + .string() .min(1) - .describe("The .sol domain to resolve (with or without .sol suffix)") + .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) + const res = await resolveSolDomain(agent, domain); return { status: "success", owner: res.toString(), - message: `Successfully resolved ${res}` + message: `Successfully resolved ${res}`, }; } catch (error: any) { return { status: "error", - message: `Failed to resolve domain: ${error.message}` + message: `Failed to resolve domain: ${error.message}`, }; } - } + }, }; -export default resolveSolDomainAction; \ No newline at end of file +export default resolveSolDomainAction; diff --git a/src/actions/stakeWithJup.ts b/src/actions/stakeWithJup.ts index 7110a72..630f024 100644 --- a/src/actions/stakeWithJup.ts +++ b/src/actions/stakeWithJup.ts @@ -1,8 +1,7 @@ import { Action } from "../types/action"; import { SolanaAgentKit } from "../agent"; import { z } from "zod"; -import { VersionedTransaction } from "@solana/web3.js"; -import { stakeWithJup, trade } from "../tools"; +import { stakeWithJup } from "../tools"; const stakeWithJupAction: Action = { name: "solana_stake_with_jup", @@ -12,46 +11,45 @@ const stakeWithJupAction: Action = { "jup staking", "stake with jup", "liquid staking", - "get jupsol" + "get jupsol", ], - description: "Stake SOL tokens with Jupiter's liquid staking protocol to receive jupSOL", + description: + "Stake SOL tokens with Jupiter's liquid staking protocol to receive jupSOL", examples: [ [ { input: { - amount: 1.5 + amount: 1.5, }, output: { status: "success", signature: "5KtPn3...", - message: "Successfully staked 1.5 SOL for jupSOL" + message: "Successfully staked 1.5 SOL for jupSOL", }, - explanation: "Stake 1.5 SOL to receive jupSOL tokens" - } - ] + explanation: "Stake 1.5 SOL to receive jupSOL tokens", + }, + ], ], schema: z.object({ - amount: z.number() - .positive() - .describe("Amount of SOL to stake") + 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) + + const res = await stakeWithJup(agent, amount); return { status: "success", res, - message: `Successfully staked ${amount} SOL for jupSOL` + message: `Successfully staked ${amount} SOL for jupSOL`, }; } catch (error: any) { return { status: "error", - message: `jupSOL staking failed: ${error.message}` + message: `jupSOL staking failed: ${error.message}`, }; } - } + }, }; -export default stakeWithJupAction; \ No newline at end of file +export default stakeWithJupAction; diff --git a/src/actions/trade.ts b/src/actions/trade.ts index bb27f79..397f0ca 100644 --- a/src/actions/trade.ts +++ b/src/actions/trade.ts @@ -11,7 +11,7 @@ const tradeAction: Action = { "exchange tokens", "trade tokens", "convert tokens", - "swap sol" + "swap sol", ], description: `This tool can be used to swap tokens to another token (It uses Jupiter Exchange).`, examples: [ @@ -19,18 +19,19 @@ const tradeAction: Action = { { input: { outputMint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", - inputAmount: 1 + inputAmount: 1, }, output: { status: "success", message: "Trade executed successfully", - transaction: "5UfgJ5vVZxUxefDGqzqkVLHzHxVTyYH9StYyHKgvHYmXJgqJKxEqy9k4Rz9LpXrHF9kUZB7", + transaction: + "5UfgJ5vVZxUxefDGqzqkVLHzHxVTyYH9StYyHKgvHYmXJgqJKxEqy9k4Rz9LpXrHF9kUZB7", inputAmount: 1, inputToken: "SOL", - outputToken: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v" + outputToken: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", }, - explanation: "Swap 1 SOL for USDC" - } + explanation: "Swap 1 SOL for USDC", + }, ], [ { @@ -38,34 +39,36 @@ const tradeAction: Action = { outputMint: "So11111111111111111111111111111111111111112", inputAmount: 100, inputMint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", - slippageBps: 100 + slippageBps: 100, }, output: { status: "success", message: "Trade executed successfully", - transaction: "4VfgJ5vVZxUxefDGqzqkVLHzHxVTyYH9StYyHKgvHYmXJgqJKxEqy9k4Rz9LpXrHF9kUZB7", + transaction: + "4VfgJ5vVZxUxefDGqzqkVLHzHxVTyYH9StYyHKgvHYmXJgqJKxEqy9k4Rz9LpXrHF9kUZB7", inputAmount: 100, inputToken: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", - outputToken: "So11111111111111111111111111111111111111112" + outputToken: "So11111111111111111111111111111111111111112", }, - explanation: "Swap 100 USDC for SOL with 1% slippage" - } - ] + 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() + slippageBps: z.number().min(0).max(10000).optional(), }), handler: async (agent: SolanaAgentKit, input: Record) => { - const tx = await trade(agent, + const tx = await trade( + agent, new PublicKey(input.outputMint), input.inputAmount, input.inputMint ? new PublicKey(input.inputMint) : new PublicKey("So11111111111111111111111111111111111111112"), - input.slippageBps + input.slippageBps, ); return { @@ -74,9 +77,9 @@ const tradeAction: Action = { transaction: tx, inputAmount: input.inputAmount, inputToken: input.inputMint || "SOL", - outputToken: input.outputMint + outputToken: input.outputMint, }; - } + }, }; -export default tradeAction; \ No newline at end of file +export default tradeAction; diff --git a/src/actions/transfer.ts b/src/actions/transfer.ts index 3769481..cc8a648 100644 --- a/src/actions/transfer.ts +++ b/src/actions/transfer.ts @@ -11,7 +11,7 @@ const transferAction: Action = { "transfer funds", "send money", "send sol", - "transfer tokens" + "transfer tokens", ], description: `Transfer tokens or SOL to another address (also called as wallet address).`, examples: [ @@ -19,7 +19,7 @@ const transferAction: Action = { { input: { to: "8x2dR8Mpzuz2YqyZyZjUbYWKSWesBo5jMx2Q9Y86udVk", - amount: 1 + amount: 1, }, output: { status: "success", @@ -27,17 +27,18 @@ const transferAction: Action = { amount: 1, recipient: "8x2dR8Mpzuz2YqyZyZjUbYWKSWesBo5jMx2Q9Y86udVk", token: "SOL", - transaction: "5UfgJ5vVZxUxefDGqzqkVLHzHxVTyYH9StYyHKgvHYmXJgqJKxEqy9k4Rz9LpXrHF9kUZB7" + transaction: + "5UfgJ5vVZxUxefDGqzqkVLHzHxVTyYH9StYyHKgvHYmXJgqJKxEqy9k4Rz9LpXrHF9kUZB7", }, - explanation: "Transfer 1 SOL to the recipient address" - } + explanation: "Transfer 1 SOL to the recipient address", + }, ], [ { input: { to: "8x2dR8Mpzuz2YqyZyZjUbYWKSWesBo5jMx2Q9Y86udVk", amount: 100, - mint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v" + mint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", }, output: { status: "success", @@ -45,22 +46,23 @@ const transferAction: Action = { amount: 100, recipient: "8x2dR8Mpzuz2YqyZyZjUbYWKSWesBo5jMx2Q9Y86udVk", token: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", - transaction: "4VfgJ5vVZxUxefDGqzqkVLHzHxVTyYH9StYyHKgvHYmXJgqJKxEqy9k4Rz9LpXrHF9kUZB7" + transaction: + "4VfgJ5vVZxUxefDGqzqkVLHzHxVTyYH9StYyHKgvHYmXJgqJKxEqy9k4Rz9LpXrHF9kUZB7", }, - explanation: "Transfer 100 USDC tokens to the recipient address" - } - ] + 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() + 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); + const tx = await transfer(agent, recipient, input.amount, mintAddress); return { status: "success", @@ -68,9 +70,9 @@ const transferAction: Action = { amount: input.amount, recipient: input.to, token: input.mint || "SOL", - transaction: tx + transaction: tx, }; - } + }, }; -export default transferAction; \ No newline at end of file +export default transferAction; diff --git a/src/langchain/index.ts b/src/langchain/index.ts index 4e64556..09df522 100644 --- a/src/langchain/index.ts +++ b/src/langchain/index.ts @@ -10,7 +10,6 @@ import { create_image } from "../tools/create_image"; import { BN } from "@coral-xyz/anchor"; import { FEE_TIERS } from "../tools"; import { toJSON } from "../utils/toJSON"; -import { wrapLangChainTool } from "../utils/langchainWrapper"; import deployTokenAction from "../actions/deployToken"; import balanceAction from "../actions/balance"; import transferAction from "../actions/transfer"; @@ -41,7 +40,7 @@ export class SolanaBalanceTool extends Tool { return JSON.stringify({ status: "error", message: error.message, - code: error.code || "UNKNOWN_ERROR" + code: error.code || "UNKNOWN_ERROR", }); } } @@ -69,7 +68,7 @@ export class SolanaTransferTool extends Tool { return JSON.stringify({ status: "error", message: error.message, - code: error.code || "UNKNOWN_ERROR" + code: error.code || "UNKNOWN_ERROR", }); } } @@ -97,7 +96,7 @@ export class SolanaDeployTokenTool extends Tool { return JSON.stringify({ status: "error", message: error.message, - code: error.code || "UNKNOWN_ERROR" + code: error.code || "UNKNOWN_ERROR", }); } } @@ -125,7 +124,7 @@ export class SolanaDeployCollectionTool extends Tool { return JSON.stringify({ status: "error", message: error.message, - code: error.code || "UNKNOWN_ERROR" + code: error.code || "UNKNOWN_ERROR", }); } } @@ -153,7 +152,7 @@ export class SolanaMintNFTTool extends Tool { return JSON.stringify({ status: "error", message: error.message, - code: error.code || "UNKNOWN_ERROR" + code: error.code || "UNKNOWN_ERROR", }); } } @@ -181,7 +180,7 @@ export class SolanaTradeTool extends Tool { return JSON.stringify({ status: "error", message: error.message, - code: error.code || "UNKNOWN_ERROR" + code: error.code || "UNKNOWN_ERROR", }); } } @@ -206,7 +205,7 @@ export class SolanaRequestFundsTool extends Tool { return JSON.stringify({ status: "error", message: error.message, - code: error.code || "UNKNOWN_ERROR" + code: error.code || "UNKNOWN_ERROR", }); } } diff --git a/src/types/action.ts b/src/types/action.ts index d715177..df4c0be 100644 --- a/src/types/action.ts +++ b/src/types/action.ts @@ -13,7 +13,10 @@ export interface ActionExample { /** * Handler function type for executing the action */ -export type Handler = (agent: SolanaAgentKit, input: Record) => Promise>; +export type Handler = ( + agent: SolanaAgentKit, + input: Record, +) => Promise>; /** * Main Action interface inspired by ELIZA @@ -50,4 +53,4 @@ export interface Action { * Function that executes the action */ handler: Handler; -} \ No newline at end of file +} diff --git a/src/utils/actionExecutor.ts b/src/utils/actionExecutor.ts index 0bb8f4b..e152ecb 100644 --- a/src/utils/actionExecutor.ts +++ b/src/utils/actionExecutor.ts @@ -7,9 +7,10 @@ import { actions } from "../actions"; */ 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) + return actions.find( + (action) => + action.name.toLowerCase() === normalizedQuery || + action.similes.some((simile) => simile.toLowerCase() === normalizedQuery), ); } @@ -19,18 +20,18 @@ export function findAction(query: string): Action | undefined { export async function executeAction( action: Action, agent: SolanaAgentKit, - input: Record + 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 + ...result, }; } catch (error: any) { // Handle Zod validation errors specially @@ -39,14 +40,14 @@ export async function executeAction( status: "error", message: "Validation error", details: error.errors, - code: "VALIDATION_ERROR" + code: "VALIDATION_ERROR", }; } - + return { status: "error", message: error.message, - code: error.code || "EXECUTION_ERROR" + code: error.code || "EXECUTION_ERROR", }; } } @@ -57,11 +58,11 @@ export async function executeAction( export function getActionExamples(action: Action): string { return action.examples .flat() - .map(example => { + .map((example) => { return `Input: ${JSON.stringify(example.input, null, 2)} Output: ${JSON.stringify(example.output, null, 2)} Explanation: ${example.explanation} ---`; }) .join("\n"); -} \ No newline at end of file +} diff --git a/src/utils/langchainWrapper.ts b/src/utils/langchainWrapper.ts deleted file mode 100644 index c7bf500..0000000 --- a/src/utils/langchainWrapper.ts +++ /dev/null @@ -1,96 +0,0 @@ -import { Tool } from "langchain/tools"; -import { Action } from "../types/action"; -import { SolanaAgentKit } from "../agent"; -import { z } from "zod"; - -/** - * Convert a LangChain tool to our Action interface - */ -export function wrapLangChainTool(tool: Tool, agent: SolanaAgentKit): Action { - // Parse the description to extract input parameters - const inputParams = parseToolDescription(tool.description); - - return { - name: tool.name, - similes: [], // LangChain tools don't have similes - description: tool.description, - examples: [], // LangChain tools don't have examples - schema: createZodSchema(inputParams), - handler: async (agent: SolanaAgentKit, input: Record) => { - const result = await tool.call(JSON.stringify(input)); - try { - return JSON.parse(result); - } catch { - return { result }; - } - } - }; -} - -/** - * Parse tool description to extract input parameters - */ -function parseToolDescription(description: string): Array<{name: string, type: string, required: boolean}> { - const lines = description.split('\n'); - const params: Array<{name: string, type: string, required: boolean}> = []; - - let inInputsSection = false; - for (const line of lines) { - const trimmed = line.trim(); - - if (trimmed === 'Inputs:' || trimmed === 'Inputs (input is a JSON string):') { - inInputsSection = true; - continue; - } - - if (inInputsSection && trimmed) { - // Match patterns like: name: string, eg "value" (required) - const match = trimmed.match(/(\w+):\s*([\w\[\]]+)(?:,\s*eg[:\s]+"[^"]+")?(?:\s*\((required|optional)\))?/); - if (match) { - params.push({ - name: match[1], - type: match[2], - required: match[3] === 'required' - }); - } - } - } - - return params; -} - -/** - * Create a Zod schema from parsed parameters - */ -function createZodSchema(params: Array<{name: string, type: string, required: boolean}>): z.ZodType { - const schemaObj: Record> = {}; - - for (const param of params) { - let schema: z.ZodType; - - switch (param.type.toLowerCase()) { - case 'string': - schema = z.string(); - break; - case 'number': - schema = z.number(); - break; - case 'boolean': - schema = z.boolean(); - break; - case 'string[]': - schema = z.array(z.string()); - break; - default: - schema = z.any(); - } - - if (!param.required) { - schema = schema.optional(); - } - - schemaObj[param.name] = schema; - } - - return z.object(schemaObj); -} \ No newline at end of file diff --git a/test/index.ts b/test/index.ts index 5aae92e..d60f8d2 100644 --- a/test/index.ts +++ b/test/index.ts @@ -7,8 +7,6 @@ import { ChatOpenAI } from "@langchain/openai"; import * as dotenv from "dotenv"; import * as fs from "fs"; import * as readline from "readline"; -import { Tool } from "@langchain/core/tools"; -import { Action } from "../src/types/action"; dotenv.config(); From 1b601b839d4f5a13762a4f4d03567048e83a2a2f Mon Sep 17 00:00:00 2001 From: Fahri Bilici <28020526+FahriBilici@users.noreply.github.com> Date: Tue, 31 Dec 2024 01:17:53 +0100 Subject: [PATCH 08/11] adapting langchain to use actions --- src/actions/getAllDomainsTLDs.ts | 2 +- src/actions/getMainAllDomainsDomain.ts | 2 +- src/actions/getPrimaryDomain.ts | 2 +- src/actions/getTokenData.ts | 2 +- src/actions/pythFetchPrice.ts | 4 +- src/actions/raydiumCreateAmmV4.ts | 2 +- src/langchain/index.ts | 341 ++++++++++--------------- 7 files changed, 138 insertions(+), 217 deletions(-) diff --git a/src/actions/getAllDomainsTLDs.ts b/src/actions/getAllDomainsTLDs.ts index 34dbd19..1eae1aa 100644 --- a/src/actions/getAllDomainsTLDs.ts +++ b/src/actions/getAllDomainsTLDs.ts @@ -4,7 +4,7 @@ import { z } from "zod"; import { getAllDomainsTLDs } from "../tools"; const getAllDomainsTLDsAction: Action = { - name: "solana_get_all_domains_tlds", + name: "solana_get_all_tlds", similes: [ "list domain tlds", "get domain extensions", diff --git a/src/actions/getMainAllDomainsDomain.ts b/src/actions/getMainAllDomainsDomain.ts index ad57161..de35ee2 100644 --- a/src/actions/getMainAllDomainsDomain.ts +++ b/src/actions/getMainAllDomainsDomain.ts @@ -5,7 +5,7 @@ import { PublicKey } from "@solana/web3.js"; import { getMainAllDomainsDomain } from "../tools"; const getMainAllDomainsDomainAction: Action = { - name: "solana_get_main_all_domains_domain", + name: "solana_get_main_domain", similes: [ "get main domain", "fetch primary domain", diff --git a/src/actions/getPrimaryDomain.ts b/src/actions/getPrimaryDomain.ts index d903c04..f678a31 100644 --- a/src/actions/getPrimaryDomain.ts +++ b/src/actions/getPrimaryDomain.ts @@ -5,7 +5,7 @@ import { PublicKey } from "@solana/web3.js"; import { getPrimaryDomain } from "../tools"; const getPrimaryDomainAction: Action = { - name: "solana_get_primary_domain", + name: "solana_get_domain", similes: [ "get primary domain", "lookup primary domain", diff --git a/src/actions/getTokenData.ts b/src/actions/getTokenData.ts index 57b288b..f416a0c 100644 --- a/src/actions/getTokenData.ts +++ b/src/actions/getTokenData.ts @@ -6,7 +6,7 @@ import { JupiterTokenData } from "../types"; import { getTokenAddressFromTicker, getTokenDataByAddress } from "../tools"; const getTokenDataAction: Action = { - name: "solana_get_token_data", + name: "solana_token_data", similes: [ "get token info", "token details", diff --git a/src/actions/pythFetchPrice.ts b/src/actions/pythFetchPrice.ts index 02ff1d8..ed3d5be 100644 --- a/src/actions/pythFetchPrice.ts +++ b/src/actions/pythFetchPrice.ts @@ -37,10 +37,8 @@ const pythFetchPriceAction: Action = { }), handler: async (_agent: SolanaAgentKit, input: Record) => { try { - const priceFeedId = input.priceFeedId as string; - + const priceFeedId = input.tokenId as string; const priceStr = await pythFetchPrice(priceFeedId); - return { status: "success", price: priceStr, diff --git a/src/actions/raydiumCreateAmmV4.ts b/src/actions/raydiumCreateAmmV4.ts index 412ef18..7d4e37b 100644 --- a/src/actions/raydiumCreateAmmV4.ts +++ b/src/actions/raydiumCreateAmmV4.ts @@ -6,7 +6,7 @@ import BN from "bn.js"; import { raydiumCreateAmmV4 } from "../tools"; const raydiumCreateAmmV4Action: Action = { - name: "solana_raydium_create_amm_v4", + name: "raydium_create_ammV4", similes: [ "create raydium v4 pool", "setup raydium v4 liquidity pool", diff --git a/src/langchain/index.ts b/src/langchain/index.ts index 09df522..75aadb8 100644 --- a/src/langchain/index.ts +++ b/src/langchain/index.ts @@ -8,7 +8,7 @@ import { } from "../index"; import { create_image } from "../tools/create_image"; import { BN } from "@coral-xyz/anchor"; -import { FEE_TIERS } from "../tools"; +import { FEE_TIERS, getMainAllDomainsDomain } from "../tools"; import { toJSON } from "../utils/toJSON"; import deployTokenAction from "../actions/deployToken"; import balanceAction from "../actions/balance"; @@ -17,6 +17,25 @@ import deployCollectionAction from "../actions/deployCollection"; import mintNFTAction from "../actions/mintNFT"; import tradeAction from "../actions/trade"; import requestFundsAction from "../actions/requestFunds"; +import fetchPriceAction from "../actions/fetchPrice"; +import registerDomainAction from "../actions/registerDomain"; +import resolveDomainAction from "../actions/resolveDomain"; +import getPrimaryDomainAction from "../actions/getPrimaryDomain"; +import launchPumpfunTokenAction from "../actions/launchPumpfunToken"; +import createImageAction from "../actions/createImage"; +import lendAssetAction from "../actions/lendAsset"; +import getTPSAction from "../actions/getTPS"; +import createOrcaSingleSidedWhirlpoolAction from "../actions/createOrcaSingleSidedWhirlpool"; +import raydiumCreateCpmmAction from "../actions/raydiumCreateCpmm"; +import pythFetchPriceAction from "../actions/pythFetchPrice"; +import getOwnedDomainsForTLDAction from "../actions/getOwnedDomainsForTLD"; +import createGibworkTaskAction from "../actions/createGibworkTask"; +import getTokenDataAction from "../actions/getTokenData"; +import stakeWithJupAction from "../actions/stakeWithJup"; +import resolveSolDomainAction from "../actions/resolveSolDomain"; +import getAllDomainsTLDsAction from "../actions/getAllDomainsTLDs"; +import getMainAllDomainsDomainAction from "../actions/getMainAllDomainsDomain"; +import raydiumCreateAmmV4Action from "../actions/raydiumCreateAmmV4"; export class SolanaBalanceTool extends Tool { private action = balanceAction; @@ -212,13 +231,9 @@ export class SolanaRequestFundsTool extends Tool { } export class SolanaRegisterDomainTool extends Tool { - name = "solana_register_domain"; - description = `Register a .sol domain name for your wallet. - - Inputs: - name: string, eg "pumpfun.sol" (required) - spaceKB: number, eg 1 (optional, default is 1) - `; + private action = registerDomainAction + name = this.action.name; + description = this.action.description; constructor(private solanaKit: SolanaAgentKit) { super(); @@ -241,10 +256,7 @@ export class SolanaRegisterDomainTool extends Tool { const parsedInput = toJSON(input); this.validateInput(parsedInput); - const tx = await this.solanaKit.registerDomain( - parsedInput.name, - parsedInput.spaceKB || 1, - ); + const tx = await this.action.handler(this.solanaKit, parsedInput); return JSON.stringify({ status: "success", @@ -264,14 +276,9 @@ export class SolanaRegisterDomainTool extends Tool { } export class SolanaResolveDomainTool extends Tool { - name = "solana_resolve_domain"; - description = `Resolve ONLY .sol domain names to a Solana PublicKey. - This tool is exclusively for .sol domains. - DO NOT use this for other domain types like .blink, .bonk, etc. - - Inputs: - domain: string, eg "pumpfun.sol" (required) - `; + private action = resolveSolDomainAction; + name = this.action.name; + description = this.action.description; constructor(private solanaKit: SolanaAgentKit) { super(); @@ -279,8 +286,8 @@ export class SolanaResolveDomainTool extends Tool { protected async _call(input: string): Promise { try { - const domain = input.trim(); - const publicKey = await this.solanaKit.resolveSolDomain(domain); + const domain = { domain: input.trim() }; + const publicKey = await this.action.handler(this.solanaKit, domain); return JSON.stringify({ status: "success", @@ -298,12 +305,9 @@ export class SolanaResolveDomainTool extends Tool { } export class SolanaGetDomainTool extends Tool { - name = "solana_get_domain"; - description = `Retrieve the .sol domain associated for a given account address. - - Inputs: - account: string, eg "4Be9CvxqHW6BYiRAxW9Q3xu1ycTMWaL5z8NX4HR3ha7t" (required) - `; + private action = getPrimaryDomainAction; + name = this.action.name; + description = this.action.description; constructor(private solanaKit: SolanaAgentKit) { super(); @@ -343,18 +347,10 @@ export class SolanaGetWalletAddressTool extends Tool { } export class SolanaPumpfunTokenLaunchTool extends Tool { - name = "solana_launch_pumpfun_token"; + private action = launchPumpfunTokenAction; + name = this.action.name; - description = `This tool can be used to launch a token on Pump.fun, - do not use this tool for any other purpose, or for creating SPL tokens. - If the user asks you to chose the parameters, you should generate valid values. - For generating the image, you can use the solana_create_image tool. - - Inputs: - tokenName: string, eg "PumpFun Token", - tokenTicker: string, eg "PUMP", - description: string, eg "PumpFun Token is a token on the Solana blockchain", - imageUrl: string, eg "https://i.imgur.com/UFm07Np_d.png`; + description = this.action.description; constructor(private solanaKit: SolanaAgentKit) { super(); @@ -390,17 +386,9 @@ export class SolanaPumpfunTokenLaunchTool extends Tool { this.validateInput(parsedInput); // Launch token with validated input - await this.solanaKit.launchPumpFunToken( - parsedInput.tokenName, - parsedInput.tokenTicker, - parsedInput.description, - parsedInput.imageUrl, - { - twitter: parsedInput.twitter, - telegram: parsedInput.telegram, - website: parsedInput.website, - initialLiquiditySOL: parsedInput.initialLiquiditySOL, - }, + await this.action.handler( + this.solanaKit, + parsedInput, ); return JSON.stringify({ @@ -420,9 +408,9 @@ export class SolanaPumpfunTokenLaunchTool extends Tool { } export class SolanaCreateImageTool extends Tool { - name = "solana_create_image"; - description = - "Create an image using OpenAI's DALL-E. Input should be a string prompt for the image."; + private action = createImageAction; + name = this.action.name; + description = this.action.description; constructor(private solanaKit: SolanaAgentKit) { super(); @@ -437,7 +425,8 @@ export class SolanaCreateImageTool extends Tool { protected async _call(input: string): Promise { try { this.validateInput(input); - const result = await create_image(this.solanaKit, input.trim()); + const parsedInput = JSON.parse(input); + const result = await this.action.handler(this.solanaKit, parsedInput); return JSON.stringify({ status: "success", @@ -455,11 +444,9 @@ export class SolanaCreateImageTool extends Tool { } export class SolanaLendAssetTool extends Tool { - name = "solana_lend_asset"; - description = `Lend idle USDC for yield using Lulo. ( only USDC is supported ) - - Inputs (input is a json string): - amount: number, eg 1, 0.01 (required)`; + private action = lendAssetAction; + name = this.action.name; + description = this.action.description; constructor(private solanaKit: SolanaAgentKit) { super(); @@ -469,7 +456,7 @@ export class SolanaLendAssetTool extends Tool { try { const amount = JSON.parse(input).amount || input; - const tx = await this.solanaKit.lendAssets(amount); + const tx = await this.action.handler(this.solanaKit, { amount }); return JSON.stringify({ status: "success", @@ -488,8 +475,9 @@ export class SolanaLendAssetTool extends Tool { } export class SolanaTPSCalculatorTool extends Tool { - name = "solana_get_tps"; - description = "Get the current TPS of the Solana network"; + private action = getTPSAction; + name = this.action.name; + description = this.action.description; constructor(private solanaKit: SolanaAgentKit) { super(); @@ -497,7 +485,7 @@ export class SolanaTPSCalculatorTool extends Tool { async _call(_input: string): Promise { try { - const tps = await this.solanaKit.getTPS(); + const tps = await this.action.handler(this.solanaKit, {}); return `Solana (mainnet-beta) current transactions per second: ${tps}`; } catch (error: any) { return `Error fetching TPS: ${error.message}`; @@ -506,11 +494,9 @@ export class SolanaTPSCalculatorTool extends Tool { } export class SolanaStakeTool extends Tool { - name = "solana_stake"; - description = `This tool can be used to stake your SOL (Solana), also called as SOL staking or liquid staking. - - Inputs ( input is a JSON string ): - amount: number, eg 1 or 0.01 (required)`; + private action = stakeWithJupAction; + name = this.action.name; + description = this.action.description; constructor(private solanaKit: SolanaAgentKit) { super(); @@ -520,7 +506,7 @@ export class SolanaStakeTool extends Tool { try { const parsedInput = JSON.parse(input) || Number(input); - const tx = await this.solanaKit.stake(parsedInput.amount); + const tx = await this.action.handler(this.solanaKit, parsedInput); return JSON.stringify({ status: "success", @@ -542,11 +528,9 @@ export class SolanaStakeTool extends Tool { * Tool to fetch the price of a token in USDC */ export class SolanaFetchPriceTool extends Tool { - name = "solana_fetch_price"; - description = `Fetch the price of a given token in USDC. - - Inputs: - - tokenId: string, the mint address of the token, e.g., "JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN"`; + private action = fetchPriceAction; + name = this.action.name; + description = this.action.description; constructor(private solanaKit: SolanaAgentKit) { super(); @@ -554,7 +538,10 @@ export class SolanaFetchPriceTool extends Tool { async _call(input: string): Promise { try { - const price = await this.solanaKit.fetchTokenPrice(input.trim()); + + const parsedInput = { tokenId: input.trim() }; + const price = await this.action.handler(this.solanaKit, parsedInput); + //const price = await this.solanaKit.fetchTokenPrice(input.trim()); return JSON.stringify({ status: "success", tokenId: input.trim(), @@ -571,11 +558,9 @@ export class SolanaFetchPriceTool extends Tool { } export class SolanaTokenDataTool extends Tool { - name = "solana_token_data"; - description = `Get the token data for a given token mint address - - Inputs: mintAddress is required. - mintAddress: string, eg "So11111111111111111111111111111111111111112" (required)`; + private action = getTokenDataAction + name = this.action.name; + description = this.action.description; constructor(private solanaKit: SolanaAgentKit) { super(); @@ -583,9 +568,9 @@ export class SolanaTokenDataTool extends Tool { protected async _call(input: string): Promise { try { - const parsedInput = input.trim(); - const tokenData = await this.solanaKit.getTokenDataByAddress(parsedInput); + const parsedInput = JSON.parse(input); + const tokenData = await this.action.handler(this.solanaKit, parsedInput); return JSON.stringify({ status: "success", @@ -605,8 +590,8 @@ export class SolanaTokenDataByTickerTool extends Tool { name = "solana_token_data_by_ticker"; description = `Get the token data for a given token ticker - Inputs: ticker is required. - ticker: string, eg "USDC" (required)`; +Inputs: ticker is required. + ticker: string, eg "USDC"(required)`; constructor(private solanaKit: SolanaAgentKit) { super(); @@ -632,15 +617,15 @@ export class SolanaTokenDataByTickerTool extends Tool { export class SolanaCompressedAirdropTool extends Tool { name = "solana_compressed_airdrop"; - description = `Airdrop SPL tokens with ZK Compression (also called as airdropping tokens) + description = `Airdrop SPL tokens with ZK Compression(also called as airdropping tokens) - Inputs (input is a JSON string): - mintAddress: string, the mint address of the token, e.g., "JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN" (required) - amount: number, the amount of tokens to airdrop per recipient, e.g., 42 (required) - decimals: number, the decimals of the token, e.g., 6 (required) - recipients: string[], the recipient addresses, e.g., ["1nc1nerator11111111111111111111111111111111"] (required) - priorityFeeInLamports: number, the priority fee in lamports. Default is 30_000. (optional) - shouldLog: boolean, whether to log progress to stdout. Default is false. (optional)`; +Inputs(input is a JSON string): +mintAddress: string, the mint address of the token, e.g., "JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN"(required) +amount: number, the amount of tokens to airdrop per recipient, e.g., 42(required) +decimals: number, the decimals of the token, e.g., 6(required) +recipients: string[], the recipient addresses, e.g., ["1nc1nerator11111111111111111111111111111111"](required) +priorityFeeInLamports: number, the priority fee in lamports.Default is 30_000.(optional) +shouldLog: boolean, whether to log progress to stdout.Default is false. (optional)`; constructor(private solanaKit: SolanaAgentKit) { super(); @@ -675,16 +660,9 @@ export class SolanaCompressedAirdropTool extends Tool { } export class SolanaCreateSingleSidedWhirlpoolTool extends Tool { - name = "create_orca_single_sided_whirlpool"; - description = `Create a single-sided Whirlpool with liquidity. - - Inputs (input is a JSON string): - - depositTokenAmount: number, eg: 1000000000 (required, in units of deposit token including decimals) - - depositTokenMint: string, eg: "DepositTokenMintAddress" (required, mint address of deposit token) - - otherTokenMint: string, eg: "OtherTokenMintAddress" (required, mint address of other token) - - initialPrice: number, eg: 0.001 (required, initial price of deposit token in terms of other token) - - maxPrice: number, eg: 5.0 (required, maximum price at which liquidity is added) - - feeTier: number, eg: 0.30 (required, fee tier for the pool)`; + private action = createOrcaSingleSidedWhirlpoolAction; + name = this.action.name; + description = this.action.description; constructor(private solanaKit: SolanaAgentKit) { super(); @@ -702,21 +680,20 @@ export class SolanaCreateSingleSidedWhirlpoolTool extends Tool { if (!feeTier || !(feeTier in FEE_TIERS)) { throw new Error( - `Invalid feeTier. Available options: ${Object.keys(FEE_TIERS).join( + `Invalid feeTier.Available options: ${Object.keys(FEE_TIERS).join( ", ", - )}`, + ) + } `, ); } - - const txId = await this.solanaKit.createOrcaSingleSidedWhirlpool( + const txId = await this.action.handler(this.solanaKit, { depositTokenAmount, depositTokenMint, otherTokenMint, initialPrice, maxPrice, feeTier, - ); - + }); return JSON.stringify({ status: "success", message: "Single-sided Whirlpool created successfully", @@ -733,15 +710,9 @@ export class SolanaCreateSingleSidedWhirlpoolTool extends Tool { } export class SolanaRaydiumCreateAmmV4 extends Tool { - name = "raydium_create_ammV4"; - description = `Raydium's Legacy AMM that requiers an OpenBook marketID - - Inputs (input is a json string): - marketId: string (required) - baseAmount: number(int), eg: 111111 (required) - quoteAmount: number(int), eg: 111111 (required) - startTime: number(seconds), eg: now number or zero (required) - `; + private action = raydiumCreateAmmV4Action; + name = this.action.name; + description = this.action.description; constructor(private solanaKit: SolanaAgentKit) { super(); @@ -751,12 +722,7 @@ export class SolanaRaydiumCreateAmmV4 extends Tool { try { const inputFormat = JSON.parse(input); - const tx = await this.solanaKit.raydiumCreateAmmV4( - new PublicKey(inputFormat.marketId), - new BN(inputFormat.baseAmount), - new BN(inputFormat.quoteAmount), - new BN(inputFormat.startTime), - ); + const tx = await this.action.handler(this.solanaKit, inputFormat); return JSON.stringify({ status: "success", @@ -777,12 +743,12 @@ export class SolanaRaydiumCreateClmm extends Tool { name = "raydium_create_clmm"; description = `Concentrated liquidity market maker, custom liquidity ranges, increased capital efficiency - Inputs (input is a json string): - mint1: string (required) - mint2: string (required) - configId: string (required) stores pool info, id, index, protocolFeeRate, tradeFeeRate, tickSpacing, fundFeeRate - initialPrice: number, eg: 123.12 (required) - startTime: number(seconds), eg: now number or zero (required) +Inputs(input is a json string): +mint1: string(required) +mint2: string(required) +configId: string(required) stores pool info, id, index, protocolFeeRate, tradeFeeRate, tickSpacing, fundFeeRate +initialPrice: number, eg: 123.12(required) +startTime: number(seconds), eg: now number or zero(required) `; constructor(private solanaKit: SolanaAgentKit) { @@ -819,17 +785,9 @@ export class SolanaRaydiumCreateClmm extends Tool { } export class SolanaRaydiumCreateCpmm extends Tool { - name = "raydium_create_cpmm"; - description = `Raydium's newest CPMM, does not require marketID, supports Token 2022 standard - - Inputs (input is a json string): - mint1: string (required) - mint2: string (required) - configId: string (required), stores pool info, index, protocolFeeRate, tradeFeeRate, fundFeeRate, createPoolFee - mintAAmount: number(int), eg: 1111 (required) - mintBAmount: number(int), eg: 2222 (required) - startTime: number(seconds), eg: now number or zero (required) - `; + private action = raydiumCreateCpmmAction; + name = this.action.name; + description = this.action.description; constructor(private solanaKit: SolanaAgentKit) { super(); @@ -838,18 +796,7 @@ export class SolanaRaydiumCreateCpmm extends Tool { async _call(input: string): Promise { try { const inputFormat = JSON.parse(input); - - const tx = await this.solanaKit.raydiumCreateCpmm( - new PublicKey(inputFormat.mint1), - new PublicKey(inputFormat.mint2), - - new PublicKey(inputFormat.configId), - - new BN(inputFormat.mintAAmount), - new BN(inputFormat.mintBAmount), - - new BN(inputFormat.startTime), - ); + const tx = await this.action.handler(this.solanaKit, inputFormat); return JSON.stringify({ status: "success", @@ -870,11 +817,11 @@ export class SolanaOpenbookCreateMarket extends Tool { name = "solana_openbook_create_market"; description = `Openbook marketId, required for ammv4 - Inputs (input is a json string): - baseMint: string (required) - quoteMint: string (required) - lotSize: number (required) - tickSize: number (required) + Inputs(input is a json string): + baseMint: string(required) +quoteMint: string(required) +lotSize: number(required) +tickSize: number(required) `; constructor(private solanaKit: SolanaAgentKit) { @@ -909,11 +856,9 @@ export class SolanaOpenbookCreateMarket extends Tool { } export class SolanaPythFetchPrice extends Tool { - name = "solana_pyth_fetch_price"; - description = `Fetch the price of a given price feed from Pyth's Hermes service - - Inputs: - priceFeedID: string, the price feed ID, e.g., "0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43" for BTC/USD`; + private action = pythFetchPriceAction; + name = this.action.name; + description = this.action.description; constructor(private solanaKit: SolanaAgentKit) { super(); @@ -921,11 +866,12 @@ export class SolanaPythFetchPrice extends Tool { async _call(input: string): Promise { try { - const price = await this.solanaKit.pythFetchPrice(input); + const parsedInput = { tokenId: input.trim() }; + const price = await this.action.handler(this.solanaKit, parsedInput); const response: PythFetchPriceResponse = { status: "success", priceFeedID: input, - price: price, + price: price.price, }; return JSON.stringify(response); } catch (error: any) { @@ -942,12 +888,12 @@ export class SolanaPythFetchPrice extends Tool { export class SolanaResolveAllDomainsTool extends Tool { name = "solana_resolve_all_domains"; - description = `Resolve domain names to a public key for ALL domain types EXCEPT .sol domains. - Use this for domains like .blink, .bonk, etc. - DO NOT use this for .sol domains (use solana_resolve_domain instead). + description = `Resolve domain names to a public key for ALL domain types EXCEPT.sol domains. + Use this for domains like.blink, .bonk, etc. + DO NOT use this for .sol domains(use solana_resolve_domain instead). Input: - domain: string, eg "mydomain.blink" or "mydomain.bonk" (required)`; + domain: string, eg "mydomain.blink" or "mydomain.bonk"(required)`; constructor(private solanaKit: SolanaAgentKit) { super(); @@ -981,11 +927,9 @@ export class SolanaResolveAllDomainsTool extends Tool { } export class SolanaGetOwnedDomains extends Tool { - name = "solana_get_owned_domains"; - description = `Get all domains owned by a specific wallet address. - - Inputs: - owner: string, eg "4Be9CvxqHW6BYiRAxW9Q3xu1ycTMWaL5z8NX4HR3ha7t" (required)`; + private action = getOwnedDomainsForTLDAction; + name = this.action.name; + description = this.action.description; constructor(private solanaKit: SolanaAgentKit) { super(); @@ -994,7 +938,7 @@ export class SolanaGetOwnedDomains extends Tool { async _call(input: string): Promise { try { const ownerPubkey = new PublicKey(input.trim()); - const domains = await this.solanaKit.getOwnedAllDomains(ownerPubkey); + const domains = await this.action.handler(this.solanaKit, ownerPubkey); return JSON.stringify({ status: "success", @@ -1015,8 +959,8 @@ export class SolanaGetOwnedTldDomains extends Tool { name = "solana_get_owned_tld_domains"; description = `Get all domains owned by the agent's wallet for a specific TLD. - Inputs: - tld: string, eg "bonk" (required)`; +Inputs: +tld: string, eg "bonk"(required)`; constructor(private solanaKit: SolanaAgentKit) { super(); @@ -1042,8 +986,9 @@ export class SolanaGetOwnedTldDomains extends Tool { } export class SolanaGetAllTlds extends Tool { - name = "solana_get_all_tlds"; - description = `Get all active top-level domains (TLDs) in the AllDomains Name Service`; + private action = getAllDomainsTLDsAction; + name = this.action.name; + description = this.action.description; constructor(private solanaKit: SolanaAgentKit) { super(); @@ -1051,7 +996,7 @@ export class SolanaGetAllTlds extends Tool { async _call(): Promise { try { - const tlds = await this.solanaKit.getAllDomainsTLDs(); + const tlds = await this.action.handler(this.solanaKit, {}); return JSON.stringify({ status: "success", @@ -1069,12 +1014,9 @@ export class SolanaGetAllTlds extends Tool { } export class SolanaGetMainDomain extends Tool { - name = "solana_get_main_domain"; - description = `Get the main/favorite domain for a given wallet address. - - Inputs: - owner: string, eg "4Be9CvxqHW6BYiRAxW9Q3xu1ycTMWaL5z8NX4HR3ha7t" (required)`; - + private action = getMainAllDomainsDomainAction; + name = this.action.name; + description = this.action.description; constructor(private solanaKit: SolanaAgentKit) { super(); } @@ -1083,7 +1025,7 @@ export class SolanaGetMainDomain extends Tool { try { const ownerPubkey = new PublicKey(input.trim()); const mainDomain = - await this.solanaKit.getMainAllDomainsDomain(ownerPubkey); + await this.action.handler(this.solanaKit, ownerPubkey); return JSON.stringify({ status: "success", @@ -1101,19 +1043,9 @@ export class SolanaGetMainDomain extends Tool { } export class SolanaCreateGibworkTask extends Tool { - name = "create_gibwork_task"; - description = `Create a task on Gibwork. - - Inputs (input is a JSON string): - title: string, title of the task (required) - content: string, description of the task (required) - requirements: string, requirements to complete the task (required) - tags: string[], list of tags associated with the task (required) - payer: string, payer address (optional, defaults to agent wallet) - tokenMintAddress: string, the mint address of the token, e.g., "JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN" (required) - amount: number, payment amount (required) - `; - + private action = createGibworkTaskAction; + name = this.action.name; + description = this.action.description; constructor(private solanaSdk: SolanaAgentKit) { super(); } @@ -1121,16 +1053,7 @@ export class SolanaCreateGibworkTask extends Tool { protected async _call(input: string): Promise { try { const parsedInput = JSON.parse(input); - - const taskData = await this.solanaSdk.createGibworkTask( - parsedInput.title, - parsedInput.content, - parsedInput.requirements, - parsedInput.tags, - parsedInput.tokenMintAddress, - parsedInput.amount, - parsedInput.payer, - ); + const taskData = await this.action.handler(this.solanaSdk, parsedInput); const response: GibworkCreateTaskReponse = { status: "success", From 2a08017dcd210c68a2a33fd20481a28b3b8788e8 Mon Sep 17 00:00:00 2001 From: Fahri Bilici <28020526+FahriBilici@users.noreply.github.com> Date: Tue, 31 Dec 2024 17:54:33 +0100 Subject: [PATCH 09/11] adding missing actions --- src/actions/compressedAirdrop.ts | 105 ++++++++++++++++++++++++++ src/actions/getOwnedDomainsForTLD.ts | 2 +- src/actions/raydiumCreateClmm.ts | 76 +++++++++++++++++++ src/actions/resolveDomain.ts | 2 +- src/actions/tokenDataByTicker.ts | 60 +++++++++++++++ src/langchain/index.ts | 106 ++++++++------------------- 6 files changed, 272 insertions(+), 79 deletions(-) create mode 100644 src/actions/compressedAirdrop.ts create mode 100644 src/actions/raydiumCreateClmm.ts create mode 100644 src/actions/tokenDataByTicker.ts diff --git a/src/actions/compressedAirdrop.ts b/src/actions/compressedAirdrop.ts new file mode 100644 index 0000000..652ae28 --- /dev/null +++ b/src/actions/compressedAirdrop.ts @@ -0,0 +1,105 @@ +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; \ No newline at end of file diff --git a/src/actions/getOwnedDomainsForTLD.ts b/src/actions/getOwnedDomainsForTLD.ts index b419e58..1c0b4be 100644 --- a/src/actions/getOwnedDomainsForTLD.ts +++ b/src/actions/getOwnedDomainsForTLD.ts @@ -4,7 +4,7 @@ import { z } from "zod"; import { getOwnedDomainsForTLD } from "../tools"; const getOwnedDomainsForTLDAction: Action = { - name: "solana_get_owned_domains_for_tld", + name: "solana_get_owned_tld_domains", similes: [ "list owned domains for tld", "get my domains for extension", diff --git a/src/actions/raydiumCreateClmm.ts b/src/actions/raydiumCreateClmm.ts new file mode 100644 index 0000000..60a24db --- /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; \ No newline at end of file diff --git a/src/actions/resolveDomain.ts b/src/actions/resolveDomain.ts index aca514e..d159141 100644 --- a/src/actions/resolveDomain.ts +++ b/src/actions/resolveDomain.ts @@ -4,7 +4,7 @@ import { z } from "zod"; import { resolveAllDomains } from "../tools"; const resolveDomainAction: Action = { - name: "solana_resolve_domain", + name: "solana_resolve_all_domains", similes: [ "resolve domain", "lookup domain", diff --git a/src/actions/tokenDataByTicker.ts b/src/actions/tokenDataByTicker.ts new file mode 100644 index 0000000..6f45702 --- /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; \ No newline at end of file diff --git a/src/langchain/index.ts b/src/langchain/index.ts index 75aadb8..3dad335 100644 --- a/src/langchain/index.ts +++ b/src/langchain/index.ts @@ -36,6 +36,10 @@ import resolveSolDomainAction from "../actions/resolveSolDomain"; import getAllDomainsTLDsAction from "../actions/getAllDomainsTLDs"; import getMainAllDomainsDomainAction from "../actions/getMainAllDomainsDomain"; import raydiumCreateAmmV4Action from "../actions/raydiumCreateAmmV4"; +import tokenDataByTickerAction from "../actions/tokenDataByTicker"; +import compressedAirdropAction from "../actions/compressedAirdrop"; +import raydiumCreateClmmAction from "../actions/raydiumCreateClmm"; +import createOpenbookMarketAction from "../actions/createOpenbookMarket"; export class SolanaBalanceTool extends Tool { private action = balanceAction; @@ -541,7 +545,6 @@ export class SolanaFetchPriceTool extends Tool { const parsedInput = { tokenId: input.trim() }; const price = await this.action.handler(this.solanaKit, parsedInput); - //const price = await this.solanaKit.fetchTokenPrice(input.trim()); return JSON.stringify({ status: "success", tokenId: input.trim(), @@ -587,11 +590,9 @@ export class SolanaTokenDataTool extends Tool { } export class SolanaTokenDataByTickerTool extends Tool { - name = "solana_token_data_by_ticker"; - description = `Get the token data for a given token ticker - -Inputs: ticker is required. - ticker: string, eg "USDC"(required)`; + private action = tokenDataByTickerAction; + name = this.action.name; + description = this.action.description; constructor(private solanaKit: SolanaAgentKit) { super(); @@ -600,7 +601,7 @@ Inputs: ticker is required. protected async _call(input: string): Promise { try { const ticker = input.trim(); - const tokenData = await this.solanaKit.getTokenDataByTicker(ticker); + const tokenData = await this.action.handler(this.solanaKit, { ticker }); return JSON.stringify({ status: "success", tokenData: tokenData, @@ -616,16 +617,9 @@ Inputs: ticker is required. } export class SolanaCompressedAirdropTool extends Tool { - name = "solana_compressed_airdrop"; - description = `Airdrop SPL tokens with ZK Compression(also called as airdropping tokens) - -Inputs(input is a JSON string): -mintAddress: string, the mint address of the token, e.g., "JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN"(required) -amount: number, the amount of tokens to airdrop per recipient, e.g., 42(required) -decimals: number, the decimals of the token, e.g., 6(required) -recipients: string[], the recipient addresses, e.g., ["1nc1nerator11111111111111111111111111111111"](required) -priorityFeeInLamports: number, the priority fee in lamports.Default is 30_000.(optional) -shouldLog: boolean, whether to log progress to stdout.Default is false. (optional)`; + private action = compressedAirdropAction; + name = this.action.name; + description = this.action.description; constructor(private solanaKit: SolanaAgentKit) { super(); @@ -634,16 +628,7 @@ shouldLog: boolean, whether to log progress to stdout.Default is false. (optiona protected async _call(input: string): Promise { try { const parsedInput = JSON.parse(input); - - const txs = await this.solanaKit.sendCompressedAirdrop( - parsedInput.mintAddress, - parsedInput.amount, - parsedInput.decimals, - parsedInput.recipients, - parsedInput.priorityFeeInLamports || 30_000, - parsedInput.shouldLog || false, - ); - + const txs = await this.action.handler(this.solanaKit, parsedInput); return JSON.stringify({ status: "success", message: `Airdropped ${parsedInput.amount} tokens to ${parsedInput.recipients.length} recipients.`, @@ -740,16 +725,9 @@ export class SolanaRaydiumCreateAmmV4 extends Tool { } export class SolanaRaydiumCreateClmm extends Tool { - name = "raydium_create_clmm"; - description = `Concentrated liquidity market maker, custom liquidity ranges, increased capital efficiency - -Inputs(input is a json string): -mint1: string(required) -mint2: string(required) -configId: string(required) stores pool info, id, index, protocolFeeRate, tradeFeeRate, tickSpacing, fundFeeRate -initialPrice: number, eg: 123.12(required) -startTime: number(seconds), eg: now number or zero(required) - `; + private action = raydiumCreateClmmAction; + name = this.action.name; + description = this.action.description; constructor(private solanaKit: SolanaAgentKit) { super(); @@ -759,15 +737,7 @@ startTime: number(seconds), eg: now number or zero(required) try { const inputFormat = JSON.parse(input); - const tx = await this.solanaKit.raydiumCreateClmm( - new PublicKey(inputFormat.mint1), - new PublicKey(inputFormat.mint2), - - new PublicKey(inputFormat.configId), - - new Decimal(inputFormat.initialPrice), - new BN(inputFormat.startTime), - ); + const tx = await this.action.handler(this.solanaKit, inputFormat); return JSON.stringify({ status: "success", @@ -814,15 +784,9 @@ export class SolanaRaydiumCreateCpmm extends Tool { } export class SolanaOpenbookCreateMarket extends Tool { - name = "solana_openbook_create_market"; - description = `Openbook marketId, required for ammv4 - - Inputs(input is a json string): - baseMint: string(required) -quoteMint: string(required) -lotSize: number(required) -tickSize: number(required) - `; + private action = createOpenbookMarketAction; + name = this.action.name; + description = this.action.description; constructor(private solanaKit: SolanaAgentKit) { super(); @@ -831,14 +795,7 @@ tickSize: number(required) async _call(input: string): Promise { try { const inputFormat = JSON.parse(input); - - const tx = await this.solanaKit.openbookCreateMarket( - new PublicKey(inputFormat.baseMint), - new PublicKey(inputFormat.quoteMint), - - inputFormat.lotSize, - inputFormat.tickSize, - ); + const tx = await this.action.handler(this.solanaKit, inputFormat); return JSON.stringify({ status: "success", @@ -887,13 +844,9 @@ export class SolanaPythFetchPrice extends Tool { } export class SolanaResolveAllDomainsTool extends Tool { - name = "solana_resolve_all_domains"; - description = `Resolve domain names to a public key for ALL domain types EXCEPT.sol domains. - Use this for domains like.blink, .bonk, etc. - DO NOT use this for .sol domains(use solana_resolve_domain instead). - - Input: - domain: string, eg "mydomain.blink" or "mydomain.bonk"(required)`; + private action = resolveDomainAction; + name = this.action.name; + description = this.action.description; constructor(private solanaKit: SolanaAgentKit) { super(); @@ -901,7 +854,8 @@ export class SolanaResolveAllDomainsTool extends Tool { async _call(input: string): Promise { try { - const owner = await this.solanaKit.resolveAllDomains(input); + const parsedInput = JSON.parse(input); + const owner = await this.action.handler(this.solanaKit, parsedInput); if (!owner) { return JSON.stringify({ @@ -956,11 +910,9 @@ export class SolanaGetOwnedDomains extends Tool { } export class SolanaGetOwnedTldDomains extends Tool { - name = "solana_get_owned_tld_domains"; - description = `Get all domains owned by the agent's wallet for a specific TLD. - -Inputs: -tld: string, eg "bonk"(required)`; + private action = getOwnedDomainsForTLDAction; + name = this.action.name; + description = this.action.description; constructor(private solanaKit: SolanaAgentKit) { super(); @@ -968,7 +920,7 @@ tld: string, eg "bonk"(required)`; async _call(input: string): Promise { try { - const domains = await this.solanaKit.getOwnedDomainsForTLD(input); + const domains = await this.action.handler(this.solanaKit, { tld: input }); return JSON.stringify({ status: "success", From 42e8e4758ad858332c3696a432ee84653f8365d3 Mon Sep 17 00:00:00 2001 From: aryan Date: Wed, 1 Jan 2025 02:51:40 +0530 Subject: [PATCH 10/11] wip: langchain --- src/actions/compressedAirdrop.ts | 187 +++++---- src/actions/raydiumCreateClmm.ts | 124 +++--- src/actions/tokenDataByTicker.ts | 98 ++--- src/langchain/index.ts | 650 +++++++++++++++++++------------ 4 files changed, 601 insertions(+), 458 deletions(-) diff --git a/src/actions/compressedAirdrop.ts b/src/actions/compressedAirdrop.ts index 652ae28..4be2fb4 100644 --- a/src/actions/compressedAirdrop.ts +++ b/src/actions/compressedAirdrop.ts @@ -4,102 +4,101 @@ 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", + 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.", + }, ], - 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; + ], + // 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, - ); + // 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", - }; - } - }, + 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; \ No newline at end of file +export default compressedAirdropAction; diff --git a/src/actions/raydiumCreateClmm.ts b/src/actions/raydiumCreateClmm.ts index 60a24db..277cc06 100644 --- a/src/actions/raydiumCreateClmm.ts +++ b/src/actions/raydiumCreateClmm.ts @@ -7,70 +7,70 @@ 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", + 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.", + }, ], - 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; + ], + // 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), - ); + 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", - }; - } - }, + 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; \ No newline at end of file +export default raydiumCreateClmmAction; diff --git a/src/actions/tokenDataByTicker.ts b/src/actions/tokenDataByTicker.ts index 6f45702..3ab3f12 100644 --- a/src/actions/tokenDataByTicker.ts +++ b/src/actions/tokenDataByTicker.ts @@ -4,57 +4,57 @@ 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", + 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.", + }, ], - 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; + ], + 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); + // 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", - }; - } - }, + 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; \ No newline at end of file +export default tokenDataByTickerAction; diff --git a/src/langchain/index.ts b/src/langchain/index.ts index 3dad335..43e0ea6 100644 --- a/src/langchain/index.ts +++ b/src/langchain/index.ts @@ -1,6 +1,6 @@ import { PublicKey } from "@solana/web3.js"; import Decimal from "decimal.js"; -import { Tool } from "@langchain/core/tools"; +import { Tool } from "langchain/tools"; import { GibworkCreateTaskReponse, PythFetchPriceResponse, @@ -8,57 +8,31 @@ import { } from "../index"; import { create_image } from "../tools/create_image"; import { BN } from "@coral-xyz/anchor"; -import { FEE_TIERS, getMainAllDomainsDomain } from "../tools"; -import { toJSON } from "../utils/toJSON"; -import deployTokenAction from "../actions/deployToken"; -import balanceAction from "../actions/balance"; -import transferAction from "../actions/transfer"; -import deployCollectionAction from "../actions/deployCollection"; -import mintNFTAction from "../actions/mintNFT"; -import tradeAction from "../actions/trade"; -import requestFundsAction from "../actions/requestFunds"; -import fetchPriceAction from "../actions/fetchPrice"; -import registerDomainAction from "../actions/registerDomain"; -import resolveDomainAction from "../actions/resolveDomain"; -import getPrimaryDomainAction from "../actions/getPrimaryDomain"; -import launchPumpfunTokenAction from "../actions/launchPumpfunToken"; -import createImageAction from "../actions/createImage"; -import lendAssetAction from "../actions/lendAsset"; -import getTPSAction from "../actions/getTPS"; -import createOrcaSingleSidedWhirlpoolAction from "../actions/createOrcaSingleSidedWhirlpool"; -import raydiumCreateCpmmAction from "../actions/raydiumCreateCpmm"; -import pythFetchPriceAction from "../actions/pythFetchPrice"; -import getOwnedDomainsForTLDAction from "../actions/getOwnedDomainsForTLD"; -import createGibworkTaskAction from "../actions/createGibworkTask"; -import getTokenDataAction from "../actions/getTokenData"; -import stakeWithJupAction from "../actions/stakeWithJup"; -import resolveSolDomainAction from "../actions/resolveSolDomain"; -import getAllDomainsTLDsAction from "../actions/getAllDomainsTLDs"; -import getMainAllDomainsDomainAction from "../actions/getMainAllDomainsDomain"; -import raydiumCreateAmmV4Action from "../actions/raydiumCreateAmmV4"; -import tokenDataByTickerAction from "../actions/tokenDataByTicker"; -import compressedAirdropAction from "../actions/compressedAirdrop"; -import raydiumCreateClmmAction from "../actions/raydiumCreateClmm"; -import createOpenbookMarketAction from "../actions/createOpenbookMarket"; export class SolanaBalanceTool extends Tool { - private action = balanceAction; - name = this.action.name; - description = this.action.description; + name = "solana_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. + + Inputs ( input is a JSON string ): + tokenAddress: string, eg "So11111111111111111111111111111111111111112" (optional)`; constructor(private solanaKit: SolanaAgentKit) { super(); } - async _call(input: string): Promise { + protected async _call(input: string): Promise { try { - // Parse input as JSON if provided, otherwise use empty object - const parsedInput = input ? JSON.parse(input) : {}; + const tokenAddress = input ? new PublicKey(input) : undefined; + const balance = await this.solanaKit.getBalance(tokenAddress); - // Validate and execute using the action - const result = await this.action.handler(this.solanaKit, parsedInput); - - return JSON.stringify(result); + return JSON.stringify({ + status: "success", + balance, + token: input || "SOL", + }); } catch (error: any) { return JSON.stringify({ status: "error", @@ -70,23 +44,41 @@ export class SolanaBalanceTool extends Tool { } export class SolanaTransferTool extends Tool { - private action = transferAction; - name = this.action.name; - description = this.action.description; + name = "solana_transfer"; + description = `Transfer tokens or SOL to another address ( also called as wallet address ). + + Inputs ( input is a JSON string ): + to: string, eg "8x2dR8Mpzuz2YqyZyZjUbYWKSWesBo5jMx2Q9Y86udVk" (required) + amount: number, eg 1 (required) + mint?: string, eg "So11111111111111111111111111111111111111112" or "SENDdRQtYMWaQrBroBrJ2Q53fgVuq95CV9UPGEvpCxa" (optional)`; constructor(private solanaKit: SolanaAgentKit) { super(); } - async _call(input: string): Promise { + protected async _call(input: string): Promise { try { - // Parse input as JSON const parsedInput = JSON.parse(input); - // Validate and execute using the action - const result = await this.action.handler(this.solanaKit, parsedInput); + const recipient = new PublicKey(parsedInput.to); + const mintAddress = parsedInput.mint + ? new PublicKey(parsedInput.mint) + : undefined; - return JSON.stringify(result); + const tx = await this.solanaKit.transfer( + recipient, + parsedInput.amount, + mintAddress, + ); + + return JSON.stringify({ + status: "success", + message: "Transfer completed successfully", + amount: parsedInput.amount, + recipient: parsedInput.to, + token: parsedInput.mint || "SOL", + transaction: tx, + }); } catch (error: any) { return JSON.stringify({ status: "error", @@ -98,23 +90,38 @@ export class SolanaTransferTool extends Tool { } export class SolanaDeployTokenTool extends Tool { - private action = deployTokenAction; - name = this.action.name; - description = this.action.description; + name = "solana_deploy_token"; + description = `Deploy a new token on Solana blockchain. + + Inputs (input is a JSON string): + name: string, eg "My Token" (required) + uri: string, eg "https://example.com/token.json" (required) + symbol: string, eg "MTK" (required) + decimals?: number, eg 9 (optional, defaults to 9) + initialSupply?: number, eg 1000000 (optional)`; constructor(private solanaKit: SolanaAgentKit) { super(); } - async _call(input: string): Promise { + protected async _call(input: string): Promise { try { - // Parse input as JSON const parsedInput = JSON.parse(input); - // Validate and execute using the action - const result = await this.action.handler(this.solanaKit, parsedInput); + const result = await this.solanaKit.deployToken( + parsedInput.name, + parsedInput.uri, + parsedInput.symbol, + parsedInput.decimals, + parsedInput.initialSupply, + ); - return JSON.stringify(result); + return JSON.stringify({ + status: "success", + message: "Token deployed successfully", + mintAddress: result.mint.toString(), + decimals: parsedInput.decimals || 9, + }); } catch (error: any) { return JSON.stringify({ status: "error", @@ -126,23 +133,30 @@ export class SolanaDeployTokenTool extends Tool { } export class SolanaDeployCollectionTool extends Tool { - private action = deployCollectionAction; - name = this.action.name; - description = this.action.description; + name = "solana_deploy_collection"; + description = `Deploy a new NFT collection on Solana blockchain. + + Inputs (input is a JSON string): + name: string, eg "My Collection" (required) + uri: string, eg "https://example.com/collection.json" (required) + royaltyBasisPoints?: number, eg 500 for 5% (optional)`; constructor(private solanaKit: SolanaAgentKit) { super(); } - async _call(input: string): Promise { + protected async _call(input: string): Promise { try { - // Parse input as JSON const parsedInput = JSON.parse(input); - // Validate and execute using the action - const result = await this.action.handler(this.solanaKit, parsedInput); + const result = await this.solanaKit.deployCollection(parsedInput); - return JSON.stringify(result); + return JSON.stringify({ + status: "success", + message: "Collection deployed successfully", + collectionAddress: result.collectionAddress.toString(), + name: parsedInput.name, + }); } catch (error: any) { return JSON.stringify({ status: "error", @@ -154,23 +168,45 @@ export class SolanaDeployCollectionTool extends Tool { } export class SolanaMintNFTTool extends Tool { - private action = mintNFTAction; - name = this.action.name; - description = this.action.description; + name = "solana_mint_nft"; + description = `Mint a new NFT in a collection on Solana blockchain. + + Inputs (input is a JSON string): + collectionMint: string, eg "J1S9H3QjnRtBbbuD4HjPV6RpRhwuk4zKbxsnCHuTgh9w" (required) - The address of the collection to mint into + name: string, eg "My NFT" (required) + uri: string, eg "https://example.com/nft.json" (required) + recipient?: string, eg "9aUn5swQzUTRanaaTwmszxiv89cvFwUCjEBv1vZCoT1u" (optional) - The wallet to receive the NFT, defaults to agent's wallet which is ${this.solanaKit.wallet_address.toString()}`; constructor(private solanaKit: SolanaAgentKit) { super(); } - async _call(input: string): Promise { + protected async _call(input: string): Promise { try { - // Parse input as JSON const parsedInput = JSON.parse(input); - // Validate and execute using the action - const result = await this.action.handler(this.solanaKit, parsedInput); + const result = await this.solanaKit.mintNFT( + new PublicKey(parsedInput.collectionMint), + { + name: parsedInput.name, + uri: parsedInput.uri, + }, + parsedInput.recipient + ? new PublicKey(parsedInput.recipient) + : this.solanaKit.wallet_address, + ); - return JSON.stringify(result); + return JSON.stringify({ + status: "success", + message: "NFT minted successfully", + mintAddress: result.mint.toString(), + metadata: { + name: parsedInput.name, + symbol: parsedInput.symbol, + uri: parsedInput.uri, + }, + recipient: parsedInput.recipient || result.mint.toString(), + }); } catch (error: any) { return JSON.stringify({ status: "error", @@ -182,23 +218,40 @@ export class SolanaMintNFTTool extends Tool { } export class SolanaTradeTool extends Tool { - private action = tradeAction; - name = this.action.name; - description = this.action.description; + name = "solana_trade"; + description = `This tool can be used to swap tokens to another token ( It uses Jupiter Exchange ). + + Inputs ( input is a JSON string ): + outputMint: string, eg "So11111111111111111111111111111111111111112" or "SENDdRQtYMWaQrBroBrJ2Q53fgVuq95CV9UPGEvpCxa" (required) + inputAmount: number, eg 1 or 0.01 (required) + inputMint?: string, eg "So11111111111111111111111111111111111111112" (optional) + slippageBps?: number, eg 100 (optional)`; constructor(private solanaKit: SolanaAgentKit) { super(); } - async _call(input: string): Promise { + protected async _call(input: string): Promise { try { - // Parse input as JSON const parsedInput = JSON.parse(input); - // Validate and execute using the action - const result = await this.action.handler(this.solanaKit, parsedInput); + const tx = await this.solanaKit.trade( + new PublicKey(parsedInput.outputMint), + parsedInput.inputAmount, + parsedInput.inputMint + ? new PublicKey(parsedInput.inputMint) + : new PublicKey("So11111111111111111111111111111111111111112"), + parsedInput.slippageBps, + ); - return JSON.stringify(result); + return JSON.stringify({ + status: "success", + message: "Trade executed successfully", + transaction: tx, + inputAmount: parsedInput.inputAmount, + inputToken: parsedInput.inputMint || "SOL", + outputToken: parsedInput.outputMint, + }); } catch (error: any) { return JSON.stringify({ status: "error", @@ -210,20 +263,22 @@ export class SolanaTradeTool extends Tool { } export class SolanaRequestFundsTool extends Tool { - private action = requestFundsAction; - name = this.action.name; - description = this.action.description; + name = "solana_request_funds"; + description = "Request SOL from Solana faucet (devnet/testnet only)"; constructor(private solanaKit: SolanaAgentKit) { super(); } - async _call(_input: string): Promise { + protected async _call(_input: string): Promise { try { - // No input needed for this action - const result = await this.action.handler(this.solanaKit, {}); + await this.solanaKit.requestFaucetFunds(); - return JSON.stringify(result); + return JSON.stringify({ + status: "success", + message: "Successfully requested faucet funds", + network: this.solanaKit.connection.rpcEndpoint.split("/")[2], + }); } catch (error: any) { return JSON.stringify({ status: "error", @@ -235,9 +290,13 @@ export class SolanaRequestFundsTool extends Tool { } export class SolanaRegisterDomainTool extends Tool { - private action = registerDomainAction - name = this.action.name; - description = this.action.description; + name = "solana_register_domain"; + description = `Register a .sol domain name for your wallet. + + Inputs: + name: string, eg "pumpfun.sol" (required) + spaceKB: number, eg 1 (optional, default is 1) + `; constructor(private solanaKit: SolanaAgentKit) { super(); @@ -257,10 +316,13 @@ export class SolanaRegisterDomainTool extends Tool { protected async _call(input: string): Promise { try { - const parsedInput = toJSON(input); + const parsedInput = JSON.parse(input); this.validateInput(parsedInput); - const tx = await this.action.handler(this.solanaKit, parsedInput); + const tx = await this.solanaKit.registerDomain( + parsedInput.name, + parsedInput.spaceKB || 1, + ); return JSON.stringify({ status: "success", @@ -280,9 +342,14 @@ export class SolanaRegisterDomainTool extends Tool { } export class SolanaResolveDomainTool extends Tool { - private action = resolveSolDomainAction; - name = this.action.name; - description = this.action.description; + name = "solana_resolve_domain"; + description = `Resolve ONLY .sol domain names to a Solana PublicKey. + This tool is exclusively for .sol domains. + DO NOT use this for other domain types like .blink, .bonk, etc. + + Inputs: + domain: string, eg "pumpfun.sol" (required) + `; constructor(private solanaKit: SolanaAgentKit) { super(); @@ -290,8 +357,8 @@ export class SolanaResolveDomainTool extends Tool { protected async _call(input: string): Promise { try { - const domain = { domain: input.trim() }; - const publicKey = await this.action.handler(this.solanaKit, domain); + const domain = input.trim(); + const publicKey = await this.solanaKit.resolveSolDomain(domain); return JSON.stringify({ status: "success", @@ -309,9 +376,12 @@ export class SolanaResolveDomainTool extends Tool { } export class SolanaGetDomainTool extends Tool { - private action = getPrimaryDomainAction; - name = this.action.name; - description = this.action.description; + name = "solana_get_domain"; + description = `Retrieve the .sol domain associated for a given account address. + + Inputs: + account: string, eg "4Be9CvxqHW6BYiRAxW9Q3xu1ycTMWaL5z8NX4HR3ha7t" (required) + `; constructor(private solanaKit: SolanaAgentKit) { super(); @@ -351,10 +421,18 @@ export class SolanaGetWalletAddressTool extends Tool { } export class SolanaPumpfunTokenLaunchTool extends Tool { - private action = launchPumpfunTokenAction; - name = this.action.name; + name = "solana_launch_pumpfun_token"; - description = this.action.description; + description = `This tool can be used to launch a token on Pump.fun, + do not use this tool for any other purpose, or for creating SPL tokens. + If the user asks you to chose the parameters, you should generate valid values. + For generating the image, you can use the solana_create_image tool. + + Inputs: + tokenName: string, eg "PumpFun Token", + tokenTicker: string, eg "PUMP", + description: string, eg "PumpFun Token is a token on the Solana blockchain", + imageUrl: string, eg "https://i.imgur.com/UFm07Np_d.png`; constructor(private solanaKit: SolanaAgentKit) { super(); @@ -390,9 +468,17 @@ export class SolanaPumpfunTokenLaunchTool extends Tool { this.validateInput(parsedInput); // Launch token with validated input - await this.action.handler( - this.solanaKit, - parsedInput, + await this.solanaKit.launchPumpFunToken( + parsedInput.tokenName, + parsedInput.tokenTicker, + parsedInput.description, + parsedInput.imageUrl, + { + twitter: parsedInput.twitter, + telegram: parsedInput.telegram, + website: parsedInput.website, + initialLiquiditySOL: parsedInput.initialLiquiditySOL, + }, ); return JSON.stringify({ @@ -412,9 +498,9 @@ export class SolanaPumpfunTokenLaunchTool extends Tool { } export class SolanaCreateImageTool extends Tool { - private action = createImageAction; - name = this.action.name; - description = this.action.description; + name = "solana_create_image"; + description = + "Create an image using OpenAI's DALL-E. Input should be a string prompt for the image."; constructor(private solanaKit: SolanaAgentKit) { super(); @@ -429,8 +515,7 @@ export class SolanaCreateImageTool extends Tool { protected async _call(input: string): Promise { try { this.validateInput(input); - const parsedInput = JSON.parse(input); - const result = await this.action.handler(this.solanaKit, parsedInput); + const result = await create_image(this.solanaKit, input.trim()); return JSON.stringify({ status: "success", @@ -448,9 +533,11 @@ export class SolanaCreateImageTool extends Tool { } export class SolanaLendAssetTool extends Tool { - private action = lendAssetAction; - name = this.action.name; - description = this.action.description; + name = "solana_lend_asset"; + description = `Lend idle USDC for yield using Lulo. ( only USDC is supported ) + + Inputs (input is a json string): + amount: number, eg 1, 0.01 (required)`; constructor(private solanaKit: SolanaAgentKit) { super(); @@ -460,13 +547,13 @@ export class SolanaLendAssetTool extends Tool { try { const amount = JSON.parse(input).amount || input; - const tx = await this.action.handler(this.solanaKit, { amount }); + const tx = await this.solanaKit.lendAssets(amount); return JSON.stringify({ status: "success", message: "Asset lent successfully", transaction: tx, - amount: amount, + amount, }); } catch (error: any) { return JSON.stringify({ @@ -479,9 +566,8 @@ export class SolanaLendAssetTool extends Tool { } export class SolanaTPSCalculatorTool extends Tool { - private action = getTPSAction; - name = this.action.name; - description = this.action.description; + name = "solana_get_tps"; + description = "Get the current TPS of the Solana network"; constructor(private solanaKit: SolanaAgentKit) { super(); @@ -489,7 +575,7 @@ export class SolanaTPSCalculatorTool extends Tool { async _call(_input: string): Promise { try { - const tps = await this.action.handler(this.solanaKit, {}); + const tps = await this.solanaKit.getTPS(); return `Solana (mainnet-beta) current transactions per second: ${tps}`; } catch (error: any) { return `Error fetching TPS: ${error.message}`; @@ -498,9 +584,11 @@ export class SolanaTPSCalculatorTool extends Tool { } export class SolanaStakeTool extends Tool { - private action = stakeWithJupAction; - name = this.action.name; - description = this.action.description; + name = "solana_stake"; + description = `This tool can be used to stake your SOL (Solana), also called as SOL staking or liquid staking. + + Inputs ( input is a JSON string ): + amount: number, eg 1 or 0.01 (required)`; constructor(private solanaKit: SolanaAgentKit) { super(); @@ -510,7 +598,7 @@ export class SolanaStakeTool extends Tool { try { const parsedInput = JSON.parse(input) || Number(input); - const tx = await this.action.handler(this.solanaKit, parsedInput); + const tx = await this.solanaKit.stake(parsedInput.amount); return JSON.stringify({ status: "success", @@ -532,9 +620,11 @@ export class SolanaStakeTool extends Tool { * Tool to fetch the price of a token in USDC */ export class SolanaFetchPriceTool extends Tool { - private action = fetchPriceAction; - name = this.action.name; - description = this.action.description; + name = "solana_fetch_price"; + description = `Fetch the price of a given token in USDC. + + Inputs: + - tokenId: string, the mint address of the token, e.g., "JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN"`; constructor(private solanaKit: SolanaAgentKit) { super(); @@ -542,9 +632,7 @@ export class SolanaFetchPriceTool extends Tool { async _call(input: string): Promise { try { - - const parsedInput = { tokenId: input.trim() }; - const price = await this.action.handler(this.solanaKit, parsedInput); + const price = await this.solanaKit.fetchTokenPrice(input.trim()); return JSON.stringify({ status: "success", tokenId: input.trim(), @@ -561,9 +649,11 @@ export class SolanaFetchPriceTool extends Tool { } export class SolanaTokenDataTool extends Tool { - private action = getTokenDataAction - name = this.action.name; - description = this.action.description; + name = "solana_token_data"; + description = `Get the token data for a given token mint address + + Inputs: mintAddress is required. + mintAddress: string, eg "So11111111111111111111111111111111111111112" (required)`; constructor(private solanaKit: SolanaAgentKit) { super(); @@ -571,13 +661,13 @@ export class SolanaTokenDataTool extends Tool { protected async _call(input: string): Promise { try { + const parsedInput = input.trim(); - const parsedInput = JSON.parse(input); - const tokenData = await this.action.handler(this.solanaKit, parsedInput); + const tokenData = await this.solanaKit.getTokenDataByAddress(parsedInput); return JSON.stringify({ status: "success", - tokenData: tokenData, + tokenData, }); } catch (error: any) { return JSON.stringify({ @@ -590,9 +680,11 @@ export class SolanaTokenDataTool extends Tool { } export class SolanaTokenDataByTickerTool extends Tool { - private action = tokenDataByTickerAction; - name = this.action.name; - description = this.action.description; + name = "solana_token_data_by_ticker"; + description = `Get the token data for a given token ticker + + Inputs: ticker is required. + ticker: string, eg "USDC" (required)`; constructor(private solanaKit: SolanaAgentKit) { super(); @@ -601,10 +693,10 @@ export class SolanaTokenDataByTickerTool extends Tool { protected async _call(input: string): Promise { try { const ticker = input.trim(); - const tokenData = await this.action.handler(this.solanaKit, { ticker }); + const tokenData = await this.solanaKit.getTokenDataByTicker(ticker); return JSON.stringify({ status: "success", - tokenData: tokenData, + tokenData, }); } catch (error: any) { return JSON.stringify({ @@ -617,9 +709,16 @@ export class SolanaTokenDataByTickerTool extends Tool { } export class SolanaCompressedAirdropTool extends Tool { - private action = compressedAirdropAction; - name = this.action.name; - description = this.action.description; + name = "solana_compressed_airdrop"; + description = `Airdrop SPL tokens with ZK Compression (also called as airdropping tokens) + + Inputs (input is a JSON string): + mintAddress: string, the mint address of the token, e.g., "JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN" (required) + amount: number, the amount of tokens to airdrop per recipient, e.g., 42 (required) + decimals: number, the decimals of the token, e.g., 6 (required) + recipients: string[], the recipient addresses, e.g., ["1nc1nerator11111111111111111111111111111111"] (required) + priorityFeeInLamports: number, the priority fee in lamports. Default is 30_000. (optional) + shouldLog: boolean, whether to log progress to stdout. Default is false. (optional)`; constructor(private solanaKit: SolanaAgentKit) { super(); @@ -628,7 +727,16 @@ export class SolanaCompressedAirdropTool extends Tool { protected async _call(input: string): Promise { try { const parsedInput = JSON.parse(input); - const txs = await this.action.handler(this.solanaKit, parsedInput); + + const txs = await this.solanaKit.sendCompressedAirdrop( + parsedInput.mintAddress, + parsedInput.amount, + parsedInput.decimals, + parsedInput.recipients, + parsedInput.priorityFeeInLamports || 30_000, + parsedInput.shouldLog || false, + ); + return JSON.stringify({ status: "success", message: `Airdropped ${parsedInput.amount} tokens to ${parsedInput.recipients.length} recipients.`, @@ -644,60 +752,16 @@ export class SolanaCompressedAirdropTool extends Tool { } } -export class SolanaCreateSingleSidedWhirlpoolTool extends Tool { - private action = createOrcaSingleSidedWhirlpoolAction; - name = this.action.name; - description = this.action.description; - - constructor(private solanaKit: SolanaAgentKit) { - super(); - } - - async _call(input: string): Promise { - try { - const inputFormat = JSON.parse(input); - const depositTokenAmount = new BN(inputFormat.depositTokenAmount); - const depositTokenMint = new PublicKey(inputFormat.depositTokenMint); - const otherTokenMint = new PublicKey(inputFormat.otherTokenMint); - const initialPrice = new Decimal(inputFormat.initialPrice); - const maxPrice = new Decimal(inputFormat.maxPrice); - const feeTier = inputFormat.feeTier; - - if (!feeTier || !(feeTier in FEE_TIERS)) { - throw new Error( - `Invalid feeTier.Available options: ${Object.keys(FEE_TIERS).join( - ", ", - ) - } `, - ); - } - const txId = await this.action.handler(this.solanaKit, { - depositTokenAmount, - depositTokenMint, - otherTokenMint, - initialPrice, - maxPrice, - feeTier, - }); - return JSON.stringify({ - status: "success", - message: "Single-sided Whirlpool created successfully", - transaction: txId, - }); - } catch (error: any) { - return JSON.stringify({ - status: "error", - message: error.message, - code: error.code || "UNKNOWN_ERROR", - }); - } - } -} - export class SolanaRaydiumCreateAmmV4 extends Tool { - private action = raydiumCreateAmmV4Action; - name = this.action.name; - description = this.action.description; + name = "raydium_create_ammV4"; + description = `Raydium's Legacy AMM that requires an OpenBook marketID + + Inputs (input is a json string): + marketId: string (required) + baseAmount: number(int), eg: 111111 (required) + quoteAmount: number(int), eg: 111111 (required) + startTime: number(seconds), eg: now number or zero (required) + `; constructor(private solanaKit: SolanaAgentKit) { super(); @@ -707,11 +771,16 @@ export class SolanaRaydiumCreateAmmV4 extends Tool { try { const inputFormat = JSON.parse(input); - const tx = await this.action.handler(this.solanaKit, inputFormat); + const tx = await this.solanaKit.raydiumCreateAmmV4( + new PublicKey(inputFormat.marketId), + new BN(inputFormat.baseAmount), + new BN(inputFormat.quoteAmount), + new BN(inputFormat.startTime), + ); return JSON.stringify({ status: "success", - message: "Create raydium amm v4 pool successfully", + message: "Raydium amm v4 pool created successfully", transaction: tx, }); } catch (error: any) { @@ -725,9 +794,16 @@ export class SolanaRaydiumCreateAmmV4 extends Tool { } export class SolanaRaydiumCreateClmm extends Tool { - private action = raydiumCreateClmmAction; - name = this.action.name; - description = this.action.description; + name = "raydium_create_clmm"; + description = `Concentrated liquidity market maker, custom liquidity ranges, increased capital efficiency + + Inputs (input is a json string): + mint1: string (required) + mint2: string (required) + configId: string (required) stores pool info, id, index, protocolFeeRate, tradeFeeRate, tickSpacing, fundFeeRate + initialPrice: number, eg: 123.12 (required) + startTime: number(seconds), eg: now number or zero (required) + `; constructor(private solanaKit: SolanaAgentKit) { super(); @@ -737,11 +813,19 @@ export class SolanaRaydiumCreateClmm extends Tool { try { const inputFormat = JSON.parse(input); - const tx = await this.action.handler(this.solanaKit, inputFormat); + const tx = await this.solanaKit.raydiumCreateClmm( + new PublicKey(inputFormat.mint1), + new PublicKey(inputFormat.mint2), + + new PublicKey(inputFormat.configId), + + new Decimal(inputFormat.initialPrice), + new BN(inputFormat.startTime), + ); return JSON.stringify({ status: "success", - message: "Create raydium clmm pool successfully", + message: "Raydium clmm pool created successfully", transaction: tx, }); } catch (error: any) { @@ -755,9 +839,17 @@ export class SolanaRaydiumCreateClmm extends Tool { } export class SolanaRaydiumCreateCpmm extends Tool { - private action = raydiumCreateCpmmAction; - name = this.action.name; - description = this.action.description; + name = "raydium_create_cpmm"; + description = `Raydium's newest CPMM, does not require marketID, supports Token 2022 standard + + Inputs (input is a json string): + mint1: string (required) + mint2: string (required) + configId: string (required), stores pool info, index, protocolFeeRate, tradeFeeRate, fundFeeRate, createPoolFee + mintAAmount: number(int), eg: 1111 (required) + mintBAmount: number(int), eg: 2222 (required) + startTime: number(seconds), eg: now number or zero (required) + `; constructor(private solanaKit: SolanaAgentKit) { super(); @@ -766,11 +858,22 @@ export class SolanaRaydiumCreateCpmm extends Tool { async _call(input: string): Promise { try { const inputFormat = JSON.parse(input); - const tx = await this.action.handler(this.solanaKit, inputFormat); + + const tx = await this.solanaKit.raydiumCreateCpmm( + new PublicKey(inputFormat.mint1), + new PublicKey(inputFormat.mint2), + + new PublicKey(inputFormat.configId), + + new BN(inputFormat.mintAAmount), + new BN(inputFormat.mintBAmount), + + new BN(inputFormat.startTime), + ); return JSON.stringify({ status: "success", - message: "Create raydium cpmm pool successfully", + message: "Raydium cpmm pool created successfully", transaction: tx, }); } catch (error: any) { @@ -784,9 +887,15 @@ export class SolanaRaydiumCreateCpmm extends Tool { } export class SolanaOpenbookCreateMarket extends Tool { - private action = createOpenbookMarketAction; - name = this.action.name; - description = this.action.description; + name = "solana_openbook_create_market"; + description = `Openbook marketId, required for ammv4 + + Inputs (input is a json string): + baseMint: string (required) + quoteMint: string (required) + lotSize: number (required) + tickSize: number (required) + `; constructor(private solanaKit: SolanaAgentKit) { super(); @@ -795,11 +904,18 @@ export class SolanaOpenbookCreateMarket extends Tool { async _call(input: string): Promise { try { const inputFormat = JSON.parse(input); - const tx = await this.action.handler(this.solanaKit, inputFormat); + + const tx = await this.solanaKit.openbookCreateMarket( + new PublicKey(inputFormat.baseMint), + new PublicKey(inputFormat.quoteMint), + + inputFormat.lotSize, + inputFormat.tickSize, + ); return JSON.stringify({ status: "success", - message: "Create openbook market successfully", + message: "Openbook market created successfully", transaction: tx, }); } catch (error: any) { @@ -813,9 +929,11 @@ export class SolanaOpenbookCreateMarket extends Tool { } export class SolanaPythFetchPrice extends Tool { - private action = pythFetchPriceAction; - name = this.action.name; - description = this.action.description; + name = "solana_pyth_fetch_price"; + description = `Fetch the price of a given price feed from Pyth's Hermes service + + Inputs: + priceFeedID: string, the price feed ID, e.g., "0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43" for BTC/USD`; constructor(private solanaKit: SolanaAgentKit) { super(); @@ -823,12 +941,11 @@ export class SolanaPythFetchPrice extends Tool { async _call(input: string): Promise { try { - const parsedInput = { tokenId: input.trim() }; - const price = await this.action.handler(this.solanaKit, parsedInput); + const price = await this.solanaKit.pythFetchPrice(input); const response: PythFetchPriceResponse = { status: "success", priceFeedID: input, - price: price.price, + price, }; return JSON.stringify(response); } catch (error: any) { @@ -844,9 +961,13 @@ export class SolanaPythFetchPrice extends Tool { } export class SolanaResolveAllDomainsTool extends Tool { - private action = resolveDomainAction; - name = this.action.name; - description = this.action.description; + name = "solana_resolve_all_domains"; + description = `Resolve domain names to a public key for ALL domain types EXCEPT .sol domains. + Use this for domains like .blink, .bonk, etc. + DO NOT use this for .sol domains (use solana_resolve_domain instead). + + Input: + domain: string, eg "mydomain.blink" or "mydomain.bonk" (required)`; constructor(private solanaKit: SolanaAgentKit) { super(); @@ -854,8 +975,7 @@ export class SolanaResolveAllDomainsTool extends Tool { async _call(input: string): Promise { try { - const parsedInput = JSON.parse(input); - const owner = await this.action.handler(this.solanaKit, parsedInput); + const owner = await this.solanaKit.resolveAllDomains(input); if (!owner) { return JSON.stringify({ @@ -881,9 +1001,11 @@ export class SolanaResolveAllDomainsTool extends Tool { } export class SolanaGetOwnedDomains extends Tool { - private action = getOwnedDomainsForTLDAction; - name = this.action.name; - description = this.action.description; + name = "solana_get_owned_domains"; + description = `Get all domains owned by a specific wallet address. + + Inputs: + owner: string, eg "4Be9CvxqHW6BYiRAxW9Q3xu1ycTMWaL5z8NX4HR3ha7t" (required)`; constructor(private solanaKit: SolanaAgentKit) { super(); @@ -892,12 +1014,12 @@ export class SolanaGetOwnedDomains extends Tool { async _call(input: string): Promise { try { const ownerPubkey = new PublicKey(input.trim()); - const domains = await this.action.handler(this.solanaKit, ownerPubkey); + const domains = await this.solanaKit.getOwnedAllDomains(ownerPubkey); return JSON.stringify({ status: "success", message: "Owned domains fetched successfully", - domains: domains, + domains, }); } catch (error: any) { return JSON.stringify({ @@ -910,9 +1032,11 @@ export class SolanaGetOwnedDomains extends Tool { } export class SolanaGetOwnedTldDomains extends Tool { - private action = getOwnedDomainsForTLDAction; - name = this.action.name; - description = this.action.description; + name = "solana_get_owned_tld_domains"; + description = `Get all domains owned by the agent's wallet for a specific TLD. + + Inputs: + tld: string, eg "bonk" (required)`; constructor(private solanaKit: SolanaAgentKit) { super(); @@ -920,12 +1044,12 @@ export class SolanaGetOwnedTldDomains extends Tool { async _call(input: string): Promise { try { - const domains = await this.action.handler(this.solanaKit, { tld: input }); + const domains = await this.solanaKit.getOwnedDomainsForTLD(input); return JSON.stringify({ status: "success", message: "TLD domains fetched successfully", - domains: domains, + domains, }); } catch (error: any) { return JSON.stringify({ @@ -938,9 +1062,8 @@ export class SolanaGetOwnedTldDomains extends Tool { } export class SolanaGetAllTlds extends Tool { - private action = getAllDomainsTLDsAction; - name = this.action.name; - description = this.action.description; + name = "solana_get_all_tlds"; + description = `Get all active top-level domains (TLDs) in the AllDomains Name Service`; constructor(private solanaKit: SolanaAgentKit) { super(); @@ -948,12 +1071,12 @@ export class SolanaGetAllTlds extends Tool { async _call(): Promise { try { - const tlds = await this.action.handler(this.solanaKit, {}); + const tlds = await this.solanaKit.getAllDomainsTLDs(); return JSON.stringify({ status: "success", message: "TLDs fetched successfully", - tlds: tlds, + tlds, }); } catch (error: any) { return JSON.stringify({ @@ -966,9 +1089,12 @@ export class SolanaGetAllTlds extends Tool { } export class SolanaGetMainDomain extends Tool { - private action = getMainAllDomainsDomainAction; - name = this.action.name; - description = this.action.description; + name = "solana_get_main_domain"; + description = `Get the main/favorite domain for a given wallet address. + + Inputs: + owner: string, eg "4Be9CvxqHW6BYiRAxW9Q3xu1ycTMWaL5z8NX4HR3ha7t" (required)`; + constructor(private solanaKit: SolanaAgentKit) { super(); } @@ -977,7 +1103,7 @@ export class SolanaGetMainDomain extends Tool { try { const ownerPubkey = new PublicKey(input.trim()); const mainDomain = - await this.action.handler(this.solanaKit, ownerPubkey); + await this.solanaKit.getMainAllDomainsDomain(ownerPubkey); return JSON.stringify({ status: "success", @@ -995,9 +1121,19 @@ export class SolanaGetMainDomain extends Tool { } export class SolanaCreateGibworkTask extends Tool { - private action = createGibworkTaskAction; - name = this.action.name; - description = this.action.description; + name = "create_gibwork_task"; + description = `Create a task on Gibwork. + + Inputs (input is a JSON string): + title: string, title of the task (required) + content: string, description of the task (required) + requirements: string, requirements to complete the task (required) + tags: string[], list of tags associated with the task (required) + payer: string, payer address (optional, defaults to agent wallet) + tokenMintAddress: string, the mint address of the token, e.g., "JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN" (required) + amount: number, payment amount (required) + `; + constructor(private solanaSdk: SolanaAgentKit) { super(); } @@ -1005,7 +1141,16 @@ export class SolanaCreateGibworkTask extends Tool { protected async _call(input: string): Promise { try { const parsedInput = JSON.parse(input); - const taskData = await this.action.handler(this.solanaSdk, parsedInput); + + const taskData = await this.solanaSdk.createGibworkTask( + parsedInput.title, + parsedInput.content, + parsedInput.requirements, + parsedInput.tags, + parsedInput.tokenMintAddress, + parsedInput.amount, + parsedInput.payer, + ); const response: GibworkCreateTaskReponse = { status: "success", @@ -1049,7 +1194,6 @@ export function createSolanaTools(solanaKit: SolanaAgentKit) { new SolanaRaydiumCreateClmm(solanaKit), new SolanaRaydiumCreateCpmm(solanaKit), new SolanaOpenbookCreateMarket(solanaKit), - new SolanaCreateSingleSidedWhirlpoolTool(solanaKit), new SolanaPythFetchPrice(solanaKit), new SolanaResolveDomainTool(solanaKit), new SolanaGetOwnedDomains(solanaKit), From e438f8e4fa4c330d12ddeff8fca190040674d664 Mon Sep 17 00:00:00 2001 From: aryan Date: Wed, 1 Jan 2025 02:56:34 +0530 Subject: [PATCH 11/11] fix: dependency --- package.json | 13 +++++++------ pnpm-lock.yaml | 3 +++ src/types/index.ts | 1 + 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index abd3301..564ecd3 100644 --- a/package.json +++ b/package.json @@ -50,19 +50,20 @@ "form-data": "^4.0.1", "langchain": "^0.3.6", "openai": "^4.75.0", - "typedoc": "^0.26.11" + "typedoc": "^0.26.11", + "zod": "^3.24.1" }, "devDependencies": { "@types/bn.js": "^5.1.5", "@types/chai": "^5.0.1", "@types/node": "^22.9.0", - "ts-node": "^10.9.2", - "typescript": "^5.7.2", + "@typescript-eslint/eslint-plugin": "^7.0.0", + "@typescript-eslint/parser": "^7.0.0", "eslint": "^8.56.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-prettier": "^5.1.3", - "@typescript-eslint/eslint-plugin": "^7.0.0", - "@typescript-eslint/parser": "^7.0.0", - "prettier": "^3.2.5" + "prettier": "^3.2.5", + "ts-node": "^10.9.2", + "typescript": "^5.7.2" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ddfc583..7e2d727 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -98,6 +98,9 @@ importers: typedoc: specifier: ^0.26.11 version: 0.26.11(typescript@5.7.2) + zod: + specifier: ^3.24.1 + version: 3.24.1 devDependencies: '@types/bn.js': specifier: ^5.1.5 diff --git a/src/types/index.ts b/src/types/index.ts index 95d1fc5..3d27431 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -91,3 +91,4 @@ export interface GibworkCreateTaskReponse { taskId?: string | undefined; signature?: string | undefined; } +