diff --git a/README.md b/README.md index a4a0093..6e2df64 100644 --- a/README.md +++ b/README.md @@ -78,10 +78,10 @@ npm install solana-agent-kit ## Quick Start ```typescript -import { SolanaAgentKit, createSolanaTools } from "solana-agent-kit"; +import { SolanaAgent, createSolanaTools } from "solana-agent-kit"; // Initialize with private key and optional RPC URL -const agent = new SolanaAgentKit( +const agent = new SolanaAgent( "your-wallet-private-key-as-base58", "https://api.mainnet-beta.solana.com", "your-openai-api-key" diff --git a/guides/add_your_own_tool.md b/guides/add_your_own_tool.md index 63d1172..71bfb7c 100644 --- a/guides/add_your_own_tool.md +++ b/guides/add_your_own_tool.md @@ -21,19 +21,19 @@ Create a new TypeScript file in the `src/tools/` directory for your tool (e.g., ```typescript:src/tools/custom_tool.ts import { Tool } from "langchain/tools"; -import { SolanaAgentKit } from "../agent"; +import { SolanaAgent } from "../agent"; export class CustomTool extends Tool { name = "custom_tool"; description = "Description of what the custom tool does."; - constructor(private solanaKit: SolanaAgentKit) { + constructor(private solanaAgent: SolanaAgent) { super(); } protected async _call(input: string): Promise { try { - const result = await this.solanaKit.customFunction(input); + const result = await this.solanaAgent.customFunction(input); return JSON.stringify({ status: "success", message: "Custom tool executed successfully", @@ -53,7 +53,7 @@ export class CustomTool extends Tool { ### 3. Add Supporting Functions to SolanaAgentKit ```typescript:src/agent/index.ts -export class SolanaAgentKit { +export class SolanaAgent { // ... existing code ... async customFunction(input: string): Promise { @@ -87,9 +87,9 @@ export function createSolanaTools(agent: SolanaAgentKit) { ### 6. Usage Example ```typescript -import { SolanaAgentKit, createSolanaTools } from "solana-agent-kit"; +import { SolanaAgent, createSolanaTools } from "solana-agent-kit"; -const agent = new SolanaAgentKit( +const agent = new SolanaAgent( "your-wallet-private-key-as-base58", "https://api.mainnet-beta.solana.com", "your-openai-api-key" @@ -118,19 +118,19 @@ Here's a complete example of implementing a tool to fetch token prices: ```typescript:src/tools/fetch_token_price.ts import { Tool } from "langchain/tools"; -import { SolanaAgentKit } from "../agent"; +import { SolanaAgent } from "../agent"; export class FetchTokenPriceTool extends Tool { name = "fetch_token_price"; description = "Fetches the current price of a specified token."; - constructor(private solanaKit: SolanaAgentKit) { + constructor(private solanaAgent: SolanaAgent) { super(); } protected async _call(tokenSymbol: string): Promise { try { - const price = await this.solanaKit.getTokenPrice(tokenSymbol); + const price = await this.solanaAgent.getTokenPrice(tokenSymbol); return JSON.stringify({ status: "success", message: `Price fetched successfully for ${tokenSymbol}.`, @@ -176,4 +176,4 @@ If you encounter any issues while implementing your custom tool: - Contact the maintainer - Check existing tools for implementation examples ---- \ No newline at end of file +--- diff --git a/package.json b/package.json index 53165fb..ebf8984 100644 --- a/package.json +++ b/package.json @@ -15,10 +15,9 @@ "pnpm": ">=8.0.0" }, "keywords": [], - "author": "", + "author": "sendaifun", "license": "ISC", "dependencies": { - "@raydium-io/raydium-sdk-v2": "0.1.95-alpha", "@bonfida/spl-name-service": "^3.0.7", "@coral-xyz/anchor": "0.29", "@langchain/core": "^0.3.18", @@ -34,11 +33,12 @@ "@metaplex-foundation/umi-web3js-adapters": "^0.9.2", "@orca-so/common-sdk": "0.6.4", "@orca-so/whirlpools-sdk": "^0.13.12", + "@raydium-io/raydium-sdk-v2": "0.1.95-alpha", "@solana/spl-token": "^0.4.9", "@solana/web3.js": "^1.95.4", + "bn.js": "^5.2.1", "bs58": "^6.0.0", "decimal.js": "^10.4.3", - "bn.js": "^5.2.1", "dotenv": "^16.4.5", "form-data": "^4.0.1", "langchain": "^0.3.6", @@ -51,4 +51,4 @@ "ts-node": "^10.9.2", "typescript": "^5.7.2" } -} \ No newline at end of file +} diff --git a/src/agent/index.ts b/src/agent/index.ts index 90e47ef..f40e18d 100644 --- a/src/agent/index.ts +++ b/src/agent/index.ts @@ -26,6 +26,7 @@ import { sendCompressedAirdrop, createOrcaSingleSidedWhirlpool, FEE_TIERS, + fetchPrice, } from "../tools"; import { CollectionDeployment, @@ -39,14 +40,14 @@ import { BN } from "@coral-xyz/anchor"; /** * Main class for interacting with Solana blockchain - * Provides a unified interface for token operations, NFT management, and trading + * Provides a unified interface for token operations, NFT management, trading and more * - * @class SolanaAgentKit + * @class SolanaAgent * @property {Connection} connection - Solana RPC connection * @property {Keypair} wallet - Wallet keypair for signing transactions * @property {PublicKey} wallet_address - Public key of the wallet */ -export class SolanaAgentKit { +export class SolanaAgent { public connection: Connection; public wallet: Keypair; public wallet_address: PublicKey; @@ -145,6 +146,10 @@ export class SolanaAgentKit { return getTokenDataByTicker(ticker); } + async fetchTokenPrice(mint: string) { + return fetchPrice(new PublicKey(mint)); + } + async launchPumpFunToken( tokenName: string, tokenTicker: string, diff --git a/src/index.ts b/src/index.ts index a1fd258..8c267a1 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,7 +1,7 @@ -import { SolanaAgentKit } from './agent'; // Move the SolanaAgentKit class to src/agent.ts -import { createSolanaTools } from './langchain'; +import { SolanaAgent } from "./agent"; +import { createSolanaTools } from "./langchain"; -export { SolanaAgentKit, createSolanaTools }; +export { SolanaAgent, createSolanaTools }; // Optional: Export types that users might need -export * from './types'; \ No newline at end of file +export * from "./types"; diff --git a/src/langchain/index.ts b/src/langchain/index.ts index 930b0bf..11a0ff1 100644 --- a/src/langchain/index.ts +++ b/src/langchain/index.ts @@ -1,9 +1,8 @@ import { PublicKey } from "@solana/web3.js"; import Decimal from "decimal.js"; import { Tool } from "langchain/tools"; -import { SolanaAgentKit } from "../index"; +import { SolanaAgent } 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"; @@ -18,14 +17,14 @@ export class SolanaBalanceTool extends Tool { Inputs: tokenAddress: string, eg "So11111111111111111111111111111111111111112" (optional)`; - constructor(private solanaKit: SolanaAgentKit) { + constructor(private solanaAgent: SolanaAgent) { super(); } protected async _call(input: string): Promise { try { const tokenAddress = input ? new PublicKey(input) : undefined; - const balance = await this.solanaKit.getBalance(tokenAddress); + const balance = await this.solanaAgent.getBalance(tokenAddress); return JSON.stringify({ status: "success", @@ -51,7 +50,7 @@ export class SolanaTransferTool extends Tool { amount: number, eg 1 (required) mint?: string, eg "So11111111111111111111111111111111111111112" or "SENDdRQtYMWaQrBroBrJ2Q53fgVuq95CV9UPGEvpCxa" (optional)`; - constructor(private solanaKit: SolanaAgentKit) { + constructor(private solanaAgent: SolanaAgent) { super(); } @@ -64,10 +63,10 @@ export class SolanaTransferTool extends Tool { ? new PublicKey(parsedInput.mint) : undefined; - const tx = await this.solanaKit.transfer( + const tx = await this.solanaAgent.transfer( recipient, parsedInput.amount, - mintAddress + mintAddress, ); return JSON.stringify({ @@ -94,12 +93,12 @@ export class SolanaDeployTokenTool extends Tool { Inputs (input is a JSON string): name: string, eg "My Token" (required) - uri: string, eg "https://example.com/token.json" (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) { + constructor(private solanaAgent: SolanaAgent) { super(); } @@ -107,12 +106,12 @@ export class SolanaDeployTokenTool extends Tool { try { const parsedInput = JSON.parse(input); - const result = await this.solanaKit.deployToken( + const result = await this.solanaAgent.deployToken( parsedInput.name, parsedInput.uri, parsedInput.symbol, parsedInput.decimals, - parsedInput.initialSupply + parsedInput.initialSupply, ); return JSON.stringify({ @@ -140,7 +139,7 @@ export class SolanaDeployCollectionTool extends Tool { uri: string, eg "https://example.com/collection.json" (required) royaltyBasisPoints?: number, eg 500 for 5% (optional)`; - constructor(private solanaKit: SolanaAgentKit) { + constructor(private solanaAgent: SolanaAgent) { super(); } @@ -148,7 +147,7 @@ export class SolanaDeployCollectionTool extends Tool { try { const parsedInput = JSON.parse(input); - const result = await this.solanaKit.deployCollection(parsedInput); + const result = await this.solanaAgent.deployCollection(parsedInput); return JSON.stringify({ status: "success", @@ -174,9 +173,9 @@ export class SolanaMintNFTTool extends Tool { 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()}`; + recipient?: string, eg "9aUn5swQzUTRanaaTwmszxiv89cvFwUCjEBv1vZCoT1u" (optional) - The wallet to receive the NFT, defaults to agent's wallet which is ${this.solanaAgent.wallet_address.toString()}`; - constructor(private solanaKit: SolanaAgentKit) { + constructor(private solanaAgent: SolanaAgent) { super(); } @@ -184,7 +183,7 @@ export class SolanaMintNFTTool extends Tool { try { const parsedInput = JSON.parse(input); - const result = await this.solanaKit.mintNFT( + const result = await this.solanaAgent.mintNFT( new PublicKey(parsedInput.collectionMint), { name: parsedInput.name, @@ -192,7 +191,7 @@ export class SolanaMintNFTTool extends Tool { }, parsedInput.recipient ? new PublicKey(parsedInput.recipient) - : this.solanaKit.wallet_address + : this.solanaAgent.wallet_address, ); return JSON.stringify({ @@ -226,7 +225,7 @@ export class SolanaTradeTool extends Tool { inputMint?: string, eg "So11111111111111111111111111111111111111112" (optional) slippageBps?: number, eg 100 (optional)`; - constructor(private solanaKit: SolanaAgentKit) { + constructor(private solanaAgent: SolanaAgent) { super(); } @@ -234,13 +233,13 @@ export class SolanaTradeTool extends Tool { try { const parsedInput = JSON.parse(input); - const tx = await this.solanaKit.trade( + const tx = await this.solanaAgent.trade( new PublicKey(parsedInput.outputMint), parsedInput.inputAmount, parsedInput.inputMint ? new PublicKey(parsedInput.inputMint) : new PublicKey("So11111111111111111111111111111111111111112"), - parsedInput.slippageBps + parsedInput.slippageBps, ); return JSON.stringify({ @@ -265,18 +264,18 @@ export class SolanaRequestFundsTool extends Tool { name = "solana_request_funds"; description = "Request SOL from Solana faucet (devnet/testnet only)"; - constructor(private solanaKit: SolanaAgentKit) { + constructor(private solanaAgent: SolanaAgent) { super(); } protected async _call(_input: string): Promise { try { - await this.solanaKit.requestFaucetFunds(); + await this.solanaAgent.requestFaucetFunds(); return JSON.stringify({ status: "success", message: "Successfully requested faucet funds", - network: this.solanaKit.connection.rpcEndpoint.split("/")[2], + network: this.solanaAgent.connection.rpcEndpoint.split("/")[2], }); } catch (error: any) { return JSON.stringify({ @@ -297,7 +296,7 @@ export class SolanaRegisterDomainTool extends Tool { spaceKB: number, eg 1 (optional, default is 1) `; - constructor(private solanaKit: SolanaAgentKit) { + constructor(private solanaAgent: SolanaAgent) { super(); } @@ -318,9 +317,9 @@ export class SolanaRegisterDomainTool extends Tool { const parsedInput = toJSON(input); this.validateInput(parsedInput); - const tx = await this.solanaKit.registerDomain( + const tx = await this.solanaAgent.registerDomain( parsedInput.name, - parsedInput.spaceKB || 1 + parsedInput.spaceKB || 1, ); return JSON.stringify({ @@ -348,14 +347,14 @@ export class SolanaResolveDomainTool extends Tool { domain: string, eg "pumpfun.sol" or "pumpfun"(required) `; - constructor(private solanaKit: SolanaAgentKit) { + constructor(private solanaAgent: SolanaAgent) { super(); } protected async _call(input: string): Promise { try { const domain = input.trim(); - const publicKey = await this.solanaKit.resolveSolDomain(domain); + const publicKey = await this.solanaAgent.resolveSolDomain(domain); return JSON.stringify({ status: "success", @@ -380,14 +379,14 @@ export class SolanaGetDomainTool extends Tool { account: string, eg "4Be9CvxqHW6BYiRAxW9Q3xu1ycTMWaL5z8NX4HR3ha7t" (required) `; - constructor(private solanaKit: SolanaAgentKit) { + constructor(private solanaAgent: SolanaAgent) { super(); } protected async _call(input: string): Promise { try { const account = new PublicKey(input.trim()); - const domain = await this.solanaKit.getPrimaryDomain(account); + const domain = await this.solanaAgent.getPrimaryDomain(account); return JSON.stringify({ status: "success", @@ -408,12 +407,12 @@ export class SolanaGetWalletAddressTool extends Tool { name = "solana_get_wallet_address"; description = `Get the wallet address of the agent`; - constructor(private solanaKit: SolanaAgentKit) { + constructor(private solanaAgent: SolanaAgent) { super(); } async _call(_input: string): Promise { - return this.solanaKit.wallet_address.toString(); + return this.solanaAgent.wallet_address.toString(); } } @@ -431,7 +430,7 @@ export class SolanaPumpfunTokenLaunchTool extends Tool { 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) { + constructor(private solanaAgent: SolanaAgent) { super(); } @@ -465,7 +464,7 @@ export class SolanaPumpfunTokenLaunchTool extends Tool { this.validateInput(parsedInput); // Launch token with validated input - await this.solanaKit.launchPumpFunToken( + await this.solanaAgent.launchPumpFunToken( parsedInput.tokenName, parsedInput.tokenTicker, parsedInput.description, @@ -475,7 +474,7 @@ export class SolanaPumpfunTokenLaunchTool extends Tool { telegram: parsedInput.telegram, website: parsedInput.website, initialLiquiditySOL: parsedInput.initialLiquiditySOL, - } + }, ); return JSON.stringify({ @@ -499,7 +498,7 @@ export class SolanaCreateImageTool extends Tool { description = "Create an image using OpenAI's DALL-E. Input should be a string prompt for the image."; - constructor(private solanaKit: SolanaAgentKit) { + constructor(private solanaAgent: SolanaAgent) { super(); } @@ -512,7 +511,7 @@ export class SolanaCreateImageTool extends Tool { protected async _call(input: string): Promise { try { this.validateInput(input); - const result = await create_image(this.solanaKit, input.trim()); + const result = await create_image(this.solanaAgent, input.trim()); return JSON.stringify({ status: "success", @@ -536,7 +535,7 @@ export class SolanaLendAssetTool extends Tool { Inputs (input is a json string): amount: number, eg 1, 0.01 (required)`; - constructor(private solanaKit: SolanaAgentKit) { + constructor(private solanaAgent: SolanaAgent) { super(); } @@ -544,7 +543,7 @@ export class SolanaLendAssetTool extends Tool { try { let amount = JSON.parse(input).amount || input; - const tx = await this.solanaKit.lendAssets(amount); + const tx = await this.solanaAgent.lendAssets(amount); return JSON.stringify({ status: "success", @@ -566,13 +565,13 @@ export class SolanaTPSCalculatorTool extends Tool { name = "solana_get_tps"; description = "Get the current TPS of the Solana network"; - constructor(private solanaKit: SolanaAgentKit) { + constructor(private solanaAgent: SolanaAgent) { super(); } async _call(_input: string): Promise { try { - const tps = await this.solanaKit.getTPS(); + const tps = await this.solanaAgent.getTPS(); return `Solana (mainnet-beta) current transactions per second: ${tps}`; } catch (error: any) { return `Error fetching TPS: ${error.message}`; @@ -587,7 +586,7 @@ export class SolanaStakeTool extends Tool { Inputs ( input is a JSON string ): amount: number, eg 1 or 0.01 (required)`; - constructor(private solanaKit: SolanaAgentKit) { + constructor(private solanaAgent: SolanaAgent) { super(); } @@ -595,7 +594,7 @@ export class SolanaStakeTool extends Tool { try { const parsedInput = JSON.parse(input) || Number(input); - const tx = await this.solanaKit.stake(parsedInput.amount); + const tx = await this.solanaAgent.stake(parsedInput.amount); return JSON.stringify({ status: "success", @@ -619,17 +618,17 @@ export class SolanaStakeTool extends Tool { 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) { + constructor(private solanaAgent: SolanaAgent) { super(); } async _call(input: string): Promise { try { - const price = await fetchPrice(this.solanaKit, input.trim()); + const price = await this.solanaAgent.fetchTokenPrice(input.trim()); return JSON.stringify({ status: "success", tokenId: input.trim(), @@ -652,7 +651,7 @@ export class SolanaTokenDataTool extends Tool { Inputs: mintAddress is required. mintAddress: string, eg "So11111111111111111111111111111111111111112" (required)`; - constructor(private solanaKit: SolanaAgentKit) { + constructor(private solanaAgent: SolanaAgent) { super(); } @@ -660,7 +659,8 @@ export class SolanaTokenDataTool extends Tool { try { const parsedInput = input.trim(); - const tokenData = await this.solanaKit.getTokenDataByAddress(parsedInput); + const tokenData = + await this.solanaAgent.getTokenDataByAddress(parsedInput); return JSON.stringify({ status: "success", @@ -683,14 +683,14 @@ export class SolanaTokenDataByTickerTool extends Tool { Inputs: ticker is required. ticker: string, eg "USDC" (required)`; - constructor(private solanaKit: SolanaAgentKit) { + constructor(private solanaAgent: SolanaAgent) { super(); } protected async _call(input: string): Promise { try { const ticker = input.trim(); - const tokenData = await this.solanaKit.getTokenDataByTicker(ticker); + const tokenData = await this.solanaAgent.getTokenDataByTicker(ticker); return JSON.stringify({ status: "success", tokenData: tokenData, @@ -708,7 +708,7 @@ export class SolanaTokenDataByTickerTool extends Tool { export class SolanaCompressedAirdropTool extends Tool { name = "solana_compressed_airdrop"; description = `Airdrop SPL tokens with ZK Compression (also called as airdropping tokens) - + 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) @@ -717,7 +717,7 @@ export class SolanaCompressedAirdropTool extends Tool { 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) { + constructor(private solanaAgent: SolanaAgent) { super(); } @@ -725,13 +725,13 @@ export class SolanaCompressedAirdropTool extends Tool { try { const parsedInput = JSON.parse(input); - const txs = await this.solanaKit.sendCompressedAirdrop( + const txs = await this.solanaAgent.sendCompressedAirdrop( parsedInput.mintAddress, parsedInput.amount, parsedInput.decimals, parsedInput.recipients, parsedInput.priorityFeeInLamports || 30_000, - parsedInput.shouldLog || false + parsedInput.shouldLog || false, ); return JSON.stringify({ @@ -761,7 +761,7 @@ export class SolanaCreateSingleSidedWhirlpoolTool extends Tool { - 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) { + constructor(private solanaAgent: SolanaAgent) { super(); } @@ -776,10 +776,12 @@ export class SolanaCreateSingleSidedWhirlpoolTool extends Tool { const feeTier = inputFormat.feeTier; if (!feeTier || !(feeTier in FEE_TIERS)) { - throw new Error(`Invalid feeTier. Available options: ${Object.keys(FEE_TIERS).join(", ")}`); + throw new Error( + `Invalid feeTier. Available options: ${Object.keys(FEE_TIERS).join(", ")}`, + ); } - const txId = await this.solanaKit.createOrcaSingleSidedWhirlpool( + const txId = await this.solanaAgent.createOrcaSingleSidedWhirlpool( depositTokenAmount, depositTokenMint, otherTokenMint, @@ -803,7 +805,6 @@ export class SolanaCreateSingleSidedWhirlpoolTool extends Tool { } } - export class SolanaRaydiumCreateAmmV4 extends Tool { name = "raydium_create_ammV4"; description = `Raydium's Legacy AMM that requiers an OpenBook marketID @@ -815,15 +816,15 @@ export class SolanaRaydiumCreateAmmV4 extends Tool { startTime: number(seconds), eg: now number or zero (required) `; - constructor(private solanaKit: SolanaAgentKit) { + constructor(private solanaAgent: SolanaAgent) { super(); } async _call(input: string): Promise { try { - let inputFormat = JSON.parse(input) + let inputFormat = JSON.parse(input); - const tx = await this.solanaKit.raydiumCreateAmmV4( + const tx = await this.solanaAgent.raydiumCreateAmmV4( new PublicKey(inputFormat.marketId), new BN(inputFormat.baseAmount), new BN(inputFormat.quoteAmount), @@ -857,15 +858,15 @@ export class SolanaRaydiumCreateClmm extends Tool { startTime: number(seconds), eg: now number or zero (required) `; - constructor(private solanaKit: SolanaAgentKit) { + constructor(private solanaAgent: SolanaAgent) { super(); } async _call(input: string): Promise { try { - let inputFormat = JSON.parse(input) + let inputFormat = JSON.parse(input); - const tx = await this.solanaKit.raydiumCreateClmm( + const tx = await this.solanaAgent.raydiumCreateClmm( new PublicKey(inputFormat.mint1), new PublicKey(inputFormat.mint2), @@ -892,7 +893,7 @@ export class SolanaRaydiumCreateClmm extends Tool { export class SolanaRaydiumCreateCpmm extends Tool { name = "raydium_create_cpmm"; - description = `Raydium's newest CPMM, does not require marketID, supports Token 2022 standard + description = `Raydium's newest CPMM, does not require marketID, supports Token 2022 standard Inputs (input is a json string): mint1: string (required) @@ -903,15 +904,15 @@ export class SolanaRaydiumCreateCpmm extends Tool { startTime: number(seconds), eg: now number or zero (required) `; - constructor(private solanaKit: SolanaAgentKit) { + constructor(private solanaAgent: SolanaAgent) { super(); } async _call(input: string): Promise { try { - let inputFormat = JSON.parse(input) + let inputFormat = JSON.parse(input); - const tx = await this.solanaKit.raydiumCreateCpmm( + const tx = await this.solanaAgent.raydiumCreateCpmm( new PublicKey(inputFormat.mint1), new PublicKey(inputFormat.mint2), @@ -940,7 +941,7 @@ export class SolanaRaydiumCreateCpmm extends Tool { export class SolanaOpenbookCreateMarket extends Tool { name = "solana_openbook_create_market"; - description = `Openbook marketId, required for ammv4 + description = `Openbook marketId, required for ammv4 Inputs (input is a json string): baseMint: string (required) @@ -949,15 +950,15 @@ export class SolanaOpenbookCreateMarket extends Tool { tickSize: number (required) `; - constructor(private solanaKit: SolanaAgentKit) { + constructor(private solanaAgent: SolanaAgent) { super(); } async _call(input: string): Promise { try { - let inputFormat = JSON.parse(input) + let inputFormat = JSON.parse(input); - const tx = await this.solanaKit.openbookCreateMarket( + const tx = await this.solanaAgent.openbookCreateMarket( new PublicKey(inputFormat.baseMint), new PublicKey(inputFormat.quoteMint), @@ -980,32 +981,32 @@ export class SolanaOpenbookCreateMarket extends Tool { } } -export function createSolanaTools(solanaKit: SolanaAgentKit) { +export function createSolanaTools(solanaAgent: SolanaAgent) { return [ - new SolanaBalanceTool(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 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), + new SolanaBalanceTool(solanaAgent), + new SolanaTransferTool(solanaAgent), + new SolanaDeployTokenTool(solanaAgent), + new SolanaDeployCollectionTool(solanaAgent), + new SolanaMintNFTTool(solanaAgent), + new SolanaTradeTool(solanaAgent), + new SolanaRequestFundsTool(solanaAgent), + new SolanaRegisterDomainTool(solanaAgent), + new SolanaGetWalletAddressTool(solanaAgent), + new SolanaPumpfunTokenLaunchTool(solanaAgent), + new SolanaCreateImageTool(solanaAgent), + new SolanaLendAssetTool(solanaAgent), + new SolanaTPSCalculatorTool(solanaAgent), + new SolanaStakeTool(solanaAgent), + new SolanaFetchPriceTool(solanaAgent), + new SolanaResolveDomainTool(solanaAgent), + new SolanaGetDomainTool(solanaAgent), + new SolanaTokenDataTool(solanaAgent), + new SolanaTokenDataByTickerTool(solanaAgent), + new SolanaCompressedAirdropTool(solanaAgent), + new SolanaRaydiumCreateAmmV4(solanaAgent), + new SolanaRaydiumCreateClmm(solanaAgent), + new SolanaRaydiumCreateCpmm(solanaAgent), + new SolanaOpenbookCreateMarket(solanaAgent), + new SolanaCreateSingleSidedWhirlpoolTool(solanaAgent), ]; } diff --git a/src/tools/create_image.ts b/src/tools/create_image.ts index 7fbb2cf..c5660ae 100644 --- a/src/tools/create_image.ts +++ b/src/tools/create_image.ts @@ -1,16 +1,16 @@ -import { SolanaAgentKit } from "../index"; +import { SolanaAgent } from "../index"; import OpenAI from "openai"; /** * Generate an image using OpenAI's DALL-E - * @param agent SolanaAgentKit instance + * @param agent SolanaAgent 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, + agent: SolanaAgent, prompt: string, size: "256x256" | "512x512" | "1024x1024" = "1024x1024", n: number = 1, diff --git a/src/tools/create_orca_single_sided_whirlpool.ts b/src/tools/create_orca_single_sided_whirlpool.ts index aefc93c..095d8fc 100644 --- a/src/tools/create_orca_single_sided_whirlpool.ts +++ b/src/tools/create_orca_single_sided_whirlpool.ts @@ -1,5 +1,5 @@ import { Keypair, PublicKey, Transaction } from "@solana/web3.js"; -import { SolanaAgentKit } from "../agent"; +import { SolanaAgent } from "../index"; import { BN, Wallet } from "@coral-xyz/anchor"; import { Decimal } from "decimal.js"; import { @@ -40,7 +40,7 @@ import { sendTx } from "../utils/send_tx"; * @remarks * Fee tiers determine the percentage of fees collected on swaps, while tick spacing affects * the granularity of price ranges for liquidity positions. - * + * * For more details, refer to: * - [Whirlpool Fees](https://orca-so.github.io/whirlpools/Architecture%20Overview/Whirlpool%20Fees) * - [Whirlpool Parameters](https://orca-so.github.io/whirlpools/Architecture%20Overview/Whirlpool%20Parameters) @@ -54,17 +54,17 @@ export const FEE_TIERS = { 0.04: 4, 0.05: 8, 0.16: 16, - 0.30: 64, + 0.3: 64, 0.65: 96, - 1.00: 128, - 2.00: 256, + 1.0: 128, + 2.0: 256, } as const; /** * # Creates a single-sided Whirlpool. * * This function initializes a new Whirlpool (liquidity pool) on Orca and seeds it with liquidity from a single token. - * + * * ## Example Usage: * You created a new token called SHARK, and you want to set the initial price to 0.001 USDC. * You set `depositTokenMint` to SHARK's mint address and `otherTokenMint` to USDC's mint address. @@ -72,7 +72,7 @@ export const FEE_TIERS = { * 1. Increase the amount of tokens you deposit * 2. Set the initial price very low * 3. Set the maximum price closer to the initial price - * + * * ### Note for experts: * The Wrhirlpool program initializes the Whirlpool with the in a specific order. This might not be * the order you expect, so the function checks the order and adjusts the inverts the prices. This means that @@ -86,13 +86,13 @@ export const FEE_TIERS = { * @param initialPrice - The initial price of the deposit token in terms of the other token. * @param maxPrice - The maximum price at which liquidity is added. * @param feeTier - The fee tier percentage for the pool, determining tick spacing and fee collection rates. - * + * * @returns A promise that resolves to a transaction ID (`string`) of the transaction creating the pool. - * + * * @throws Will throw an error if: * - Mint accounts for the tokens cannot be fetched. * - Prices are out of bounds. - * + * * @remarks * This function is designed for single-sided deposits where users only contribute one type of token, * and the function manages mint order and necessary calculations. @@ -103,7 +103,7 @@ export const FEE_TIERS = { * import { PublicKey } from "@solana/web3.js"; * import { BN } from "@coral-xyz/anchor"; * import Decimal from "decimal.js"; - * + * * const agent = new SolanaAgentKit(wallet, connection); * const depositAmount = new BN(1_000_000_000_000); // 1 million SHARK if SHARK has 6 decimals * const depositTokenMint = new PublicKey("DEPOSTI_TOKEN_ADDRESS"); @@ -111,7 +111,7 @@ export const FEE_TIERS = { * const initialPrice = new Decimal(0.001); * const maxPrice = new Decimal(5.0); * const feeTier = 0.30; - * + * * const txId = await createOrcaSingleSidedWhirlpool( * agent, * depositAmount, @@ -125,7 +125,7 @@ export const FEE_TIERS = { * ``` */ export async function createOrcaSingleSidedWhirlpool( - agent: SolanaAgentKit, + agent: SolanaAgent, depositTokenAmount: BN, depositTokenMint: PublicKey, otherTokenMint: PublicKey, @@ -134,13 +134,19 @@ export async function createOrcaSingleSidedWhirlpool( feeTier: keyof typeof FEE_TIERS, ): Promise { const wallet = new Wallet(agent.wallet); - const ctx = WhirlpoolContext.from(agent.connection, wallet, ORCA_WHIRLPOOL_PROGRAM_ID); + const ctx = WhirlpoolContext.from( + agent.connection, + wallet, + ORCA_WHIRLPOOL_PROGRAM_ID, + ); const fetcher = ctx.fetcher; - const correctTokenOrder = PoolUtil.orderMints(otherTokenMint, depositTokenMint).map( - (addr) => addr.toString(), - ); - const isCorrectMintOrder = correctTokenOrder[0] === depositTokenMint.toString(); + const correctTokenOrder = PoolUtil.orderMints( + otherTokenMint, + depositTokenMint, + ).map((addr) => addr.toString()); + const isCorrectMintOrder = + correctTokenOrder[0] === depositTokenMint.toString(); let mintA, mintB; if (isCorrectMintOrder) { [mintA, mintB] = [depositTokenMint, otherTokenMint]; @@ -151,10 +157,18 @@ export async function createOrcaSingleSidedWhirlpool( } const mintAAccount = await fetcher.getMintInfo(mintA); const mintBAccount = await fetcher.getMintInfo(mintB); - if (mintAAccount === null || mintBAccount === null) throw Error('Mint account not found'); + if (mintAAccount === null || mintBAccount === null) + throw Error("Mint account not found"); const tickSpacing = FEE_TIERS[feeTier]; - const tickIndex = PriceMath.priceToTickIndex(initialPrice, mintAAccount.decimals, mintBAccount.decimals); - const initialTick = TickUtil.getInitializableTickIndex(tickIndex, tickSpacing); + const tickIndex = PriceMath.priceToTickIndex( + initialPrice, + mintAAccount.decimals, + mintBAccount.decimals, + ); + const initialTick = TickUtil.getInitializableTickIndex( + tickIndex, + tickSpacing, + ); const tokenExtensionCtx: TokenExtensionContextForPool = { ...NO_TOKEN_EXTENSION_CONTEXT, @@ -196,17 +210,17 @@ export async function createOrcaSingleSidedWhirlpool( tokenVaultBKeypair, feeTierKey, tickSpacing: tickSpacing, - funder: wallet.publicKey + funder: wallet.publicKey, }; const initPoolIx = !TokenExtensionUtil.isV2IxRequiredPool(tokenExtensionCtx) ? WhirlpoolIx.initializePoolIx(ctx.program, baseParamsPool) : WhirlpoolIx.initializePoolV2Ix(ctx.program, { - ...baseParamsPool, - tokenProgramA: tokenExtensionCtx.tokenMintWithProgramA.tokenProgram, - tokenProgramB: tokenExtensionCtx.tokenMintWithProgramB.tokenProgram, - tokenBadgeA, - tokenBadgeB, - }); + ...baseParamsPool, + tokenProgramA: tokenExtensionCtx.tokenMintWithProgramA.tokenProgram, + tokenProgramB: tokenExtensionCtx.tokenMintWithProgramB.tokenProgram, + tokenBadgeA, + tokenBadgeB, + }); const initialTickArrayStartTick = TickUtil.getStartTickIndex( initialTick, tickSpacing, @@ -235,14 +249,32 @@ export async function createOrcaSingleSidedWhirlpool( let tickLowerIndex, tickUpperIndex; if (isCorrectMintOrder) { tickLowerIndex = initialTick; - tickUpperIndex = PriceMath.priceToTickIndex(maxPrice, mintAAccount.decimals, mintBAccount.decimals); + tickUpperIndex = PriceMath.priceToTickIndex( + maxPrice, + mintAAccount.decimals, + mintBAccount.decimals, + ); } else { - tickLowerIndex = PriceMath.priceToTickIndex(maxPrice, mintAAccount.decimals, mintBAccount.decimals); + tickLowerIndex = PriceMath.priceToTickIndex( + maxPrice, + mintAAccount.decimals, + mintBAccount.decimals, + ); tickUpperIndex = initialTick; } - const tickLowerInitializableIndex = TickUtil.getInitializableTickIndex(tickLowerIndex, tickSpacing); - const tickUpperInitializableIndex = TickUtil.getInitializableTickIndex(tickUpperIndex, tickSpacing); - if (!TickUtil.checkTickInBounds(tickLowerInitializableIndex) || !TickUtil.checkTickInBounds(tickUpperInitializableIndex)) throw Error('Prices out of bounds'); + const tickLowerInitializableIndex = TickUtil.getInitializableTickIndex( + tickLowerIndex, + tickSpacing, + ); + const tickUpperInitializableIndex = TickUtil.getInitializableTickIndex( + tickUpperIndex, + tickSpacing, + ); + if ( + !TickUtil.checkTickInBounds(tickLowerInitializableIndex) || + !TickUtil.checkTickInBounds(tickUpperInitializableIndex) + ) + throw Error("Prices out of bounds"); const increasLiquidityQuoteParam: IncreaseLiquidityQuoteParam = { inputTokenAmount: new BN(depositTokenAmount), inputTokenMint: depositTokenMint, @@ -253,11 +285,11 @@ export async function createOrcaSingleSidedWhirlpool( tickLowerIndex: tickLowerInitializableIndex, tickUpperIndex: tickUpperInitializableIndex, tokenExtensionCtx: tokenExtensionCtx, - slippageTolerance: Percentage.fromFraction(0, 100) - } + slippageTolerance: Percentage.fromFraction(0, 100), + }; const liquidityInput = increaseLiquidityQuoteByInputTokenWithParams( - increasLiquidityQuoteParam - ) + increasLiquidityQuoteParam, + ); const { liquidityAmount: liquidity, tokenMaxA, tokenMaxB } = liquidityInput; const positionMintKeypair = Keypair.generate(); @@ -285,7 +317,7 @@ export async function createOrcaSingleSidedWhirlpool( ...params, positionMint: positionMintPubkey, withTokenMetadataExtension: true, - }) + }); txBuilder.addInstruction(positionIx); txBuilder.addSigner(positionMintKeypair); @@ -365,35 +397,33 @@ export async function createOrcaSingleSidedWhirlpool( tickArrayUpper: tickArrayUpperPda.publicKey, }; - const liquidityIx = !TokenExtensionUtil.isV2IxRequiredPool( - tokenExtensionCtx, - ) + const liquidityIx = !TokenExtensionUtil.isV2IxRequiredPool(tokenExtensionCtx) ? increaseLiquidityIx(ctx.program, baseParamsLiquidity) : increaseLiquidityV2Ix(ctx.program, { - ...baseParamsLiquidity, - tokenMintA: mintA, - tokenMintB: mintB, - tokenProgramA: tokenExtensionCtx.tokenMintWithProgramA.tokenProgram, - tokenProgramB: tokenExtensionCtx.tokenMintWithProgramB.tokenProgram, - }); + ...baseParamsLiquidity, + tokenMintA: mintA, + tokenMintB: mintB, + tokenProgramA: tokenExtensionCtx.tokenMintWithProgramA.tokenProgram, + tokenProgramB: tokenExtensionCtx.tokenMintWithProgramB.tokenProgram, + }); txBuilder.addInstruction(liquidityIx); const txPayload = await txBuilder.build({ - maxSupportedTransactionVersion: "legacy" + maxSupportedTransactionVersion: "legacy", }); if (txPayload.transaction instanceof Transaction) { try { - const txId = await sendTx( - agent, - txPayload.transaction, - [positionMintKeypair, tokenVaultAKeypair, tokenVaultBKeypair], - ); + const txId = await sendTx(agent, txPayload.transaction, [ + positionMintKeypair, + tokenVaultAKeypair, + tokenVaultBKeypair, + ]); return txId; } catch (error) { - throw new Error(`Failed to create pool: ${JSON.stringify(error)}`); + throw new Error(`Failed to create pool: ${JSON.stringify(error)}`); } } else { - throw new Error('Failed to create pool: Transaction not created'); + throw new Error("Failed to create pool: Transaction not created"); } } diff --git a/src/tools/deploy_collection.ts b/src/tools/deploy_collection.ts index 4528b92..086d736 100644 --- a/src/tools/deploy_collection.ts +++ b/src/tools/deploy_collection.ts @@ -1,18 +1,29 @@ -import { SolanaAgentKit } from "../index"; -import { generateSigner, keypairIdentity, publicKey } from "@metaplex-foundation/umi"; -import { createCollection, mplCore, ruleSet } from "@metaplex-foundation/mpl-core"; +import { SolanaAgent } from "../index"; +import { + generateSigner, + keypairIdentity, + publicKey, +} from "@metaplex-foundation/umi"; +import { + createCollection, + mplCore, + ruleSet, +} from "@metaplex-foundation/mpl-core"; import { CollectionOptions, CollectionDeployment } from "../types"; -import { fromWeb3JsKeypair, toWeb3JsPublicKey } from "@metaplex-foundation/umi-web3js-adapters"; +import { + fromWeb3JsKeypair, + toWeb3JsPublicKey, +} from "@metaplex-foundation/umi-web3js-adapters"; import { createUmi } from "@metaplex-foundation/umi-bundle-defaults"; /** * Deploy a new NFT collection - * @param agent SolanaAgentKit instance + * @param agent SolanaAgent instance * @param options Collection options including name, URI, royalties, and creators * @returns Object containing collection address and metadata */ export async function deploy_collection( - agent: SolanaAgentKit, + agent: SolanaAgent, options: CollectionOptions, ): Promise { try { @@ -28,11 +39,11 @@ export async function deploy_collection( address: publicKey(creator.address), percentage: creator.percentage, })) || [ - { - address: publicKey(agent.wallet_address.toString()), - percentage: 100, - }, - ]; + { + address: publicKey(agent.wallet_address.toString()), + percentage: 100, + }, + ]; // Create collection const tx = await createCollection(umi, { diff --git a/src/tools/deploy_token.ts b/src/tools/deploy_token.ts index a36d5de..5940141 100644 --- a/src/tools/deploy_token.ts +++ b/src/tools/deploy_token.ts @@ -1,13 +1,21 @@ -import { SolanaAgentKit } from "../index"; +import { SolanaAgent } from "../index"; import { PublicKey } from "@solana/web3.js"; import { createUmi } from "@metaplex-foundation/umi-bundle-defaults"; import { generateSigner, keypairIdentity } from "@metaplex-foundation/umi"; -import { createFungible, mintV1, TokenStandard } from "@metaplex-foundation/mpl-token-metadata"; -import { fromWeb3JsKeypair, fromWeb3JsPublicKey, toWeb3JsPublicKey } from "@metaplex-foundation/umi-web3js-adapters"; +import { + createFungible, + mintV1, + TokenStandard, +} from "@metaplex-foundation/mpl-token-metadata"; +import { + fromWeb3JsKeypair, + fromWeb3JsPublicKey, + toWeb3JsPublicKey, +} from "@metaplex-foundation/umi-web3js-adapters"; /** * Deploy a new SPL token - * @param agent SolanaAgentKit instance + * @param agent SolanaAgent instance * @param name Name of the token * @param uri URI for the token metadata * @param symbol Symbol of the token @@ -16,16 +24,16 @@ import { fromWeb3JsKeypair, fromWeb3JsPublicKey, toWeb3JsPublicKey } from "@meta * @returns Object containing token mint address and initial account (if supply was minted) */ export async function deploy_token( - agent: SolanaAgentKit, + agent: SolanaAgent, name: string, uri: string, symbol: string, decimals: number = 9, - initialSupply?: number + initialSupply?: number, ): Promise<{ mint: PublicKey }> { try { // Create UMI instance from agent - const umi = createUmi(agent.connection.rpcEndpoint) + const umi = createUmi(agent.connection.rpcEndpoint); umi.use(keypairIdentity(fromWeb3JsKeypair(agent.wallet))); // Create new token mint @@ -51,11 +59,11 @@ export async function deploy_token( tokenStandard: TokenStandard.Fungible, tokenOwner: fromWeb3JsPublicKey(agent.wallet_address), amount: initialSupply, - }) + }), ); } - builder.sendAndConfirm(umi, { confirm: { commitment: 'finalized' } }); + builder.sendAndConfirm(umi, { confirm: { commitment: "finalized" } }); return { mint: toWeb3JsPublicKey(mint.publicKey), diff --git a/src/tools/fetch_price.ts b/src/tools/fetch_price.ts index 764e8e6..cca1925 100644 --- a/src/tools/fetch_price.ts +++ b/src/tools/fetch_price.ts @@ -1,20 +1,13 @@ -import { SolanaAgentKit } from "../index"; -import { Tool } from "langchain/tools"; +import { PublicKey } from "@solana/web3.js"; /** - * Fetch the price of a given token in USDC using Jupiter API - * @param agent SolanaAgentKit instance + * Fetch the price of a given token quoted in USDC using Jupiter API * @param tokenId The token mint address - * @returns The price of the token in USDC + * @returns The price of the token quoted in USDC */ -export async function fetchPrice( - agent: SolanaAgentKit, - tokenId: string -): Promise { +export async function fetchPrice(tokenId: PublicKey): Promise { try { - const response = await fetch( - `https://api.jup.ag/price/v2?ids=${tokenId}` - ); + const response = await fetch(`https://api.jup.ag/price/v2?ids=${tokenId}`); if (!response.ok) { throw new Error(`Failed to fetch price: ${response.statusText}`); @@ -22,7 +15,7 @@ export async function fetchPrice( const data = await response.json(); - const price = data.data[tokenId]?.price; + const price = data.data[tokenId.toBase58()]?.price; if (!price) { throw new Error("Price data not available for the given token."); @@ -32,4 +25,4 @@ export async function fetchPrice( } catch (error: any) { throw new Error(`Price fetch failed: ${error.message}`); } -} \ No newline at end of file +} diff --git a/src/tools/get_balance.ts b/src/tools/get_balance.ts index 36cdfef..fb70699 100644 --- a/src/tools/get_balance.ts +++ b/src/tools/get_balance.ts @@ -1,14 +1,14 @@ import { LAMPORTS_PER_SOL, PublicKey } from "@solana/web3.js"; -import { SolanaAgentKit } from "../index"; +import { SolanaAgent } from "../index"; /** * Get the balance of SOL or an SPL token for the agent's wallet - * @param agent - SolanaAgentKit instance + * @param agent - SolanaAgent instance * @param token_address - Optional SPL token mint address. If not provided, returns SOL balance * @returns Promise resolving to the balance as a number (in UI units) or null if account doesn't exist */ export async function get_balance( - agent: SolanaAgentKit, + agent: SolanaAgent, token_address?: PublicKey, ): Promise { if (!token_address) diff --git a/src/tools/get_primary_domain.ts b/src/tools/get_primary_domain.ts index 775af4b..01f19b6 100644 --- a/src/tools/get_primary_domain.ts +++ b/src/tools/get_primary_domain.ts @@ -1,6 +1,6 @@ import { getPrimaryDomain as _getPrimaryDomain } from "@bonfida/spl-name-service"; import { PublicKey } from "@solana/web3.js"; -import { SolanaAgentKit } from "../index"; +import { SolanaAgent } from "../index"; /** * Retrieves the primary .sol domain associated with a given Solana public key. @@ -9,29 +9,29 @@ import { SolanaAgentKit } from "../index"; * a specified Solana public key. If the primary domain is stale or an error occurs during * the resolution, it throws an error. * - * @param agent SolanaAgentKit instance + * @param agent SolanaAgent instance * @param account The Solana public key for which to retrieve the primary domain * @returns A promise that resolves to the primary .sol domain as a string * @throws Error if the domain is stale or if the domain resolution fails */ export async function getPrimaryDomain( - agent: SolanaAgentKit, - account: PublicKey + agent: SolanaAgent, + account: PublicKey, ): Promise { try { const { reverse, stale } = await _getPrimaryDomain( agent.connection, - account + account, ); if (stale) { throw new Error( - `Primary domain is stale for account: ${account.toBase58()}` + `Primary domain is stale for account: ${account.toBase58()}`, ); } return reverse; } catch (error) { throw new Error( - `Failed to get primary domain for account: ${account.toBase58()}` + `Failed to get primary domain for account: ${account.toBase58()}`, ); } } diff --git a/src/tools/get_tps.ts b/src/tools/get_tps.ts index 7314ce3..38eb356 100644 --- a/src/tools/get_tps.ts +++ b/src/tools/get_tps.ts @@ -1,4 +1,4 @@ -import { SolanaAgentKit } from "../index"; +import { SolanaAgent } from "../index"; export async function getTPS(agent: SolanaAgentKit): Promise { const perfSamples = await agent.connection.getRecentPerformanceSamples(); diff --git a/src/tools/index.ts b/src/tools/index.ts index 303cde2..32a3c53 100644 --- a/src/tools/index.ts +++ b/src/tools/index.ts @@ -15,8 +15,8 @@ export * from "./get_token_data"; export * from "./stake_with_jup"; export * from "./fetch_price"; export * from "./send_compressed_airdrop"; - -export * from "./create_orca_single_sided_whirlpool";export * from "./raydium_create_ammV4"; +export * from "./create_orca_single_sided_whirlpool"; +export * from "./raydium_create_ammV4"; export * from "./raydium_create_clmm"; export * from "./raydium_create_cpmm"; -export * from "./openbook_create_market"; \ No newline at end of file +export * from "./openbook_create_market"; diff --git a/src/tools/launch_pumpfun_token.ts b/src/tools/launch_pumpfun_token.ts index 0c3e6f5..ce997b4 100644 --- a/src/tools/launch_pumpfun_token.ts +++ b/src/tools/launch_pumpfun_token.ts @@ -55,7 +55,7 @@ async function uploadMetadata( } async function createTokenTransaction( - agent: SolanaAgentKit, + agent: SolanaAgent, mintKeypair: Keypair, metadataResponse: any, options?: PumpFunTokenOptions, @@ -140,7 +140,7 @@ async function signAndSendTransaction( /** * Launch a token on Pump.fun - * @param agent - SolanaAgentKit instance + * @param agent - SolanaAgent instance * @param tokenName - Name of the token * @param tokenTicker - Ticker of the token * @param description - Description of the token @@ -149,7 +149,7 @@ async function signAndSendTransaction( * @returns - Signature of the transaction, mint address and metadata URI, if successful, else error */ export async function launchPumpFunToken( - agent: SolanaAgentKit, + agent: SolanaAgent, tokenName: string, tokenTicker: string, description: string, diff --git a/src/tools/lend.ts b/src/tools/lend.ts index 7889343..f036aa7 100644 --- a/src/tools/lend.ts +++ b/src/tools/lend.ts @@ -1,16 +1,15 @@ import { VersionedTransaction } from "@solana/web3.js"; -import { LuloAccountDetailsResponse } from "../types"; -import { SolanaAgentKit } from "../agent"; +import { SolanaAgent } from "../index"; /** * Lend tokens for yields using Lulo - * @param agent SolanaAgentKit instance + * @param agent SolanaAgent instance * @param amount Amount of USDC to lend * @returns Transaction signature */ export async function lendAsset( - agent: SolanaAgentKit, - amount: number + agent: SolanaAgent, + amount: number, ): Promise { try { const response = await fetch( @@ -23,14 +22,14 @@ export async function lendAsset( body: JSON.stringify({ account: agent.wallet.publicKey.toBase58(), }), - } + }, ); const data = await response.json(); // Deserialize the transaction const luloTxn = VersionedTransaction.deserialize( - Buffer.from(data.transaction, "base64") + Buffer.from(data.transaction, "base64"), ); // Get a recent blockhash and set it diff --git a/src/tools/mint_nft.ts b/src/tools/mint_nft.ts index 09bfe32..97e5fd2 100644 --- a/src/tools/mint_nft.ts +++ b/src/tools/mint_nft.ts @@ -1,22 +1,26 @@ -import { SolanaAgentKit } from "../index"; -import { generateSigner, keypairIdentity } from '@metaplex-foundation/umi'; -import { create, mplCore } from '@metaplex-foundation/mpl-core'; -import { fetchCollection } from '@metaplex-foundation/mpl-core'; +import { SolanaAgent } from "../index"; +import { generateSigner, keypairIdentity } from "@metaplex-foundation/umi"; +import { create, mplCore } from "@metaplex-foundation/mpl-core"; +import { fetchCollection } from "@metaplex-foundation/mpl-core"; import { PublicKey } from "@solana/web3.js"; -import { fromWeb3JsKeypair, fromWeb3JsPublicKey, toWeb3JsPublicKey } from "@metaplex-foundation/umi-web3js-adapters"; -import { createUmi } from '@metaplex-foundation/umi-bundle-defaults'; -import { MintCollectionNFTResponse } from '../types'; +import { + fromWeb3JsKeypair, + fromWeb3JsPublicKey, + toWeb3JsPublicKey, +} from "@metaplex-foundation/umi-web3js-adapters"; +import { createUmi } from "@metaplex-foundation/umi-bundle-defaults"; +import { MintCollectionNFTResponse } from "../types"; /** * Mint a new NFT as part of an existing collection - * @param agent SolanaAgentKit instance + * @param agent SolanaAgent instance * @param collectionMint Address of the collection's master NFT * @param metadata NFT metadata object * @param recipient Optional recipient address (defaults to wallet address) * @returns Object containing NFT mint address and token account */ export async function mintCollectionNFT( - agent: SolanaAgentKit, + agent: SolanaAgent, collectionMint: PublicKey, metadata: { name: string; @@ -27,7 +31,7 @@ export async function mintCollectionNFT( share: number; }>; }, - recipient?: PublicKey + recipient?: PublicKey, ): Promise { try { // Create UMI instance from agent @@ -49,15 +53,15 @@ export async function mintCollectionNFT( collection: collection, name: metadata.name, uri: metadata.uri, - owner: fromWeb3JsPublicKey(recipient ?? agent.wallet.publicKey) + owner: fromWeb3JsPublicKey(recipient ?? agent.wallet.publicKey), }).sendAndConfirm(umi); return { mint: toWeb3JsPublicKey(assetSigner.publicKey), // Note: Token account is now handled automatically by the create instruction - metadata: toWeb3JsPublicKey(assetSigner.publicKey) + metadata: toWeb3JsPublicKey(assetSigner.publicKey), }; } catch (error: any) { throw new Error(`Collection NFT minting failed: ${error.message}`); } -} \ No newline at end of file +} diff --git a/src/tools/openbook_create_market.ts b/src/tools/openbook_create_market.ts index 4e9c592..29dd829 100644 --- a/src/tools/openbook_create_market.ts +++ b/src/tools/openbook_create_market.ts @@ -1,33 +1,34 @@ -import { OPEN_BOOK_PROGRAM, Raydium, TxVersion } from "@raydium-io/raydium-sdk-v2"; +import { + OPEN_BOOK_PROGRAM, + Raydium, + TxVersion, +} from "@raydium-io/raydium-sdk-v2"; import { MintLayout, TOKEN_PROGRAM_ID } from "@solana/spl-token"; import { PublicKey } from "@solana/web3.js"; -import { SolanaAgentKit } from "../agent"; +import { SolanaAgent } from "../index"; export async function openbookCreateMarket( - agent: SolanaAgentKit, - + agent: SolanaAgent, baseMint: PublicKey, quoteMint: PublicKey, - lotSize: number = 1, tickSize: number = 0.01, ): Promise { - const raydium = await Raydium.load({ owner: agent.wallet, connection: agent.connection, - }) + }); - const baseMintInfo = await agent.connection.getAccountInfo(baseMint) - const quoteMintInfo = await agent.connection.getAccountInfo(quoteMint) + const baseMintInfo = await agent.connection.getAccountInfo(baseMint); + const quoteMintInfo = await agent.connection.getAccountInfo(quoteMint); if ( baseMintInfo?.owner.toString() !== TOKEN_PROGRAM_ID.toBase58() || quoteMintInfo?.owner.toString() !== TOKEN_PROGRAM_ID.toBase58() ) { throw new Error( - 'openbook market only support TOKEN_PROGRAM_ID mints, if you want to create pool with token-2022, please create raydium cpmm pool instead' - ) + "openbook market only support TOKEN_PROGRAM_ID mints, if you want to create pool with token-2022, please create raydium cpmm pool instead", + ); } const { execute } = await raydium.marketV2.create({ @@ -44,9 +45,9 @@ export async function openbookCreateMarket( dexProgramId: OPEN_BOOK_PROGRAM, txVersion: TxVersion.V0, - }) + }); - const { txIds } = await execute({ sequentially: true, }) + const { txIds } = await execute({ sequentially: true }); - return txIds + return txIds; } diff --git a/src/tools/raydium_create_ammV4.ts b/src/tools/raydium_create_ammV4.ts index 53d78b3..cc06431 100644 --- a/src/tools/raydium_create_ammV4.ts +++ b/src/tools/raydium_create_ammV4.ts @@ -1,42 +1,59 @@ -import { AMM_V4, FEE_DESTINATION_ID, MARKET_STATE_LAYOUT_V3, OPEN_BOOK_PROGRAM, Raydium, TxVersion } from "@raydium-io/raydium-sdk-v2"; +import { + AMM_V4, + FEE_DESTINATION_ID, + MARKET_STATE_LAYOUT_V3, + OPEN_BOOK_PROGRAM, + Raydium, + TxVersion, +} from "@raydium-io/raydium-sdk-v2"; import { MintLayout, TOKEN_PROGRAM_ID } from "@solana/spl-token"; import { PublicKey } from "@solana/web3.js"; -import BN from 'bn.js'; -import { SolanaAgentKit } from "../agent"; +import BN from "bn.js"; +import { SolanaAgent } from "../index"; export async function raydiumCreateAmmV4( - agent: SolanaAgentKit, - + agent: SolanaAgent, marketId: PublicKey, - baseAmount: BN, quoteAmount: BN, - startTime: BN, ): Promise { - const raydium = await Raydium.load({ owner: agent.wallet, connection: agent.connection, - }) + }); - const marketBufferInfo = await agent.connection.getAccountInfo(new PublicKey(marketId)) - const { baseMint, quoteMint } = MARKET_STATE_LAYOUT_V3.decode(marketBufferInfo!.data) + const marketBufferInfo = await agent.connection.getAccountInfo( + new PublicKey(marketId), + ); + const { baseMint, quoteMint } = MARKET_STATE_LAYOUT_V3.decode( + marketBufferInfo!.data, + ); - const baseMintInfo = await agent.connection.getAccountInfo(baseMint) - const quoteMintInfo = await agent.connection.getAccountInfo(quoteMint) + const baseMintInfo = await agent.connection.getAccountInfo(baseMint); + const quoteMintInfo = await agent.connection.getAccountInfo(quoteMint); if ( baseMintInfo?.owner.toString() !== TOKEN_PROGRAM_ID.toBase58() || quoteMintInfo?.owner.toString() !== TOKEN_PROGRAM_ID.toBase58() ) { throw new Error( - 'amm pools with openbook market only support TOKEN_PROGRAM_ID mints, if you want to create pool with token-2022, please create cpmm pool instead' - ) + "amm pools with openbook market only support TOKEN_PROGRAM_ID mints, if you want to create pool with token-2022, please create cpmm pool instead", + ); } - if (baseAmount.mul(quoteAmount).lte(new BN(1).mul(new BN(10 ** MintLayout.decode(baseMintInfo.data).decimals)).pow(new BN(2)))) { - throw new Error('initial liquidity too low, try adding more baseAmount/quoteAmount') + if ( + baseAmount + .mul(quoteAmount) + .lte( + new BN(1) + .mul(new BN(10 ** MintLayout.decode(baseMintInfo.data).decimals)) + .pow(new BN(2)), + ) + ) { + throw new Error( + "initial liquidity too low, try adding more baseAmount/quoteAmount", + ); } const { execute } = await raydium.liquidity.createPoolV4({ @@ -63,9 +80,9 @@ export async function raydiumCreateAmmV4( associatedOnly: false, txVersion: TxVersion.V0, feeDestinationId: FEE_DESTINATION_ID, - }) + }); - const { txId } = await execute({ sendAndConfirm: true }) + const { txId } = await execute({ sendAndConfirm: true }); - return txId -} \ No newline at end of file + return txId; +} diff --git a/src/tools/raydium_create_clmm.ts b/src/tools/raydium_create_clmm.ts index 8dbd5e6..2ce62f1 100644 --- a/src/tools/raydium_create_clmm.ts +++ b/src/tools/raydium_create_clmm.ts @@ -1,55 +1,58 @@ -import { CLMM_PROGRAM_ID, Raydium, TxVersion } from '@raydium-io/raydium-sdk-v2' -import { MintLayout } from '@solana/spl-token' -import { PublicKey } from '@solana/web3.js' -import BN from 'bn.js' -import Decimal from 'decimal.js' -import { SolanaAgentKit } from '../agent' +import { + CLMM_PROGRAM_ID, + Raydium, + TxVersion, +} from "@raydium-io/raydium-sdk-v2"; +import { MintLayout } from "@solana/spl-token"; +import { PublicKey } from "@solana/web3.js"; +import BN from "bn.js"; +import Decimal from "decimal.js"; +import { SolanaAgent } from "../index"; export async function raydiumCreateClmm( - agent: SolanaAgentKit, - + agent: SolanaAgent, mint1: PublicKey, mint2: PublicKey, - configId: PublicKey, - initialPrice: Decimal, startTime: BN, ): Promise { - const raydium = await Raydium.load({ owner: agent.wallet, connection: agent.connection, - }) + }); - const [mintInfo1, mintInfo2] = await agent.connection.getMultipleAccountsInfo([mint1, mint2]) - if (mintInfo1 === null || mintInfo2 === null) throw Error('fetch mint info error') + const [mintInfo1, mintInfo2] = await agent.connection.getMultipleAccountsInfo( + [mint1, mint2], + ); + if (mintInfo1 === null || mintInfo2 === null) + throw Error("fetch mint info error"); - const mintDecodeInfo1 = MintLayout.decode(mintInfo1.data) - const mintDecodeInfo2 = MintLayout.decode(mintInfo2.data) + const mintDecodeInfo1 = MintLayout.decode(mintInfo1.data); + const mintDecodeInfo2 = MintLayout.decode(mintInfo2.data); const mintFormatInfo1 = { chainId: 101, address: mint1.toString(), programId: mintInfo1.owner.toString(), - logoURI: '', - symbol: '', - name: '', + logoURI: "", + symbol: "", + name: "", decimals: mintDecodeInfo1.decimals, tags: [], - extensions: {} - } + extensions: {}, + }; const mintFormatInfo2 = { chainId: 101, address: mint2.toString(), programId: mintInfo2.owner.toString(), - logoURI: '', - symbol: '', - name: '', + logoURI: "", + symbol: "", + name: "", decimals: mintDecodeInfo2.decimals, tags: [], - extensions: {} - } + extensions: {}, + }; const { execute } = await raydium.clmm.createPool({ programId: CLMM_PROGRAM_ID, @@ -65,9 +68,9 @@ export async function raydiumCreateClmm( // units: 600000, // microLamports: 46591500, // }, - }) + }); - const { txId } = await execute({ sendAndConfirm: true }) + const { txId } = await execute({ sendAndConfirm: true }); - return txId + return txId; } diff --git a/src/tools/raydium_create_cpmm.ts b/src/tools/raydium_create_cpmm.ts index 4a520e2..6e129c6 100644 --- a/src/tools/raydium_create_cpmm.ts +++ b/src/tools/raydium_create_cpmm.ts @@ -2,60 +2,58 @@ import { CREATE_CPMM_POOL_FEE_ACC, CREATE_CPMM_POOL_PROGRAM, Raydium, - TxVersion -} from '@raydium-io/raydium-sdk-v2' -import { MintLayout } from '@solana/spl-token' -import { PublicKey } from '@solana/web3.js' -import BN from 'bn.js' -import { SolanaAgentKit } from '../agent' + TxVersion, +} from "@raydium-io/raydium-sdk-v2"; +import { MintLayout } from "@solana/spl-token"; +import { PublicKey } from "@solana/web3.js"; +import BN from "bn.js"; +import { SolanaAgent } from "../index"; export async function raydiumCreateCpmm( - agent: SolanaAgentKit, - + agent: SolanaAgent, mintA: PublicKey, mintB: PublicKey, - configId: PublicKey, - mintAAmount: BN, mintBAmount: BN, - startTime: BN, ): Promise { - const raydium = await Raydium.load({ owner: agent.wallet, connection: agent.connection, - }) + }); - const [mintInfoA, mintInfoB] = await agent.connection.getMultipleAccountsInfo([mintA, mintB]) - if (mintInfoA === null || mintInfoB === null) throw Error('fetch mint info error') + const [mintInfoA, mintInfoB] = await agent.connection.getMultipleAccountsInfo( + [mintA, mintB], + ); + if (mintInfoA === null || mintInfoB === null) + throw Error("fetch mint info error"); - const mintDecodeInfoA = MintLayout.decode(mintInfoA.data) - const mintDecodeInfoB = MintLayout.decode(mintInfoB.data) + const mintDecodeInfoA = MintLayout.decode(mintInfoA.data); + const mintDecodeInfoB = MintLayout.decode(mintInfoB.data); const mintFormatInfoA = { chainId: 101, address: mintA.toString(), programId: mintInfoA.owner.toString(), - logoURI: '', - symbol: '', - name: '', + logoURI: "", + symbol: "", + name: "", decimals: mintDecodeInfoA.decimals, tags: [], - extensions: {} - } + extensions: {}, + }; const mintFormatInfoB = { chainId: 101, address: mintB.toString(), programId: mintInfoB.owner.toString(), - logoURI: '', - symbol: '', - name: '', + logoURI: "", + symbol: "", + name: "", decimals: mintDecodeInfoB.decimals, tags: [], - extensions: {} - } + extensions: {}, + }; const { execute, extInfo } = await raydium.cpmm.createPool({ programId: CREATE_CPMM_POOL_PROGRAM, @@ -76,9 +74,9 @@ export async function raydiumCreateCpmm( // units: 600000, // microLamports: 46591500, // }, - }) + }); - const { txId } = await execute({ sendAndConfirm: true }) + const { txId } = await execute({ sendAndConfirm: true }); - return txId + return txId; } diff --git a/src/tools/register_domain.ts b/src/tools/register_domain.ts index 1348001..20bcf79 100644 --- a/src/tools/register_domain.ts +++ b/src/tools/register_domain.ts @@ -1,18 +1,18 @@ import { registerDomainNameV2 } from "@bonfida/spl-name-service"; import { Transaction } from "@solana/web3.js"; -import { SolanaAgentKit } from "../index"; +import { SolanaAgent } from "../index"; import { getAssociatedTokenAddressSync } from "@solana/spl-token"; import { TOKENS } from "../constants"; /** * Register a .sol domain name using Bonfida Name Service - * @param agent SolanaAgentKit instance + * @param agent SolanaAgent instance * @param name Domain name to register (without .sol) * @param spaceKB Space allocation in KB (max 10KB) * @returns Transaction signature */ export async function registerDomain( - agent: SolanaAgentKit, + agent: SolanaAgent, name: string, spaceKB: number = 1, ): Promise { diff --git a/src/tools/request_faucet_funds.ts b/src/tools/request_faucet_funds.ts index bde8eed..dd44cb3 100644 --- a/src/tools/request_faucet_funds.ts +++ b/src/tools/request_faucet_funds.ts @@ -1,14 +1,14 @@ -import { SolanaAgentKit } from "../index"; +import { SolanaAgent } from "../index"; import { LAMPORTS_PER_SOL } from "@solana/web3.js"; /** * Request SOL from the Solana faucet (devnet/testnet only) - * @param agent - SolanaAgentKit instance + * @param agent - SolanaAgent instance * @returns Transaction signature * @throws Error if the request fails or times out */ export async function request_faucet_funds( - agent: SolanaAgentKit, + agent: SolanaAgent, ): Promise { const tx = await agent.connection.requestAirdrop( agent.wallet_address, diff --git a/src/tools/resolve_sol_domain.ts b/src/tools/resolve_sol_domain.ts index d8764cc..e5c87a5 100644 --- a/src/tools/resolve_sol_domain.ts +++ b/src/tools/resolve_sol_domain.ts @@ -1,6 +1,6 @@ import { resolve } from "@bonfida/spl-name-service"; import { PublicKey } from "@solana/web3.js"; -import { SolanaAgentKit } from "../index"; +import { SolanaAgent } from "../index"; /** * Resolves a .sol domain to a Solana PublicKey. @@ -9,14 +9,14 @@ import { SolanaAgentKit } from "../index"; * to the corresponding Solana PublicKey. The domain can be provided with or without * the .sol suffix. * - * @param agent SolanaAgentKit instance + * @param agent SolanaAgent instance * @param domain The .sol domain to resolve. This can be provided with or without the .sol TLD suffix * @returns A promise that resolves to the corresponding Solana PublicKey * @throws Error if the domain resolution fails */ export async function resolveSolDomain( - agent: SolanaAgentKit, - domain: string + agent: SolanaAgent, + domain: string, ): Promise { if (!domain || typeof domain !== "string") { throw new Error("Invalid domain. Expected a non-empty string."); diff --git a/src/tools/send_compressed_airdrop.ts b/src/tools/send_compressed_airdrop.ts index 313bbdb..3a80f42 100644 --- a/src/tools/send_compressed_airdrop.ts +++ b/src/tools/send_compressed_airdrop.ts @@ -1,12 +1,11 @@ import { AddressLookupTableAccount, ComputeBudgetProgram, - Connection, Keypair, PublicKey, TransactionInstruction, } from "@solana/web3.js"; -import { SolanaAgentKit } from "../agent/index.js"; +import { SolanaAgent } from "../index"; import { buildAndSignTx, calculateComputeUnitPrice, @@ -33,7 +32,7 @@ const MAX_CONCURRENT_TXS = 30; */ export const getAirdropCostEstimate = ( numberOfRecipients: number, - priorityFeeInLamports: number + priorityFeeInLamports: number, ) => { const baseFee = 5000; const perRecipientCompressedStateFee = 300; @@ -57,28 +56,27 @@ export const getAirdropCostEstimate = ( * @param shouldLog Whether to log progress to stdout. Defaults to false. */ export async function sendCompressedAirdrop( - agent: SolanaAgentKit, + agent: SolanaAgent, mintAddress: PublicKey, amount: number, decimals: number, recipients: PublicKey[], priorityFeeInLamports: number, - shouldLog: boolean = false + shouldLog: boolean = false, ): Promise { if (recipients.length > MAX_AIRDROP_RECIPIENTS) { throw new Error( - `Max airdrop can be ${MAX_AIRDROP_RECIPIENTS} recipients at a time. For more scale, use open source ZK Compression airdrop tools such as https://github.com/helius-labs/airship.` + `Max airdrop can be ${MAX_AIRDROP_RECIPIENTS} recipients at a time. For more scale, use open source ZK Compression airdrop tools such as https://github.com/helius-labs/airship.`, ); } - const url = agent.connection.rpcEndpoint; if (url.includes("devnet")) { throw new Error("Devnet is not supported for airdrop. Please use mainnet."); } if (!url.includes("helius")) { console.warn( - "Warning: Must use RPC with ZK Compression support. Double check with your RPC provider if in doubt." + "Warning: Must use RPC with ZK Compression support. Double check with your RPC provider if in doubt.", ); } @@ -88,11 +86,11 @@ export async function sendCompressedAirdrop( agent.connection, agent.wallet, mintAddress, - agent.wallet.publicKey + agent.wallet.publicKey, ); } catch (error) { throw new Error( - "Source token account not found and failed to create it. Please add funds to your wallet and try again." + "Source token account not found and failed to create it. Please add funds to your wallet and try again.", ); } @@ -100,7 +98,7 @@ export async function sendCompressedAirdrop( await createTokenPool( agent.connection as unknown as Rpc, agent.wallet, - mintAddress + mintAddress, ); } catch (error: any) { if (error.message.includes("already in use")) { @@ -116,17 +114,17 @@ export async function sendCompressedAirdrop( mintAddress, recipients, priorityFeeInLamports, - shouldLog + shouldLog, ); } async function processAll( - agent: SolanaAgentKit, + agent: SolanaAgent, amount: number, mint: PublicKey, recipients: PublicKey[], priorityFeeInLamports: number, - shouldLog: boolean + shouldLog: boolean, ): Promise { const mintAddress = mint; const payer = agent.wallet; @@ -135,13 +133,13 @@ async function processAll( agent.connection, agent.wallet, mintAddress, - agent.wallet.publicKey + agent.wallet.publicKey, ); const maxRecipientsPerInstruction = 5; const maxIxs = 3; // empirically determined (as of 12/15/2024) const lookupTableAddress = new PublicKey( - "9NYFyEqPkyXUhkerbGHXUXkvb4qpzeEdHuGpgbgpH1NJ" + "9NYFyEqPkyXUhkerbGHXUXkvb4qpzeEdHuGpgbgpH1NJ", ); const lookupTableAccount = ( @@ -164,7 +162,7 @@ async function processAll( ComputeBudgetProgram.setComputeUnitPrice({ microLamports: calculateComputeUnitPrice( priorityFeeInLamports, - 500_000 + 500_000, ), }), ]; @@ -184,13 +182,13 @@ async function processAll( toAddress: batch, amount: batch.map(() => amount), mint: mintAddress, - }) + }), ); } const compressIxs = await Promise.all(compressIxPromises); return [...instructions, ...compressIxs]; - }) + }), ); const url = agent.connection.rpcEndpoint; @@ -225,12 +223,12 @@ async function processAll( instructions, payer, lookupTableAccount, - i + idx + i + idx, ).then((signature) => { confirmedCount++; log("\r" + renderProgressBar(confirmedCount, totalBatches)); return signature; - }) + }), ); const batchResults = await Promise.allSettled(batchPromises); @@ -250,7 +248,7 @@ async function processAll( throw new Error( `Failed to process ${failures.length} batches: ${failures .map((f) => f.error) - .join(", ")}` + .join(", ")}`, ); } @@ -262,7 +260,7 @@ async function sendTransactionWithRetry( instructions: TransactionInstruction[], payer: Keypair, lookupTableAccount: AddressLookupTableAccount, - batchIndex: number + batchIndex: number, ): Promise { const MAX_RETRIES = 3; const INITIAL_BACKOFF = 500; // ms @@ -275,7 +273,7 @@ async function sendTransactionWithRetry( payer, blockhash, [], - [lookupTableAccount] + [lookupTableAccount], ); const signature = await sendAndConfirmTx(connection, tx); @@ -292,7 +290,7 @@ async function sendTransactionWithRetry( throw new Error( `Batch ${batchIndex} failed after ${attempt + 1} attempts: ${ error.message - }` + }`, ); } diff --git a/src/tools/stake_with_jup.ts b/src/tools/stake_with_jup.ts index e152532..981dd64 100644 --- a/src/tools/stake_with_jup.ts +++ b/src/tools/stake_with_jup.ts @@ -1,14 +1,14 @@ import { VersionedTransaction } from "@solana/web3.js"; -import { SolanaAgentKit } from "../agent"; +import { SolanaAgent } from "../index"; /** * Stake SOL with Jup validator - * @param agent SolanaAgentKit instance + * @param agent SolanaAgent instance * @param amount Amount of SOL to stake * @returns Transaction signature */ export async function stakeWithJup( - agent: SolanaAgentKit, + agent: SolanaAgent, amount: number, ): Promise { try { diff --git a/src/tools/trade.ts b/src/tools/trade.ts index b17bc41..4c9b713 100644 --- a/src/tools/trade.ts +++ b/src/tools/trade.ts @@ -1,10 +1,14 @@ -import { VersionedTransaction, PublicKey, LAMPORTS_PER_SOL } from "@solana/web3.js"; -import { SolanaAgentKit } from "../index"; +import { + VersionedTransaction, + PublicKey, + LAMPORTS_PER_SOL, +} from "@solana/web3.js"; +import { SolanaAgent } from "../index"; import { TOKENS, DEFAULT_OPTIONS, JUP_API } from "../constants"; /** * Swap tokens using Jupiter Exchange - * @param agent SolanaAgentKit instance + * @param agent SolanaAgent instance * @param outputMint Target token mint address * @param inputAmount Amount to swap (in token decimals) * @param inputMint Source token mint address (defaults to USDC) @@ -12,7 +16,7 @@ import { TOKENS, DEFAULT_OPTIONS, JUP_API } from "../constants"; * @returns Transaction signature */ export async function trade( - agent: SolanaAgentKit, + agent: SolanaAgent, outputMint: PublicKey, inputAmount: number, inputMint: PublicKey = TOKENS.USDC, diff --git a/src/tools/transfer.ts b/src/tools/transfer.ts index acfec82..93607de 100644 --- a/src/tools/transfer.ts +++ b/src/tools/transfer.ts @@ -1,29 +1,25 @@ -import { SolanaAgentKit } from "../index"; -import { - PublicKey, - SystemProgram, - Transaction -} from "@solana/web3.js"; +import { SolanaAgent } from "../index"; +import { PublicKey, SystemProgram, Transaction } from "@solana/web3.js"; import { LAMPORTS_PER_SOL } from "@solana/web3.js"; -import { - getAssociatedTokenAddress, +import { + getAssociatedTokenAddress, createTransferInstruction, - getMint + getMint, } from "@solana/spl-token"; /** * Transfer SOL or SPL tokens to a recipient - * @param agent SolanaAgentKit instance + * @param agent SolanaAgent instance * @param to Recipient's public key * @param amount Amount to transfer * @param mint Optional mint address for SPL tokens * @returns Transaction signature */ export async function transfer( - agent: SolanaAgentKit, + agent: SolanaAgent, to: PublicKey, amount: number, - mint?: PublicKey + mint?: PublicKey, ): Promise { try { let tx: string; @@ -34,19 +30,19 @@ export async function transfer( SystemProgram.transfer({ fromPubkey: agent.wallet_address, toPubkey: to, - lamports: amount * LAMPORTS_PER_SOL - }) + lamports: amount * LAMPORTS_PER_SOL, + }), ); - tx = await agent.connection.sendTransaction( - transaction, - [agent.wallet] - ); + tx = await agent.connection.sendTransaction(transaction, [agent.wallet]); } else { // Transfer SPL token - const fromAta = await getAssociatedTokenAddress(mint, agent.wallet_address); + const fromAta = await getAssociatedTokenAddress( + mint, + agent.wallet_address, + ); const toAta = await getAssociatedTokenAddress(mint, to); - + // Get mint info to determine decimals const mintInfo = await getMint(agent.connection, mint); const adjustedAmount = amount * Math.pow(10, mintInfo.decimals); @@ -56,18 +52,15 @@ export async function transfer( fromAta, toAta, agent.wallet_address, - adjustedAmount - ) + adjustedAmount, + ), ); - tx = await agent.connection.sendTransaction( - transaction, - [agent.wallet] - ); + tx = await agent.connection.sendTransaction(transaction, [agent.wallet]); } return tx; } catch (error: any) { throw new Error(`Transfer failed: ${error.message}`); } -} \ No newline at end of file +} diff --git a/src/utils/send_tx.ts b/src/utils/send_tx.ts index 903ddd5..65fafe4 100644 --- a/src/utils/send_tx.ts +++ b/src/utils/send_tx.ts @@ -1,4 +1,4 @@ -import { SolanaAgentKit } from "../agent"; +import { SolanaAgent } from "../agent"; import { Transaction, Keypair, TransactionInstruction } from "@solana/web3.js"; import { Connection, ComputeBudgetProgram } from "@solana/web3.js"; @@ -41,7 +41,7 @@ export async function getPriorityFees(connection: Connection): Promise<{ const median = sortedFees.length % 2 === 0 ? ((sortedFees[mid - 1] ?? 0) + (sortedFees[mid] ?? 0)) / 2 - : sortedFees[mid] ?? 0; + : (sortedFees[mid] ?? 0); // Helper to create priority fee IX based on chosen strategy const createPriorityFeeIx = (fee: number) => { @@ -69,14 +69,14 @@ export async function getPriorityFees(connection: Connection): Promise<{ /** * Send a transaction with priority fees - * @param agent - SolanaAgentKit instance + * @param agent - SolanaAgent instance * @param tx - Transaction to send * @returns Transaction ID */ export async function sendTx( - agent: SolanaAgentKit, + agent: SolanaAgent, tx: Transaction, - otherKeypairs?: Keypair[] + otherKeypairs?: Keypair[], ) { tx.recentBlockhash = (await agent.connection.getLatestBlockhash()).blockhash; tx.feePayer = agent.wallet_address; @@ -90,9 +90,8 @@ export async function sendTx( await agent.connection.confirmTransaction({ signature: txid, blockhash: (await agent.connection.getLatestBlockhash()).blockhash, - lastValidBlockHeight: ( - await agent.connection.getLatestBlockhash() - ).lastValidBlockHeight, + lastValidBlockHeight: (await agent.connection.getLatestBlockhash()) + .lastValidBlockHeight, }); return txid; } diff --git a/test/index.ts b/test/index.ts index 9522c23..b226ce4 100644 --- a/test/index.ts +++ b/test/index.ts @@ -1,4 +1,4 @@ -import { SolanaAgentKit } from "../src"; +import { SolanaAgent } from "../src"; import { createSolanaTools } from "../src/langchain"; import { HumanMessage } from "@langchain/core/messages"; import { MemorySaver } from "@langchain/langgraph"; @@ -14,7 +14,7 @@ function validateEnvironment(): void { const missingVars: string[] = []; const requiredVars = ["OPENAI_API_KEY", "RPC_URL", "SOLANA_PRIVATE_KEY"]; - requiredVars.forEach(varName => { + requiredVars.forEach((varName) => { if (!process.env[varName]) { missingVars.push(varName); } @@ -22,7 +22,7 @@ function validateEnvironment(): void { if (missingVars.length > 0) { console.error("Error: Required environment variables are not set"); - missingVars.forEach(varName => { + missingVars.forEach((varName) => { console.error(`${varName}=your_${varName.toLowerCase()}_here`); }); process.exit(1); @@ -50,13 +50,13 @@ async function initializeAgent() { } } - const solanaKit = new SolanaAgentKit( + const solanaAgent = new SolanaAgent( process.env.SOLANA_PRIVATE_KEY!, process.env.RPC_URL, - process.env.OPENAI_API_KEY! + process.env.OPENAI_API_KEY!, ); - const tools = createSolanaTools(solanaKit); + const tools = createSolanaTools(solanaAgent); const memory = new MemorySaver(); const config = { configurable: { thread_id: "Solana Agent Kit!" } }; @@ -65,13 +65,13 @@ async function initializeAgent() { tools, checkpointSaver: memory, messageModifier: ` - You are a helpful agent that can interact onchain using the Solana Agent Kit. You are - empowered to interact onchain using your tools. If you ever need funds, you can request them from the - faucet. If not, you can provide your wallet details and request funds from the user. If there is a 5XX - (internal) HTTP error code, ask the user to try again later. If someone asks you to do something you - can't do with your currently available tools, you must say so, and encourage them to implement it - themselves using the Solana Agent Kit, recommend they go to https://sendai.fun/kit for more information. Be - concise and helpful with your responses. Refrain from restating your tools' descriptions unless it is + You are a helpful agent that can interact onchain using the Solana Agent Kit. You are + empowered to interact onchain using your tools. If you ever need funds, you can request them from the + faucet. If not, you can provide your wallet details and request funds from the user. If there is a 5XX + (internal) HTTP error code, ask the user to try again later. If someone asks you to do something you + can't do with your currently available tools, you must say so, and encourage them to implement it + themselves using the Solana Agent Kit, recommend they go to https://sendai.fun/kit for more information. Be + concise and helpful with your responses. Refrain from restating your tools' descriptions unless it is explicitly requested. `, }); @@ -96,7 +96,10 @@ async function runAutonomousMode(agent: any, config: any, interval = 10) { "Be creative and do something interesting on the blockchain. " + "Choose an action or set of actions and execute it that highlights your abilities."; - const stream = await agent.stream({ messages: [new HumanMessage(thought)] }, config); + const stream = await agent.stream( + { messages: [new HumanMessage(thought)] }, + config, + ); for await (const chunk of stream) { if ("agent" in chunk) { @@ -107,7 +110,7 @@ async function runAutonomousMode(agent: any, config: any, interval = 10) { console.log("-------------------"); } - await new Promise(resolve => setTimeout(resolve, interval * 1000)); + await new Promise((resolve) => setTimeout(resolve, interval * 1000)); } catch (error) { if (error instanceof Error) { console.error("Error:", error.message); @@ -126,7 +129,7 @@ async function runChatMode(agent: any, config: any) { }); const question = (prompt: string): Promise => - new Promise(resolve => rl.question(prompt, resolve)); + new Promise((resolve) => rl.question(prompt, resolve)); try { while (true) { @@ -136,7 +139,10 @@ async function runChatMode(agent: any, config: any) { break; } - const stream = await agent.stream({ messages: [new HumanMessage(userInput)] }, config); + const stream = await agent.stream( + { messages: [new HumanMessage(userInput)] }, + config, + ); for await (const chunk of stream) { if ("agent" in chunk) { @@ -164,7 +170,7 @@ async function chooseMode(): Promise<"chat" | "auto"> { }); const question = (prompt: string): Promise => - new Promise(resolve => rl.question(prompt, resolve)); + new Promise((resolve) => rl.question(prompt, resolve)); while (true) { console.log("\nAvailable modes:"); @@ -206,7 +212,7 @@ async function main() { } if (require.main === module) { - main().catch(error => { + main().catch((error) => { console.error("Fatal error:", error); process.exit(1); });