Merge branch 'main' into rudy5348/main

This commit is contained in:
aryan
2024-12-20 23:49:28 +05:30
43 changed files with 4059 additions and 1505 deletions

View File

@@ -1,9 +1,11 @@
import { PublicKey } from "@solana/web3.js";
import { BN } from "bn.js";
import Decimal from "decimal.js";
import { Tool } from "langchain/tools";
import { SolanaAgentKit } from "../index";
import { create_image } from "../tools/create_image";
import { fetchPrice } from "../tools/fetch_price";
import { BN } from "@coral-xyz/anchor";
import { FEE_TIERS } from "../tools";
import { toJSON } from "../utils/toJSON";
export class SolanaBalanceTool extends Tool {
@@ -56,7 +58,6 @@ export class SolanaTransferTool extends Tool {
protected async _call(input: string): Promise<string> {
try {
const parsedInput = JSON.parse(input);
console.log(parsedInput);
const recipient = new PublicKey(parsedInput.to);
const mintAddress = parsedInput.mint
@@ -89,38 +90,30 @@ export class SolanaTransferTool extends Tool {
export class SolanaDeployTokenTool extends Tool {
name = "solana_deploy_token";
description =
"Deploy a new SPL token. Input should be JSON string with: {decimals?: number, initialSupply?: number}";
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();
}
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 parsedInput = toJSON(input);
this.validateInput(parsedInput);
const parsedInput = JSON.parse(input);
const result = await this.solanaKit.deployToken(parsedInput.decimals);
const result = await this.solanaKit.deployToken(
parsedInput.name,
parsedInput.uri,
parsedInput.symbol,
parsedInput.decimals,
parsedInput.initialSupply
);
return JSON.stringify({
status: "success",
@@ -140,57 +133,20 @@ export class SolanaDeployTokenTool extends Tool {
export class SolanaDeployCollectionTool extends Tool {
name = "solana_deploy_collection";
description =
"Deploy a new NFT collection. Input should be JSON with: {name: string, uri: string, royaltyBasisPoints?: number, creators?: Array<{address: string, percentage: number}>}";
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();
}
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 parsedInput = toJSON(input);
this.validateInput(parsedInput);
const parsedInput = JSON.parse(input);
const result = await this.solanaKit.deployCollection(parsedInput);
@@ -212,50 +168,42 @@ export class SolanaDeployCollectionTool extends Tool {
export class SolanaMintNFTTool extends Tool {
name = "solana_mint_nft";
description =
"Mint a new NFT in a collection. Input should be JSON with: {collectionMint: string, metadata: {name: string, symbol: string, uri: string}, recipient?: string}";
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();
}
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 parsedInput = toJSON(input);
this.validateInput(parsedInput);
const parsedInput = JSON.parse(input);
const result = await this.solanaKit.mintNFT(
new PublicKey(parsedInput.collectionMint),
parsedInput.metadata,
parsedInput.recipient ? new PublicKey(parsedInput.recipient) : undefined
{
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(),
name: parsedInput.metadata.name,
metadata: {
name: parsedInput.name,
symbol: parsedInput.symbol,
uri: parsedInput.uri,
},
recipient: parsedInput.recipient || result.mint.toString(),
});
} catch (error: any) {
@@ -304,7 +252,6 @@ export class SolanaTradeTool extends Tool {
outputToken: parsedInput.outputMint,
});
} catch (error: any) {
console.log(error);
return JSON.stringify({
status: "error",
message: error.message,
@@ -393,6 +340,70 @@ export class SolanaRegisterDomainTool extends Tool {
}
}
export class SolanaResolveDomainTool extends Tool {
name = "solana_resolve_domain";
description = `Resolve a .sol domain to a Solana PublicKey.
Inputs:
domain: string, eg "pumpfun.sol" or "pumpfun"(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`;
@@ -425,7 +436,6 @@ export class SolanaPumpfunTokenLaunchTool extends Tool {
}
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");
}
@@ -570,6 +580,230 @@ export class SolanaTPSCalculatorTool extends Tool {
}
}
export class SolanaStakeTool extends Tool {
name = "solana_stake";
description = `This tool can be used to stake your SOL (Solana), also called as SOL staking or liquid staking.
Inputs ( input is a JSON string ):
amount: number, eg 1 or 0.01 (required)`;
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",
});
}
}
}
/**
* 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 fetchPrice(this.solanaKit, 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: 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: 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 SolanaCreateSingleSidedWhirlpoolTool extends Tool {
name = "create_orca_single_sided_whirlpool";
description = `Create a single-sided Whirlpool with liquidity.
Inputs (input is a JSON string):
- depositTokenAmount: number, eg: 1000000000 (required, in units of deposit token including decimals)
- depositTokenMint: string, eg: "DepositTokenMintAddress" (required, mint address of deposit token)
- otherTokenMint: string, eg: "OtherTokenMintAddress" (required, mint address of other token)
- initialPrice: number, eg: 0.001 (required, initial price of deposit token in terms of other token)
- maxPrice: number, eg: 5.0 (required, maximum price at which liquidity is added)
- feeTier: number, eg: 0.30 (required, fee tier for the pool)`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
async _call(input: string): Promise<string> {
try {
const inputFormat = JSON.parse(input);
const depositTokenAmount = new BN(inputFormat.depositTokenAmount);
const depositTokenMint = new PublicKey(inputFormat.depositTokenMint);
const otherTokenMint = new PublicKey(inputFormat.otherTokenMint);
const initialPrice = new Decimal(inputFormat.initialPrice);
const maxPrice = new Decimal(inputFormat.maxPrice);
const feeTier = inputFormat.feeTier;
if (!feeTier || !(feeTier in FEE_TIERS)) {
throw new Error(`Invalid feeTier. Available options: ${Object.keys(FEE_TIERS).join(", ")}`);
}
const txId = await this.solanaKit.createOrcaSingleSidedWhirlpool(
depositTokenAmount,
depositTokenMint,
otherTokenMint,
initialPrice,
maxPrice,
feeTier,
);
return JSON.stringify({
status: "success",
message: "Single-sided Whirlpool created successfully",
transaction: txId,
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
code: error.code || "UNKNOWN_ERROR",
});
}
}
}
export class SolanaRaydiumCreateAmmV4 extends Tool {
name = "raydium_create_ammV4";
description = `Raydium's Legacy AMM that requiers an OpenBook marketID
@@ -761,9 +995,17 @@ export function createSolanaTools(solanaKit: SolanaAgentKit) {
new SolanaCreateImageTool(solanaKit),
new SolanaLendAssetTool(solanaKit),
new SolanaTPSCalculatorTool(solanaKit),
new SolanaStakeTool(solanaKit),
new SolanaFetchPriceTool(solanaKit),
new SolanaResolveDomainTool(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 SolanaCreateSingleSidedWhirlpoolTool(solanaKit),
];
}