diff --git a/Readme.md b/Readme.md index 95fb0e1..1f766e1 100644 --- a/Readme.md +++ b/Readme.md @@ -158,7 +158,7 @@ echo 'GMGN_API_KEY=' > ~/.config/gmgn/.env chmod 600 ~/.config/gmgn/.env ``` -If the user also needs swap capability, append the private key: +If the user also needs swap capability or BSC order quotes, append the private key: ```bash # Append private key (use the PEM content from Step 3) @@ -500,7 +500,7 @@ gmgn-cli track smartmoney --limit 100 --raw gmgn-cli track smartmoney --chain sol --side sell --limit 50 --raw ``` -### Swap (requires private key) +### Swap / Quote / Query ```bash # Submit swap with fixed slippage @@ -539,10 +539,21 @@ gmgn-cli order quote \ --amount 1000000 \ --slippage 0.01 +# BSC quotes use critical auth and require GMGN_PRIVATE_KEY +gmgn-cli order quote \ + --chain bsc \ + --from \ + --input-token \ + --output-token \ + --amount 1000000000000000000 \ + --slippage 0.01 + # Query order gmgn-cli order get --chain sol --order-id ``` +> `order quote` uses API Key only on `sol` / `base`. On `bsc`, the same route uses critical auth and requires `GMGN_PRIVATE_KEY`. + ### Swap with Take-Profit / Stop-Loss Orders (requires private key) **`hold_amount` mode** — each condition order fires based on current holdings at trigger time: diff --git a/Readme.zh.md b/Readme.zh.md index bad5b34..b04b3e8 100644 --- a/Readme.zh.md +++ b/Readme.zh.md @@ -164,7 +164,7 @@ echo 'GMGN_API_KEY=' > ~/.config/gmgn/.env chmod 600 ~/.config/gmgn/.env ``` -如果用户需要 swap 能力,追加私钥: +如果用户需要 swap 能力或 BSC 链上的 `order quote`,追加私钥: ```bash # 追加私钥(使用第 3 步输出的 PEM 内容) @@ -532,7 +532,7 @@ gmgn-cli track smartmoney --limit 100 --raw gmgn-cli track smartmoney --chain sol --side sell --limit 50 --raw ``` -### Swap(需要私钥) +### Swap / Quote / Query ```bash # 提交兑换(固定滑点) @@ -571,10 +571,21 @@ gmgn-cli order quote \ --amount 1000000 \ --slippage 0.01 +# BSC 报价走关键鉴权,需要 GMGN_PRIVATE_KEY +gmgn-cli order quote \ + --chain bsc \ + --from \ + --input-token \ + --output-token \ + --amount 1000000000000000000 \ + --slippage 0.01 + # 查询订单状态 gmgn-cli order get --chain sol --order-id ``` +> `order quote` 在 `sol` / `base` 上仍只需要 API Key;在 `bsc` 上改为关键鉴权,必须配置 `GMGN_PRIVATE_KEY`。 + ### 带止盈止损的 Swap(需要私钥) **`hold_amount` 模式** — 按触发时的实际持仓比例卖出: diff --git a/docs/cli-usage.md b/docs/cli-usage.md index 9797a49..0c498f2 100644 --- a/docs/cli-usage.md +++ b/docs/cli-usage.md @@ -344,7 +344,7 @@ npx gmgn-cli portfolio smartmoney [--limit ] [--raw] ## order quote -Get a swap quote without submitting a transaction. Uses normal auth — no private key required. +Get a swap quote without submitting a transaction. `sol` / `base` use normal auth (API Key only). `bsc` uses critical auth and requires `GMGN_PRIVATE_KEY`. ```bash npx gmgn-cli order quote \ @@ -359,7 +359,7 @@ npx gmgn-cli order quote \ | Option | Required | Description | |--------|----------|-------------| -| `--chain` | Yes | `sol` / `bsc` / `base` | +| `--chain` | Yes | `sol` / `bsc` / `base` (`bsc` quote requires `GMGN_PRIVATE_KEY`) | | `--from` | Yes | Wallet address (must match API Key binding) | | `--input-token` | Yes | Input token contract address | | `--output-token` | Yes | Output token contract address | diff --git a/skills/gmgn-swap/SKILL.md b/skills/gmgn-swap/SKILL.md index 865146d..6def277 100644 --- a/skills/gmgn-swap/SKILL.md +++ b/skills/gmgn-swap/SKILL.md @@ -12,7 +12,7 @@ metadata: **⚠️ 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 submit a token swap or query an existing order. **Requires private key** (`GMGN_PRIVATE_KEY` in `.env`). +Use the `gmgn-cli` tool to submit a token swap or query an existing order. `GMGN_API_KEY` is always required. `GMGN_PRIVATE_KEY` is required for critical-auth commands such as `swap`, `order get`, strategy order commands, and `order quote --chain bsc`. ## Core Concepts @@ -26,7 +26,7 @@ Use the `gmgn-cli` tool to submit a token swap or query an existing order. **Req - **Anti-MEV** — MEV (Miner/Maximal Extractable Value) refers to frontrunning and sandwich attacks where bots exploit pending transactions. `--anti-mev` routes the transaction through protected channels to reduce this risk. **Recommended: always enable.** Default: on. -- **Critical auth** — `swap` requires both `GMGN_API_KEY` and `GMGN_PRIVATE_KEY`. The private key never leaves the machine — the CLI uses it only for local signing and sends only the resulting signature. Normal commands (like `order quote`) use API Key alone. +- **Critical auth** — `swap`, `order get`, strategy order commands, and `order quote --chain bsc` require both `GMGN_API_KEY` and `GMGN_PRIVATE_KEY`. The private key never leaves the machine — the CLI uses it only for local signing and sends only the resulting signature. `order quote` on `sol` / `base` still uses API Key alone. - **`order_id` / `status`** — After submitting a swap, the response includes an `order_id`. Use `order get --order-id` to poll for final status. Possible values: `pending` → `processed` → `confirmed` (success) or `failed` / `expired`. Do not report success until status is `confirmed`. @@ -46,7 +46,7 @@ Use the `gmgn-cli` tool to submit a token swap or query an existing order. **Req | Sub-command | Description | |-------------|-------------| | `swap` | Submit a token swap | -| `order quote` | Get a swap quote (no transaction submitted) | +| `order quote` | Get a swap quote (no transaction submitted; BSC uses critical auth) | | `order get` | Query order status | | `order strategy create` | Create a limit/strategy order (requires private key) | | `order strategy list` | List strategy orders (requires private key) | @@ -70,7 +70,7 @@ Currency tokens are the base/native assets of each chain. They are used to buy o ## Prerequisites -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_API_KEY` must be configured in `~/.config/gmgn/.env`. `GMGN_PRIVATE_KEY` is additionally required for `swap`, `order get`, strategy order commands, and `order quote --chain bsc`. 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` @@ -165,7 +165,7 @@ gmgn-cli swap \ ## `order quote` Usage -Get an estimated output amount before submitting a swap. Uses normal auth — no private key required. +Get an estimated output amount before submitting a swap. `sol` / `base` use normal auth (API Key only). `bsc` uses critical auth and requires `GMGN_PRIVATE_KEY`. ```bash gmgn-cli order quote \ @@ -292,7 +292,7 @@ gmgn-cli swap \ ### Pre-swap Confirmation -Before displaying the confirmation, run `order quote` to get the estimated output (uses normal auth — no private key required): +Before displaying the confirmation, run `order quote` to get the estimated output (`sol` / `base` use normal auth; `bsc` requires critical auth and `GMGN_PRIVATE_KEY`): ```bash gmgn-cli order quote \ diff --git a/src/client/OpenApiClient.ts b/src/client/OpenApiClient.ts index d0224a6..1b65f72 100644 --- a/src/client/OpenApiClient.ts +++ b/src/client/OpenApiClient.ts @@ -3,7 +3,7 @@ * * Auth modes: * Normal (market/token/portfolio): X-APIKEY + timestamp + client_id - * Critical (swap/order): normal auth + X-Signature (private key signature) + * Critical (swap, order get/strategy, BSC order quote): normal auth + X-Signature (private key signature) */ import { buildAuthQuery, buildMessage, detectAlgorithm, sign } from "./signer.js"; @@ -293,9 +293,11 @@ export class OpenApiClient { input_amount: string, slippage: number ): Promise { - return this.normalRequest("GET", "/v1/trade/quote", { - chain, from_address, input_token, output_token, input_amount, slippage, - }); + const query = { chain, from_address, input_token, output_token, input_amount, slippage }; + if (chain === "bsc") { + return this.criticalRequest("GET", "/v1/trade/quote", query, null); + } + return this.normalRequest("GET", "/v1/trade/quote", query); } // ---- Swap endpoints (critical auth) ---- @@ -367,7 +369,7 @@ export class OpenApiClient { body: unknown ): Promise { if (!this.privateKeyPem) { - throw new Error("GMGN_PRIVATE_KEY is required for swap/order commands"); + throw new Error("GMGN_PRIVATE_KEY is required for critical-auth commands (swap, order get/strategy, and order quote on BSC)"); } return this.executePreparedRequest(() => { diff --git a/src/commands/swap.ts b/src/commands/swap.ts index c678854..799992e 100644 --- a/src/commands/swap.ts +++ b/src/commands/swap.ts @@ -75,8 +75,8 @@ export function registerSwapCommands(program: Command): void { order .command("quote") - .description("Get a swap quote without submitting a transaction") - .requiredOption("--chain ", "Chain: sol / bsc / base") + .description("Get a swap quote without submitting a transaction (BSC uses critical auth)") + .requiredOption("--chain ", "Chain: sol / bsc / base (BSC quote requires GMGN_PRIVATE_KEY)") .requiredOption("--from
", "Wallet address (must match API Key binding)") .requiredOption("--input-token
", "Input token contract address") .requiredOption("--output-token
", "Output token contract address") @@ -89,7 +89,7 @@ export function registerSwapCommands(program: Command): void { validateAddress(opts.inputToken, opts.chain, "--input-token"); validateAddress(opts.outputToken, opts.chain, "--output-token"); validatePositiveInt(opts.amount, "--amount"); - const client = new OpenApiClient(getConfig()); + const client = new OpenApiClient(getConfig(opts.chain === "bsc")); const data = await client .quoteOrder(opts.chain, opts.from, opts.inputToken, opts.outputToken, opts.amount, opts.slippage) .catch(exitOnError); diff --git a/src/config.ts b/src/config.ts index ac02273..928ae5a 100644 --- a/src/config.ts +++ b/src/config.ts @@ -14,11 +14,13 @@ export interface Config { } let _config: Config | null = null; +const PRIVATE_KEY_REQUIRED_MSG = + "GMGN_PRIVATE_KEY is required for critical-auth commands (swap, order get/strategy, and order quote on BSC)"; export function getConfig(requirePrivateKey = false): Config { if (_config) { if (requirePrivateKey && !_config.privateKeyPem) { - die("GMGN_PRIVATE_KEY is required for swap/order commands"); + die(PRIVATE_KEY_REQUIRED_MSG); } return _config; } @@ -34,7 +36,7 @@ export function getConfig(requirePrivateKey = false): Config { // Support escaped newlines (e.g. from single-line .env values) privateKeyPem = privateKey.replace(/\\n/g, "\n"); } else if (requirePrivateKey) { - die("GMGN_PRIVATE_KEY is required for swap/order commands"); + die(PRIVATE_KEY_REQUIRED_MSG); } const host = process.env.GMGN_HOST ?? "https://openapi.gmgn.ai";