Files
solana-agent-kit/src/langchain/index.ts
2025-01-06 20:39:55 +05:30

2311 lines
68 KiB
TypeScript

import { PublicKey } from "@solana/web3.js";
import { BN } from "@coral-xyz/anchor";
import Decimal from "decimal.js";
import { Tool } from "langchain/tools";
import {
GibworkCreateTaskReponse,
OrderParams,
PythFetchPriceResponse,
SolanaAgentKit,
} from "../index";
import { create_image, FEE_TIERS, generateOrdersfromPattern } from "../tools";
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 ( input is a JSON string ):
tokenAddress: string, eg "So11111111111111111111111111111111111111112" (optional)`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
protected async _call(input: string): Promise<string> {
try {
const tokenAddress = input ? new PublicKey(input) : undefined;
const balance = await this.solanaKit.getBalance(tokenAddress);
return JSON.stringify({
status: "success",
balance,
token: input || "SOL",
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
code: error.code || "UNKNOWN_ERROR",
});
}
}
}
export class SolanaBalanceOtherTool extends Tool {
name = "solana_balance_other";
description = `Get the balance of a Solana wallet or token account which is different from the agent's wallet.
If no tokenAddress is provided, the SOL balance of the wallet will be returned.
Inputs ( input is a JSON string ):
walletAddress: string, eg "GDEkQF7UMr7RLv1KQKMtm8E2w3iafxJLtyXu3HVQZnME" (required)
tokenAddress: string, eg "SENDdRQtYMWaQrBroBrJ2Q53fgVuq95CV9UPGEvpCxa" (optional)`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
protected async _call(input: string): Promise<string> {
try {
const { walletAddress, tokenAddress } = JSON.parse(input);
const tokenPubKey = tokenAddress
? new PublicKey(tokenAddress)
: undefined;
const balance = await this.solanaKit.getBalanceOther(
new PublicKey(walletAddress),
tokenPubKey,
);
return JSON.stringify({
status: "success",
balance,
wallet: walletAddress,
token: tokenAddress || "SOL",
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
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)`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
protected async _call(input: string): Promise<string> {
try {
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,
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
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)`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
protected async _call(input: string): Promise<string> {
try {
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,
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
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)`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
protected async _call(input: string): Promise<string> {
try {
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,
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
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()}`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
protected async _call(input: string): Promise<string> {
try {
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(),
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
code: error.code || "UNKNOWN_ERROR",
});
}
}
}
export class SolanaPerpCloseTradeTool extends Tool {
name = "solana_close_perp_trade";
description = `This tool can be used to close perpetuals trade ( It uses Adrena Protocol ).
Inputs ( input is a JSON string ):
tradeMint: string, eg "J1toso1uCk3RLmjorhTtrVwY9HJ7X8V9yYac6Y7kGCPn", "DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263" etc. (optional)
price?: number, eg 100 (optional)
side: string, eg: "long" or "short"`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
protected async _call(input: string): Promise<string> {
try {
const parsedInput = JSON.parse(input);
const tx =
parsedInput.side === "long"
? await this.solanaKit.closePerpTradeLong({
price: parsedInput.price,
tradeMint: new PublicKey(parsedInput.tradeMint),
})
: await this.solanaKit.closePerpTradeShort({
price: parsedInput.price,
tradeMint: new PublicKey(parsedInput.tradeMint),
});
return JSON.stringify({
status: "success",
message: "Perpetual trade closed successfully",
transaction: tx,
price: parsedInput.price,
tradeMint: new PublicKey(parsedInput.tradeMint),
side: parsedInput.side,
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
code: error.code || "UNKNOWN_ERROR",
});
}
}
}
export class SolanaPerpOpenTradeTool extends Tool {
name = "solana_open_perp_trade";
description = `This tool can be used to open perpetuals trade ( It uses Adrena Protocol ).
Inputs ( input is a JSON string ):
collateralAmount: number, eg 1 or 0.01 (required)
collateralMint: string, eg "J1toso1uCk3RLmjorhTtrVwY9HJ7X8V9yYac6Y7kGCPn" or "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v" etc. (optional)
tradeMint: string, eg "J1toso1uCk3RLmjorhTtrVwY9HJ7X8V9yYac6Y7kGCPn", "DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263" etc. (optional)
leverage: number, eg 50000 = x5, 100000 = x10, 1000000 = x100 (optional)
price?: number, eg 100 (optional)
slippage?: number, eg 0.3 (optional)
side: string, eg: "long" or "short"`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
protected async _call(input: string): Promise<string> {
try {
const parsedInput = JSON.parse(input);
const tx =
parsedInput.side === "long"
? await this.solanaKit.openPerpTradeLong({
price: parsedInput.price,
collateralAmount: parsedInput.collateralAmount,
collateralMint: new PublicKey(parsedInput.collateralMint),
leverage: parsedInput.leverage,
tradeMint: new PublicKey(parsedInput.tradeMint),
slippage: parsedInput.slippage,
})
: await this.solanaKit.openPerpTradeLong({
price: parsedInput.price,
collateralAmount: parsedInput.collateralAmount,
collateralMint: new PublicKey(parsedInput.collateralMint),
leverage: parsedInput.leverage,
tradeMint: new PublicKey(parsedInput.tradeMint),
slippage: parsedInput.slippage,
});
return JSON.stringify({
status: "success",
message: "Perpetual trade opened successfully",
transaction: tx,
price: parsedInput.price,
collateralAmount: parsedInput.collateralAmount,
collateralMint: new PublicKey(parsedInput.collateralMint),
leverage: parsedInput.leverage,
tradeMint: new PublicKey(parsedInput.tradeMint),
slippage: parsedInput.slippage,
side: parsedInput.side,
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
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)`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
protected async _call(input: string): Promise<string> {
try {
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,
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
code: error.code || "UNKNOWN_ERROR",
});
}
}
}
export class SolanaLimitOrderTool extends Tool {
name = "solana_limit_order";
description = `This tool can be used to place limit orders using Manifest.
Do not allow users to place multiple orders with this instruction, use solana_batch_order instead.
Inputs ( input is a JSON string ):
marketId: PublicKey, eg "ENhU8LsaR7vDD2G1CsWcsuSGNrih9Cv5WZEk7q9kPapQ" for SOL/USDC (required)
quantity: number, eg 1 or 0.01 (required)
side: string, eg "Buy" or "Sell" (required)
price: number, in tokens eg 200 for SOL/USDC (required)`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
protected async _call(input: string): Promise<string> {
try {
const parsedInput = JSON.parse(input);
const tx = await this.solanaKit.limitOrder(
new PublicKey(parsedInput.marketId),
parsedInput.quantity,
parsedInput.side,
parsedInput.price,
);
return JSON.stringify({
status: "success",
message: "Trade executed successfully",
transaction: tx,
marketId: parsedInput.marketId,
quantity: parsedInput.quantity,
side: parsedInput.side,
price: parsedInput.price,
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
code: error.code || "UNKNOWN_ERROR",
});
}
}
}
export class SolanaBatchOrderTool extends Tool {
name = "solana_batch_order";
description = `Places multiple limit orders in one transaction using Manifest. Submit orders either as a list or pattern:
1. List format:
{
"marketId": "ENhU8LsaR7vDD2G1CsWcsuSGNrih9Cv5WZEk7q9kPapQ",
"orders": [
{ "quantity": 1, "side": "Buy", "price": 200 },
{ "quantity": 0.5, "side": "Sell", "price": 205 }
]
}
2. Pattern format:
{
"marketId": "ENhU8LsaR7vDD2G1CsWcsuSGNrih9Cv5WZEk7q9kPapQ",
"pattern": {
"side": "Buy",
"totalQuantity": 100,
"priceRange": { "max": 1.0 },
"spacing": { "type": "percentage", "value": 1 },
"numberOfOrders": 5
}
}
Examples:
- "Place 5 buy orders totaling 100 tokens, 1% apart below $1"
- "Create 3 sell orders of 10 tokens each between $50-$55"
- "Place buy orders worth 50 tokens, $0.10 spacing from $0.80"
Important: All orders must be in one transaction. Combine buy and sell orders into a single pattern or list. Never break the orders down to individual buy or sell orders.`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
protected async _call(input: string): Promise<string> {
try {
const parsedInput = JSON.parse(input);
let ordersToPlace: OrderParams[] = [];
if (!parsedInput.marketId) {
throw new Error("Market ID is required");
}
if (parsedInput.pattern) {
ordersToPlace = generateOrdersfromPattern(parsedInput.pattern);
} else if (Array.isArray(parsedInput.orders)) {
ordersToPlace = parsedInput.orders;
} else {
throw new Error("Either pattern or orders array is required");
}
if (ordersToPlace.length === 0) {
throw new Error("No orders generated or provided");
}
ordersToPlace.forEach((order: OrderParams, index: number) => {
if (!order.quantity || !order.side || !order.price) {
throw new Error(
`Invalid order at index ${index}: quantity, side, and price are required`,
);
}
if (order.side !== "Buy" && order.side !== "Sell") {
throw new Error(
`Invalid side at index ${index}: must be "Buy" or "Sell"`,
);
}
});
const tx = await this.solanaKit.batchOrder(
new PublicKey(parsedInput.marketId),
parsedInput.orders,
);
return JSON.stringify({
status: "success",
message: "Batch order executed successfully",
transaction: tx,
marketId: parsedInput.marketId,
orders: parsedInput.orders,
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
code: error.code || "UNKNOWN_ERROR",
});
}
}
}
export class SolanaCancelAllOrdersTool extends Tool {
name = "solana_cancel_all_orders";
description = `This tool can be used to cancel all orders from a Manifest market.
Input ( input is a JSON string ):
marketId: string, eg "ENhU8LsaR7vDD2G1CsWcsuSGNrih9Cv5WZEk7q9kPapQ" for SOL/USDC (required)`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
protected async _call(input: string): Promise<string> {
try {
const marketId = new PublicKey(input.trim());
const tx = await this.solanaKit.cancelAllOrders(marketId);
return JSON.stringify({
status: "success",
message: "Cancel orders successfully",
transaction: tx,
marketId,
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
code: error.code || "UNKNOWN_ERROR",
});
}
}
}
export class SolanaWithdrawAllTool extends Tool {
name = "solana_withdraw_all";
description = `This tool can be used to withdraw all funds from a Manifest market.
Input ( input is a JSON string ):
marketId: string, eg "ENhU8LsaR7vDD2G1CsWcsuSGNrih9Cv5WZEk7q9kPapQ" for SOL/USDC (required)`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
protected async _call(input: string): Promise<string> {
try {
const marketId = new PublicKey(input.trim());
const tx = await this.solanaKit.withdrawAll(marketId);
return JSON.stringify({
status: "success",
message: "Withdrew successfully",
transaction: tx,
marketId,
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
code: error.code || "UNKNOWN_ERROR",
});
}
}
}
export class SolanaRequestFundsTool extends Tool {
name = "solana_request_funds";
description = "Request SOL from Solana faucet (devnet/testnet only)";
constructor(private solanaKit: SolanaAgentKit) {
super();
}
protected async _call(_input: string): Promise<string> {
try {
await this.solanaKit.requestFaucetFunds();
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",
message: error.message,
code: error.code || "UNKNOWN_ERROR",
});
}
}
}
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)
`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
private validateInput(input: any): void {
if (!input.name || typeof input.name !== "string") {
throw new Error("name is required and must be a string");
}
if (
input.spaceKB !== undefined &&
(typeof input.spaceKB !== "number" || input.spaceKB <= 0)
) {
throw new Error("spaceKB must be a positive number when provided");
}
}
protected async _call(input: string): Promise<string> {
try {
const parsedInput = JSON.parse(input);
this.validateInput(parsedInput);
const tx = await this.solanaKit.registerDomain(
parsedInput.name,
parsedInput.spaceKB || 1,
);
return JSON.stringify({
status: "success",
message: "Domain registered successfully",
transaction: tx,
domain: `${parsedInput.name}.sol`,
spaceKB: parsedInput.spaceKB || 1,
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
code: error.code || "UNKNOWN_ERROR",
});
}
}
}
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)
`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
protected async _call(input: string): Promise<string> {
try {
const domain = input.trim();
const publicKey = await this.solanaKit.resolveSolDomain(domain);
return JSON.stringify({
status: "success",
message: "Domain resolved successfully",
publicKey: publicKey.toBase58(),
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
code: error.code || "UNKNOWN_ERROR",
});
}
}
}
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)
`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
protected async _call(input: string): Promise<string> {
try {
const account = new PublicKey(input.trim());
const domain = await this.solanaKit.getPrimaryDomain(account);
return JSON.stringify({
status: "success",
message: "Primary domain retrieved successfully",
domain,
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
code: error.code || "UNKNOWN_ERROR",
});
}
}
}
export class SolanaGetWalletAddressTool extends Tool {
name = "solana_get_wallet_address";
description = `Get the wallet address of the agent`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
async _call(_input: string): Promise<string> {
return this.solanaKit.wallet_address.toString();
}
}
export class SolanaFlashOpenTrade extends Tool {
name = "solana_flash_open_trade";
description = `This tool can be used to open a new leveraged trading position on Flash.Trade exchange.
Inputs ( input is a JSON string ):
token: string, eg "SOL", "BTC", "ETH" (required)
type: string, eg "long", "short" (required)
collateral: number, eg 10, 100, 1000 (required)
leverage: number, eg 5, 10, 20 (required)
Example prompt is Open a 20x leveraged trade for SOL on long side using flash trade with 500 USD as collateral`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
protected async _call(input: string): Promise<string> {
try {
const parsedInput = JSON.parse(input);
// Validate input parameters
if (!parsedInput.token) {
throw new Error("Token is required, received: " + parsedInput.token);
}
if (!["SOL", "BTC", "ETH", "USDC"].includes(parsedInput.token)) {
throw new Error(
'Token must be one of ["SOL", "BTC", "ETH", "USDC"], received: ' +
parsedInput.token,
);
}
if (!["long", "short"].includes(parsedInput.type)) {
throw new Error(
'Type must be either "long" or "short", received: ' +
parsedInput.type,
);
}
if (!parsedInput.collateral || parsedInput.collateral <= 0) {
throw new Error(
"Collateral amount must be positive, received: " +
parsedInput.collateral,
);
}
if (!parsedInput.leverage || parsedInput.leverage <= 0) {
throw new Error(
"Leverage must be positive, received: " + parsedInput.leverage,
);
}
const tx = await this.solanaKit.flashOpenTrade({
token: parsedInput.token,
side: parsedInput.type,
collateralUsd: parsedInput.collateral,
leverage: parsedInput.leverage,
});
return JSON.stringify({
status: "success",
message: "Flash trade position opened successfully",
transaction: tx,
token: parsedInput.token,
side: parsedInput.type,
collateral: parsedInput.collateral,
leverage: parsedInput.leverage,
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
code: error.code || "UNKNOWN_ERROR",
});
}
}
}
export class SolanaFlashCloseTrade extends Tool {
name = "solana_flash_close_trade";
description = `Close an existing leveraged trading position on Flash.Trade exchange.
Inputs ( input is a JSON string ):
token: string, eg "SOL", "BTC", "ETH" (required)
side: string, eg "long", "short" (required)
Example prompt is Close a 20x leveraged trade for SOL on long side`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
protected async _call(input: string): Promise<string> {
try {
const parsedInput = JSON.parse(input);
// Validate input parameters
if (!parsedInput.token) {
throw new Error("Token is required");
}
if (!["SOL", "BTC", "ETH"].includes(parsedInput.token)) {
throw new Error('Token must be one of ["SOL", "BTC", "ETH"]');
}
if (!["long", "short"].includes(parsedInput.side)) {
throw new Error('Side must be either "long" or "short"');
}
const tx = await this.solanaKit.flashCloseTrade({
token: parsedInput.token,
side: parsedInput.side,
});
return JSON.stringify({
status: "success",
message: "Flash trade position closed successfully",
transaction: tx,
token: parsedInput.token,
side: parsedInput.side,
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
code: error.code || "UNKNOWN_ERROR",
});
}
}
}
export class SolanaPumpfunTokenLaunchTool extends Tool {
name = "solana_launch_pumpfun_token";
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();
}
private validateInput(input: any): void {
if (!input.tokenName || typeof input.tokenName !== "string") {
throw new Error("tokenName is required and must be a string");
}
if (!input.tokenTicker || typeof input.tokenTicker !== "string") {
throw new Error("tokenTicker is required and must be a string");
}
if (!input.description || typeof input.description !== "string") {
throw new Error("description is required and must be a string");
}
if (!input.imageUrl || typeof input.imageUrl !== "string") {
throw new Error("imageUrl is required and must be a string");
}
if (
input.initialLiquiditySOL !== undefined &&
typeof input.initialLiquiditySOL !== "number"
) {
throw new Error("initialLiquiditySOL must be a number when provided");
}
}
protected async _call(input: string): Promise<string> {
try {
// Parse and normalize input
input = input.trim();
const parsedInput = JSON.parse(input);
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,
},
);
return JSON.stringify({
status: "success",
message: "Token launched successfully on Pump.fun",
tokenName: parsedInput.tokenName,
tokenTicker: parsedInput.tokenTicker,
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
code: error.code || "UNKNOWN_ERROR",
});
}
}
}
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.";
constructor(private solanaKit: SolanaAgentKit) {
super();
}
private validateInput(input: string): void {
if (typeof input !== "string" || input.trim().length === 0) {
throw new Error("Input must be a non-empty string prompt");
}
}
protected async _call(input: string): Promise<string> {
try {
this.validateInput(input);
const result = await create_image(this.solanaKit, input.trim());
return JSON.stringify({
status: "success",
message: "Image created successfully",
...result,
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
code: error.code || "UNKNOWN_ERROR",
});
}
}
}
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)`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
async _call(input: string): Promise<string> {
try {
const amount = JSON.parse(input).amount || input;
const tx = await this.solanaKit.lendAssets(amount);
return JSON.stringify({
status: "success",
message: "Asset lent successfully",
transaction: tx,
amount,
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
code: error.code || "UNKNOWN_ERROR",
});
}
}
}
export class SolanaTPSCalculatorTool extends Tool {
name = "solana_get_tps";
description = "Get the current TPS of the Solana network";
constructor(private solanaKit: SolanaAgentKit) {
super();
}
async _call(_input: string): Promise<string> {
try {
const tps = await this.solanaKit.getTPS();
return `Solana (mainnet-beta) current transactions per second: ${tps}`;
} catch (error: any) {
return `Error fetching TPS: ${error.message}`;
}
}
}
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)`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
protected async _call(input: string): Promise<string> {
try {
const parsedInput = JSON.parse(input) || Number(input);
const tx = await this.solanaKit.stake(parsedInput.amount);
return JSON.stringify({
status: "success",
message: "Staked successfully",
transaction: tx,
amount: parsedInput.amount,
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
code: error.code || "UNKNOWN_ERROR",
});
}
}
}
export class SolanaRestakeTool extends Tool {
name = "solana_restake";
description = `This tool can be used to restake your SOL on Solayer to receive Solayer SOL (sSOL) as a Liquid Staking Token (LST).
Inputs:
amount: number, eg 1 or 0.01 (required)`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
protected async _call(input: string): Promise<string> {
try {
const parsedInput = JSON.parse(input) || Number(input);
const tx = await this.solanaKit.restake(parsedInput.amount);
return JSON.stringify({
status: "success",
message: "Staked successfully",
transaction: tx,
amount: parsedInput.amount,
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
code: error.code || "UNKNOWN_ERROR",
});
}
}
}
/**
* 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"`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
async _call(input: string): Promise<string> {
try {
const price = await this.solanaKit.fetchTokenPrice(input.trim());
return JSON.stringify({
status: "success",
tokenId: input.trim(),
priceInUSDC: price,
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
code: error.code || "UNKNOWN_ERROR",
});
}
}
}
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)`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
protected async _call(input: string): Promise<string> {
try {
const parsedInput = input.trim();
const tokenData = await this.solanaKit.getTokenDataByAddress(parsedInput);
return JSON.stringify({
status: "success",
tokenData,
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
code: error.code || "UNKNOWN_ERROR",
});
}
}
}
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)`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
protected async _call(input: string): Promise<string> {
try {
const ticker = input.trim();
const tokenData = await this.solanaKit.getTokenDataByTicker(ticker);
return JSON.stringify({
status: "success",
tokenData,
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
code: error.code || "UNKNOWN_ERROR",
});
}
}
}
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)`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
protected async _call(input: string): Promise<string> {
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,
);
return JSON.stringify({
status: "success",
message: `Airdropped ${parsedInput.amount} tokens to ${parsedInput.recipients.length} recipients.`,
transactionHashes: txs,
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
code: error.code || "UNKNOWN_ERROR",
});
}
}
}
export class SolanaClosePosition extends Tool {
name = "orca_close_position";
description = `Closes an existing liquidity position in an Orca Whirlpool. This function fetches the position
details using the provided mint address and closes the position with a 1% slippage.
Inputs (JSON string):
- positionMintAddress: string, the address of the position mint that represents the liquidity position.`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
async _call(input: string): Promise<string> {
try {
const inputFormat = JSON.parse(input);
const positionMintAddress = new PublicKey(
inputFormat.positionMintAddress,
);
const txId = await this.solanaKit.orcaClosePosition(positionMintAddress);
return JSON.stringify({
status: "success",
message: "Liquidity position closed successfully.",
transaction: txId,
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
code: error.code || "UNKNOWN_ERROR",
});
}
}
}
export class SolanaOrcaCreateCLMM extends Tool {
name = "orca_create_clmm";
description = `Create a Concentrated Liquidity Market Maker (CLMM) pool on Orca, the most efficient and capital-optimized CLMM on Solana. This function initializes a CLMM pool but does not add liquidity. You can add liquidity later using a centered position or a single-sided position.
Inputs (JSON string):
- mintDeploy: string, the mint of the token you want to deploy (required).
- mintPair: string, The mint of the token you want to pair the deployed mint with (required).
- initialPrice: number, initial price of mintA in terms of mintB, e.g., 0.001 (required).
- feeTier: number, fee tier in bps. Options: 1, 2, 4, 5, 16, 30, 65, 100, 200 (required).`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
async _call(input: string): Promise<string> {
try {
const inputFormat = JSON.parse(input);
const mintA = new PublicKey(inputFormat.mintDeploy);
const mintB = new PublicKey(inputFormat.mintPair);
const initialPrice = new Decimal(inputFormat.initialPrice);
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.solanaKit.orcaCreateCLMM(
mintA,
mintB,
initialPrice,
feeTier,
);
return JSON.stringify({
status: "success",
message:
"CLMM pool created successfully. Note: No liquidity was added.",
transaction: txId,
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
code: error.code || "UNKNOWN_ERROR",
});
}
}
}
export class SolanaOrcaCreateSingleSideLiquidityPool extends Tool {
name = "orca_create_single_sided_liquidity_pool";
description = `Create a single-sided liquidity pool on Orca, the most efficient and capital-optimized CLMM platform on Solana.
This function initializes a single-sided liquidity pool, ideal for community driven project, fair launches, and fundraising. Minimize price impact by setting a narrow price range.
Inputs (JSON string):
- depositTokenAmount: number, in units of the deposit token including decimals, e.g., 1000000000 (required).
- depositTokenMint: string, mint address of the deposit token, e.g., "DepositTokenMintAddress" (required).
- otherTokenMint: string, mint address of the other token, e.g., "OtherTokenMintAddress" (required).
- initialPrice: number, initial price of the deposit token in terms of the other token, e.g., 0.001 (required).
- maxPrice: number, maximum price at which liquidity is added, e.g., 5.0 (required).
- feeTier: number, fee tier for the pool in bps. Options: 1, 2, 4, 5, 16, 30, 65, 100, 200 (required).`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
async _call(input: string): Promise<string> {
try {
const inputFormat = JSON.parse(input);
const depositTokenAmount = 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.solanaKit.orcaCreateSingleSidedLiquidityPool(
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 SolanaOrcaFetchPositions extends Tool {
name = "orca_fetch_positions";
description = `Fetch all the liquidity positions in an Orca Whirlpool by owner. Returns an object with positiont mint addresses as keys and position status details as values.`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
async _call(): Promise<string> {
try {
const txId = await this.solanaKit.orcaFetchPositions();
return JSON.stringify({
status: "success",
message: "Liquidity positions fetched.",
transaction: txId,
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
code: error.code || "UNKNOWN_ERROR",
});
}
}
}
export class SolanaOrcaOpenCenteredPosition extends Tool {
name = "orca_open_centered_position_with_liquidity";
description = `Add liquidity to a CLMM by opening a centered position in an Orca Whirlpool, the most efficient liquidity pool on Solana.
Inputs (JSON string):
- whirlpoolAddress: string, address of the Orca Whirlpool (required).
- priceOffsetBps: number, bps offset (one side) from the current pool price, e.g., 500 for 5% (required).
- inputTokenMint: string, mint address of the deposit token (required).
- inputAmount: number, amount of the deposit token, e.g., 100.0 (required).`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
async _call(input: string): Promise<string> {
try {
const inputFormat = JSON.parse(input);
const whirlpoolAddress = new PublicKey(inputFormat.whirlpoolAddress);
const priceOffsetBps = parseInt(inputFormat.priceOffsetBps, 10);
const inputTokenMint = new PublicKey(inputFormat.inputTokenMint);
const inputAmount = new Decimal(inputFormat.inputAmount);
if (priceOffsetBps < 0) {
throw new Error(
"Invalid distanceFromCurrentPriceBps. It must be equal or greater than 0.",
);
}
const txId = await this.solanaKit.orcaOpenCenteredPositionWithLiquidity(
whirlpoolAddress,
priceOffsetBps,
inputTokenMint,
inputAmount,
);
return JSON.stringify({
status: "success",
message: "Centered liquidity position opened successfully.",
transaction: txId,
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
code: error.code || "UNKNOWN_ERROR",
});
}
}
}
export class SolanaOrcaOpenSingleSidedPosition extends Tool {
name = "orca_open_single_sided_position";
description = `Add liquidity to a CLMM by opening a single-sided position in an Orca Whirlpool, the most efficient liquidity pool on Solana.
Inputs (JSON string):
- whirlpoolAddress: string, address of the Orca Whirlpool (required).
- distanceFromCurrentPriceBps: number, distance in basis points from the current price for the position (required).
- widthBps: number, width of the position in basis points (required).
- inputTokenMint: string, mint address of the deposit token (required).
- inputAmount: number, amount of the deposit token, e.g., 100.0 (required).`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
async _call(input: string): Promise<string> {
try {
const inputFormat = JSON.parse(input);
const whirlpoolAddress = new PublicKey(inputFormat.whirlpoolAddress);
const distanceFromCurrentPriceBps =
inputFormat.distanceFromCurrentPriceBps;
const widthBps = inputFormat.widthBps;
const inputTokenMint = new PublicKey(inputFormat.inputTokenMint);
const inputAmount = new Decimal(inputFormat.inputAmount);
if (distanceFromCurrentPriceBps < 0 || widthBps < 0) {
throw new Error(
"Invalid distanceFromCurrentPriceBps or width. It must be equal or greater than 0.",
);
}
const txId = await this.solanaKit.orcaOpenSingleSidedPosition(
whirlpoolAddress,
distanceFromCurrentPriceBps,
widthBps,
inputTokenMint,
inputAmount,
);
return JSON.stringify({
status: "success",
message: "Single-sided liquidity position opened successfully.",
transaction: txId,
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
code: error.code || "UNKNOWN_ERROR",
});
}
}
}
export class SolanaRaydiumCreateAmmV4 extends Tool {
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();
}
async _call(input: string): Promise<string> {
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),
);
return JSON.stringify({
status: "success",
message: "Raydium amm v4 pool created successfully",
transaction: tx,
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
code: error.code || "UNKNOWN_ERROR",
});
}
}
}
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)
`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
async _call(input: string): Promise<string> {
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),
);
return JSON.stringify({
status: "success",
message: "Raydium clmm pool created successfully",
transaction: tx,
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
code: error.code || "UNKNOWN_ERROR",
});
}
}
}
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)
`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
async _call(input: string): Promise<string> {
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),
);
return JSON.stringify({
status: "success",
message: "Raydium cpmm pool created successfully",
transaction: tx,
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
code: error.code || "UNKNOWN_ERROR",
});
}
}
}
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)
`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
async _call(input: string): Promise<string> {
try {
const inputFormat = JSON.parse(input);
const tx = await this.solanaKit.openbookCreateMarket(
new PublicKey(inputFormat.baseMint),
new PublicKey(inputFormat.quoteMint),
inputFormat.lotSize,
inputFormat.tickSize,
);
return JSON.stringify({
status: "success",
message: "Openbook market created successfully",
transaction: tx,
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
code: error.code || "UNKNOWN_ERROR",
});
}
}
}
export class SolanaManifestCreateMarket extends Tool {
name = "solana_manifest_create_market";
description = `Manifest market
Inputs (input is a json string):
baseMint: string (required)
quoteMint: string (required)
`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
async _call(input: string): Promise<string> {
try {
const inputFormat = JSON.parse(input);
const tx = await this.solanaKit.manifestCreateMarket(
new PublicKey(inputFormat.baseMint),
new PublicKey(inputFormat.quoteMint),
);
return JSON.stringify({
status: "success",
message: "Create manifest market successfully",
transaction: tx[0],
marketId: tx[1],
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
code: error.code || "UNKNOWN_ERROR",
});
}
}
}
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:
tokenSymbol: string, e.g., BTC for bitcoin`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
async _call(input: string): Promise<string> {
try {
const priceFeedID = await this.solanaKit.getPythPriceFeedID(input);
const price = await this.solanaKit.getPythPrice(priceFeedID);
const response: PythFetchPriceResponse = {
status: "success",
tokenSymbol: input,
priceFeedID,
price,
};
return JSON.stringify(response);
} catch (error: any) {
const response: PythFetchPriceResponse = {
status: "error",
tokenSymbol: input,
message: error.message,
code: error.code || "UNKNOWN_ERROR",
};
return JSON.stringify(response);
}
}
}
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)`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
async _call(input: string): Promise<string> {
try {
const owner = await this.solanaKit.resolveAllDomains(input);
if (!owner) {
return JSON.stringify({
status: "error",
message: "Domain not found",
code: "DOMAIN_NOT_FOUND",
});
}
return JSON.stringify({
status: "success",
message: "Domain resolved successfully",
owner: owner?.toString(),
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
code: error.code || "DOMAIN_RESOLUTION_ERROR",
});
}
}
}
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)`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
async _call(input: string): Promise<string> {
try {
const ownerPubkey = new PublicKey(input.trim());
const domains = await this.solanaKit.getOwnedAllDomains(ownerPubkey);
return JSON.stringify({
status: "success",
message: "Owned domains fetched successfully",
domains,
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
code: error.code || "FETCH_OWNED_DOMAINS_ERROR",
});
}
}
}
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)`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
async _call(input: string): Promise<string> {
try {
const domains = await this.solanaKit.getOwnedDomainsForTLD(input);
return JSON.stringify({
status: "success",
message: "TLD domains fetched successfully",
domains,
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
code: error.code || "FETCH_TLD_DOMAINS_ERROR",
});
}
}
}
export class SolanaGetAllTlds extends Tool {
name = "solana_get_all_tlds";
description = `Get all active top-level domains (TLDs) in the AllDomains Name Service`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
async _call(): Promise<string> {
try {
const tlds = await this.solanaKit.getAllDomainsTLDs();
return JSON.stringify({
status: "success",
message: "TLDs fetched successfully",
tlds,
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
code: error.code || "FETCH_TLDS_ERROR",
});
}
}
}
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)`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
async _call(input: string): Promise<string> {
try {
const ownerPubkey = new PublicKey(input.trim());
const mainDomain =
await this.solanaKit.getMainAllDomainsDomain(ownerPubkey);
return JSON.stringify({
status: "success",
message: "Main domain fetched successfully",
domain: mainDomain,
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
code: error.code || "FETCH_MAIN_DOMAIN_ERROR",
});
}
}
}
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)
`;
constructor(private solanaSdk: SolanaAgentKit) {
super();
}
protected async _call(input: string): Promise<string> {
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 response: GibworkCreateTaskReponse = {
status: "success",
taskId: taskData.taskId,
signature: taskData.signature,
};
return JSON.stringify(response);
} catch (err: any) {
return JSON.stringify({
status: "error",
message: err.message,
code: err.code || "CREATE_TASK_ERROR",
});
}
}
}
export class SolanaRockPaperScissorsTool extends Tool {
name = "rock_paper_scissors";
description = `Play rock paper scissors to win SEND coins.
Inputs (input is a JSON string):
choice: string, either "rock", "paper", or "scissors" (required)
amount: number, amount of SOL to play with - must be 0.1, 0.01, or 0.005 SOL (required)`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
private validateInput(input: any): void {
if (input.choice !== undefined) {
throw new Error("choice is required.");
}
if (
input.amount !== undefined &&
(typeof input.spaceKB !== "number" || input.spaceKB <= 0)
) {
throw new Error("amount must be a positive number when provided");
}
}
protected async _call(input: string): Promise<string> {
try {
const parsedInput = JSON.parse(input);
this.validateInput(parsedInput);
const result = await this.solanaKit.rockPaperScissors(
Number(parsedInput['"amount"']),
parsedInput['"choice"'].replace(/^"|"$/g, "") as
| "rock"
| "paper"
| "scissors",
);
return JSON.stringify({
status: "success",
message: result,
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
code: error.code || "UNKNOWN_ERROR",
});
}
}
}
export class SolanaTipLinkTool extends Tool {
name = "solana_tiplink";
description = `Create a TipLink for transferring SOL or SPL tokens.
Input is a JSON string with:
- amount: number (required) - Amount to transfer
- splmintAddress: string (optional) - SPL token mint address`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
protected async _call(input: string): Promise<string> {
try {
const parsedInput = JSON.parse(input);
if (!parsedInput.amount) {
throw new Error("Amount is required");
}
const amount = parseFloat(parsedInput.amount);
const splmintAddress = parsedInput.splmintAddress
? new PublicKey(parsedInput.splmintAddress)
: undefined;
const { url, signature } = await this.solanaKit.createTiplink(
amount,
splmintAddress,
);
return JSON.stringify({
status: "success",
url,
signature,
amount,
tokenType: splmintAddress ? "SPL" : "SOL",
message: `TipLink created successfully`,
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
code: error.code || "UNKNOWN_ERROR",
});
}
}
}
export class SolanaListNFTForSaleTool extends Tool {
name = "solana_list_nft_for_sale";
description = `List an NFT for sale on Tensor Trade.
Inputs (input is a JSON string):
nftMint: string, the mint address of the NFT (required)
price: number, price in SOL (required)`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
protected async _call(input: string): Promise<string> {
try {
const parsedInput = JSON.parse(input);
// Validate NFT ownership first
const nftAccount =
await this.solanaKit.connection.getTokenAccountsByOwner(
this.solanaKit.wallet_address,
{ mint: new PublicKey(parsedInput.nftMint) },
);
if (nftAccount.value.length === 0) {
return JSON.stringify({
status: "error",
message:
"NFT not found in wallet. Please make sure you own this NFT.",
code: "NFT_NOT_FOUND",
});
}
const tx = await this.solanaKit.tensorListNFT(
new PublicKey(parsedInput.nftMint),
parsedInput.price,
);
return JSON.stringify({
status: "success",
message: "NFT listed for sale successfully",
transaction: tx,
price: parsedInput.price,
nftMint: parsedInput.nftMint,
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
code: error.code || "UNKNOWN_ERROR",
});
}
}
}
export class SolanaCancelNFTListingTool extends Tool {
name = "solana_cancel_nft_listing";
description = `Cancel an NFT listing on Tensor Trade.
Inputs (input is a JSON string):
nftMint: string, the mint address of the NFT (required)`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
protected async _call(input: string): Promise<string> {
try {
const parsedInput = JSON.parse(input);
const tx = await this.solanaKit.tensorCancelListing(
new PublicKey(parsedInput.nftMint),
);
return JSON.stringify({
status: "success",
message: "NFT listing cancelled successfully",
transaction: tx,
nftMint: parsedInput.nftMint,
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
code: error.code || "UNKNOWN_ERROR",
});
}
}
}
export class SolanaFetchTokenReportSummaryTool extends Tool {
name = "solana_fetch_token_report_summary";
description = `Fetches a summary report for a specific token from RugCheck.
Inputs:
- mint: string, the mint address of the token, e.g., "JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN" (required).`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
protected async _call(input: string): Promise<string> {
try {
const mint = input.trim();
const report = await this.solanaKit.fetchTokenReportSummary(mint);
return JSON.stringify({
status: "success",
message: "Token report summary fetched successfully",
report,
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
code: error.code || "FETCH_TOKEN_REPORT_SUMMARY_ERROR",
});
}
}
}
export class SolanaFetchTokenDetailedReportTool extends Tool {
name = "solana_fetch_token_detailed_report";
description = `Fetches a detailed report for a specific token from RugCheck.
Inputs:
- mint: string, the mint address of the token, e.g., "JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN" (required).`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
protected async _call(input: string): Promise<string> {
try {
const mint = input.trim();
const detailedReport =
await this.solanaKit.fetchTokenDetailedReport(mint);
return JSON.stringify({
status: "success",
message: "Detailed token report fetched successfully",
report: detailedReport,
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
code: error.code || "FETCH_TOKEN_DETAILED_REPORT_ERROR",
});
}
}
}
export function createSolanaTools(solanaKit: SolanaAgentKit) {
return [
new SolanaBalanceTool(solanaKit),
new SolanaBalanceOtherTool(solanaKit),
new SolanaTransferTool(solanaKit),
new SolanaDeployTokenTool(solanaKit),
new SolanaDeployCollectionTool(solanaKit),
new SolanaMintNFTTool(solanaKit),
new SolanaTradeTool(solanaKit),
new SolanaRequestFundsTool(solanaKit),
new SolanaRegisterDomainTool(solanaKit),
new SolanaGetWalletAddressTool(solanaKit),
new SolanaPumpfunTokenLaunchTool(solanaKit),
new SolanaCreateImageTool(solanaKit),
new SolanaLendAssetTool(solanaKit),
new SolanaTPSCalculatorTool(solanaKit),
new SolanaStakeTool(solanaKit),
new SolanaRestakeTool(solanaKit),
new SolanaFetchPriceTool(solanaKit),
new SolanaGetDomainTool(solanaKit),
new SolanaTokenDataTool(solanaKit),
new SolanaTokenDataByTickerTool(solanaKit),
new SolanaCompressedAirdropTool(solanaKit),
new SolanaRaydiumCreateAmmV4(solanaKit),
new SolanaRaydiumCreateClmm(solanaKit),
new SolanaRaydiumCreateCpmm(solanaKit),
new SolanaOpenbookCreateMarket(solanaKit),
new SolanaManifestCreateMarket(solanaKit),
new SolanaLimitOrderTool(solanaKit),
new SolanaBatchOrderTool(solanaKit),
new SolanaCancelAllOrdersTool(solanaKit),
new SolanaWithdrawAllTool(solanaKit),
new SolanaClosePosition(solanaKit),
new SolanaOrcaCreateCLMM(solanaKit),
new SolanaOrcaCreateSingleSideLiquidityPool(solanaKit),
new SolanaOrcaFetchPositions(solanaKit),
new SolanaOrcaOpenCenteredPosition(solanaKit),
new SolanaOrcaOpenSingleSidedPosition(solanaKit),
new SolanaPythFetchPrice(solanaKit),
new SolanaResolveDomainTool(solanaKit),
new SolanaGetOwnedDomains(solanaKit),
new SolanaGetOwnedTldDomains(solanaKit),
new SolanaGetAllTlds(solanaKit),
new SolanaGetMainDomain(solanaKit),
new SolanaResolveAllDomainsTool(solanaKit),
new SolanaCreateGibworkTask(solanaKit),
new SolanaRockPaperScissorsTool(solanaKit),
new SolanaTipLinkTool(solanaKit),
new SolanaListNFTForSaleTool(solanaKit),
new SolanaCancelNFTListingTool(solanaKit),
new SolanaFetchTokenReportSummaryTool(solanaKit),
new SolanaFetchTokenDetailedReportTool(solanaKit),
new SolanaPerpOpenTradeTool(solanaKit),
new SolanaPerpCloseTradeTool(solanaKit),
new SolanaFlashOpenTrade(solanaKit),
new SolanaFlashCloseTrade(solanaKit),
];
}