Compare commits

...
Author SHA1 Message Date
David LauandClaude Sonnet 4.6 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.AIandGitHub 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 LauandClaude Sonnet 4.6 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.AIandGitHub 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 LauandClaude Sonnet 4.6 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.AIandGitHub 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
gina888666andClaude Sonnet 4.6 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
gina888666andClaude Sonnet 4.6 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
gina888666andClaude Sonnet 4.6 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
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
deepfeatureandGitHub 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 LauandClaude Sonnet 4.6 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
deepfeatureandClaude Sonnet 4.6 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.AIandGitHub 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
gina888666andClaude Sonnet 4.6 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.AIandGitHub 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 LauandClaude Sonnet 4.6 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
gumponchainandClaude Sonnet 4.6 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.AIandGitHub 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 LauandClaude Sonnet 4.6 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
17 changed files with 286 additions and 97 deletions
+1
View File
@@ -4,6 +4,7 @@ on:
push: push:
tags: tags:
- "v*" - "v*"
workflow_dispatch:
permissions: permissions:
contents: read contents: read
+2 -2
View File
@@ -93,8 +93,8 @@ EOF
| Mode | Commands | Requirements | | Mode | Commands | Requirements |
|------|----------|--------------| |------|----------|--------------|
| Normal | token / market / portfolio / track kol / track smartmoney | `GMGN_API_KEY` only, no signature | | Normal | token / market / portfolio (except holdings) / 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 | | Critical | swap / order / portfolio holdings / track follow-wallet | `GMGN_API_KEY` + `GMGN_PRIVATE_KEY` — CLI handles signing automatically |
## SKILL.md Authoring Rules ## SKILL.md Authoring Rules
+37 -2
View File
@@ -557,7 +557,7 @@ gmgn-cli order quote \
--amount 1000000 \ --amount 1000000 \
--slippage 0.01 --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 \ gmgn-cli order quote \
--chain bsc \ --chain bsc \
--from <wallet-address> \ --from <wallet-address> \
@@ -569,6 +569,12 @@ gmgn-cli order quote \
# Query order # Query order
gmgn-cli order get --chain sol --order-id <order-id> 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 # Multi-wallet concurrent swap
gmgn-cli multi-swap \ gmgn-cli multi-swap \
--chain sol \ --chain sol \
@@ -579,7 +585,35 @@ gmgn-cli multi-swap \
--slippage 0.01 --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) ### 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` | — | | 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 | | 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> 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 # 多钱包并发 Swap
gmgn-cli multi-swap \ gmgn-cli multi-swap \
--chain sol \ --chain sol \
@@ -605,6 +611,34 @@ gmgn-cli multi-swap \
> `order quote` 在 `sol` / `bsc` / `base` / `eth` 上都走关键鉴权,必须配置 `GMGN_PRIVATE_KEY`。 > `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(需要私钥) ### 带止盈止损的 Swap(需要私钥)
**`hold_amount` 模式** — 按触发时的实际持仓比例卖出: **`hold_amount` 模式** — 按触发时的实际持仓比例卖出:
@@ -698,6 +732,7 @@ gmgn-cli cooking \
|----------|----------|-----------| |----------|----------|-----------|
| token / market / portfolio / track | `sol` / `bsc` / `base` / `eth` | — | | 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 | | swap / order | `sol` / `bsc` / `base` / `eth` | sol: SOL、USDC · bsc: BNB、USDC · base: ETH、USDC · eth: ETH |
| gas-price | `sol` / `bsc` / `base` / `eth` | — |
--- ---
+8 -8
View File
@@ -351,7 +351,7 @@ gmgn-cli market signal --chain sol --groups '<json_array>' [--raw]
## portfolio follow-wallet ## 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 ```bash
gmgn-cli track follow-wallet \ gmgn-cli track follow-wallet \
@@ -411,7 +411,7 @@ gmgn-cli track smartmoney [--chain <chain>] [--limit <n>] [--side <side>] [--raw
## order quote ## 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 ```bash
npx gmgn-cli order quote \ npx gmgn-cli order quote \
@@ -487,11 +487,11 @@ npx gmgn-cli swap \
| `--tip-fee` | No | `sol` / `bsc` | Tip fee (SOL ≥ 0.00001 / BSC ≥ 0.000001 BNB) | | `--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-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`. | | `--gas-level` | No | `eth` | Gas price tier: `low` / `average` / `high`. Mutually exclusive with `--gas-price`. |
| `--auto-fee` | No | `eth` | Auto fee mode — delegates fee selection to the trading bot for `--condition-orders` strategy | | `--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-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 | | `--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) | | `--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 | Sell ratio base for `--condition-orders`: `buy_amount` (default) / `hold_amount` | | `--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): **`--condition-orders` example** (100% sell at 2× price, 100% sell at 50% price):
@@ -564,11 +564,11 @@ gmgn-cli multi-swap \
| `--tip-fee` | No | `sol` / `bsc` | Tip fee (SOL ≥ 0.00001 / BSC ≥ 0.000001 BNB) | | `--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-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`. | | `--gas-level` | No | `eth` | Gas price tier: `low` / `average` / `high`. Mutually exclusive with `--gas-price`. |
| `--auto-fee` | No | `eth` | Auto fee mode — delegates fee selection to the trading bot for `--condition-orders` strategy | | `--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-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 | | `--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) | | `--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 | Sell ratio base: `buy_amount` (default) / `hold_amount` | | `--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: **Response fields (data):** Array of per-wallet results:
@@ -801,7 +801,7 @@ Important notes:
| `AUTH_SIGNATURE_INVALID` | 401 | Signature verification failed | | `AUTH_SIGNATURE_INVALID` | 401 | Signature verification failed |
| `AUTH_TIMESTAMP_EXPIRED` | 401 | Timestamp is outside the valid window (±5s) | | `AUTH_TIMESTAMP_EXPIRED` | 401 | Timestamp is outside the valid window (±5s) |
| `AUTH_CLIENT_ID_REPLAYED` | 401 | client_id replayed within 7s | | `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_EXCEEDED` | 429 | Rate limit exceeded |
| `RATE_LIMIT_BANNED` | 429 | Temporarily banned due to repeated rate limit violations | | `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` | | `ERROR_RATE_LIMIT_BLOCKED` | 429 | Temporarily blocked after repeated business errors on `swap` |
@@ -810,5 +810,5 @@ Important notes:
| `BAD_REQUEST` | 400 | Missing or invalid request parameters | | `BAD_REQUEST` | 400 | Missing or invalid request parameters |
| `INTERNAL_API_UNAVAILABLE` | 502 | Downstream market API unavailable | | `INTERNAL_API_UNAVAILABLE` | 502 | Downstream market API unavailable |
| `BROKER_UNAVAILABLE` | 502 | Downstream trade broker 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 | | `INTERNAL_ERROR` | 500 | Internal server error |
+2 -2
View File
@@ -1,12 +1,12 @@
{ {
"name": "gmgn-cli", "name": "gmgn-cli",
"version": "1.3.2", "version": "1.3.6",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "gmgn-cli", "name": "gmgn-cli",
"version": "1.3.2", "version": "1.3.6",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"commander": "^12.1.0", "commander": "^12.1.0",
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "gmgn-cli", "name": "gmgn-cli",
"version": "1.3.2", "version": "1.3.6",
"description": "GMGN OpenAPI CLI — call GMGN market, token, portfolio and swap APIs from the command line", "description": "GMGN OpenAPI CLI — call GMGN market, token, portfolio and swap APIs from the command line",
"type": "module", "type": "module",
"bin": { "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. - **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. - **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 | | Sub-command | Description |
|-------------|-------------| |-------------|-------------|
| `cooking stats` | Get token creation count statistics grouped by launchpad platform (normal auth) | | `cooking stats` | Get token creation count statistics grouped by launchpad platform (exist auth) |
| `cooking create` | Deploy a new token on a launchpad platform (requires private key) | | `cooking create` | Deploy a new token on a launchpad platform (signed auth) |
## Supported Chains ## Supported Chains
@@ -364,8 +364,8 @@ Once all information is collected, present the pre-create confirmation summary (
## Notes ## Notes
- `cooking create` uses **critical auth** (API Key + signature) — CLI handles signing automatically. - `cooking create` uses **signed auth** (API Key + signature) — CLI handles signing automatically.
- `cooking stats` uses normal auth (API Key only — no private key needed). - `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. - 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. - 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 kline`: `--from` and `--to` are Unix timestamps in **seconds** — CLI converts to milliseconds automatically
- `market trending`: `--filter` and `--platform` are repeatable flags - `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 - 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 - 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. - **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. 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 | | Command | Route | Weight |
|---------|-------|--------| |---------|-------|--------|
| `portfolio info` | `GET /v1/user/info` | 1 | | `portfolio info` | `GET /v1/user/info` | 1 |
| `portfolio holdings` | `GET /v1/user/wallet_holdings` | 2 |
| `portfolio activity` | `GET /v1/user/wallet_activity` | 3 | | `portfolio activity` | `GET /v1/user/wallet_activity` | 3 |
| `portfolio stats` | `GET /v1/user/wallet_stats` | 3 | | `portfolio stats` | `GET /v1/user/wallet_stats` | 3 |
| `portfolio token-balance` | `GET /v1/user/wallet_token_balance` | 1 | | `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.symbol` | Token ticker |
| `token_amount` | Token quantity in this transaction | | `token_amount` | Token quantity in this transaction |
| `cost_usd` | USD value of 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 | | `timestamp` | Unix timestamp of the transaction |
| `next` | Pagination cursor — pass to `--cursor` to fetch the next page | | `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 ## 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 - `portfolio stats` supports multiple `--wallet` flags for batch queries
- Use `--raw` to get single-line JSON for further processing - 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. - **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.
+128 -19
View File
@@ -26,7 +26,7 @@ Use the `gmgn-cli` tool to submit a token swap or query an existing order. `GMGN
- **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.** - **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`. - **`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,9 +47,9 @@ Use the `gmgn-cli` tool to submit a token swap or query an existing order. `GMGN
|-------------|-------------| |-------------|-------------|
| `swap` | Submit a token swap | | `swap` | Submit a token swap |
| `multi-swap` | Submit token swaps across multiple wallets concurrently (up to 100) | | `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 | | `order get` | Query order status |
| `gas-price` | Query recommended gas price (low / average / high tiers) for any chain; normal auth | | `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 create` | Create a limit/strategy order (requires private key) |
| `order strategy list` | List strategy orders (requires private key) | | `order strategy list` | List strategy orders (requires private key) |
| `order strategy cancel` | Cancel a strategy order (requires private key) | | `order strategy cancel` | Cancel a strategy order (requires private key) |
@@ -88,6 +88,9 @@ All swap-related routes used by this skill go through GMGN's leaky-bucket limite
| `multi-swap` | `POST /v1/trade/multi_swap` | 5 | | `multi-swap` | `POST /v1/trade/multi_swap` | 5 |
| `order quote` | `GET /v1/trade/quote` | 2 | | `order quote` | `GET /v1/trade/quote` | 2 |
| `order get` | `GET /v1/trade/query_order` | 1 | | `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 | | `gas-price` | `GET /v1/trade/gas_price` | 1 |
When a request returns `429`: When a request returns `429`:
@@ -189,11 +192,11 @@ gmgn-cli swap \
| `--tip-fee <n>` | No | `sol` / `bsc` | Tip fee (SOL ≥ 0.00001 / BSC ≥ 0.000001 BNB). 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-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`. | | `--gas-level <level>` | No | `eth` | Gas price tier: `low` / `average` / `high`. Mutually exclusive with `--gas-price`. |
| `--auto-fee` | No | `eth` | Auto fee mode — delegates fee selection to the trading bot for `--condition-orders` strategy. | | `--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-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`. | | `--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. | | `--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 | 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 | | `--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`) ### ConditionOrder Fields (for `--condition-orders`)
@@ -291,7 +294,7 @@ gmgn-cli swap \
### Pre-swap Confirmation ### 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 ```bash
gmgn-cli order quote \ gmgn-cli order quote \
@@ -407,11 +410,11 @@ gmgn-cli multi-swap \
| `--tip-fee <amount>` | No | `sol` / `bsc` | Tip fee (SOL ≥ 0.00001 / BSC ≥ 0.000001 BNB). 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-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`. | | `--gas-level <level>` | No | `eth` | Gas price tier: `low` / `average` / `high`. Mutually exclusive with `--gas-price`. |
| `--auto-fee` | No | `eth` | Auto fee mode — delegates fee selection to the trading bot for `--condition-orders` strategy. | | `--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-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`. | | `--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. | | `--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 | Sell ratio base for `--condition-orders`: `buy_amount` (default) / `hold_amount`. | | `--sell-ratio-type <type>` | No | all | **Only with `--condition-orders`.** Sell ratio base: `buy_amount` (default) / `hold_amount`. |
## `multi-swap` Response Fields ## `multi-swap` Response Fields
@@ -430,7 +433,7 @@ The response `data` is an array — one element per wallet:
## `order quote` Usage ## `order quote` Usage
Get an estimated output amount before submitting a swap. All supported quote chains use critical auth and require `GMGN_PRIVATE_KEY`. Get an estimated output amount before submitting a swap. All supported quote chains use signed auth and require `GMGN_PRIVATE_KEY`.
```bash ```bash
gmgn-cli order quote \ gmgn-cli order quote \
@@ -467,7 +470,7 @@ Response fields are shared with `swap` — see [`swap` / `order get` Response Fi
## `gas-price` Usage ## `gas-price` Usage
Query recommended gas price tiers for any chain. Uses normal auth (API Key only — no private key required). Query recommended gas price tiers for any chain. API Key only — no signature or private key required.
```bash ```bash
gmgn-cli gas-price --chain eth gmgn-cli gas-price --chain eth
@@ -544,7 +547,7 @@ gmgn-cli order strategy create \
| `--quote-token` | Yes | all | Quote token contract address | | `--quote-token` | Yes | all | Quote token contract address |
| `--order-type` | Yes | all | Order type: `limit_order` | | `--order-type` | Yes | all | Order type: `limit_order` |
| `--sub-order-type` | Yes | all | Sub-order type: `buy_low` / `buy_high` / `stop_loss` / `take_profit` | | `--sub-order-type` | Yes | all | Sub-order type: `buy_low` / `buy_high` / `stop_loss` / `take_profit` |
| `--check-price` | Yes | all | Trigger check price | | `--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` | 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` | | `--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`) | | `--limit-price-mode` | No | all | `exact` / `slippage` (default: `slippage`) |
@@ -554,7 +557,7 @@ gmgn-cli order strategy create \
| `--auto-slippage` | No | all | Enable automatic slippage | | `--auto-slippage` | No | all | Enable automatic slippage |
| `--priority-fee` | No | `sol` | Priority fee in SOL (≥ 0.00001). **Required** for SOL. | | `--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. | | `--tip-fee` | No | `sol` / `bsc` | Tip fee (SOL ≥ 0.00001 / BSC ≥ 0.000001 BNB). **Required** for SOL. |
| `--auto-fee` | No | `eth` | Auto fee mode — delegates fee selection to the trading bot. | | `--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-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`. | | `--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-fee-per-gas` | No | `bsc` / `base` / `eth` | EIP-1559 max fee per gas. Clamped per chain minimums. |
@@ -600,11 +603,117 @@ gmgn-cli order strategy list --chain sol --group-tag STMix --base-token <token_a
### `order strategy list` Response Fields ### `order strategy list` Response Fields
| Field | Type | Description | | Field | Type | Description |
|-------|------|-------------| | ----------------- | ------ | ---- |
| `next_page_token` | string | Cursor for next page; empty when no more data | | `next_page_token` | string | Cursor for next page; empty when no more data |
| `total` | int | Total count (only returned when `--type open`) | | `total` | int | Total count (only returned when `--type open`) |
| `list` | array | Strategy order list | | `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 |
--- ---
@@ -632,10 +741,10 @@ gmgn-cli order strategy cancel \
## Notes ## 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 - After submitting a swap, use `order get` to poll for confirmation
- `--amount` is in the **smallest unit** (e.g., lamports for SOL) - `--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 - 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`.) - **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:** - **EIP-1559 minimum values per chain:**
@@ -680,7 +789,7 @@ For full token research before swapping, see [`docs/workflow-token-research.md`]
## Execution Guidelines ## 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. - **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` - 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` - Sell ("sell TOKEN for SOL", "sell 50% of TOKEN to USDC") → resolve currency to `--output-token`
+3 -3
View File
@@ -288,7 +288,7 @@ The response has five nested objects: `pool`, `dev`, `link`, `stat`, `wallet_tag
| `price.swaps_{window}` | Total swap count for the window | | `price.swaps_{window}` | Total swap count for the window |
| `price.hot_level` | Heat level integer | | `price.hot_level` | Heat level integer |
**`fee_distribution` Object** — Launchpad fee-sharing config (optional; present for `pump` / `bankr` tokens) **`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 | | Field | Description |
|-------|-------------| |-------|-------------|
@@ -724,8 +724,8 @@ Show top rows only. Highlight wallets tagged `kol`, `smart_degen`, or flagged `b
## Notes ## Notes
- **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. - **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 (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. - **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 normal auth (API Key only, no signature required) - All token commands use exist auth (API Key only, no signature required)
- Use `--raw` to get single-line JSON for further processing - 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 - `--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 - `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-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_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 ## Rate Limit Handling
@@ -311,7 +311,7 @@ To research any token surfaced by smart money activity, follow [`docs/workflow-t
## Notes ## 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 - `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 - 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 - `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
+41 -41
View File
@@ -2,8 +2,8 @@
* OpenApiClient GMGN OpenAPI external client * OpenApiClient GMGN OpenAPI external client
* *
* Auth modes: * Auth modes:
* Normal (market/token/portfolio): X-APIKEY + timestamp + client_id * Exist (market/token/portfolio): X-APIKEY + timestamp + client_id
* Critical (swap and order routes): normal auth + X-Signature (private key signature) * Signed (swap and order routes): X-APIKEY + timestamp + client_id + X-Signature (private key signature)
*/ */
import { buildAuthQuery, buildMessage, detectAlgorithm, sign } from "./signer.js"; import { buildAuthQuery, buildMessage, detectAlgorithm, sign } from "./signer.js";
@@ -204,29 +204,29 @@ export class OpenApiClient {
this.host = config.host.replace(/\/$/, ""); this.host = config.host.replace(/\/$/, "");
} }
// ---- Token endpoints (normal auth) ---- // ---- Token endpoints (exist auth) ----
async getTokenInfo(chain: string, address: string): Promise<unknown> { 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> { 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> { 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> { 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> { 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( async getTokenKline(
chain: string, chain: string,
@@ -238,21 +238,21 @@ export class OpenApiClient {
const query: Record<string, string | number> = { chain, address, resolution }; const query: Record<string, string | number> = { chain, address, resolution };
if (from != null) query["from"] = from; if (from != null) query["from"] = from;
if (to != null) query["to"] = to; 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( async getWalletHoldings(
chain: string, chain: string,
walletAddress: string, walletAddress: string,
extra: Record<string, string | number> = {} extra: Record<string, string | number> = {}
): Promise<unknown> { ): Promise<unknown> {
return this.normalRequest("GET", "/v1/user/wallet_holdings", { return this.authSignedRequest("GET", "/v1/user/wallet_holdings", {
chain, chain,
wallet_address: walletAddress, wallet_address: walletAddress,
...extra, ...extra,
}); }, null);
} }
async getWalletActivity( async getWalletActivity(
@@ -260,7 +260,7 @@ export class OpenApiClient {
walletAddress: string, walletAddress: string,
extra: Record<string, string | number | string[]> = {} extra: Record<string, string | number | string[]> = {}
): Promise<unknown> { ): Promise<unknown> {
return this.normalRequest("GET", "/v1/user/wallet_activity", { return this.authExistRequest("GET", "/v1/user/wallet_activity", {
chain, chain,
wallet_address: walletAddress, wallet_address: walletAddress,
...extra, ...extra,
@@ -268,7 +268,7 @@ export class OpenApiClient {
} }
async getWalletStats(chain: string, walletAddresses: string[], period = "7d"): Promise<unknown> { 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, chain,
wallet_address: walletAddresses, wallet_address: walletAddresses,
period, period,
@@ -280,54 +280,54 @@ export class OpenApiClient {
walletAddress: string, walletAddress: string,
tokenAddress: string tokenAddress: string
): Promise<unknown> { ): 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> { 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); 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( async getTrendingSwaps(
chain: string, chain: string,
interval: string, interval: string,
extra: Record<string, string | number | string[]> = {} extra: Record<string, string | number | string[]> = {}
): Promise<unknown> { ): 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> { 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> { 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> { 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> { async getKol(chain?: string, limit?: number): Promise<unknown> {
const query: Record<string, string | number> = {}; const query: Record<string, string | number> = {};
if (chain) query["chain"] = chain; if (chain) query["chain"] = chain;
if (limit != null) query["limit"] = limit; 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> { async getSmartMoney(chain?: string, limit?: number): Promise<unknown> {
const query: Record<string, string | number> = {}; const query: Record<string, string | number> = {};
if (chain) query["chain"] = chain; if (chain) query["chain"] = chain;
if (limit != null) query["limit"] = limit; 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> { 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( async quoteOrder(
@@ -339,54 +339,54 @@ export class OpenApiClient {
slippage: number slippage: number
): Promise<unknown> { ): Promise<unknown> {
const query = { chain, from_address, input_token, output_token, input_amount, slippage }; 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> { 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> { 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> { 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);
} }
async getGasPrice(chain: string): Promise<unknown> { async getGasPrice(chain: string): Promise<unknown> {
return this.normalRequest("GET", "/v1/trade/gas_price", { chain }); return this.authExistRequest("GET", "/v1/trade/gas_price", { chain });
} }
// ---- Strategy order endpoints (critical auth) ---- // ---- Strategy order endpoints (signed auth) ----
async createStrategyOrder(params: StrategyCreateParams): Promise<unknown> { 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> { 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> { 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 ---- // ---- Cooking endpoints ----
async getCookingStatistics(): Promise<unknown> { async getCookingStatistics(): Promise<unknown> {
return this.normalRequest("GET", "/v1/cooking/statistics", {}); return this.authExistRequest("GET", "/v1/cooking/statistics", {});
} }
async createToken(params: CreateTokenParams): Promise<unknown> { 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 ---- // ---- Internal methods ----
private async normalRequest( private async authExistRequest(
method: string, method: string,
subPath: string, subPath: string,
queryExtra: Record<string, string | number | string[]>, queryExtra: Record<string, string | number | string[]>,
@@ -412,14 +412,14 @@ export class OpenApiClient {
}, true); }, true);
} }
private async criticalRequest( private async authSignedRequest(
method: string, method: string,
subPath: string, subPath: string,
queryExtra: Record<string, string | number | string[]>, queryExtra: Record<string, string | number | string[]>,
body: unknown body: unknown
): Promise<unknown> { ): Promise<unknown> {
if (!this.privateKeyPem) { 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(() => { 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} * Format: {sub_path}:{sorted_query_string}:{request_body}:{timestamp}
* sorted_query_string: all query params (including timestamp, client_id) sorted alphabetically by key. * 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. * 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) const sortedQs = Object.keys(queryParams)
.sort() .sort()
.flatMap((k) => { .flatMap((k) => {
const ek = encodeURIComponent(k);
const v = queryParams[k]; const v = queryParams[k];
if (Array.isArray(v)) { 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("&"); .join("&");
return `${subPath}:${sortedQs}:${body}:${timestamp}`; return `${subPath}:${sortedQs}:${body}:${timestamp}`;
+1 -1
View File
@@ -9,7 +9,7 @@ export function registerCookingCommands(program: Command): void {
cooking cooking
.command("stats") .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") .option("--raw", "Output raw JSON")
.action(async (opts) => { .action(async (opts) => {
const client = new OpenApiClient(getConfig()); const client = new OpenApiClient(getConfig());
+4 -4
View File
@@ -41,7 +41,7 @@ export function registerSwapCommands(program: Command): void {
if (opts.percent != null) validatePercent(opts.percent); if (opts.percent != null) validatePercent(opts.percent);
const params: SwapParams = { const params: SwapParams = {
chain: opts.chain, chain: opts.chain,
from_address: opts.from, from_address: opts.chain === "sol" ? opts.from : opts.from.toLowerCase(),
input_token: opts.inputToken, input_token: opts.inputToken,
output_token: opts.outputToken, output_token: opts.outputToken,
input_amount: opts.percent != null ? (opts.amount ?? "0") : opts.amount, input_amount: opts.percent != null ? (opts.amount ?? "0") : opts.amount,
@@ -109,7 +109,7 @@ export function registerSwapCommands(program: Command): void {
} }
const params: MultiSwapParams = { const params: MultiSwapParams = {
chain: opts.chain, chain: opts.chain,
accounts, accounts: opts.chain === "sol" ? accounts : accounts.map((a: string) => a.toLowerCase()),
input_token: opts.inputToken, input_token: opts.inputToken,
output_token: opts.outputToken, output_token: opts.outputToken,
}; };
@@ -149,7 +149,7 @@ export function registerSwapCommands(program: Command): void {
order order
.command("quote") .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("--chain <chain>", "Chain: sol / bsc / base / eth (requires GMGN_PRIVATE_KEY)")
.requiredOption("--from <address>", "Wallet address (must match API Key binding)") .requiredOption("--from <address>", "Wallet address (must match API Key binding)")
.requiredOption("--input-token <address>", "Input token contract address") .requiredOption("--input-token <address>", "Input token contract address")
@@ -185,7 +185,7 @@ export function registerSwapCommands(program: Command): void {
program program
.command("gas-price") .command("gas-price")
.description("Query recommended gas price tiers for any chain (normal auth; eth / bsc / base / sol)") .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") .requiredOption("--chain <chain>", "Chain: eth / bsc / base / sol")
.option("--raw", "Output raw JSON") .option("--raw", "Output raw JSON")
.action(async (opts) => { .action(async (opts) => {