This commit is contained in:
Blockiosaurus
2024-12-18 19:56:04 -05:00
22 changed files with 380 additions and 46 deletions

View File

@@ -9,9 +9,13 @@ import {
transfer,
trade,
registerDomain,
resolveSolDomain,
getPrimaryDomain,
launchPumpFunToken,
lendAsset,
getTPS,
getTokenDataByAddress,
getTokenDataByTicker,
stakeWithJup,
} from "../tools";
import { CollectionOptions, PumpFunTokenOptions } from "../types";
@@ -35,7 +39,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));
@@ -69,7 +73,7 @@ export class SolanaAgentKit {
async mintNFT(
collectionMint: PublicKey,
metadata: Parameters<typeof mintCollectionNFT>[2],
recipient?: PublicKey,
recipient?: PublicKey
) {
return mintCollectionNFT(this, collectionMint, metadata, recipient);
}
@@ -82,11 +86,19 @@ 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,
inputMint?: PublicKey,
slippageBps: number = DEFAULT_OPTIONS.SLIPPAGE_BPS,
slippageBps: number = DEFAULT_OPTIONS.SLIPPAGE_BPS
) {
return trade(this, outputMint, inputAmount, inputMint, slippageBps);
}
@@ -99,12 +111,20 @@ export class SolanaAgentKit {
return getTPS(this);
}
async getTokenDataByAddress(mint: string) {
return getTokenDataByAddress(new PublicKey(mint));
}
async getTokenDataByTicker(ticker: string) {
return getTokenDataByTicker(ticker);
}
async launchPumpFunToken(
tokenName: string,
tokenTicker: string,
description: string,
imageUrl: string,
options?: PumpFunTokenOptions,
options?: PumpFunTokenOptions
) {
return launchPumpFunToken(
this,
@@ -112,10 +132,10 @@ export class SolanaAgentKit {
tokenTicker,
description,
imageUrl,
options,
options
);
}
async stake(
amount: number,
) {

View File

@@ -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({
@@ -370,7 +372,7 @@ export class SolanaRegisterDomainTool extends Tool {
const tx = await this.solanaKit.registerDomain(
parsedInput.name,
parsedInput.spaceKB || 1
parsedInput.spaceKB || 1,
);
return JSON.stringify({
@@ -390,6 +392,72 @@ export class SolanaRegisterDomainTool extends Tool {
}
}
export class SolanaResolveDomainTool extends Tool {
name = "solana_resolve_domain";
description = `Resolve a .sol domain to a Solana PublicKey.
Inputs:
domain: string, eg "pumpfun.sol" or "pumpfun"(required)
`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
protected async _call(input: string): Promise<string> {
try {
const domain = input.trim();
const publicKey = await this.solanaKit.resolveSolDomain(domain);
return JSON.stringify({
status: "success",
message: "Domain resolved successfully",
publicKey: publicKey.toBase58(),
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
code: error.code || "UNKNOWN_ERROR",
});
}
}
}
export class SolanaGetDomainTool extends Tool {
name = "solana_get_domain";
description = `Retrieve the .sol domain associated for a given account address.
Inputs:
account: string, eg "4Be9CvxqHW6BYiRAxW9Q3xu1ycTMWaL5z8NX4HR3ha7t" (required)
`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
protected async _call(input: string): Promise<string> {
try {
const account = new PublicKey(input.trim());
const domain = await this.solanaKit.getPrimaryDomain(account);
return JSON.stringify({
status: "success",
message: "Primary domain retrieved successfully",
domain,
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
code: error.code || "UNKNOWN_ERROR",
});
}
}
}
export class SolanaGetWalletAddressTool extends Tool {
name = "solana_get_wallet_address";
description = `Get the wallet address of the agent`;
@@ -461,7 +529,7 @@ export class SolanaPumpfunTokenLaunchTool extends Tool {
telegram: parsedInput.telegram,
website: parsedInput.website,
initialLiquiditySOL: parsedInput.initialLiquiditySOL,
}
},
);
return JSON.stringify({
@@ -631,6 +699,68 @@ export class SolanaFetchPriceTool extends Tool {
}
}
export class SolanaTokenDataTool extends Tool {
name = "solana_token_data";
description = `Get the token data for a given token mint address
Inputs: mintAddress is required.
mintAddress: string, eg "So11111111111111111111111111111111111111112" (required)`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
protected async _call(input: string): Promise<string> {
try {
const parsedInput = input.trim();
const tokenData = await this.solanaKit.getTokenDataByAddress(
parsedInput
);
return JSON.stringify({
status: "success",
tokenData: tokenData,
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
code: error.code || "UNKNOWN_ERROR",
});
}
}
}
export class SolanaTokenDataByTickerTool extends Tool {
name = "solana_token_data_by_ticker";
description = `Get the token data for a given token ticker
Inputs: ticker is required.
ticker: string, eg "USDC" (required)`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
protected async _call(input: string): Promise<string> {
try {
const ticker = input.trim();
const tokenData = await this.solanaKit.getTokenDataByTicker(ticker);
return JSON.stringify({
status: "success",
tokenData: tokenData,
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
code: error.code || "UNKNOWN_ERROR",
});
}
}
}
export function createSolanaTools(solanaKit: SolanaAgentKit) {
return [
new SolanaBalanceTool(solanaKit),
@@ -648,5 +778,9 @@ export function createSolanaTools(solanaKit: SolanaAgentKit) {
new SolanaTPSCalculatorTool(solanaKit),
new SolanaStakeTool(solanaKit),
new SolanaFetchPriceTool(solanaKit),
new SolanaResolveDomainTool(solanaKit),
new SolanaGetDomainTool(solanaKit),
new SolanaTokenDataTool(solanaKit),
new SolanaTokenDataByTickerTool(solanaKit),
];
}

View File

@@ -0,0 +1,37 @@
import { getPrimaryDomain as _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 getPrimaryDomain(
agent: SolanaAgentKit,
account: PublicKey
): Promise<string> {
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()}`
);
}
}

View File

@@ -0,0 +1,70 @@
import { PublicKey } from "@solana/web3.js";
import { JupiterTokenData } from "../types";
export async function getTokenDataByAddress(
mint: PublicKey,
): Promise<JupiterTokenData | undefined> {
try {
if (!mint) {
throw new Error("Mint address is required");
}
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) => {
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<string | null> {
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<JupiterTokenData | undefined> {
const address = await getTokenAddressFromTicker(ticker);
if (!address) {
throw new Error(`Token address not found for ticker: ${ticker}`);
}
return getTokenDataByAddress(new PublicKey(address));
}

View File

@@ -6,8 +6,11 @@ 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";
export * from "./get_token_data";
export * from './stake_with_jup';
export * from "./fetch_price";
export * from "./fetch_price";

View File

@@ -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 resolveSolDomain(
agent: SolanaAgentKit,
domain: string
): Promise<PublicKey> {
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}`);
}
}

View File

@@ -39,7 +39,6 @@ export interface PumpfunLaunchResponse {
error?: string;
}
/**
* Lulo Account Details response format
*/
@@ -55,6 +54,22 @@ export interface LuloAccountDetailsResponse {
};
}
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;
};
}
export interface FetchPriceResponse {
status: "success" | "error";
tokenId?: string;