From d277d0d2834155fab91bd909f0460d5628555499 Mon Sep 17 00:00:00 2001 From: michaelessiet Date: Wed, 1 Jan 2025 20:46:56 +0100 Subject: [PATCH 1/7] feat: get all token balances --- .../app/api/chat/route.ts | 80 +++++++++--------- .../data/DefaultRetrievalText.ts | 2 +- .../utils/markdownToHTML.ts | 30 +++---- src/actions/balance.ts | 1 - src/actions/createOrcaSingleSidedWhirlpool.ts | 2 +- src/actions/index.ts | 57 ++++++------- src/agent/index.ts | 30 +++++-- src/tools/get_balance.ts | 49 ++++++++++- src/utils/tokenMetadata.ts | 83 +++++++++++++++++++ test/index.ts | 2 +- 10 files changed, 239 insertions(+), 97 deletions(-) create mode 100644 src/utils/tokenMetadata.ts diff --git a/examples/agent-kit-nextjs-langchain/app/api/chat/route.ts b/examples/agent-kit-nextjs-langchain/app/api/chat/route.ts index 06e6911..badd6b3 100644 --- a/examples/agent-kit-nextjs-langchain/app/api/chat/route.ts +++ b/examples/agent-kit-nextjs-langchain/app/api/chat/route.ts @@ -5,24 +5,24 @@ import { createReactAgent } from "@langchain/langgraph/prebuilt"; import { SolanaAgentKit, createSolanaTools } from "solana-agent-kit"; const llm = new ChatOpenAI({ - temperature: 0.7, - model: "gpt-4o-mini", + temperature: 0.7, + model: "gpt-4o-mini", }); const solanaAgent = new SolanaAgentKit( - process.env.SOLANA_PRIVATE_KEY!, - process.env.RPC_URL, - process.env.OPENAI_API_KEY!, + process.env.SOLANA_PRIVATE_KEY!, + process.env.RPC_URL, + process.env.OPENAI_API_KEY!, ); const tools = createSolanaTools(solanaAgent); const memory = new MemorySaver(); const agent = createReactAgent({ - llm, - tools, - checkpointSaver: memory, - messageModifier: ` + llm, + 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 @@ -34,38 +34,38 @@ const agent = createReactAgent({ }); export async function POST(req: NextRequest) { - try { - const body = await req.json(); - const messages = body.messages ?? []; + try { + const body = await req.json(); + const messages = body.messages ?? []; - const eventStream = agent.streamEvents( - { - messages, - }, - { - version: "v2", - configurable: { - thread_id: "Solana Agent Kit!", - }, - }, - ); + const eventStream = agent.streamEvents( + { + messages, + }, + { + version: "v2", + configurable: { + thread_id: "Solana Agent Kit!", + }, + }, + ); - const textEncoder = new TextEncoder(); - const transformStream = new ReadableStream({ - async start(controller) { - for await (const { event, data } of eventStream) { - if (event === "on_chat_model_stream") { - if (!!data.chunk.content) { - controller.enqueue(textEncoder.encode(data.chunk.content)); - } - } - } - controller.close(); - }, - }); + const textEncoder = new TextEncoder(); + const transformStream = new ReadableStream({ + async start(controller) { + for await (const { event, data } of eventStream) { + if (event === "on_chat_model_stream") { + if (data.chunk.content) { + controller.enqueue(textEncoder.encode(data.chunk.content)); + } + } + } + controller.close(); + }, + }); - return new Response(transformStream); - } catch (e: any) { - return NextResponse.json({ error: e.message }, { status: e.status ?? 500 }); - } + return new Response(transformStream); + } catch (e: any) { + return NextResponse.json({ error: e.message }, { status: e.status ?? 500 }); + } } diff --git a/examples/agent-kit-nextjs-langchain/data/DefaultRetrievalText.ts b/examples/agent-kit-nextjs-langchain/data/DefaultRetrievalText.ts index 898acba..6973d98 100644 --- a/examples/agent-kit-nextjs-langchain/data/DefaultRetrievalText.ts +++ b/examples/agent-kit-nextjs-langchain/data/DefaultRetrievalText.ts @@ -537,4 +537,4 @@ const executor = await initializeAgentExecutorWithOptions(tools, llm, { }, }); \`\`\` -`; \ No newline at end of file +`; diff --git a/examples/agent-kit-nextjs-langchain/utils/markdownToHTML.ts b/examples/agent-kit-nextjs-langchain/utils/markdownToHTML.ts index dc265b1..135fdd9 100644 --- a/examples/agent-kit-nextjs-langchain/utils/markdownToHTML.ts +++ b/examples/agent-kit-nextjs-langchain/utils/markdownToHTML.ts @@ -2,29 +2,29 @@ import { marked } from "marked"; import DOMPurify from "isomorphic-dompurify"; interface MarkedOptions { - gfm: boolean; - breaks: boolean; - headerIds: boolean; - mangle: false; - highlight?: (code: string, lang: string) => string; + gfm: boolean; + breaks: boolean; + headerIds: boolean; + mangle: false; + highlight?: (code: string, lang: string) => string; } // Configure marked options const markedOptions: MarkedOptions = { - gfm: true, // GitHub Flavored Markdown - breaks: true, // Convert \n to
- headerIds: true, // Add ids to headers - mangle: false, // Don't escape HTML - highlight: function (code: string, lang: string): string { - // You can add syntax highlighting here if needed - return code; - }, + gfm: true, // GitHub Flavored Markdown + breaks: true, // Convert \n to
+ headerIds: true, // Add ids to headers + mangle: false, // Don't escape HTML + highlight: function (code: string, lang: string): string { + // You can add syntax highlighting here if needed + return code; + }, }; marked.setOptions(markedOptions); // Basic markdown to HTML conversion with sanitization export default function markdownToHtml(markdown: string) { - const rawHtml = marked.parse(markdown); - return DOMPurify.sanitize(rawHtml as string); + const rawHtml = marked.parse(markdown); + return DOMPurify.sanitize(rawHtml as string); } diff --git a/src/actions/balance.ts b/src/actions/balance.ts index 381b2a5..1ee1b56 100644 --- a/src/actions/balance.ts +++ b/src/actions/balance.ts @@ -54,7 +54,6 @@ const balanceAction: Action = { return { status: "success", balance: balance, - token: input.tokenAddress || "SOL", }; }, }; diff --git a/src/actions/createOrcaSingleSidedWhirlpool.ts b/src/actions/createOrcaSingleSidedWhirlpool.ts index affc445..3a1fb52 100644 --- a/src/actions/createOrcaSingleSidedWhirlpool.ts +++ b/src/actions/createOrcaSingleSidedWhirlpool.ts @@ -87,7 +87,7 @@ const createOrcaSingleSidedWhirlpoolAction: Action = { const otherTokenMint = new PublicKey(input.otherTokenMint); const initialPrice = new Decimal(input.initialPrice); const maxPrice = new Decimal(input.maxPrice); - const feeTier = input.feeTier + const feeTier = input.feeTier; // Create the whirlpool const signature = await orcaCreateSingleSidedLiquidityPool( diff --git a/src/actions/index.ts b/src/actions/index.ts index b66c89e..1ccdb64 100644 --- a/src/actions/index.ts +++ b/src/actions/index.ts @@ -28,34 +28,35 @@ import createOrcaSingleSidedWhirlpoolAction from "./createOrcaSingleSidedWhirlpo import launchPumpfunTokenAction from "./launchPumpfunToken"; export const ACTIONS = { - "DEPLOY_TOKEN_ACTION" : deployTokenAction, - "BALANCE_ACTION" : balanceAction, - "TRANSFER_ACTION" : transferAction, - "DEPLOY_COLLECTION_ACTION" : deployCollectionAction, - "MINT_NFT_ACTION" : mintNFTAction, - "TRADE_ACTION" : tradeAction, - "REQUEST_FUNDS_ACTION" : requestFundsAction, - "RESOLVE_DOMAIN_ACTION" : resolveDomainAction, - "GET_TOKEN_DATA_ACTION" : getTokenDataAction, - "GET_TPS_ACTION" : getTPSAction, - "FETCH_PRICE_ACTION" : fetchPriceAction, - "STAKE_WITH_JUP_ACTION" : stakeWithJupAction, - "REGISTER_DOMAIN_ACTION" : registerDomainAction, - "LEND_ASSET_ACTION" : lendAssetAction, - "CREATE_GIBWORK_TASK_ACTION" : createGibworkTaskAction, - "RESOLVE_SOL_DOMAIN_ACTION" : resolveSolDomainAction, - "PYTH_FETCH_PRICE_ACTION" : pythFetchPriceAction, - "GET_OWNED_DOMAINS_FOR_TLD_ACTION" : getOwnedDomainsForTLDAction, - "GET_PRIMARY_DOMAIN_ACTION" : getPrimaryDomainAction, - "GET_ALL_DOMAINS_TLDS_ACTION" : getAllDomainsTLDsAction, - "GET_OWNED_ALL_DOMAINS_ACTION" : getOwnedAllDomainsAction, - "CREATE_IMAGE_ACTION" : createImageAction, - "GET_MAIN_ALL_DOMAINS_DOMAIN_ACTION" : getMainAllDomainsDomainAction, - "GET_ALL_REGISTERED_ALL_DOMAINS_ACTION" : getAllRegisteredAllDomainsAction, - "RAYDIUM_CREATE_CPMM_ACTION" : raydiumCreateCpmmAction, - "RAYDIUM_CREATE_AMM_V4_ACTION" : raydiumCreateAmmV4Action, - "CREATE_ORCA_SINGLE_SIDED_WHIRLPOOL_ACTION" : createOrcaSingleSidedWhirlpoolAction, - "LAUNCH_PUMPFUN_TOKEN_ACTION" : launchPumpfunTokenAction, + DEPLOY_TOKEN_ACTION: deployTokenAction, + BALANCE_ACTION: balanceAction, + TRANSFER_ACTION: transferAction, + DEPLOY_COLLECTION_ACTION: deployCollectionAction, + MINT_NFT_ACTION: mintNFTAction, + TRADE_ACTION: tradeAction, + REQUEST_FUNDS_ACTION: requestFundsAction, + RESOLVE_DOMAIN_ACTION: resolveDomainAction, + GET_TOKEN_DATA_ACTION: getTokenDataAction, + GET_TPS_ACTION: getTPSAction, + FETCH_PRICE_ACTION: fetchPriceAction, + STAKE_WITH_JUP_ACTION: stakeWithJupAction, + REGISTER_DOMAIN_ACTION: registerDomainAction, + LEND_ASSET_ACTION: lendAssetAction, + CREATE_GIBWORK_TASK_ACTION: createGibworkTaskAction, + RESOLVE_SOL_DOMAIN_ACTION: resolveSolDomainAction, + PYTH_FETCH_PRICE_ACTION: pythFetchPriceAction, + GET_OWNED_DOMAINS_FOR_TLD_ACTION: getOwnedDomainsForTLDAction, + GET_PRIMARY_DOMAIN_ACTION: getPrimaryDomainAction, + GET_ALL_DOMAINS_TLDS_ACTION: getAllDomainsTLDsAction, + GET_OWNED_ALL_DOMAINS_ACTION: getOwnedAllDomainsAction, + CREATE_IMAGE_ACTION: createImageAction, + GET_MAIN_ALL_DOMAINS_DOMAIN_ACTION: getMainAllDomainsDomainAction, + GET_ALL_REGISTERED_ALL_DOMAINS_ACTION: getAllRegisteredAllDomainsAction, + RAYDIUM_CREATE_CPMM_ACTION: raydiumCreateCpmmAction, + RAYDIUM_CREATE_AMM_V4_ACTION: raydiumCreateAmmV4Action, + CREATE_ORCA_SINGLE_SIDED_WHIRLPOOL_ACTION: + createOrcaSingleSidedWhirlpoolAction, + LAUNCH_PUMPFUN_TOKEN_ACTION: launchPumpfunTokenAction, }; export type { Action, ActionExample, Handler } from "../types/action"; diff --git a/src/agent/index.ts b/src/agent/index.ts index 4acf694..8191a80 100644 --- a/src/agent/index.ts +++ b/src/agent/index.ts @@ -83,24 +83,30 @@ export class SolanaAgentKit { * @deprecated Using openai_api_key directly in constructor is deprecated. * Please use the new constructor with Config object instead: * @example - * const agent = new SolanaAgentKit(privateKey, rpcUrl, { + * const agent = new SolanaAgentKit(privateKey, rpcUrl, { * OPENAI_API_KEY: 'your-key' * }); */ - constructor(private_key: string, rpc_url: string, openai_api_key: string | null); + constructor( + private_key: string, + rpc_url: string, + openai_api_key: string | null, + ); constructor(private_key: string, rpc_url: string, config: Config); constructor( private_key: string, rpc_url: string, configOrKey: Config | string | null, ) { - this.connection = new Connection(rpc_url || "https://api.mainnet-beta.solana.com"); + this.connection = new Connection( + rpc_url || "https://api.mainnet-beta.solana.com", + ); this.wallet = Keypair.fromSecretKey(bs58.decode(private_key)); this.wallet_address = this.wallet.publicKey; // Handle both old and new patterns - if (typeof configOrKey === 'string' || configOrKey === null) { - this.config = { OPENAI_API_KEY: configOrKey || '' }; + if (typeof configOrKey === "string" || configOrKey === null) { + this.config = { OPENAI_API_KEY: configOrKey || "" }; } else { this.config = configOrKey; } @@ -127,7 +133,19 @@ export class SolanaAgentKit { return deploy_collection(this, options); } - async getBalance(token_address?: PublicKey): Promise { + async getBalance(token_address?: PublicKey): Promise< + | number + | { + sol: number; + tokens: Array<{ + tokenAddress: string; + name: string; + symbol: string; + balance: number; + decimals: number; + }>; + } + > { return get_balance(this, token_address); } diff --git a/src/tools/get_balance.ts b/src/tools/get_balance.ts index a1e3736..3e30021 100644 --- a/src/tools/get_balance.ts +++ b/src/tools/get_balance.ts @@ -1,5 +1,7 @@ import { LAMPORTS_PER_SOL, PublicKey } from "@solana/web3.js"; import { SolanaAgentKit } from "../index"; +import { TOKEN_PROGRAM_ID } from "@solana/spl-token"; +import { getTokenMetadata } from "../utils/tokenMetadata"; /** * Get the balance of SOL or an SPL token for the agent's wallet @@ -10,12 +12,51 @@ import { SolanaAgentKit } from "../index"; export async function get_balance( agent: SolanaAgentKit, token_address?: PublicKey, -): Promise { +): Promise< + | number + | { + sol: number; + tokens: Array<{ + tokenAddress: string; + name: string; + symbol: string; + balance: number; + decimals: number; + }>; + } +> { if (!token_address) { - return ( - (await agent.connection.getBalance(agent.wallet_address)) / - LAMPORTS_PER_SOL + const [lamportsBalance, tokenAccountData] = await Promise.all([ + agent.connection.getBalance(agent.wallet_address), + agent.connection.getParsedTokenAccountsByOwner(agent.wallet_address, { + programId: TOKEN_PROGRAM_ID, + }), + ]); + + const removedZeroBalance = tokenAccountData.value.filter( + (v) => v.account.data.parsed.info.tokenAmount.uiAmount !== 0, ); + + const tokenBalances = await Promise.all( + removedZeroBalance.map(async (v) => { + const mint = v.account.data.parsed.info.mint; + const mintInfo = await getTokenMetadata(agent.connection, mint); + return { + tokenAddress: mint, + name: mintInfo.name ?? "", + symbol: mintInfo.symbol ?? "", + balance: v.account.data.parsed.info.tokenAmount.uiAmount as number, + decimals: v.account.data.parsed.info.tokenAmount.decimals as number, + }; + }), + ); + + const solBalance = lamportsBalance / LAMPORTS_PER_SOL; + + return { + sol: solBalance, + tokens: tokenBalances, + }; } const token_account = diff --git a/src/utils/tokenMetadata.ts b/src/utils/tokenMetadata.ts new file mode 100644 index 0000000..514a208 --- /dev/null +++ b/src/utils/tokenMetadata.ts @@ -0,0 +1,83 @@ +import { Connection, PublicKey } from "@solana/web3.js"; + +export async function getTokenMetadata( + connection: Connection, + tokenMint: string, +) { + const METADATA_PROGRAM_ID = new PublicKey( + "metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s", + ); + + const [metadataPDA] = PublicKey.findProgramAddressSync( + [ + Buffer.from("metadata"), + METADATA_PROGRAM_ID.toBuffer(), + new PublicKey(tokenMint).toBuffer(), + ], + METADATA_PROGRAM_ID, + ); + + const metadata = await connection.getAccountInfo(metadataPDA); + if (!metadata?.data) { + throw new Error("Metadata not found"); + } + + let offset = 1 + 32 + 32; // key + update auth + mint + const data = metadata.data; + const decoder = new TextDecoder(); + + // Read variable length strings + const readString = () => { + let nameLength = data[offset]; + + while (nameLength === 0) { + offset++; + nameLength = data[offset]; + if (offset >= data.length) { + return null; + } + } + + offset++; + const name = decoder + .decode(data.slice(offset, offset + nameLength)) + // @eslint-disable-next-line no-control-regex + .replace(new RegExp(String.fromCharCode(0), "g"), ""); + offset += nameLength; + return name; + }; + + const name = readString(); + const symbol = readString(); + const uri = readString(); + + // Read remaining data + const sellerFeeBasisPoints = data.readUInt16LE(offset); + offset += 2; + + let creators: + | { address: PublicKey; verified: boolean; share: number }[] + | null = null; + if (data[offset] === 1) { + offset++; + const numCreators = data[offset]; + offset++; + creators = [...Array(numCreators)].map(() => { + const creator = { + address: new PublicKey(data.slice(offset, offset + 32)), + verified: data[offset + 32] === 1, + share: data[offset + 33], + }; + offset += 34; + return creator; + }); + } + + return { + name, + symbol, + uri, + sellerFeeBasisPoints, + creators, + }; +} diff --git a/test/index.ts b/test/index.ts index 2a3312b..00f9976 100644 --- a/test/index.ts +++ b/test/index.ts @@ -1,4 +1,4 @@ -import { SolanaAgentKit , ACTIONS} from "../src"; +import { SolanaAgentKit, ACTIONS } from "../src"; import { createSolanaTools } from "../src/langchain"; import { HumanMessage } from "@langchain/core/messages"; import { MemorySaver } from "@langchain/langgraph"; From ad23d8c92b3f8cbc3feffe9d7d3b92cf8b26e780 Mon Sep 17 00:00:00 2001 From: michaelessiet Date: Fri, 10 Jan 2025 17:08:02 +0100 Subject: [PATCH 2/7] catch up to main --- package.json | 157 +++++++++++++++---------------- pnpm-lock.yaml | 248 +++++++++++++++++++++++++------------------------ 2 files changed, 208 insertions(+), 197 deletions(-) diff --git a/package.json b/package.json index e64c039..63f82e7 100644 --- a/package.json +++ b/package.json @@ -1,81 +1,82 @@ { - "name": "solana-agent-kit", - "version": "1.3.7", - "description": "connect any ai agents to solana protocols", - "main": "dist/index.js", - "types": "dist/index.d.ts", - "scripts": { - "build": "tsc", - "docs": "typedoc src --out docs", - "test": "ts-node test/index.ts", - "test:vercel-ai": "ts-node test/agent_sdks/vercel_ai.ts", - "generate": "ts-node src/utils/keypair.ts", - "lint": "eslint . --ext .ts", - "lint:fix": "eslint . --ext .ts --fix", - "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", - "prepare": "husky" - }, - "engines": { - "node": ">=22.0.0", - "pnpm": ">=8.0.0" - }, - "keywords": [], - "author": "sendaifun", - "license": "Apache-2.0", - "dependencies": { - "@3land/listings-sdk": "^0.0.4", - "@ai-sdk/openai": "^1.0.11", - "@bonfida/spl-name-service": "^3.0.7", - "@cks-systems/manifest-sdk": "0.1.59", - "@coral-xyz/anchor": "0.29", - "@langchain/core": "^0.3.26", - "@langchain/groq": "^0.1.2", - "@langchain/langgraph": "^0.2.36", - "@langchain/openai": "^0.3.16", - "@lightprotocol/compressed-token": "^0.17.1", - "@lightprotocol/stateless.js": "^0.17.1", - "@metaplex-foundation/mpl-core": "^1.1.1", - "@metaplex-foundation/mpl-token-metadata": "^3.3.0", - "@metaplex-foundation/mpl-toolbox": "^0.9.4", - "@metaplex-foundation/umi": "^0.9.2", - "@metaplex-foundation/umi-bundle-defaults": "^0.9.2", - "@metaplex-foundation/umi-web3js-adapters": "^0.9.2", - "@onsol/tldparser": "^0.6.7", - "@orca-so/common-sdk": "0.6.4", - "@orca-so/whirlpools-sdk": "^0.13.12", - "@pythnetwork/hermes-client": "^1.3.0", - "@raydium-io/raydium-sdk-v2": "0.1.95-alpha", - "@solana/spl-token": "^0.4.9", - "@solana/web3.js": "^1.98.0", - "@tensor-oss/tensorswap-sdk": "^4.5.0", + "name": "solana-agent-kit", + "version": "1.3.7", + "description": "connect any ai agents to solana protocols", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "scripts": { + "build": "tsc", + "docs": "typedoc src --out docs", + "test": "ts-node test/index.ts", + "test:vercel-ai": "ts-node test/agent_sdks/vercel_ai.ts", + "generate": "ts-node src/utils/keypair.ts", + "lint": "eslint . --ext .ts", + "lint:fix": "eslint . --ext .ts --fix", + "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", + "prepare": "husky" + }, + "engines": { + "node": ">=22.0.0", + "pnpm": ">=8.0.0" + }, + "keywords": [], + "author": "sendaifun", + "license": "Apache-2.0", + "dependencies": { + "@3land/listings-sdk": "^0.0.4", + "@ai-sdk/openai": "^1.0.11", + "@bonfida/spl-name-service": "^3.0.7", + "@cks-systems/manifest-sdk": "0.1.59", + "@coral-xyz/anchor": "0.29", + "@langchain/core": "^0.3.26", + "@langchain/groq": "^0.1.2", + "@langchain/langgraph": "^0.2.36", + "@langchain/openai": "^0.3.16", + "@lightprotocol/compressed-token": "^0.17.1", + "@lightprotocol/stateless.js": "^0.17.1", + "@metaplex-foundation/mpl-core": "^1.1.1", + "@metaplex-foundation/mpl-token-metadata": "^3.3.0", + "@metaplex-foundation/mpl-toolbox": "^0.9.4", + "@metaplex-foundation/umi": "^0.9.2", + "@metaplex-foundation/umi-bundle-defaults": "^0.9.2", + "@metaplex-foundation/umi-web3js-adapters": "^0.9.2", + "@onsol/tldparser": "^0.6.7", + "@orca-so/common-sdk": "0.6.4", + "@orca-so/whirlpools-sdk": "^0.13.12", + "@pythnetwork/hermes-client": "^1.3.0", + "@raydium-io/raydium-sdk-v2": "0.1.95-alpha", + "@solana/spl-token": "^0.4.9", + "@solana/web3.js": "^1.98.0", + "@tensor-oss/tensorswap-sdk": "^4.5.0", "@sqds/multisig": "^2.1.3", - "@tiplink/api": "^0.3.1", - "ai": "^4.0.22", - "bn.js": "^5.2.1", - "bs58": "^6.0.0", - "chai": "^5.1.2", - "decimal.js": "^10.4.3", - "dotenv": "^16.4.7", - "flash-sdk": "^2.24.3", - "form-data": "^4.0.1", - "langchain": "^0.3.8", - "openai": "^4.77.0", - "typedoc": "^0.27.6", - "zod": "^3.24.1" - }, - "devDependencies": { - "@types/bn.js": "^5.1.6", - "@types/chai": "^5.0.1", - "@types/node": "^22.10.2", - "@typescript-eslint/eslint-plugin": "^8.18.2", - "@typescript-eslint/parser": "^8.18.2", - "eslint": "^8.56.0", - "eslint-config-prettier": "^9.1.0", - "eslint-plugin-prettier": "^5.2.1", - "husky": "^9.1.7", - "lint-staged": "^15.3.0", - "prettier": "^3.4.2", - "ts-node": "^10.9.2", - "typescript": "^5.7.2" - } + "@tiplink/api": "^0.3.1", + "ai": "^4.0.22", + "bn.js": "^5.2.1", + "bs58": "^6.0.0", + "chai": "^5.1.2", + "decimal.js": "^10.4.3", + "dotenv": "^16.4.7", + "flash-sdk": "^2.24.3", + "form-data": "^4.0.1", + "langchain": "^0.3.8", + "openai": "^4.77.0", + "typedoc": "^0.27.6", + "zod": "^3.24.1" + }, + "devDependencies": { + "@types/bn.js": "^5.1.6", + "@types/chai": "^5.0.1", + "@types/node": "^22.10.2", + "@typescript-eslint/eslint-plugin": "^8.18.2", + "@typescript-eslint/parser": "^8.18.2", + "eslint": "^8.56.0", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-prettier": "^5.2.1", + "husky": "^9.1.7", + "lint-staged": "^15.3.0", + "prettier": "^3.4.2", + "ts-node": "^10.9.2", + "typescript": "^5.7.2" + }, + "packageManager": "pnpm@9.15.3" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b98d918..2ef0c43 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -10,10 +10,10 @@ importers: dependencies: '@3land/listings-sdk': specifier: ^0.0.4 - version: 0.0.4(@types/node@22.10.5)(arweave@1.15.5)(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.2)(utf-8-validate@5.0.10) + version: 0.0.4(@types/node@22.10.5)(arweave@1.15.5)(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.2)(utf-8-validate@5.0.10) '@ai-sdk/openai': specifier: ^1.0.11 - version: 1.0.13(zod@3.24.1) + version: 1.0.11(zod@3.24.1) '@bonfida/spl-name-service': specifier: ^3.0.7 version: 3.0.7(@solana/web3.js@1.98.0(bufferutil@4.0.8)(utf-8-validate@5.0.10))(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.2)(utf-8-validate@5.0.10) @@ -109,7 +109,7 @@ importers: version: 16.4.7 flash-sdk: specifier: ^2.24.3 - version: 2.24.3(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.2)(utf-8-validate@5.0.10) + version: 2.24.3(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.2)(utf-8-validate@5.0.10) form-data: specifier: ^4.0.1 version: 4.0.1 @@ -134,7 +134,7 @@ importers: version: 5.0.1 '@types/node': specifier: ^22.10.2 - version: 22.10.2 + version: 22.10.5 '@typescript-eslint/eslint-plugin': specifier: ^8.18.2 version: 8.19.0(@typescript-eslint/parser@8.19.0(eslint@8.57.1)(typescript@5.7.2))(eslint@8.57.1)(typescript@5.7.2) @@ -161,7 +161,7 @@ importers: version: 3.4.2 ts-node: specifier: ^10.9.2 - version: 10.9.2(@types/node@22.10.2)(typescript@5.7.2) + version: 10.9.2(@types/node@22.10.5)(typescript@5.7.2) typescript: specifier: ^5.7.2 version: 5.7.2 @@ -855,6 +855,9 @@ packages: '@shikijs/vscode-textmate@10.0.1': resolution: {integrity: sha512-fTIQwLF+Qhuws31iw7Ncl1R3HUDtGwIipiJ9iU+UsDUwMhegFcQKQHd51nZjb7CArq0MvON8rbgCGQYWHUKAdg==} + '@shikijs/vscode-textmate@9.3.1': + resolution: {integrity: sha512-79QfK1393x9Ho60QFyLti+QfdJzRQCVLFb97kOIV7Eo9vQU/roINgk7m24uv0a7AUvN//RDH36FLjjK48v0s9g==} + '@sindresorhus/is@4.6.0': resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==} engines: {node: '>=10'} @@ -1036,6 +1039,10 @@ packages: '@solana/web3.js@1.98.0': resolution: {integrity: sha512-nz3Q5OeyGFpFCR+erX2f6JPt3sKhzhYcSycBCSPkWjzSVDh/Rr1FqTVMRe58FKO16/ivTUcuJjeS5MyBvpkbzA==} + '@sqds/multisig@2.1.3': + resolution: {integrity: sha512-WOiL7La+RSiJsz7jVO85yhSiiSvNMUthiWuLPeWVOoD6IYa34BEAzanF1RdXRWGglSbRFYCTkyr+Ay1WmXmSRQ==} + engines: {node: '>=14'} + '@supercharge/promise-pool@3.2.0': resolution: {integrity: sha512-pj0cAALblTZBPtMltWOlZTQSLT07jIaFNeM8TWoJD1cQMgDB9mcMlVMoetiB35OzNJpqQ2b+QEtwiR9f20mADg==} engines: {node: '>=8'} @@ -2121,12 +2128,8 @@ packages: resolution: {integrity: sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==} engines: {node: '>=18'} - get-intrinsic@1.2.7: - resolution: {integrity: sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==} - engines: {node: '>= 0.4'} - - get-proto@1.0.1: - resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + get-intrinsic@1.2.6: + resolution: {integrity: sha512-qxsEs+9A+u85HhllWJJFicJfPDhRmjzoYdl64aMWW9yRIJmSyxdn8IEkuIM530/7T+lv0TIHd8L6Q/ra0tEoeA==} engines: {node: '>= 0.4'} get-stream@5.2.0: @@ -2284,6 +2287,9 @@ packages: resolution: {integrity: sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg==} engines: {node: '>=12.0.0'} + invariant@2.2.4: + resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==} + ipaddr.js@1.9.1: resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} engines: {node: '>= 0.10'} @@ -2347,10 +2353,6 @@ packages: resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} engines: {node: '>=8'} - is-regex@1.2.1: - resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} - engines: {node: '>= 0.4'} - is-retry-allowed@2.2.0: resolution: {integrity: sha512-XVm7LOeLpTW4jV19QSH38vkswxoLud8sQ57YwJVTPWdiaI9I8keEhGFpBlslyVsgdQy4Opg8QOLb8YRgsyZiQg==} engines: {node: '>=10'} @@ -3060,8 +3062,8 @@ packages: regex-utilities@2.3.0: resolution: {integrity: sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==} - regex@5.1.1: - resolution: {integrity: sha512-dN5I359AVGPnwzJm2jN1k0W9LPZ+ePvoOeVMMfqIMFz53sSwXkxaJoxr50ptnsC771lK95BnTrVSZxq0b9yCGw==} + regex@5.0.2: + resolution: {integrity: sha512-/pczGbKIQgfTMRV0XjABvc5RzLqQmwqxLHdQao2RTXPk+pmTXB2P0IaUHYdYyk412YLwUIkaeMd5T+RzVgTqnQ==} resolve-alpn@1.2.1: resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==} @@ -3661,14 +3663,14 @@ packages: snapshots: - '@3land/listings-sdk@0.0.4(@types/node@22.10.5)(arweave@1.15.5)(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.2)(utf-8-validate@5.0.10)': + '@3land/listings-sdk@0.0.4(@types/node@22.10.5)(arweave@1.15.5)(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.2)(utf-8-validate@5.0.10)': dependencies: - '@coral-xyz/borsh': 0.30.1(@solana/web3.js@1.98.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - '@irys/sdk': 0.2.11(arweave@1.15.5)(bufferutil@4.0.9)(utf-8-validate@5.0.10) + '@coral-xyz/borsh': 0.30.1(@solana/web3.js@1.98.0(bufferutil@4.0.8)(utf-8-validate@5.0.10)) + '@irys/sdk': 0.2.11(arweave@1.15.5)(bufferutil@4.0.8)(utf-8-validate@5.0.10) '@metaplex-foundation/beet': 0.7.2 - '@metaplex-foundation/mpl-token-metadata': 2.13.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.2)(utf-8-validate@5.0.10) - '@project-serum/anchor': 0.26.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) - '@solana/web3.js': 1.98.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) + '@metaplex-foundation/mpl-token-metadata': 2.13.0(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.2)(utf-8-validate@5.0.10) + '@project-serum/anchor': 0.26.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) + '@solana/web3.js': 1.98.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) bn: 1.0.5 bn.js: 5.2.1 bs58: 6.0.0 @@ -3743,8 +3745,8 @@ snapshots: dependencies: '@aptos-labs/aptos-cli': 1.0.2 '@aptos-labs/aptos-client': 0.1.1 - '@noble/curves': 1.8.0 - '@noble/hashes': 1.7.0 + '@noble/curves': 1.7.0 + '@noble/hashes': 1.6.1 '@scure/bip32': 1.4.0 '@scure/bip39': 1.3.0 eventemitter3: 5.0.1 @@ -3838,16 +3840,16 @@ snapshots: - encoding - utf-8-validate - '@coral-xyz/anchor@0.27.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)': + '@coral-xyz/anchor@0.27.0(bufferutil@4.0.8)(utf-8-validate@5.0.10)': dependencies: - '@coral-xyz/borsh': 0.27.0(@solana/web3.js@1.98.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - '@solana/web3.js': 1.98.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) + '@coral-xyz/borsh': 0.27.0(@solana/web3.js@1.98.0(bufferutil@4.0.8)(utf-8-validate@5.0.10)) + '@solana/web3.js': 1.98.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) base64-js: 1.5.1 bn.js: 5.2.1 bs58: 4.0.1 buffer-layout: 1.2.2 camelcase: 6.3.0 - cross-fetch: 3.2.0 + cross-fetch: 3.1.8 crypto-hash: 1.3.0 eventemitter3: 4.0.7 js-sha256: 0.9.0 @@ -3860,7 +3862,7 @@ snapshots: - encoding - utf-8-validate - '@coral-xyz/anchor@0.29.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)': + '@coral-xyz/anchor@0.29.0(bufferutil@4.0.8)(utf-8-validate@5.0.10)': dependencies: '@coral-xyz/borsh': 0.29.0(@solana/web3.js@1.98.0(bufferutil@4.0.8)(utf-8-validate@5.0.10)) '@noble/hashes': 1.6.1 @@ -3887,27 +3889,27 @@ snapshots: bn.js: 5.2.1 buffer-layout: 1.2.2 - '@coral-xyz/borsh@0.27.0(@solana/web3.js@1.98.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))': + '@coral-xyz/borsh@0.27.0(@solana/web3.js@1.98.0(bufferutil@4.0.8)(utf-8-validate@5.0.10))': dependencies: - '@solana/web3.js': 1.98.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) + '@solana/web3.js': 1.98.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) bn.js: 5.2.1 buffer-layout: 1.2.2 - '@coral-xyz/borsh@0.28.0(@solana/web3.js@1.95.8(bufferutil@4.0.9)(utf-8-validate@5.0.10))': + '@coral-xyz/borsh@0.28.0(@solana/web3.js@1.95.8(bufferutil@4.0.8)(utf-8-validate@5.0.10))': dependencies: - '@solana/web3.js': 1.95.8(bufferutil@4.0.9)(utf-8-validate@5.0.10) + '@solana/web3.js': 1.95.8(bufferutil@4.0.8)(utf-8-validate@5.0.10) bn.js: 5.2.1 buffer-layout: 1.2.2 - '@coral-xyz/borsh@0.29.0(@solana/web3.js@1.98.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))': + '@coral-xyz/borsh@0.29.0(@solana/web3.js@1.98.0(bufferutil@4.0.8)(utf-8-validate@5.0.10))': dependencies: - '@solana/web3.js': 1.98.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) + '@solana/web3.js': 1.98.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) bn.js: 5.2.1 buffer-layout: 1.2.2 - '@coral-xyz/borsh@0.30.1(@solana/web3.js@1.98.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))': + '@coral-xyz/borsh@0.30.1(@solana/web3.js@1.98.0(bufferutil@4.0.8)(utf-8-validate@5.0.10))': dependencies: - '@solana/web3.js': 1.98.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) + '@solana/web3.js': 1.98.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) bn.js: 5.2.1 buffer-layout: 1.2.2 @@ -4122,7 +4124,7 @@ snapshots: dependencies: '@ethersproject/logger': 5.7.0 - '@ethersproject/providers@5.7.2(bufferutil@4.0.9)(utf-8-validate@5.0.10)': + '@ethersproject/providers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10)': dependencies: '@ethersproject/abstract-provider': 5.7.0 '@ethersproject/abstract-signer': 5.7.0 @@ -4143,7 +4145,7 @@ snapshots: '@ethersproject/transactions': 5.7.0 '@ethersproject/web': 5.7.1 bech32: 1.1.4 - ws: 7.4.6(bufferutil@4.0.9)(utf-8-validate@5.0.10) + ws: 7.4.6(bufferutil@4.0.8)(utf-8-validate@5.0.10) transitivePeerDependencies: - bufferutil - utf-8-validate @@ -4271,22 +4273,22 @@ snapshots: transitivePeerDependencies: - debug - '@irys/sdk@0.2.11(arweave@1.15.5)(bufferutil@4.0.9)(utf-8-validate@5.0.10)': + '@irys/sdk@0.2.11(arweave@1.15.5)(bufferutil@4.0.8)(utf-8-validate@5.0.10)': dependencies: '@aptos-labs/ts-sdk': 1.33.1 '@ethersproject/bignumber': 5.7.0 '@ethersproject/contracts': 5.7.0 - '@ethersproject/providers': 5.7.2(bufferutil@4.0.9)(utf-8-validate@5.0.10) + '@ethersproject/providers': 5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10) '@ethersproject/wallet': 5.7.0 '@irys/query': 0.0.8 '@near-js/crypto': 0.0.3 '@near-js/keystores-browser': 0.0.3 '@near-js/providers': 0.0.4 '@near-js/transactions': 0.1.1 - '@solana/web3.js': 1.98.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) + '@solana/web3.js': 1.98.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) '@supercharge/promise-pool': 3.2.0 algosdk: 1.24.1 - arbundles: 0.11.2(arweave@1.15.5)(bufferutil@4.0.9)(utf-8-validate@5.0.10) + arbundles: 0.11.2(arweave@1.15.5)(bufferutil@4.0.8)(utf-8-validate@5.0.10) async-retry: 1.3.3 axios: 1.7.9 base64url: 3.0.1 @@ -4778,7 +4780,7 @@ snapshots: '@noble/curves@1.7.0': dependencies: - '@noble/hashes': 1.7.0 + '@noble/hashes': 1.6.0 '@noble/ed25519@1.7.3': {} @@ -4839,16 +4841,16 @@ snapshots: '@pkgr/core@0.1.1': {} - '@project-serum/anchor@0.26.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)': + '@project-serum/anchor@0.26.0(bufferutil@4.0.8)(utf-8-validate@5.0.10)': dependencies: - '@coral-xyz/borsh': 0.26.0(@solana/web3.js@1.98.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - '@solana/web3.js': 1.98.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) + '@coral-xyz/borsh': 0.26.0(@solana/web3.js@1.98.0(bufferutil@4.0.8)(utf-8-validate@5.0.10)) + '@solana/web3.js': 1.98.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) base64-js: 1.5.1 bn.js: 5.2.1 bs58: 4.0.1 buffer-layout: 1.2.2 camelcase: 6.3.0 - cross-fetch: 3.2.0 + cross-fetch: 3.1.8 crypto-hash: 1.3.0 eventemitter3: 4.0.7 js-sha256: 0.9.0 @@ -4861,11 +4863,11 @@ snapshots: - encoding - utf-8-validate - '@pythnetwork/client@2.22.0(@solana/web3.js@1.95.8(bufferutil@4.0.9)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(utf-8-validate@5.0.10)': + '@pythnetwork/client@2.22.0(@solana/web3.js@1.95.8(bufferutil@4.0.8)(utf-8-validate@5.0.10))(bufferutil@4.0.8)(utf-8-validate@5.0.10)': dependencies: - '@coral-xyz/anchor': 0.29.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) - '@coral-xyz/borsh': 0.28.0(@solana/web3.js@1.95.8(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - '@solana/web3.js': 1.95.8(bufferutil@4.0.9)(utf-8-validate@5.0.10) + '@coral-xyz/anchor': 0.29.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) + '@coral-xyz/borsh': 0.28.0(@solana/web3.js@1.95.8(bufferutil@4.0.8)(utf-8-validate@5.0.10)) + '@solana/web3.js': 1.95.8(bufferutil@4.0.8)(utf-8-validate@5.0.10) buffer: 6.0.3 transitivePeerDependencies: - bufferutil @@ -4880,15 +4882,15 @@ snapshots: transitivePeerDependencies: - axios - '@pythnetwork/price-service-client@1.9.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)': + '@pythnetwork/price-service-client@1.9.0(bufferutil@4.0.8)(utf-8-validate@5.0.10)': dependencies: '@pythnetwork/price-service-sdk': 1.8.0 '@types/ws': 8.5.13 axios: 1.7.9 axios-retry: 3.9.1 - isomorphic-ws: 4.0.1(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + isomorphic-ws: 4.0.1(ws@8.18.0(bufferutil@4.0.8)(utf-8-validate@5.0.10)) ts-log: 2.2.7 - ws: 8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) + ws: 8.18.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) transitivePeerDependencies: - bufferutil - debug @@ -4906,7 +4908,7 @@ snapshots: '@randlabs/communication-bridge': 1.0.1 optional: true - '@raydium-io/raydium-sdk-v2@0.1.95-alpha(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.2)(utf-8-validate@5.0.10)': + '@raydium-io/raydium-sdk-v2@0.1.95-alpha(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.2)(utf-8-validate@5.0.10)': dependencies: '@solana/buffer-layout': 4.0.1 '@solana/spl-token': 0.4.9(@solana/web3.js@1.98.0(bufferutil@4.0.8)(utf-8-validate@5.0.10))(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.2)(utf-8-validate@5.0.10) @@ -4997,9 +4999,11 @@ snapshots: '@shikijs/vscode-textmate@10.0.1': {} + '@shikijs/vscode-textmate@9.3.1': {} + '@sindresorhus/is@4.6.0': {} - '@solana/buffer-layout-utils@0.2.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)': + '@solana/buffer-layout-utils@0.2.0(bufferutil@4.0.8)(utf-8-validate@5.0.10)': dependencies: '@solana/buffer-layout': 4.0.1 '@solana/web3.js': 1.98.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) @@ -5266,15 +5270,15 @@ snapshots: - fastestsmallesttextencoderdecoder - typescript - '@solana/spl-token-metadata@0.1.6(@solana/web3.js@1.95.8(bufferutil@4.0.9)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.2)': + '@solana/spl-token-metadata@0.1.6(@solana/web3.js@1.95.8(bufferutil@4.0.8)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.2)': dependencies: '@solana/codecs': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.2) - '@solana/web3.js': 1.95.8(bufferutil@4.0.9)(utf-8-validate@5.0.10) + '@solana/web3.js': 1.95.8(bufferutil@4.0.8)(utf-8-validate@5.0.10) transitivePeerDependencies: - fastestsmallesttextencoderdecoder - typescript - '@solana/spl-token-metadata@0.1.6(@solana/web3.js@1.98.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@4.9.5)': + '@solana/spl-token-metadata@0.1.6(@solana/web3.js@1.98.0(bufferutil@4.0.8)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@4.9.5)': dependencies: '@solana/codecs': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@4.9.5) '@solana/web3.js': 1.98.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) @@ -5303,12 +5307,12 @@ snapshots: - encoding - utf-8-validate - '@solana/spl-token@0.3.11(@solana/web3.js@1.95.8(bufferutil@4.0.9)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.2)(utf-8-validate@5.0.10)': + '@solana/spl-token@0.3.11(@solana/web3.js@1.95.8(bufferutil@4.0.8)(utf-8-validate@5.0.10))(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.2)(utf-8-validate@5.0.10)': dependencies: '@solana/buffer-layout': 4.0.1 - '@solana/buffer-layout-utils': 0.2.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) - '@solana/spl-token-metadata': 0.1.6(@solana/web3.js@1.95.8(bufferutil@4.0.9)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.2) - '@solana/web3.js': 1.95.8(bufferutil@4.0.9)(utf-8-validate@5.0.10) + '@solana/buffer-layout-utils': 0.2.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) + '@solana/spl-token-metadata': 0.1.6(@solana/web3.js@1.95.8(bufferutil@4.0.8)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.2) + '@solana/web3.js': 1.95.8(bufferutil@4.0.8)(utf-8-validate@5.0.10) buffer: 6.0.3 transitivePeerDependencies: - bufferutil @@ -5317,7 +5321,7 @@ snapshots: - typescript - utf-8-validate - '@solana/spl-token@0.3.11(@solana/web3.js@1.98.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@4.9.5)(utf-8-validate@5.0.10)': + '@solana/spl-token@0.3.11(@solana/web3.js@1.98.0(bufferutil@4.0.8)(utf-8-validate@5.0.10))(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@4.9.5)(utf-8-validate@5.0.10)': dependencies: '@solana/buffer-layout': 4.0.1 '@solana/buffer-layout-utils': 0.2.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) @@ -5400,7 +5404,7 @@ snapshots: '@noble/curves': 1.7.0 '@noble/hashes': 1.6.1 '@solana/buffer-layout': 4.0.1 - agentkeepalive: 4.5.0 + agentkeepalive: 4.6.0 bigint-buffer: 1.1.5 bn.js: 5.2.1 borsh: 0.7.0 @@ -5416,11 +5420,11 @@ snapshots: - encoding - utf-8-validate - '@solana/web3.js@1.95.8(bufferutil@4.0.9)(utf-8-validate@5.0.10)': + '@solana/web3.js@1.95.8(bufferutil@4.0.8)(utf-8-validate@5.0.10)': dependencies: '@babel/runtime': 7.26.0 - '@noble/curves': 1.8.0 - '@noble/hashes': 1.7.0 + '@noble/curves': 1.7.0 + '@noble/hashes': 1.6.1 '@solana/buffer-layout': 4.0.1 agentkeepalive: 4.6.0 bigint-buffer: 1.1.5 @@ -5429,7 +5433,7 @@ snapshots: bs58: 4.0.1 buffer: 6.0.3 fast-stable-stringify: 1.0.0 - jayson: 4.1.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) + jayson: 4.1.3(bufferutil@4.0.8)(utf-8-validate@5.0.10) node-fetch: 2.7.0 rpc-websockets: 9.0.4 superstruct: 2.0.2 @@ -5438,13 +5442,13 @@ snapshots: - encoding - utf-8-validate - '@solana/web3.js@1.98.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)': + '@solana/web3.js@1.98.0(bufferutil@4.0.8)(utf-8-validate@5.0.10)': dependencies: '@babel/runtime': 7.26.0 '@noble/curves': 1.7.0 '@noble/hashes': 1.6.1 '@solana/buffer-layout': 4.0.1 - agentkeepalive: 4.5.0 + agentkeepalive: 4.6.0 bigint-buffer: 1.1.5 bn.js: 5.2.1 borsh: 0.7.0 @@ -5460,6 +5464,26 @@ snapshots: - encoding - utf-8-validate + '@sqds/multisig@2.1.3(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.2)(utf-8-validate@5.0.10)': + dependencies: + '@metaplex-foundation/beet': 0.7.1 + '@metaplex-foundation/beet-solana': 0.4.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) + '@metaplex-foundation/cusper': 0.0.2 + '@solana/spl-token': 0.3.11(@solana/web3.js@1.98.0(bufferutil@4.0.8)(utf-8-validate@5.0.10))(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.2)(utf-8-validate@5.0.10) + '@solana/web3.js': 1.98.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) + '@types/bn.js': 5.1.6 + assert: 2.1.0 + bn.js: 5.2.1 + buffer: 6.0.3 + invariant: 2.2.4 + transitivePeerDependencies: + - bufferutil + - encoding + - fastestsmallesttextencoderdecoder + - supports-color + - typescript + - utf-8-validate + '@supercharge/promise-pool@3.2.0': {} '@swc/helpers@0.5.15': @@ -5470,7 +5494,7 @@ snapshots: dependencies: defer-to-connect: 2.0.1 - '@tensor-hq/tensor-common@8.3.1(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.2)(utf-8-validate@5.0.10)': + '@tensor-hq/tensor-common@8.3.1(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.2)(utf-8-validate@5.0.10)': dependencies: '@coral-xyz/anchor': 0.26.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) '@metaplex-foundation/mpl-auction-house': 2.5.1(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.2)(utf-8-validate@5.0.10) @@ -5550,7 +5574,7 @@ snapshots: '@types/bn.js@5.1.6': dependencies: - '@types/node': 22.10.2 + '@types/node': 22.10.5 '@types/body-parser@1.19.5': dependencies: @@ -5570,7 +5594,7 @@ snapshots: '@types/connect@3.4.38': dependencies: - '@types/node': 22.10.2 + '@types/node': 22.10.5 '@types/deep-eql@4.0.2': {} @@ -5580,7 +5604,7 @@ snapshots: '@types/express-serve-static-core@4.19.6': dependencies: - '@types/node': 22.10.2 + '@types/node': 22.10.5 '@types/qs': 6.9.17 '@types/range-parser': 1.2.7 '@types/send': 0.17.4 @@ -5614,7 +5638,7 @@ snapshots: '@types/node-fetch@2.6.12': dependencies: - '@types/node': 22.10.2 + '@types/node': 22.10.5 form-data: 4.0.1 '@types/node@11.11.6': {} @@ -5652,12 +5676,12 @@ snapshots: '@types/send@0.17.4': dependencies: '@types/mime': 1.3.5 - '@types/node': 22.10.2 + '@types/node': 22.10.5 '@types/serve-static@1.15.7': dependencies: '@types/http-errors': 2.0.4 - '@types/node': 22.10.2 + '@types/node': 22.10.5 '@types/send': 0.17.4 '@types/unist@3.0.3': {} @@ -5668,11 +5692,11 @@ snapshots: '@types/ws@7.4.7': dependencies: - '@types/node': 22.10.2 + '@types/node': 22.10.5 '@types/ws@8.5.13': dependencies: - '@types/node': 22.10.2 + '@types/node': 22.10.5 '@typescript-eslint/eslint-plugin@8.19.0(@typescript-eslint/parser@8.19.0(eslint@8.57.1)(typescript@5.7.2))(eslint@8.57.1)(typescript@5.7.2)': dependencies: @@ -5814,7 +5838,7 @@ snapshots: dependencies: algo-msgpack-with-bigint: 2.1.1 buffer: 6.0.3 - cross-fetch: 3.2.0 + cross-fetch: 3.1.8 hi-base32: 0.5.1 js-sha256: 0.9.0 js-sha3: 0.8.0 @@ -5847,11 +5871,11 @@ snapshots: ansicolors@0.3.2: {} - arbundles@0.11.2(arweave@1.15.5)(bufferutil@4.0.9)(utf-8-validate@5.0.10): + arbundles@0.11.2(arweave@1.15.5)(bufferutil@4.0.8)(utf-8-validate@5.0.10): dependencies: '@ethersproject/bytes': 5.7.0 '@ethersproject/hash': 5.7.0 - '@ethersproject/providers': 5.7.2(bufferutil@4.0.9)(utf-8-validate@5.0.10) + '@ethersproject/providers': 5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10) '@ethersproject/signing-key': 5.7.0 '@ethersproject/transactions': 5.7.0 '@ethersproject/wallet': 5.7.0 @@ -5901,7 +5925,7 @@ snapshots: asn1.js@5.4.1: dependencies: - bn.js: 4.11.6 + bn.js: 4.12.1 inherits: 2.0.4 minimalistic-assert: 1.0.1 safer-buffer: 2.1.2 @@ -6715,13 +6739,13 @@ snapshots: locate-path: 6.0.0 path-exists: 4.0.0 - flash-sdk@2.24.3(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.2)(utf-8-validate@5.0.10): + flash-sdk@2.24.3(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.2)(utf-8-validate@5.0.10): dependencies: - '@coral-xyz/anchor': 0.27.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) - '@pythnetwork/client': 2.22.0(@solana/web3.js@1.95.8(bufferutil@4.0.9)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(utf-8-validate@5.0.10) - '@pythnetwork/price-service-client': 1.9.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) - '@solana/spl-token': 0.3.11(@solana/web3.js@1.95.8(bufferutil@4.0.9)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.2)(utf-8-validate@5.0.10) - '@solana/web3.js': 1.95.8(bufferutil@4.0.9)(utf-8-validate@5.0.10) + '@coral-xyz/anchor': 0.27.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) + '@pythnetwork/client': 2.22.0(@solana/web3.js@1.95.8(bufferutil@4.0.8)(utf-8-validate@5.0.10))(bufferutil@4.0.8)(utf-8-validate@5.0.10) + '@pythnetwork/price-service-client': 1.9.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) + '@solana/spl-token': 0.3.11(@solana/web3.js@1.95.8(bufferutil@4.0.8)(utf-8-validate@5.0.10))(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.2)(utf-8-validate@5.0.10) + '@solana/web3.js': 1.95.8(bufferutil@4.0.8)(utf-8-validate@5.0.10) '@types/node': 20.17.11 bignumber.js: 9.1.2 bs58: 5.0.0 @@ -6809,11 +6833,6 @@ snapshots: hasown: 2.0.2 math-intrinsics: 1.1.0 - get-proto@1.0.1: - dependencies: - dunder-proto: 1.0.1 - es-object-atoms: 1.0.0 - get-stream@5.2.0: dependencies: pump: 3.0.2 @@ -6892,7 +6911,7 @@ snapshots: '@types/node': 18.19.68 '@types/node-fetch': 2.6.12 abort-controller: 3.0.0 - agentkeepalive: 4.5.0 + agentkeepalive: 4.6.0 form-data-encoder: 1.7.2 formdata-node: 4.4.1 node-fetch: 2.7.0 @@ -7026,6 +7045,10 @@ snapshots: through: 2.3.8 wrap-ansi: 6.2.0 + invariant@2.2.4: + dependencies: + loose-envify: 1.4.0 + ipaddr.js@1.9.1: {} ipaddr.js@2.2.0: {} @@ -7070,13 +7093,6 @@ snapshots: is-path-inside@3.0.3: {} - is-regex@1.2.1: - dependencies: - call-bound: 1.0.3 - gopd: 1.2.0 - has-tostringtag: 1.0.2 - hasown: 2.0.2 - is-retry-allowed@2.2.0: {} is-stream@3.0.0: {} @@ -7093,11 +7109,11 @@ snapshots: isomorphic-ws@4.0.1(ws@7.5.10(bufferutil@4.0.8)(utf-8-validate@5.0.10)): dependencies: - ws: 7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10) + ws: 7.5.10(bufferutil@4.0.8)(utf-8-validate@5.0.10) - isomorphic-ws@4.0.1(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)): + isomorphic-ws@4.0.1(ws@8.18.0(bufferutil@4.0.8)(utf-8-validate@5.0.10)): dependencies: - ws: 8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) + ws: 8.18.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) jackspeak@3.4.3: dependencies: @@ -7553,7 +7569,7 @@ snapshots: '@types/node': 18.19.68 '@types/node-fetch': 2.6.12 abort-controller: 3.0.0 - agentkeepalive: 4.5.0 + agentkeepalive: 4.6.0 form-data-encoder: 1.7.2 formdata-node: 4.4.1 node-fetch: 2.7.0 @@ -7817,12 +7833,6 @@ snapshots: safe-buffer@5.2.1: {} - safe-regex-test@1.1.0: - dependencies: - call-bound: 1.0.3 - es-errors: 1.3.0 - is-regex: 1.2.1 - safer-buffer@2.1.2: {} scrypt-js@3.0.1: {} @@ -8116,7 +8126,7 @@ snapshots: '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 - '@types/node': 22.10.2 + '@types/node': 22.10.5 acorn: 8.14.0 acorn-walk: 8.3.4 arg: 4.1.3 @@ -8338,12 +8348,12 @@ snapshots: wrappy@1.0.2: {} - ws@7.4.6(bufferutil@4.0.9)(utf-8-validate@5.0.10): + ws@7.4.6(bufferutil@4.0.8)(utf-8-validate@5.0.10): optionalDependencies: - bufferutil: 4.0.9 + bufferutil: 4.0.8 utf-8-validate: 5.0.10 - ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10): + ws@7.5.10(bufferutil@4.0.8)(utf-8-validate@5.0.10): optionalDependencies: bufferutil: 4.0.8 utf-8-validate: 5.0.10 From 272f77f851071eef212873dc39c95a5dde6bfd44 Mon Sep 17 00:00:00 2001 From: michaelessiet Date: Fri, 10 Jan 2025 17:31:08 +0100 Subject: [PATCH 3/7] feat: token balances --- src/actions/balance.ts | 5 ++- src/actions/index.ts | 2 + src/actions/tokenBalances.ts | 80 +++++++++++++++++++++++++++++++++ src/tools/get_balance.ts | 53 +++------------------- src/tools/get_token_balances.ts | 65 +++++++++++++++++++++++++++ test/agent_sdks/vercel_ai.ts | 1 - 6 files changed, 156 insertions(+), 50 deletions(-) create mode 100644 src/actions/tokenBalances.ts create mode 100644 src/tools/get_token_balances.ts diff --git a/src/actions/balance.ts b/src/actions/balance.ts index 1ee1b56..437e479 100644 --- a/src/actions/balance.ts +++ b/src/actions/balance.ts @@ -1,6 +1,6 @@ import { PublicKey } from "@solana/web3.js"; -import { Action } from "../types/action"; -import { SolanaAgentKit } from "../agent"; +import type { Action } from "../types/action"; +import type { SolanaAgentKit } from "../agent"; import { z } from "zod"; import { get_balance } from "../tools"; @@ -54,6 +54,7 @@ const balanceAction: Action = { return { status: "success", balance: balance, + token: input.tokenAddress || "SOL", }; }, }; diff --git a/src/actions/index.ts b/src/actions/index.ts index c974209..9eec07d 100644 --- a/src/actions/index.ts +++ b/src/actions/index.ts @@ -30,9 +30,11 @@ import launchPumpfunTokenAction from "./launchPumpfunToken"; import getWalletAddressAction from "./getWalletAddress"; import flashOpenTradeAction from "./flashOpenTrade"; import flashCloseTradeAction from "./flashCloseTrade"; +import tokenBalancesAction from "./tokenBalances"; export const ACTIONS = { WALLET_ADDRESS_ACTION: getWalletAddressAction, + TOKEN_BALANCES_ACTION: tokenBalancesAction, DEPLOY_TOKEN_ACTION: deployTokenAction, BALANCE_ACTION: balanceAction, TRANSFER_ACTION: transferAction, diff --git a/src/actions/tokenBalances.ts b/src/actions/tokenBalances.ts new file mode 100644 index 0000000..960fca1 --- /dev/null +++ b/src/actions/tokenBalances.ts @@ -0,0 +1,80 @@ +import { PublicKey } from "@solana/web3.js"; +import type { Action } from "../types/action"; +import type { SolanaAgentKit } from "../agent"; +import { z } from "zod"; +import { get_token_balance } from "../tools/get_token_balances"; + +const tokenBalancesAction: Action = { + name: "TOKEN_BALANCE_ACTION", + similes: [ + "check token balances", + "get wallet token balances", + "view token balances", + "show token balances", + "check token balance", + ], + description: `Get the token balances of a Solana wallet. + If you want to get the balance of your wallet, you don't need to provide the wallet address.`, + examples: [ + [ + { + input: {}, + output: { + status: "success", + balance: { + sol: 100, + tokens: [ + { + tokenAddress: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", + name: "USD Coin", + symbol: "USDC", + balance: 100, + decimals: 9, + }, + ], + }, + }, + explanation: "Get token balances of the wallet", + }, + ], + [ + { + input: { + walletAddress: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", + }, + output: { + status: "success", + balance: { + sol: 100, + tokens: [ + { + tokenAddress: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", + name: "USD Coin", + symbol: "USDC", + balance: 100, + decimals: 9, + }, + ], + }, + }, + explanation: "Get address token balance", + }, + ], + ], + schema: z.object({ + walletAddress: z.string().optional(), + }), + handler: async (agent: SolanaAgentKit, input: Record) => { + const balance = await get_token_balance( + agent, + input.tokenAddress && new PublicKey(input.tokenAddress), + ); + + return { + status: "success", + balance: balance, + }; + }, +}; + +export default tokenBalancesAction; diff --git a/src/tools/get_balance.ts b/src/tools/get_balance.ts index 3e30021..f7a6a1e 100644 --- a/src/tools/get_balance.ts +++ b/src/tools/get_balance.ts @@ -1,7 +1,5 @@ -import { LAMPORTS_PER_SOL, PublicKey } from "@solana/web3.js"; -import { SolanaAgentKit } from "../index"; -import { TOKEN_PROGRAM_ID } from "@solana/spl-token"; -import { getTokenMetadata } from "../utils/tokenMetadata"; +import { LAMPORTS_PER_SOL, type PublicKey } from "@solana/web3.js"; +import type { SolanaAgentKit } from "../index"; /** * Get the balance of SOL or an SPL token for the agent's wallet @@ -12,51 +10,12 @@ import { getTokenMetadata } from "../utils/tokenMetadata"; export async function get_balance( agent: SolanaAgentKit, token_address?: PublicKey, -): Promise< - | number - | { - sol: number; - tokens: Array<{ - tokenAddress: string; - name: string; - symbol: string; - balance: number; - decimals: number; - }>; - } -> { +): Promise { if (!token_address) { - const [lamportsBalance, tokenAccountData] = await Promise.all([ - agent.connection.getBalance(agent.wallet_address), - agent.connection.getParsedTokenAccountsByOwner(agent.wallet_address, { - programId: TOKEN_PROGRAM_ID, - }), - ]); - - const removedZeroBalance = tokenAccountData.value.filter( - (v) => v.account.data.parsed.info.tokenAmount.uiAmount !== 0, + return ( + (await agent.connection.getBalance(agent.wallet_address)) / + LAMPORTS_PER_SOL ); - - const tokenBalances = await Promise.all( - removedZeroBalance.map(async (v) => { - const mint = v.account.data.parsed.info.mint; - const mintInfo = await getTokenMetadata(agent.connection, mint); - return { - tokenAddress: mint, - name: mintInfo.name ?? "", - symbol: mintInfo.symbol ?? "", - balance: v.account.data.parsed.info.tokenAmount.uiAmount as number, - decimals: v.account.data.parsed.info.tokenAmount.decimals as number, - }; - }), - ); - - const solBalance = lamportsBalance / LAMPORTS_PER_SOL; - - return { - sol: solBalance, - tokens: tokenBalances, - }; } const token_account = diff --git a/src/tools/get_token_balances.ts b/src/tools/get_token_balances.ts new file mode 100644 index 0000000..00e6310 --- /dev/null +++ b/src/tools/get_token_balances.ts @@ -0,0 +1,65 @@ +import { LAMPORTS_PER_SOL, type PublicKey } from "@solana/web3.js"; +import type { SolanaAgentKit } from "../index"; +import { TOKEN_PROGRAM_ID } from "@solana/spl-token"; +import { getTokenMetadata } from "../utils/tokenMetadata"; + +/** + * Get the token balances of a Solana wallet + * @param agent - SolanaAgentKit instance + * @param token_address - Optional SPL token mint address. If not provided, returns SOL balance + * @returns Promise resolving to the balance as an object containing sol balance and token balances with their respective mints, symbols, names and decimals + */ +export async function get_token_balance( + agent: SolanaAgentKit, + token_address?: PublicKey, +): Promise< + | number + | { + sol: number; + tokens: Array<{ + tokenAddress: string; + name: string; + symbol: string; + balance: number; + decimals: number; + }>; + } +> { + if (!token_address) { + const [lamportsBalance, tokenAccountData] = await Promise.all([ + agent.connection.getBalance(agent.wallet_address), + agent.connection.getParsedTokenAccountsByOwner(agent.wallet_address, { + programId: TOKEN_PROGRAM_ID, + }), + ]); + + const removedZeroBalance = tokenAccountData.value.filter( + (v) => v.account.data.parsed.info.tokenAmount.uiAmount !== 0, + ); + + const tokenBalances = await Promise.all( + removedZeroBalance.map(async (v) => { + const mint = v.account.data.parsed.info.mint; + const mintInfo = await getTokenMetadata(agent.connection, mint); + return { + tokenAddress: mint, + name: mintInfo.name ?? "", + symbol: mintInfo.symbol ?? "", + balance: v.account.data.parsed.info.tokenAmount.uiAmount as number, + decimals: v.account.data.parsed.info.tokenAmount.decimals as number, + }; + }), + ); + + const solBalance = lamportsBalance / LAMPORTS_PER_SOL; + + return { + sol: solBalance, + tokens: tokenBalances, + }; + } + + const token_account = + await agent.connection.getTokenAccountBalance(token_address); + return token_account.value.uiAmount || 0; +} diff --git a/test/agent_sdks/vercel_ai.ts b/test/agent_sdks/vercel_ai.ts index 77fda22..bf1585e 100644 --- a/test/agent_sdks/vercel_ai.ts +++ b/test/agent_sdks/vercel_ai.ts @@ -95,7 +95,6 @@ async function runChatMode() { ); const tools = createVercelAITools(solanaAgent); - console.log(tools); const rl = readline.createInterface({ input: process.stdin, From 51cf665c74c1a647c7598984e59f82758993c0eb Mon Sep 17 00:00:00 2001 From: michaelessiet Date: Fri, 10 Jan 2025 17:31:29 +0100 Subject: [PATCH 4/7] lint --- src/tools/squads_multisig/deposit_to_multisig.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/squads_multisig/deposit_to_multisig.ts b/src/tools/squads_multisig/deposit_to_multisig.ts index 11a2582..e2b23ad 100644 --- a/src/tools/squads_multisig/deposit_to_multisig.ts +++ b/src/tools/squads_multisig/deposit_to_multisig.ts @@ -54,7 +54,7 @@ export async function deposit_to_multisig( mint, agent.wallet_address, ); - let transaction = new Transaction(); + const transaction = new Transaction(); const toAta = await getAssociatedTokenAddress(mint, to, true); const toTokenAccountInfo = await agent.connection.getAccountInfo(toAta); // Create associated token account if it doesn't exist From 6ef55a04dad4173387523d62de2b6a1a5d2898e7 Mon Sep 17 00:00:00 2001 From: michaelessiet Date: Fri, 10 Jan 2025 17:38:20 +0100 Subject: [PATCH 5/7] fix: parse provided wallet address to token balances --- src/agent/index.ts | 28 ++++++----- src/tools/get_token_balances.ts | 86 +++++++++++++++------------------ 2 files changed, 55 insertions(+), 59 deletions(-) diff --git a/src/agent/index.ts b/src/agent/index.ts index 0238b0d..2eec42a 100644 --- a/src/agent/index.ts +++ b/src/agent/index.ts @@ -92,6 +92,7 @@ import { create_proposal } from "../tools/squads_multisig/create_proposal"; import { approve_proposal } from "../tools/squads_multisig/approve_proposal"; import { execute_transaction } from "../tools/squads_multisig/execute_proposal"; import { reject_proposal } from "../tools/squads_multisig/reject_proposal"; +import { get_token_balance } from "../tools/get_token_balances"; /** * Main class for interacting with Solana blockchain @@ -163,22 +164,23 @@ export class SolanaAgentKit { return deploy_collection(this, options); } - async getBalance(token_address?: PublicKey): Promise< - | number - | { - sol: number; - tokens: Array<{ - tokenAddress: string; - name: string; - symbol: string; - balance: number; - decimals: number; - }>; - } - > { + async getBalance(token_address?: PublicKey): Promise { return get_balance(this, token_address); } + async getTokenBalances(wallet_address?: PublicKey): Promise<{ + sol: number; + tokens: Array<{ + tokenAddress: string; + name: string; + symbol: string; + balance: number; + decimals: number; + }>; + }> { + return get_token_balance(this, wallet_address); + } + async getBalanceOther( walletAddress: PublicKey, tokenAddress?: PublicKey, diff --git a/src/tools/get_token_balances.ts b/src/tools/get_token_balances.ts index 00e6310..01f2f2d 100644 --- a/src/tools/get_token_balances.ts +++ b/src/tools/get_token_balances.ts @@ -11,55 +11,49 @@ import { getTokenMetadata } from "../utils/tokenMetadata"; */ export async function get_token_balance( agent: SolanaAgentKit, - token_address?: PublicKey, -): Promise< - | number - | { - sol: number; - tokens: Array<{ - tokenAddress: string; - name: string; - symbol: string; - balance: number; - decimals: number; - }>; - } -> { - if (!token_address) { - const [lamportsBalance, tokenAccountData] = await Promise.all([ - agent.connection.getBalance(agent.wallet_address), - agent.connection.getParsedTokenAccountsByOwner(agent.wallet_address, { + walletAddress?: PublicKey, +): Promise<{ + sol: number; + tokens: Array<{ + tokenAddress: string; + name: string; + symbol: string; + balance: number; + decimals: number; + }>; +}> { + const [lamportsBalance, tokenAccountData] = await Promise.all([ + agent.connection.getBalance(walletAddress ?? agent.wallet_address), + agent.connection.getParsedTokenAccountsByOwner( + walletAddress ?? agent.wallet_address, + { programId: TOKEN_PROGRAM_ID, - }), - ]); + }, + ), + ]); - const removedZeroBalance = tokenAccountData.value.filter( - (v) => v.account.data.parsed.info.tokenAmount.uiAmount !== 0, - ); + const removedZeroBalance = tokenAccountData.value.filter( + (v) => v.account.data.parsed.info.tokenAmount.uiAmount !== 0, + ); - const tokenBalances = await Promise.all( - removedZeroBalance.map(async (v) => { - const mint = v.account.data.parsed.info.mint; - const mintInfo = await getTokenMetadata(agent.connection, mint); - return { - tokenAddress: mint, - name: mintInfo.name ?? "", - symbol: mintInfo.symbol ?? "", - balance: v.account.data.parsed.info.tokenAmount.uiAmount as number, - decimals: v.account.data.parsed.info.tokenAmount.decimals as number, - }; - }), - ); + const tokenBalances = await Promise.all( + removedZeroBalance.map(async (v) => { + const mint = v.account.data.parsed.info.mint; + const mintInfo = await getTokenMetadata(agent.connection, mint); + return { + tokenAddress: mint, + name: mintInfo.name ?? "", + symbol: mintInfo.symbol ?? "", + balance: v.account.data.parsed.info.tokenAmount.uiAmount as number, + decimals: v.account.data.parsed.info.tokenAmount.decimals as number, + }; + }), + ); - const solBalance = lamportsBalance / LAMPORTS_PER_SOL; + const solBalance = lamportsBalance / LAMPORTS_PER_SOL; - return { - sol: solBalance, - tokens: tokenBalances, - }; - } - - const token_account = - await agent.connection.getTokenAccountBalance(token_address); - return token_account.value.uiAmount || 0; + return { + sol: solBalance, + tokens: tokenBalances, + }; } From 6635d0e934458ffac080131b65836e22a23e197e Mon Sep 17 00:00:00 2001 From: michaelessiet Date: Wed, 15 Jan 2025 15:45:30 +0100 Subject: [PATCH 6/7] fix: failing CI workflow --- .github/workflows/build.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c862186..6b4d5a2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -14,6 +14,9 @@ jobs: with: version: 9.4.0 + - name: Install node-gyp prerequisites + run: sudo apt update && sudo apt install -y build-essential python3 pkg-config libudev-dev libusb-1.0-0-dev + - uses: actions/setup-node@v4 with: node-version: "23" @@ -21,7 +24,7 @@ jobs: - name: Install dependencies run: pnpm install -r --no-frozen-lockfile - + - name: Run lint and fix run: pnpm run lint:fix From ccbdc27f63bf94ce2185fae59196ae2ed5c35d86 Mon Sep 17 00:00:00 2001 From: michaelessiet Date: Wed, 15 Jan 2025 17:37:54 +0100 Subject: [PATCH 7/7] feat: add drift tools to langchain --- src/langchain/drift/create_user_account.ts | 38 ++++++++++++++ src/langchain/drift/create_vault.ts | 42 +++++++++++++++ src/langchain/drift/deposit_into_vault.ts | 37 +++++++++++++ .../drift/deposit_to_user_account.ts | 39 ++++++++++++++ src/langchain/drift/derive_vault_address.ts | 32 ++++++++++++ .../drift/does_user_have_drift_account.ts | 38 ++++++++++++++ .../drift/drift_user_account_info.ts | 29 +++++++++++ src/langchain/drift/index.ts | 15 ++++++ src/langchain/drift/request_withdrawal.ts | 37 +++++++++++++ src/langchain/drift/trade_delegated_vault.ts | 49 +++++++++++++++++ src/langchain/drift/trade_perp_account.ts | 42 +++++++++++++++ .../drift/update_drift_vault_delegate.ts | 37 +++++++++++++ src/langchain/drift/update_vault.ts | 52 +++++++++++++++++++ src/langchain/drift/vault_info.ts | 32 ++++++++++++ src/langchain/drift/withdraw_from_account.ts | 39 ++++++++++++++ src/langchain/drift/withdraw_from_vault.ts | 32 ++++++++++++ src/langchain/index.ts | 31 +++++++++++ 17 files changed, 621 insertions(+) create mode 100644 src/langchain/drift/create_user_account.ts create mode 100644 src/langchain/drift/create_vault.ts create mode 100644 src/langchain/drift/deposit_into_vault.ts create mode 100644 src/langchain/drift/deposit_to_user_account.ts create mode 100644 src/langchain/drift/derive_vault_address.ts create mode 100644 src/langchain/drift/does_user_have_drift_account.ts create mode 100644 src/langchain/drift/drift_user_account_info.ts create mode 100644 src/langchain/drift/index.ts create mode 100644 src/langchain/drift/request_withdrawal.ts create mode 100644 src/langchain/drift/trade_delegated_vault.ts create mode 100644 src/langchain/drift/trade_perp_account.ts create mode 100644 src/langchain/drift/update_drift_vault_delegate.ts create mode 100644 src/langchain/drift/update_vault.ts create mode 100644 src/langchain/drift/vault_info.ts create mode 100644 src/langchain/drift/withdraw_from_account.ts create mode 100644 src/langchain/drift/withdraw_from_vault.ts diff --git a/src/langchain/drift/create_user_account.ts b/src/langchain/drift/create_user_account.ts new file mode 100644 index 0000000..36f8ecf --- /dev/null +++ b/src/langchain/drift/create_user_account.ts @@ -0,0 +1,38 @@ +import { Tool } from "langchain/tools"; +import { SolanaAgentKit } from "../../agent"; + +export class SolanaCreateDriftUserAccountTool extends Tool { + name = "create_drift_user_account"; + description = `Create a new user account with a deposit on Drift protocol. + + Inputs (JSON string): + - amount: number, amount of the token to deposit (required) + - symbol: string, symbol of the token to deposit (required)`; + + constructor(private solanaKit: SolanaAgentKit) { + super(); + } + + protected async _call(input: string): Promise { + try { + const parsedInput = JSON.parse(input); + const res = await this.solanaKit.createDriftUserAccount( + parsedInput.amount, + parsedInput.symbol, + ); + + return JSON.stringify({ + status: "success", + message: `User account created with ${parsedInput.amount} ${parsedInput.symbol} successfully deposited`, + account: res.account, + signature: res.txSignature, + }); + } catch (error: any) { + return JSON.stringify({ + status: "error", + message: error.message, + code: error.code || "CREATE_DRIFT_USER_ACCOUNT_ERROR", + }); + } + } +} diff --git a/src/langchain/drift/create_vault.ts b/src/langchain/drift/create_vault.ts new file mode 100644 index 0000000..5ff62f2 --- /dev/null +++ b/src/langchain/drift/create_vault.ts @@ -0,0 +1,42 @@ +import { Tool } from "langchain/tools"; +import { SolanaAgentKit } from "../../agent"; + +export class SolanaCreateDriftVaultTool extends Tool { + name = "create_drift_vault"; + description = `Create a new drift vault delegating the agents address as the owner. + + Inputs (JSON string): + - name: string, unique vault name (min 5 chars) + - marketName: string, market name in TOKEN-SPOT format + - redeemPeriod: number, days to wait before funds can be redeemed (min 1) + - maxTokens: number, maximum tokens vault can accommodate (min 100) + - minDepositAmount: number, minimum deposit amount + - managementFee: number, fee percentage for managing funds (max 20) + - profitShare: number, profit sharing percentage (max 90, default 5) + - hurdleRate: number, optional hurdle rate + - permissioned: boolean, whether vault has whitelist`; + + constructor(private solanaKit: SolanaAgentKit) { + super(); + } + + protected async _call(input: string): Promise { + try { + const parsedInput = JSON.parse(input); + const tx = await this.solanaKit.createDriftVault(parsedInput); + + return JSON.stringify({ + status: "success", + message: "Drift vault created successfully", + vaultName: parsedInput.name, + signature: tx, + }); + } catch (error: any) { + return JSON.stringify({ + status: "error", + message: error.message, + code: error.code || "CREATE_DRIFT_VAULT_ERROR", + }); + } + } +} diff --git a/src/langchain/drift/deposit_into_vault.ts b/src/langchain/drift/deposit_into_vault.ts new file mode 100644 index 0000000..689fba1 --- /dev/null +++ b/src/langchain/drift/deposit_into_vault.ts @@ -0,0 +1,37 @@ +import { Tool } from "langchain/tools"; +import { SolanaAgentKit } from "../../agent"; + +export class SolanaDepositIntoDriftVaultTool extends Tool { + name = "deposit_into_drift_vault"; + description = `Deposit funds into an existing drift vault. + + Inputs (JSON string): + - vaultAddress: string, address of the vault (required) + - amount: number, amount to deposit (required)`; + + constructor(private solanaKit: SolanaAgentKit) { + super(); + } + + protected async _call(input: string): Promise { + try { + const parsedInput = JSON.parse(input); + const tx = await this.solanaKit.depositIntoDriftVault( + parsedInput.amount, + parsedInput.vaultAddress, + ); + + return JSON.stringify({ + status: "success", + message: "Funds deposited successfully", + signature: tx, + }); + } catch (error: any) { + return JSON.stringify({ + status: "error", + message: error.message, + code: error.code || "DEPOSIT_INTO_VAULT_ERROR", + }); + } + } +} diff --git a/src/langchain/drift/deposit_to_user_account.ts b/src/langchain/drift/deposit_to_user_account.ts new file mode 100644 index 0000000..a9ecb43 --- /dev/null +++ b/src/langchain/drift/deposit_to_user_account.ts @@ -0,0 +1,39 @@ +import { Tool } from "langchain/tools"; +import { SolanaAgentKit } from "../../agent"; + +export class SolanaDepositToDriftUserAccountTool extends Tool { + name = "deposit_to_drift_user_account"; + description = `Deposit funds into your drift user account. + + Inputs (JSON string): + - amount: number, amount to deposit (required) + - symbol: string, token symbol (required) + - repay: boolean, whether to repay borrowed funds (optional, default: false)`; + + constructor(private solanaKit: SolanaAgentKit) { + super(); + } + + protected async _call(input: string): Promise { + try { + const parsedInput = JSON.parse(input); + const tx = await this.solanaKit.depositToDriftUserAccount( + parsedInput.amount, + parsedInput.symbol, + parsedInput.repay, + ); + + return JSON.stringify({ + status: "success", + message: "Funds deposited successfully", + signature: tx, + }); + } catch (error: any) { + return JSON.stringify({ + status: "error", + message: error.message, + code: error.code || "DEPOSIT_TO_DRIFT_ACCOUNT_ERROR", + }); + } + } +} diff --git a/src/langchain/drift/derive_vault_address.ts b/src/langchain/drift/derive_vault_address.ts new file mode 100644 index 0000000..bbd3b8a --- /dev/null +++ b/src/langchain/drift/derive_vault_address.ts @@ -0,0 +1,32 @@ +import { Tool } from "langchain/tools"; +import { SolanaAgentKit } from "../../agent"; + +export class SolanaDeriveVaultAddressTool extends Tool { + name = "derive_drift_vault_address"; + description = `Derive a drift vault address from the vault's name. + + Inputs (JSON string): + - name: string, name of the vault to derive the address of (required)`; + + constructor(private solanaKit: SolanaAgentKit) { + super(); + } + + protected async _call(input: string): Promise { + try { + const address = await this.solanaKit.deriveDriftVaultAddress(input); + + return JSON.stringify({ + status: "success", + message: "Vault address derived successfully", + address, + }); + } catch (error: any) { + return JSON.stringify({ + status: "error", + message: error.message, + code: error.code || "DERIVE_VAULT_ADDRESS_ERROR", + }); + } + } +} diff --git a/src/langchain/drift/does_user_have_drift_account.ts b/src/langchain/drift/does_user_have_drift_account.ts new file mode 100644 index 0000000..7673b57 --- /dev/null +++ b/src/langchain/drift/does_user_have_drift_account.ts @@ -0,0 +1,38 @@ +import { Tool } from "langchain/tools"; +import { SolanaAgentKit } from "../../agent"; + +export class SolanaCheckDriftAccountTool extends Tool { + name = "does_user_have_drift_account"; + description = `Check if a user has a Drift account. + + Inputs: No inputs required - checks the current user's account`; + + constructor(private solanaKit: SolanaAgentKit) { + super(); + } + + protected async _call(_input: string): Promise { + try { + const res = await this.solanaKit.doesUserHaveDriftAccount(); + + if (!res.hasAccount) { + return JSON.stringify({ + status: "error", + message: "You do not have a Drift account", + }); + } + + return JSON.stringify({ + status: "success", + message: "Nice! You have a Drift account", + account: res.account, + }); + } catch (error: any) { + return JSON.stringify({ + status: "error", + message: error.message, + code: error.code || "CHECK_DRIFT_ACCOUNT_ERROR", + }); + } + } +} diff --git a/src/langchain/drift/drift_user_account_info.ts b/src/langchain/drift/drift_user_account_info.ts new file mode 100644 index 0000000..191577a --- /dev/null +++ b/src/langchain/drift/drift_user_account_info.ts @@ -0,0 +1,29 @@ +import { Tool } from "langchain/tools"; +import { SolanaAgentKit } from "../../agent"; + +export class SolanaDriftUserAccountInfoTool extends Tool { + name = "drift_user_account_info"; + description = `Get information about your drift account. + + Inputs: No inputs required - retrieves current user's account info`; + + constructor(private solanaKit: SolanaAgentKit) { + super(); + } + + protected async _call(_input: string): Promise { + try { + const accountInfo = await this.solanaKit.driftUserAccountInfo(); + return JSON.stringify({ + status: "success", + data: accountInfo, + }); + } catch (error: any) { + return JSON.stringify({ + status: "error", + message: error.message, + code: error.code || "DRIFT_ACCOUNT_INFO_ERROR", + }); + } + } +} diff --git a/src/langchain/drift/index.ts b/src/langchain/drift/index.ts new file mode 100644 index 0000000..3c9a4c8 --- /dev/null +++ b/src/langchain/drift/index.ts @@ -0,0 +1,15 @@ +export * from "./create_user_account"; +export * from "./create_vault"; +export * from "./deposit_into_vault"; +export * from "./deposit_to_user_account"; +export * from "./derive_vault_address"; +export * from "./does_user_have_drift_account"; +export * from "./drift_user_account_info"; +export * from "./request_withdrawal"; +export * from "./trade_delegated_vault"; +export * from "./trade_perp_account"; +export * from "./update_drift_vault_delegate"; +export * from "./update_vault"; +export * from "./vault_info"; +export * from "./withdraw_from_account"; +export * from "./withdraw_from_vault"; diff --git a/src/langchain/drift/request_withdrawal.ts b/src/langchain/drift/request_withdrawal.ts new file mode 100644 index 0000000..507a31e --- /dev/null +++ b/src/langchain/drift/request_withdrawal.ts @@ -0,0 +1,37 @@ +import { Tool } from "langchain/tools"; +import { SolanaAgentKit } from "../../agent"; + +export class SolanaRequestDriftWithdrawalTool extends Tool { + name = "request_withdrawal_from_drift_vault"; + description = `Request a withdrawal from an existing drift vault. + + Inputs (JSON string): + - vaultAddress: string, vault address (required) + - amount: number, amount of shares to withdraw (required)`; + + constructor(private solanaKit: SolanaAgentKit) { + super(); + } + + protected async _call(input: string): Promise { + try { + const parsedInput = JSON.parse(input); + const tx = await this.solanaKit.requestWithdrawalFromDriftVault( + parsedInput.amount, + parsedInput.vaultAddress, + ); + + return JSON.stringify({ + status: "success", + message: "Withdrawal request successful", + signature: tx, + }); + } catch (error: any) { + return JSON.stringify({ + status: "error", + message: error.message, + code: error.code || "REQUEST_DRIFT_WITHDRAWAL_ERROR", + }); + } + } +} diff --git a/src/langchain/drift/trade_delegated_vault.ts b/src/langchain/drift/trade_delegated_vault.ts new file mode 100644 index 0000000..131efd8 --- /dev/null +++ b/src/langchain/drift/trade_delegated_vault.ts @@ -0,0 +1,49 @@ +import { Tool } from "langchain/tools"; +import { SolanaAgentKit } from "../../agent"; + +export class SolanaTradeDelegatedDriftVaultTool extends Tool { + name = "trade_delegated_drift_vault"; + description = `Carry out trades in a Drift vault. + + Inputs (JSON string): + - vaultAddress: string, address of the Drift vault + - amount: number, amount to trade + - symbol: string, symbol of the token to trade + - action: "long" | "short", trade direction + - type: "market" | "limit", order type + - price: number, optional limit price`; + + constructor(private solanaKit: SolanaAgentKit) { + super(); + } + + protected async _call(input: string): Promise { + try { + const parsedInput = JSON.parse(input); + const tx = await this.solanaKit.tradeUsingDelegatedDriftVault( + parsedInput.vaultAddress, + parsedInput.amount, + parsedInput.symbol, + parsedInput.action, + parsedInput.type, + parsedInput.price, + ); + + return JSON.stringify({ + status: "success", + message: + parsedInput.type === "limit" + ? "Order placed successfully" + : "Trade successful", + transactionId: tx, + ...parsedInput, + }); + } catch (error: any) { + return JSON.stringify({ + status: "error", + message: error.message, + code: error.code || "TRADE_DRIFT_VAULT_ERROR", + }); + } + } +} diff --git a/src/langchain/drift/trade_perp_account.ts b/src/langchain/drift/trade_perp_account.ts new file mode 100644 index 0000000..29f4ac1 --- /dev/null +++ b/src/langchain/drift/trade_perp_account.ts @@ -0,0 +1,42 @@ +import { Tool } from "langchain/tools"; +import { SolanaAgentKit } from "../../agent"; + +export class SolanaTradeDriftPerpAccountTool extends Tool { + name = "trade_drift_perp_account"; + description = `Trade a perpetual account on Drift protocol. + + Inputs (JSON string): + - amount: number, amount to trade (required) + - symbol: string, token symbol (required) + - action: "long" | "short", trade direction (required) + - type: "market" | "limit", order type (required) + - price: number, required for limit orders`; + + constructor(private solanaKit: SolanaAgentKit) { + super(); + } + + protected async _call(input: string): Promise { + try { + const parsedInput = JSON.parse(input); + const signature = await this.solanaKit.tradeUsingDriftPerpAccount( + parsedInput.amount, + parsedInput.symbol, + parsedInput.action, + parsedInput.type, + parsedInput.price, + ); + + return JSON.stringify({ + status: "success", + signature, + }); + } catch (error: any) { + return JSON.stringify({ + status: "error", + message: error.message, + code: error.code || "TRADE_PERP_ACCOUNT_ERROR", + }); + } + } +} diff --git a/src/langchain/drift/update_drift_vault_delegate.ts b/src/langchain/drift/update_drift_vault_delegate.ts new file mode 100644 index 0000000..8608593 --- /dev/null +++ b/src/langchain/drift/update_drift_vault_delegate.ts @@ -0,0 +1,37 @@ +import { Tool } from "langchain/tools"; +import { SolanaAgentKit } from "../../agent"; + +export class SolanaUpdateDriftVaultDelegateTool extends Tool { + name = "update_drift_vault_delegate"; + description = `Update the delegate of a drift vault. + + Inputs (JSON string): + - vaultAddress: string, address of the vault (required) + - newDelegate: string, address of the new delegate (required)`; + + constructor(private solanaKit: SolanaAgentKit) { + super(); + } + + protected async _call(input: string): Promise { + try { + const parsedInput = JSON.parse(input); + const tx = await this.solanaKit.updateDriftVaultDelegate( + parsedInput.vaultAddress, + parsedInput.newDelegate, + ); + + return JSON.stringify({ + status: "success", + message: "Vault delegate updated successfully", + signature: tx, + }); + } catch (error: any) { + return JSON.stringify({ + status: "error", + message: error.message, + code: error.code || "UPDATE_DRIFT_VAULT_DELEGATE_ERROR", + }); + } + } +} diff --git a/src/langchain/drift/update_vault.ts b/src/langchain/drift/update_vault.ts new file mode 100644 index 0000000..608d963 --- /dev/null +++ b/src/langchain/drift/update_vault.ts @@ -0,0 +1,52 @@ +import { Tool } from "langchain/tools"; +import { SolanaAgentKit } from "../../agent"; + +export class SolanaUpdateDriftVaultTool extends Tool { + name = "update_drift_vault"; + description = `Update an existing drift vault with new settings. + + Inputs (JSON string): + - vaultAddress: string, vault address (required) + - redeemPeriod: number, days until redemption (optional) + - maxTokens: number, maximum tokens allowed (optional) + - minDepositAmount: number, minimum deposit amount (optional) + - managementFee: number, management fee percentage (optional) + - profitShare: number, profit sharing percentage (optional) + - hurdleRate: number, hurdle rate (optional) + - permissioned: boolean, whitelist requirement (optional)`; + + constructor(private solanaKit: SolanaAgentKit) { + super(); + } + + protected async _call(input: string): Promise { + try { + const parsedInput = JSON.parse(input); + const tx = await this.solanaKit.updateDriftVault( + parsedInput.vaultAddress, + // @ts-expect-error - type mismatch + { + hurdleRate: parsedInput.hurdleRate, + maxTokens: parsedInput.maxTokens, + minDepositAmount: parsedInput.minDepositAmount, + profitShare: parsedInput.profitShare, + managementFee: parsedInput.managementFee, + permissioned: parsedInput.permissioned, + redeemPeriod: parsedInput.redeemPeriod, + }, + ); + + return JSON.stringify({ + status: "success", + message: "Drift vault parameters updated successfully", + signature: tx, + }); + } catch (error: any) { + return JSON.stringify({ + status: "error", + message: error.message, + code: error.code || "UPDATE_DRIFT_VAULT_ERROR", + }); + } + } +} diff --git a/src/langchain/drift/vault_info.ts b/src/langchain/drift/vault_info.ts new file mode 100644 index 0000000..fc250e9 --- /dev/null +++ b/src/langchain/drift/vault_info.ts @@ -0,0 +1,32 @@ +import { Tool } from "langchain/tools"; +import { SolanaAgentKit } from "../../agent"; + +export class SolanaDriftVaultInfoTool extends Tool { + name = "drift_vault_info"; + description = `Get information about a drift vault. + + Inputs (JSON string): + - vaultNameOrAddress: string, name or address of the vault (required)`; + + constructor(private solanaKit: SolanaAgentKit) { + super(); + } + + protected async _call(input: string): Promise { + try { + const vaultInfo = await this.solanaKit.getDriftVaultInfo(input); + + return JSON.stringify({ + status: "success", + message: "Vault info retrieved successfully", + data: vaultInfo, + }); + } catch (error: any) { + return JSON.stringify({ + status: "error", + message: error.message, + code: error.code || "DRIFT_VAULT_INFO_ERROR", + }); + } + } +} diff --git a/src/langchain/drift/withdraw_from_account.ts b/src/langchain/drift/withdraw_from_account.ts new file mode 100644 index 0000000..ce76375 --- /dev/null +++ b/src/langchain/drift/withdraw_from_account.ts @@ -0,0 +1,39 @@ +import { Tool } from "langchain/tools"; +import { SolanaAgentKit } from "../../agent"; + +export class SolanaWithdrawFromDriftAccountTool extends Tool { + name = "withdraw_from_drift_account"; + description = `Withdraw or borrow funds from your drift account. + + Inputs (JSON string): + - amount: number, amount to withdraw (required) + - symbol: string, token symbol (required) + - isBorrow: boolean, whether to borrow funds instead of withdrawing (optional, default: false)`; + + constructor(private solanaKit: SolanaAgentKit) { + super(); + } + + protected async _call(input: string): Promise { + try { + const parsedInput = JSON.parse(input); + const tx = await this.solanaKit.withdrawFromDriftAccount( + parsedInput.amount, + parsedInput.symbol, + parsedInput.isBorrow, + ); + + return JSON.stringify({ + status: "success", + message: "Funds withdrawn successfully", + signature: tx, + }); + } catch (error: any) { + return JSON.stringify({ + status: "error", + message: error.message, + code: error.code || "WITHDRAW_FROM_DRIFT_ACCOUNT_ERROR", + }); + } + } +} diff --git a/src/langchain/drift/withdraw_from_vault.ts b/src/langchain/drift/withdraw_from_vault.ts new file mode 100644 index 0000000..1bace25 --- /dev/null +++ b/src/langchain/drift/withdraw_from_vault.ts @@ -0,0 +1,32 @@ +import { Tool } from "langchain/tools"; +import { SolanaAgentKit } from "../../agent"; + +export class SolanaWithdrawFromDriftVaultTool extends Tool { + name = "withdraw_from_drift_vault"; + description = `Withdraw funds from a vault given the redemption time has elapsed. + + Inputs (JSON string): + - vaultAddress: string, vault address (required)`; + + constructor(private solanaKit: SolanaAgentKit) { + super(); + } + + protected async _call(input: string): Promise { + try { + const tx = await this.solanaKit.withdrawFromDriftVault(input); + + return JSON.stringify({ + status: "success", + message: "Withdrawal successful", + signature: tx, + }); + } catch (error: any) { + return JSON.stringify({ + status: "error", + message: error.message, + code: error.code || "WITHDRAW_FROM_DRIFT_VAULT_ERROR", + }); + } + } +} diff --git a/src/langchain/index.ts b/src/langchain/index.ts index a37bfa8..809c340 100644 --- a/src/langchain/index.ts +++ b/src/langchain/index.ts @@ -25,6 +25,7 @@ export * from "./sns"; export * from "./lightprotocol"; export * from "./squads"; export * from "./helius"; +export * from "./drift"; import { SolanaAgentKit } from "../agent"; import { @@ -98,6 +99,21 @@ import { SolanaDeleteHeliusWebhookTool, SolanaParseTransactionHeliusTool, SolanaGetAllAssetsByOwner, + SolanaCheckDriftAccountTool, + SolanaCreateDriftUserAccountTool, + SolanaCreateDriftVaultTool, + SolanaDepositIntoDriftVaultTool, + SolanaDepositToDriftUserAccountTool, + SolanaDeriveVaultAddressTool, + SolanaDriftUserAccountInfoTool, + SolanaDriftVaultInfoTool, + SolanaRequestDriftWithdrawalTool, + SolanaTradeDelegatedDriftVaultTool, + SolanaTradeDriftPerpAccountTool, + SolanaUpdateDriftVaultDelegateTool, + SolanaUpdateDriftVaultTool, + SolanaWithdrawFromDriftAccountTool, + SolanaWithdrawFromDriftVaultTool, } from "./index"; export function createSolanaTools(solanaKit: SolanaAgentKit) { @@ -177,5 +193,20 @@ export function createSolanaTools(solanaKit: SolanaAgentKit) { new SolanaHeliusWebhookTool(solanaKit), new SolanaGetHeliusWebhookTool(solanaKit), new SolanaDeleteHeliusWebhookTool(solanaKit), + new SolanaCreateDriftUserAccountTool(solanaKit), + new SolanaCreateDriftVaultTool(solanaKit), + new SolanaDepositIntoDriftVaultTool(solanaKit), + new SolanaDepositToDriftUserAccountTool(solanaKit), + new SolanaDeriveVaultAddressTool(solanaKit), + new SolanaCheckDriftAccountTool(solanaKit), + new SolanaDriftUserAccountInfoTool(solanaKit), + new SolanaRequestDriftWithdrawalTool(solanaKit), + new SolanaTradeDelegatedDriftVaultTool(solanaKit), + new SolanaTradeDriftPerpAccountTool(solanaKit), + new SolanaUpdateDriftVaultDelegateTool(solanaKit), + new SolanaUpdateDriftVaultTool(solanaKit), + new SolanaDriftVaultInfoTool(solanaKit), + new SolanaWithdrawFromDriftAccountTool(solanaKit), + new SolanaWithdrawFromDriftVaultTool(solanaKit), ]; }