From 2e71f283f0f787fb82fb69c27479626a33e33585 Mon Sep 17 00:00:00 2001 From: deepfeature Date: Thu, 2 Apr 2026 14:58:55 +0800 Subject: [PATCH 1/3] feat: add cooking command and update swap/docs --- docs/cli-usage.md | 165 +++++++++++++++++++++++++++++++++++ skills/gmgn-cooking/SKILL.md | 123 ++++++++++++++++++++++++++ skills/gmgn-swap/SKILL.md | 111 +++++++++++++++++++++++ src/client/OpenApiClient.ts | 81 +++++++++++++++++ src/commands/cooking.ts | 74 ++++++++++++++++ src/commands/swap.ts | 105 +++++++++++++++++++++- src/index.ts | 2 + 7 files changed, 660 insertions(+), 1 deletion(-) create mode 100644 skills/gmgn-cooking/SKILL.md create mode 100644 src/commands/cooking.ts diff --git a/docs/cli-usage.md b/docs/cli-usage.md index 7b02aa1..6677dde 100644 --- a/docs/cli-usage.md +++ b/docs/cli-usage.md @@ -457,6 +457,171 @@ npx gmgn-cli order get --chain --order-id [--raw] **Response fields (data):** Same structure as the `swap` response above. +## order strategy create + +Create a limit/strategy order. **Requires `GMGN_PRIVATE_KEY` configured in `.env`.** + +```bash +gmgn-cli order strategy create \ + --chain \ + --from \ + --base-token \ + --quote-token \ + --side \ + --open-price \ + --check-price \ + [--amount-in | --amount-in-percent ] \ + [--slippage | --auto-slippage] \ + [--limit-price-mode ] \ + [--expire-in ] \ + [--sell-ratio-type ] \ + [--priority-fee ] \ + [--tip-fee ] \ + [--gas-price ] \ + [--anti-mev] \ + [--raw] +``` + +| Option | Required | Description | +|--------|----------|-------------| +| `--chain` | Yes | `sol` / `bsc` / `base` | +| `--from` | Yes | Wallet address (must match API Key binding) | +| `--base-token` | Yes | Base token contract address | +| `--quote-token` | Yes | Quote token contract address | +| `--side` | Yes | `buy` / `sell` | +| `--open-price` | Yes | Open price | +| `--check-price` | Yes | Trigger check price | +| `--amount-in` | No* | Input amount (smallest unit); required unless `--amount-in-percent` is used | +| `--amount-in-percent` | No* | Input as percentage (e.g. `50` = 50%); required unless `--amount-in` is used | +| `--limit-price-mode` | No | `exact` / `slippage` (default: `slippage`) | +| `--expire-in` | No | Order expiry in seconds | +| `--sell-ratio-type` | No | `buy_amount` (default) / `hold_amount` | +| `--slippage` | No | Slippage tolerance, e.g. `0.01` = 1% | +| `--auto-slippage` | No | Enable automatic slippage | +| `--priority-fee` | No | Priority fee in SOL (SOL only) | +| `--tip-fee` | No | Tip fee | +| `--gas-price` | No | Gas price in wei (EVM chains) | +| `--anti-mev` | No | Enable anti-MEV protection | + +**Response fields (data):** + +| Field | Type | Description | +|-------|------|-------------| +| `order_id` | string | Created strategy order ID | +| `is_update` | bool | `true` if an existing order was updated | + +--- + +## order strategy list + +List strategy orders. Uses normal auth. + +```bash +gmgn-cli order strategy list --chain [--type ] [--from
] [--base-token
] [--page-token ] [--limit ] [--raw] +``` + +| Option | Required | Description | +|--------|----------|-------------| +| `--chain` | Yes | `sol` / `bsc` / `base` | +| `--type` | No | `open` (default) / `history` | +| `--from` | No | Filter by wallet address | +| `--base-token` | No | Filter by token address | +| `--page-token` | No | Pagination cursor from previous response | +| `--limit` | No | Results per page | + +**Response fields (data):** + +| Field | Type | Description | +|-------|------|-------------| +| `next_page_token` | string | Cursor for next page; empty when no more data | +| `total` | int | Total count (only when `--type open`) | +| `list` | array | Strategy order list | + +--- + +## order strategy cancel + +Cancel a strategy order. **Requires `GMGN_PRIVATE_KEY` configured in `.env`.** + +```bash +gmgn-cli order strategy cancel --chain --from --order-id [--close-sell-model ] [--raw] +``` + +| Option | Required | Description | +|--------|----------|-------------| +| `--chain` | Yes | `sol` / `bsc` / `base` | +| `--from` | Yes | Wallet address (must match API Key binding) | +| `--order-id` | Yes | Order ID to cancel | +| `--close-sell-model` | No | Sell model when closing | + +--- + +## cooking stats + +Get token creation statistics grouped by launchpad. + +```bash +gmgn-cli cooking stats [--raw] +``` + +No additional options required. Returns an array of `{ launchpad, token_count }` entries. + +--- + +## cooking create + +Create a token on a launchpad platform. **Requires `GMGN_PRIVATE_KEY` configured in `.env`.** + +```bash +gmgn-cli cooking create \ + --chain \ + --dex \ + --from \ + --name \ + --symbol \ + --buy-amt \ + [--image | --image-url ] \ + [--slippage | --auto-slippage] \ + [--website ] [--twitter ] [--telegram ] \ + [--priority-fee ] [--tip-fee ] [--gas-price ] \ + [--anti-mev] \ + [--raw] +``` + +| Option | Required | Description | +|--------|----------|-------------| +| `--chain` | Yes | `sol` / `bsc` / `base` / `eth` / `ton` | +| `--dex` | Yes | Launchpad: `pump` / `raydium` / `pancakeswap` / `flap` / `fourmeme` / `bonk` / `bags` / ... | +| `--from` | Yes | Wallet address (must match API Key binding) | +| `--name` | Yes | Token name | +| `--symbol` | Yes | Token symbol | +| `--buy-amt` | Yes | Initial buy amount in native token (e.g. `0.01` SOL) | +| `--image` | No* | Token logo as base64-encoded data (max 2MB decoded); required unless `--image-url` is used | +| `--image-url` | No* | Token logo URL; required unless `--image` is used | +| `--website` | No | Website URL | +| `--twitter` | No | Twitter link | +| `--telegram` | No | Telegram link | +| `--slippage` | No | Slippage tolerance, e.g. `0.01` = 1% | +| `--auto-slippage` | No | Enable automatic slippage | +| `--priority-fee` | No | Priority fee in SOL (SOL only) | +| `--tip-fee` | No | Tip fee | +| `--gas-price` | No | Gas price in wei (EVM chains) | +| `--anti-mev` | No | Enable anti-MEV protection | + +**Response fields (data):** + +| Field | Type | Description | +|-------|------|-------------| +| `status` | string | `pending` / `confirmed` / `failed` | +| `hash` | string | Transaction hash | +| `order_id` | string | Order ID for polling | +| `error_code` | string | Error code on failure | +| `error_status` | string | Error description on failure | + +Token creation is asynchronous. Poll `order get` with the returned `order_id` if `status` is `pending`. + +--- + ## Rate Limit Handling All business routes are protected by GMGN's leaky-bucket limiter. Current production behavior is: diff --git a/skills/gmgn-cooking/SKILL.md b/skills/gmgn-cooking/SKILL.md new file mode 100644 index 0000000..48cd99c --- /dev/null +++ b/skills/gmgn-cooking/SKILL.md @@ -0,0 +1,123 @@ +--- +name: gmgn-cooking +description: "[FINANCIAL EXECUTION] Create tokens on launchpad platforms (Pump.fun, Raydium, PancakeSwap, Flap, FourMeme, Bonk, BAGS, etc.) or query token creation statistics by launchpad. Token creation executes irreversible on-chain transactions. Requires explicit user confirmation before every create. Supports sol / bsc / base." +argument-hint: "stats | [create --chain --dex --from --name --symbol --buy-amt (--image | --image-url )]" +metadata: + cliHelp: "gmgn-cli cooking --help" +--- + +**IMPORTANT: Always use `gmgn-cli` commands below. Do NOT use web search, WebFetch, curl, or visit gmgn.ai.** + +**⚠️ Token creation executes REAL, IRREVERSIBLE blockchain transactions. Always require explicit user confirmation before running `cooking create`.** + +## Sub-commands + +| Sub-command | Description | +|-------------|-------------| +| `cooking stats` | Get token creation statistics grouped by launchpad (normal auth) | +| `cooking create` | Create a token on a launchpad platform (requires private key) | + +## Supported Chains + +`sol` / `bsc` / `base` / `eth` / `ton` + +## Supported Launchpads by Chain + +| Chain | Supported DEX / Launchpad | +|-------|--------------------------| +| `sol` | `pump` / `raydium` / `bonk` / `bags` / `memoo` / `letsbonk` / `bonkers` | +| `bsc` | `pancakeswap` / `flap` / `fourmeme` | +| `base` | `clanker` / `flaunch` / `baseapp` / `basememe` / `zora` / `virtuals_v2` | + +## Prerequisites + +- `cooking stats`: Only `GMGN_API_KEY` required +- `cooking create`: Both `GMGN_API_KEY` and `GMGN_PRIVATE_KEY` must be configured in `~/.config/gmgn/.env`. The private key must correspond to the wallet bound to the API Key. +- `gmgn-cli` installed globally — if missing, run: `npm install -g gmgn-cli` + +## `cooking stats` Usage + +```bash +gmgn-cli cooking stats [--raw] +``` + +**Response fields (data array):** + +| Field | Type | Description | +|-------|------|-------------| +| `launchpad` | string | Launchpad identifier (e.g. `pump`, `raydium`, `pancakeswap`) | +| `token_count` | int | Number of tokens created on that launchpad | + +## `cooking create` Parameters + +| Parameter | Required | Description | +|-----------|----------|-------------| +| `--chain` | Yes | Chain: `sol` / `bsc` / `base` / `eth` / `ton` | +| `--dex` | Yes | Launchpad platform (see Supported Launchpads table) | +| `--from` | Yes | Wallet address (must match API Key binding) | +| `--name` | Yes | Token name | +| `--symbol` | Yes | Token symbol | +| `--buy-amt` | Yes | Initial buy amount in native token (e.g. `0.01` for 0.01 SOL) | +| `--image` | No* | Token logo as base64-encoded data (max 2MB decoded). One of `--image` or `--image-url` is required | +| `--image-url` | No* | Token logo URL. One of `--image` or `--image-url` is required | +| `--website` | No | Website URL | +| `--twitter` | No | Twitter link | +| `--telegram` | No | Telegram link | +| `--slippage` | No | Slippage tolerance, e.g. `0.01` = 1%. Mutually exclusive with `--auto-slippage` | +| `--auto-slippage` | No | Enable automatic slippage | +| `--priority-fee` | No | Priority fee in SOL (SOL only, ≥ 0.0001 SOL) | +| `--tip-fee` | No | Tip fee (SOL ≥ 0.00001 / BSC ≥ 0.000001 BNB; ignored on ETH/BASE) | +| `--gas-price` | No | Gas price in wei (EVM chains) | +| `--anti-mev` | No | Enable anti-MEV protection | + +## `cooking create` Response Fields + +| Field | Type | Description | +|-------|------|-------------| +| `status` | string | `pending` / `confirmed` / `failed` | +| `hash` | string | Transaction hash (may be empty while pending) | +| `order_id` | string | Order ID for polling via `order get` | +| `error_code` | string | Error code on failure | +| `error_status` | string | Error description on failure | + +Token creation is asynchronous. If `status` is `pending`, poll with `gmgn-cli order get --chain --order-id ` every 2 seconds (up to 30 seconds). The new token's mint address is available in the order detail's `output_token` field once confirmed. + +## Usage Examples + +```bash +# Get launchpad creation statistics +gmgn-cli cooking stats + +# Create a token on Pump.fun (SOL) +gmgn-cli cooking create \ + --chain sol \ + --dex pump \ + --from \ + --name "My Token" \ + --symbol MAT \ + --buy-amt 0.01 \ + --image-url https://example.com/logo.png \ + --slippage 0.01 \ + --priority-fee 0.001 + +# Create a token on PancakeSwap (BSC) +gmgn-cli cooking create \ + --chain bsc \ + --dex pancakeswap \ + --from \ + --name "BSC Token" \ + --symbol BSCT \ + --buy-amt 0.01 \ + --image-url https://example.com/logo.png \ + --slippage 0.02 \ + --gas-price 5000000000 \ + --website https://mytoken.io \ + --twitter https://twitter.com/mytoken +``` + +## Notes + +- `cooking create` uses **critical auth** (API Key + signature) — CLI handles signing automatically +- Either `--image` (base64) or `--image-url` is required; both `--slippage` and `--auto-slippage` cannot be omitted (provide one) +- After creation, poll `order get` to get `confirmed` status and the new token's mint address from `output_token` +- Use `--raw` to get single-line JSON for further processing diff --git a/skills/gmgn-swap/SKILL.md b/skills/gmgn-swap/SKILL.md index 18f4e0a..dea1f43 100644 --- a/skills/gmgn-swap/SKILL.md +++ b/skills/gmgn-swap/SKILL.md @@ -48,6 +48,9 @@ Use the `gmgn-cli` tool to submit a token swap or query an existing order. **Req | `swap` | Submit a token swap | | `order quote` | Get a swap quote (no transaction submitted) | | `order get` | Query order status | +| `order strategy create` | Create a limit/strategy order (requires private key) | +| `order strategy list` | List strategy orders (normal auth) | +| `order strategy cancel` | Cancel a strategy order (requires private key) | ## Supported Chains @@ -279,11 +282,119 @@ Order ID: {order_id} Convert `filled_input_amount` and `filled_output_amount` from smallest unit using token decimals before displaying. +## `order strategy create` Parameters + +| Parameter | Required | Description | +|-----------|----------|-------------| +| `--chain` | Yes | `sol` / `bsc` / `base` | +| `--from` | Yes | Wallet address (must match API Key binding) | +| `--base-token` | Yes | Base token contract address | +| `--quote-token` | Yes | Quote token contract address | +| `--side` | Yes | Direction: `buy` / `sell` | +| `--open-price` | Yes | Open price; compared with `--check-price` to infer order type | +| `--check-price` | Yes | Trigger check price; compared with `--open-price` to infer order type | +| `--amount-in` | No* | Input amount (smallest unit). Mutually exclusive with `--amount-in-percent` | +| `--amount-in-percent` | No* | Input as percentage (e.g. `50` = 50%). Mutually exclusive with `--amount-in` | +| `--limit-price-mode` | No | `exact` / `slippage` (default: `slippage`) | +| `--expire-in` | No | Order expiry in seconds | +| `--sell-ratio-type` | No | `buy_amount` (default) / `hold_amount` | +| `--slippage` | No | Slippage tolerance, e.g. `0.01` = 1%. Mutually exclusive with `--auto-slippage` | +| `--auto-slippage` | No | Enable automatic slippage | +| `--priority-fee` | No | Priority fee in SOL (SOL only) | +| `--tip-fee` | No | Tip fee | +| `--gas-price` | No | Gas price in wei (EVM chains) | +| `--anti-mev` | No | Enable anti-MEV protection | + +Order direction is inferred automatically from `--side` and the comparison between `--open-price` and `--check-price`: + +| `--side` | open_price vs check_price | Inferred type | +|----------|--------------------------|---------------| +| `buy` | open_price > check_price | buy_low | +| `buy` | open_price < check_price | buy_high | +| `sell` | open_price > check_price | stop_loss | +| `sell` | open_price < check_price | take_profit | + +**`order strategy create` Response Fields:** + +| Field | Type | Description | +|-------|------|-------------| +| `order_id` | string | Created strategy order ID | +| `is_update` | bool | `true` if an existing order was updated, `false` if newly created | + +## `order strategy list` Parameters + +| Parameter | Required | Description | +|-----------|----------|-------------| +| `--chain` | Yes | `sol` / `bsc` / `base` | +| `--type` | No | `open` (default) / `history` | +| `--from` | No | Filter by wallet address | +| `--base-token` | No | Filter by token address | +| `--page-token` | No | Pagination cursor from previous response | +| `--limit` | No | Results per page (default 10 for history) | + +**`order strategy list` Response Fields:** + +| Field | Type | Description | +|-------|------|-------------| +| `next_page_token` | string | Cursor for next page; empty when no more data | +| `total` | int | Total count (only returned when `--type open`) | +| `list` | array | Strategy order list | + +## `order strategy cancel` Parameters + +| Parameter | Required | Description | +|-----------|----------|-------------| +| `--chain` | Yes | `sol` / `bsc` / `base` | +| `--from` | Yes | Wallet address (must match API Key binding) | +| `--order-id` | Yes | Order ID to cancel | +| `--close-sell-model` | No | Sell model when closing the order | + +## `order strategy` Usage Examples + +```bash +# Create a take-profit order: sell when price rises above open price +gmgn-cli order strategy create \ + --chain sol \ + --from \ + --base-token \ + --quote-token \ + --side sell \ + --open-price 0.001 \ + --check-price 0.002 \ + --amount-in 1000000 \ + --slippage 0.01 + +# Create a stop-loss order: sell when price drops below open price +gmgn-cli order strategy create \ + --chain sol \ + --from \ + --base-token \ + --quote-token \ + --side sell \ + --open-price 0.001 \ + --check-price 0.0005 \ + --amount-in-percent 100 \ + --slippage 0.01 + +# List open strategy orders +gmgn-cli order strategy list --chain sol + +# List history orders with pagination +gmgn-cli order strategy list --chain sol --type history --limit 20 + +# Cancel a strategy order +gmgn-cli order strategy cancel \ + --chain sol \ + --from \ + --order-id +``` + ## Notes - Swap uses **critical auth** (API Key + signature) — CLI handles signing automatically, no manual processing needed - After submitting a swap, use `order get` to poll for confirmation - `--amount` is in the **smallest unit** (e.g., lamports for SOL) +- `order strategy create` and `order strategy cancel` use critical auth (require `GMGN_PRIVATE_KEY`); `order strategy list` uses normal auth - Use `--raw` to get single-line JSON for further processing ## Input Validation diff --git a/src/client/OpenApiClient.ts b/src/client/OpenApiClient.ts index a03fef7..c9d2ce6 100644 --- a/src/client/OpenApiClient.ts +++ b/src/client/OpenApiClient.ts @@ -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 { + return this.criticalRequest("POST", "/v1/trade/strategy/create", {}, params); + } + + async getStrategyOrders(chain: string, extra: Record = {}): Promise { + return this.normalRequest("GET", "/v1/trade/strategy/orders", { chain, ...extra }); + } + + async cancelStrategyOrder(params: StrategyCancelParams): Promise { + return this.criticalRequest("POST", "/v1/trade/strategy/cancel", {}, params); + } + + // ---- Cooking endpoints ---- + + async getCookingStatistics(): Promise { + return this.normalRequest("GET", "/v1/cooking/statistics", {}); + } + + async createToken(params: CreateTokenParams): Promise { + return this.criticalRequest("POST", "/v1/cooking/create_token", {}, params); + } + // ---- Internal methods ---- private async normalRequest( diff --git a/src/commands/cooking.ts b/src/commands/cooking.ts new file mode 100644 index 0000000..df547bb --- /dev/null +++ b/src/commands/cooking.ts @@ -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: sol / bsc / base / eth / ton") + .requiredOption("--dex ", "Launchpad: pump / raydium / pancakeswap / flap / fourmeme / bonk / bags / ...") + .requiredOption("--from
", "Wallet address (must match API Key binding)") + .requiredOption("--name ", "Token name") + .requiredOption("--symbol ", "Token symbol") + .requiredOption("--buy-amt ", "Initial buy amount in native token (e.g. 0.01 SOL)") + .option("--image ", "Token logo as base64-encoded data (max 2MB decoded)") + .option("--image-url ", "Token logo URL") + .option("--website ", "Website URL") + .option("--twitter ", "Twitter link") + .option("--telegram ", "Telegram link") + .option("--slippage ", "Slippage tolerance (e.g. 0.01 = 1%)", parseFloat) + .option("--auto-slippage", "Enable automatic slippage") + .option("--priority-fee ", "Priority fee in SOL (SOL only)") + .option("--tip-fee ", "Tip fee") + .option("--gas-price ", "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); + }); +} diff --git a/src/commands/swap.ts b/src/commands/swap.ts index 35cabb1..d4d9f7d 100644 --- a/src/commands/swap.ts +++ b/src/commands/swap.ts @@ -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: sol / bsc / base") + .requiredOption("--from
", "Wallet address (must match API Key binding)") + .requiredOption("--base-token
", "Base token contract address") + .requiredOption("--quote-token
", "Quote token contract address") + .requiredOption("--side ", "Direction: buy / sell") + .requiredOption("--open-price ", "Open price") + .requiredOption("--check-price ", "Trigger check price") + .option("--amount-in ", "Input amount (smallest unit)") + .option("--amount-in-percent ", "Input amount as a percentage (e.g. 50 = 50%)") + .option("--limit-price-mode ", "Price mode: exact / slippage (default: slippage)") + .option("--expire-in ", "Order expiry in seconds", parseInt) + .option("--sell-ratio-type ", "Sell ratio basis: buy_amount (default) / hold_amount") + .option("--slippage ", "Slippage tolerance (e.g. 0.01 = 1%)", parseFloat) + .option("--auto-slippage", "Enable automatic slippage") + .option("--priority-fee ", "Priority fee in SOL (SOL only)") + .option("--tip-fee ", "Tip fee") + .option("--gas-price ", "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: sol / bsc / base") + .option("--type ", "open (default) / history") + .option("--from
", "Filter by wallet address") + .option("--base-token
", "Filter by token address") + .option("--page-token ", "Pagination cursor from previous response") + .option("--limit ", "Results per page", parseInt) + .option("--raw", "Output raw JSON") + .action(async (opts) => { + validateChain(opts.chain); + const extra: Record = {}; + 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: sol / bsc / base") + .requiredOption("--from
", "Wallet address (must match API Key binding)") + .requiredOption("--order-id ", "Order ID to cancel") + .option("--close-sell-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); + }); } diff --git a/src/index.ts b/src/index.ts index 21fdf6f..b3897f2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -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}`); From 138d571f8be7283c301d5f7da50f09f48197c407 Mon Sep 17 00:00:00 2001 From: axel Date: Thu, 2 Apr 2026 21:16:23 +0800 Subject: [PATCH 2/3] docs(cooking): rewrite SKILL.md with full guided launch flow and safety infrastructure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Align opening banners with other GMGN skills (IPv6, no-guess, no-websearch) - Add Core Concepts section (bonding curve, --buy-amt human units, --dex identifiers, image input, status polling, slippage) - Add Financial Risk Notice (irreversible transactions, explicit confirmation required) - Add Guided Launch Flow (6-step wizard: chain/platform → identity → logo → social → buy-amt → confirm) - Add Rate Limit Handling with leaky-bucket weights - Add First-time setup and Credential Model sections - Add Pre-create Confirmation and Post-create Receipt output templates - Add Execution Guidelines (confirmation, dex validation, image handling, order polling) - Add --description parameter for token project pitch - Add References table linking to other GMGN skills (swap, token, market, track, portfolio) - Remove cross-skill dependencies (standalone skill, no gmgn-swap references) Co-Authored-By: Claude Sonnet 4.6 --- skills/gmgn-cooking/SKILL.md | 302 +++++++++++++++++++++++++++++++---- 1 file changed, 271 insertions(+), 31 deletions(-) diff --git a/skills/gmgn-cooking/SKILL.md b/skills/gmgn-cooking/SKILL.md index 48cd99c..4d95968 100644 --- a/skills/gmgn-cooking/SKILL.md +++ b/skills/gmgn-cooking/SKILL.md @@ -1,21 +1,50 @@ --- name: gmgn-cooking -description: "[FINANCIAL EXECUTION] Create tokens on launchpad platforms (Pump.fun, Raydium, PancakeSwap, Flap, FourMeme, Bonk, BAGS, etc.) or query token creation statistics by launchpad. Token creation executes irreversible on-chain transactions. Requires explicit user confirmation before every create. Supports sol / bsc / base." +description: "[FINANCIAL EXECUTION] Create tokens on launchpad platforms (Pump.fun, Raydium, PancakeSwap, Flap, FourMeme, Bonk, BAGS, etc.) or query token creation statistics by launchpad. Token creation executes irreversible on-chain transactions. Requires explicit user confirmation before every create. Supports sol / bsc / base / eth / ton." argument-hint: "stats | [create --chain --dex --from --name --symbol --buy-amt (--image | --image-url )]" metadata: cliHelp: "gmgn-cli cooking --help" --- -**IMPORTANT: Always use `gmgn-cli` commands below. Do NOT use web search, WebFetch, curl, or visit gmgn.ai.** +**IMPORTANT: Always use `gmgn-cli` commands below. Do NOT use web search, WebFetch, curl, or visit gmgn.ai — all token creation operations must go through the CLI. The CLI handles signing and submission automatically.** -**⚠️ Token creation executes REAL, IRREVERSIBLE blockchain transactions. Always require explicit user confirmation before running `cooking create`.** +**IMPORTANT: Do NOT guess field names or values. When a field's meaning is unclear, look it up in the Response Fields sections below before using it.** + +**⚠️ IPv6 NOT SUPPORTED: If you get a `401` or `403` error and credentials look correct, check for IPv6 immediately: (1) list all network interfaces and their IPv6 addresses — run `ifconfig | grep inet6` (macOS) or `ip addr show | grep inet6` (Linux); (2) send a test request to `https://ipv6.icanhazip.com` — if the response is an IPv6 address, outbound traffic is going via IPv6. Tell the user immediately: "Please disable IPv6 on your network interface — gmgn-cli commands only work over IPv4."** + +Use the `gmgn-cli` tool to create a token on a launchpad platform or query token creation statistics per launchpad. **Requires private key** (`GMGN_PRIVATE_KEY` in `.env`) for `cooking create`. + +## Core Concepts + +- **Bonding curve** — Most launchpad platforms (Pump.fun, FourMeme, Flap, etc.) launch tokens on an internal bonding curve. The token price rises as buyers enter. Once the threshold is reached, the token "graduates" to an open DEX (e.g. Raydium on SOL, PancakeSwap on BSC). Token creation happens on the bonding curve — not the open market. + +- **`--buy-amt` is in human units** — `--buy-amt` is expressed in full native token units, not smallest unit. `0.01` = 0.01 SOL. `0.05` = 0.05 BNB. Always confirm the human-readable amount with the user before executing. + +- **`--dex` identifiers** — Each launchpad has a fixed identifier passed to `--dex`. These are not free-form names — use only the identifiers listed in the Supported Launchpads table. Never guess a `--dex` value not in that table. + +- **Image input** — Token logo can be provided as base64-encoded data (`--image`, max 2MB decoded) or a publicly accessible URL (`--image-url`). Provide one or the other — not both. If the user gives a file path, read and base64-encode it before passing to `--image`. If they give a URL, use `--image-url` directly. + +- **Status polling via `order get`** — `cooking create` is asynchronous. The immediate response may show `pending`. Poll with `gmgn-cli order get --chain --order-id ` until `confirmed`. The new token's contract address is in the `output_token` field of the `order get` response, not in the initial create response. + +- **Critical auth** — `cooking create` requires both `GMGN_API_KEY` and `GMGN_PRIVATE_KEY`. The private key never leaves the machine — the CLI uses it only for local signing. `cooking stats` uses normal auth (API Key only). + +- **Slippage** — The initial buy is executed as part of the same transaction as token creation. Slippage applies to that buy. Use `--slippage` (decimal, e.g. `0.01` = 1%) or `--auto-slippage`. One of the two is required when `--buy-amt` is set. + +## Financial Risk Notice + +**This skill executes REAL, IRREVERSIBLE blockchain transactions.** + +- Every `cooking create` command deploys an on-chain token contract and spends real funds (initial buy amount). +- Token deployments cannot be undone once confirmed on-chain. +- The AI agent must **never auto-execute a create** — explicit user confirmation is required every time, without exception. +- Only use this skill with funds you are willing to spend. Initial buy amounts are non-refundable. ## Sub-commands | Sub-command | Description | |-------------|-------------| -| `cooking stats` | Get token creation statistics grouped by launchpad (normal auth) | -| `cooking create` | Create a token on a launchpad platform (requires private key) | +| `cooking stats` | Get token creation count statistics grouped by launchpad platform (normal auth) | +| `cooking create` | Deploy a new token on a launchpad platform (requires private key) | ## Supported Chains @@ -23,72 +52,126 @@ metadata: ## Supported Launchpads by Chain -| Chain | Supported DEX / Launchpad | -|-------|--------------------------| +| Chain | `--dex` values | +|-------|----------------| | `sol` | `pump` / `raydium` / `bonk` / `bags` / `memoo` / `letsbonk` / `bonkers` | | `bsc` | `pancakeswap` / `flap` / `fourmeme` | | `base` | `clanker` / `flaunch` / `baseapp` / `basememe` / `zora` / `virtuals_v2` | +When the user names a platform colloquially (e.g. "pump.fun", "four.meme", "PancakeSwap"), map it to the correct `--dex` identifier from this table before running the command. + ## Prerequisites - `cooking stats`: Only `GMGN_API_KEY` required - `cooking create`: Both `GMGN_API_KEY` and `GMGN_PRIVATE_KEY` must be configured in `~/.config/gmgn/.env`. The private key must correspond to the wallet bound to the API Key. - `gmgn-cli` installed globally — if missing, run: `npm install -g gmgn-cli` +## Rate Limit Handling + +All cooking routes go through GMGN's leaky-bucket limiter with `rate=10` and `capacity=10`. Sustained throughput is roughly `10 ÷ weight` requests/second. + +| Command | Weight | +|---------|--------| +| `cooking create` | 5 | +| `cooking stats` | 1 | + +When a request returns `429`: + +- Read `X-RateLimit-Reset` from the response headers — Unix timestamp for when the limit resets. +- `cooking create` is a real transaction: **never loop or auto-resubmit** after a `429`. Wait until the reset time, then ask for confirmation again before retrying. +- For `RATE_LIMIT_EXCEEDED` or `RATE_LIMIT_BANNED`, repeated requests during cooldown extend the ban by 5 seconds each time, up to 5 minutes. + +**First-time setup** (if credentials are not configured): + +1. Generate key pair and show the public key to the user: + ```bash + openssl genpkey -algorithm ed25519 -out /tmp/gmgn_private.pem 2>/dev/null && \ + openssl pkey -in /tmp/gmgn_private.pem -pubout 2>/dev/null + ``` + Tell the user: *"This is your Ed25519 public key. Go to **https://gmgn.ai/ai**, paste it into the API key creation form (enable swap/cooking capability), then send me the API Key value shown on the page."* + +2. Wait for the user's API key, then configure both credentials: + ```bash + mkdir -p ~/.config/gmgn + echo 'GMGN_API_KEY=' > ~/.config/gmgn/.env + echo 'GMGN_PRIVATE_KEY=""' >> ~/.config/gmgn/.env + chmod 600 ~/.config/gmgn/.env + ``` + +### Credential Model + +- `GMGN_PRIVATE_KEY` is used exclusively for **local message signing** — the private key never leaves the machine. The CLI computes an Ed25519 signature in-process and transmits only the base64-encoded result in the `X-Signature` request header. +- `GMGN_API_KEY` is transmitted in the `X-APIKEY` header over HTTPS. +- Neither credential is ever passed as a command-line argument. + ## `cooking stats` Usage ```bash gmgn-cli cooking stats [--raw] ``` -**Response fields (data array):** +### `cooking stats` Response Fields | Field | Type | Description | |-------|------|-------------| | `launchpad` | string | Launchpad identifier (e.g. `pump`, `raydium`, `pancakeswap`) | -| `token_count` | int | Number of tokens created on that launchpad | +| `token_count` | int | Number of tokens created via GMGN on that launchpad | ## `cooking create` Parameters | Parameter | Required | Description | |-----------|----------|-------------| | `--chain` | Yes | Chain: `sol` / `bsc` / `base` / `eth` / `ton` | -| `--dex` | Yes | Launchpad platform (see Supported Launchpads table) | +| `--dex` | Yes | Launchpad platform identifier — see Supported Launchpads table. Never guess this value. | | `--from` | Yes | Wallet address (must match API Key binding) | -| `--name` | Yes | Token name | -| `--symbol` | Yes | Token symbol | -| `--buy-amt` | Yes | Initial buy amount in native token (e.g. `0.01` for 0.01 SOL) | -| `--image` | No* | Token logo as base64-encoded data (max 2MB decoded). One of `--image` or `--image-url` is required | -| `--image-url` | No* | Token logo URL. One of `--image` or `--image-url` is required | -| `--website` | No | Website URL | -| `--twitter` | No | Twitter link | -| `--telegram` | No | Telegram link | -| `--slippage` | No | Slippage tolerance, e.g. `0.01` = 1%. Mutually exclusive with `--auto-slippage` | -| `--auto-slippage` | No | Enable automatic slippage | +| `--name` | Yes | Token full name (e.g. `Doge Killer`) | +| `--symbol` | Yes | Token ticker symbol (e.g. `DOGEK`) | +| `--description` | No | Token description / project pitch | +| `--buy-amt` | Yes | Initial buy amount in **human-readable native token units** (e.g. `0.01` = 0.01 SOL). This is NOT in smallest unit. | +| `--image` | No* | Token logo as **base64-encoded** data (max 2MB decoded). Mutually exclusive with `--image-url`. One of the two is required. | +| `--image-url` | No* | Token logo as a publicly accessible URL. Mutually exclusive with `--image`. One of the two is required. | +| `--slippage` | No* | Slippage tolerance, e.g. `0.01` = 1%. **Mutually exclusive with `--auto-slippage`** — provide one or the other. | +| `--auto-slippage` | No* | Enable automatic slippage. **Mutually exclusive with `--slippage`.** | +| `--website` | No | Project website URL | +| `--twitter` | No | Twitter / X URL | +| `--telegram` | No | Telegram group URL | | `--priority-fee` | No | Priority fee in SOL (SOL only, ≥ 0.0001 SOL) | | `--tip-fee` | No | Tip fee (SOL ≥ 0.00001 / BSC ≥ 0.000001 BNB; ignored on ETH/BASE) | | `--gas-price` | No | Gas price in wei (EVM chains) | | `--anti-mev` | No | Enable anti-MEV protection | +\* `--image` or `--image-url`: provide exactly one. `--slippage` or `--auto-slippage`: provide exactly one. + ## `cooking create` Response Fields | Field | Type | Description | |-------|------|-------------| | `status` | string | `pending` / `confirmed` / `failed` | -| `hash` | string | Transaction hash (may be empty while pending) | -| `order_id` | string | Order ID for polling via `order get` | +| `hash` | string | Transaction hash (may be empty while `pending`) | +| `order_id` | string | Order ID — pass to `gmgn-cli order get` to poll for final status | | `error_code` | string | Error code on failure | | `error_status` | string | Error description on failure | -Token creation is asynchronous. If `status` is `pending`, poll with `gmgn-cli order get --chain --order-id ` every 2 seconds (up to 30 seconds). The new token's mint address is available in the order detail's `output_token` field once confirmed. +## Status Polling + +Token creation is **asynchronous**. If the initial `cooking create` response shows `status: pending`: + +1. Poll with `gmgn-cli order get` every **2 seconds**, up to **30 seconds**: + ```bash + gmgn-cli order get --chain --order-id + ``` +2. The new token's contract / mint address is in the **`output_token`** field of the `order get` response — it is NOT returned by `cooking create` directly. +3. Stop polling once `status` is `confirmed`, `failed`, or `expired`. +4. On `confirmed`: display `output_token` as the token address and include the block explorer link. +5. On `failed` / `expired`: report the `error_status` and do not retry automatically. ## Usage Examples ```bash -# Get launchpad creation statistics +# Get token creation statistics per launchpad gmgn-cli cooking stats -# Create a token on Pump.fun (SOL) +# Create a token on Pump.fun (SOL) — with URL image gmgn-cli cooking create \ --chain sol \ --dex pump \ @@ -100,24 +183,181 @@ gmgn-cli cooking create \ --slippage 0.01 \ --priority-fee 0.001 -# Create a token on PancakeSwap (BSC) +# Create a token on PancakeSwap (BSC) — with URL image and social links gmgn-cli cooking create \ --chain bsc \ --dex pancakeswap \ --from \ --name "BSC Token" \ --symbol BSCT \ - --buy-amt 0.01 \ + --buy-amt 0.05 \ --image-url https://example.com/logo.png \ --slippage 0.02 \ --gas-price 5000000000 \ --website https://mytoken.io \ --twitter https://twitter.com/mytoken + +# Create a token on FourMeme (BSC) — using base64 image from local file +gmgn-cli cooking create \ + --chain bsc \ + --dex fourmeme \ + --from \ + --name "Four Token" \ + --symbol FOUR \ + --buy-amt 0.05 \ + --image "$(base64 -i /path/to/logo.png)" \ + --auto-slippage + +# Create a token on letsbonk (SOL) +gmgn-cli cooking create \ + --chain sol \ + --dex letsbonk \ + --from \ + --name "Bonk Token" \ + --symbol BNKT \ + --buy-amt 0.01 \ + --image-url https://example.com/logo.png \ + --auto-slippage \ + --anti-mev + ``` +## Output Format + +### Pre-create Confirmation + +Before every `cooking create`, present this summary and wait for explicit user confirmation: + +``` +⚠️ Token Creation Confirmation Required + +Chain: {chain} +Platform: {--dex} (e.g. pump / fourmeme) +Wallet: {--from} +Token Name: {--name} +Symbol: {--symbol} +Initial Buy: {--buy-amt} {native currency} (e.g. 0.01 SOL) +Slippage: {--slippage}% (or "auto") +Image: {--image-url or "base64 provided"} +Social: {twitter / telegram / website if provided} + +Reply "confirm" to deploy this token. This action is IRREVERSIBLE. +``` + +### Post-create Receipt + +After polling confirms a successful deployment: + +``` +✅ Token Created + +Token: {--name} ({--symbol}) +Address: {output_token from order get} +Chain: {chain} +Platform: {--dex} +Tx: {explorer link for hash} +Order ID: {order_id} +``` + +Block explorer links: + +| Chain | Explorer | +|-------|----------| +| sol | `https://solscan.io/tx/` | +| bsc | `https://bscscan.com/tx/` | +| base | `https://basescan.org/tx/` | +| eth | `https://etherscan.io/tx/` | + +## Guided Launch Flow + +When a user says they want to launch / create / deploy a token but has not provided all required information, do NOT ask for everything at once. Collect information in this order, one step at a time: + +### Step 1 — Chain & Platform + +Ask: *"Which chain and platform do you want to launch on?"* + +Show the available options: + +| Chain | Platform | `--dex` | +|-------|----------|---------| +| Solana | Pump.fun | `pump` | +| Solana | letsbonk | `letsbonk` | +| Solana | Raydium | `raydium` | +| Solana | BAGS | `bags` | +| Solana | Memoo | `memoo` | +| Solana | Bonkers | `bonkers` | +| BSC | FourMeme | `fourmeme` | +| BSC | PancakeSwap | `pancakeswap` | +| BSC | Flap | `flap` | +| Base | Clanker | `clanker` | +| Base | Zora | `zora` | +| Base | Flaunch | `flaunch` | +| Base | Virtuals | `virtuals_v2` | + +If the user is unsure, recommend: **Pump.fun (SOL)** or **FourMeme (BSC)** — the two most active launchpads. + +### Step 2 — Token Identity + +Ask for name, symbol, and description together: + +*"What is your token's name, ticker symbol, and a short description?"* + +- Name: the full display name (e.g. `Doge Killer`) +- Symbol: short uppercase ticker, typically 3–8 characters (e.g. `DOGEK`) +- Description: optional but recommended — a one-line pitch shown on the launchpad + +### Step 3 — Logo + +Ask: *"Do you have a logo image? You can share a file path (e.g. `/Users/you/logo.png`) or an image URL."* + +- If the user provides a **file path**: silently run `base64 -i ` and pass the result to `--image`. Do not mention "base64" to the user. +- If the user provides a **URL**: use `--image-url` directly. +- If the user has no logo: note that most platforms accept a launch without one, but it significantly reduces visibility. Ask if they want to proceed without it. + +### Step 4 — Social Links (optional) + +Ask: *"Do you have a Twitter, Telegram, or website to attach to the token? You can skip this."* + +Collect any combination of `--twitter`, `--telegram`, `--website`. If the user skips, proceed to the next step. + +### Step 5 — Initial Buy Amount + +Ask: *"How much {SOL / BNB / ETH} do you want to spend on the initial buy?"* + +Pass the user's answer directly to `--buy-amt` — this is already in full token units (e.g. `0.01` = 0.01 SOL), do NOT convert to lamports or wei. + +### Step 6 — Confirmation & Execute + +Once all information is collected, present the pre-create confirmation summary (see Output Format section) and wait for the user to reply "confirm" before executing. + +--- + +## Execution Guidelines + +- **[REQUIRED] Pre-create confirmation** — Before executing `cooking create`, present the full summary above and receive explicit "confirm" from the user. No exceptions. Do NOT auto-create. +- **[REQUIRED] `--dex` validation** — Before running, look up the user's named platform in the Supported Launchpads table and resolve to the correct `--dex` identifier. Never guess or pass a freeform platform name. If the chain/platform combination is not in the table, tell the user it is unsupported. +- **Slippage requirement** — Either `--slippage` or `--auto-slippage` must be provided. If the user did not specify, suggest `--auto-slippage` for volatile new tokens or ask for a preference. +- **Image handling** — If the user provides a file path, run `base64 -i ` and pass the result to `--image`. If they provide a URL, use `--image-url`. If neither is provided, ask before building the confirmation — most platforms require a logo. +- **Address validation** — Validate `--from` wallet address format before submitting: + - `sol`: base58, 32–44 characters + - `bsc` / `base` / `eth`: `0x` + 40 hex digits +- **Chain-wallet compatibility** — SOL addresses are incompatible with EVM chains and vice versa. Warn the user and abort if the address format does not match the chain. +- **Order polling** — After `cooking create`, if `status` is `pending`, poll `order get` every 2 seconds up to 30 seconds. The token address is in `output_token`. Do not report success until `status` is `confirmed`. +- **Credential sensitivity** — `GMGN_API_KEY` and `GMGN_PRIVATE_KEY` can execute real transactions. Never log, display, or expose these values. + ## Notes -- `cooking create` uses **critical auth** (API Key + signature) — CLI handles signing automatically -- Either `--image` (base64) or `--image-url` is required; both `--slippage` and `--auto-slippage` cannot be omitted (provide one) -- After creation, poll `order get` to get `confirmed` status and the new token's mint address from `output_token` -- Use `--raw` to get single-line JSON for further processing +- `cooking create` uses **critical auth** (API Key + signature) — CLI handles signing automatically. +- `cooking stats` uses normal auth (API Key only — no private key needed). +- The new token's mint address is in `output_token` from `gmgn-cli order get`, not in the initial `cooking create` response. +- Use `--raw` on any command to get single-line JSON for further processing. + +## References + +| Skill | Description | +|-------|-------------| +| [gmgn-swap](https://github.com/GMGNAI/gmgn-skills/tree/main/skills/gmgn-swap) | Contains `order get` command used for polling token creation status | +| [gmgn-token](https://github.com/GMGNAI/gmgn-skills/tree/main/skills/gmgn-token) | Token security check, info, holders, and traders — useful after launch to monitor your token | +| [gmgn-market](https://github.com/GMGNAI/gmgn-skills/tree/main/skills/gmgn-market) | `market trenches` for tracking bonding curve progress; `market trending` to see if your token is gaining traction | +| [gmgn-track](https://github.com/GMGNAI/gmgn-skills/tree/main/skills/gmgn-track) | Smart money and KOL trade tracking — monitor whether smart wallets are buying your token after launch | +| [gmgn-portfolio](https://github.com/GMGNAI/gmgn-skills/tree/main/skills/gmgn-portfolio) | Wallet holdings and P&L — check your own wallet balance before deciding on `--buy-amt` | From cb21fff38c0d1254ff4ae8b9179438105139077d Mon Sep 17 00:00:00 2001 From: David Lau Date: Thu, 2 Apr 2026 21:50:41 +0800 Subject: [PATCH 3/3] fix(strategy): add missing order_type/sub_order_type, condition_orders, and group_tag MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add --order-type and --sub-order-type as required flags to order strategy create (server requires both; omitting them caused every create call to 400) - Remove --side from strategy create (field no longer exists in the API) - Make --open-price optional in strategy create (matches API spec) - Fix --gas-price in strategy create to convert gwei→wei (consistent with swap) - Add --condition-orders and --sell-ratio-type to swap command for take-profit/stop-loss conditions attached after a successful swap - Add --group-tag (LimitOrder/STMix) to order strategy list - Add --order-type to order strategy cancel - Add StrategyConditionOrder interface; extend SwapParams with condition_orders and sell_ratio_type; add order_type/sub_order_type to StrategyCreateParams; remove side; add order_type to StrategyCancelParams - Update docs/cli-usage.md: fix strategy create table, add chain fee requirement notes (SOL requires priority_fee+tip_fee; BSC requires gas_price), add condition_orders example and strategy_order_id response field, add TRADING_BOT_UNAVAILABLE error code Co-Authored-By: Claude Sonnet 4.6 --- docs/cli-usage.md | 43 ++++++++++++++++++++++++++++--------- src/client/OpenApiClient.ts | 15 +++++++++++-- src/commands/swap.ts | 33 +++++++++++++++++++++------- 3 files changed, 71 insertions(+), 20 deletions(-) diff --git a/docs/cli-usage.md b/docs/cli-usage.md index 6677dde..5fd41e0 100644 --- a/docs/cli-usage.md +++ b/docs/cli-usage.md @@ -400,6 +400,8 @@ npx gmgn-cli swap \ [--gas-price ] \ [--max-fee-per-gas ] \ [--max-priority-fee-per-gas ] \ + [--condition-orders ] \ + [--sell-ratio-type ] \ [--raw] ``` @@ -421,6 +423,16 @@ npx gmgn-cli swap \ | `--gas-price` | No | Gas price in gwei (BSC ≥ 0.05 gwei / BASE/ETH ≥ 0.01 gwei) | | `--max-fee-per-gas` | No | EIP-1559 max fee per gas (Base/ETH only) | | `--max-priority-fee-per-gas` | No | EIP-1559 max priority fee per gas (Base/ETH only) | +| `--condition-orders` | No | JSON array of take-profit/stop-loss conditions attached after a successful swap (see example below); only `profit_stop` and `loss_stop` are supported | +| `--sell-ratio-type` | No | Sell ratio base for `--condition-orders`: `buy_amount` (default) / `hold_amount` | + +**`--condition-orders` example** (100% sell at 2× price, 100% sell at 50% price): + +```json +[{"order_type":"profit_stop","side":"sell","price_scale":"100","sell_ratio":"100"},{"order_type":"loss_stop","side":"sell","price_scale":"50","sell_ratio":"100"}] +``` + +> Strategy creation is **best-effort**: if the swap succeeds but strategy creation fails, the swap result is still returned (with `strategy_order_id` absent). Only `order_type`, `side`, `price_scale`, and `sell_ratio` are accepted per condition — extra fields cause a 400 error. **Response fields (data):** @@ -439,6 +451,7 @@ npx gmgn-cli swap \ | `output_token` | string | Output token contract address | | `filled_input_amount` | string | Actual input consumed (smallest unit); empty if not filled | | `filled_output_amount` | string | Actual output received (smallest unit); empty if not filled | +| `strategy_order_id` | string | Strategy order ID; only present when `--condition-orders` was passed and strategy creation succeeded | --- @@ -467,9 +480,10 @@ gmgn-cli order strategy create \ --from \ --base-token \ --quote-token \ - --side \ - --open-price \ + --order-type \ + --sub-order-type \ --check-price \ + [--open-price ] \ [--amount-in | --amount-in-percent ] \ [--slippage | --auto-slippage] \ [--limit-price-mode ] \ @@ -477,7 +491,7 @@ gmgn-cli order strategy create \ [--sell-ratio-type ] \ [--priority-fee ] \ [--tip-fee ] \ - [--gas-price ] \ + [--gas-price ] \ [--anti-mev] \ [--raw] ``` @@ -488,9 +502,10 @@ gmgn-cli order strategy create \ | `--from` | Yes | Wallet address (must match API Key binding) | | `--base-token` | Yes | Base token contract address | | `--quote-token` | Yes | Quote token contract address | -| `--side` | Yes | `buy` / `sell` | -| `--open-price` | Yes | Open price | +| `--order-type` | Yes | Order type: `limit_order` | +| `--sub-order-type` | Yes | Sub-order type: `buy_low` / `buy_high` / `stop_loss` / `take_profit` | | `--check-price` | Yes | Trigger check price | +| `--open-price` | No | Open/entry price | | `--amount-in` | No* | Input amount (smallest unit); required unless `--amount-in-percent` is used | | `--amount-in-percent` | No* | Input as percentage (e.g. `50` = 50%); required unless `--amount-in` is used | | `--limit-price-mode` | No | `exact` / `slippage` (default: `slippage`) | @@ -498,11 +513,16 @@ gmgn-cli order strategy create \ | `--sell-ratio-type` | No | `buy_amount` (default) / `hold_amount` | | `--slippage` | No | Slippage tolerance, e.g. `0.01` = 1% | | `--auto-slippage` | No | Enable automatic slippage | -| `--priority-fee` | No | Priority fee in SOL (SOL only) | -| `--tip-fee` | No | Tip fee | -| `--gas-price` | No | Gas price in wei (EVM chains) | +| `--priority-fee` | No | Priority fee in SOL (**required for SOL chain**) | +| `--tip-fee` | No | Tip fee (**required for SOL chain**) | +| `--gas-price` | No | Gas price in gwei (**required for BSC**; ≥ 0.05 / BASE/ETH ≥ 0.01) | | `--anti-mev` | No | Enable anti-MEV protection | +> **Chain-specific fee requirements:** +> - **SOL:** `--priority-fee` and `--tip-fee` are both **required** (returns 400 if missing) +> - **BSC:** `--gas-price` is **required** (returns 400 if missing) +> - **ETH/BASE:** no required fee fields + **Response fields (data):** | Field | Type | Description | @@ -517,7 +537,7 @@ gmgn-cli order strategy create \ List strategy orders. Uses normal auth. ```bash -gmgn-cli order strategy list --chain [--type ] [--from
] [--base-token
] [--page-token ] [--limit ] [--raw] +gmgn-cli order strategy list --chain [--type ] [--from
] [--group-tag ] [--base-token
] [--page-token ] [--limit ] [--raw] ``` | Option | Required | Description | @@ -525,6 +545,7 @@ gmgn-cli order strategy list --chain [--type ] [--from [--type ] [--from --from --order-id [--close-sell-model ] [--raw] +gmgn-cli order strategy cancel --chain --from --order-id [--order-type ] [--close-sell-model ] [--raw] ``` | Option | Required | Description | @@ -552,6 +573,7 @@ gmgn-cli order strategy cancel --chain --from --order-i | `--chain` | Yes | `sol` / `bsc` / `base` | | `--from` | Yes | Wallet address (must match API Key binding) | | `--order-id` | Yes | Order ID to cancel | +| `--order-type` | No | Order type: `limit_order` / `smart_trade` | | `--close-sell-model` | No | Sell model when closing | --- @@ -662,4 +684,5 @@ Important notes: | `BAD_REQUEST` | 400 | Missing or invalid request parameters | | `INTERNAL_API_UNAVAILABLE` | 502 | Downstream market API unavailable | | `BROKER_UNAVAILABLE` | 502 | Downstream trade broker unavailable | +| `TRADING_BOT_UNAVAILABLE` | 502 | Trading bot service unreachable (strategy endpoints) | | `INTERNAL_ERROR` | 500 | Internal server error | diff --git a/src/client/OpenApiClient.ts b/src/client/OpenApiClient.ts index c9d2ce6..cb51e3c 100644 --- a/src/client/OpenApiClient.ts +++ b/src/client/OpenApiClient.ts @@ -85,6 +85,15 @@ export interface SwapParams { gas_price?: string; max_fee_per_gas?: string; max_priority_fee_per_gas?: string; + condition_orders?: StrategyConditionOrder[]; + sell_ratio_type?: string; +} + +export interface StrategyConditionOrder { + order_type: string; // "profit_stop" | "loss_stop" + side: string; // "sell" + price_scale: string; + sell_ratio: string; } export interface StrategyCreateParams { @@ -92,9 +101,10 @@ export interface StrategyCreateParams { from_address: string; base_token: string; quote_token: string; - side: string; - open_price: string; + order_type: string; + sub_order_type: string; check_price: string; + open_price?: string; amount_in?: string; amount_in_percent?: string; limit_price_mode?: string; @@ -118,6 +128,7 @@ export interface StrategyCancelParams { chain: string; from_address: string; order_id: string; + order_type?: string; close_sell_model?: string; } diff --git a/src/commands/swap.ts b/src/commands/swap.ts index d4d9f7d..7a3abff 100644 --- a/src/commands/swap.ts +++ b/src/commands/swap.ts @@ -24,6 +24,8 @@ export function registerSwapCommands(program: Command): void { .option("--gas-price ", "Gas price in gwei (BSC ≥ 0.05 / BASE/ETH ≥ 0.01)") .option("--max-fee-per-gas ", "EIP-1559 max fee per gas (Base)") .option("--max-priority-fee-per-gas ", "EIP-1559 max priority fee per gas (Base)") + .option("--condition-orders ", 'JSON array of take-profit/stop-loss conditions, e.g. \'[{"order_type":"profit_stop","side":"sell","price_scale":"150","sell_ratio":"100"}]\'') + .option("--sell-ratio-type ", "Sell ratio base: buy_amount (default) / hold_amount; only used with --condition-orders") .option("--raw", "Output raw JSON") .action(async (opts) => { if (opts.percent == null && !opts.amount) { @@ -54,6 +56,15 @@ export function registerSwapCommands(program: Command): void { if (opts.gasPrice) params.gas_price = String(Math.round(parseFloat(opts.gasPrice) * 1e9)); if (opts.maxFeePerGas) params.max_fee_per_gas = opts.maxFeePerGas; if (opts.maxPriorityFeePerGas) params.max_priority_fee_per_gas = opts.maxPriorityFeePerGas; + if (opts.conditionOrders) { + try { + params.condition_orders = JSON.parse(opts.conditionOrders); + } catch { + console.error("[gmgn-cli] --condition-orders must be valid JSON"); + process.exit(1); + } + } + if (opts.sellRatioType) params.sell_ratio_type = opts.sellRatioType; const client = new OpenApiClient(getConfig(true)); const data = await client.swap(params).catch(exitOnError); @@ -107,9 +118,10 @@ export function registerSwapCommands(program: Command): void { .requiredOption("--from
", "Wallet address (must match API Key binding)") .requiredOption("--base-token
", "Base token contract address") .requiredOption("--quote-token
", "Quote token contract address") - .requiredOption("--side ", "Direction: buy / sell") - .requiredOption("--open-price ", "Open price") + .requiredOption("--order-type ", "Order type: limit_order") + .requiredOption("--sub-order-type ", "Sub-order type: buy_low / buy_high / stop_loss / take_profit") .requiredOption("--check-price ", "Trigger check price") + .option("--open-price ", "Open/entry price") .option("--amount-in ", "Input amount (smallest unit)") .option("--amount-in-percent ", "Input amount as a percentage (e.g. 50 = 50%)") .option("--limit-price-mode ", "Price mode: exact / slippage (default: slippage)") @@ -117,9 +129,9 @@ export function registerSwapCommands(program: Command): void { .option("--sell-ratio-type ", "Sell ratio basis: buy_amount (default) / hold_amount") .option("--slippage ", "Slippage tolerance (e.g. 0.01 = 1%)", parseFloat) .option("--auto-slippage", "Enable automatic slippage") - .option("--priority-fee ", "Priority fee in SOL (SOL only)") - .option("--tip-fee ", "Tip fee") - .option("--gas-price ", "Gas price in wei (EVM chains)") + .option("--priority-fee ", "Priority fee in SOL (required for SOL chain)") + .option("--tip-fee ", "Tip fee (required for SOL chain)") + .option("--gas-price ", "Gas price in gwei (required for BSC; ≥ 0.05 gwei / BASE/ETH ≥ 0.01 gwei)") .option("--anti-mev", "Enable anti-MEV protection") .option("--raw", "Output raw JSON") .action(async (opts) => { @@ -137,10 +149,11 @@ export function registerSwapCommands(program: Command): void { from_address: opts.from, base_token: opts.baseToken, quote_token: opts.quoteToken, - side: opts.side, - open_price: opts.openPrice, + order_type: opts.orderType, + sub_order_type: opts.subOrderType, check_price: opts.checkPrice, }; + if (opts.openPrice) params.open_price = opts.openPrice; 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; @@ -150,7 +163,7 @@ export function registerSwapCommands(program: Command): void { 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.gasPrice) params.gas_price = String(Math.round(parseFloat(opts.gasPrice) * 1e9)); if (opts.antiMev) params.is_anti_mev = true; const client = new OpenApiClient(getConfig(true)); const data = await client.createStrategyOrder(params).catch(exitOnError); @@ -163,6 +176,7 @@ export function registerSwapCommands(program: Command): void { .requiredOption("--chain ", "Chain: sol / bsc / base") .option("--type ", "open (default) / history") .option("--from
", "Filter by wallet address") + .option("--group-tag ", "Filter by group: LimitOrder / STMix") .option("--base-token
", "Filter by token address") .option("--page-token ", "Pagination cursor from previous response") .option("--limit ", "Results per page", parseInt) @@ -172,6 +186,7 @@ export function registerSwapCommands(program: Command): void { const extra: Record = {}; if (opts.type) extra["type"] = opts.type; if (opts.from) extra["from_address"] = opts.from; + if (opts.groupTag) extra["group_tag"] = opts.groupTag; if (opts.baseToken) extra["base_token"] = opts.baseToken; if (opts.pageToken) extra["page_token"] = opts.pageToken; if (opts.limit != null) extra["limit"] = opts.limit; @@ -186,6 +201,7 @@ export function registerSwapCommands(program: Command): void { .requiredOption("--chain ", "Chain: sol / bsc / base") .requiredOption("--from
", "Wallet address (must match API Key binding)") .requiredOption("--order-id ", "Order ID to cancel") + .option("--order-type ", "Order type: limit_order / smart_trade") .option("--close-sell-model ", "Sell model when closing") .option("--raw", "Output raw JSON") .action(async (opts) => { @@ -195,6 +211,7 @@ export function registerSwapCommands(program: Command): void { from_address: opts.from, order_id: opts.orderId, }; + if (opts.orderType) params.order_type = opts.orderType; if (opts.closeSellModel) params.close_sell_model = opts.closeSellModel; const client = new OpenApiClient(getConfig(true)); const data = await client.cancelStrategyOrder(params).catch(exitOnError);