feat: add cooking command and update swap/docs

This commit is contained in:
deepfeature
2026-04-02 21:57:45 +08:00
committed by GMGN.AI
parent 56086fa692
commit b12a2294b7
7 changed files with 660 additions and 1 deletions
+81
View File
@@ -87,6 +87,63 @@ export interface SwapParams {
max_priority_fee_per_gas?: string;
}
export interface StrategyCreateParams {
chain: string;
from_address: string;
base_token: string;
quote_token: string;
side: string;
open_price: string;
check_price: string;
amount_in?: string;
amount_in_percent?: string;
limit_price_mode?: string;
price_gap_ratio?: string;
expire_in?: number;
sell_ratio_type?: string;
slippage?: number;
auto_slippage?: boolean;
fee?: string;
gas_price?: string;
max_fee_per_gas?: string;
max_priority_fee_per_gas?: string;
is_anti_mev?: boolean;
anti_mev_mode?: string;
priority_fee?: string;
tip_fee?: string;
custom_rpc?: string;
}
export interface StrategyCancelParams {
chain: string;
from_address: string;
order_id: string;
close_sell_model?: string;
}
export interface CreateTokenParams {
chain: string;
dex: string;
from_address: string;
name: string;
symbol: string;
buy_amt: string;
image?: string;
image_url?: string;
website?: string;
twitter?: string;
telegram?: string;
slippage?: number;
auto_slippage?: boolean;
priority_fee?: string;
tip_fee?: string;
gas_price?: string;
max_priority_fee_per_gas?: string;
max_fee_per_gas?: string;
is_anti_mev?: boolean;
anti_mev_mode?: string;
}
export class OpenApiClient {
private readonly apiKey: string;
private readonly privateKeyPem: string | undefined;
@@ -239,6 +296,30 @@ export class OpenApiClient {
return this.criticalRequest("GET", "/v1/trade/query_order", { order_id: orderId, chain }, null);
}
// ---- Strategy order endpoints ----
async createStrategyOrder(params: StrategyCreateParams): Promise<unknown> {
return this.criticalRequest("POST", "/v1/trade/strategy/create", {}, params);
}
async getStrategyOrders(chain: string, extra: Record<string, string | number> = {}): Promise<unknown> {
return this.normalRequest("GET", "/v1/trade/strategy/orders", { chain, ...extra });
}
async cancelStrategyOrder(params: StrategyCancelParams): Promise<unknown> {
return this.criticalRequest("POST", "/v1/trade/strategy/cancel", {}, params);
}
// ---- Cooking endpoints ----
async getCookingStatistics(): Promise<unknown> {
return this.normalRequest("GET", "/v1/cooking/statistics", {});
}
async createToken(params: CreateTokenParams): Promise<unknown> {
return this.criticalRequest("POST", "/v1/cooking/create_token", {}, params);
}
// ---- Internal methods ----
private async normalRequest(
+74
View File
@@ -0,0 +1,74 @@
import { Command } from "commander";
import { OpenApiClient, CreateTokenParams } from "../client/OpenApiClient.js";
import { getConfig } from "../config.js";
import { exitOnError, printResult } from "../output.js";
import { validateChain } from "../validate.js";
export function registerCookingCommands(program: Command): void {
const cooking = program.command("cooking").description("Token creation and launchpad commands");
cooking
.command("stats")
.description("Get token creation statistics by launchpad (normal auth)")
.option("--raw", "Output raw JSON")
.action(async (opts) => {
const client = new OpenApiClient(getConfig());
const data = await client.getCookingStatistics().catch(exitOnError);
printResult(data, opts.raw);
});
cooking
.command("create")
.description("Create a token on a launchpad platform (requires private key)")
.requiredOption("--chain <chain>", "Chain: sol / bsc / base / eth / ton")
.requiredOption("--dex <dex>", "Launchpad: pump / raydium / pancakeswap / flap / fourmeme / bonk / bags / ...")
.requiredOption("--from <address>", "Wallet address (must match API Key binding)")
.requiredOption("--name <name>", "Token name")
.requiredOption("--symbol <symbol>", "Token symbol")
.requiredOption("--buy-amt <amount>", "Initial buy amount in native token (e.g. 0.01 SOL)")
.option("--image <base64>", "Token logo as base64-encoded data (max 2MB decoded)")
.option("--image-url <url>", "Token logo URL")
.option("--website <url>", "Website URL")
.option("--twitter <url>", "Twitter link")
.option("--telegram <url>", "Telegram link")
.option("--slippage <n>", "Slippage tolerance (e.g. 0.01 = 1%)", parseFloat)
.option("--auto-slippage", "Enable automatic slippage")
.option("--priority-fee <sol>", "Priority fee in SOL (SOL only)")
.option("--tip-fee <amount>", "Tip fee")
.option("--gas-price <amount>", "Gas price in wei (EVM chains)")
.option("--anti-mev", "Enable anti-MEV protection")
.option("--raw", "Output raw JSON")
.action(async (opts) => {
if (!opts.image && !opts.imageUrl) {
console.error("[gmgn-cli] Either --image or --image-url must be provided");
process.exit(1);
}
if (!opts.slippage && !opts.autoSlippage) {
console.error("[gmgn-cli] Either --slippage or --auto-slippage must be provided");
process.exit(1);
}
validateChain(opts.chain);
const params: CreateTokenParams = {
chain: opts.chain,
dex: opts.dex,
from_address: opts.from,
name: opts.name,
symbol: opts.symbol,
buy_amt: opts.buyAmt,
};
if (opts.image) params.image = opts.image;
if (opts.imageUrl) params.image_url = opts.imageUrl;
if (opts.website) params.website = opts.website;
if (opts.twitter) params.twitter = opts.twitter;
if (opts.telegram) params.telegram = opts.telegram;
if (opts.slippage != null) params.slippage = opts.slippage;
if (opts.autoSlippage) params.auto_slippage = true;
if (opts.priorityFee) params.priority_fee = opts.priorityFee;
if (opts.tipFee) params.tip_fee = opts.tipFee;
if (opts.gasPrice) params.gas_price = opts.gasPrice;
if (opts.antiMev) params.is_anti_mev = true;
const client = new OpenApiClient(getConfig(true));
const data = await client.createToken(params).catch(exitOnError);
printResult(data, opts.raw);
});
}
+104 -1
View File
@@ -1,5 +1,5 @@
import { Command } from "commander";
import { OpenApiClient, SwapParams } from "../client/OpenApiClient.js";
import { OpenApiClient, SwapParams, StrategyCreateParams, StrategyCancelParams } from "../client/OpenApiClient.js";
import { getConfig } from "../config.js";
import { exitOnError, printResult } from "../output.js";
import { validateAddress, validateChain, validatePercent, validatePositiveInt } from "../validate.js";
@@ -97,4 +97,107 @@ export function registerSwapCommands(program: Command): void {
const data = await client.queryOrder(opts.orderId, opts.chain).catch(exitOnError);
printResult(data, opts.raw);
});
const strategy = order.command("strategy").description("Limit/strategy order management");
strategy
.command("create")
.description("Create a limit/strategy order (requires private key)")
.requiredOption("--chain <chain>", "Chain: sol / bsc / base")
.requiredOption("--from <address>", "Wallet address (must match API Key binding)")
.requiredOption("--base-token <address>", "Base token contract address")
.requiredOption("--quote-token <address>", "Quote token contract address")
.requiredOption("--side <side>", "Direction: buy / sell")
.requiredOption("--open-price <price>", "Open price")
.requiredOption("--check-price <price>", "Trigger check price")
.option("--amount-in <amount>", "Input amount (smallest unit)")
.option("--amount-in-percent <pct>", "Input amount as a percentage (e.g. 50 = 50%)")
.option("--limit-price-mode <mode>", "Price mode: exact / slippage (default: slippage)")
.option("--expire-in <seconds>", "Order expiry in seconds", parseInt)
.option("--sell-ratio-type <type>", "Sell ratio basis: buy_amount (default) / hold_amount")
.option("--slippage <n>", "Slippage tolerance (e.g. 0.01 = 1%)", parseFloat)
.option("--auto-slippage", "Enable automatic slippage")
.option("--priority-fee <sol>", "Priority fee in SOL (SOL only)")
.option("--tip-fee <amount>", "Tip fee")
.option("--gas-price <amount>", "Gas price in wei (EVM chains)")
.option("--anti-mev", "Enable anti-MEV protection")
.option("--raw", "Output raw JSON")
.action(async (opts) => {
if (!opts.amountIn && !opts.amountInPercent) {
console.error("[gmgn-cli] Either --amount-in or --amount-in-percent must be provided");
process.exit(1);
}
if (!opts.slippage && !opts.autoSlippage) {
console.error("[gmgn-cli] Either --slippage or --auto-slippage must be provided");
process.exit(1);
}
validateChain(opts.chain);
const params: StrategyCreateParams = {
chain: opts.chain,
from_address: opts.from,
base_token: opts.baseToken,
quote_token: opts.quoteToken,
side: opts.side,
open_price: opts.openPrice,
check_price: opts.checkPrice,
};
if (opts.amountIn) params.amount_in = opts.amountIn;
if (opts.amountInPercent) params.amount_in_percent = opts.amountInPercent;
if (opts.limitPriceMode) params.limit_price_mode = opts.limitPriceMode;
if (opts.expireIn != null) params.expire_in = opts.expireIn;
if (opts.sellRatioType) params.sell_ratio_type = opts.sellRatioType;
if (opts.slippage != null) params.slippage = opts.slippage;
if (opts.autoSlippage) params.auto_slippage = true;
if (opts.priorityFee) params.priority_fee = opts.priorityFee;
if (opts.tipFee) params.tip_fee = opts.tipFee;
if (opts.gasPrice) params.gas_price = opts.gasPrice;
if (opts.antiMev) params.is_anti_mev = true;
const client = new OpenApiClient(getConfig(true));
const data = await client.createStrategyOrder(params).catch(exitOnError);
printResult(data, opts.raw);
});
strategy
.command("list")
.description("List strategy orders (normal auth)")
.requiredOption("--chain <chain>", "Chain: sol / bsc / base")
.option("--type <type>", "open (default) / history")
.option("--from <address>", "Filter by wallet address")
.option("--base-token <address>", "Filter by token address")
.option("--page-token <token>", "Pagination cursor from previous response")
.option("--limit <n>", "Results per page", parseInt)
.option("--raw", "Output raw JSON")
.action(async (opts) => {
validateChain(opts.chain);
const extra: Record<string, string | number> = {};
if (opts.type) extra["type"] = opts.type;
if (opts.from) extra["from_address"] = opts.from;
if (opts.baseToken) extra["base_token"] = opts.baseToken;
if (opts.pageToken) extra["page_token"] = opts.pageToken;
if (opts.limit != null) extra["limit"] = opts.limit;
const client = new OpenApiClient(getConfig());
const data = await client.getStrategyOrders(opts.chain, extra).catch(exitOnError);
printResult(data, opts.raw);
});
strategy
.command("cancel")
.description("Cancel a strategy order (requires private key)")
.requiredOption("--chain <chain>", "Chain: sol / bsc / base")
.requiredOption("--from <address>", "Wallet address (must match API Key binding)")
.requiredOption("--order-id <id>", "Order ID to cancel")
.option("--close-sell-model <model>", "Sell model when closing")
.option("--raw", "Output raw JSON")
.action(async (opts) => {
validateChain(opts.chain);
const params: StrategyCancelParams = {
chain: opts.chain,
from_address: opts.from,
order_id: opts.orderId,
};
if (opts.closeSellModel) params.close_sell_model = opts.closeSellModel;
const client = new OpenApiClient(getConfig(true));
const data = await client.cancelStrategyOrder(params).catch(exitOnError);
printResult(data, opts.raw);
});
}
+2
View File
@@ -10,6 +10,7 @@ import { registerMarketCommands } from "./commands/market.js";
import { registerPortfolioCommands } from "./commands/portfolio.js";
import { registerTrackCommands } from "./commands/track.js";
import { registerSwapCommands } from "./commands/swap.js";
import { registerCookingCommands } from "./commands/cooking.js";
const proxy = process.env.HTTPS_PROXY ?? process.env.https_proxy
?? process.env.HTTP_PROXY ?? process.env.http_proxy;
@@ -58,6 +59,7 @@ registerMarketCommands(program);
registerPortfolioCommands(program);
registerTrackCommands(program);
registerSwapCommands(program);
registerCookingCommands(program);
program.parseAsync().catch((err) => {
console.error(`[gmgn-cli] ${err.message}`);