Compare commits

...

35 Commits

Author SHA1 Message Date
David Lau 412531f04e fix(signer): percent-encode query params in buildMessage to match server
Server's CanonicalQueryString uses url.QueryEscape on all keys and values,
but buildMessage was using raw string interpolation. Cursor values (base64)
contain +, =, / which diverge under encoding — causing 401 on all paginated
signed requests (e.g. portfolio holdings page 2+).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 19:32:54 +08:00
GMGN.AI b0b25118aa Merge pull request #143 from GMGNAI/docs/swap-ratelimit-weights
docs(swap): add rate limit weights for strategy and multi_swap routes
2026-05-29 17:25:39 +08:00
David Lau 0a12f0615d docs(swap): add rate limit weights for strategy and multi_swap routes
- order strategy create: POST /v1/trade/strategy/create — weight 1
- order strategy cancel: POST /v1/trade/strategy/cancel — weight 1
- order strategy list:   GET  /v1/trade/strategy/orders — weight 1
- multi-swap already listed; confirms weight 5 (same as swap)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 17:15:25 +08:00
GMGN.AI 0979d14d6a Merge pull request #142 from GMGNAI/docs/strategy-order-list-response-fields
docs(swap): expand order strategy list response fields
2026-05-29 12:05:09 +08:00
David Lau 8729cff4d3 docs(swap): expand order strategy list response fields
Add full sub-tables for list[], condition_orders[], order_statistic,
and sell_param objects in the `order strategy list` response section.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 12:03:35 +08:00
GMGN.AI 0cf2e514e6 Merge pull request #141 from gina888666/fix/portfolio-activity-price-fields-and-strategy-check-price
docs: clarify price/price_usd in portfolio activity and check-price unit in strategy create
2026-05-29 12:03:13 +08:00
gina888666 693a63c723 docs(gmgn-portfolio): remove example from price field description
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 10:53:20 +08:00
gina888666 81d67808fa docs(gmgn-portfolio): fix price field description to mention quote token may vary (SOL or USDC)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 10:51:34 +08:00
gina888666 8a095a99a8 docs: clarify price/price_usd in portfolio activity and check-price unit in strategy create
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 10:47:39 +08:00
GMGN.AI 0bc73a6430 1.3.6 2026-05-27 12:08:27 +08:00
gumponchain d00a1d3ce3 fix(swap): preserve Solana address case when normalizing wallet addresses
toLowerCase() breaks base58 Solana addresses which are case-sensitive.
Only apply lowercase normalization on EVM chains (bsc/base/eth).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-27 12:06:22 +08:00
GMGN.AI 30e535617f 1.3.5 2026-05-26 20:03:37 +08:00
GMGN.AI f64a3e086b ci: add workflow_dispatch to Release workflow 2026-05-26 20:01:04 +08:00
GMGN.AI 60438dff5c 1.3.4 2026-05-26 19:55:08 +08:00
deepfeature 6f0d4464e2 Merge pull request #138 from GMGNAI/feat/wallet-holdings-critical-auth
breaking(portfolio): upgrade wallet_holdings to critical auth
2026-05-26 19:48:09 +08:00
David Lau 48e7346d8e fix(swap): normalize wallet addresses to lowercase before API call
Ensures from_address and multi-swap accounts are lowercased at the CLI
boundary to prevent case-sensitivity issues on the API side.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 13:55:25 +08:00
deepfeature 2f76b375c5 breaking(portfolio): upgrade wallet_holdings to critical auth
Align CLI with server-side breaking change: GET /v1/user/wallet_holdings
was upgraded from authExistMW to authCriticalMW on the openapi-service.

- OpenApiClient: switch getWalletHoldings to authSignedRequest
- SKILL.md: split rate-limit table by auth level; update Notes section
- CLAUDE.md: move portfolio holdings from Normal to Critical auth row

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 11:19:30 +08:00
GMGN.AI a65d5fd1cb Merge pull request #136 from gina888666/fix/token-info-skill-doc-corrections-v2
fix(gmgn-token): correct fee_distribution usage hint and volume fields note
2026-05-25 17:34:10 +08:00
gina888666 7d16bd80e8 fix(gmgn-token): correct fee_distribution usage hint and volume fields note
- Add usage context to fee_distribution section so agents know to use
  token info when querying claim status and royalty allocation
- Fix incorrect Note that said trading volume is not in token info;
  price.volume_{window}, buys_{window}, sells_{window}, swaps_{window}
  are all available in the token info price object

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 16:02:17 +08:00
GMGN.AI 9dfccd129d 1.3.3 2026-05-19 21:03:49 +08:00
GMGN.AI ce7f762e5b Merge pull request #132 from GMGNAI/feat/gas-price-all-chains
refactor(auth): rename normalRequest/criticalRequest; align auth terminology
2026-05-19 20:35:11 +08:00
David Lau d51f1c49d3 refactor(auth): rename normalRequest/criticalRequest; align auth terminology
- Rename private methods: normalRequest → authExistRequest,
  criticalRequest → authSignedRequest in OpenApiClient.ts (both gmgn-skills
  and scripts/src)
- Replace all "normal auth" / "critical auth" labels across SKILL.md files,
  cli-usage.md, Readme.md, and CLI command descriptions with:
    exist auth  — API Key only, no private key required
    signed auth — API Key + GMGN_PRIVATE_KEY signature required
- Add explicit "requires GMGN_PRIVATE_KEY" notes to signed-auth commands
  and "API Key only" notes to exist-auth commands throughout docs

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 17:45:10 +08:00
gumponchain 17b901c29f docs: add gas-price command and ETH gas control examples to README
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 16:19:22 +08:00
GMGN.AI f3738e0bd4 Merge pull request #130 from GMGNAI/feat/gas-price-all-chains
docs: remove "trading bot" references; label condition-orders-only flags
2026-05-19 15:55:37 +08:00
David Lau f05e1d5d20 docs: remove "trading bot" references; label condition-orders-only flags
- Replace "trading bot" with user-facing language throughout SKILL.md
  and cli-usage.md
- Mark --auto-fee and --sell-ratio-type as "Only with --condition-orders"
  in swap and multi-swap parameter tables
- TRADING_BOT_UNAVAILABLE error now describes the symptom, not the
  internal service name

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 15:51:11 +08:00
GMGN.AI beedf14b5b 1.3.2 2026-05-19 15:22:14 +08:00
GMGN.AI 1b9e2c24d6 Merge pull request #129 from GMGNAI/feat/gas-price-all-chains
feat(swap): promote gas-price to top-level command, support all chains
2026-05-19 15:20:59 +08:00
GMGN.AI a3dfa99763 1.3.1 2026-05-19 14:49:12 +08:00
David Lau 317c7d982b docs(swap): expand gas-price response fields to match full upstream schema
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 14:25:48 +08:00
David Lau 15872373cd feat(swap): promote gas-price to top-level command, support all chains
- Move `order gas-price` → `gmgn-cli gas-price` (top-level command)
- Expand chain support to include sol alongside eth / bsc / base
- Reorder commands in swap.ts to match SKILL.md: swap → multi-swap →
  order quote/get → gas-price → order strategy create/list/cancel

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 12:05:39 +08:00
GMGN.AI 674c7032a8 Merge pull request #126 from gina888666/fix/base-chain-no-anti-mev
fix(gmgn-swap): note that --anti-mev is not supported on base chain
2026-05-18 20:49:05 +08:00
gina888666 4649a7a287 fix(gmgn-swap): note that --anti-mev is not supported on base chain
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 20:41:12 +08:00
GMGN.AI 2b33ee666c Merge pull request #125 from GMGNAI/feat/gas-level-remove-auto-tip-fee
Feat/gas level remove auto tip fee
2026-05-18 19:13:53 +08:00
David Lau b3e99dde94 docs(swap): add Chain column to fee params; reorder SKILL.md by sub-command
- Add Chain column to swap, multi-swap, and strategy-create parameter
  tables in SKILL.md and cli-usage.md, explicitly scoping each fee flag
  to its supported chain(s)
- Reorder SKILL.md sections to follow Sub-commands table order: swap →
  multi-swap → order quote → order get → order gas-price →
  order strategy create/list/cancel
- Group each command's Usage + Parameters + Response Fields together
- Add horizontal dividers between command groups for readability
- Add order get cross-reference to shared swap response fields
- Collapse separate strategy usage examples into their respective command sections

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 19:59:24 +08:00
David Lau 072b180119 feat: add gas-level/auto-fee/gas-price endpoint; remove auto_tip_fee/max_auto_fee
- Add --gas-level (eth only), --auto-fee (eth only) to swap, multi-swap,
  and strategy create commands
- Add order gas-price subcommand (GET /v1/trade/gas_price, normal auth)
- Add --max-fee-per-gas / --max-priority-fee-per-gas to strategy create
- Remove --auto-tip-fee and --max-auto-fee from swap and multi-swap
- Update token SKILL.md: price field is now a nested object (price.price),
  add price window fields and fee_distribution object docs
- Update EIP-1559 chain coverage from Base-only to BSC/BASE/ETH

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 18:55:49 +08:00
17 changed files with 727 additions and 384 deletions
+1
View File
@@ -4,6 +4,7 @@ on:
push:
tags:
- "v*"
workflow_dispatch:
permissions:
contents: read
+2 -2
View File
@@ -93,8 +93,8 @@ EOF
| Mode | Commands | Requirements |
|------|----------|--------------|
| Normal | token / market / portfolio / track kol / track smartmoney | `GMGN_API_KEY` only, no signature |
| Critical | swap / order / track follow-wallet | `GMGN_API_KEY` + `GMGN_PRIVATE_KEY` — CLI handles signing automatically |
| Normal | token / market / portfolio (except holdings) / track kol / track smartmoney | `GMGN_API_KEY` only, no signature |
| Critical | swap / order / portfolio holdings / track follow-wallet | `GMGN_API_KEY` + `GMGN_PRIVATE_KEY` — CLI handles signing automatically |
## SKILL.md Authoring Rules
+37 -2
View File
@@ -557,7 +557,7 @@ gmgn-cli order quote \
--amount 1000000 \
--slippage 0.01
# Quotes use critical auth and require GMGN_PRIVATE_KEY on every chain
# Quotes use signed auth and require GMGN_PRIVATE_KEY on every chain
gmgn-cli order quote \
--chain bsc \
--from <wallet-address> \
@@ -569,6 +569,12 @@ gmgn-cli order quote \
# Query order
gmgn-cli order get --chain sol --order-id <order-id>
# Query real-time gas price (all chains)
gmgn-cli gas-price --chain sol
gmgn-cli gas-price --chain eth
gmgn-cli gas-price --chain bsc
gmgn-cli gas-price --chain base
# Multi-wallet concurrent swap
gmgn-cli multi-swap \
--chain sol \
@@ -579,7 +585,35 @@ gmgn-cli multi-swap \
--slippage 0.01
```
> `order quote` uses critical auth on `sol` / `bsc` / `base` / `eth` and requires `GMGN_PRIVATE_KEY`.
> `order quote` uses signed auth on `sol` / `bsc` / `base` / `eth` and requires `GMGN_PRIVATE_KEY`.
### ETH Gas Control (ETH only)
```bash
# Pick a gas tier instead of entering gwei manually (low / average / high)
gmgn-cli swap \
--chain eth \
--from <wallet-address> \
--input-token <input-token-addr> \
--output-token <output-token-addr> \
--amount <amount> \
--slippage 0.01 \
--gas-level high
# Let GMGN auto-select the optimal gas fee for condition orders
gmgn-cli swap \
--chain eth \
--from <wallet-address> \
--input-token <input-token-addr> \
--output-token <output-token-addr> \
--amount <amount> \
--slippage 0.01 \
--condition-orders '[...]' \
--auto-fee
```
> `--gas-level` and `--auto-fee` are ETH only. `--auto-fee` only takes effect when used with `--condition-orders`.
> For other chains (SOL / BSC / BASE), use `gas-price` to query the current gas, then pass the result via `--gas-price`.
### Swap with Take-Profit / Stop-Loss Orders (requires private key)
@@ -674,6 +708,7 @@ gmgn-cli cooking \
|----------|--------|-----------------|
| token / market / portfolio / track | `sol` / `bsc` / `base` / `eth` | — |
| swap / order | `sol` / `bsc` / `base` / `eth` | sol: SOL, USDC · bsc: BNB, USDC · base: ETH, USDC · eth: ETH |
| gas-price | `sol` / `bsc` / `base` / `eth` | — |
---
+35
View File
@@ -593,6 +593,12 @@ gmgn-cli order quote \
# 查询订单状态
gmgn-cli order get --chain sol --order-id <order-id>
# 查询实时 Gas 价格(支持全链)
gmgn-cli gas-price --chain sol
gmgn-cli gas-price --chain eth
gmgn-cli gas-price --chain bsc
gmgn-cli gas-price --chain base
# 多钱包并发 Swap
gmgn-cli multi-swap \
--chain sol \
@@ -605,6 +611,34 @@ gmgn-cli multi-swap \
> `order quote` 在 `sol` / `bsc` / `base` / `eth` 上都走关键鉴权,必须配置 `GMGN_PRIVATE_KEY`。
### ETH Gas 档位控制(仅限 ETH
```bash
# 按档位设置 Gaslow / average / high),替代手动填写 gwei
gmgn-cli swap \
--chain eth \
--from <wallet-address> \
--input-token <input-token-addr> \
--output-token <output-token-addr> \
--amount <amount> \
--slippage 0.01 \
--gas-level high
# 策略单(condition-orders)由 GMGN 自动选择最优 Gas Fee
gmgn-cli swap \
--chain eth \
--from <wallet-address> \
--input-token <input-token-addr> \
--output-token <output-token-addr> \
--amount <amount> \
--slippage 0.01 \
--condition-orders '[...]' \
--auto-fee
```
> `--gas-level` 和 `--auto-fee` 仅支持 ETH 链。`--auto-fee` 仅在携带 `--condition-orders` 时生效。
> 其他链(SOL / BSC / BASE)请先用 `gas-price` 查询当前 Gas,再通过 `--gas-price` 手动传入。
### 带止盈止损的 Swap(需要私钥)
**`hold_amount` 模式** — 按触发时的实际持仓比例卖出:
@@ -698,6 +732,7 @@ gmgn-cli cooking \
|----------|----------|-----------|
| token / market / portfolio / track | `sol` / `bsc` / `base` / `eth` | — |
| swap / order | `sol` / `bsc` / `base` / `eth` | sol: SOL、USDC · bsc: BNB、USDC · base: ETH、USDC · eth: ETH |
| gas-price | `sol` / `bsc` / `base` / `eth` | — |
---
+46 -48
View File
@@ -351,7 +351,7 @@ gmgn-cli market signal --chain sol --groups '<json_array>' [--raw]
## portfolio follow-wallet
Query follow-wallet trade records. Returns trades from wallets you personally follow on the GMGN platform. The follow list is resolved automatically from the GMGN user account bound to the API Key — `--wallet` is optional. Normal auth (API Key only, no private key needed).
Query follow-wallet trade records. Returns trades from wallets you personally follow on the GMGN platform. The follow list is resolved automatically from the GMGN user account bound to the API Key — `--wallet` is optional. Signed auth (API Key + private key signature).
```bash
gmgn-cli track follow-wallet \
@@ -411,7 +411,7 @@ gmgn-cli track smartmoney [--chain <chain>] [--limit <n>] [--side <side>] [--raw
## order quote
Get a swap quote without submitting a transaction. All supported quote chains use critical auth and require `GMGN_PRIVATE_KEY`.
Get a swap quote without submitting a transaction. All supported quote chains use signed auth and require `GMGN_PRIVATE_KEY`.
```bash
npx gmgn-cli order quote \
@@ -463,7 +463,6 @@ npx gmgn-cli swap \
[--anti-mev] \
[--priority-fee <sol>] \
[--tip-fee <amount>] \
[--max-auto-fee <amount>] \
[--gas-price <gwei>] \
[--max-fee-per-gas <amount>] \
[--max-priority-fee-per-gas <amount>] \
@@ -472,26 +471,27 @@ npx gmgn-cli swap \
[--raw]
```
| Option | Required | Description |
|--------|----------|-------------|
| `--chain` | Yes | `sol` / `bsc` / `base` / `eth` |
| `--from` | Yes | Wallet address (must match the wallet bound to the API Key) |
| `--input-token` | Yes | Input token contract address |
| `--output-token` | Yes | Output token contract address |
| `--amount` | No* | Input raw amount in minimal unit (e.g., lamports for SOL); required unless `--percent` is used |
| `--percent` | No* | Input amount as a percentage, e.g. `50` = 50%; required unless `--amount` is used; only valid when input token is not a currency (not SOL/BNB/ETH/USDC) |
| `--slippage` | No | Slippage tolerance, e.g. `0.01` = 1% |
| `--auto-slippage` | No | Enable automatic slippage |
| `--min-output` | No | Minimum output amount (raw amount) |
| `--anti-mev` | No | Enable anti-MEV protection (default true) |
| `--priority-fee` | No | Priority fee in SOL (≥ 0.00001 SOL, SOL only) |
| `--tip-fee` | No | Tip fee (SOL ≥ 0.00001 SOL / BSC ≥ 0.000001 BNB) |
| `--max-auto-fee` | No | Max automatic fee cap |
| `--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` |
| Option | Required | Chain | Description |
|--------|----------|-------|-------------|
| `--chain` | Yes | all | `sol` / `bsc` / `base` / `eth` |
| `--from` | Yes | all | Wallet address (must match the wallet bound to the API Key) |
| `--input-token` | Yes | all | Input token contract address |
| `--output-token` | Yes | all | Output token contract address |
| `--amount` | No* | all | Input raw amount in minimal unit (e.g., lamports for SOL); required unless `--percent` is used |
| `--percent` | No* | all | Input amount as a percentage, e.g. `50` = 50%; required unless `--amount` is used; only valid when input token is not a currency (not SOL/BNB/ETH/USDC) |
| `--slippage` | No | all | Slippage tolerance, e.g. `0.01` = 1% |
| `--auto-slippage` | No | all | Enable automatic slippage |
| `--min-output` | No | all | Minimum output amount (raw amount) |
| `--anti-mev` | No | all | Enable anti-MEV protection (default true) |
| `--priority-fee` | No | `sol` | Priority fee in SOL (≥ 0.00001) |
| `--tip-fee` | No | `sol` / `bsc` | Tip fee (SOL ≥ 0.00001 / BSC ≥ 0.000001 BNB) |
| `--gas-price` | No | `bsc` / `base` / `eth` | Gas price in gwei (BSC ≥ 0.05 / BASE/ETH ≥ 0.01) |
| `--gas-level` | No | `eth` | Gas price tier: `low` / `average` / `high`. Mutually exclusive with `--gas-price`. |
| `--auto-fee` | No | `eth` | **Only with `--condition-orders`.** GMGN automatically selects the optimal fee. |
| `--max-fee-per-gas` | No | `bsc` / `base` / `eth` | EIP-1559 max fee per gas |
| `--max-priority-fee-per-gas` | No | `bsc` / `base` / `eth` | EIP-1559 max priority fee per gas |
| `--condition-orders` | No | all | JSON array of take-profit/stop-loss conditions attached after a successful swap (see example below) |
| `--sell-ratio-type` | No | all | **Only with `--condition-orders`.** Sell ratio base: `buy_amount` (default) / `hold_amount` |
**`--condition-orders` example** (100% sell at 2× price, 100% sell at 50% price):
@@ -540,8 +540,6 @@ gmgn-cli multi-swap \
[--anti-mev] \
[--priority-fee <sol>] \
[--tip-fee <amount>] \
[--auto-tip-fee] \
[--max-auto-fee <amount>] \
[--gas-price <gwei>] \
[--max-fee-per-gas <amount>] \
[--max-priority-fee-per-gas <amount>] \
@@ -550,27 +548,27 @@ gmgn-cli multi-swap \
[--raw]
```
| Option | Required | Description |
|--------|----------|-------------|
| `--chain` | Yes | `sol` / `bsc` / `base` |
| `--accounts` | Yes | Comma-separated wallet addresses (1100, all bound to API Key) |
| `--input-token` | Yes | Input token contract address |
| `--output-token` | Yes | Output token contract address |
| `--input-amount` | No* | JSON map `{"addr":"amount"}` in smallest unit; one of the three amount fields is required |
| `--input-amount-bps` | No* | JSON map `{"addr":"bps"}` where 5000 = 50%; only valid when input token is not a currency |
| `--output-amount` | No* | JSON map `{"addr":"amount"}` target output in smallest unit |
| `--slippage` | No | Slippage tolerance, e.g. `0.01` = 1% |
| `--auto-slippage` | No | Enable automatic slippage |
| `--anti-mev` | No | Enable anti-MEV protection |
| `--priority-fee` | No | Priority fee in SOL (≥ 0.00001, SOL only) |
| `--tip-fee` | No | Tip fee (SOL ≥ 0.00001 / BSC ≥ 0.000001 BNB) |
| `--auto-tip-fee` | No | Enable automatic tip fee |
| `--max-auto-fee` | No | Max automatic fee cap |
| `--gas-price` | No | Gas price in gwei (BSC ≥ 0.05 / BASE/ETH ≥ 0.01) |
| `--max-fee-per-gas` | No | EIP-1559 max fee per gas (Base only) |
| `--max-priority-fee-per-gas` | No | EIP-1559 max priority fee per gas (Base only) |
| `--condition-orders` | No | JSON array of take-profit/stop-loss conditions, attached to each successful wallet's swap (best-effort) |
| `--sell-ratio-type` | No | Sell ratio base: `buy_amount` (default) / `hold_amount` |
| Option | Required | Chain | Description |
|--------|----------|-------|-------------|
| `--chain` | Yes | all | `sol` / `bsc` / `base` / `eth` |
| `--accounts` | Yes | all | Comma-separated wallet addresses (1100, all bound to API Key) |
| `--input-token` | Yes | all | Input token contract address |
| `--output-token` | Yes | all | Output token contract address |
| `--input-amount` | No* | all | JSON map `{"addr":"amount"}` in smallest unit; one of the three amount fields is required |
| `--input-amount-bps` | No* | all | JSON map `{"addr":"bps"}` where 5000 = 50%; only valid when input token is not a currency |
| `--output-amount` | No* | all | JSON map `{"addr":"amount"}` target output in smallest unit |
| `--slippage` | No | all | Slippage tolerance, e.g. `0.01` = 1% |
| `--auto-slippage` | No | all | Enable automatic slippage |
| `--anti-mev` | No | all | Enable anti-MEV protection |
| `--priority-fee` | No | `sol` | Priority fee in SOL (≥ 0.00001) |
| `--tip-fee` | No | `sol` / `bsc` | Tip fee (SOL ≥ 0.00001 / BSC ≥ 0.000001 BNB) |
| `--gas-price` | No | `bsc` / `base` / `eth` | Gas price in gwei (BSC ≥ 0.05 / BASE/ETH ≥ 0.01) |
| `--gas-level` | No | `eth` | Gas price tier: `low` / `average` / `high`. Mutually exclusive with `--gas-price`. |
| `--auto-fee` | No | `eth` | **Only with `--condition-orders`.** GMGN automatically selects the optimal fee. |
| `--max-fee-per-gas` | No | `bsc` / `base` / `eth` | EIP-1559 max fee per gas |
| `--max-priority-fee-per-gas` | No | `bsc` / `base` / `eth` | EIP-1559 max priority fee per gas |
| `--condition-orders` | No | all | JSON array of take-profit/stop-loss conditions, attached to each successful wallet's swap (best-effort) |
| `--sell-ratio-type` | No | all | **Only with `--condition-orders`.** Sell ratio base: `buy_amount` (default) / `hold_amount` |
**Response fields (data):** Array of per-wallet results:
@@ -803,7 +801,7 @@ Important notes:
| `AUTH_SIGNATURE_INVALID` | 401 | Signature verification failed |
| `AUTH_TIMESTAMP_EXPIRED` | 401 | Timestamp is outside the valid window (±5s) |
| `AUTH_CLIENT_ID_REPLAYED` | 401 | client_id replayed within 7s |
| `AUTH_REPLAY_CHECK_UNAVAILABLE` | 503 | Anti-replay Redis unavailable (critical auth only) |
| `AUTH_REPLAY_CHECK_UNAVAILABLE` | 503 | Anti-replay Redis unavailable (signed auth only) |
| `RATE_LIMIT_EXCEEDED` | 429 | Rate limit exceeded |
| `RATE_LIMIT_BANNED` | 429 | Temporarily banned due to repeated rate limit violations |
| `ERROR_RATE_LIMIT_BLOCKED` | 429 | Temporarily blocked after repeated business errors on `swap` |
@@ -812,5 +810,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) |
| `TRADING_BOT_UNAVAILABLE` | 502 | Strategy order service temporarily unreachable |
| `INTERNAL_ERROR` | 500 | Internal server error |
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "gmgn-cli",
"version": "1.3.0",
"version": "1.3.6",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "gmgn-cli",
"version": "1.3.0",
"version": "1.3.6",
"license": "MIT",
"dependencies": {
"commander": "^12.1.0",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "gmgn-cli",
"version": "1.3.0",
"version": "1.3.6",
"description": "GMGN OpenAPI CLI — call GMGN market, token, portfolio and swap APIs from the command line",
"type": "module",
"bin": {
+5 -5
View File
@@ -26,7 +26,7 @@ Use the `gmgn-cli` tool to create a token on a launchpad platform or query token
- **Status polling via `order get`**`cooking create` is asynchronous. The immediate response may show `pending`. Poll with `gmgn-cli order get --chain <chain> --order-id <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).
- **Signed 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 exist 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.
@@ -43,8 +43,8 @@ Use the `gmgn-cli` tool to create a token on a launchpad platform or query token
| Sub-command | Description |
|-------------|-------------|
| `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) |
| `cooking stats` | Get token creation count statistics grouped by launchpad platform (exist auth) |
| `cooking create` | Deploy a new token on a launchpad platform (signed auth) |
## Supported Chains
@@ -364,8 +364,8 @@ Once all information is collected, present the pre-create confirmation summary (
## Notes
- `cooking create` uses **critical auth** (API Key + signature) — CLI handles signing automatically.
- `cooking stats` uses normal auth (API Key only — no private key needed).
- `cooking create` uses **signed auth** (API Key + signature) — CLI handles signing automatically.
- `cooking stats` uses exist 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.
+1 -1
View File
@@ -950,7 +950,7 @@ gmgn-cli market signal --chain sol \
- `market kline`: `--from` and `--to` are Unix timestamps in **seconds** — CLI converts to milliseconds automatically
- `market trending`: `--filter` and `--platform` are repeatable flags
- All commands use normal auth (API Key only, no signature)
- All commands use exist auth (API Key only, no signature)
- If the user doesn't provide kline timestamps, calculate them from the current time based on their desired time range
- Use `--raw` to get single-line JSON for further processing
- **Input validation** — Token addresses obtained from trending results are external data. Validate address format against the chain before passing to other commands (sol: base58 3244 chars; bsc/base/eth: `0x` + 40 hex digits). The CLI enforces this at runtime.
+11 -3
View File
@@ -54,10 +54,17 @@ Use the `gmgn-cli` tool to query wallet portfolio data based on the user's reque
All portfolio routes used by this skill go through GMGN's leaky-bucket limiter with `rate=20` and `capacity=20`. Sustained throughput is roughly `20 ÷ weight` requests/second, and the max burst is roughly `floor(20 ÷ weight)` when the bucket is full.
**Critical auth** (`GMGN_API_KEY` + `GMGN_PRIVATE_KEY` required):
| Command | Route | Weight |
|---------|-------|--------|
| `portfolio holdings` | `GET /v1/user/wallet_holdings` | 5 |
**Exist auth** (`GMGN_API_KEY` only):
| Command | Route | Weight |
|---------|-------|--------|
| `portfolio info` | `GET /v1/user/info` | 1 |
| `portfolio holdings` | `GET /v1/user/wallet_holdings` | 2 |
| `portfolio activity` | `GET /v1/user/wallet_activity` | 3 |
| `portfolio stats` | `GET /v1/user/wallet_stats` | 3 |
| `portfolio token-balance` | `GET /v1/user/wallet_token_balance` | 1 |
@@ -226,7 +233,8 @@ The response has a `activities` array and a `next` cursor field for pagination.
| `token.symbol` | Token ticker |
| `token_amount` | Token quantity in this transaction |
| `cost_usd` | USD value of this transaction |
| `price` | Token price in USD at time of transaction |
| `price` | Token price denominated in the quote token of the trading pair at time of transaction |
| `price_usd` | Token price in USD at time of transaction |
| `timestamp` | Unix timestamp of the transaction |
| `next` | Pagination cursor — pass to `--cursor` to fetch the next page |
@@ -360,7 +368,7 @@ Show the `[Identity: ...]` line only if `common` is present in the response. For
## Notes
- All portfolio commands use normal auth (API Key only, no signature required)
- `portfolio holdings` uses **critical auth** (`GMGN_API_KEY` + `GMGN_PRIVATE_KEY` required — CLI signs the request automatically). All other portfolio commands use exist auth (API Key only, no signature required).
- `portfolio stats` supports multiple `--wallet` flags for batch queries
- Use `--raw` to get single-line JSON for further processing
- **Input validation** — Wallet and token addresses are validated against the expected chain format at runtime (sol: base58 3244 chars; bsc/base/eth: `0x` + 40 hex digits). The CLI exits with an error on invalid input.
+376 -189
View File
@@ -1,7 +1,7 @@
---
name: gmgn-swap
description: "[FINANCIAL EXECUTION] Buy and sell meme coins and crypto tokens on Solana, BSC, Base, or Ethereum — single swap, multi-wallet batch trading, limit orders, stop loss, take profit, trailing stop loss, trailing take profit via GMGN API. Requires explicit user confirmation. Use when user asks to buy, sell, or swap a token, trade from multiple wallets, set a limit order, stop loss, take profit, or check order status."
argument-hint: "[--chain <chain> --from <wallet> --input-token <addr> --output-token <addr> --amount <n>] | [order get --chain <chain> --order-id <id>] | [order strategy list --chain <chain> --group-tag <LimitOrder|STMix>] | [order strategy create --chain <chain> --order-type limit_order --sub-order-type <buy_low|buy_high|stop_loss|take_profit> ...]"
argument-hint: "[--chain <chain> --from <wallet> --input-token <addr> --output-token <addr> --amount <n>] | [order get --chain <chain> --order-id <id>] | [gas-price --chain <eth|bsc|base|sol>] | [order strategy list --chain <chain> --group-tag <LimitOrder|STMix>] | [order strategy create --chain <chain> --order-type limit_order --sub-order-type <buy_low|buy_high|stop_loss|take_profit> ...]"
metadata:
cliHelp: "gmgn-cli swap --help"
---
@@ -24,9 +24,9 @@ Use the `gmgn-cli` tool to submit a token swap or query an existing order. `GMGN
- **Currency tokens** — Each chain has designated currency tokens (SOL, BNB, ETH, USDC). These are the base assets used to buy other tokens or receive swap proceeds. Their contract addresses are fixed — look them up in the Chain Currencies table, never guess them.
- **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.
- **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. **Not supported on `base` chain.**
- **Critical auth**`swap` and all `order` subcommands 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.
- **Signed auth**`swap` and all `order` subcommands 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_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`.
@@ -47,8 +47,9 @@ Use the `gmgn-cli` tool to submit a token swap or query an existing order. `GMGN
|-------------|-------------|
| `swap` | Submit a token swap |
| `multi-swap` | Submit token swaps across multiple wallets concurrently (up to 100) |
| `order quote` | Get a swap quote (no transaction submitted; requires critical auth) |
| `order quote` | Get a swap quote (no transaction submitted; requires signed auth) |
| `order get` | Query order status |
| `gas-price` | Query recommended gas price (low / average / high tiers) for any chain; exist auth (API Key only) |
| `order strategy create` | Create a limit/strategy order (requires private key) |
| `order strategy list` | List strategy orders (requires private key) |
| `order strategy cancel` | Cancel a strategy order (requires private key) |
@@ -87,6 +88,10 @@ All swap-related routes used by this skill go through GMGN's leaky-bucket limite
| `multi-swap` | `POST /v1/trade/multi_swap` | 5 |
| `order quote` | `GET /v1/trade/quote` | 2 |
| `order get` | `GET /v1/trade/query_order` | 1 |
| `order strategy create` | `POST /v1/trade/strategy/create` | 5 |
| `order strategy cancel` | `POST /v1/trade/strategy/cancel` | 2 |
| `order strategy list` | `GET /v1/trade/strategy/orders` | 1 |
| `gas-price` | `GET /v1/trade/gas_price` | 1 |
When a request returns `429`:
@@ -120,6 +125,8 @@ When a request returns `429`:
- `GMGN_PRIVATE_KEY` is used exclusively for **local message signing** — the private key never leaves the machine. The CLI computes an Ed25519 or RSA-SHA256 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` request header to GMGN's servers over HTTPS.
---
## `swap` Usage
```bash
@@ -167,142 +174,29 @@ gmgn-cli swap \
--percent 50
```
## `multi-swap` Usage
Submit a token swap across multiple wallets concurrently. Each wallet executes independently — one wallet's failure does not affect others. Up to 100 wallets per request. All wallets must be bound to the API Key. Requires `GMGN_PRIVATE_KEY`.
```bash
# Basic multi-wallet swap
gmgn-cli multi-swap \
--chain sol \
--accounts <addr1>,<addr2> \
--input-token <input_token_address> \
--output-token <output_token_address> \
--input-amount '{"<addr1>":"1000000","<addr2>":"2000000"}' \
--slippage 0.01
# Sell a percentage of each wallet's balance (use --input-amount-bps)
gmgn-cli multi-swap \
--chain sol \
--accounts <addr1>,<addr2> \
--input-token <token_address> \
--output-token <sol_address> \
--input-amount-bps '{"<addr1>":"5000","<addr2>":"10000"}' \
--slippage 0.01
# With per-wallet take-profit / stop-loss (condition_orders)
gmgn-cli multi-swap \
--chain sol \
--accounts <addr1>,<addr2> \
--input-token So11111111111111111111111111111111111111112 \
--output-token <token_address> \
--input-amount '{"<addr1>":"1000000","<addr2>":"2000000"}' \
--slippage 0.3 \
--priority-fee 0.00001 \
--tip-fee 0.00001 \
--condition-orders '[{"order_type":"profit_stop","side":"sell","price_scale":"100","sell_ratio":"100"},{"order_type":"loss_stop","side":"sell","price_scale":"50","sell_ratio":"100"}]'
# ETH multi-wallet swap (EIP-1559 gas)
gmgn-cli multi-swap \
--chain eth \
--accounts <0xaddr1>,<0xaddr2> \
--input-token 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 \
--output-token <token_address> \
--input-amount '{"<0xaddr1>":"1000000","<0xaddr2>":"2000000"}' \
--slippage 0.01 \
--gas-price 5
```
## `multi-swap` Parameters
| Parameter | Required | Description |
|-----------|----------|-------------|
| `--chain` | Yes | `sol` / `bsc` / `base` / `eth` |
| `--accounts` | Yes | Comma-separated wallet addresses (1100, all must be bound to the API Key) |
| `--input-token` | Yes | Input token contract address |
| `--output-token` | Yes | Output token contract address |
| `--input-amount` | No* | JSON map of `wallet_address → input amount` (smallest unit). One of `--input-amount`, `--input-amount-bps`, or `--output-amount` is required. |
| `--input-amount-bps` | No* | JSON map of `wallet_address → percent in bps` (110000; 5000 = 50%). Only valid when `input_token` is NOT a currency. |
| `--output-amount` | No* | JSON map of `wallet_address → target output amount` (smallest unit). |
| `--slippage <n>` | No | Slippage tolerance, e.g. `0.01` = 1%. Mutually exclusive with `--auto-slippage`. |
| `--auto-slippage` | No | Enable automatic slippage. |
| `--anti-mev` | No | Enable anti-MEV protection. |
| `--priority-fee <sol>` | No | Priority fee in SOL (≥ 0.00001, SOL only). Required when using `--condition-orders` on SOL. |
| `--tip-fee <amount>` | No | Tip fee (SOL ≥ 0.00001 / BSC ≥ 0.000001 BNB). Required when using `--condition-orders` on SOL. |
| `--auto-tip-fee` | No | Enable automatic tip fee. |
| `--max-auto-fee <amount>` | No | Max automatic fee cap. |
| `--gas-price <gwei>` | No | Gas price in gwei (BSC: 0.0510 / BASE: 0.0110 / ETH: 220). Required when using `--condition-orders` on BSC. |
| `--max-fee-per-gas <amount>` | No | EIP-1559 max fee per gas (Base / ETH). Defaults to `--gas-price` if omitted. |
| `--max-priority-fee-per-gas <amount>` | No | EIP-1559 max priority fee per gas (Base / ETH). Defaults to `--gas-price` if omitted. |
| `--condition-orders <json>` | No | JSON array of condition sub-orders (take-profit / stop-loss) attached to each successful wallet's swap. Same structure as `swap --condition-orders`. Strategy creation is best-effort per wallet. |
| `--sell-ratio-type <type>` | No | Sell ratio base for `--condition-orders`: `buy_amount` (default) / `hold_amount`. |
## `multi-swap` Response Fields
The response `data` is an array — one element per wallet:
| Field | Type | Description |
|-------|------|-------------|
| `account` | string | Wallet address |
| `success` | bool | Whether this wallet's swap succeeded |
| `error` | string | Error message on failure; absent on success |
| `error_code` | string | Error code on failure; absent on success |
| `result` | object | On success: OrderResponse (same fields as `swap` response). On failure: absent. |
| `result.strategy_order_id` | string | Strategy order ID; only present when `--condition-orders` was passed and strategy creation succeeded (best-effort) |
## `order quote` Usage
Get an estimated output amount before submitting a swap. All supported quote chains use critical auth and require `GMGN_PRIVATE_KEY`.
```bash
gmgn-cli order quote \
--chain sol \
--from <wallet_address> \
--input-token <input_token_address> \
--output-token <output_token_address> \
--amount <input_amount_smallest_unit> \
--slippage 0.01
```
### `order quote` Response Fields
| Field | Type | Description |
|-------|------|-------------|
| `input_token` | string | Input token contract address |
| `output_token` | string | Output token contract address |
| `input_amount` | string | Input amount (smallest unit) |
| `output_amount` | string | Expected output amount (smallest unit) |
| `min_output_amount` | string | Minimum output after slippage |
| `slippage` | number | Actual slippage percentage |
## `order get` Usage
```bash
gmgn-cli order get --chain sol --order-id <order_id>
```
## `swap` Parameters
| Parameter | Required | Description |
|-----------|----------|-------------|
| `--chain` | Yes | `sol` / `bsc` / `base` / `eth` |
| `--from` | Yes | Wallet address (must match API Key binding) |
| `--input-token` | Yes | Input token contract address |
| `--output-token` | Yes | Output token contract address |
| `--amount` | No* | Input amount in smallest unit. **Mutually exclusive with `--percent`** — provide one or the other, never both. Required unless `--percent` is used. |
| `--percent <pct>` | No* | Sell percentage of `input_token`, e.g. `50` = 50%, `1` = 1%. Sets `input_amount` to `0` automatically. **Mutually exclusive with `--amount`. Only valid when `input_token` is NOT a currency (SOL/BNB/ETH/USDC).** |
| `--slippage <n>` | No | Slippage tolerance, e.g. `0.01` = 1%. **Mutually exclusive with `--auto-slippage`** — use one or the other. |
| `--auto-slippage` | No | Enable automatic slippage. **Mutually exclusive with `--slippage`.** |
| `--min-output <n>` | No | Minimum output amount |
| `--anti-mev` | No | Enable anti-MEV protection — **recommended**; protects against frontrunning and sandwich attacks. Default: on |
| `--priority-fee <sol>` | No | Priority fee in SOL (≥ 0.00001, SOL only) |
| `--tip-fee <n>` | No | Tip fee (SOL ≥ 0.00001 / BSC ≥ 0.000001 BNB) |
| `--max-auto-fee <n>` | No | Max automatic fee cap |
| `--gas-price <gwei>` | No | Gas price in gwei (BSC ≥ 0.05 / BASE/ETH ≥ 0.01) |
| `--max-fee-per-gas <n>` | No | EIP-1559 max fee per gas (Base only) |
| `--max-priority-fee-per-gas <n>` | No | EIP-1559 max priority fee per gas (Base only) |
| `--condition-orders <json>` | No | JSON array of condition sub-orders (take-profit / stop-loss) to attach after a successful swap. **Max 10 sub-orders.** Strategy creation is best-effort: if the swap succeeds but strategy creation fails, the swap result is still returned. See ConditionOrder fields below. |
| `--sell-ratio-type <type>` | No | Sell ratio basis for `--condition-orders`: `buy_amount` (default) — when triggered, sells a fixed token amount stored at strategy creation time; `hold_amount` — when triggered, sells a fixed percentage of the position held at trigger time |
| Parameter | Required | Chain | Description |
|-----------|----------|-------|-------------|
| `--chain` | Yes | all | `sol` / `bsc` / `base` / `eth` |
| `--from` | Yes | all | Wallet address (must match API Key binding) |
| `--input-token` | Yes | all | Input token contract address |
| `--output-token` | Yes | all | Output token contract address |
| `--amount` | No* | all | Input amount in smallest unit. **Mutually exclusive with `--percent`** — provide one or the other, never both. Required unless `--percent` is used. |
| `--percent <pct>` | No* | all | Sell percentage of `input_token`, e.g. `50` = 50%, `1` = 1%. Sets `input_amount` to `0` automatically. **Mutually exclusive with `--amount`. Only valid when `input_token` is NOT a currency (SOL/BNB/ETH/USDC).** |
| `--slippage <n>` | No | all | Slippage tolerance, e.g. `0.01` = 1%. **Mutually exclusive with `--auto-slippage`** — use one or the other. |
| `--auto-slippage` | No | all | Enable automatic slippage. **Mutually exclusive with `--slippage`.** |
| `--min-output <n>` | No | all | Minimum output amount |
| `--anti-mev` | No | sol / bsc / eth | Enable anti-MEV protection — **recommended**; protects against frontrunning and sandwich attacks. Default: on. **Not supported on `base`.** |
| `--priority-fee <sol>` | No | `sol` | Priority fee in SOL (≥ 0.00001). Required when using `--condition-orders` on SOL. |
| `--tip-fee <n>` | No | `sol` / `bsc` | Tip fee (SOL ≥ 0.00001 / BSC ≥ 0.000001 BNB). Required when using `--condition-orders` on SOL. |
| `--gas-price <gwei>` | No | `bsc` / `base` / `eth` | Gas price in gwei (BSC ≥ 0.05 / BASE/ETH ≥ 0.01). Required when using `--condition-orders` on BSC. Mutually exclusive with `--gas-level`. |
| `--gas-level <level>` | No | `eth` | Gas price tier: `low` / `average` / `high`. Mutually exclusive with `--gas-price`. |
| `--auto-fee` | No | `eth` | **Only with `--condition-orders`.** GMGN automatically selects the optimal fee. |
| `--max-fee-per-gas <n>` | No | `bsc` / `base` / `eth` | EIP-1559 max fee per gas. Clamped per chain minimums. Defaults to `--gas-price` if omitted (BASE/ETH). |
| `--max-priority-fee-per-gas <n>` | No | `bsc` / `base` / `eth` | EIP-1559 max priority fee per gas. Clamped per chain minimums; capped to `--max-fee-per-gas`. |
| `--condition-orders <json>` | No | all | JSON array of condition sub-orders (take-profit / stop-loss) to attach after a successful swap. **Max 10 sub-orders.** Strategy creation is best-effort: if the swap succeeds but strategy creation fails, the swap result is still returned. See ConditionOrder fields below. |
| `--sell-ratio-type <type>` | No | all | **Only with `--condition-orders`.** Sell ratio basis: `buy_amount` (default) — sells a fixed token amount stored at strategy creation time; `hold_amount` — sells a fixed percentage of the position held at trigger time |
### ConditionOrder Fields (for `--condition-orders`)
@@ -400,7 +294,7 @@ gmgn-cli swap \
### Pre-swap Confirmation
Before displaying the confirmation, run `order quote` to get the estimated output (requires critical auth and `GMGN_PRIVATE_KEY` on every supported quote chain):
Before displaying the confirmation, run `order quote` to get the estimated output (requires signed auth and `GMGN_PRIVATE_KEY` on every supported quote chain):
```bash
gmgn-cli order quote \
@@ -450,68 +344,172 @@ Order ID: {order_id}
Convert `report.input_amount` and `report.output_amount` from smallest unit using `report.input_token_decimals` and `report.output_token_decimals` before displaying.
## `order strategy create` Parameters
---
| Parameter | Required | Description |
|-----------|----------|-------------|
| `--chain` | Yes | `sol` / `bsc` / `base` / `eth` |
| `--from` | Yes | Wallet address (must match API Key binding) |
| `--base-token` | Yes | Base token contract address |
| `--quote-token` | Yes | Quote token contract address |
| `--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 |
| `--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) — when triggered, sells a fixed token amount stored at strategy creation time; `hold_amount` — when triggered, sells a fixed percentage of the position held at trigger time |
| `--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 gwei (BSC ≥ 0.05 gwei / BASE/ETH ≥ 0.01 gwei) |
| `--anti-mev` | No | Enable anti-MEV protection |
## `multi-swap` Usage
Submit a token swap across multiple wallets concurrently. Each wallet executes independently — one wallet's failure does not affect others. Up to 100 wallets per request. All wallets must be bound to the API Key. Requires `GMGN_PRIVATE_KEY`.
### `order strategy create` Response Fields
```bash
# Basic multi-wallet swap
gmgn-cli multi-swap \
--chain sol \
--accounts <addr1>,<addr2> \
--input-token <input_token_address> \
--output-token <output_token_address> \
--input-amount '{"<addr1>":"1000000","<addr2>":"2000000"}' \
--slippage 0.01
# Sell a percentage of each wallet's balance (use --input-amount-bps)
gmgn-cli multi-swap \
--chain sol \
--accounts <addr1>,<addr2> \
--input-token <token_address> \
--output-token <sol_address> \
--input-amount-bps '{"<addr1>":"5000","<addr2>":"10000"}' \
--slippage 0.01
# With per-wallet take-profit / stop-loss (condition_orders)
gmgn-cli multi-swap \
--chain sol \
--accounts <addr1>,<addr2> \
--input-token So11111111111111111111111111111111111111112 \
--output-token <token_address> \
--input-amount '{"<addr1>":"1000000","<addr2>":"2000000"}' \
--slippage 0.3 \
--priority-fee 0.00001 \
--tip-fee 0.00001 \
--condition-orders '[{"order_type":"profit_stop","side":"sell","price_scale":"100","sell_ratio":"100"},{"order_type":"loss_stop","side":"sell","price_scale":"50","sell_ratio":"100"}]'
# ETH multi-wallet swap (EIP-1559 gas)
gmgn-cli multi-swap \
--chain eth \
--accounts <0xaddr1>,<0xaddr2> \
--input-token 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 \
--output-token <token_address> \
--input-amount '{"<0xaddr1>":"1000000","<0xaddr2>":"2000000"}' \
--slippage 0.01 \
--gas-price 5
```
## `multi-swap` Parameters
| Parameter | Required | Chain | Description |
|-----------|----------|-------|-------------|
| `--chain` | Yes | all | `sol` / `bsc` / `base` / `eth` |
| `--accounts` | Yes | all | Comma-separated wallet addresses (1100, all must be bound to the API Key) |
| `--input-token` | Yes | all | Input token contract address |
| `--output-token` | Yes | all | Output token contract address |
| `--input-amount` | No* | all | JSON map of `wallet_address → input amount` (smallest unit). One of `--input-amount`, `--input-amount-bps`, or `--output-amount` is required. |
| `--input-amount-bps` | No* | all | JSON map of `wallet_address → percent in bps` (110000; 5000 = 50%). Only valid when `input_token` is NOT a currency. |
| `--output-amount` | No* | all | JSON map of `wallet_address → target output amount` (smallest unit). |
| `--slippage <n>` | No | all | Slippage tolerance, e.g. `0.01` = 1%. Mutually exclusive with `--auto-slippage`. |
| `--auto-slippage` | No | all | Enable automatic slippage. |
| `--anti-mev` | No | sol / bsc / eth | Enable anti-MEV protection. Not supported on `base`. |
| `--priority-fee <sol>` | No | `sol` | Priority fee in SOL (≥ 0.00001). Required when using `--condition-orders` on SOL. |
| `--tip-fee <amount>` | No | `sol` / `bsc` | Tip fee (SOL ≥ 0.00001 / BSC ≥ 0.000001 BNB). Required when using `--condition-orders` on SOL. |
| `--gas-price <gwei>` | No | `bsc` / `base` / `eth` | Gas price in gwei (BSC ≥ 0.05 / BASE/ETH ≥ 0.01). Required when using `--condition-orders` on BSC. Mutually exclusive with `--gas-level`. |
| `--gas-level <level>` | No | `eth` | Gas price tier: `low` / `average` / `high`. Mutually exclusive with `--gas-price`. |
| `--auto-fee` | No | `eth` | **Only with `--condition-orders`.** GMGN automatically selects the optimal fee. |
| `--max-fee-per-gas <amount>` | No | `bsc` / `base` / `eth` | EIP-1559 max fee per gas. Clamped per chain minimums. Defaults to `--gas-price` if omitted (BASE/ETH). |
| `--max-priority-fee-per-gas <amount>` | No | `bsc` / `base` / `eth` | EIP-1559 max priority fee per gas. Clamped per chain minimums; capped to `--max-fee-per-gas`. |
| `--condition-orders <json>` | No | all | JSON array of condition sub-orders (take-profit / stop-loss) attached to each successful wallet's swap. Same structure as `swap --condition-orders`. Strategy creation is best-effort per wallet. |
| `--sell-ratio-type <type>` | No | all | **Only with `--condition-orders`.** Sell ratio base: `buy_amount` (default) / `hold_amount`. |
## `multi-swap` Response Fields
The response `data` is an array — one element per wallet:
| Field | Type | Description |
|-------|------|-------------|
| `order_id` | string | Created strategy order ID |
| `is_update` | bool | `true` if an existing order was updated, `false` if newly created |
| `account` | string | Wallet address |
| `success` | bool | Whether this wallet's swap succeeded |
| `error` | string | Error message on failure; absent on success |
| `error_code` | string | Error code on failure; absent on success |
| `result` | object | On success: OrderResponse (same fields as `swap` response). On failure: absent. |
| `result.strategy_order_id` | string | Strategy order ID; only present when `--condition-orders` was passed and strategy creation succeeded (best-effort) |
## `order strategy list` Parameters
---
| Parameter | Required | Description |
|-----------|----------|-------------|
| `--chain` | Yes | `sol` / `bsc` / `base` / `eth` |
| `--type` | No | `open` (default) / `history` |
| `--from` | No | Filter by wallet address |
| `--group-tag` | Yes | Filter by order group: `LimitOrder` (limit orders only) / `STMix` (mixed strategy orders: take-profit, stop-loss, trailing take-profit, trailing stop-loss) |
| `--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 quote` Usage
### `order strategy list` Response Fields
Get an estimated output amount before submitting a swap. All supported quote chains use signed auth and require `GMGN_PRIVATE_KEY`.
```bash
gmgn-cli order quote \
--chain sol \
--from <wallet_address> \
--input-token <input_token_address> \
--output-token <output_token_address> \
--amount <input_amount_smallest_unit> \
--slippage 0.01
```
### `order quote` 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 |
| `input_token` | string | Input token contract address |
| `output_token` | string | Output token contract address |
| `input_amount` | string | Input amount (smallest unit) |
| `output_amount` | string | Expected output amount (smallest unit) |
| `min_output_amount` | string | Minimum output after slippage |
| `slippage` | number | Actual slippage percentage |
## `order strategy cancel` Parameters
---
| Parameter | Required | Description |
|-----------|----------|-------------|
| `--chain` | Yes | `sol` / `bsc` / `base` / `eth` |
| `--from` | Yes | Wallet address (must match API Key binding) |
| `--order-id` | Yes | Order ID to cancel |
| `--order-type` | No | Order type: `limit_order` (limit order) / `smart_trade` (mixed strategy order: take-profit, stop-loss, trailing take-profit, trailing stop-loss) |
| `--close-sell-model` | No | Sell model when closing the order |
## `order get` Usage
## `order strategy` Usage Examples
```bash
gmgn-cli order get --chain sol --order-id <order_id>
```
Response fields are shared with `swap` — see [`swap` / `order get` Response Fields](#swap--order-get-response-fields) above.
---
## `gas-price` Usage
Query recommended gas price tiers for any chain. API Key only — no signature or private key required.
```bash
gmgn-cli gas-price --chain eth
gmgn-cli gas-price --chain bsc
gmgn-cli gas-price --chain base
gmgn-cli gas-price --chain sol
```
### `gas-price` Response Fields
All fields are omitempty — fields unsupported by a chain are omitted. Units are chain-native (wei for EVM chains; lamports / chain-native for SOL).
| Field | Type | Description |
| ------------------------ | ------- | ----------- |
| `chain` | string | Chain identifier |
| `auto` | string | Automatic gas price |
| `auto_mev` | string | Anti-MEV automatic gas price |
| `last_block` | int64 | Latest block number |
| `high` | string | High-priority gas price |
| `average` | string | Average-priority gas price |
| `low` | string | Low-priority gas price |
| `suggest_base_fee` | string | Suggested base fee |
| `high_prio_fee` | string | High-priority fee |
| `average_prio_fee` | string | Average-priority fee |
| `low_prio_fee` | string | Low-priority fee |
| `high_prio_fee_mixed` | string | High mixed priority fee |
| `average_prio_fee_mixed` | string | Average mixed priority fee |
| `low_prio_fee_mixed` | string | Low mixed priority fee |
| `native_token_usd_price` | float32 | Native token USD price |
| `high_estimate_time` | int64 | Estimated confirmation time for high tier (seconds) |
| `average_estimate_time` | int64 | Estimated confirmation time for average tier (seconds) |
| `low_estimate_time` | int64 | Estimated confirmation time for low tier (seconds) |
| `high_orign` | string | High-priority raw origin value |
| `average_orign` | string | Average-priority raw origin value |
| `low_orign` | string | Low-priority raw origin value |
---
## `order strategy create` Usage
```bash
# Create a take-profit order: sell when price rises to target
@@ -537,7 +535,47 @@ gmgn-cli order strategy create \
--check-price 0.0005 \
--amount-in-percent 100 \
--slippage 0.01
```
## `order strategy create` Parameters
| Parameter | Required | Chain | Description |
|-----------|----------|-------|-------------|
| `--chain` | Yes | all | `sol` / `bsc` / `base` / `eth` |
| `--from` | Yes | all | Wallet address (must match API Key binding) |
| `--base-token` | Yes | all | Base token contract address |
| `--quote-token` | Yes | all | Quote token contract address |
| `--order-type` | Yes | all | Order type: `limit_order` |
| `--sub-order-type` | Yes | all | Sub-order type: `buy_low` / `buy_high` / `stop_loss` / `take_profit` |
| `--check-price` | Yes | all | Trigger price in USD — the order fires when the token's USD price crosses this value |
| `--amount-in` | No* | all | Input amount (smallest unit). Mutually exclusive with `--amount-in-percent` |
| `--amount-in-percent` | No* | all | Input as percentage (e.g. `50` = 50%). Mutually exclusive with `--amount-in` |
| `--limit-price-mode` | No | all | `exact` / `slippage` (default: `slippage`) |
| `--expire-in` | No | all | Order expiry in seconds |
| `--sell-ratio-type` | No | all | `buy_amount` (default) — when triggered, sells a fixed token amount stored at strategy creation time; `hold_amount` — when triggered, sells a fixed percentage of the position held at trigger time |
| `--slippage` | No | all | Slippage tolerance, e.g. `0.01` = 1%. Mutually exclusive with `--auto-slippage` |
| `--auto-slippage` | No | all | Enable automatic slippage |
| `--priority-fee` | No | `sol` | Priority fee in SOL (≥ 0.00001). **Required** for SOL. |
| `--tip-fee` | No | `sol` / `bsc` | Tip fee (SOL ≥ 0.00001 / BSC ≥ 0.000001 BNB). **Required** for SOL. |
| `--auto-fee` | No | `eth` | Auto fee mode — GMGN automatically selects the optimal fee. |
| `--gas-price` | No | `bsc` / `base` / `eth` | Gas price in gwei (BSC ≥ 0.05 / BASE/ETH ≥ 0.01). **Required** for BSC. Mutually exclusive with `--gas-level`. |
| `--gas-level` | No | `eth` | Gas price tier: `low` / `average` / `high`. Mutually exclusive with `--gas-price`. |
| `--max-fee-per-gas` | No | `bsc` / `base` / `eth` | EIP-1559 max fee per gas. Clamped per chain minimums. |
| `--max-priority-fee-per-gas` | No | `bsc` / `base` / `eth` | EIP-1559 max priority fee per gas. Clamped per chain minimums; capped to `--max-fee-per-gas`. |
| `--anti-mev` | No | sol / bsc / eth | Enable anti-MEV protection. Not supported on `base`. |
### `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` Usage
```bash
# List open condition orders (profit_stop / loss_stop / trace types) — use STMix
gmgn-cli order strategy list --chain sol --group-tag STMix
@@ -549,7 +587,139 @@ gmgn-cli order strategy list --chain sol --group-tag STMix --type history --limi
# Filter by token
gmgn-cli order strategy list --chain sol --group-tag STMix --base-token <token_address>
```
## `order strategy list` Parameters
| Parameter | Required | Description |
|-----------|----------|-------------|
| `--chain` | Yes | `sol` / `bsc` / `base` / `eth` |
| `--type` | No | `open` (default) / `history` |
| `--from` | No | Filter by wallet address |
| `--group-tag` | Yes | Filter by order group: `LimitOrder` (limit orders only) / `STMix` (mixed strategy orders: take-profit, stop-loss, trailing take-profit, trailing stop-loss) |
| `--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 | Array of strategy order objects; see fields below |
#### `list[]` — Strategy Order Object
| Field | Type | Description |
| -------------------------- | ------ | ---- |
| `anti_mev_mode` | string | Anti-MEV mode string; empty when not set |
| `auto_slippage` | bool | Whether auto slippage is enabled |
| `base_decimal` | int | Base token decimal places |
| `base_token` | string | Base token contract address |
| `chain` | string | Chain: `sol` / `bsc` / `base` / `eth` |
| `close_amount` | string | Token amount sold on close; empty when order is open |
| `close_price` | string | Token price at close; empty when order is open |
| `close_sell_model` | string | Sell model used on close; empty when order is open |
| `close_sign_hash` | string | Close transaction hash; empty when order is open |
| `close_time` | int | Close timestamp (ms); `0` when order is open |
| `condition_orders` | array | Condition sub-orders; each element is an object — see `condition_orders[]` below |
| `create_time` | int | Creation timestamp (ms) |
| `custom_rpc` | string | Custom RPC endpoint; empty string when not set |
| `dev_sell_ratio` | string | Dev sell trigger ratio; empty when not set |
| `drawdown_rate` | string | Trailing drawdown rate for `profit_stop_trace` / `loss_stop_trace`; empty when not set |
| `expire_time` | int | Expiration timestamp (ms) |
| `fee` | string | Base transaction fee |
| `gas_price` | string | Gas price |
| `is_anti_mev` | bool | Whether anti-MEV protection is active |
| `limit_price_mode` | string | Limit price mode; empty when not set |
| `loss_stop` | string | Stop-loss trigger price; empty when not set |
| `loss_stop_type` | string | Stop-loss type; empty when not set |
| `max_fee_per_gas` | string | EIP-1559 max fee per gas; EVM only; empty on SOL |
| `max_priority_fee_per_gas` | string | EIP-1559 max priority fee per gas; EVM only; empty on SOL |
| `open_amount` | string | Token amount at open (smallest unit) |
| `open_price` | string | Token price at open |
| `open_sign_hash` | string | Open transaction hash; empty before confirmed |
| `order_id` | string | Unique order ID (UUID) |
| `order_statistic` | object | Cumulative order statistics; see `order_statistic` Object below |
| `order_type` | string | Order type: `smart_trade` / `limit_order` |
| `place_action` | string | Placement action; empty when not applicable |
| `prepare_status` | string | Preparation status; empty when not applicable |
| `priority_fee` | string | Priority fee; SOL / BSC only |
| `profit_stop` | string | Take-profit trigger price; empty when not set |
| `profit_stop_type` | string | Take-profit type; empty when not set |
| `quote_decimal` | int | Quote token decimal places |
| `quote_investment` | string | Quote token investment amount (smallest unit) |
| `quote_token` | string | Quote token contract address |
| `reason_by` | string | Entity that triggered the close; empty when open |
| `reason_code` | string | Reason code for the close action; empty when open |
| `record_high_price` | string | Highest recorded price since open; used for trailing stops |
| `sell_param` | object | Sell transaction parameters; see `sell_param` Object below |
| `sell_ratio` | string | Sell ratio; empty when not set |
| `sell_ratio_type` | string | Sell ratio base: `buy_amount` / others |
| `slippage` | int | Slippage tolerance (0 = auto) |
| `status` | string | Order lifecycle status: `open` / `closed` |
| `strategy_status` | string | Strategy running status: `running` / `stopped` |
| `sub_order_type` | string | Sub-order type: `mix_trade` / others |
| `tip_fee` | string | Tip fee; SOL only |
| `token_balance` | string | Remaining token balance; empty when not available |
| `token_logo` | string | Token logo URL |
| `token_name` | string | Token display name |
| `token_price` | string | Current token price; empty when not available |
| `total_supply` | string | Token total supply |
| `version` | int | Order schema version |
| `wallet_address` | string | Wallet address that placed the order |
#### `condition_orders[]` — Condition Sub-Order Object
| Field | Type | Description |
| ------------- | ------ | ---- |
| `cid` | string | Condition sub-order ID (UUID) |
| `order_type` | string | Sub-order type: `profit_stop` / `loss_stop` / `profit_stop_trace` / `loss_stop_trace` |
| `side` | string | Trade side: `sell` |
| `price_scale` | string | Price ratio relative to open price (string); `profit_stop` / `loss_stop` required |
| `sell_ratio` | string | Sell ratio (string), e.g. `"100"` |
| `check_price` | string | Computed trigger price derived from `price_scale` and open price |
| `status` | string | Sub-order status: `cancel` / `success` / `failed` |
#### `order_statistic` Object
| Field | Type | Description |
| ---------------------- | ------ | ---- |
| `buy_amount` | string | Bought token amount (smallest unit) |
| `buy_quote_price` | string | Quote token price at buy |
| `buy_usdt_price` | string | USDT-denominated price at buy |
| `quote_profit` | string | Realized profit in quote token |
| `sell_amount` | string | Total token amount sold |
| `sell_num` | int | Total number of sell attempts |
| `success_sell_amount` | string | Successfully sold token amount |
| `success_sell_num` | int | Number of successful sells |
| `usdt_profit` | string | Realized profit in USDT |
#### `sell_param` Object
| Field | Type | Description |
| -------------------------- | ------ | ---- |
| `anti_mev_mode` | string | Anti-MEV mode for the sell transaction |
| `auto_fee` | bool | Whether auto fee is enabled for the sell |
| `auto_slippage` | bool | Whether auto slippage is enabled for the sell |
| `auto_tip` | bool | Whether auto tip is enabled |
| `custom_rpc` | string | Custom RPC endpoint; empty string when not set |
| `fee` | string | Sell transaction fee |
| `gas_price` | string | Gas price for the sell |
| `is_anti_mev` | bool | Whether anti-MEV protection is active for the sell |
| `max_fee_per_gas` | string | EIP-1559 max fee per gas for the sell; EVM only |
| `max_priority_fee_per_gas` | string | EIP-1559 max priority fee per gas for the sell; EVM only |
| `max_tip_fee` | string | Maximum tip fee; empty when not set |
| `priority_fee` | string | Priority fee for the sell; SOL / BSC only |
| `slippage` | int | Slippage tolerance for the sell (0 = auto) |
| `tip_fee` | string | Tip fee for the sell; SOL only |
---
## `order strategy cancel` Usage
```bash
# Cancel a strategy order
gmgn-cli order strategy cancel \
--chain sol \
@@ -557,13 +727,30 @@ gmgn-cli order strategy cancel \
--order-id <order_id>
```
## `order strategy cancel` Parameters
| Parameter | Required | Description |
|-----------|----------|-------------|
| `--chain` | Yes | `sol` / `bsc` / `base` / `eth` |
| `--from` | Yes | Wallet address (must match API Key binding) |
| `--order-id` | Yes | Order ID to cancel |
| `--order-type` | No | Order type: `limit_order` (limit order) / `smart_trade` (mixed strategy order: take-profit, stop-loss, trailing take-profit, trailing stop-loss) |
| `--close-sell-model` | No | Sell model when closing the order |
---
## Notes
- Swap uses **critical auth** (API Key + signature) — CLI handles signing automatically, no manual processing needed
- Swap uses **signed 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`, `order strategy list`, and `order strategy cancel` use critical auth (require `GMGN_PRIVATE_KEY`)
- `order strategy create`, `order strategy list`, and `order strategy cancel` use signed auth (require `GMGN_PRIVATE_KEY`)
- Use `--raw` to get single-line JSON for further processing
- **Chain restrictions for fee flags** — see the `Chain` column in each parameter table above. `--priority-fee` and `--tip-fee` are SOL/BSC only; `--gas-price`, `--max-fee-per-gas`, `--max-priority-fee-per-gas` are BSC/BASE/ETH only; `--gas-level` and `--auto-fee` are ETH only. The server returns 400 if a chain-restricted flag is sent on the wrong chain. (`gas-price` itself supports all four chains including `sol`.)
- **EIP-1559 minimum values per chain:**
- BSC: `max_fee_per_gas` and `max_priority_fee_per_gas` min 50 000 000 wei (≈ 0.05 gwei); passing `"0"` returns 400
- BASE / ETH: `max_fee_per_gas` and `max_priority_fee_per_gas` min 200 000 wei
- EIP-1559 clamping applies only when `--condition-orders` is present (swap / multi-swap) or on every request (strategy/create)
## Input Validation
@@ -602,7 +789,7 @@ For full token research before swapping, see [`docs/workflow-token-research.md`]
## Execution Guidelines
- **[REQUIRED] Token security check** — Run before every swap. See **Pre-Swap Safety Check (REQUIRED)** section above. Uses normal auth (API Key only — no private key needed for this step).
- **[REQUIRED] Token security check** — Run before every swap. See **Pre-Swap Safety Check (REQUIRED)** section above. Uses exist auth (API Key only — no private key needed for this step).
- **Currency resolution** — When the user names a currency (SOL/BNB/ETH/USDC) instead of providing an address, look up its address in the Chain Currencies table and apply it automatically — never ask the user for it.
- Buy ("buy X SOL of TOKEN", "spend 0.5 USDC on TOKEN") → resolve currency to `--input-token`
- Sell ("sell TOKEN for SOL", "sell 50% of TOKEN to USDC") → resolve currency to `--output-token`
+58 -7
View File
@@ -18,7 +18,7 @@ Use the `gmgn-cli` tool to query token information based on the user's request.
- **Token address** — The on-chain contract address that uniquely identifies a token on its chain. Required for all token sub-commands. Format: base58 (SOL) or `0x...` hex (BSC/Base).
- **Chain** — The blockchain network: `sol` = Solana, `bsc` = BNB Smart Chain, `base` = Base (Coinbase L2), `eth` = Ethereum mainnet.
- **Market cap** — Not returned directly by `token info`. Calculate as `price × circulating_supply` (both are top-level fields in the response, already in human-readable units).
- **Market cap** — Not returned directly by `token info`. Calculate as `price.price × circulating_supply` (`price` is a nested object; use `price.price` for the current USD price string).
- **Liquidity** — USD value of token reserves in the main trading pool. Low liquidity (< $10k) means high price impact / slippage when buying or selling.
- **Holder** — A wallet that currently holds the token. `token holders` returns wallets ranked by current balance.
- **Trader** — Any wallet that has transacted with the token (bought or sold), regardless of current holdings. `token traders` covers both current holders and past traders.
@@ -34,7 +34,7 @@ Use the `gmgn-cli` tool to query token information based on the user's request.
| Sub-command | Description |
|-------------|-------------|
| `token info` | Basic info + realtime price, liquidity, market cap, total supply, holder count, social links (market cap = price × circulating_supply) |
| `token info` | Basic info + realtime price, liquidity, market cap, total supply, holder count, social links (market cap = price.price × circulating_supply) |
| `token security` | Security metrics (honeypot, taxes, holder concentration, contract risks) |
| `token pool` | Liquidity pool info (DEX, reserves, liquidity depth) |
| `token holders` | Top token holders list with profit/loss breakdown |
@@ -162,7 +162,7 @@ The response has five nested objects: `pool`, `dev`, `link`, `stat`, `wallet_tag
| `total_supply` | Total token supply (same as `circulating_supply` for most tokens) |
| `circulating_supply` | Circulating supply |
| `max_supply` | Maximum supply |
| `price` | Current price in USD |
| `price` | **Object** — price and trading stats (see `price` Object below). Access current price as `price.price`. |
| `liquidity` | Total liquidity in USD (from biggest pool) |
| `holder_count` | Number of unique token holders |
| `logo` | Token logo image URL |
@@ -274,6 +274,57 @@ The response has five nested objects: `pool`, `dev`, `link`, `stat`, `wallet_tag
| `wallet_tags_stat.fresh_wallets` | Number of fresh wallets |
| `wallet_tags_stat.top_wallets` | Number of top-ranked wallets |
**`price` Object** — Price and trading statistics (access current price via `price.price`)
| Field | Description |
|-------|-------------|
| `price.price` | Current price in USD (string) |
| `price.price_{window}` | Price at the start of the window; windows: `1m`, `5m`, `1h`, `6h`, `24h` |
| `price.buys_{window}` | Buy transaction count in the window |
| `price.sells_{window}` | Sell transaction count in the window |
| `price.volume_{window}` | Total trading volume in USD for the window |
| `price.buy_volume_{window}` | Buy volume in USD for the window |
| `price.sell_volume_{window}` | Sell volume in USD for the window |
| `price.swaps_{window}` | Total swap count for the window |
| `price.hot_level` | Heat level integer |
**`fee_distribution` Object** — Launchpad fee-sharing config (optional; present for `pump` / `bankr` tokens). Use `token info` to check fee distribution, creator reward claim status (`has_claimed_fee`), and royalty allocation for pump/bankr tokens.
| Field | Description |
|-------|-------------|
| `fee_distribution.launchpad` | Launchpad identifier: `"pump"`, `"bankr"`, or `""` (unknown) |
| `fee_distribution.platform_data` | Platform-specific fee config; structure varies by `launchpad` (see below) |
When `fee_distribution.launchpad = "pump"`:
| Field | Description |
|-------|-------------|
| `platform_data.fee_authority` | Fee authority wallet address |
| `platform_data.is_locked` | Whether the fee config is locked |
| `platform_data.show` | Whether fee distribution is displayed in the UI |
| `platform_data.list` | Array of fee-share holders (see FeeShareHolder below) |
| `platform_data.bonus_category` | Bonus category list (e.g. `creator_reward`, `cashback`) |
When `fee_distribution.launchpad = "bankr"`:
| Field | Description |
|-------|-------------|
| `platform_data.deployer` | Original deployer wallet address |
| `platform_data.fee_recipient` | Fee recipient wallet address |
| `platform_data.list` | Array of fee-share holders (see FeeShareHolder below) |
FeeShareHolder fields (each item in `platform_data.list`):
| Field | Description |
|-------|-------------|
| `wallet` | Wallet address |
| `royalty_bps` | Royalty share in basis points (10000 = 100%) |
| `is_creator` | Whether this is the original creator |
| `has_claimed_fee` | Whether fees have been claimed |
| `username` | Display name |
| `pfp` | Avatar URL |
| `twitter_username` | Twitter / X username |
---
### `token security` — Key Fields
@@ -629,7 +680,7 @@ Present as a concise card. Do not dump raw JSON.
```
{symbol} ({name})
Price: ${price} | Market Cap: ~${price × circulating_supply} | Liquidity: ${liquidity}
Price: ${price.price} | Market Cap: ~${price.price × circulating_supply} | Liquidity: ${liquidity}
Holders: {holder_count} | Smart Money: {wallet_tags_stat.smart_wallets} | KOLs: {wallet_tags_stat.renowned_wallets}
Social: @{link.twitter_username} | {link.website} | {link.telegram}
```
@@ -672,9 +723,9 @@ Show top rows only. Highlight wallets tagged `kol`, `smart_degen`, or flagged `b
## Notes
- **Market cap is not returned directly** — calculate it as `price × circulating_supply` (both fields are top-level; `circulating_supply` is already in human-readable token units, no decimal adjustment needed). Example: `price=3.11` × `circulating_supply=999999151` ≈ $3.11B market cap.
- **Trading volume (1h, 24h, etc.) is not included in `token info`** — to get volume or OHLCV data, use the `gmgn-market` skill and query K-line data: `gmgn-cli market kline --chain <chain> --address <token_address> --resolution <1m|5m|15m|1h|4h|1d>`. See the `gmgn-market` SKILL.md for full details.
- All token commands use normal auth (API Key only, no signature required)
- **Market cap is not returned directly** — calculate it as `price.price × circulating_supply` (`price` is now a nested object; use `price.price` for the current USD price string, and `circulating_supply` is a top-level field already in human-readable token units). Example: `price.price="3.11"` × `circulating_supply=999999151` ≈ $3.11B market cap.
- **Trading volume and swap counts by window are available in `token info`** via the `price` object: `volume_{window}`, `buy_volume_{window}`, `sell_volume_{window}`, `buys_{window}`, `sells_{window}`, `swaps_{window}` (windows: `1m`, `5m`, `1h`, `6h`, `24h`). For OHLCV candlestick data, use `gmgn-market kline`.
- All token commands use exist auth (API Key only, no signature required)
- Use `--raw` to get single-line JSON for further processing
- `--tag` applies to both `holders` and `traders` and filters to only wallets with that tag — if few results are returned, try the other tag value
- `amount_percentage` in holders/traders is a ratio (01), not a percentage — `0.05` means 5% of supply
+2 -2
View File
@@ -61,7 +61,7 @@ Use the `gmgn-cli` tool to query on-chain tracking data based on the user's requ
- `gmgn-cli` installed globally — if missing, run: `npm install -g gmgn-cli`
- `GMGN_API_KEY` configured in `~/.config/gmgn/.env` — required for all sub-commands
- `GMGN_PRIVATE_KEY` — required for `track follow-wallet` only (critical auth); not needed for `kol` or `smartmoney`
- `GMGN_PRIVATE_KEY` — required for `track follow-wallet` only (signed auth); not needed for `kol` or `smartmoney`
## Rate Limit Handling
@@ -311,7 +311,7 @@ To research any token surfaced by smart money activity, follow [`docs/workflow-t
## Notes
- `track follow-wallet` uses critical auth (API Key + private key signature); `track kol` and `track smartmoney` use normal auth (API Key only)
- `track follow-wallet` uses signed auth (API Key + private key signature); `track kol` and `track smartmoney` use exist auth (API Key only)
- `track follow-wallet` returns trades from wallets followed on the GMGN platform; the follow list is resolved automatically from the GMGN user account bound to the API Key — `--wallet` is optional
- Use `--raw` to get single-line JSON for further processing
- `track kol` / `track smartmoney` `--side` is a **client-side filter** — the CLI fetches all results then filters locally; it is NOT sent to the API
+50 -44
View File
@@ -2,8 +2,8 @@
* OpenApiClient GMGN OpenAPI external client
*
* Auth modes:
* Normal (market/token/portfolio): X-APIKEY + timestamp + client_id
* Critical (swap and order routes): normal auth + X-Signature (private key signature)
* Exist (market/token/portfolio): X-APIKEY + timestamp + client_id
* Signed (swap and order routes): X-APIKEY + timestamp + client_id + X-Signature (private key signature)
*/
import { buildAuthQuery, buildMessage, detectAlgorithm, sign } from "./signer.js";
@@ -80,9 +80,9 @@ export interface SwapParams {
is_anti_mev?: boolean;
priority_fee?: string;
tip_fee?: string;
auto_tip_fee?: boolean;
max_auto_fee?: string;
gas_price?: string;
gas_level?: string;
auto_fee?: boolean;
max_fee_per_gas?: string;
max_priority_fee_per_gas?: string;
condition_orders?: StrategyConditionOrder[];
@@ -111,9 +111,9 @@ export interface MultiSwapParams {
is_anti_mev?: boolean;
priority_fee?: string;
tip_fee?: string;
auto_tip_fee?: boolean;
max_auto_fee?: string;
gas_price?: string;
gas_level?: string;
auto_fee?: boolean;
max_fee_per_gas?: string;
max_priority_fee_per_gas?: string;
condition_orders?: StrategyConditionOrder[];
@@ -138,7 +138,9 @@ export interface StrategyCreateParams {
slippage?: number;
auto_slippage?: boolean;
fee?: string;
auto_fee?: boolean;
gas_price?: string;
gas_level?: string;
max_fee_per_gas?: string;
max_priority_fee_per_gas?: string;
is_anti_mev?: boolean;
@@ -202,29 +204,29 @@ export class OpenApiClient {
this.host = config.host.replace(/\/$/, "");
}
// ---- Token endpoints (normal auth) ----
// ---- Token endpoints (exist auth) ----
async getTokenInfo(chain: string, address: string): Promise<unknown> {
return this.normalRequest("GET", "/v1/token/info", { chain, address });
return this.authExistRequest("GET", "/v1/token/info", { chain, address });
}
async getTokenSecurity(chain: string, address: string): Promise<unknown> {
return this.normalRequest("GET", "/v1/token/security", { chain, address });
return this.authExistRequest("GET", "/v1/token/security", { chain, address });
}
async getTokenPoolInfo(chain: string, address: string): Promise<unknown> {
return this.normalRequest("GET", "/v1/token/pool_info", { chain, address });
return this.authExistRequest("GET", "/v1/token/pool_info", { chain, address });
}
async getTokenTopHolders(chain: string, address: string, extra: Record<string, string | number> = {}): Promise<unknown> {
return this.normalRequest("GET", "/v1/market/token_top_holders", { chain, address, ...extra });
return this.authExistRequest("GET", "/v1/market/token_top_holders", { chain, address, ...extra });
}
async getTokenTopTraders(chain: string, address: string, extra: Record<string, string | number> = {}): Promise<unknown> {
return this.normalRequest("GET", "/v1/market/token_top_traders", { chain, address, ...extra });
return this.authExistRequest("GET", "/v1/market/token_top_traders", { chain, address, ...extra });
}
// ---- Market endpoints (normal auth) ----
// ---- Market endpoints (exist auth) ----
async getTokenKline(
chain: string,
@@ -236,21 +238,21 @@ export class OpenApiClient {
const query: Record<string, string | number> = { chain, address, resolution };
if (from != null) query["from"] = from;
if (to != null) query["to"] = to;
return this.normalRequest("GET", "/v1/market/token_kline", query);
return this.authExistRequest("GET", "/v1/market/token_kline", query);
}
// ---- Portfolio endpoints (normal auth) ----
// ---- Portfolio endpoints ----
async getWalletHoldings(
chain: string,
walletAddress: string,
extra: Record<string, string | number> = {}
): Promise<unknown> {
return this.normalRequest("GET", "/v1/user/wallet_holdings", {
return this.authSignedRequest("GET", "/v1/user/wallet_holdings", {
chain,
wallet_address: walletAddress,
...extra,
});
}, null);
}
async getWalletActivity(
@@ -258,7 +260,7 @@ export class OpenApiClient {
walletAddress: string,
extra: Record<string, string | number | string[]> = {}
): Promise<unknown> {
return this.normalRequest("GET", "/v1/user/wallet_activity", {
return this.authExistRequest("GET", "/v1/user/wallet_activity", {
chain,
wallet_address: walletAddress,
...extra,
@@ -266,7 +268,7 @@ export class OpenApiClient {
}
async getWalletStats(chain: string, walletAddresses: string[], period = "7d"): Promise<unknown> {
return this.normalRequest("GET", "/v1/user/wallet_stats", {
return this.authExistRequest("GET", "/v1/user/wallet_stats", {
chain,
wallet_address: walletAddresses,
period,
@@ -278,54 +280,54 @@ export class OpenApiClient {
walletAddress: string,
tokenAddress: string
): Promise<unknown> {
return this.normalRequest("GET", "/v1/user/wallet_token_balance", { chain, wallet_address: walletAddress, token_address: tokenAddress });
return this.authExistRequest("GET", "/v1/user/wallet_token_balance", { chain, wallet_address: walletAddress, token_address: tokenAddress });
}
async getTrenches(chain: string, types?: string[], platforms?: string[], limit?: number, filters?: Record<string, number | string>): Promise<unknown> {
const body = buildTrenchesBody(chain, types, platforms, limit, filters);
return this.normalRequest("POST", "/v1/trenches", { chain }, body);
return this.authExistRequest("POST", "/v1/trenches", { chain }, body);
}
// ---- Market trending endpoints (normal auth) ----
// ---- Market trending endpoints (exist auth) ----
async getTrendingSwaps(
chain: string,
interval: string,
extra: Record<string, string | number | string[]> = {}
): Promise<unknown> {
return this.normalRequest("GET", "/v1/market/rank", { chain, interval, ...extra });
return this.authExistRequest("GET", "/v1/market/rank", { chain, interval, ...extra });
}
async getTokenSignalV2(chain: string, groups: TokenSignalGroup[]): Promise<unknown> {
return this.normalRequest("POST", "/v1/market/token_signal", {}, { chain, groups });
return this.authExistRequest("POST", "/v1/market/token_signal", {}, { chain, groups });
}
// ---- User endpoints (normal auth) ----
// ---- User endpoints (exist auth) ----
async getUserInfo(): Promise<unknown> {
return this.normalRequest("GET", "/v1/user/info", {});
return this.authExistRequest("GET", "/v1/user/info", {});
}
async getFollowWallet(chain: string, extra: Record<string, string | number | string[]> = {}): Promise<unknown> {
return this.criticalRequest("GET", "/v1/trade/follow_wallet", { chain, ...extra }, null);
return this.authSignedRequest("GET", "/v1/trade/follow_wallet", { chain, ...extra }, null);
}
async getKol(chain?: string, limit?: number): Promise<unknown> {
const query: Record<string, string | number> = {};
if (chain) query["chain"] = chain;
if (limit != null) query["limit"] = limit;
return this.normalRequest("GET", "/v1/user/kol", query);
return this.authExistRequest("GET", "/v1/user/kol", query);
}
async getSmartMoney(chain?: string, limit?: number): Promise<unknown> {
const query: Record<string, string | number> = {};
if (chain) query["chain"] = chain;
if (limit != null) query["limit"] = limit;
return this.normalRequest("GET", "/v1/user/smartmoney", query);
return this.authExistRequest("GET", "/v1/user/smartmoney", query);
}
async getCreatedTokens(chain: string, walletAddress: string, extra: Record<string, string | number> = {}): Promise<unknown> {
return this.normalRequest("GET", "/v1/user/created_tokens", { chain, wallet_address: walletAddress, ...extra });
return this.authExistRequest("GET", "/v1/user/created_tokens", { chain, wallet_address: walletAddress, ...extra });
}
async quoteOrder(
@@ -337,50 +339,54 @@ export class OpenApiClient {
slippage: number
): Promise<unknown> {
const query = { chain, from_address, input_token, output_token, input_amount, slippage };
return this.criticalRequest("GET", "/v1/trade/quote", query, null);
return this.authSignedRequest("GET", "/v1/trade/quote", query, null);
}
// ---- Swap endpoints (critical auth) ----
// ---- Swap endpoints (signed auth) ----
async swap(params: SwapParams): Promise<unknown> {
return this.criticalRequest("POST", "/v1/trade/swap", {}, params);
return this.authSignedRequest("POST", "/v1/trade/swap", {}, params);
}
async multiSwap(params: MultiSwapParams): Promise<unknown> {
return this.criticalRequest("POST", "/v1/trade/multi_swap", {}, params);
return this.authSignedRequest("POST", "/v1/trade/multi_swap", {}, params);
}
async queryOrder(orderId: string, chain: string): Promise<unknown> {
return this.criticalRequest("GET", "/v1/trade/query_order", { order_id: orderId, chain }, null);
return this.authSignedRequest("GET", "/v1/trade/query_order", { order_id: orderId, chain }, null);
}
// ---- Strategy order endpoints (critical auth) ----
async getGasPrice(chain: string): Promise<unknown> {
return this.authExistRequest("GET", "/v1/trade/gas_price", { chain });
}
// ---- Strategy order endpoints (signed auth) ----
async createStrategyOrder(params: StrategyCreateParams): Promise<unknown> {
return this.criticalRequest("POST", "/v1/trade/strategy/create", {}, params);
return this.authSignedRequest("POST", "/v1/trade/strategy/create", {}, params);
}
async getStrategyOrders(chain: string, extra: Record<string, string | number> = {}): Promise<unknown> {
return this.criticalRequest("GET", "/v1/trade/strategy/orders", { chain, ...extra }, null);
return this.authSignedRequest("GET", "/v1/trade/strategy/orders", { chain, ...extra }, null);
}
async cancelStrategyOrder(params: StrategyCancelParams): Promise<unknown> {
return this.criticalRequest("POST", "/v1/trade/strategy/cancel", {}, params);
return this.authSignedRequest("POST", "/v1/trade/strategy/cancel", {}, params);
}
// ---- Cooking endpoints ----
async getCookingStatistics(): Promise<unknown> {
return this.normalRequest("GET", "/v1/cooking/statistics", {});
return this.authExistRequest("GET", "/v1/cooking/statistics", {});
}
async createToken(params: CreateTokenParams): Promise<unknown> {
return this.criticalRequest("POST", "/v1/cooking/create_token", {}, params);
return this.authSignedRequest("POST", "/v1/cooking/create_token", {}, params);
}
// ---- Internal methods ----
private async normalRequest(
private async authExistRequest(
method: string,
subPath: string,
queryExtra: Record<string, string | number | string[]>,
@@ -406,14 +412,14 @@ export class OpenApiClient {
}, true);
}
private async criticalRequest(
private async authSignedRequest(
method: string,
subPath: string,
queryExtra: Record<string, string | number | string[]>,
body: unknown
): Promise<unknown> {
if (!this.privateKeyPem) {
throw new Error("GMGN_PRIVATE_KEY is required for critical-auth commands (swap, order, and follow-wallet commands)");
throw new Error("GMGN_PRIVATE_KEY is required for critical-auth commands (swap, order, follow-wallet, and portfolio holdings commands)");
}
return this.executePreparedRequest(() => {
+4 -3
View File
@@ -30,7 +30,7 @@ export function buildAuthQuery(): { timestamp: number; client_id: string } {
}
/**
* Build the signature message (critical auth)
* Build the signature message (signed auth)
* Format: {sub_path}:{sorted_query_string}:{request_body}:{timestamp}
* sorted_query_string: all query params (including timestamp, client_id) sorted alphabetically by key.
* Array values are serialized as repeated k=v pairs (same as buildUrl / URLSearchParams), sorted by value.
@@ -44,11 +44,12 @@ export function buildMessage(
const sortedQs = Object.keys(queryParams)
.sort()
.flatMap((k) => {
const ek = encodeURIComponent(k);
const v = queryParams[k];
if (Array.isArray(v)) {
return [...v].sort().map((item) => `${k}=${item}`);
return [...v].sort().map((item) => `${ek}=${encodeURIComponent(item)}`);
}
return [`${k}=${v}`];
return [`${ek}=${encodeURIComponent(String(v))}`];
})
.join("&");
return `${subPath}:${sortedQs}:${body}:${timestamp}`;
+1 -1
View File
@@ -9,7 +9,7 @@ export function registerCookingCommands(program: Command): void {
cooking
.command("stats")
.description("Get token creation statistics by launchpad (normal auth)")
.description("Get token creation statistics by launchpad (exist auth)")
.option("--raw", "Output raw JSON")
.action(async (opts) => {
const client = new OpenApiClient(getConfig());
+95 -74
View File
@@ -20,10 +20,11 @@ export function registerSwapCommands(program: Command): void {
.option("--anti-mev", "Enable anti-MEV protection, default true")
.option("--priority-fee <sol>", "Priority fee in SOL (≥ 0.00001, SOL only)")
.option("--tip-fee <amount>", "Tip fee (SOL ≥ 0.00001 SOL / BSC ≥ 0.000001 BNB)")
.option("--max-auto-fee <amount>", "Max auto fee cap")
.option("--gas-price <gwei>", "Gas price in gwei (BSC ≥ 0.05 / BASE/ETH ≥ 0.01)")
.option("--max-fee-per-gas <amount>", "EIP-1559 max fee per gas (Base)")
.option("--max-priority-fee-per-gas <amount>", "EIP-1559 max priority fee per gas (Base)")
.option("--gas-price <gwei>", "Gas price in gwei (BSC ≥ 0.05 / BASE/ETH ≥ 0.01); mutually exclusive with --gas-level")
.option("--gas-level <level>", "Gas price tier (eth only): low / average / high; mutually exclusive with --gas-price")
.option("--auto-fee", "Auto fee mode (eth only); delegates fee selection to trading bot for condition_orders strategy")
.option("--max-fee-per-gas <amount>", "EIP-1559 max fee per gas (BSC / BASE / ETH)")
.option("--max-priority-fee-per-gas <amount>", "EIP-1559 max priority fee per gas (BSC / BASE / ETH)")
.option("--condition-orders <json>", 'JSON array of take-profit/stop-loss conditions, e.g. \'[{"order_type":"profit_stop","side":"sell","price_scale":"150","sell_ratio":"100"}]\'; trace types: \'[{"order_type":"profit_stop_trace","side":"sell","price_scale":"150","sell_ratio":"100","drawdown_rate":"50"}]\'')
.option("--sell-ratio-type <type>", "Sell ratio base: buy_amount (default) / hold_amount; only used with --condition-orders")
.option("--raw", "Output raw JSON")
@@ -40,7 +41,7 @@ export function registerSwapCommands(program: Command): void {
if (opts.percent != null) validatePercent(opts.percent);
const params: SwapParams = {
chain: opts.chain,
from_address: opts.from,
from_address: opts.chain === "sol" ? opts.from : opts.from.toLowerCase(),
input_token: opts.inputToken,
output_token: opts.outputToken,
input_amount: opts.percent != null ? (opts.amount ?? "0") : opts.amount,
@@ -52,8 +53,9 @@ export function registerSwapCommands(program: Command): void {
if (opts.antiMev) params.is_anti_mev = true;
if (opts.priorityFee) params.priority_fee = opts.priorityFee;
if (opts.tipFee) params.tip_fee = opts.tipFee;
if (opts.maxAutoFee) params.max_auto_fee = opts.maxAutoFee;
if (opts.autoFee) params.auto_fee = true;
if (opts.gasPrice) params.gas_price = String(Math.round(parseFloat(opts.gasPrice) * 1e9));
if (opts.gasLevel) params.gas_level = opts.gasLevel;
if (opts.maxFeePerGas) params.max_fee_per_gas = opts.maxFeePerGas;
if (opts.maxPriorityFeePerGas) params.max_priority_fee_per_gas = opts.maxPriorityFeePerGas;
if (opts.conditionOrders) {
@@ -71,11 +73,83 @@ export function registerSwapCommands(program: Command): void {
printResult(data, opts.raw);
});
program
.command("multi-swap")
.description("Submit token swaps across multiple wallets concurrently (up to 100 wallets)")
.requiredOption("--chain <chain>", "Chain: sol / bsc / base / eth")
.requiredOption("--accounts <addresses>", "Comma-separated wallet addresses (all must be bound to the API Key)")
.requiredOption("--input-token <address>", "Input token contract address")
.requiredOption("--output-token <address>", "Output token contract address")
.option("--input-amount <json>", 'JSON map of wallet→amount (smallest unit), e.g. \'{"addr1":"1000000","addr2":"2000000"}\'')
.option("--input-amount-bps <json>", 'JSON map of wallet→percent in bps (110000, e.g. 5000=50%), e.g. \'{"addr1":"5000"}\'')
.option("--output-amount <json>", "JSON map of wallet→target output amount")
.option("--slippage <n>", "Slippage tolerance (e.g. 0.01 = 1%)", parseFloat)
.option("--auto-slippage", "Enable automatic slippage")
.option("--anti-mev", "Enable anti-MEV protection")
.option("--priority-fee <sol>", "Priority fee in SOL (SOL only, ≥ 0.00001)")
.option("--tip-fee <amount>", "Tip fee (SOL ≥ 0.00001 / BSC ≥ 0.000001 BNB)")
.option("--gas-price <gwei>", "Gas price in gwei (BSC ≥ 0.05 / BASE/ETH ≥ 0.01); mutually exclusive with --gas-level")
.option("--gas-level <level>", "Gas price tier (eth only): low / average / high; mutually exclusive with --gas-price")
.option("--auto-fee", "Auto fee mode (eth only); delegates fee selection to trading bot for condition_orders strategy")
.option("--max-fee-per-gas <amount>", "EIP-1559 max fee per gas (BSC / BASE / ETH)")
.option("--max-priority-fee-per-gas <amount>", "EIP-1559 max priority fee per gas (BSC / BASE / ETH)")
.option("--condition-orders <json>", "JSON array of take-profit/stop-loss conditions attached to each successful wallet's swap")
.option("--sell-ratio-type <type>", "Sell ratio base: buy_amount (default) / hold_amount; only used with --condition-orders")
.option("--raw", "Output raw JSON")
.action(async (opts) => {
if (!opts.inputAmount && !opts.inputAmountBps && !opts.outputAmount) {
console.error("[gmgn-cli] At least one of --input-amount, --input-amount-bps, or --output-amount must be provided");
process.exit(1);
}
validateChain(opts.chain);
const accounts = (opts.accounts as string).split(",").map((a: string) => a.trim()).filter(Boolean);
if (accounts.length === 0 || accounts.length > 100) {
console.error("[gmgn-cli] --accounts must be 1100 comma-separated wallet addresses");
process.exit(1);
}
const params: MultiSwapParams = {
chain: opts.chain,
accounts: opts.chain === "sol" ? accounts : accounts.map((a: string) => a.toLowerCase()),
input_token: opts.inputToken,
output_token: opts.outputToken,
};
if (opts.inputAmount) {
try { params.input_amount = JSON.parse(opts.inputAmount); }
catch { console.error("[gmgn-cli] --input-amount must be valid JSON"); process.exit(1); }
}
if (opts.inputAmountBps) {
try { params.input_amount_bps = JSON.parse(opts.inputAmountBps); }
catch { console.error("[gmgn-cli] --input-amount-bps must be valid JSON"); process.exit(1); }
}
if (opts.outputAmount) {
try { params.output_amount = JSON.parse(opts.outputAmount); }
catch { console.error("[gmgn-cli] --output-amount must be valid JSON"); process.exit(1); }
}
if (opts.slippage != null) params.slippage = opts.slippage;
if (opts.autoSlippage) params.auto_slippage = true;
if (opts.antiMev) params.is_anti_mev = true;
if (opts.priorityFee) params.priority_fee = opts.priorityFee;
if (opts.tipFee) params.tip_fee = opts.tipFee;
if (opts.autoFee) params.auto_fee = true;
if (opts.gasPrice) params.gas_price = String(Math.round(parseFloat(opts.gasPrice) * 1e9));
if (opts.gasLevel) params.gas_level = opts.gasLevel;
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.multiSwap(params).catch(exitOnError);
printResult(data, opts.raw);
});
const order = program.command("order").description("Order management commands");
order
.command("quote")
.description("Get a swap quote without submitting a transaction (requires critical auth)")
.description("Get a swap quote without submitting a transaction (signed auth — requires GMGN_PRIVATE_KEY)")
.requiredOption("--chain <chain>", "Chain: sol / bsc / base / eth (requires GMGN_PRIVATE_KEY)")
.requiredOption("--from <address>", "Wallet address (must match API Key binding)")
.requiredOption("--input-token <address>", "Input token contract address")
@@ -110,74 +184,13 @@ export function registerSwapCommands(program: Command): void {
});
program
.command("multi-swap")
.description("Submit token swaps across multiple wallets concurrently (up to 100 wallets)")
.requiredOption("--chain <chain>", "Chain: sol / bsc / base / eth")
.requiredOption("--accounts <addresses>", "Comma-separated wallet addresses (all must be bound to the API Key)")
.requiredOption("--input-token <address>", "Input token contract address")
.requiredOption("--output-token <address>", "Output token contract address")
.option("--input-amount <json>", 'JSON map of wallet→amount (smallest unit), e.g. \'{"addr1":"1000000","addr2":"2000000"}\'')
.option("--input-amount-bps <json>", 'JSON map of wallet→percent in bps (110000, e.g. 5000=50%), e.g. \'{"addr1":"5000"}\'')
.option("--output-amount <json>", "JSON map of wallet→target output amount")
.option("--slippage <n>", "Slippage tolerance (e.g. 0.01 = 1%)", parseFloat)
.option("--auto-slippage", "Enable automatic slippage")
.option("--anti-mev", "Enable anti-MEV protection")
.option("--priority-fee <sol>", "Priority fee in SOL (SOL only, ≥ 0.00001)")
.option("--tip-fee <amount>", "Tip fee (SOL ≥ 0.00001 / BSC ≥ 0.000001 BNB)")
.option("--auto-tip-fee", "Enable automatic tip fee")
.option("--max-auto-fee <amount>", "Max auto fee cap")
.option("--gas-price <gwei>", "Gas price in gwei (BSC ≥ 0.05 / BASE/ETH ≥ 0.01)")
.option("--max-fee-per-gas <amount>", "EIP-1559 max fee per gas (Base only)")
.option("--max-priority-fee-per-gas <amount>", "EIP-1559 max priority fee per gas (Base only)")
.option("--condition-orders <json>", "JSON array of take-profit/stop-loss conditions attached to each successful wallet's swap")
.option("--sell-ratio-type <type>", "Sell ratio base: buy_amount (default) / hold_amount; only used with --condition-orders")
.command("gas-price")
.description("Query recommended gas price tiers for any chain (exist auth — API Key only; eth / bsc / base / sol)")
.requiredOption("--chain <chain>", "Chain: eth / bsc / base / sol")
.option("--raw", "Output raw JSON")
.action(async (opts) => {
if (!opts.inputAmount && !opts.inputAmountBps && !opts.outputAmount) {
console.error("[gmgn-cli] At least one of --input-amount, --input-amount-bps, or --output-amount must be provided");
process.exit(1);
}
validateChain(opts.chain);
const accounts = (opts.accounts as string).split(",").map((a: string) => a.trim()).filter(Boolean);
if (accounts.length === 0 || accounts.length > 100) {
console.error("[gmgn-cli] --accounts must be 1100 comma-separated wallet addresses");
process.exit(1);
}
const params: MultiSwapParams = {
chain: opts.chain,
accounts,
input_token: opts.inputToken,
output_token: opts.outputToken,
};
if (opts.inputAmount) {
try { params.input_amount = JSON.parse(opts.inputAmount); }
catch { console.error("[gmgn-cli] --input-amount must be valid JSON"); process.exit(1); }
}
if (opts.inputAmountBps) {
try { params.input_amount_bps = JSON.parse(opts.inputAmountBps); }
catch { console.error("[gmgn-cli] --input-amount-bps must be valid JSON"); process.exit(1); }
}
if (opts.outputAmount) {
try { params.output_amount = JSON.parse(opts.outputAmount); }
catch { console.error("[gmgn-cli] --output-amount must be valid JSON"); process.exit(1); }
}
if (opts.slippage != null) params.slippage = opts.slippage;
if (opts.autoSlippage) params.auto_slippage = true;
if (opts.antiMev) params.is_anti_mev = true;
if (opts.priorityFee) params.priority_fee = opts.priorityFee;
if (opts.tipFee) params.tip_fee = opts.tipFee;
if (opts.autoTipFee) params.auto_tip_fee = true;
if (opts.maxAutoFee) params.max_auto_fee = opts.maxAutoFee;
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.multiSwap(params).catch(exitOnError);
const client = new OpenApiClient(getConfig(false));
const data = await client.getGasPrice(opts.chain).catch(exitOnError);
printResult(data, opts.raw);
});
@@ -202,7 +215,11 @@ export function registerSwapCommands(program: Command): void {
.option("--auto-slippage", "Enable automatic slippage")
.option("--priority-fee <sol>", "Priority fee in SOL (required for SOL chain)")
.option("--tip-fee <amount>", "Tip fee (required for SOL chain)")
.option("--gas-price <gwei>", "Gas price in gwei (required for BSC; ≥ 0.05 gwei / BASE/ETH ≥ 0.01 gwei)")
.option("--auto-fee", "Auto fee mode (eth only); delegates fee selection to trading bot")
.option("--gas-price <gwei>", "Gas price in gwei (BSC ≥ 0.05 / BASE/ETH ≥ 0.01 gwei); mutually exclusive with --gas-level")
.option("--gas-level <level>", "Gas price tier (eth only): low / average / high; mutually exclusive with --gas-price")
.option("--max-fee-per-gas <amount>", "EIP-1559 max fee per gas (BSC / BASE / ETH)")
.option("--max-priority-fee-per-gas <amount>", "EIP-1559 max priority fee per gas (BSC / BASE / ETH)")
.option("--anti-mev", "Enable anti-MEV protection")
.option("--raw", "Output raw JSON")
.action(async (opts) => {
@@ -233,7 +250,11 @@ 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.autoFee) params.auto_fee = true;
if (opts.gasPrice) params.gas_price = String(Math.round(parseFloat(opts.gasPrice) * 1e9));
if (opts.gasLevel) params.gas_level = opts.gasLevel;
if (opts.maxFeePerGas) params.max_fee_per_gas = opts.maxFeePerGas;
if (opts.maxPriorityFeePerGas) params.max_priority_fee_per_gas = opts.maxPriorityFeePerGas;
if (opts.antiMev) params.is_anti_mev = true;
const client = new OpenApiClient(getConfig(true));
const data = await client.createStrategyOrder(params).catch(exitOnError);