From fe53b0cbd95138eb4a009367ff4cc405e5cff04b Mon Sep 17 00:00:00 2001 From: Arihant Bansal <17180950+arihantbansal@users.noreply.github.com> Date: Fri, 13 Dec 2024 16:06:52 +0530 Subject: [PATCH 01/11] feat: add jup list fetch --- src/tools/get_token_data.ts | 42 +++++++++++++++++++++++++++++++++++++ src/types/index.ts | 17 ++++++++++++++- 2 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 src/tools/get_token_data.ts diff --git a/src/tools/get_token_data.ts b/src/tools/get_token_data.ts new file mode 100644 index 0000000..d284d54 --- /dev/null +++ b/src/tools/get_token_data.ts @@ -0,0 +1,42 @@ +import { PublicKey } from "@solana/web3.js"; +import { JupiterTokenData } from "../types"; + +export async function fetchTokenDataByMint( + mint: PublicKey, +): Promise { + try { + const response = await fetch("https://tokens.jup.ag/tokens?tags=verified", { + method: "GET", + headers: { + "Content-Type": "application/json", + }, + }); + + const data = (await response.json()) as JupiterTokenData[]; + const token = data.find( + (token: JupiterTokenData) => token.address === mint.toString(), + ); + return token; + } catch (error: any) { + throw new Error(`Error fetching token data: ${error.message}`); + } +} + +export async function fetchTokenDataByName( + name: string, +): Promise { + try { + const response = await fetch("https://tokens.jup.ag/tokens?tags=verified", { + method: "GET", + headers: { + "Content-Type": "application/json", + }, + }); + + const data = (await response.json()) as JupiterTokenData[]; + const token = data.find((token: JupiterTokenData) => token.name === name); + return token; + } catch (error) { + throw new Error(`Error fetching token data: ${error.message}`); + } +} diff --git a/src/types/index.ts b/src/types/index.ts index e5c591e..54954c8 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -39,7 +39,6 @@ export interface PumpfunLaunchResponse { error?: string; } - /** * Lulo Account Details response format */ @@ -54,3 +53,19 @@ export interface LuloAccountDetailsResponse { minimumRate: string; }; } + +export interface JupiterTokenData { + address: string; + name: string; + symbol: string; + decimals: number; + tags: string[]; + logoURI: string; + daily_volume: number; + freeze_authority: string | null; + mint_authority: string | null; + permanent_delegate: string | null; + extensions: { + coingeckoId?: string; + }; +} From 68933a1dfd17bdfe2cd55fa43680cbf530f52330 Mon Sep 17 00:00:00 2001 From: Arihant Bansal <17180950+arihantbansal@users.noreply.github.com> Date: Fri, 13 Dec 2024 16:08:31 +0530 Subject: [PATCH 02/11] wip --- src/agent/index.ts | 10 ++++++++++ src/tools/index.ts | 1 + 2 files changed, 11 insertions(+) diff --git a/src/agent/index.ts b/src/agent/index.ts index 0d7a4ef..b6e688a 100644 --- a/src/agent/index.ts +++ b/src/agent/index.ts @@ -12,6 +12,8 @@ import { launchPumpFunToken, lendAsset, getTPS, + fetchTokenDataByMint, + fetchTokenDataByName, } from "../tools"; import { CollectionOptions, PumpFunTokenOptions } from "../types"; import { DEFAULT_OPTIONS } from "../constants"; @@ -95,6 +97,14 @@ export class SolanaAgentKit { return getTPS(this); } + async getTokenDataByMint(mint: PublicKey) { + return fetchTokenDataByMint(mint); + } + + async getTokenDataByName(name: string) { + return fetchTokenDataByName(name); + } + async launchPumpFunToken( tokenName: string, tokenTicker: string, diff --git a/src/tools/index.ts b/src/tools/index.ts index b9ca5ae..aab67a1 100644 --- a/src/tools/index.ts +++ b/src/tools/index.ts @@ -9,3 +9,4 @@ export * from "./register_domain"; export * from "./launch_pumpfun_token"; export * from "./lend"; export * from "./get_tps"; +export * from "./get_token_data"; From 28a795279705b753d327a3fb64335e351a946e74 Mon Sep 17 00:00:00 2001 From: Arihant Bansal <17180950+arihantbansal@users.noreply.github.com> Date: Fri, 13 Dec 2024 17:03:22 +0530 Subject: [PATCH 03/11] feat: fetch tokens from jup list --- src/agent/index.ts | 11 ++---- src/langchain/index.ts | 72 ++++++++++++++++++++++++++++++++----- src/tools/get_token_data.ts | 44 +++++++++++------------ 3 files changed, 86 insertions(+), 41 deletions(-) diff --git a/src/agent/index.ts b/src/agent/index.ts index b6e688a..80b1516 100644 --- a/src/agent/index.ts +++ b/src/agent/index.ts @@ -12,8 +12,7 @@ import { launchPumpFunToken, lendAsset, getTPS, - fetchTokenDataByMint, - fetchTokenDataByName, + fetchTokenData, } from "../tools"; import { CollectionOptions, PumpFunTokenOptions } from "../types"; import { DEFAULT_OPTIONS } from "../constants"; @@ -97,12 +96,8 @@ export class SolanaAgentKit { return getTPS(this); } - async getTokenDataByMint(mint: PublicKey) { - return fetchTokenDataByMint(mint); - } - - async getTokenDataByName(name: string) { - return fetchTokenDataByName(name); + async getTokenData(name?: string, symbol?: string, mint?: PublicKey) { + return fetchTokenData(name, symbol, mint); } async launchPumpFunToken( diff --git a/src/langchain/index.ts b/src/langchain/index.ts index 2ba3b23..3d4896d 100644 --- a/src/langchain/index.ts +++ b/src/langchain/index.ts @@ -64,7 +64,7 @@ export class SolanaTransferTool extends Tool { const tx = await this.solanaKit.transfer( recipient, parsedInput.amount, - mintAddress + mintAddress, ); return JSON.stringify({ @@ -102,7 +102,7 @@ export class SolanaDeployTokenTool extends Tool { input.decimals > 9) ) { throw new Error( - "decimals must be a number between 0 and 9 when provided" + "decimals must be a number between 0 and 9 when provided", ); } if ( @@ -159,7 +159,7 @@ export class SolanaDeployCollectionTool extends Tool { input.royaltyBasisPoints > 10000) ) { throw new Error( - "royaltyBasisPoints must be a number between 0 and 10000 when provided" + "royaltyBasisPoints must be a number between 0 and 10000 when provided", ); } if (input.creators) { @@ -169,7 +169,7 @@ export class SolanaDeployCollectionTool extends Tool { input.creators.forEach((creator: any, index: number) => { if (!creator.address || typeof creator.address !== "string") { throw new Error( - `creator[${index}].address is required and must be a string` + `creator[${index}].address is required and must be a string`, ); } if ( @@ -178,7 +178,7 @@ export class SolanaDeployCollectionTool extends Tool { creator.percentage > 100 ) { throw new Error( - `creator[${index}].percentage must be a number between 0 and 100` + `creator[${index}].percentage must be a number between 0 and 100`, ); } }); @@ -246,7 +246,9 @@ export class SolanaMintNFTTool extends Tool { const result = await this.solanaKit.mintNFT( new PublicKey(parsedInput.collectionMint), parsedInput.metadata, - parsedInput.recipient ? new PublicKey(parsedInput.recipient) : undefined + parsedInput.recipient + ? new PublicKey(parsedInput.recipient) + : undefined, ); return JSON.stringify({ @@ -290,7 +292,7 @@ export class SolanaTradeTool extends Tool { parsedInput.inputMint ? new PublicKey(parsedInput.inputMint) : new PublicKey("So11111111111111111111111111111111111111112"), - parsedInput.slippageBps + parsedInput.slippageBps, ); return JSON.stringify({ @@ -371,7 +373,7 @@ export class SolanaRegisterDomainTool extends Tool { const tx = await this.solanaKit.registerDomain( parsedInput.name, - parsedInput.spaceKB || 1 + parsedInput.spaceKB || 1, ); return JSON.stringify({ @@ -463,7 +465,7 @@ export class SolanaPumpfunTokenLaunchTool extends Tool { telegram: parsedInput.telegram, website: parsedInput.website, initialLiquiditySOL: parsedInput.initialLiquiditySOL, - } + }, ); return JSON.stringify({ @@ -568,6 +570,57 @@ export class SolanaTPSCalculatorTool extends Tool { } } +export class SolanaTokenDataTool extends Tool { + name = "solana_token_data"; + description = `Get the token data for a given token mint address, token name or symbol. + + Inputs: Either one of mintAddress, tokenName or symbol is required. + mintAddress: string, eg "So11111111111111111111111111111111111111112" (optional) + tokenName: string, eg "USD Coin" (optional) + symbol: string, eg "USDC" (optional)`; + + constructor(private solanaKit: SolanaAgentKit) { + super(); + } + + private validateInput(input: any): void { + if (!input.mintAddress && !input.tokenName && !input.symbol) { + throw new Error("Either mintAddress, tokenName or symbol is required"); + } + if ( + input.mintAddress && + typeof input.mintAddress !== "string" && + !PublicKey.isOnCurve(input.mintAddress) + ) { + throw new Error("mintAddress must be a valid base58 string"); + } + } + + protected async _call(input: string): Promise { + try { + const parsedInput = JSON.parse(input); + this.validateInput(parsedInput); + + const tokenData = await this.solanaKit.getTokenData( + parsedInput.tokenName, + parsedInput.symbol, + new PublicKey(parsedInput.mintAddress), + ); + + return JSON.stringify({ + status: "success", + tokenData: tokenData, + }); + } catch (error: any) { + return JSON.stringify({ + status: "error", + message: error.message, + code: error.code || "UNKNOWN_ERROR", + }); + } + } +} + export function createSolanaTools(solanaKit: SolanaAgentKit) { return [ new SolanaBalanceTool(solanaKit), @@ -583,5 +636,6 @@ export function createSolanaTools(solanaKit: SolanaAgentKit) { new SolanaCreateImageTool(solanaKit), new SolanaLendAssetTool(solanaKit), new SolanaTPSCalculatorTool(solanaKit), + new SolanaTokenDataTool(solanaKit), ]; } diff --git a/src/tools/get_token_data.ts b/src/tools/get_token_data.ts index d284d54..ed8769e 100644 --- a/src/tools/get_token_data.ts +++ b/src/tools/get_token_data.ts @@ -1,10 +1,16 @@ import { PublicKey } from "@solana/web3.js"; import { JupiterTokenData } from "../types"; -export async function fetchTokenDataByMint( - mint: PublicKey, +export async function fetchTokenData( + name?: string, + symbol?: string, + mint?: PublicKey, ): Promise { try { + if (!mint && !symbol && !name) { + throw new Error("Either mint address, name or symbol is required"); + } + const response = await fetch("https://tokens.jup.ag/tokens?tags=verified", { method: "GET", headers: { @@ -13,30 +19,20 @@ export async function fetchTokenDataByMint( }); const data = (await response.json()) as JupiterTokenData[]; - const token = data.find( - (token: JupiterTokenData) => token.address === mint.toString(), - ); + const token = data.find((token: JupiterTokenData) => { + if (mint) { + return token.address === mint.toBase58(); + } + if (symbol) { + return token.symbol === symbol; + } + if (name) { + return token.name === name; + } + return false; + }); return token; } catch (error: any) { throw new Error(`Error fetching token data: ${error.message}`); } } - -export async function fetchTokenDataByName( - name: string, -): Promise { - try { - const response = await fetch("https://tokens.jup.ag/tokens?tags=verified", { - method: "GET", - headers: { - "Content-Type": "application/json", - }, - }); - - const data = (await response.json()) as JupiterTokenData[]; - const token = data.find((token: JupiterTokenData) => token.name === name); - return token; - } catch (error) { - throw new Error(`Error fetching token data: ${error.message}`); - } -} From 40501226fa778b2fc3497251c06cdfe7c7cac223 Mon Sep 17 00:00:00 2001 From: fm2055 <48504961+fm2055@users.noreply.github.com> Date: Mon, 16 Dec 2024 03:23:30 +0000 Subject: [PATCH 04/11] feat: add resolve_sol_domain --- src/tools/resolve_sol_domain.ts | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 src/tools/resolve_sol_domain.ts diff --git a/src/tools/resolve_sol_domain.ts b/src/tools/resolve_sol_domain.ts new file mode 100644 index 0000000..607d0a1 --- /dev/null +++ b/src/tools/resolve_sol_domain.ts @@ -0,0 +1,30 @@ +import { resolve } from "@bonfida/spl-name-service"; +import { PublicKey } from "@solana/web3.js"; +import { SolanaAgentKit } from "../index"; + +/** + * Resolves a .sol domain to a Solana PublicKey. + * + * This function uses the Bonfida SPL Name Service to resolve a given .sol domain + * to the corresponding Solana PublicKey. The domain can be provided with or without + * the .sol suffix. + * + * @param agent SolanaAgentKit 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 resolve_sol_domain( + agent: SolanaAgentKit, + domain: string +): Promise { + if (!domain || typeof domain !== "string") { + throw new Error("Invalid domain. Expected a non-empty string."); + } + + try { + return await resolve(agent.connection, domain); + } catch (error) { + throw new Error(`Failed to resolve domain: ${domain}`); + } +} From 8927ac4130e9e38be48316f3015c6212bdee92bc Mon Sep 17 00:00:00 2001 From: fm2055 <48504961+fm2055@users.noreply.github.com> Date: Mon, 16 Dec 2024 03:23:17 +0000 Subject: [PATCH 05/11] feat: add get_primary_domain --- src/tools/get_primary_domain.ts | 37 +++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 src/tools/get_primary_domain.ts diff --git a/src/tools/get_primary_domain.ts b/src/tools/get_primary_domain.ts new file mode 100644 index 0000000..1fe3892 --- /dev/null +++ b/src/tools/get_primary_domain.ts @@ -0,0 +1,37 @@ +import { getPrimaryDomain } from "@bonfida/spl-name-service"; +import { PublicKey } from "@solana/web3.js"; +import { SolanaAgentKit } from "../index"; + +/** + * Retrieves the primary .sol domain associated with a given Solana public key. + * + * This function queries the Bonfida SPL Name Service to get the primary .sol domain for + * 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 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 get_primary_domain( + agent: SolanaAgentKit, + account: PublicKey +): Promise { + try { + const { reverse, stale } = await getPrimaryDomain( + agent.connection, + account + ); + if (stale) { + throw new Error( + `Primary domain is stale for account: ${account.toBase58()}` + ); + } + return reverse; + } catch (error) { + throw new Error( + `Failed to get primary domain for account: ${account.toBase58()}` + ); + } +} From ff8cb4b842bbdfa4f63dcbe18560ddc5ef204446 Mon Sep 17 00:00:00 2001 From: fm2055 <48504961+fm2055@users.noreply.github.com> Date: Mon, 16 Dec 2024 23:00:31 +0800 Subject: [PATCH 06/11] feat: langchain integration --- src/agent/index.ts | 10 ++++ src/langchain/index.ts | 87 +++++++++++++++++++++++++++++++++ src/tools/get_primary_domain.ts | 6 +-- src/tools/index.ts | 2 + src/tools/resolve_sol_domain.ts | 2 +- 5 files changed, 103 insertions(+), 4 deletions(-) diff --git a/src/agent/index.ts b/src/agent/index.ts index 23af3ad..c5cbf2b 100644 --- a/src/agent/index.ts +++ b/src/agent/index.ts @@ -9,6 +9,8 @@ import { transfer, trade, registerDomain, + resolveSolDomain, + getPrimaryDomain, launchPumpFunToken, lendAsset, getTPS, @@ -79,6 +81,14 @@ export class SolanaAgentKit { return registerDomain(this, name, spaceKB); } + async resolveSolDomain(domain:string ){ + return resolveSolDomain(this, domain) + } + + async getPrimaryDomain(account: PublicKey){ + return getPrimaryDomain(this, account) + } + async trade( outputMint: PublicKey, inputAmount: number, diff --git a/src/langchain/index.ts b/src/langchain/index.ts index c4d506b..89b3864 100644 --- a/src/langchain/index.ts +++ b/src/langchain/index.ts @@ -390,6 +390,93 @@ export class SolanaRegisterDomainTool extends Tool { } } +export class SolanaResolveDomainTool extends Tool { + name = "solana_resolve_domain"; + description = `Resolve a .sol domain to a Solana PublicKey. + + Inputs: + domain: string, eg "pumpfun.sol" or "pumpfun"(required) + `; + + constructor(private solanaKit: SolanaAgentKit) { + super(); + } + + private validateInput(input: any): void { + if (!input.domain || typeof input.domain !== "string") { + throw new Error("domain is required and must be a string"); + } + } + + protected async _call(input: string): Promise { + try { + const parsedInput = JSON.parse(input); + this.validateInput(parsedInput); + + const publicKey = await this.solanaKit.resolveSolDomain(parsedInput.domain); + + return JSON.stringify({ + status: "success", + message: "Domain resolved successfully", + publicKey: publicKey.toBase58(), + }); + } catch (error: any) { + return JSON.stringify({ + status: "error", + message: error.message, + code: error.code || "UNKNOWN_ERROR", + }); + } + } +} + + +export class SolanaGetPrimaryDomainTool extends Tool { + name = "solana_get_primary_domain"; + description = `Retrieve the primary .sol domain associated with a given Solana public key. + + Inputs: + account: string, eg "4Be9CvxqHW6BYiRAxW9Q3xu1ycTMWaL5z8NX4HR3ha7t" (required) + `; + + constructor(private solanaKit: SolanaAgentKit) { + super(); + } + + private validateInput(input: any): void { + if (!input.account || typeof input.account !== "string") { + throw new Error("account is required and must be a string"); + } + try { + new PublicKey(input.account); + } catch { + throw new Error("account is not a valid public key"); + } + } + + protected async _call(input: string): Promise { + try { + const parsedInput = JSON.parse(input); + this.validateInput(parsedInput); + + const account = new PublicKey(parsedInput.account); + const domain = await this.solanaKit.getPrimaryDomain(account); + + return JSON.stringify({ + status: "success", + message: "Primary domain retrieved successfully", + domain, + }); + } catch (error: any) { + return JSON.stringify({ + status: "error", + message: error.message, + code: error.code || "UNKNOWN_ERROR", + }); + } + } +} + export class SolanaGetWalletAddressTool extends Tool { name = "solana_get_wallet_address"; description = `Get the wallet address of the agent`; diff --git a/src/tools/get_primary_domain.ts b/src/tools/get_primary_domain.ts index 1fe3892..775af4b 100644 --- a/src/tools/get_primary_domain.ts +++ b/src/tools/get_primary_domain.ts @@ -1,4 +1,4 @@ -import { getPrimaryDomain } from "@bonfida/spl-name-service"; +import { getPrimaryDomain as _getPrimaryDomain } from "@bonfida/spl-name-service"; import { PublicKey } from "@solana/web3.js"; import { SolanaAgentKit } from "../index"; @@ -14,12 +14,12 @@ import { SolanaAgentKit } from "../index"; * @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 get_primary_domain( +export async function getPrimaryDomain( agent: SolanaAgentKit, account: PublicKey ): Promise { try { - const { reverse, stale } = await getPrimaryDomain( + const { reverse, stale } = await _getPrimaryDomain( agent.connection, account ); diff --git a/src/tools/index.ts b/src/tools/index.ts index dca3c04..8d7576e 100644 --- a/src/tools/index.ts +++ b/src/tools/index.ts @@ -6,6 +6,8 @@ export * from "./mint_nft"; export * from "./transfer"; export * from "./trade"; export * from "./register_domain"; +export * from "./resolve_sol_domain"; +export * from "./get_primary_domain"; export * from "./launch_pumpfun_token"; export * from "./lend"; export * from "./get_tps"; diff --git a/src/tools/resolve_sol_domain.ts b/src/tools/resolve_sol_domain.ts index 607d0a1..d8764cc 100644 --- a/src/tools/resolve_sol_domain.ts +++ b/src/tools/resolve_sol_domain.ts @@ -14,7 +14,7 @@ import { SolanaAgentKit } from "../index"; * @returns A promise that resolves to the corresponding Solana PublicKey * @throws Error if the domain resolution fails */ -export async function resolve_sol_domain( +export async function resolveSolDomain( agent: SolanaAgentKit, domain: string ): Promise { From b078cc3b3787d9de7da6e8bae1dc901db6f8e41c Mon Sep 17 00:00:00 2001 From: aryan Date: Thu, 19 Dec 2024 05:36:03 +0530 Subject: [PATCH 07/11] fix: token address prompt --- src/agent/index.ts | 23 +++++++------ src/langchain/index.ts | 30 ++++------------- src/tools/get_token_data.ts | 64 +++++++++++++++++++++++++++---------- 3 files changed, 68 insertions(+), 49 deletions(-) diff --git a/src/agent/index.ts b/src/agent/index.ts index 80b1516..d640eea 100644 --- a/src/agent/index.ts +++ b/src/agent/index.ts @@ -12,7 +12,8 @@ import { launchPumpFunToken, lendAsset, getTPS, - fetchTokenData, + getTokenDataByAddress, + getTokenDataByTicker, } from "../tools"; import { CollectionOptions, PumpFunTokenOptions } from "../types"; import { DEFAULT_OPTIONS } from "../constants"; @@ -35,7 +36,7 @@ export class SolanaAgentKit { constructor( private_key: string, rpc_url = "https://api.mainnet-beta.solana.com", - openai_api_key: string, + openai_api_key: string ) { this.connection = new Connection(rpc_url); this.wallet = Keypair.fromSecretKey(bs58.decode(private_key)); @@ -49,7 +50,7 @@ export class SolanaAgentKit { } async deployToken( - decimals: number = DEFAULT_OPTIONS.TOKEN_DECIMALS, + decimals: number = DEFAULT_OPTIONS.TOKEN_DECIMALS // initialSupply?: number ) { return deploy_token(this, decimals); @@ -66,7 +67,7 @@ export class SolanaAgentKit { async mintNFT( collectionMint: PublicKey, metadata: Parameters[2], - recipient?: PublicKey, + recipient?: PublicKey ) { return mintCollectionNFT(this, collectionMint, metadata, recipient); } @@ -83,7 +84,7 @@ export class SolanaAgentKit { outputMint: PublicKey, inputAmount: number, inputMint?: PublicKey, - slippageBps: number = DEFAULT_OPTIONS.SLIPPAGE_BPS, + slippageBps: number = DEFAULT_OPTIONS.SLIPPAGE_BPS ) { return trade(this, outputMint, inputAmount, inputMint, slippageBps); } @@ -96,8 +97,12 @@ export class SolanaAgentKit { return getTPS(this); } - async getTokenData(name?: string, symbol?: string, mint?: PublicKey) { - return fetchTokenData(name, symbol, mint); + async getTokenDataByAddress(mint: string) { + return getTokenDataByAddress(new PublicKey(mint)); + } + + async getTokenDataByTicker(ticker: string) { + return getTokenDataByTicker(ticker); } async launchPumpFunToken( @@ -105,7 +110,7 @@ export class SolanaAgentKit { tokenTicker: string, description: string, imageUrl: string, - options?: PumpFunTokenOptions, + options?: PumpFunTokenOptions ) { return launchPumpFunToken( this, @@ -113,7 +118,7 @@ export class SolanaAgentKit { tokenTicker, description, imageUrl, - options, + options ); } } diff --git a/src/langchain/index.ts b/src/langchain/index.ts index 3d4896d..fb4f94a 100644 --- a/src/langchain/index.ts +++ b/src/langchain/index.ts @@ -572,39 +572,21 @@ export class SolanaTPSCalculatorTool extends Tool { export class SolanaTokenDataTool extends Tool { name = "solana_token_data"; - description = `Get the token data for a given token mint address, token name or symbol. + description = `Get the token data for a given token mint address - Inputs: Either one of mintAddress, tokenName or symbol is required. - mintAddress: string, eg "So11111111111111111111111111111111111111112" (optional) - tokenName: string, eg "USD Coin" (optional) - symbol: string, eg "USDC" (optional)`; + Inputs: mintAddress is required. + mintAddress: string, eg "So11111111111111111111111111111111111111112" (required)`; constructor(private solanaKit: SolanaAgentKit) { super(); } - private validateInput(input: any): void { - if (!input.mintAddress && !input.tokenName && !input.symbol) { - throw new Error("Either mintAddress, tokenName or symbol is required"); - } - if ( - input.mintAddress && - typeof input.mintAddress !== "string" && - !PublicKey.isOnCurve(input.mintAddress) - ) { - throw new Error("mintAddress must be a valid base58 string"); - } - } - protected async _call(input: string): Promise { try { - const parsedInput = JSON.parse(input); - this.validateInput(parsedInput); + const parsedInput = input.trim(); - const tokenData = await this.solanaKit.getTokenData( - parsedInput.tokenName, - parsedInput.symbol, - new PublicKey(parsedInput.mintAddress), + const tokenData = await this.solanaKit.getTokenDataByAddress( + parsedInput ); return JSON.stringify({ diff --git a/src/tools/get_token_data.ts b/src/tools/get_token_data.ts index ed8769e..31d6032 100644 --- a/src/tools/get_token_data.ts +++ b/src/tools/get_token_data.ts @@ -1,14 +1,12 @@ import { PublicKey } from "@solana/web3.js"; import { JupiterTokenData } from "../types"; -export async function fetchTokenData( - name?: string, - symbol?: string, - mint?: PublicKey, +export async function getTokenDataByAddress( + mint: PublicKey, ): Promise { try { - if (!mint && !symbol && !name) { - throw new Error("Either mint address, name or symbol is required"); + if (!mint) { + throw new Error("Mint address is required"); } const response = await fetch("https://tokens.jup.ag/tokens?tags=verified", { @@ -20,19 +18,53 @@ export async function fetchTokenData( const data = (await response.json()) as JupiterTokenData[]; const token = data.find((token: JupiterTokenData) => { - if (mint) { - return token.address === mint.toBase58(); - } - if (symbol) { - return token.symbol === symbol; - } - if (name) { - return token.name === name; - } - return false; + return token.address === mint.toBase58(); }); return token; } catch (error: any) { throw new Error(`Error fetching token data: ${error.message}`); } } + +export async function getTokenAddressFromTicker( + ticker: string +): Promise { + try { + const response = await fetch( + `https://api.dexscreener.com/latest/dex/search?q=${ticker}` + ); + const data = await response.json(); + + if (!data.pairs || data.pairs.length === 0) { + return null; + } + + // Filter for Solana pairs only and sort by FDV + let solanaPairs = data.pairs + .filter((pair: any) => pair.chainId === "solana") + .sort((a: any, b: any) => (b.fdv || 0) - (a.fdv || 0)); + + console.log("solanaPairs", solanaPairs); + + solanaPairs = solanaPairs.filter( + (pair: any) => + pair.baseToken.symbol.toLowerCase() === ticker.toLowerCase() + ); + + // Return the address of the highest FDV Solana pair + return solanaPairs[0].baseToken.address; + } catch (error) { + console.error("Error fetching token address from DexScreener:", error); + return null; + } +} + +export async function getTokenDataByTicker( + ticker: string +): Promise { + const address = await getTokenAddressFromTicker(ticker); + if (!address) { + throw new Error(`Token address not found for ticker: ${ticker}`); + } + return getTokenDataByAddress(new PublicKey(address)); +} \ No newline at end of file From 1ff3d1de350dcc4477293593b21b104126d2ac5b Mon Sep 17 00:00:00 2001 From: aryan Date: Thu, 19 Dec 2024 05:38:00 +0530 Subject: [PATCH 08/11] feat: by ticker name --- src/langchain/index.ts | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/langchain/index.ts b/src/langchain/index.ts index fb4f94a..82ff1d7 100644 --- a/src/langchain/index.ts +++ b/src/langchain/index.ts @@ -603,6 +603,36 @@ export class SolanaTokenDataTool extends Tool { } } +export class SolanaTokenDataByTickerTool extends Tool { + name = "solana_token_data_by_ticker"; + description = `Get the token data for a given token ticker + + Inputs: ticker is required. + ticker: string, eg "USDC" (required)`; + + constructor(private solanaKit: SolanaAgentKit) { + super(); + } + + protected async _call(input: string): Promise { + try { + console.log(input); + const ticker = input.trim(); + const tokenData = await this.solanaKit.getTokenDataByTicker(ticker); + return JSON.stringify({ + status: "success", + tokenData: tokenData, + }); + } catch (error: any) { + return JSON.stringify({ + status: "error", + message: error.message, + code: error.code || "UNKNOWN_ERROR", + }); + } + } +} + export function createSolanaTools(solanaKit: SolanaAgentKit) { return [ new SolanaBalanceTool(solanaKit), @@ -619,5 +649,6 @@ export function createSolanaTools(solanaKit: SolanaAgentKit) { new SolanaLendAssetTool(solanaKit), new SolanaTPSCalculatorTool(solanaKit), new SolanaTokenDataTool(solanaKit), + new SolanaTokenDataByTickerTool(solanaKit), ]; } From 46058bc8db2328c732596e0e73e641c9995f5bf0 Mon Sep 17 00:00:00 2001 From: aryan Date: Thu, 19 Dec 2024 05:38:28 +0530 Subject: [PATCH 09/11] chore --- src/langchain/index.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/langchain/index.ts b/src/langchain/index.ts index 82ff1d7..820476e 100644 --- a/src/langchain/index.ts +++ b/src/langchain/index.ts @@ -54,7 +54,6 @@ export class SolanaTransferTool extends Tool { protected async _call(input: string): Promise { try { const parsedInput = JSON.parse(input); - console.log(parsedInput); const recipient = new PublicKey(parsedInput.to); const mintAddress = parsedInput.mint @@ -304,7 +303,6 @@ export class SolanaTradeTool extends Tool { outputToken: parsedInput.outputMint, }); } catch (error: any) { - console.log(error); return JSON.stringify({ status: "error", message: error.message, @@ -425,7 +423,6 @@ export class SolanaPumpfunTokenLaunchTool extends Tool { } private validateInput(input: any): void { - console.log(input); if (!input.tokenName || typeof input.tokenName !== "string") { throw new Error("tokenName is required and must be a string"); } @@ -616,7 +613,6 @@ export class SolanaTokenDataByTickerTool extends Tool { protected async _call(input: string): Promise { try { - console.log(input); const ticker = input.trim(); const tokenData = await this.solanaKit.getTokenDataByTicker(ticker); return JSON.stringify({ From 47db362f0b0849f9a160dbe896c1511fc2d4ded8 Mon Sep 17 00:00:00 2001 From: aryan Date: Thu, 19 Dec 2024 05:57:28 +0530 Subject: [PATCH 10/11] fix: parsing --- src/langchain/index.ts | 37 +++++++++---------------------------- 1 file changed, 9 insertions(+), 28 deletions(-) diff --git a/src/langchain/index.ts b/src/langchain/index.ts index 89b3864..7f9062c 100644 --- a/src/langchain/index.ts +++ b/src/langchain/index.ts @@ -402,18 +402,10 @@ export class SolanaResolveDomainTool extends Tool { super(); } - private validateInput(input: any): void { - if (!input.domain || typeof input.domain !== "string") { - throw new Error("domain is required and must be a string"); - } - } - protected async _call(input: string): Promise { try { - const parsedInput = JSON.parse(input); - this.validateInput(parsedInput); - - const publicKey = await this.solanaKit.resolveSolDomain(parsedInput.domain); + const domain = input.trim(); + const publicKey = await this.solanaKit.resolveSolDomain(domain); return JSON.stringify({ status: "success", @@ -431,9 +423,9 @@ export class SolanaResolveDomainTool extends Tool { } -export class SolanaGetPrimaryDomainTool extends Tool { - name = "solana_get_primary_domain"; - description = `Retrieve the primary .sol domain associated with a given Solana public key. +export class SolanaGetDomainTool extends Tool { + name = "solana_get_domain"; + description = `Retrieve the .sol domain associated for a given account address. Inputs: account: string, eg "4Be9CvxqHW6BYiRAxW9Q3xu1ycTMWaL5z8NX4HR3ha7t" (required) @@ -443,25 +435,12 @@ export class SolanaGetPrimaryDomainTool extends Tool { super(); } - private validateInput(input: any): void { - if (!input.account || typeof input.account !== "string") { - throw new Error("account is required and must be a string"); - } - try { - new PublicKey(input.account); - } catch { - throw new Error("account is not a valid public key"); - } - } protected async _call(input: string): Promise { try { - const parsedInput = JSON.parse(input); - this.validateInput(parsedInput); - - const account = new PublicKey(parsedInput.account); + const account = new PublicKey(input.trim()); const domain = await this.solanaKit.getPrimaryDomain(account); - + return JSON.stringify({ status: "success", message: "Primary domain retrieved successfully", @@ -735,5 +714,7 @@ export function createSolanaTools(solanaKit: SolanaAgentKit) { new SolanaTPSCalculatorTool(solanaKit), new SolanaStakeTool(solanaKit), new SolanaFetchPriceTool(solanaKit), + new SolanaResolveDomainTool(solanaKit), + new SolanaGetDomainTool(solanaKit), ]; } From d80dd49d719ee23c7a8a20d6b46d1d0de1fa9f6c Mon Sep 17 00:00:00 2001 From: ARYAN <48391385+thearyanag@users.noreply.github.com> Date: Thu, 19 Dec 2024 06:13:43 +0530 Subject: [PATCH 11/11] Create CNAME --- docs/CNAME | 1 + 1 file changed, 1 insertion(+) create mode 100644 docs/CNAME diff --git a/docs/CNAME b/docs/CNAME new file mode 100644 index 0000000..2708636 --- /dev/null +++ b/docs/CNAME @@ -0,0 +1 @@ +solanaagentkit.xyz \ No newline at end of file