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

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.`,
);