mirror of
https://github.com/d0zingcat/solana-agent-kit.git
synced 2026-05-13 23:16:55 +00:00
Add Limit Orders Support from Manifest (#100)
# Pull Request Description Add support for Agents to be able to create limit orders on Manifest. Implements create market and place limit order. Can consider adding cancel and other features in follow ups. ## Changes Made This PR adds the following changes: - support for manifest limit orders and market creation ## Implementation Details - use manifest-sdk ## Transaction executed by agent <!-- If applicable, provide example usage, transactions, or screenshots --> Example transaction: Create Market: https://solscan.io/tx/59HgraXSzpmNtDovUfBUErjBwc2ggLtwfaYmrXdwR88HES6pLWHQWobHR85WmR3gHMavoEKLshCyJqx3Y6BZu8ku Place limit order: https://solscan.io/tx/41d2CGSGdqkxXbuBDJwBniN3E9tN6hW7Tmo7oHQ1N1p5szejYhugKTLHaMNwS6KVp3DhMcTmZpkJMoWwKEpJAuf5 ## Prompt Used <!-- If relevant, include the prompt or configuration used --> ``` ``` ## Checklist - [x] I have tested these changes locally - [x] I have updated the documentation - [x] I have added a transaction link - [x] I have added the prompt used to test it
This commit is contained in:
@@ -47,6 +47,7 @@ Anyone - whether an SF-based AI researcher or a crypto-native builder - can brin
|
||||
- Launch on Pump via PumpPortal
|
||||
- Raydium pool creation (CPMM, CLMM, AMMv4)
|
||||
- Orca Whirlpool integration
|
||||
- Manifest market creation, and limit orders
|
||||
- Meteora Dynamic AMM, DLMM Pool, and Alpha Vault
|
||||
- Openbook market creation
|
||||
- Register and Resolve SNS
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
<li>Launch on Pump via PumpPortal</li>
|
||||
<li>Raydium pool creation (CPMM, CLMM, AMMv4)</li>
|
||||
<li>Orca Whirlpool integration</li>
|
||||
<li>Manifest market creation, and limit orders</li>
|
||||
<li>Meteora Dynamic AMM, DLMM Pool, and Alpha Vault</li>
|
||||
<li>Openbook market creation</li>
|
||||
<li>Register and Resolve SNS</li>
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@bonfida/spl-name-service": "^3.0.7",
|
||||
"@cks-systems/manifest-sdk": "^0.1.73",
|
||||
"@coral-xyz/anchor": "0.29",
|
||||
"@langchain/core": "^0.3.26",
|
||||
"@langchain/groq": "^0.1.2",
|
||||
|
||||
@@ -15,12 +15,16 @@ import {
|
||||
lendAsset,
|
||||
mintCollectionNFT,
|
||||
openbookCreateMarket,
|
||||
manifestCreateMarket,
|
||||
raydiumCreateAmmV4,
|
||||
raydiumCreateClmm,
|
||||
raydiumCreateCpmm,
|
||||
registerDomain,
|
||||
request_faucet_funds,
|
||||
trade,
|
||||
limitOrder,
|
||||
cancelAllOrders,
|
||||
withdrawAll,
|
||||
transfer,
|
||||
getTokenDataByAddress,
|
||||
getTokenDataByTicker,
|
||||
@@ -171,6 +175,23 @@ export class SolanaAgentKit {
|
||||
return trade(this, outputMint, inputAmount, inputMint, slippageBps);
|
||||
}
|
||||
|
||||
async limitOrder(
|
||||
marketId: PublicKey,
|
||||
quantity: number,
|
||||
side: string,
|
||||
price: number,
|
||||
): Promise<string> {
|
||||
return limitOrder(this, marketId, quantity, side, price);
|
||||
}
|
||||
|
||||
async cancelAllOrders(marketId: PublicKey): Promise<string> {
|
||||
return cancelAllOrders(this, marketId);
|
||||
}
|
||||
|
||||
async withdrawAll(marketId: PublicKey): Promise<string> {
|
||||
return withdrawAll(this, marketId);
|
||||
}
|
||||
|
||||
async lendAssets(amount: number): Promise<string> {
|
||||
return lendAsset(this, amount);
|
||||
}
|
||||
@@ -397,6 +418,13 @@ export class SolanaAgentKit {
|
||||
);
|
||||
}
|
||||
|
||||
async manifestCreateMarket(
|
||||
baseMint: PublicKey,
|
||||
quoteMint: PublicKey,
|
||||
): Promise<string[]> {
|
||||
return manifestCreateMarket(this, baseMint, quoteMint);
|
||||
}
|
||||
|
||||
async pythFetchPrice(priceFeedID: string): Promise<string> {
|
||||
return pythFetchPrice(priceFeedID);
|
||||
}
|
||||
|
||||
@@ -306,6 +306,114 @@ export class SolanaTradeTool extends Tool {
|
||||
}
|
||||
}
|
||||
|
||||
export class SolanaLimitOrderTool extends Tool {
|
||||
name = "solana_limit_order";
|
||||
description = `This tool can be used to place limit orders using Manifest.
|
||||
|
||||
Inputs ( input is a JSON string ):
|
||||
marketId: PublicKey, eg "ENhU8LsaR7vDD2G1CsWcsuSGNrih9Cv5WZEk7q9kPapQ" for SOL/USDC (required)
|
||||
quantity: number, eg 1 or 0.01 (required)
|
||||
side: string, eg "Buy" or "Sell" (required)
|
||||
price: number, in tokens eg 200 for SOL/USDC (required)`;
|
||||
|
||||
constructor(private solanaKit: SolanaAgentKit) {
|
||||
super();
|
||||
}
|
||||
|
||||
protected async _call(input: string): Promise<string> {
|
||||
try {
|
||||
const parsedInput = JSON.parse(input);
|
||||
|
||||
const tx = await this.solanaKit.limitOrder(
|
||||
new PublicKey(parsedInput.marketId),
|
||||
parsedInput.quantity,
|
||||
parsedInput.side,
|
||||
parsedInput.price,
|
||||
);
|
||||
|
||||
return JSON.stringify({
|
||||
status: "success",
|
||||
message: "Trade executed successfully",
|
||||
transaction: tx,
|
||||
marketId: parsedInput.marketId,
|
||||
quantity: parsedInput.quantity,
|
||||
side: parsedInput.side,
|
||||
price: parsedInput.price,
|
||||
});
|
||||
} 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.
|
||||
|
||||
Input ( input is a JSON string ):
|
||||
marketId: string, eg "ENhU8LsaR7vDD2G1CsWcsuSGNrih9Cv5WZEk7q9kPapQ" for SOL/USDC (required)`;
|
||||
|
||||
constructor(private solanaKit: SolanaAgentKit) {
|
||||
super();
|
||||
}
|
||||
|
||||
protected async _call(input: string): Promise<string> {
|
||||
try {
|
||||
const marketId = new PublicKey(input.trim());
|
||||
const tx = await this.solanaKit.cancelAllOrders(marketId);
|
||||
|
||||
return JSON.stringify({
|
||||
status: "success",
|
||||
message: "Cancel orders successfully",
|
||||
transaction: tx,
|
||||
marketId,
|
||||
});
|
||||
} catch (error: any) {
|
||||
return JSON.stringify({
|
||||
status: "error",
|
||||
message: error.message,
|
||||
code: error.code || "UNKNOWN_ERROR",
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class SolanaWithdrawAllTool extends Tool {
|
||||
name = "solana_withdraw_all";
|
||||
description = `This tool can be used to withdraw all funds from a Manifest market.
|
||||
|
||||
Input ( input is a JSON string ):
|
||||
marketId: string, eg "ENhU8LsaR7vDD2G1CsWcsuSGNrih9Cv5WZEk7q9kPapQ" for SOL/USDC (required)`;
|
||||
|
||||
constructor(private solanaKit: SolanaAgentKit) {
|
||||
super();
|
||||
}
|
||||
|
||||
protected async _call(input: string): Promise<string> {
|
||||
try {
|
||||
const marketId = new PublicKey(input.trim());
|
||||
const tx = await this.solanaKit.withdrawAll(marketId);
|
||||
|
||||
return JSON.stringify({
|
||||
status: "success",
|
||||
message: "Withdrew successfully",
|
||||
transaction: tx,
|
||||
marketId,
|
||||
});
|
||||
} catch (error: any) {
|
||||
return JSON.stringify({
|
||||
status: "error",
|
||||
message: error.message,
|
||||
code: error.code || "UNKNOWN_ERROR",
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class SolanaRequestFundsTool extends Tool {
|
||||
name = "solana_request_funds";
|
||||
description = "Request SOL from Solana faucet (devnet/testnet only)";
|
||||
@@ -1252,6 +1360,44 @@ export class SolanaOpenbookCreateMarket extends Tool {
|
||||
}
|
||||
}
|
||||
|
||||
export class SolanaManifestCreateMarket extends Tool {
|
||||
name = "solana_manifest_create_market";
|
||||
description = `Manifest market
|
||||
|
||||
Inputs (input is a json string):
|
||||
baseMint: string (required)
|
||||
quoteMint: string (required)
|
||||
`;
|
||||
|
||||
constructor(private solanaKit: SolanaAgentKit) {
|
||||
super();
|
||||
}
|
||||
|
||||
async _call(input: string): Promise<string> {
|
||||
try {
|
||||
const inputFormat = JSON.parse(input);
|
||||
|
||||
const tx = await this.solanaKit.manifestCreateMarket(
|
||||
new PublicKey(inputFormat.baseMint),
|
||||
new PublicKey(inputFormat.quoteMint),
|
||||
);
|
||||
|
||||
return JSON.stringify({
|
||||
status: "success",
|
||||
message: "Create manifest market successfully",
|
||||
transaction: tx[0],
|
||||
marketId: tx[1],
|
||||
});
|
||||
} catch (error: any) {
|
||||
return JSON.stringify({
|
||||
status: "error",
|
||||
message: error.message,
|
||||
code: error.code || "UNKNOWN_ERROR",
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class SolanaPythFetchPrice extends Tool {
|
||||
name = "solana_pyth_fetch_price";
|
||||
description = `Fetch the price of a given price feed from Pyth's Hermes service
|
||||
@@ -1705,6 +1851,10 @@ export function createSolanaTools(solanaKit: SolanaAgentKit) {
|
||||
new SolanaRaydiumCreateClmm(solanaKit),
|
||||
new SolanaRaydiumCreateCpmm(solanaKit),
|
||||
new SolanaOpenbookCreateMarket(solanaKit),
|
||||
new SolanaManifestCreateMarket(solanaKit),
|
||||
new SolanaLimitOrderTool(solanaKit),
|
||||
new SolanaCancelAllOrdersTool(solanaKit),
|
||||
new SolanaWithdrawAllTool(solanaKit),
|
||||
new SolanaClosePosition(solanaKit),
|
||||
new SolanaOrcaCreateCLMM(solanaKit),
|
||||
new SolanaOrcaCreateSingleSideLiquidityPool(solanaKit),
|
||||
|
||||
37
src/tools/cancel_all_orders.ts
Normal file
37
src/tools/cancel_all_orders.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import {
|
||||
PublicKey,
|
||||
sendAndConfirmTransaction,
|
||||
Transaction,
|
||||
} from "@solana/web3.js";
|
||||
import { SolanaAgentKit } from "../index";
|
||||
import { ManifestClient } from "@cks-systems/manifest-sdk";
|
||||
|
||||
/**
|
||||
* Cancels all orders from Manifest
|
||||
* @param agent SolanaAgentKit instance
|
||||
* @param marketId Public key for the manifest market
|
||||
* @returns Transaction signature
|
||||
*/
|
||||
export async function cancelAllOrders(
|
||||
agent: SolanaAgentKit,
|
||||
marketId: PublicKey,
|
||||
): Promise<string> {
|
||||
try {
|
||||
const mfxClient = await ManifestClient.getClientForMarket(
|
||||
agent.connection,
|
||||
marketId,
|
||||
agent.wallet,
|
||||
);
|
||||
|
||||
const cancelAllOrdersIx = await mfxClient.cancelAllIx();
|
||||
const signature = await sendAndConfirmTransaction(
|
||||
agent.connection,
|
||||
new Transaction().add(cancelAllOrdersIx),
|
||||
[agent.wallet],
|
||||
);
|
||||
|
||||
return signature;
|
||||
} catch (error: any) {
|
||||
throw new Error(`Cancel all orders failed: ${error.message}`);
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,9 @@ export * from "./get_balance_other";
|
||||
export * from "./mint_nft";
|
||||
export * from "./transfer";
|
||||
export * from "./trade";
|
||||
export * from "./limit_order";
|
||||
export * from "./cancel_all_orders";
|
||||
export * from "./withdraw_all";
|
||||
export * from "./register_domain";
|
||||
export * from "./resolve_sol_domain";
|
||||
export * from "./get_primary_domain";
|
||||
@@ -40,6 +43,7 @@ export * from "./raydium_create_ammV4";
|
||||
export * from "./raydium_create_clmm";
|
||||
export * from "./raydium_create_cpmm";
|
||||
export * from "./openbook_create_market";
|
||||
export * from "./manifest_create_market";
|
||||
export * from "./pyth_fetch_price";
|
||||
|
||||
export * from "./create_gibwork_task";
|
||||
|
||||
61
src/tools/limit_order.ts
Normal file
61
src/tools/limit_order.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
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";
|
||||
|
||||
/**
|
||||
* Place limit 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 limitOrder(
|
||||
agent: SolanaAgentKit,
|
||||
marketId: PublicKey,
|
||||
quantity: number,
|
||||
side: string,
|
||||
price: number,
|
||||
): Promise<string> {
|
||||
try {
|
||||
const mfxClient = await ManifestClient.getClientForMarket(
|
||||
agent.connection,
|
||||
marketId,
|
||||
agent.wallet,
|
||||
);
|
||||
|
||||
const orderParams: WrapperPlaceOrderParamsExternal = {
|
||||
numBaseTokens: quantity,
|
||||
tokenPrice: price,
|
||||
isBid: side === "Buy",
|
||||
lastValidSlot: 0,
|
||||
orderType: OrderType.Limit,
|
||||
clientOrderId: Number(Math.random() * 1000),
|
||||
};
|
||||
|
||||
const depositPlaceOrderIx: TransactionInstruction[] =
|
||||
await mfxClient.placeOrderWithRequiredDepositIx(
|
||||
agent.wallet.publicKey,
|
||||
orderParams,
|
||||
);
|
||||
const signature = await sendAndConfirmTransaction(
|
||||
agent.connection,
|
||||
new Transaction().add(...depositPlaceOrderIx),
|
||||
[agent.wallet],
|
||||
);
|
||||
|
||||
return signature;
|
||||
} catch (error: any) {
|
||||
throw new Error(`Limit Order failed: ${error.message}`);
|
||||
}
|
||||
}
|
||||
43
src/tools/manifest_create_market.ts
Normal file
43
src/tools/manifest_create_market.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import { ManifestClient } from "@cks-systems/manifest-sdk";
|
||||
import {
|
||||
Keypair,
|
||||
PublicKey,
|
||||
sendAndConfirmTransaction,
|
||||
SystemProgram,
|
||||
Transaction,
|
||||
TransactionInstruction,
|
||||
} from "@solana/web3.js";
|
||||
import { SolanaAgentKit } from "../index";
|
||||
|
||||
export async function manifestCreateMarket(
|
||||
agent: SolanaAgentKit,
|
||||
baseMint: PublicKey,
|
||||
quoteMint: PublicKey,
|
||||
): Promise<string[]> {
|
||||
const marketKeypair: Keypair = Keypair.generate();
|
||||
const FIXED_MANIFEST_HEADER_SIZE: number = 256;
|
||||
const createAccountIx: TransactionInstruction = SystemProgram.createAccount({
|
||||
fromPubkey: agent.wallet.publicKey,
|
||||
newAccountPubkey: marketKeypair.publicKey,
|
||||
space: FIXED_MANIFEST_HEADER_SIZE,
|
||||
lamports: await agent.connection.getMinimumBalanceForRentExemption(
|
||||
FIXED_MANIFEST_HEADER_SIZE,
|
||||
),
|
||||
programId: new PublicKey("MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms"),
|
||||
});
|
||||
const createMarketIx = ManifestClient["createMarketIx"](
|
||||
agent.wallet.publicKey,
|
||||
baseMint,
|
||||
quoteMint,
|
||||
marketKeypair.publicKey,
|
||||
);
|
||||
|
||||
const tx: Transaction = new Transaction();
|
||||
tx.add(createAccountIx);
|
||||
tx.add(createMarketIx);
|
||||
const signature = await sendAndConfirmTransaction(agent.connection, tx, [
|
||||
agent.wallet,
|
||||
marketKeypair,
|
||||
]);
|
||||
return [signature, marketKeypair.publicKey.toBase58()];
|
||||
}
|
||||
37
src/tools/withdraw_all.ts
Normal file
37
src/tools/withdraw_all.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import {
|
||||
PublicKey,
|
||||
sendAndConfirmTransaction,
|
||||
Transaction,
|
||||
} from "@solana/web3.js";
|
||||
import { SolanaAgentKit } from "../index";
|
||||
import { ManifestClient } from "@cks-systems/manifest-sdk";
|
||||
|
||||
/**
|
||||
* Withdraws all funds from Manifest
|
||||
* @param agent SolanaAgentKit instance
|
||||
* @param marketId Public key for the manifest market
|
||||
* @returns Transaction signature
|
||||
*/
|
||||
export async function withdrawAll(
|
||||
agent: SolanaAgentKit,
|
||||
marketId: PublicKey,
|
||||
): Promise<string> {
|
||||
try {
|
||||
const mfxClient = await ManifestClient.getClientForMarket(
|
||||
agent.connection,
|
||||
marketId,
|
||||
agent.wallet,
|
||||
);
|
||||
|
||||
const withdrawAllIx = await mfxClient.withdrawAllIx();
|
||||
const signature = await sendAndConfirmTransaction(
|
||||
agent.connection,
|
||||
new Transaction().add(...withdrawAllIx),
|
||||
[agent.wallet],
|
||||
);
|
||||
|
||||
return signature;
|
||||
} catch (error: any) {
|
||||
throw new Error(`Withdraw all failed: ${error.message}`);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user