mirror of
https://github.com/d0zingcat/solana-agent-kit.git
synced 2026-05-24 15:10:46 +00:00
fix
This commit is contained in:
@@ -9,11 +9,12 @@ import {
|
||||
transfer,
|
||||
trade,
|
||||
registerDomain,
|
||||
lendAsset,
|
||||
launchPumpFunToken,
|
||||
getLendingDetails,
|
||||
lendAsset,
|
||||
getTPS,
|
||||
} from "../tools";
|
||||
import { CollectionOptions } from "../types";
|
||||
import { CollectionOptions, PumpFunTokenOptions } from "../types";
|
||||
import { DEFAULT_OPTIONS } from "../constants";
|
||||
|
||||
/**
|
||||
@@ -29,26 +30,17 @@ export class SolanaAgentKit {
|
||||
public connection: Connection;
|
||||
public wallet: Keypair;
|
||||
public wallet_address: PublicKey;
|
||||
public openai_api_key: string;
|
||||
|
||||
constructor(
|
||||
privateKey?: string,
|
||||
rpcURL = "https://api.mainnet-beta.solana.com",
|
||||
private_key: string,
|
||||
rpc_url = "https://api.mainnet-beta.solana.com",
|
||||
openai_api_key: string,
|
||||
) {
|
||||
this.connection = new Connection(rpcURL);
|
||||
if (privateKey) {
|
||||
this.wallet = Keypair.fromSecretKey(bs58.decode(privateKey));
|
||||
} else {
|
||||
this.wallet = Keypair.generate();
|
||||
console.log("Generated new wallet: ", this.wallet.publicKey.toBase58());
|
||||
console.log(
|
||||
"Safely store the private key: ",
|
||||
"\n----------------------------------\n",
|
||||
this.wallet.secretKey,
|
||||
"\n----------------------------------\n",
|
||||
"Please fund this wallet with SOL to use it (on mainnet), or use the faucet method to request funds (only on devnet/testnet).",
|
||||
);
|
||||
}
|
||||
this.connection = new Connection(rpc_url);
|
||||
this.wallet = Keypair.fromSecretKey(bs58.decode(private_key));
|
||||
this.wallet_address = this.wallet.publicKey;
|
||||
this.openai_api_key = openai_api_key;
|
||||
}
|
||||
|
||||
// Tool methods
|
||||
@@ -107,4 +99,21 @@ export class SolanaAgentKit {
|
||||
async getTPS() {
|
||||
return getTPS(this);
|
||||
}
|
||||
|
||||
async launchPumpFunToken(
|
||||
tokenName: string,
|
||||
tokenTicker: string,
|
||||
description: string,
|
||||
imageUrl: string,
|
||||
options?: PumpFunTokenOptions,
|
||||
) {
|
||||
return launchPumpFunToken(
|
||||
this,
|
||||
tokenName,
|
||||
tokenTicker,
|
||||
description,
|
||||
imageUrl,
|
||||
options,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,46 +1,86 @@
|
||||
import { Tool } from "langchain/tools";
|
||||
import { SolanaAgentKit } from "../index";
|
||||
import { PublicKey } from "@solana/web3.js";
|
||||
import { toJSON } from "../utils/toJSON";
|
||||
import { create_image } from "../tools/create_image";
|
||||
|
||||
export class SolanaBalanceTool extends Tool {
|
||||
name = "solana_balance";
|
||||
description =
|
||||
"Get the balance of a Solana wallet or token account. Input can be a token address or empty for SOL 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)`;
|
||||
|
||||
constructor(private solanaKit: SolanaAgentKit) {
|
||||
super();
|
||||
}
|
||||
|
||||
async _call(input: string): Promise<string> {
|
||||
protected async _call(input: string): Promise<string> {
|
||||
try {
|
||||
const tokenAddress = input ? new PublicKey(input) : undefined;
|
||||
const balance = await this.solanaKit.getBalance(tokenAddress);
|
||||
return `Balance: ${balance}`;
|
||||
|
||||
return JSON.stringify({
|
||||
status: "success",
|
||||
balance: balance,
|
||||
token: input || "SOL",
|
||||
});
|
||||
} catch (error: any) {
|
||||
return `Error getting balance: ${error.message}`;
|
||||
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. Input should be JSON string with: {to: string, amount: number, mint?: string}";
|
||||
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<string> {
|
||||
protected async _call(input: string): Promise<string> {
|
||||
try {
|
||||
const { to, amount, mint } = JSON.parse(input);
|
||||
const recipient = new PublicKey(to);
|
||||
const mintAddress = mint ? new PublicKey(mint) : undefined;
|
||||
const parsedInput = JSON.parse(input);
|
||||
console.log(parsedInput);
|
||||
|
||||
await this.solanaKit.transfer(recipient, amount, mintAddress);
|
||||
return `Successfully transferred ${amount} to ${to}`;
|
||||
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 `Error making transfer: ${error.message}`;
|
||||
return JSON.stringify({
|
||||
status: "error",
|
||||
message: error.message,
|
||||
code: error.code || "UNKNOWN_ERROR",
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -54,16 +94,44 @@ export class SolanaDeployTokenTool extends Tool {
|
||||
super();
|
||||
}
|
||||
|
||||
async _call(input: string): Promise<string> {
|
||||
private validateInput(input: any): void {
|
||||
if (
|
||||
input.decimals !== undefined &&
|
||||
(typeof input.decimals !== "number" ||
|
||||
input.decimals < 0 ||
|
||||
input.decimals > 9)
|
||||
) {
|
||||
throw new Error(
|
||||
"decimals must be a number between 0 and 9 when provided",
|
||||
);
|
||||
}
|
||||
if (
|
||||
input.initialSupply !== undefined &&
|
||||
(typeof input.initialSupply !== "number" || input.initialSupply <= 0)
|
||||
) {
|
||||
throw new Error("initialSupply must be a positive number when provided");
|
||||
}
|
||||
}
|
||||
|
||||
protected async _call(input: string): Promise<string> {
|
||||
try {
|
||||
const validJson = input
|
||||
.replace(/([a-zA-Z0-9_]+):/g, '"$1":') // Add quotes around keys
|
||||
.trim();
|
||||
const { decimals = 9 } = JSON.parse(validJson);
|
||||
const result = await this.solanaKit.deployToken(decimals);
|
||||
return `Token deployed successfully. Mint address: ${result.mint.toString()}`;
|
||||
const parsedInput = toJSON(input);
|
||||
this.validateInput(parsedInput);
|
||||
|
||||
const result = await this.solanaKit.deployToken(parsedInput.decimals);
|
||||
|
||||
return JSON.stringify({
|
||||
status: "success",
|
||||
message: "Token deployed successfully",
|
||||
mintAddress: result.mint.toString(),
|
||||
decimals: parsedInput.decimals || 9,
|
||||
});
|
||||
} catch (error: any) {
|
||||
return `Error deploying token: ${error.message}`;
|
||||
return JSON.stringify({
|
||||
status: "error",
|
||||
message: error.message,
|
||||
code: error.code || "UNKNOWN_ERROR",
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -77,13 +145,65 @@ export class SolanaDeployCollectionTool extends Tool {
|
||||
super();
|
||||
}
|
||||
|
||||
async _call(input: string): Promise<string> {
|
||||
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.uri || typeof input.uri !== "string") {
|
||||
throw new Error("uri is required and must be a string");
|
||||
}
|
||||
if (
|
||||
input.royaltyBasisPoints !== undefined &&
|
||||
(typeof input.royaltyBasisPoints !== "number" ||
|
||||
input.royaltyBasisPoints < 0 ||
|
||||
input.royaltyBasisPoints > 10000)
|
||||
) {
|
||||
throw new Error(
|
||||
"royaltyBasisPoints must be a number between 0 and 10000 when provided",
|
||||
);
|
||||
}
|
||||
if (input.creators) {
|
||||
if (!Array.isArray(input.creators)) {
|
||||
throw new Error("creators must be an array when provided");
|
||||
}
|
||||
input.creators.forEach((creator: any, index: number) => {
|
||||
if (!creator.address || typeof creator.address !== "string") {
|
||||
throw new Error(
|
||||
`creator[${index}].address is required and must be a string`,
|
||||
);
|
||||
}
|
||||
if (
|
||||
typeof creator.percentage !== "number" ||
|
||||
creator.percentage < 0 ||
|
||||
creator.percentage > 100
|
||||
) {
|
||||
throw new Error(
|
||||
`creator[${index}].percentage must be a number between 0 and 100`,
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
protected async _call(input: string): Promise<string> {
|
||||
try {
|
||||
const options = JSON.parse(input);
|
||||
const result = await this.solanaKit.deployCollection(options);
|
||||
return `Collection deployed successfully. Address: ${result.collectionAddress.toString()}`;
|
||||
const parsedInput = toJSON(input);
|
||||
this.validateInput(parsedInput);
|
||||
|
||||
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 `Error deploying collection: ${error.message}`;
|
||||
return JSON.stringify({
|
||||
status: "error",
|
||||
message: error.message,
|
||||
code: error.code || "UNKNOWN_ERROR",
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -97,44 +217,99 @@ export class SolanaMintNFTTool extends Tool {
|
||||
super();
|
||||
}
|
||||
|
||||
async _call(input: string): Promise<string> {
|
||||
private validateInput(input: any): void {
|
||||
if (!input.collectionMint || typeof input.collectionMint !== "string") {
|
||||
throw new Error("collectionMint is required and must be a string");
|
||||
}
|
||||
if (!input.metadata || typeof input.metadata !== "object") {
|
||||
throw new Error("metadata is required and must be an object");
|
||||
}
|
||||
if (!input.metadata.name || typeof input.metadata.name !== "string") {
|
||||
throw new Error("metadata.name is required and must be a string");
|
||||
}
|
||||
if (!input.metadata.symbol || typeof input.metadata.symbol !== "string") {
|
||||
throw new Error("metadata.symbol is required and must be a string");
|
||||
}
|
||||
if (!input.metadata.uri || typeof input.metadata.uri !== "string") {
|
||||
throw new Error("metadata.uri is required and must be a string");
|
||||
}
|
||||
if (input.recipient !== undefined && typeof input.recipient !== "string") {
|
||||
throw new Error("recipient must be a string when provided");
|
||||
}
|
||||
}
|
||||
|
||||
protected async _call(input: string): Promise<string> {
|
||||
try {
|
||||
const { collectionMint, metadata, recipient } = JSON.parse(input);
|
||||
const recipientPubkey = recipient ? new PublicKey(recipient) : undefined;
|
||||
const parsedInput = toJSON(input);
|
||||
this.validateInput(parsedInput);
|
||||
|
||||
const result = await this.solanaKit.mintNFT(
|
||||
new PublicKey(collectionMint),
|
||||
metadata,
|
||||
recipientPubkey,
|
||||
new PublicKey(parsedInput.collectionMint),
|
||||
parsedInput.metadata,
|
||||
parsedInput.recipient
|
||||
? new PublicKey(parsedInput.recipient)
|
||||
: undefined,
|
||||
);
|
||||
return `NFT minted successfully. Mint address: ${result.mint.toString()}`;
|
||||
|
||||
return JSON.stringify({
|
||||
status: "success",
|
||||
message: "NFT minted successfully",
|
||||
mintAddress: result.mint.toString(),
|
||||
name: parsedInput.metadata.name,
|
||||
recipient: parsedInput.recipient || result.mint.toString(),
|
||||
});
|
||||
} catch (error: any) {
|
||||
return `Error minting NFT: ${error.message}`;
|
||||
return JSON.stringify({
|
||||
status: "error",
|
||||
message: error.message,
|
||||
code: error.code || "UNKNOWN_ERROR",
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class SolanaTradeTool extends Tool {
|
||||
name = "solana_trade";
|
||||
description =
|
||||
"Swap tokens using Jupiter Exchange. Input should be JSON with: {outputMint: string, inputAmount: number, inputMint?: string, slippageBps?: number}";
|
||||
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<string> {
|
||||
protected async _call(input: string): Promise<string> {
|
||||
try {
|
||||
const { outputMint, inputAmount, inputMint, slippageBps } =
|
||||
JSON.parse(input);
|
||||
const parsedInput = JSON.parse(input);
|
||||
|
||||
const tx = await this.solanaKit.trade(
|
||||
new PublicKey(outputMint),
|
||||
inputAmount,
|
||||
inputMint ? new PublicKey(inputMint) : undefined,
|
||||
slippageBps,
|
||||
new PublicKey(parsedInput.outputMint),
|
||||
parsedInput.inputAmount,
|
||||
parsedInput.inputMint
|
||||
? new PublicKey(parsedInput.inputMint)
|
||||
: new PublicKey("So11111111111111111111111111111111111111112"),
|
||||
parsedInput.slippageBps,
|
||||
);
|
||||
return `Trade executed successfully. Transaction: ${tx}`;
|
||||
|
||||
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 `Error executing trade: ${error.message}`;
|
||||
console.log(error);
|
||||
return JSON.stringify({
|
||||
status: "error",
|
||||
message: error.message,
|
||||
code: error.code || "UNKNOWN_ERROR",
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -147,39 +322,80 @@ export class SolanaRequestFundsTool extends Tool {
|
||||
super();
|
||||
}
|
||||
|
||||
async _call(_input: string): Promise<string> {
|
||||
protected async _call(_input: string): Promise<string> {
|
||||
try {
|
||||
await this.solanaKit.requestFaucetFunds();
|
||||
return "Successfully requested faucet funds";
|
||||
|
||||
return JSON.stringify({
|
||||
status: "success",
|
||||
message: "Successfully requested faucet funds",
|
||||
network: this.solanaKit.connection.rpcEndpoint.split("/")[2],
|
||||
});
|
||||
} catch (error: any) {
|
||||
return `Error requesting funds: ${error.message}`;
|
||||
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. Input should be JSON with: {name: string, spaceKB?: number}";
|
||||
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();
|
||||
}
|
||||
|
||||
async _call(input: string): Promise<string> {
|
||||
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 { name, spaceKB = 1 } = JSON.parse(input);
|
||||
const tx = await this.solanaKit.registerDomain(name, spaceKB);
|
||||
return `Domain registered successfully. Transaction: ${tx}`;
|
||||
const parsedInput = toJSON(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 `Error registering domain: ${error.message}`;
|
||||
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";
|
||||
description = `Get the wallet address of the agent`;
|
||||
|
||||
constructor(private solanaKit: SolanaAgentKit) {
|
||||
super();
|
||||
@@ -190,6 +406,119 @@ export class SolanaGetWalletAddressTool extends Tool {
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
console.log(input);
|
||||
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();
|
||||
let 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 =
|
||||
@@ -253,7 +582,6 @@ export class SolanaTPSCalculatorTool extends Tool {
|
||||
}
|
||||
}
|
||||
|
||||
// Updated createSolanaTools function
|
||||
export function createSolanaTools(solanaKit: SolanaAgentKit) {
|
||||
return [
|
||||
new SolanaBalanceTool(solanaKit),
|
||||
@@ -265,6 +593,8 @@ export function createSolanaTools(solanaKit: SolanaAgentKit) {
|
||||
new SolanaRequestFundsTool(solanaKit),
|
||||
new SolanaRegisterDomainTool(solanaKit),
|
||||
new SolanaGetWalletAddressTool(solanaKit),
|
||||
new SolanaPumpfunTokenLaunchTool(solanaKit),
|
||||
new SolanaCreateImageTool(solanaKit),
|
||||
new SolanaLendAssetTool(solanaKit),
|
||||
new SolanaFetchLendingDetailsTool(solanaKit),
|
||||
new SolanaTPSCalculatorTool(solanaKit),
|
||||
|
||||
39
src/tools/create_image.ts
Normal file
39
src/tools/create_image.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import { SolanaAgentKit } from "../index";
|
||||
import OpenAI from "openai";
|
||||
|
||||
/**
|
||||
* Generate an image using OpenAI's DALL-E
|
||||
* @param agent SolanaAgentKit instance
|
||||
* @param prompt Text description of the image to generate
|
||||
* @param size Image size ('256x256', '512x512', or '1024x1024') (default: '1024x1024')
|
||||
* @param n Number of images to generate (default: 1)
|
||||
* @returns Object containing the generated image URLs
|
||||
*/
|
||||
export async function create_image(
|
||||
agent: SolanaAgentKit,
|
||||
prompt: string,
|
||||
size: "256x256" | "512x512" | "1024x1024" = "1024x1024",
|
||||
n: number = 1,
|
||||
) {
|
||||
try {
|
||||
if (!agent.openai_api_key) {
|
||||
throw new Error("OpenAI API key not found in agent configuration");
|
||||
}
|
||||
|
||||
const openai = new OpenAI({
|
||||
apiKey: agent.openai_api_key,
|
||||
});
|
||||
|
||||
const response = await openai.images.generate({
|
||||
prompt,
|
||||
n,
|
||||
size,
|
||||
});
|
||||
|
||||
return {
|
||||
images: response.data.map((img: any) => img.url),
|
||||
};
|
||||
} catch (error: any) {
|
||||
throw new Error(`Image generation failed: ${error.message}`);
|
||||
}
|
||||
}
|
||||
@@ -6,5 +6,6 @@ export * from "./mint_nft";
|
||||
export * from "./transfer";
|
||||
export * from "./trade";
|
||||
export * from "./register_domain";
|
||||
export * from "./launch_pumpfun_token";
|
||||
export * from "./lend";
|
||||
export * from "./get_tps";
|
||||
|
||||
192
src/tools/launch_pumpfun_token.ts
Normal file
192
src/tools/launch_pumpfun_token.ts
Normal file
@@ -0,0 +1,192 @@
|
||||
// src/tools/launch_pumpfun_token.ts
|
||||
import { VersionedTransaction, Keypair } from "@solana/web3.js";
|
||||
import { PumpFunTokenOptions, SolanaAgentKit } from "../index";
|
||||
|
||||
async function uploadMetadata(
|
||||
tokenName: string,
|
||||
tokenTicker: string,
|
||||
description: string,
|
||||
imageUrl: string,
|
||||
options?: PumpFunTokenOptions
|
||||
): Promise<any> {
|
||||
// Create metadata object
|
||||
const formData = new URLSearchParams();
|
||||
formData.append('name', tokenName);
|
||||
formData.append("symbol", tokenTicker);
|
||||
formData.append("description", description);
|
||||
|
||||
formData.append("showName", "true");
|
||||
|
||||
if (options?.twitter) formData.append('twitter', options.twitter);
|
||||
if (options?.telegram) formData.append("telegram", options.telegram);
|
||||
if (options?.website) formData.append("website", options.website);
|
||||
|
||||
const imageResponse = await fetch(imageUrl);
|
||||
const imageBlob = await imageResponse.blob();
|
||||
const files = {
|
||||
file: new File([imageBlob], "token_image.png", { type: "image/png" }),
|
||||
};
|
||||
|
||||
// Create form data with both metadata and file
|
||||
const finalFormData = new FormData();
|
||||
// Add all metadata fields
|
||||
for (const [key, value] of formData.entries()) {
|
||||
finalFormData.append(key, value);
|
||||
}
|
||||
// Add file if exists
|
||||
if (files?.file) {
|
||||
finalFormData.append('file', files.file);
|
||||
}
|
||||
|
||||
console.log("Final form data:", finalFormData);
|
||||
|
||||
const metadataResponse = await fetch("https://pump.fun/api/ipfs", {
|
||||
method: "POST",
|
||||
body: finalFormData
|
||||
});
|
||||
|
||||
if (!metadataResponse.ok) {
|
||||
console.log("Metadata response:", await metadataResponse.json());
|
||||
throw new Error(`Metadata upload failed: ${metadataResponse.statusText}`);
|
||||
}
|
||||
|
||||
return await metadataResponse.json();
|
||||
}
|
||||
|
||||
async function createTokenTransaction(
|
||||
agent: SolanaAgentKit,
|
||||
mintKeypair: Keypair,
|
||||
metadataResponse: any,
|
||||
options?: PumpFunTokenOptions
|
||||
) {
|
||||
const payload = {
|
||||
publicKey: agent.wallet_address.toBase58(),
|
||||
action: "create",
|
||||
tokenMetadata: {
|
||||
name: metadataResponse.metadata.name,
|
||||
symbol: metadataResponse.metadata.symbol,
|
||||
uri: metadataResponse.metadataUri,
|
||||
},
|
||||
mint: mintKeypair.publicKey.toBase58(),
|
||||
denominatedInSol: "true", // API expects string "true"
|
||||
amount: options?.initialLiquiditySOL || 0.0001,
|
||||
slippage: options?.slippageBps || 5,
|
||||
priorityFee: options?.priorityFee || 0.00005,
|
||||
pool: "pump",
|
||||
};
|
||||
|
||||
const response = await fetch("https://pumpportal.fun/api/trade-local", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text();
|
||||
throw new Error(`Transaction creation failed: ${response.status} - ${errorText}`);
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
async function signAndSendTransaction(
|
||||
kit: SolanaAgentKit,
|
||||
tx: VersionedTransaction,
|
||||
mintKeypair: Keypair
|
||||
) {
|
||||
try {
|
||||
// Get the latest blockhash
|
||||
const { blockhash, lastValidBlockHeight } = await kit.connection.getLatestBlockhash();
|
||||
|
||||
// Update transaction with latest blockhash
|
||||
tx.message.recentBlockhash = blockhash;
|
||||
|
||||
// Sign the transaction
|
||||
tx.sign([mintKeypair, kit.wallet]);
|
||||
|
||||
// Send and confirm transaction with options
|
||||
const signature = await kit.connection.sendTransaction(tx, {
|
||||
skipPreflight: false,
|
||||
preflightCommitment: 'confirmed',
|
||||
maxRetries: 5
|
||||
});
|
||||
|
||||
// Wait for confirmation
|
||||
const confirmation = await kit.connection.confirmTransaction({
|
||||
signature,
|
||||
blockhash,
|
||||
lastValidBlockHeight
|
||||
});
|
||||
|
||||
if (confirmation.value.err) {
|
||||
throw new Error(`Transaction failed: ${confirmation.value.err}`);
|
||||
}
|
||||
|
||||
return signature;
|
||||
} catch (error) {
|
||||
console.error('Transaction send error:', error);
|
||||
if (error instanceof Error && 'logs' in error) {
|
||||
console.error('Transaction logs:', error.logs);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Launch a token on Pump.fun
|
||||
* @param agent - SolanaAgentKit instance
|
||||
* @param tokenName - Name of the token
|
||||
* @param tokenTicker - Ticker of the token
|
||||
* @param description - Description of the token
|
||||
* @param imageUrl - URL of the token image
|
||||
* @param options - Optional token options (twitter, telegram, website, initialLiquiditySOL, slippageBps, priorityFee)
|
||||
*/
|
||||
export async function launchPumpFunToken(
|
||||
agent: SolanaAgentKit,
|
||||
tokenName: string,
|
||||
tokenTicker: string,
|
||||
description: string,
|
||||
imageUrl: string,
|
||||
options?: PumpFunTokenOptions
|
||||
) {
|
||||
try {
|
||||
// TBD : Remove clgs after approval
|
||||
console.log("Starting token launch process...");
|
||||
|
||||
// Generate mint keypair
|
||||
const mintKeypair = Keypair.generate();
|
||||
console.log("Mint public key:", mintKeypair.publicKey.toBase58());
|
||||
|
||||
// Upload metadata
|
||||
console.log("Uploading metadata to IPFS...");
|
||||
const metadataResponse = await uploadMetadata(tokenName, tokenTicker, description, imageUrl, options);
|
||||
console.log("Metadata response:", metadataResponse);
|
||||
|
||||
// Create token transaction
|
||||
console.log("Creating token transaction...");
|
||||
const response = await createTokenTransaction(agent, mintKeypair, metadataResponse, options);
|
||||
|
||||
const transactionData = await response.arrayBuffer();
|
||||
const tx = VersionedTransaction.deserialize(new Uint8Array(transactionData));
|
||||
|
||||
// Send transaction with proper blockhash handling
|
||||
console.log("Sending transaction...");
|
||||
const signature = await signAndSendTransaction(agent, tx, mintKeypair);
|
||||
|
||||
console.log("Token launch successful!");
|
||||
return {
|
||||
signature,
|
||||
mint: mintKeypair.publicKey.toBase58(),
|
||||
metadataUri: metadataResponse.metadataUri,
|
||||
};
|
||||
|
||||
} catch (error) {
|
||||
console.error("Error in launchpumpfuntoken:", error);
|
||||
if (error instanceof Error && 'logs' in error) {
|
||||
console.error('Transaction logs:', (error as any).logs);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { VersionedTransaction, PublicKey } from "@solana/web3.js";
|
||||
import { VersionedTransaction, PublicKey, LAMPORTS_PER_SOL } from "@solana/web3.js";
|
||||
import { SolanaAgentKit } from "../index";
|
||||
import { TOKENS, DEFAULT_OPTIONS, JUP_API } from "../constants";
|
||||
|
||||
@@ -20,12 +20,13 @@ export async function trade(
|
||||
): Promise<string> {
|
||||
try {
|
||||
// Get quote for the swap
|
||||
console.log(inputMint.toString(), outputMint.toString(), inputAmount, slippageBps);
|
||||
const quoteResponse = await (
|
||||
await fetch(
|
||||
`${JUP_API}/quote?` +
|
||||
`inputMint=${inputMint.toString()}` +
|
||||
`&outputMint=${outputMint.toString()}` +
|
||||
`&amount=${inputAmount}` +
|
||||
`&amount=${inputAmount * LAMPORTS_PER_SOL}` +
|
||||
`&slippageBps=${slippageBps}` +
|
||||
`&onlyDirectRoutes=true` +
|
||||
`&maxAccounts=20`,
|
||||
@@ -48,11 +49,10 @@ export async function trade(
|
||||
}),
|
||||
})
|
||||
).json();
|
||||
|
||||
// Deserialize transaction
|
||||
const swapTransactionBuf = Buffer.from(swapTransaction, "base64");
|
||||
const transaction = VersionedTransaction.deserialize(swapTransactionBuf);
|
||||
|
||||
const transaction = VersionedTransaction.deserialize(swapTransactionBuf);
|
||||
// Sign and send transaction
|
||||
transaction.sign([agent.wallet]);
|
||||
const signature = await agent.connection.sendTransaction(transaction);
|
||||
|
||||
@@ -23,6 +23,23 @@ export interface MintCollectionNFTResponse {
|
||||
metadata: PublicKey;
|
||||
}
|
||||
|
||||
export interface PumpFunTokenOptions {
|
||||
twitter?: string;
|
||||
telegram?: string;
|
||||
website?: string;
|
||||
initialLiquiditySOL?: number;
|
||||
slippageBps?: number;
|
||||
priorityFee?: number;
|
||||
}
|
||||
|
||||
export interface PumpfunLaunchResponse {
|
||||
signature: string;
|
||||
mint: string;
|
||||
metadataUri?: string;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Lulo Account Details response format
|
||||
*/
|
||||
|
||||
26
src/utils/toJSON.ts
Normal file
26
src/utils/toJSON.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
export const toJSON = (str: string): any => {
|
||||
try {
|
||||
// Remove curly braces and split by comma
|
||||
const pairs = str.trim().slice(1, -1).split(",");
|
||||
|
||||
// Convert to object with explicit type
|
||||
const obj: Record<string, any> = {};
|
||||
|
||||
pairs.forEach((pair) => {
|
||||
const [key, value] = pair
|
||||
.trim()
|
||||
.split(":")
|
||||
.map((s) => s.trim());
|
||||
|
||||
if (!key || value === undefined) {
|
||||
throw new Error("Invalid key-value pair format");
|
||||
}
|
||||
|
||||
obj[key] = isNaN(Number(value)) ? value : Number(value);
|
||||
});
|
||||
|
||||
return JSON.parse(JSON.stringify(obj));
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to parse string to JSON: ${error}`);
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user