Merge remote-tracking branch 'upstream/main'

This commit is contained in:
JoshuaSum
2025-01-03 14:35:16 -08:00
55 changed files with 3670 additions and 440 deletions

View File

@@ -2,7 +2,6 @@ import { Action } from "../types/action";
import { SolanaAgentKit } from "../agent";
import { z } from "zod";
import { PublicKey } from "@solana/web3.js";
import { BN } from "@coral-xyz/anchor";
import { Decimal } from "decimal.js";
import { orcaCreateSingleSidedLiquidityPool } from "../tools";
@@ -87,7 +86,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(

View File

@@ -0,0 +1,29 @@
import { z } from "zod";
import { SolanaAgentKit } from "..";
import { get_wallet_address } from "../tools";
import { Action } from "../types/action";
const getWalletAddressAction: Action = {
name: "GET_WALLET_ADDRESS",
similes: ["wallet address", "address", "wallet"],
description: "Get wallet address of the agent",
examples: [
[
{
input: {},
output: {
status: "success",
address: "0x1234567890abcdef",
},
explanation: "The agent's wallet address is 0x1234567890abcdef",
},
],
],
schema: z.object({}),
handler: async (agent: SolanaAgentKit) => ({
status: "success",
address: get_wallet_address(agent),
}),
};
export default getWalletAddressAction;

View File

@@ -27,37 +27,40 @@ import raydiumCreateCpmmAction from "./raydiumCreateCpmm";
import raydiumCreateAmmV4Action from "./raydiumCreateAmmV4";
import createOrcaSingleSidedWhirlpoolAction from "./createOrcaSingleSidedWhirlpool";
import launchPumpfunTokenAction from "./launchPumpfunToken";
import getWalletAddressAction from "./getWalletAddress";
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,
"STAKE_WITH_SOLAYER_ACTION" : stakeWithSolayerAction,
"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,
WALLET_ADDRESS_ACTION: getWalletAddressAction,
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,
STAKE_WITH_SOLAYER_ACTION : stakeWithSolayerAction,
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";

View File

@@ -2,7 +2,7 @@ import { Connection, Keypair, PublicKey } from "@solana/web3.js";
import bs58 from "bs58";
import Decimal from "decimal.js";
import { DEFAULT_OPTIONS } from "../constants";
import { Config } from "../types";
import { Config, TokenCheck } from "../types";
import {
deploy_collection,
deploy_token,
@@ -23,6 +23,7 @@ import {
request_faucet_funds,
trade,
limitOrder,
batchOrder,
cancelAllOrders,
withdrawAll,
transfer,
@@ -51,6 +52,9 @@ import {
create_TipLink,
listNFTForSale,
cancelListing,
fetchTokenReportSummary,
fetchTokenDetailedReport,
OrderParams,
} from "../tools";
import {
@@ -84,24 +88,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;
}
@@ -185,6 +195,13 @@ export class SolanaAgentKit {
return limitOrder(this, marketId, quantity, side, price);
}
async batchOrder(
marketId: PublicKey,
orders: OrderParams[],
): Promise<string> {
return batchOrder(this, marketId, orders);
}
async cancelAllOrders(marketId: PublicKey): Promise<string> {
return cancelAllOrders(this, marketId);
}
@@ -472,4 +489,12 @@ export class SolanaAgentKit {
async tensorCancelListing(nftMint: PublicKey): Promise<string> {
return cancelListing(this, nftMint);
}
async fetchTokenReportSummary(mint: string): Promise<TokenCheck> {
return fetchTokenReportSummary(mint);
}
async fetchTokenDetailedReport(mint: string): Promise<TokenCheck> {
return fetchTokenDetailedReport(mint);
}
}

View File

@@ -1,7 +1,8 @@
import { SolanaAgentKit } from "./agent";
import { createSolanaTools } from "./langchain";
import { createSolanaTools as createVercelAITools } from "./vercel-ai";
export { SolanaAgentKit, createSolanaTools };
export { SolanaAgentKit, createSolanaTools, createVercelAITools };
// Optional: Export types that users might need
export * from "./types";

View File

@@ -8,7 +8,7 @@ import {
} from "../index";
import { create_image } from "../tools/create_image";
import { BN } from "@coral-xyz/anchor";
import { FEE_TIERS } from "../tools";
import { FEE_TIERS, generateOrdersfromPattern, OrderParams } from "../tools";
export class SolanaBalanceTool extends Tool {
name = "solana_balance";
@@ -310,6 +310,8 @@ export class SolanaLimitOrderTool extends Tool {
name = "solana_limit_order";
description = `This tool can be used to place limit orders using Manifest.
Do not allow users to place multiple orders with this instruction, use solana_batch_order instead.
Inputs ( input is a JSON string ):
marketId: PublicKey, eg "ENhU8LsaR7vDD2G1CsWcsuSGNrih9Cv5WZEk7q9kPapQ" for SOL/USDC (required)
quantity: number, eg 1 or 0.01 (required)
@@ -350,6 +352,98 @@ export class SolanaLimitOrderTool extends Tool {
}
}
export class SolanaBatchOrderTool extends Tool {
name = "solana_batch_order";
description = `Places multiple limit orders in one transaction using Manifest. Submit orders either as a list or pattern:
1. List format:
{
"marketId": "ENhU8LsaR7vDD2G1CsWcsuSGNrih9Cv5WZEk7q9kPapQ",
"orders": [
{ "quantity": 1, "side": "Buy", "price": 200 },
{ "quantity": 0.5, "side": "Sell", "price": 205 }
]
}
2. Pattern format:
{
"marketId": "ENhU8LsaR7vDD2G1CsWcsuSGNrih9Cv5WZEk7q9kPapQ",
"pattern": {
"side": "Buy",
"totalQuantity": 100,
"priceRange": { "max": 1.0 },
"spacing": { "type": "percentage", "value": 1 },
"numberOfOrders": 5
}
}
Examples:
- "Place 5 buy orders totaling 100 tokens, 1% apart below $1"
- "Create 3 sell orders of 10 tokens each between $50-$55"
- "Place buy orders worth 50 tokens, $0.10 spacing from $0.80"
Important: All orders must be in one transaction. Combine buy and sell orders into a single pattern or list. Never break the orders down to individual buy or sell orders.`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
protected async _call(input: string): Promise<string> {
try {
const parsedInput = JSON.parse(input);
let ordersToPlace: OrderParams[] = [];
if (!parsedInput.marketId) {
throw new Error("Market ID is required");
}
if (parsedInput.pattern) {
ordersToPlace = generateOrdersfromPattern(parsedInput.pattern);
} else if (Array.isArray(parsedInput.orders)) {
ordersToPlace = parsedInput.orders;
} else {
throw new Error("Either pattern or orders array is required");
}
if (ordersToPlace.length === 0) {
throw new Error("No orders generated or provided");
}
ordersToPlace.forEach((order: OrderParams, index: number) => {
if (!order.quantity || !order.side || !order.price) {
throw new Error(
`Invalid order at index ${index}: quantity, side, and price are required`,
);
}
if (order.side !== "Buy" && order.side !== "Sell") {
throw new Error(
`Invalid side at index ${index}: must be "Buy" or "Sell"`,
);
}
});
const tx = await this.solanaKit.batchOrder(
new PublicKey(parsedInput.marketId),
parsedInput.orders,
);
return JSON.stringify({
status: "success",
message: "Batch order executed successfully",
transaction: tx,
marketId: parsedInput.marketId,
orders: parsedInput.orders,
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
code: error.code || "UNKNOWN_ERROR",
});
}
}
}
export class SolanaCancelAllOrdersTool extends Tool {
name = "solana_cancel_all_orders";
description = `This tool can be used to cancel all orders from a Manifest market.
@@ -941,7 +1035,7 @@ export class SolanaClosePosition extends Tool {
name = "orca_close_position";
description = `Closes an existing liquidity position in an Orca Whirlpool. This function fetches the position
details using the provided mint address and closes the position with a 1% slippage.
Inputs (JSON string):
- positionMintAddress: string, the address of the position mint that represents the liquidity position.`;
@@ -1028,9 +1122,9 @@ export class SolanaOrcaCreateCLMM extends Tool {
export class SolanaOrcaCreateSingleSideLiquidityPool extends Tool {
name = "orca_create_single_sided_liquidity_pool";
description = `Create a single-sided liquidity pool on Orca, the most efficient and capital-optimized CLMM platform on Solana.
description = `Create a single-sided liquidity pool on Orca, the most efficient and capital-optimized CLMM platform on Solana.
This function initializes a single-sided liquidity pool, ideal for community driven project, fair launches, and fundraising. Minimize price impact by setting a narrow price range.
This function initializes a single-sided liquidity pool, ideal for community driven project, fair launches, and fundraising. Minimize price impact by setting a narrow price range.
Inputs (JSON string):
- depositTokenAmount: number, in units of the deposit token including decimals, e.g., 1000000000 (required).
@@ -1858,6 +1952,67 @@ export class SolanaCancelNFTListingTool extends Tool {
}
}
export class SolanaFetchTokenReportSummaryTool extends Tool {
name = "solana_fetch_token_report_summary";
description = `Fetches a summary report for a specific token from RugCheck.
Inputs:
- mint: string, the mint address of the token, e.g., "JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN" (required).`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
protected async _call(input: string): Promise<string> {
try {
const mint = input.trim();
const report = await this.solanaKit.fetchTokenReportSummary(mint);
return JSON.stringify({
status: "success",
message: "Token report summary fetched successfully",
report,
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
code: error.code || "FETCH_TOKEN_REPORT_SUMMARY_ERROR",
});
}
}
}
export class SolanaFetchTokenDetailedReportTool extends Tool {
name = "solana_fetch_token_detailed_report";
description = `Fetches a detailed report for a specific token from RugCheck.
Inputs:
- mint: string, the mint address of the token, e.g., "JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN" (required).`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
protected async _call(input: string): Promise<string> {
try {
const mint = input.trim();
const detailedReport =
await this.solanaKit.fetchTokenDetailedReport(mint);
return JSON.stringify({
status: "success",
message: "Detailed token report fetched successfully",
report: detailedReport,
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
code: error.code || "FETCH_TOKEN_DETAILED_REPORT_ERROR",
});
}
}
}
export function createSolanaTools(solanaKit: SolanaAgentKit) {
return [
new SolanaBalanceTool(solanaKit),
@@ -1887,6 +2042,7 @@ export function createSolanaTools(solanaKit: SolanaAgentKit) {
new SolanaOpenbookCreateMarket(solanaKit),
new SolanaManifestCreateMarket(solanaKit),
new SolanaLimitOrderTool(solanaKit),
new SolanaBatchOrderTool(solanaKit),
new SolanaCancelAllOrdersTool(solanaKit),
new SolanaWithdrawAllTool(solanaKit),
new SolanaClosePosition(solanaKit),
@@ -1907,5 +2063,7 @@ export function createSolanaTools(solanaKit: SolanaAgentKit) {
new SolanaTipLinkTool(solanaKit),
new SolanaListNFTForSaleTool(solanaKit),
new SolanaCancelNFTListingTool(solanaKit),
new SolanaFetchTokenReportSummaryTool(solanaKit),
new SolanaFetchTokenDetailedReportTool(solanaKit),
];
}

172
src/tools/batch_order.ts Normal file
View File

@@ -0,0 +1,172 @@
import {
PublicKey,
Transaction,
sendAndConfirmTransaction,
TransactionInstruction,
} from "@solana/web3.js";
import { SolanaAgentKit } from "../index";
import {
ManifestClient,
WrapperPlaceOrderParamsExternal,
} from "@cks-systems/manifest-sdk";
import { OrderType } from "@cks-systems/manifest-sdk/client/ts/src/wrapper/types/OrderType";
export interface OrderParams {
quantity: number;
side: string;
price: number;
}
interface BatchOrderPattern {
side: string;
totalQuantity?: number;
priceRange?: {
min?: number;
max?: number;
};
spacing?: {
type: "percentage" | "fixed";
value: number;
};
numberOfOrders?: number;
individualQuantity?: number;
}
/**
* Generates an array of orders based on the specified pattern
*/
export function generateOrdersfromPattern(
pattern: BatchOrderPattern,
): OrderParams[] {
const orders: OrderParams[] = [];
// Random number of orders if not specified, max of 8
const numOrders = pattern.numberOfOrders || Math.ceil(Math.random() * 8);
// Calculate price points
const prices: number[] = [];
if (pattern.priceRange) {
const { min, max } = pattern.priceRange;
if (min && max) {
// Generate evenly spaced prices
for (let i = 0; i < numOrders; i++) {
if (pattern.spacing?.type === "percentage") {
const factor = 1 + pattern.spacing.value / 100;
prices.push(min * Math.pow(factor, i));
} else {
const step = (max - min) / (numOrders - 1);
prices.push(min + step * i);
}
}
} else if (min) {
// Generate prices starting from min with specified spacing
for (let i = 0; i < numOrders; i++) {
if (pattern.spacing?.type === "percentage") {
const factor = 1 + pattern.spacing.value / 100;
prices.push(min * Math.pow(factor, i));
} else {
prices.push(min + (pattern.spacing?.value || 0.01) * i);
}
}
}
}
// Calculate quantities
let quantities: number[] = [];
if (pattern.totalQuantity) {
const individualQty = pattern.totalQuantity / numOrders;
quantities = Array(numOrders).fill(individualQty);
} else if (pattern.individualQuantity) {
quantities = Array(numOrders).fill(pattern.individualQuantity);
}
// Generate orders
for (let i = 0; i < numOrders; i++) {
orders.push({
side: pattern.side,
price: prices[i],
quantity: quantities[i],
});
}
return orders;
}
/**
* Validates that sell orders are not priced below buy orders
* @param orders Array of order parameters to validate
* @throws Error if orders are crossed
*/
function validateNoCrossedOrders(orders: OrderParams[]): void {
// Find lowest sell and highest buy prices
let lowestSell = Number.MAX_SAFE_INTEGER;
let highestBuy = 0;
orders.forEach((order) => {
if (order.side === "Sell" && order.price < lowestSell) {
lowestSell = order.price;
}
if (order.side === "Buy" && order.price > highestBuy) {
highestBuy = order.price;
}
});
// Check if orders cross
if (lowestSell <= highestBuy) {
throw new Error(
`Invalid order prices: Sell order at ${lowestSell} is lower than or equal to Buy order at ${highestBuy}. Orders cannot cross.`,
);
}
}
/**
* Place batch orders using Manifest
* @param agent SolanaAgentKit instance
* @param marketId Public key for the manifest market
* @param quantity Amount to trade in tokens
* @param side Buy or Sell
* @param price Price in tokens ie. SOL/USDC
* @returns Transaction signature
*/
export async function batchOrder(
agent: SolanaAgentKit,
marketId: PublicKey,
orders: OrderParams[],
): Promise<string> {
try {
validateNoCrossedOrders(orders);
const mfxClient = await ManifestClient.getClientForMarket(
agent.connection,
marketId,
agent.wallet,
);
const placeParams: WrapperPlaceOrderParamsExternal[] = orders.map(
(order) => ({
numBaseTokens: order.quantity,
tokenPrice: order.price,
isBid: order.side === "Buy",
lastValidSlot: 0,
orderType: OrderType.Limit,
clientOrderId: Number(Math.random() * 10000),
}),
);
const batchOrderIx: TransactionInstruction = await mfxClient.batchUpdateIx(
placeParams,
[],
true,
);
const signature = await sendAndConfirmTransaction(
agent.connection,
new Transaction().add(batchOrderIx),
[agent.wallet],
);
return signature;
} catch (error: any) {
throw new Error(`Batch Order failed: ${error.message}`);
}
}

View File

@@ -16,6 +16,7 @@ export async function getMainAllDomainsDomain(
mainDomain = await _getFavoriteDomain(agent.connection, owner);
return mainDomain.stale ? null : mainDomain.reverse;
} catch (error: any) {
console.error(error);
return null;
}
}

View File

@@ -29,7 +29,8 @@ export async function getPrimaryDomain(
);
}
return reverse;
} catch (error) {
} catch (error: any) {
console.error(error);
throw new Error(
`Failed to get primary domain for account: ${account.toBase58()}`,
);

View File

@@ -0,0 +1,10 @@
import { SolanaAgentKit } from "..";
/**
* Get the agents wallet address
* @param agent - SolanaAgentKit instance
* @returns string
*/
export function get_wallet_address(agent: SolanaAgentKit) {
return agent.wallet_address.toBase58();
}

View File

@@ -1,3 +1,6 @@
export * from "./get_wallet_address";
export * from "./request_faucet_funds";
export * from "./get_wallet_address";
export * from "./request_faucet_funds";
export * from "./deploy_token";
export * from "./deploy_collection";
@@ -7,6 +10,7 @@ export * from "./mint_nft";
export * from "./transfer";
export * from "./trade";
export * from "./limit_order";
export * from "./batch_order";
export * from "./cancel_all_orders";
export * from "./withdraw_all";
export * from "./register_domain";
@@ -53,3 +57,4 @@ export * from "./rock_paper_scissor";
export * from "./create_tiplinks";
export * from "./tensor_trade";
export * from "./rugcheck";

View File

@@ -24,7 +24,8 @@ export async function resolveSolDomain(
try {
return await resolve(agent.connection, domain);
} catch (error) {
} catch (error: any) {
console.error(error);
throw new Error(`Failed to resolve domain: ${domain}`);
}
}

53
src/tools/rugcheck.ts Normal file
View File

@@ -0,0 +1,53 @@
import { TokenCheck } from "../types";
const BASE_URL = "https://api.rugcheck.xyz/v1";
/**
* Fetches a summary report for a specific token.
* @async
* @param {string} mint - The mint address of the token.
* @returns {Promise<TokenCheck>} The token summary report.
* @throws {Error} If the API call fails.
*/
export async function fetchTokenReportSummary(
mint: string,
): Promise<TokenCheck> {
try {
const response = await fetch(`${BASE_URL}/tokens/${mint}/report/summary`);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.json();
} catch (error: any) {
console.error(
`Error fetching report summary for token ${mint}:`,
error.message,
);
throw new Error(`Failed to fetch report summary for token ${mint}.`);
}
}
/**
* Fetches a detailed report for a specific token.
* @async
* @param {string} mint - The mint address of the token.
* @returns {Promise<TokenCheck>} The detailed token report.
* @throws {Error} If the API call fails.
*/
export async function fetchTokenDetailedReport(
mint: string,
): Promise<TokenCheck> {
try {
const response = await fetch(`${BASE_URL}/tokens/${mint}/report`);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.json();
} catch (error: any) {
console.error(
`Error fetching detailed report for token ${mint}:`,
error.message,
);
throw new Error(`Failed to fetch detailed report for token ${mint}.`);
}
}

View File

@@ -88,6 +88,7 @@ export async function sendCompressedAirdrop(
agent.wallet.publicKey,
);
} catch (error) {
console.error(error);
throw new Error(
"Source token account not found and failed to create it. Please add funds to your wallet and try again.",
);

View File

@@ -33,6 +33,7 @@ export async function listNFTForSale(
throw new Error(`You don't own this NFT (${nftMint.toString()})`);
}
} catch (error: any) {
console.error(error);
throw new Error(
`No token account found for mint ${nftMint.toString()}. Make sure you own this NFT.`,
);

View File

@@ -153,3 +153,15 @@ export interface Action {
*/
handler: Handler;
}
export interface TokenCheck {
tokenProgram: string;
tokenType: string;
risks: Array<{
name: string;
level: string;
description: string;
score: number;
}>;
score: number;
}

25
src/vercel-ai/index.ts Normal file
View File

@@ -0,0 +1,25 @@
import { tool, type CoreTool } from "ai";
import { SolanaAgentKit } from "../agent";
import { executeAction } from "../utils/actionExecutor";
import { ACTIONS } from "../actions";
export function createSolanaTools(
solanaAgentKit: SolanaAgentKit,
): Record<string, CoreTool> {
const tools: Record<string, CoreTool> = {};
const actionKeys = Object.keys(ACTIONS);
for (const key of actionKeys) {
const action = ACTIONS[key as keyof typeof ACTIONS];
tools[key] = tool({
// @ts-expect-error Value matches type however TS still shows error
id: action.name,
description: action.description,
parameters: action.schema,
execute: async (params) =>
await executeAction(action, solanaAgentKit, params),
});
}
return tools;
}