mirror of
https://github.com/GMGNAI/gmgn-skills.git
synced 2026-08-01 10:47:44 +00:00
Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f7a2d075e3 | |||
| 8e413cef25 | |||
| 7959eaaed3 | |||
| 8c27e42aff | |||
| ae7729f26c | |||
| 759ab74709 | |||
| dd1cb9d2e5 | |||
| b12a2294b7 | |||
| cb21fff38c | |||
| 138d571f8b | |||
| 2e71f283f0 | |||
| 56086fa692 | |||
| 221bf120a8 | |||
| 09351a9205 | |||
| 91128a887e | |||
| d2a91ef7d1 | |||
| 5ec76cb465 | |||
| 40f75e0b64 | |||
| 3951d9963e | |||
| 5547e9fd30 | |||
| 3ca831a976 | |||
| aa27e1560a |
@@ -10,7 +10,7 @@ English | [简体中文](Readme.zh.md)
|
||||
|
||||
## GMGN Agent Skills
|
||||
|
||||
With GMGN Agent Skills, you can use AI agents to query real-time trending token rankings across multiple chains, token fundamentals, social media signals, live trading activity, new tokens in Trenches, top holders, top traders, smart money positions, KOL holdings, insider wallets, bundled wallet exposure, and other professional on-chain analytics. It also supports market orders, limit orders, advanced take-profit/stop-loss strategy orders, and wallet management — including real-time holdings, recent P&L, and transaction history — all through natural language.
|
||||
With GMGN Agent Skills, you can use AI agents to query real-time trending token rankings across multiple chains, token fundamentals, social media signals, live trading activity, new tokens in Trenches, top holders, top traders, smart money positions, KOL holdings, insider wallets, bundled wallet exposure, and other professional on-chain analytics. It also supports market orders, limit orders, advanced take-profit/stop-loss strategy orders, one-command cooking orders (buy + condition orders in a single flow), and wallet management — including real-time holdings, recent P&L, and transaction history — all through natural language.
|
||||
|
||||
---
|
||||
|
||||
@@ -89,7 +89,8 @@ No more scraping web pages or getting blocked by Cloudflare. Query all the profe
|
||||
| [`/gmgn-market`](skills/gmgn-market/SKILL.md) | K-line market data, trending tokens | [SKILL.md](skills/gmgn-market/SKILL.md) |
|
||||
| [`/gmgn-portfolio`](skills/gmgn-portfolio/SKILL.md) | Wallet holdings, activity, stats | [SKILL.md](skills/gmgn-portfolio/SKILL.md) |
|
||||
| [`/gmgn-track`](skills/gmgn-track/SKILL.md) | Follow-wallet trades, KOL trades, Smart Money trades | [SKILL.md](skills/gmgn-track/SKILL.md) |
|
||||
| [`/gmgn-swap`](skills/gmgn-swap/SKILL.md) | Swap submission + order query | [SKILL.md](skills/gmgn-swap/SKILL.md) |
|
||||
| [`/gmgn-swap`](skills/gmgn-swap/SKILL.md) | Swap submission + limit orders + strategy orders + order query | [SKILL.md](skills/gmgn-swap/SKILL.md) |
|
||||
| [`/gmgn-cooking`](skills/gmgn-cooking/SKILL.md) | One-command cooking orders (buy + take-profit/stop-loss in a single flow) | [SKILL.md](skills/gmgn-cooking/SKILL.md) |
|
||||
|
||||
> For detailed CLI commands, parameters, and recommended values, see the [Wiki documentation](https://github.com/GMGNAI/gmgn-skills/wiki).
|
||||
|
||||
@@ -99,7 +100,7 @@ No more scraping web pages or getting blocked by Cloudflare. Query all the profe
|
||||
|
||||
Ready to install? [Jump to Installation →](#1-installation)
|
||||
|
||||
Already installed? [Upgrade Skills and CLI →](#9-upgrade-skills-and-cli)
|
||||
Already installed? [Upgrade Skills and CLI →](#10-upgrade-skills-and-cli)
|
||||
|
||||
> **Tip:** If your AI agent tries to open or scrape the gmgn.ai website instead of using the CLI, add this to your prompt: `Use gmgn-cli commands. Do not call gmgn.ai web endpoints directly.`
|
||||
|
||||
@@ -176,9 +177,10 @@ If output appears without error, setup is complete.
|
||||
|
||||
## Upgrade (AI Agent)
|
||||
|
||||
> Run this command to upgrade both the CLI and Skills to the latest version.
|
||||
> Run these two commands to upgrade both the CLI and Skills to the latest version.
|
||||
|
||||
```bash
|
||||
npm install -g gmgn-cli
|
||||
npx skills add GMGNAI/gmgn-skills
|
||||
```
|
||||
|
||||
@@ -188,7 +190,7 @@ Check the installed version after upgrading:
|
||||
gmgn-cli --version
|
||||
```
|
||||
|
||||
> For the full upgrade reference, see [Section 9 — Upgrade Skills and CLI](#9-upgrade-skills-and-cli).
|
||||
> For the full upgrade reference, see [Section 10 — Upgrade Skills and CLI](#10-upgrade-skills-and-cli).
|
||||
|
||||
---
|
||||
|
||||
@@ -340,7 +342,7 @@ Skills are automatically discovered via the `.cursor-plugin/` configuration.
|
||||
```bash
|
||||
echo "$(npm root -g)/gmgn-skills/skills"
|
||||
```
|
||||
3. Restart Cline — `/gmgn-token`, `/gmgn-market`, `/gmgn-portfolio`, `/gmgn-track`, `/gmgn-swap` will be available
|
||||
3. Restart Cline — `/gmgn-token`, `/gmgn-market`, `/gmgn-portfolio`, `/gmgn-track`, `/gmgn-swap`, `/gmgn-cooking` will be available
|
||||
|
||||
#### Codex CLI
|
||||
|
||||
@@ -373,12 +375,23 @@ Natural language prompts you can send to any AI assistant with gmgn-cli skills i
|
||||
```
|
||||
buy 0.1 SOL of <token_address>
|
||||
sell 50% of <token_address> on BSC
|
||||
sell 30% of my <token_address> position
|
||||
get a quote: how much <token_address> can I get for 1 SOL?
|
||||
check order status <order_id>
|
||||
is <token_address> safe to buy on solana?
|
||||
show top holders of <token_address>
|
||||
show smart money holdings of <token_address>, sorted by buy volume
|
||||
show recent KOL trades for <token_address>
|
||||
show my wallet holdings on SOL
|
||||
query token details for 0x1234...
|
||||
show 24h K-line and volume for <token_address>
|
||||
show trading stats for wallet <wallet_address> on BSC
|
||||
show recent trades for wallet <wallet_address>
|
||||
which wallets are linked to my API key, and what are their balances
|
||||
show the latest smart money trades on SOL
|
||||
show what KOLs are buying on SOL
|
||||
show newly launched tokens on Solana
|
||||
show Solana 1-minute trending tokens
|
||||
```
|
||||
|
||||
### Typical Workflows
|
||||
@@ -428,26 +441,26 @@ Full parameter reference: [docs/cli-usage.md](docs/cli-usage.md). All commands s
|
||||
### Token
|
||||
|
||||
```bash
|
||||
npx gmgn-cli token info --chain sol --address <addr>
|
||||
gmgn-cli token info --chain sol --address <addr>
|
||||
```
|
||||
|
||||
### Market
|
||||
|
||||
```bash
|
||||
npx gmgn-cli market trending \
|
||||
gmgn-cli market trending \
|
||||
--chain sol \
|
||||
--interval 1h \
|
||||
--order-by volume --limit 20 \
|
||||
--filter not_risk --filter not_honeypot
|
||||
|
||||
npx gmgn-cli market trenches \
|
||||
gmgn-cli market trenches \
|
||||
--chain sol \
|
||||
--type new_creation --type near_completion --type completed \
|
||||
--launchpad-platform Pump.fun --launchpad-platform pump_mayhem --launchpad-platform letsbonk \
|
||||
--limit 80
|
||||
|
||||
# With server-side filters: safe preset + require smart money + sort by smart degen count
|
||||
npx gmgn-cli market trenches \
|
||||
gmgn-cli market trenches \
|
||||
--chain sol --type new_creation \
|
||||
--filter-preset safe --min-smart-degen-count 1 --sort-by smart_degen_count
|
||||
```
|
||||
@@ -455,30 +468,89 @@ npx gmgn-cli market trenches \
|
||||
### Portfolio
|
||||
|
||||
```bash
|
||||
npx gmgn-cli portfolio holdings --chain sol --wallet <addr>
|
||||
# Holdings
|
||||
gmgn-cli portfolio holdings --chain sol --wallet <addr>
|
||||
|
||||
# Activity
|
||||
gmgn-cli portfolio activity --chain sol --wallet <addr>
|
||||
|
||||
# Stats (supports multiple wallets)
|
||||
gmgn-cli portfolio stats --chain sol --wallet <addr1> --wallet <addr2>
|
||||
|
||||
# Wallets and balances linked to API key
|
||||
gmgn-cli portfolio info
|
||||
|
||||
# Single token balance
|
||||
gmgn-cli portfolio token-balance --chain sol --wallet <addr> --token <token_addr>
|
||||
```
|
||||
|
||||
### Track
|
||||
|
||||
```bash
|
||||
# Follow-wallet trade records (requires GMGN_PRIVATE_KEY)
|
||||
npx gmgn-cli track follow-wallet --chain sol
|
||||
npx gmgn-cli track follow-wallet --chain sol --wallet <wallet_address> --side buy
|
||||
gmgn-cli track follow-wallet --chain sol
|
||||
gmgn-cli track follow-wallet --chain sol --wallet <wallet_address> --side buy
|
||||
|
||||
# KOL trade records
|
||||
npx gmgn-cli track kol --limit 100 --raw
|
||||
npx gmgn-cli track kol --chain sol --side buy --limit 50 --raw
|
||||
gmgn-cli track kol --limit 100 --raw
|
||||
gmgn-cli track kol --chain sol --side buy --limit 50 --raw
|
||||
|
||||
# Smart Money trade records
|
||||
npx gmgn-cli track smartmoney --limit 100 --raw
|
||||
npx gmgn-cli track smartmoney --chain sol --side sell --limit 50 --raw
|
||||
gmgn-cli track smartmoney --limit 100 --raw
|
||||
gmgn-cli track smartmoney --chain sol --side sell --limit 50 --raw
|
||||
```
|
||||
|
||||
### Limit Orders (requires private key)
|
||||
|
||||
```bash
|
||||
# Create a take-profit order
|
||||
gmgn-cli order strategy create \
|
||||
--chain sol \
|
||||
--from <wallet_address> \
|
||||
--base-token <token_address> \
|
||||
--quote-token <sol_address> \
|
||||
--sub-order-type take_profit \
|
||||
--check-price 0.002 \
|
||||
--amount-in-percent 100 \
|
||||
--slippage 0.01
|
||||
|
||||
# Create a stop-loss order
|
||||
gmgn-cli order strategy create \
|
||||
--chain sol \
|
||||
--from <wallet_address> \
|
||||
--base-token <token_address> \
|
||||
--quote-token <sol_address> \
|
||||
--sub-order-type stop_loss \
|
||||
--check-price 0.0005 \
|
||||
--amount-in-percent 100 \
|
||||
--slippage 0.01
|
||||
|
||||
# List open strategy orders
|
||||
gmgn-cli order strategy list --chain sol
|
||||
|
||||
# Cancel a strategy order
|
||||
gmgn-cli order strategy cancel --chain sol --from <wallet_address> --order-id <order_id>
|
||||
```
|
||||
|
||||
### Cooking (requires private key)
|
||||
|
||||
```bash
|
||||
# Buy token and automatically attach take-profit + stop-loss condition orders
|
||||
gmgn-cli cooking \
|
||||
--chain sol \
|
||||
--from <wallet_address> \
|
||||
--input-token So11111111111111111111111111111111111111112 \
|
||||
--output-token <token_address> \
|
||||
--amount 1000000000 \
|
||||
--slippage 0.1 \
|
||||
--condition-orders '[{"order_type":"profit_stop","side":"sell","price_scale":"200","sell_ratio":"100"},{"order_type":"loss_stop","side":"sell","price_scale":"50","sell_ratio":"100"}]'
|
||||
```
|
||||
|
||||
### Swap (requires private key)
|
||||
|
||||
```bash
|
||||
# Submit swap with fixed slippage
|
||||
npx gmgn-cli swap \
|
||||
gmgn-cli swap \
|
||||
--chain sol \
|
||||
--from <wallet-address> \
|
||||
--input-token <input-token-addr> \
|
||||
@@ -487,7 +559,7 @@ npx gmgn-cli swap \
|
||||
--slippage 0.01
|
||||
|
||||
# Submit swap with automatic slippage
|
||||
npx gmgn-cli swap \
|
||||
gmgn-cli swap \
|
||||
--chain sol \
|
||||
--from <wallet-address> \
|
||||
--input-token <input-token-addr> \
|
||||
@@ -495,11 +567,29 @@ npx gmgn-cli swap \
|
||||
--amount 1000000 \
|
||||
--auto-slippage
|
||||
|
||||
# Sell by position percentage (e.g. sell 50%)
|
||||
gmgn-cli swap \
|
||||
--chain sol \
|
||||
--from <wallet-address> \
|
||||
--input-token <token-addr> \
|
||||
--output-token <usdc-addr> \
|
||||
--percent 50 \
|
||||
--auto-slippage
|
||||
|
||||
# Get quote (no transaction submitted)
|
||||
gmgn-cli order quote \
|
||||
--chain sol \
|
||||
--from <wallet-address> \
|
||||
--input-token <input-token-addr> \
|
||||
--output-token <output-token-addr> \
|
||||
--amount 1000000 \
|
||||
--slippage 0.01
|
||||
|
||||
# Query order
|
||||
npx gmgn-cli order get --chain sol --order-id <order-id>
|
||||
gmgn-cli order get --chain sol --order-id <order-id>
|
||||
```
|
||||
|
||||
## 8. Supported Chains
|
||||
## 9. Supported Chains
|
||||
|
||||
| Commands | Chains | Chain Currencies |
|
||||
|----------|--------|-----------------|
|
||||
@@ -508,10 +598,13 @@ npx gmgn-cli order get --chain sol --order-id <order-id>
|
||||
|
||||
---
|
||||
|
||||
## 9. Upgrade Skills and CLI
|
||||
## 10. Upgrade Skills and CLI
|
||||
|
||||
```bash
|
||||
# Upgrade CLI and Skills
|
||||
# Upgrade CLI
|
||||
npm install -g gmgn-cli
|
||||
|
||||
# Upgrade Skills
|
||||
npx skills add GMGNAI/gmgn-skills
|
||||
|
||||
# Check current version
|
||||
@@ -522,7 +615,7 @@ gmgn-cli --version
|
||||
|
||||
---
|
||||
|
||||
## 10. Security & Disclaimer (Read Before Use)
|
||||
## 11. Security & Disclaimer (Read Before Use)
|
||||
|
||||
This tool can be invoked by an AI Agent to submit real on-chain transactions automatically. It carries inherent risks including model hallucination, uncontrolled execution, and prompt injection. Once authorized, the AI Agent will submit transactions on behalf of your linked wallet address — **on-chain transactions are irreversible once confirmed** and may result in financial loss. Use with caution.
|
||||
|
||||
|
||||
+58
-29
@@ -10,7 +10,7 @@
|
||||
|
||||
## GMGN Agent Skills
|
||||
|
||||
使用 GMGN Agent Skills,你可以通过 AI Agent 实时查询多个链上热门代币排行榜,代币基础信息,社交媒体信息,实时交易动态,实时战壕新币,报持仓大户(Top Holder),交易大户(Top Trader),聪明钱持仓占比,KOL持仓占比,老鼠仓持仓,捆绑持仓占比,等代币专业数据分析数据,以及支持代币市价单交易、限价单交易、高级止盈止损策略单交易,以及钱包资产管理相关功能,例如查询钱包实时持仓、钱包最近盈亏、钱包交易动态等,全部通过自然语言与 AI Agent 交互即可完成。
|
||||
使用 GMGN Agent Skills,你可以通过 AI Agent 实时查询多个链上热门代币排行榜,代币基础信息,社交媒体信息,实时交易动态,实时战壕新币,报持仓大户(Top Holder),交易大户(Top Trader),聪明钱持仓占比,KOL持仓占比,老鼠仓持仓,捆绑持仓占比,等代币专业数据分析数据,以及支持代币市价单交易、限价单交易、高级止盈止损策略单交易、一键 Cooking 策略单(买入 + 条件单一体化),以及钱包资产管理相关功能,例如查询钱包实时持仓、钱包最近盈亏、钱包交易动态等,全部通过自然语言与 AI Agent 交互即可完成。
|
||||
|
||||
---
|
||||
|
||||
@@ -92,7 +92,8 @@ SOL / BSC / Base 多链数据每次查询均为实时,支持多参数个性化
|
||||
| [`/gmgn-market`](skills/gmgn-market/SKILL.md) | K 线行情数据、热门代币 | [SKILL.md](skills/gmgn-market/SKILL.md) |
|
||||
| [`/gmgn-portfolio`](skills/gmgn-portfolio/SKILL.md) | 钱包持仓、活动、统计 | [SKILL.md](skills/gmgn-portfolio/SKILL.md) |
|
||||
| [`/gmgn-track`](skills/gmgn-track/SKILL.md) | 追踪关注钱包交易动态、KOL 交易动态、聪明钱交易动态 | [SKILL.md](skills/gmgn-track/SKILL.md) |
|
||||
| [`/gmgn-swap`](skills/gmgn-swap/SKILL.md) | 兑换提交 + 订单查询 | [SKILL.md](skills/gmgn-swap/SKILL.md) |
|
||||
| [`/gmgn-swap`](skills/gmgn-swap/SKILL.md) | 兑换提交 + 限价单 + 策略单 + 订单查询 | [SKILL.md](skills/gmgn-swap/SKILL.md) |
|
||||
| [`/gmgn-cooking`](skills/gmgn-cooking/SKILL.md) | 一键 Cooking 策略单(买入 + 止盈止损条件单一体化) | [SKILL.md](skills/gmgn-cooking/SKILL.md) |
|
||||
|
||||
> 如需查看详细的 CLI 接口说明、传参格式和推荐值,请参阅 [Wiki 文档](https://github.com/GMGNAI/gmgn-skills/wiki/Home-Chinese)。
|
||||
|
||||
@@ -102,7 +103,7 @@ SOL / BSC / Base 多链数据每次查询均为实时,支持多参数个性化
|
||||
|
||||
已准备好?[点击这里开始安装 Skills →](#开始安装-skills)
|
||||
|
||||
已安装?[升级 Skills 和 CLI →](#9-升级-skills-和-cli)
|
||||
已安装?[升级 Skills 和 CLI →](#10-升级-skills-和-cli)
|
||||
|
||||
> **提示:** 如果你的 AI Agent 尝试直接打开 gmgn.ai 网站而不是使用 CLI,请在提示词中加上:
|
||||
> ```
|
||||
@@ -182,9 +183,10 @@ gmgn-cli market trending --chain sol --interval 1h --limit 3
|
||||
|
||||
## AI Agent 升级
|
||||
|
||||
> 运行以下命令,将 CLI 和 Skills 同时升级到最新版本。
|
||||
> 运行以下两条命令,将 CLI 和 Skills 同时升级到最新版本。
|
||||
|
||||
```bash
|
||||
npm install -g gmgn-cli
|
||||
npx skills add GMGNAI/gmgn-skills
|
||||
```
|
||||
|
||||
@@ -194,7 +196,7 @@ npx skills add GMGNAI/gmgn-skills
|
||||
gmgn-cli --version
|
||||
```
|
||||
|
||||
> 完整升级说明请参阅[第 9 节 — 升级 Skills 和 CLI](#9-升级-skills-和-cli)。
|
||||
> 完整升级说明请参阅[第 10 节 — 升级 Skills 和 CLI](#10-升级-skills-和-cli)。
|
||||
|
||||
---
|
||||
|
||||
@@ -223,15 +225,6 @@ gmgn-cli --version
|
||||
|
||||
---
|
||||
|
||||
## 开始安装 Skills
|
||||
|
||||
安装前,请先在 **https://gmgn.ai/ai** 创建 API Key,用于:
|
||||
|
||||
1. 读取数据:代币、榜单、K 线、特色数据指标
|
||||
2. 提交交易:市价立即交易、创建限价单、策略单等
|
||||
|
||||
---
|
||||
|
||||
## 1. 安装
|
||||
|
||||
选择以下任意一种方式
|
||||
@@ -260,20 +253,10 @@ node dist/index.js <command> [options]
|
||||
|
||||
## 2. 验证连通性
|
||||
|
||||
### 方式一:通过 AI Agent 验证
|
||||
|
||||
发送以下提示词给你的 AI Agent:
|
||||
|
||||
```
|
||||
执行这个cli命令:GMGN_API_KEY=gmgn_solbscbaseethmonadtron npx gmgn-cli market trending --chain sol --interval 1h --limit 3
|
||||
```
|
||||
|
||||
### 方式二:通过 CLI 验证
|
||||
|
||||
使用公共 API Key 测试,无需注册:
|
||||
|
||||
```bash
|
||||
GMGN_API_KEY=gmgn_solbscbaseethmonadtron npx gmgn-cli market trending --chain sol --interval 1h --limit 3
|
||||
GMGN_API_KEY=gmgn_solbscbaseethmonadtron gmgn-cli market trending --chain sol --interval 1h --limit 3
|
||||
```
|
||||
|
||||
看到 JSON 输出即表示 CLI 正常工作。公共 Key 支持所有只读接口(token / market / portfolio),公共 Key 仅用于测试,正式使用任何接口均需申请个人 API Key(见第 3 步)。
|
||||
@@ -363,7 +346,7 @@ cp .env.example .env
|
||||
```bash
|
||||
echo "$(npm root -g)/gmgn-skills/skills"
|
||||
```
|
||||
3. 重启 Cline — `/gmgn-token`、`/gmgn-market`、`/gmgn-portfolio`、`/gmgn-track`、`/gmgn-swap` 即可使用
|
||||
3. 重启 Cline — `/gmgn-token`、`/gmgn-market`、`/gmgn-portfolio`、`/gmgn-track`、`/gmgn-swap`、`/gmgn-cooking` 即可使用
|
||||
|
||||
#### Codex CLI
|
||||
|
||||
@@ -549,6 +532,52 @@ gmgn-cli track smartmoney --limit 100 --raw
|
||||
gmgn-cli track smartmoney --chain sol --side sell --limit 50 --raw
|
||||
```
|
||||
|
||||
### 限价单(需要私钥)
|
||||
|
||||
```bash
|
||||
# 创建止盈单
|
||||
gmgn-cli order strategy create \
|
||||
--chain sol \
|
||||
--from <wallet_address> \
|
||||
--base-token <token_address> \
|
||||
--quote-token <sol_address> \
|
||||
--sub-order-type take_profit \
|
||||
--check-price 0.002 \
|
||||
--amount-in-percent 100 \
|
||||
--slippage 0.01
|
||||
|
||||
# 创建止损单
|
||||
gmgn-cli order strategy create \
|
||||
--chain sol \
|
||||
--from <wallet_address> \
|
||||
--base-token <token_address> \
|
||||
--quote-token <sol_address> \
|
||||
--sub-order-type stop_loss \
|
||||
--check-price 0.0005 \
|
||||
--amount-in-percent 100 \
|
||||
--slippage 0.01
|
||||
|
||||
# 查看当前挂单
|
||||
gmgn-cli order strategy list --chain sol
|
||||
|
||||
# 撤销策略单
|
||||
gmgn-cli order strategy cancel --chain sol --from <wallet_address> --order-id <order_id>
|
||||
```
|
||||
|
||||
### Cooking 一键策略单(需要私钥)
|
||||
|
||||
```bash
|
||||
# 买入代币,同时自动挂止盈 + 止损条件单
|
||||
gmgn-cli cooking \
|
||||
--chain sol \
|
||||
--from <wallet_address> \
|
||||
--input-token So11111111111111111111111111111111111111112 \
|
||||
--output-token <token_address> \
|
||||
--amount 1000000000 \
|
||||
--slippage 0.1 \
|
||||
--condition-orders '[{"order_type":"profit_stop","side":"sell","price_scale":"200","sell_ratio":"100"},{"order_type":"loss_stop","side":"sell","price_scale":"50","sell_ratio":"100"}]'
|
||||
```
|
||||
|
||||
### Swap(需要私钥)
|
||||
|
||||
```bash
|
||||
@@ -592,7 +621,7 @@ gmgn-cli order quote \
|
||||
gmgn-cli order get --chain sol --order-id <order-id>
|
||||
```
|
||||
|
||||
## 8. 支持的链
|
||||
## 9. 支持的链
|
||||
|
||||
| 接口类型 | 支持的链 | 链原生货币 |
|
||||
|----------|----------|-----------|
|
||||
@@ -601,7 +630,7 @@ gmgn-cli order get --chain sol --order-id <order-id>
|
||||
|
||||
---
|
||||
|
||||
## 9. 升级 Skills 和 CLI
|
||||
## 10. 升级 Skills 和 CLI
|
||||
|
||||
将 `gmgn-cli` 和 Skills 升级到最新版本:
|
||||
|
||||
@@ -633,7 +662,7 @@ gmgn-cli --version
|
||||
|
||||
---
|
||||
|
||||
## 10. 安全与免责(使用前必读)
|
||||
## 11. 安全与免责(使用前必读)
|
||||
|
||||
本工具可供 AI Agent 调用以自动执行链上交易,存在模型幻觉、执行不可控、提示词注入等固有风险。AI Agent 在获得授权后,将以您绑定的钱包地址提交真实的链上交易,**交易一经上链即不可撤销**,可能导致资金损失,请您谨慎使用。
|
||||
|
||||
|
||||
@@ -400,6 +400,8 @@ npx gmgn-cli swap \
|
||||
[--gas-price <gwei>] \
|
||||
[--max-fee-per-gas <amount>] \
|
||||
[--max-priority-fee-per-gas <amount>] \
|
||||
[--condition-orders <json>] \
|
||||
[--sell-ratio-type <buy_amount|hold_amount>] \
|
||||
[--raw]
|
||||
```
|
||||
|
||||
@@ -421,6 +423,16 @@ npx gmgn-cli swap \
|
||||
| `--gas-price` | No | Gas price in gwei (BSC ≥ 0.05 gwei / BASE/ETH ≥ 0.01 gwei) |
|
||||
| `--max-fee-per-gas` | No | EIP-1559 max fee per gas (Base/ETH only) |
|
||||
| `--max-priority-fee-per-gas` | No | EIP-1559 max priority fee per gas (Base/ETH only) |
|
||||
| `--condition-orders` | No | JSON array of take-profit/stop-loss conditions attached after a successful swap (see example below); only `profit_stop` and `loss_stop` are supported |
|
||||
| `--sell-ratio-type` | No | Sell ratio base for `--condition-orders`: `buy_amount` (default) / `hold_amount` |
|
||||
|
||||
**`--condition-orders` example** (100% sell at 2× price, 100% sell at 50% price):
|
||||
|
||||
```json
|
||||
[{"order_type":"profit_stop","side":"sell","price_scale":"100","sell_ratio":"100"},{"order_type":"loss_stop","side":"sell","price_scale":"50","sell_ratio":"100"}]
|
||||
```
|
||||
|
||||
> Strategy creation is **best-effort**: if the swap succeeds but strategy creation fails, the swap result is still returned (with `strategy_order_id` absent). Only `order_type`, `side`, `price_scale`, and `sell_ratio` are accepted per condition — extra fields cause a 400 error.
|
||||
|
||||
**Response fields (data):**
|
||||
|
||||
@@ -439,6 +451,7 @@ npx gmgn-cli swap \
|
||||
| `output_token` | string | Output token contract address |
|
||||
| `filled_input_amount` | string | Actual input consumed (smallest unit); empty if not filled |
|
||||
| `filled_output_amount` | string | Actual output received (smallest unit); empty if not filled |
|
||||
| `strategy_order_id` | string | Strategy order ID; only present when `--condition-orders` was passed and strategy creation succeeded |
|
||||
|
||||
---
|
||||
|
||||
@@ -457,6 +470,197 @@ npx gmgn-cli order get --chain <chain> --order-id <order_id> [--raw]
|
||||
|
||||
**Response fields (data):** Same structure as the `swap` response above.
|
||||
|
||||
## order strategy create
|
||||
|
||||
Create a limit/strategy order. **Requires `GMGN_PRIVATE_KEY` configured in `.env`.**
|
||||
|
||||
```bash
|
||||
gmgn-cli order strategy create \
|
||||
--chain <chain> \
|
||||
--from <wallet_address> \
|
||||
--base-token <base_token_address> \
|
||||
--quote-token <quote_token_address> \
|
||||
--order-type <limit_order> \
|
||||
--sub-order-type <buy_low|buy_high|stop_loss|take_profit> \
|
||||
--check-price <price> \
|
||||
[--amount-in <amount> | --amount-in-percent <pct>] \
|
||||
[--slippage <n> | --auto-slippage] \
|
||||
[--limit-price-mode <exact|slippage>] \
|
||||
[--expire-in <seconds>] \
|
||||
[--sell-ratio-type <buy_amount|hold_amount>] \
|
||||
[--priority-fee <sol>] \
|
||||
[--tip-fee <amount>] \
|
||||
[--gas-price <gwei>] \
|
||||
[--anti-mev] \
|
||||
[--raw]
|
||||
```
|
||||
|
||||
| Option | Required | Description |
|
||||
|--------|----------|-------------|
|
||||
| `--chain` | Yes | `sol` / `bsc` / `base` |
|
||||
| `--from` | Yes | Wallet address (must match API Key binding) |
|
||||
| `--base-token` | Yes | Base token contract address |
|
||||
| `--quote-token` | Yes | Quote token contract address |
|
||||
| `--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); required unless `--amount-in-percent` is used |
|
||||
| `--amount-in-percent` | No* | Input as percentage (e.g. `50` = 50%); required unless `--amount-in` is used |
|
||||
| `--limit-price-mode` | No | `exact` / `slippage` (default: `slippage`) |
|
||||
| `--expire-in` | No | Order expiry in seconds |
|
||||
| `--sell-ratio-type` | No | `buy_amount` (default) / `hold_amount` |
|
||||
| `--slippage` | No | Slippage tolerance, e.g. `0.01` = 1% |
|
||||
| `--auto-slippage` | No | Enable automatic slippage |
|
||||
| `--priority-fee` | No | Priority fee in SOL (**required for SOL chain**) |
|
||||
| `--tip-fee` | No | Tip fee (**required for SOL chain**) |
|
||||
| `--gas-price` | No | Gas price in gwei (**required for BSC**; ≥ 0.05 / BASE/ETH ≥ 0.01) |
|
||||
| `--anti-mev` | No | Enable anti-MEV protection |
|
||||
|
||||
> **Chain-specific fee requirements:**
|
||||
> - **SOL:** `--priority-fee` and `--tip-fee` are both **required** (returns 400 if missing)
|
||||
> - **BSC:** `--gas-price` is **required** (returns 400 if missing)
|
||||
> - **ETH/BASE:** no required fee fields
|
||||
|
||||
**Response fields (data):**
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `order_id` | string | Created strategy order ID |
|
||||
| `is_update` | bool | `true` if an existing order was updated |
|
||||
|
||||
---
|
||||
|
||||
## order strategy list
|
||||
|
||||
List strategy orders. Uses normal auth.
|
||||
|
||||
```bash
|
||||
gmgn-cli order strategy list --chain <chain> [--type <open|history>] [--from <address>] [--group-tag <tag>] [--base-token <address>] [--page-token <token>] [--limit <n>] [--raw]
|
||||
```
|
||||
|
||||
| Option | Required | Description |
|
||||
|--------|----------|-------------|
|
||||
| `--chain` | Yes | `sol` / `bsc` / `base` |
|
||||
| `--type` | No | `open` (default) / `history` |
|
||||
| `--from` | No | Filter by wallet address |
|
||||
| `--group-tag` | No | Filter by group: `LimitOrder` / `STMix` |
|
||||
| `--base-token` | No | Filter by token address |
|
||||
| `--page-token` | No | Pagination cursor from previous response |
|
||||
| `--limit` | No | Results per page |
|
||||
|
||||
**Response fields (data):**
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `next_page_token` | string | Cursor for next page; empty when no more data |
|
||||
| `total` | int | Total count (only when `--type open`) |
|
||||
| `list` | array | Strategy order list |
|
||||
|
||||
---
|
||||
|
||||
## order strategy cancel
|
||||
|
||||
Cancel a strategy order. **Requires `GMGN_PRIVATE_KEY` configured in `.env`.**
|
||||
|
||||
```bash
|
||||
gmgn-cli order strategy cancel --chain <chain> --from <wallet_address> --order-id <id> [--order-type <type>] [--close-sell-model <model>] [--raw]
|
||||
```
|
||||
|
||||
| Option | Required | Description |
|
||||
|--------|----------|-------------|
|
||||
| `--chain` | Yes | `sol` / `bsc` / `base` |
|
||||
| `--from` | Yes | Wallet address (must match API Key binding) |
|
||||
| `--order-id` | Yes | Order ID to cancel |
|
||||
| `--order-type` | No | Order type: `limit_order` / `smart_trade` |
|
||||
| `--close-sell-model` | No | Sell model when closing |
|
||||
|
||||
---
|
||||
|
||||
## cooking stats
|
||||
|
||||
Get token creation statistics grouped by launchpad.
|
||||
|
||||
```bash
|
||||
gmgn-cli cooking stats [--raw]
|
||||
```
|
||||
|
||||
No additional options required. Returns an array of `{ launchpad, token_count }` entries.
|
||||
|
||||
---
|
||||
|
||||
## cooking create
|
||||
|
||||
Create a token on a launchpad platform. **Requires `GMGN_PRIVATE_KEY` configured in `.env`.**
|
||||
|
||||
```bash
|
||||
gmgn-cli cooking create \
|
||||
--chain <chain> \
|
||||
--dex <dex> \
|
||||
--from <wallet_address> \
|
||||
--name <name> \
|
||||
--symbol <symbol> \
|
||||
--buy-amt <amount> \
|
||||
[--image <base64> | --image-url <url>] \
|
||||
[--slippage <n> | --auto-slippage] \
|
||||
[--website <url>] [--twitter <url>] [--telegram <url>] \
|
||||
[--priority-fee <sol>] [--tip-fee <amount>] [--gas-price <amount>] \
|
||||
[--anti-mev] \
|
||||
[--raw]
|
||||
```
|
||||
|
||||
| Option | Required | Description |
|
||||
|--------|----------|-------------|
|
||||
| `--chain` | Yes | `sol` / `bsc` / `base` / `eth` / `ton` |
|
||||
| `--dex` | Yes | Launchpad: `pump` / `raydium` / `pancakeswap` / `flap` / `fourmeme` / `bonk` / `bags` / ... |
|
||||
| `--from` | Yes | Wallet address (must match API Key binding) |
|
||||
| `--name` | Yes | Token name |
|
||||
| `--symbol` | Yes | Token symbol |
|
||||
| `--buy-amt` | Yes | Initial buy amount in native token (e.g. `0.01` SOL) |
|
||||
| `--image` | No* | Token logo as base64-encoded data (max 2MB decoded); required unless `--image-url` is used |
|
||||
| `--image-url` | No* | Token logo URL; required unless `--image` is used |
|
||||
| `--website` | No | Website URL |
|
||||
| `--twitter` | No | Twitter link |
|
||||
| `--telegram` | No | Telegram link |
|
||||
| `--slippage` | No | Slippage tolerance, e.g. `0.01` = 1% |
|
||||
| `--auto-slippage` | No | Enable automatic slippage |
|
||||
| `--priority-fee` | No | Priority fee in SOL (SOL only) |
|
||||
| `--tip-fee` | No | Tip fee |
|
||||
| `--gas-price` | No | Gas price in wei (EVM chains) |
|
||||
| `--anti-mev` | No | Enable anti-MEV protection |
|
||||
|
||||
**Response fields (data):**
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `status` | string | `pending` / `confirmed` / `failed` |
|
||||
| `hash` | string | Transaction hash |
|
||||
| `order_id` | string | Order ID for polling |
|
||||
| `error_code` | string | Error code on failure |
|
||||
| `error_status` | string | Error description on failure |
|
||||
|
||||
Token creation is asynchronous. Poll `order get` with the returned `order_id` if `status` is `pending`.
|
||||
|
||||
---
|
||||
|
||||
## Rate Limit Handling
|
||||
|
||||
All business routes are protected by GMGN's leaky-bucket limiter. Current production behavior is:
|
||||
|
||||
- `rate=10`, `capacity=10`
|
||||
- every limited `429` response includes `X-RateLimit-Reset`
|
||||
- `X-RateLimit-Reset` is a Unix timestamp in seconds, representing when the current cooldown is expected to end
|
||||
|
||||
CLI behavior:
|
||||
|
||||
- For read-only commands, `gmgn-cli` may wait until `X-RateLimit-Reset` and retry once automatically when the remaining cooldown is short.
|
||||
- For longer cooldowns, or for `swap`, the CLI stops and prints the exact reset time instead of repeatedly sending requests.
|
||||
- The auto-retry threshold defaults to `5000ms` and can be overridden with `GMGN_RATE_LIMIT_AUTO_RETRY_MAX_WAIT_MS=<milliseconds>`.
|
||||
|
||||
Important notes:
|
||||
|
||||
- `RATE_LIMIT_EXCEEDED` and `RATE_LIMIT_BANNED` are request-frequency limits. Continuing to send requests during the cooldown can extend the ban by 5 seconds each time, up to 5 minutes.
|
||||
- `ERROR_RATE_LIMIT_BLOCKED` is an error-count block on `POST /v1/trade/swap`. It is triggered by repeatedly hitting the same business error and should be treated as "fix the request first, then retry after reset".
|
||||
|
||||
---
|
||||
|
||||
## Error Codes
|
||||
@@ -471,9 +675,12 @@ npx gmgn-cli order get --chain <chain> --order-id <order_id> [--raw]
|
||||
| `AUTH_CLIENT_ID_REPLAYED` | 401 | client_id replayed within 7s |
|
||||
| `AUTH_REPLAY_CHECK_UNAVAILABLE` | 503 | Anti-replay Redis unavailable (critical 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` |
|
||||
| `TRADE_WALLET_MISMATCH` | 403 | `--from` address does not match the wallet bound to the API Key |
|
||||
| `CHAIN_NOT_SUPPORTED` | 400 | Unsupported chain |
|
||||
| `BAD_REQUEST` | 400 | Missing or invalid request parameters |
|
||||
| `INTERNAL_API_UNAVAILABLE` | 502 | Downstream market API unavailable |
|
||||
| `BROKER_UNAVAILABLE` | 502 | Downstream trade broker unavailable |
|
||||
| `TRADING_BOT_UNAVAILABLE` | 502 | Trading bot service unreachable (strategy endpoints) |
|
||||
| `INTERNAL_ERROR` | 500 | Internal server error |
|
||||
|
||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "gmgn-cli",
|
||||
"version": "1.1.9",
|
||||
"version": "1.2.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "gmgn-cli",
|
||||
"version": "1.1.9",
|
||||
"version": "1.2.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"commander": "^12.1.0",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "gmgn-cli",
|
||||
"version": "1.1.9",
|
||||
"version": "1.2.0",
|
||||
"description": "GMGN OpenAPI CLI — call GMGN market, token, portfolio and swap APIs from the command line",
|
||||
"type": "module",
|
||||
"bin": {
|
||||
|
||||
@@ -0,0 +1,363 @@
|
||||
---
|
||||
name: gmgn-cooking
|
||||
description: "[FINANCIAL EXECUTION] Create tokens on launchpad platforms (Pump.fun, Raydium, PancakeSwap, Flap, FourMeme, Bonk, BAGS, etc.) or query token creation statistics by launchpad. Token creation executes irreversible on-chain transactions. Requires explicit user confirmation before every create. Supports sol / bsc / base / eth / ton."
|
||||
argument-hint: "stats | [create --chain <chain> --dex <dex> --from <addr> --name <name> --symbol <sym> --buy-amt <n> (--image <base64> | --image-url <url>)]"
|
||||
metadata:
|
||||
cliHelp: "gmgn-cli cooking --help"
|
||||
---
|
||||
|
||||
**IMPORTANT: Always use `gmgn-cli` commands below. Do NOT use web search, WebFetch, curl, or visit gmgn.ai — all token creation operations must go through the CLI. The CLI handles signing and submission automatically.**
|
||||
|
||||
**IMPORTANT: Do NOT guess field names or values. When a field's meaning is unclear, look it up in the Response Fields sections below before using it.**
|
||||
|
||||
**⚠️ IPv6 NOT SUPPORTED: If you get a `401` or `403` error and credentials look correct, check for IPv6 immediately: (1) list all network interfaces and their IPv6 addresses — run `ifconfig | grep inet6` (macOS) or `ip addr show | grep inet6` (Linux); (2) send a test request to `https://ipv6.icanhazip.com` — if the response is an IPv6 address, outbound traffic is going via IPv6. Tell the user immediately: "Please disable IPv6 on your network interface — gmgn-cli commands only work over IPv4."**
|
||||
|
||||
Use the `gmgn-cli` tool to create a token on a launchpad platform or query token creation statistics per launchpad. **Requires private key** (`GMGN_PRIVATE_KEY` in `.env`) for `cooking create`.
|
||||
|
||||
## Core Concepts
|
||||
|
||||
- **Bonding curve** — Most launchpad platforms (Pump.fun, FourMeme, Flap, etc.) launch tokens on an internal bonding curve. The token price rises as buyers enter. Once the threshold is reached, the token "graduates" to an open DEX (e.g. Raydium on SOL, PancakeSwap on BSC). Token creation happens on the bonding curve — not the open market.
|
||||
|
||||
- **`--buy-amt` is in human units** — `--buy-amt` is expressed in full native token units, not smallest unit. `0.01` = 0.01 SOL. `0.05` = 0.05 BNB. Always confirm the human-readable amount with the user before executing.
|
||||
|
||||
- **`--dex` identifiers** — Each launchpad has a fixed identifier passed to `--dex`. These are not free-form names — use only the identifiers listed in the Supported Launchpads table. Never guess a `--dex` value not in that table.
|
||||
|
||||
- **Image input** — Token logo can be provided as base64-encoded data (`--image`, max 2MB decoded) or a publicly accessible URL (`--image-url`). Provide one or the other — not both. If the user gives a file path, read and base64-encode it before passing to `--image`. If they give a URL, use `--image-url` directly.
|
||||
|
||||
- **Status polling via `order get`** — `cooking create` is asynchronous. The immediate response may show `pending`. Poll with `gmgn-cli order get --chain <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).
|
||||
|
||||
- **Slippage** — The initial buy is executed as part of the same transaction as token creation. Slippage applies to that buy. Use `--slippage` (decimal, e.g. `0.01` = 1%) or `--auto-slippage`. One of the two is required when `--buy-amt` is set.
|
||||
|
||||
## Financial Risk Notice
|
||||
|
||||
**This skill executes REAL, IRREVERSIBLE blockchain transactions.**
|
||||
|
||||
- Every `cooking create` command deploys an on-chain token contract and spends real funds (initial buy amount).
|
||||
- Token deployments cannot be undone once confirmed on-chain.
|
||||
- The AI agent must **never auto-execute a create** — explicit user confirmation is required every time, without exception.
|
||||
- Only use this skill with funds you are willing to spend. Initial buy amounts are non-refundable.
|
||||
|
||||
## Sub-commands
|
||||
|
||||
| Sub-command | Description |
|
||||
|-------------|-------------|
|
||||
| `cooking stats` | Get token creation count statistics grouped by launchpad platform (normal auth) |
|
||||
| `cooking create` | Deploy a new token on a launchpad platform (requires private key) |
|
||||
|
||||
## Supported Chains
|
||||
|
||||
`sol` / `bsc` / `base` / `eth` / `ton`
|
||||
|
||||
## Supported Launchpads by Chain
|
||||
|
||||
| Chain | `--dex` values |
|
||||
|-------|----------------|
|
||||
| `sol` | `pump` / `raydium` / `bonk` / `bags` / `memoo` / `letsbonk` / `bonkers` |
|
||||
| `bsc` | `pancakeswap` / `flap` / `fourmeme` |
|
||||
| `base` | `clanker` / `flaunch` / `baseapp` / `basememe` / `zora` / `virtuals_v2` |
|
||||
|
||||
When the user names a platform colloquially (e.g. "pump.fun", "four.meme", "PancakeSwap"), map it to the correct `--dex` identifier from this table before running the command.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- `cooking stats`: Only `GMGN_API_KEY` required
|
||||
- `cooking create`: Both `GMGN_API_KEY` and `GMGN_PRIVATE_KEY` must be configured in `~/.config/gmgn/.env`. The private key must correspond to the wallet bound to the API Key.
|
||||
- `gmgn-cli` installed globally — if missing, run: `npm install -g gmgn-cli`
|
||||
|
||||
## Rate Limit Handling
|
||||
|
||||
All cooking routes go through GMGN's leaky-bucket limiter with `rate=10` and `capacity=10`. Sustained throughput is roughly `10 ÷ weight` requests/second.
|
||||
|
||||
| Command | Weight |
|
||||
|---------|--------|
|
||||
| `cooking create` | 5 |
|
||||
| `cooking stats` | 1 |
|
||||
|
||||
When a request returns `429`:
|
||||
|
||||
- Read `X-RateLimit-Reset` from the response headers — Unix timestamp for when the limit resets.
|
||||
- `cooking create` is a real transaction: **never loop or auto-resubmit** after a `429`. Wait until the reset time, then ask for confirmation again before retrying.
|
||||
- For `RATE_LIMIT_EXCEEDED` or `RATE_LIMIT_BANNED`, repeated requests during cooldown extend the ban by 5 seconds each time, up to 5 minutes.
|
||||
|
||||
**First-time setup** (if credentials are not configured):
|
||||
|
||||
1. Generate key pair and show the public key to the user:
|
||||
```bash
|
||||
openssl genpkey -algorithm ed25519 -out /tmp/gmgn_private.pem 2>/dev/null && \
|
||||
openssl pkey -in /tmp/gmgn_private.pem -pubout 2>/dev/null
|
||||
```
|
||||
Tell the user: *"This is your Ed25519 public key. Go to **https://gmgn.ai/ai**, paste it into the API key creation form (enable swap/cooking capability), then send me the API Key value shown on the page."*
|
||||
|
||||
2. Wait for the user's API key, then configure both credentials:
|
||||
```bash
|
||||
mkdir -p ~/.config/gmgn
|
||||
echo 'GMGN_API_KEY=<key_from_user>' > ~/.config/gmgn/.env
|
||||
echo 'GMGN_PRIVATE_KEY="<pem_content_from_step_1>"' >> ~/.config/gmgn/.env
|
||||
chmod 600 ~/.config/gmgn/.env
|
||||
```
|
||||
|
||||
### Credential Model
|
||||
|
||||
- `GMGN_PRIVATE_KEY` is used exclusively for **local message signing** — the private key never leaves the machine. The CLI computes an Ed25519 signature in-process and transmits only the base64-encoded result in the `X-Signature` request header.
|
||||
- `GMGN_API_KEY` is transmitted in the `X-APIKEY` header over HTTPS.
|
||||
- Neither credential is ever passed as a command-line argument.
|
||||
|
||||
## `cooking stats` Usage
|
||||
|
||||
```bash
|
||||
gmgn-cli cooking stats [--raw]
|
||||
```
|
||||
|
||||
### `cooking stats` Response Fields
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `launchpad` | string | Launchpad identifier (e.g. `pump`, `raydium`, `pancakeswap`) |
|
||||
| `token_count` | int | Number of tokens created via GMGN on that launchpad |
|
||||
|
||||
## `cooking create` Parameters
|
||||
|
||||
| Parameter | Required | Description |
|
||||
|-----------|----------|-------------|
|
||||
| `--chain` | Yes | Chain: `sol` / `bsc` / `base` / `eth` / `ton` |
|
||||
| `--dex` | Yes | Launchpad platform identifier — see Supported Launchpads table. Never guess this value. |
|
||||
| `--from` | Yes | Wallet address (must match API Key binding) |
|
||||
| `--name` | Yes | Token full name (e.g. `Doge Killer`) |
|
||||
| `--symbol` | Yes | Token ticker symbol (e.g. `DOGEK`) |
|
||||
| `--description` | No | Token description / project pitch |
|
||||
| `--buy-amt` | Yes | Initial buy amount in **human-readable native token units** (e.g. `0.01` = 0.01 SOL). This is NOT in smallest unit. |
|
||||
| `--image` | No* | Token logo as **base64-encoded** data (max 2MB decoded). Mutually exclusive with `--image-url`. One of the two is required. |
|
||||
| `--image-url` | No* | Token logo as a publicly accessible URL. Mutually exclusive with `--image`. One of the two is required. |
|
||||
| `--slippage` | No* | Slippage tolerance, e.g. `0.01` = 1%. **Mutually exclusive with `--auto-slippage`** — provide one or the other. |
|
||||
| `--auto-slippage` | No* | Enable automatic slippage. **Mutually exclusive with `--slippage`.** |
|
||||
| `--website` | No | Project website URL |
|
||||
| `--twitter` | No | Twitter / X URL |
|
||||
| `--telegram` | No | Telegram group URL |
|
||||
| `--priority-fee` | No | Priority fee in SOL (SOL only, ≥ 0.0001 SOL) |
|
||||
| `--tip-fee` | No | Tip fee (SOL ≥ 0.00001 / BSC ≥ 0.000001 BNB; ignored on ETH/BASE) |
|
||||
| `--gas-price` | No | Gas price in wei (EVM chains) |
|
||||
| `--anti-mev` | No | Enable anti-MEV protection |
|
||||
|
||||
\* `--image` or `--image-url`: provide exactly one. `--slippage` or `--auto-slippage`: provide exactly one.
|
||||
|
||||
## `cooking create` Response Fields
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `status` | string | `pending` / `confirmed` / `failed` |
|
||||
| `hash` | string | Transaction hash (may be empty while `pending`) |
|
||||
| `order_id` | string | Order ID — pass to `gmgn-cli order get` to poll for final status |
|
||||
| `error_code` | string | Error code on failure |
|
||||
| `error_status` | string | Error description on failure |
|
||||
|
||||
## Status Polling
|
||||
|
||||
Token creation is **asynchronous**. If the initial `cooking create` response shows `status: pending`:
|
||||
|
||||
1. Poll with `gmgn-cli order get` every **2 seconds**, up to **30 seconds**:
|
||||
```bash
|
||||
gmgn-cli order get --chain <chain> --order-id <order_id>
|
||||
```
|
||||
2. The new token's contract / mint address is in the **`output_token`** field of the `order get` response — it is NOT returned by `cooking create` directly.
|
||||
3. Stop polling once `status` is `confirmed`, `failed`, or `expired`.
|
||||
4. On `confirmed`: display `output_token` as the token address and include the block explorer link.
|
||||
5. On `failed` / `expired`: report the `error_status` and do not retry automatically.
|
||||
|
||||
## Usage Examples
|
||||
|
||||
```bash
|
||||
# Get token creation statistics per launchpad
|
||||
gmgn-cli cooking stats
|
||||
|
||||
# Create a token on Pump.fun (SOL) — with URL image
|
||||
gmgn-cli cooking create \
|
||||
--chain sol \
|
||||
--dex pump \
|
||||
--from <wallet_address> \
|
||||
--name "My Token" \
|
||||
--symbol MAT \
|
||||
--buy-amt 0.01 \
|
||||
--image-url https://example.com/logo.png \
|
||||
--slippage 0.01 \
|
||||
--priority-fee 0.001
|
||||
|
||||
# Create a token on PancakeSwap (BSC) — with URL image and social links
|
||||
gmgn-cli cooking create \
|
||||
--chain bsc \
|
||||
--dex pancakeswap \
|
||||
--from <wallet_address> \
|
||||
--name "BSC Token" \
|
||||
--symbol BSCT \
|
||||
--buy-amt 0.05 \
|
||||
--image-url https://example.com/logo.png \
|
||||
--slippage 0.02 \
|
||||
--gas-price 5000000000 \
|
||||
--website https://mytoken.io \
|
||||
--twitter https://twitter.com/mytoken
|
||||
|
||||
# Create a token on FourMeme (BSC) — using base64 image from local file
|
||||
gmgn-cli cooking create \
|
||||
--chain bsc \
|
||||
--dex fourmeme \
|
||||
--from <wallet_address> \
|
||||
--name "Four Token" \
|
||||
--symbol FOUR \
|
||||
--buy-amt 0.05 \
|
||||
--image "$(base64 -i /path/to/logo.png)" \
|
||||
--auto-slippage
|
||||
|
||||
# Create a token on letsbonk (SOL)
|
||||
gmgn-cli cooking create \
|
||||
--chain sol \
|
||||
--dex letsbonk \
|
||||
--from <wallet_address> \
|
||||
--name "Bonk Token" \
|
||||
--symbol BNKT \
|
||||
--buy-amt 0.01 \
|
||||
--image-url https://example.com/logo.png \
|
||||
--auto-slippage \
|
||||
--anti-mev
|
||||
|
||||
```
|
||||
|
||||
## Output Format
|
||||
|
||||
### Pre-create Confirmation
|
||||
|
||||
Before every `cooking create`, present this summary and wait for explicit user confirmation:
|
||||
|
||||
```
|
||||
⚠️ Token Creation Confirmation Required
|
||||
|
||||
Chain: {chain}
|
||||
Platform: {--dex} (e.g. pump / fourmeme)
|
||||
Wallet: {--from}
|
||||
Token Name: {--name}
|
||||
Symbol: {--symbol}
|
||||
Initial Buy: {--buy-amt} {native currency} (e.g. 0.01 SOL)
|
||||
Slippage: {--slippage}% (or "auto")
|
||||
Image: {--image-url or "base64 provided"}
|
||||
Social: {twitter / telegram / website if provided}
|
||||
|
||||
Reply "confirm" to deploy this token. This action is IRREVERSIBLE.
|
||||
```
|
||||
|
||||
### Post-create Receipt
|
||||
|
||||
After polling confirms a successful deployment:
|
||||
|
||||
```
|
||||
✅ Token Created
|
||||
|
||||
Token: {--name} ({--symbol})
|
||||
Address: {output_token from order get}
|
||||
Chain: {chain}
|
||||
Platform: {--dex}
|
||||
Tx: {explorer link for hash}
|
||||
Order ID: {order_id}
|
||||
```
|
||||
|
||||
Block explorer links:
|
||||
|
||||
| Chain | Explorer |
|
||||
|-------|----------|
|
||||
| sol | `https://solscan.io/tx/<hash>` |
|
||||
| bsc | `https://bscscan.com/tx/<hash>` |
|
||||
| base | `https://basescan.org/tx/<hash>` |
|
||||
| eth | `https://etherscan.io/tx/<hash>` |
|
||||
|
||||
## Guided Launch Flow
|
||||
|
||||
When a user says they want to launch / create / deploy a token but has not provided all required information, do NOT ask for everything at once. Collect information in this order, one step at a time:
|
||||
|
||||
### Step 1 — Chain & Platform
|
||||
|
||||
Ask: *"Which chain and platform do you want to launch on?"*
|
||||
|
||||
Show the available options:
|
||||
|
||||
| Chain | Platform | `--dex` |
|
||||
|-------|----------|---------|
|
||||
| Solana | Pump.fun | `pump` |
|
||||
| Solana | letsbonk | `letsbonk` |
|
||||
| Solana | Raydium | `raydium` |
|
||||
| Solana | BAGS | `bags` |
|
||||
| Solana | Memoo | `memoo` |
|
||||
| Solana | Bonkers | `bonkers` |
|
||||
| BSC | FourMeme | `fourmeme` |
|
||||
| BSC | PancakeSwap | `pancakeswap` |
|
||||
| BSC | Flap | `flap` |
|
||||
| Base | Clanker | `clanker` |
|
||||
| Base | Zora | `zora` |
|
||||
| Base | Flaunch | `flaunch` |
|
||||
| Base | Virtuals | `virtuals_v2` |
|
||||
|
||||
If the user is unsure, recommend: **Pump.fun (SOL)** or **FourMeme (BSC)** — the two most active launchpads.
|
||||
|
||||
### Step 2 — Token Identity
|
||||
|
||||
Ask for name, symbol, and description together:
|
||||
|
||||
*"What is your token's name, ticker symbol, and a short description?"*
|
||||
|
||||
- Name: the full display name (e.g. `Doge Killer`)
|
||||
- Symbol: short uppercase ticker, typically 3–8 characters (e.g. `DOGEK`)
|
||||
- Description: optional but recommended — a one-line pitch shown on the launchpad
|
||||
|
||||
### Step 3 — Logo
|
||||
|
||||
Ask: *"Do you have a logo image? You can share a file path (e.g. `/Users/you/logo.png`) or an image URL."*
|
||||
|
||||
- If the user provides a **file path**: silently run `base64 -i <path>` and pass the result to `--image`. Do not mention "base64" to the user.
|
||||
- If the user provides a **URL**: use `--image-url` directly.
|
||||
- If the user has no logo: note that most platforms accept a launch without one, but it significantly reduces visibility. Ask if they want to proceed without it.
|
||||
|
||||
### Step 4 — Social Links (optional)
|
||||
|
||||
Ask: *"Do you have a Twitter, Telegram, or website to attach to the token? You can skip this."*
|
||||
|
||||
Collect any combination of `--twitter`, `--telegram`, `--website`. If the user skips, proceed to the next step.
|
||||
|
||||
### Step 5 — Initial Buy Amount
|
||||
|
||||
Ask: *"How much {SOL / BNB / ETH} do you want to spend on the initial buy?"*
|
||||
|
||||
Pass the user's answer directly to `--buy-amt` — this is already in full token units (e.g. `0.01` = 0.01 SOL), do NOT convert to lamports or wei.
|
||||
|
||||
### Step 6 — Confirmation & Execute
|
||||
|
||||
Once all information is collected, present the pre-create confirmation summary (see Output Format section) and wait for the user to reply "confirm" before executing.
|
||||
|
||||
---
|
||||
|
||||
## Execution Guidelines
|
||||
|
||||
- **[REQUIRED] Pre-create confirmation** — Before executing `cooking create`, present the full summary above and receive explicit "confirm" from the user. No exceptions. Do NOT auto-create.
|
||||
- **[REQUIRED] `--dex` validation** — Before running, look up the user's named platform in the Supported Launchpads table and resolve to the correct `--dex` identifier. Never guess or pass a freeform platform name. If the chain/platform combination is not in the table, tell the user it is unsupported.
|
||||
- **Slippage requirement** — Either `--slippage` or `--auto-slippage` must be provided. If the user did not specify, suggest `--auto-slippage` for volatile new tokens or ask for a preference.
|
||||
- **Image handling** — If the user provides a file path, run `base64 -i <path>` and pass the result to `--image`. If they provide a URL, use `--image-url`. If neither is provided, ask before building the confirmation — most platforms require a logo.
|
||||
- **Address validation** — Validate `--from` wallet address format before submitting:
|
||||
- `sol`: base58, 32–44 characters
|
||||
- `bsc` / `base` / `eth`: `0x` + 40 hex digits
|
||||
- **Chain-wallet compatibility** — SOL addresses are incompatible with EVM chains and vice versa. Warn the user and abort if the address format does not match the chain.
|
||||
- **Order polling** — After `cooking create`, if `status` is `pending`, poll `order get` every 2 seconds up to 30 seconds. The token address is in `output_token`. Do not report success until `status` is `confirmed`.
|
||||
- **Credential sensitivity** — `GMGN_API_KEY` and `GMGN_PRIVATE_KEY` can execute real transactions. Never log, display, or expose these values.
|
||||
|
||||
## Notes
|
||||
|
||||
- `cooking create` uses **critical auth** (API Key + signature) — CLI handles signing automatically.
|
||||
- `cooking stats` uses normal auth (API Key only — no private key needed).
|
||||
- The new token's mint address is in `output_token` from `gmgn-cli order get`, not in the initial `cooking create` response.
|
||||
- Use `--raw` on any command to get single-line JSON for further processing.
|
||||
|
||||
## References
|
||||
|
||||
| Skill | Description |
|
||||
|-------|-------------|
|
||||
| [gmgn-swap](https://github.com/GMGNAI/gmgn-skills/tree/main/skills/gmgn-swap) | Contains `order get` command used for polling token creation status |
|
||||
| [gmgn-token](https://github.com/GMGNAI/gmgn-skills/tree/main/skills/gmgn-token) | Token security check, info, holders, and traders — useful after launch to monitor your token |
|
||||
| [gmgn-market](https://github.com/GMGNAI/gmgn-skills/tree/main/skills/gmgn-market) | `market trenches` for tracking bonding curve progress; `market trending` to see if your token is gaining traction |
|
||||
| [gmgn-track](https://github.com/GMGNAI/gmgn-skills/tree/main/skills/gmgn-track) | Smart money and KOL trade tracking — monitor whether smart wallets are buying your token after launch |
|
||||
| [gmgn-portfolio](https://github.com/GMGNAI/gmgn-skills/tree/main/skills/gmgn-portfolio) | Wallet holdings and P&L — check your own wallet balance before deciding on `--buy-amt` |
|
||||
@@ -55,9 +55,40 @@ Use the `gmgn-cli` tool to query K-line data for a token, browse trending tokens
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- `.env` file with `GMGN_API_KEY` set
|
||||
- Run from the directory where your `.env` file is located, or set `GMGN_HOST` in your environment
|
||||
- `gmgn-cli` installed globally: `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`
|
||||
|
||||
## Rate Limit Handling
|
||||
|
||||
All market routes used by this skill go through GMGN's leaky-bucket limiter with `rate=10` and `capacity=10`. Sustained throughput is roughly `10 ÷ weight` requests/second, and the max burst is roughly `floor(10 ÷ weight)` when the bucket is full.
|
||||
|
||||
| Command | Route | Weight |
|
||||
|---------|-------|--------|
|
||||
| `market kline` | `GET /v1/market/token_kline` | 2 |
|
||||
| `market trending` | `GET /v1/market/rank` | 1 |
|
||||
| `market trenches` | `POST /v1/trenches` | 3 |
|
||||
|
||||
When a request returns `429`:
|
||||
|
||||
- Read `X-RateLimit-Reset` from the response headers. It is a Unix timestamp in seconds that marks when the limit is expected to reset.
|
||||
- The CLI may wait and retry once automatically when the remaining cooldown is short. If it still fails, stop and tell the user the exact retry time instead of sending more requests.
|
||||
- For `RATE_LIMIT_EXCEEDED` or `RATE_LIMIT_BANNED`, repeated requests during the cooldown can extend the ban by 5 seconds each time, up to 5 minutes. Do not spam retries.
|
||||
|
||||
**First-time setup** (if `GMGN_API_KEY` is not configured):
|
||||
|
||||
1. Generate key pair and show the public key to the user:
|
||||
```bash
|
||||
openssl genpkey -algorithm ed25519 -out /tmp/gmgn_private.pem 2>/dev/null && \
|
||||
openssl pkey -in /tmp/gmgn_private.pem -pubout 2>/dev/null
|
||||
```
|
||||
Tell the user: *"This is your Ed25519 public key. Go to **https://gmgn.ai/ai**, paste it into the API key creation form, then send me the API Key value shown on the page."*
|
||||
|
||||
2. Wait for the user's API key, then configure:
|
||||
```bash
|
||||
mkdir -p ~/.config/gmgn
|
||||
echo 'GMGN_API_KEY=<key_from_user>' > ~/.config/gmgn/.env
|
||||
chmod 600 ~/.config/gmgn/.env
|
||||
```
|
||||
|
||||
## `market kline` Parameters
|
||||
|
||||
|
||||
@@ -46,9 +46,42 @@ Use the `gmgn-cli` tool to query wallet portfolio data based on the user's reque
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- `.env` file with `GMGN_API_KEY` set
|
||||
- Run from the directory where your `.env` file is located, or set `GMGN_HOST` in your environment
|
||||
- `gmgn-cli` installed globally: `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`
|
||||
|
||||
## Rate Limit Handling
|
||||
|
||||
All portfolio routes used by this skill go through GMGN's leaky-bucket limiter with `rate=10` and `capacity=10`. Sustained throughput is roughly `10 ÷ weight` requests/second, and the max burst is roughly `floor(10 ÷ weight)` when the bucket is full.
|
||||
|
||||
| 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 |
|
||||
|
||||
When a request returns `429`:
|
||||
|
||||
- Read `X-RateLimit-Reset` from the response headers. It is a Unix timestamp in seconds that marks when the limit is expected to reset.
|
||||
- The CLI may wait and retry once automatically when the remaining cooldown is short. If it still fails, stop and tell the user the exact retry time instead of sending more requests.
|
||||
- For `RATE_LIMIT_EXCEEDED` or `RATE_LIMIT_BANNED`, repeated requests during the cooldown can extend the ban by 5 seconds each time, up to 5 minutes. Do not spam retries.
|
||||
|
||||
**First-time setup** (if `GMGN_API_KEY` is not configured):
|
||||
|
||||
1. Generate key pair and show the public key to the user:
|
||||
```bash
|
||||
openssl genpkey -algorithm ed25519 -out /tmp/gmgn_private.pem 2>/dev/null && \
|
||||
openssl pkey -in /tmp/gmgn_private.pem -pubout 2>/dev/null
|
||||
```
|
||||
Tell the user: *"This is your Ed25519 public key. Go to **https://gmgn.ai/ai**, paste it into the API key creation form, then send me the API Key value shown on the page."*
|
||||
|
||||
2. Wait for the user's API key, then configure:
|
||||
```bash
|
||||
mkdir -p ~/.config/gmgn
|
||||
echo 'GMGN_API_KEY=<key_from_user>' > ~/.config/gmgn/.env
|
||||
chmod 600 ~/.config/gmgn/.env
|
||||
```
|
||||
|
||||
## Usage Examples
|
||||
|
||||
|
||||
+136
-5
@@ -48,6 +48,9 @@ Use the `gmgn-cli` tool to submit a token swap or query an existing order. **Req
|
||||
| `swap` | Submit a token swap |
|
||||
| `order quote` | Get a swap quote (no transaction submitted) |
|
||||
| `order get` | Query order status |
|
||||
| `order strategy create` | Create a limit/strategy order (requires private key) |
|
||||
| `order strategy list` | List strategy orders (normal auth) |
|
||||
| `order strategy cancel` | Cancel a strategy order (requires private key) |
|
||||
|
||||
## Supported Chains
|
||||
|
||||
@@ -67,13 +70,44 @@ Currency tokens are the base/native assets of each chain. They are used to buy o
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Both `GMGN_API_KEY` and `GMGN_PRIVATE_KEY` must be set in `.env`. The private key must correspond to the wallet bound to the API Key.
|
||||
Both `GMGN_API_KEY` and `GMGN_PRIVATE_KEY` must be configured in `~/.config/gmgn/.env`. The private key must correspond to the wallet bound to the API Key.
|
||||
|
||||
`gmgn-cli` must be installed globally before use (one-time setup):
|
||||
- `gmgn-cli` installed globally — if missing, run: `npm install -g gmgn-cli`
|
||||
|
||||
```bash
|
||||
npm install -g gmgn-cli
|
||||
```
|
||||
## Rate Limit Handling
|
||||
|
||||
All swap-related routes used by this skill go through GMGN's leaky-bucket limiter with `rate=10` and `capacity=10`. Sustained throughput is roughly `10 ÷ weight` requests/second, and the max burst is roughly `floor(10 ÷ weight)` when the bucket is full.
|
||||
|
||||
| Command | Route | Weight |
|
||||
|---------|-------|--------|
|
||||
| `swap` | `POST /v1/trade/swap` | 5 |
|
||||
| `order quote` | `GET /v1/trade/quote` | 2 |
|
||||
| `order get` | `GET /v1/trade/query_order` | 1 |
|
||||
|
||||
When a request returns `429`:
|
||||
|
||||
- Read `X-RateLimit-Reset` from the response headers. It is a Unix timestamp in seconds that marks when the limit is expected to reset.
|
||||
- `swap` is a real transaction: never loop or auto-submit repeated swap attempts after a `429`. Wait until the reset time, then ask for confirmation again before retrying.
|
||||
- The CLI may wait and retry once automatically for short cooldowns on read-only commands such as `order quote` and `order get`. If it still fails, stop and tell the user the exact retry time instead of sending more requests.
|
||||
- For `RATE_LIMIT_EXCEEDED` or `RATE_LIMIT_BANNED`, repeated requests during the cooldown can extend the ban by 5 seconds each time, up to 5 minutes.
|
||||
- `POST /v1/trade/swap` also has an error-count limiter. Repeatedly triggering the same business error, especially `40003701` (insufficient token balance), can return `ERROR_RATE_LIMIT_BLOCKED`. When this happens, do not retry until the reset time and fix the underlying request first.
|
||||
|
||||
**First-time setup** (if credentials are not configured):
|
||||
|
||||
1. Generate key pair and show the public key to the user:
|
||||
```bash
|
||||
openssl genpkey -algorithm ed25519 -out /tmp/gmgn_private.pem 2>/dev/null && \
|
||||
openssl pkey -in /tmp/gmgn_private.pem -pubout 2>/dev/null
|
||||
```
|
||||
Tell the user: *"This is your Ed25519 public key. Go to **https://gmgn.ai/ai**, paste it into the API key creation form (enable swap capability), then send me the API Key value shown on the page."*
|
||||
|
||||
2. Wait for the user's API key, then configure both credentials:
|
||||
```bash
|
||||
mkdir -p ~/.config/gmgn
|
||||
echo 'GMGN_API_KEY=<key_from_user>' > ~/.config/gmgn/.env
|
||||
echo 'GMGN_PRIVATE_KEY="<pem_content_from_step_1>"' >> ~/.config/gmgn/.env
|
||||
chmod 600 ~/.config/gmgn/.env
|
||||
```
|
||||
|
||||
### Credential Model
|
||||
|
||||
@@ -248,11 +282,108 @@ Order ID: {order_id}
|
||||
|
||||
Convert `filled_input_amount` and `filled_output_amount` from smallest unit using token decimals before displaying.
|
||||
|
||||
## `order strategy create` Parameters
|
||||
|
||||
| Parameter | Required | Description |
|
||||
|-----------|----------|-------------|
|
||||
| `--chain` | Yes | `sol` / `bsc` / `base` |
|
||||
| `--from` | Yes | Wallet address (must match API Key binding) |
|
||||
| `--base-token` | Yes | Base token contract address |
|
||||
| `--quote-token` | Yes | Quote token contract address |
|
||||
| `--side` | Yes | Direction: `buy` / `sell` |
|
||||
| `--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) / `hold_amount` |
|
||||
| `--slippage` | No | Slippage tolerance, e.g. `0.01` = 1%. Mutually exclusive with `--auto-slippage` |
|
||||
| `--auto-slippage` | No | Enable automatic slippage |
|
||||
| `--priority-fee` | No | Priority fee in SOL (SOL only) |
|
||||
| `--tip-fee` | No | Tip fee |
|
||||
| `--gas-price` | No | Gas price in wei (EVM chains) |
|
||||
| `--anti-mev` | No | Enable anti-MEV protection |
|
||||
|
||||
|
||||
**`order strategy create` Response Fields:**
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `order_id` | string | Created strategy order ID |
|
||||
| `is_update` | bool | `true` if an existing order was updated, `false` if newly created |
|
||||
|
||||
## `order strategy list` Parameters
|
||||
|
||||
| Parameter | Required | Description |
|
||||
|-----------|----------|-------------|
|
||||
| `--chain` | Yes | `sol` / `bsc` / `base` |
|
||||
| `--type` | No | `open` (default) / `history` |
|
||||
| `--from` | No | Filter by wallet address |
|
||||
| `--base-token` | No | Filter by token address |
|
||||
| `--page-token` | No | Pagination cursor from previous response |
|
||||
| `--limit` | No | Results per page (default 10 for history) |
|
||||
|
||||
**`order strategy list` Response Fields:**
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `next_page_token` | string | Cursor for next page; empty when no more data |
|
||||
| `total` | int | Total count (only returned when `--type open`) |
|
||||
| `list` | array | Strategy order list |
|
||||
|
||||
## `order strategy cancel` Parameters
|
||||
|
||||
| Parameter | Required | Description |
|
||||
|-----------|----------|-------------|
|
||||
| `--chain` | Yes | `sol` / `bsc` / `base` |
|
||||
| `--from` | Yes | Wallet address (must match API Key binding) |
|
||||
| `--order-id` | Yes | Order ID to cancel |
|
||||
| `--close-sell-model` | No | Sell model when closing the order |
|
||||
|
||||
## `order strategy` Usage Examples
|
||||
|
||||
```bash
|
||||
# Create a take-profit order: sell when price rises to target
|
||||
gmgn-cli order strategy create \
|
||||
--chain sol \
|
||||
--from <wallet_address> \
|
||||
--base-token <token_address> \
|
||||
--quote-token <sol_address> \
|
||||
--sub-order-type take_profit \
|
||||
--check-price 0.002 \
|
||||
--amount-in 1000000 \
|
||||
--slippage 0.01
|
||||
|
||||
# Create a stop-loss order: sell when price drops to target
|
||||
gmgn-cli order strategy create \
|
||||
--chain sol \
|
||||
--from <wallet_address> \
|
||||
--base-token <token_address> \
|
||||
--quote-token <sol_address> \
|
||||
--sub-order-type stop_loss \
|
||||
--check-price 0.0005 \
|
||||
--amount-in-percent 100 \
|
||||
--slippage 0.01
|
||||
|
||||
# List open strategy orders
|
||||
gmgn-cli order strategy list --chain sol
|
||||
|
||||
# List history orders with pagination
|
||||
gmgn-cli order strategy list --chain sol --type history --limit 20
|
||||
|
||||
# Cancel a strategy order
|
||||
gmgn-cli order strategy cancel \
|
||||
--chain sol \
|
||||
--from <wallet_address> \
|
||||
--order-id <order_id>
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- Swap uses **critical auth** (API Key + signature) — CLI handles signing automatically, no manual processing needed
|
||||
- After submitting a swap, use `order get` to poll for confirmation
|
||||
- `--amount` is in the **smallest unit** (e.g., lamports for SOL)
|
||||
- `order strategy create` and `order strategy cancel` use critical auth (require `GMGN_PRIVATE_KEY`); `order strategy list` uses normal auth
|
||||
- Use `--raw` to get single-line JSON for further processing
|
||||
|
||||
## Input Validation
|
||||
|
||||
@@ -46,9 +46,42 @@ Use the `gmgn-cli` tool to query token information based on the user's request.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- `.env` file with `GMGN_API_KEY` set
|
||||
- Run from the directory where your `.env` file is located, or set `GMGN_HOST` in your environment
|
||||
- `gmgn-cli` installed globally: `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`
|
||||
|
||||
## Rate Limit Handling
|
||||
|
||||
All token routes used by this skill go through GMGN's leaky-bucket limiter with `rate=10` and `capacity=10`. Sustained throughput is roughly `10 ÷ weight` requests/second, and the max burst is roughly `floor(10 ÷ weight)` when the bucket is full.
|
||||
|
||||
| Command | Route | Weight |
|
||||
|---------|-------|--------|
|
||||
| `token info` | `GET /v1/token/info` | 1 |
|
||||
| `token security` | `GET /v1/token/security` | 1 |
|
||||
| `token pool` | `GET /v1/token/pool_info` | 1 |
|
||||
| `token holders` | `GET /v1/market/token_top_holders` | 5 |
|
||||
| `token traders` | `GET /v1/market/token_top_traders` | 5 |
|
||||
|
||||
When a request returns `429`:
|
||||
|
||||
- Read `X-RateLimit-Reset` from the response headers. It is a Unix timestamp in seconds that marks when the limit is expected to reset.
|
||||
- The CLI may wait and retry once automatically when the remaining cooldown is short. If it still fails, stop and tell the user the exact retry time instead of sending more requests.
|
||||
- For `RATE_LIMIT_EXCEEDED` or `RATE_LIMIT_BANNED`, repeated requests during the cooldown can extend the ban by 5 seconds each time, up to 5 minutes. Do not spam retries.
|
||||
|
||||
**First-time setup** (if `GMGN_API_KEY` is not configured):
|
||||
|
||||
1. Generate key pair and show the public key to the user:
|
||||
```bash
|
||||
openssl genpkey -algorithm ed25519 -out /tmp/gmgn_private.pem 2>/dev/null && \
|
||||
openssl pkey -in /tmp/gmgn_private.pem -pubout 2>/dev/null
|
||||
```
|
||||
Tell the user: *"This is your Ed25519 public key. Go to **https://gmgn.ai/ai**, paste it into the API key creation form, then send me the API Key value shown on the page."*
|
||||
|
||||
2. Wait for the user's API key, then configure:
|
||||
```bash
|
||||
mkdir -p ~/.config/gmgn
|
||||
echo 'GMGN_API_KEY=<key_from_user>' > ~/.config/gmgn/.env
|
||||
chmod 600 ~/.config/gmgn/.env
|
||||
```
|
||||
|
||||
## Parameters — `token info` / `token security` / `token pool`
|
||||
|
||||
|
||||
@@ -59,10 +59,45 @@ Use the `gmgn-cli` tool to query on-chain tracking data based on the user's requ
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- `.env` file with `GMGN_API_KEY` set
|
||||
- `GMGN_PRIVATE_KEY` required for `track follow-wallet` (signature auth); not needed for `track kol` / `track smartmoney`
|
||||
- Run from the directory where your `.env` file is located, or set `GMGN_HOST` in your environment
|
||||
- `gmgn-cli` installed globally: `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`
|
||||
- `GMGN_PRIVATE_KEY` required only for `track follow-wallet`; not needed for `track kol` / `track smartmoney`
|
||||
|
||||
## Rate Limit Handling
|
||||
|
||||
All tracking routes used by this skill go through GMGN's leaky-bucket limiter with `rate=10` and `capacity=10`. Sustained throughput is roughly `10 ÷ weight` requests/second, and the max burst is roughly `floor(10 ÷ weight)` when the bucket is full.
|
||||
|
||||
| Command | Route | Weight |
|
||||
|---------|-------|--------|
|
||||
| `track follow-wallet` | `GET /v1/trade/follow_wallet` | 3 |
|
||||
| `track kol` | `GET /v1/user/kol` | 1 |
|
||||
| `track smartmoney` | `GET /v1/user/smartmoney` | 1 |
|
||||
|
||||
When a request returns `429`:
|
||||
|
||||
- Read `X-RateLimit-Reset` from the response headers. It is a Unix timestamp in seconds that marks when the limit is expected to reset.
|
||||
- The CLI may wait and retry once automatically when the remaining cooldown is short. If it still fails, stop and tell the user the exact retry time instead of sending more requests.
|
||||
- For `RATE_LIMIT_EXCEEDED` or `RATE_LIMIT_BANNED`, repeated requests during the cooldown can extend the ban by 5 seconds each time, up to 5 minutes. Do not spam retries.
|
||||
|
||||
**First-time setup** (if `GMGN_API_KEY` is not configured):
|
||||
|
||||
1. Generate key pair and show the public key to the user:
|
||||
```bash
|
||||
openssl genpkey -algorithm ed25519 -out /tmp/gmgn_private.pem 2>/dev/null && \
|
||||
openssl pkey -in /tmp/gmgn_private.pem -pubout 2>/dev/null
|
||||
```
|
||||
Tell the user: *"This is your Ed25519 public key. Go to **https://gmgn.ai/ai**, paste it into the API key creation form, then send me the API Key value shown on the page."*
|
||||
|
||||
2. Wait for the user's API key, then configure:
|
||||
```bash
|
||||
mkdir -p ~/.config/gmgn
|
||||
echo 'GMGN_API_KEY=<key_from_user>' > ~/.config/gmgn/.env
|
||||
chmod 600 ~/.config/gmgn/.env
|
||||
```
|
||||
If the user also needs `track follow-wallet`, append the private key:
|
||||
```bash
|
||||
echo 'GMGN_PRIVATE_KEY="<pem_content_from_step_1>"' >> ~/.config/gmgn/.env
|
||||
```
|
||||
|
||||
## Usage Examples
|
||||
|
||||
|
||||
+324
-31
@@ -8,6 +8,57 @@
|
||||
|
||||
import { buildAuthQuery, buildMessage, detectAlgorithm, sign } from "./signer.js";
|
||||
|
||||
const RATE_LIMIT_RETRY_BUFFER_MS = 1000;
|
||||
const DEFAULT_RATE_LIMIT_AUTO_RETRY_MAX_WAIT_MS = 5000;
|
||||
|
||||
interface PreparedRequest {
|
||||
method: string;
|
||||
subPath: string;
|
||||
url: string;
|
||||
headers: Record<string, string>;
|
||||
body: string | null;
|
||||
curlStr: string;
|
||||
}
|
||||
|
||||
interface ResponseEnvelope {
|
||||
code: number | string;
|
||||
data?: unknown;
|
||||
message?: string;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
interface OpenApiErrorParams {
|
||||
method: string;
|
||||
path: string;
|
||||
status: number;
|
||||
apiCode?: number | string;
|
||||
apiError?: string;
|
||||
apiMessage?: string;
|
||||
resetAtUnix?: number;
|
||||
}
|
||||
|
||||
class OpenApiError extends Error {
|
||||
readonly method: string;
|
||||
readonly path: string;
|
||||
readonly status: number;
|
||||
readonly apiCode?: number | string;
|
||||
readonly apiError?: string;
|
||||
readonly apiMessage?: string;
|
||||
readonly resetAtUnix?: number;
|
||||
|
||||
constructor(params: OpenApiErrorParams) {
|
||||
super(buildOpenApiErrorMessage(params));
|
||||
this.name = "OpenApiError";
|
||||
this.method = params.method;
|
||||
this.path = params.path;
|
||||
this.status = params.status;
|
||||
this.apiCode = params.apiCode;
|
||||
this.apiError = params.apiError;
|
||||
this.apiMessage = params.apiMessage;
|
||||
this.resetAtUnix = params.resetAtUnix;
|
||||
}
|
||||
}
|
||||
|
||||
export interface Config {
|
||||
apiKey: string;
|
||||
privateKeyPem?: string;
|
||||
@@ -34,6 +85,73 @@ export interface SwapParams {
|
||||
gas_price?: string;
|
||||
max_fee_per_gas?: string;
|
||||
max_priority_fee_per_gas?: string;
|
||||
condition_orders?: StrategyConditionOrder[];
|
||||
sell_ratio_type?: string;
|
||||
}
|
||||
|
||||
export interface StrategyConditionOrder {
|
||||
order_type: string; // "profit_stop" | "loss_stop"
|
||||
side: string; // "sell"
|
||||
price_scale: string;
|
||||
sell_ratio: string;
|
||||
}
|
||||
|
||||
export interface StrategyCreateParams {
|
||||
chain: string;
|
||||
from_address: string;
|
||||
base_token: string;
|
||||
quote_token: string;
|
||||
order_type: string;
|
||||
sub_order_type: string;
|
||||
check_price: string;
|
||||
amount_in?: string;
|
||||
amount_in_percent?: string;
|
||||
limit_price_mode?: string;
|
||||
price_gap_ratio?: string;
|
||||
expire_in?: number;
|
||||
sell_ratio_type?: string;
|
||||
slippage?: number;
|
||||
auto_slippage?: boolean;
|
||||
fee?: string;
|
||||
gas_price?: string;
|
||||
max_fee_per_gas?: string;
|
||||
max_priority_fee_per_gas?: string;
|
||||
is_anti_mev?: boolean;
|
||||
anti_mev_mode?: string;
|
||||
priority_fee?: string;
|
||||
tip_fee?: string;
|
||||
custom_rpc?: string;
|
||||
}
|
||||
|
||||
export interface StrategyCancelParams {
|
||||
chain: string;
|
||||
from_address: string;
|
||||
order_id: string;
|
||||
order_type?: string;
|
||||
close_sell_model?: string;
|
||||
}
|
||||
|
||||
export interface CreateTokenParams {
|
||||
chain: string;
|
||||
dex: string;
|
||||
from_address: string;
|
||||
name: string;
|
||||
symbol: string;
|
||||
buy_amt: string;
|
||||
image?: string;
|
||||
image_url?: string;
|
||||
website?: string;
|
||||
twitter?: string;
|
||||
telegram?: string;
|
||||
slippage?: number;
|
||||
auto_slippage?: boolean;
|
||||
priority_fee?: string;
|
||||
tip_fee?: string;
|
||||
gas_price?: string;
|
||||
max_priority_fee_per_gas?: string;
|
||||
max_fee_per_gas?: string;
|
||||
is_anti_mev?: boolean;
|
||||
anti_mev_mode?: string;
|
||||
}
|
||||
|
||||
export class OpenApiClient {
|
||||
@@ -188,6 +306,30 @@ export class OpenApiClient {
|
||||
return this.criticalRequest("GET", "/v1/trade/query_order", { order_id: orderId, chain }, null);
|
||||
}
|
||||
|
||||
// ---- Strategy order endpoints ----
|
||||
|
||||
async createStrategyOrder(params: StrategyCreateParams): Promise<unknown> {
|
||||
return this.criticalRequest("POST", "/v1/trade/strategy/create", {}, params);
|
||||
}
|
||||
|
||||
async getStrategyOrders(chain: string, extra: Record<string, string | number> = {}): Promise<unknown> {
|
||||
return this.normalRequest("GET", "/v1/trade/strategy/orders", { chain, ...extra });
|
||||
}
|
||||
|
||||
async cancelStrategyOrder(params: StrategyCancelParams): Promise<unknown> {
|
||||
return this.criticalRequest("POST", "/v1/trade/strategy/cancel", {}, params);
|
||||
}
|
||||
|
||||
// ---- Cooking endpoints ----
|
||||
|
||||
async getCookingStatistics(): Promise<unknown> {
|
||||
return this.normalRequest("GET", "/v1/cooking/statistics", {});
|
||||
}
|
||||
|
||||
async createToken(params: CreateTokenParams): Promise<unknown> {
|
||||
return this.criticalRequest("POST", "/v1/cooking/create_token", {}, params);
|
||||
}
|
||||
|
||||
// ---- Internal methods ----
|
||||
|
||||
private async normalRequest(
|
||||
@@ -196,18 +338,24 @@ export class OpenApiClient {
|
||||
queryExtra: Record<string, string | number | string[]>,
|
||||
body: unknown = null
|
||||
): Promise<unknown> {
|
||||
const { timestamp, client_id } = buildAuthQuery();
|
||||
const query: Record<string, string | number | string[]> = { ...queryExtra, timestamp, client_id };
|
||||
|
||||
const url = buildUrl(`${this.host}${subPath}`, query);
|
||||
const headers: Record<string, string> = {
|
||||
"X-APIKEY": this.apiKey,
|
||||
"Content-Type": "application/json",
|
||||
};
|
||||
const bodyStr = body !== null ? JSON.stringify(body) : null;
|
||||
const curlStr = formatCurl(method, url, headers, bodyStr);
|
||||
const res = await this.doFetch(method, subPath, url, headers, bodyStr, curlStr);
|
||||
return this.parseResponse(method, subPath, res, curlStr);
|
||||
return this.executePreparedRequest(() => {
|
||||
const { timestamp, client_id } = buildAuthQuery();
|
||||
const query: Record<string, string | number | string[]> = { ...queryExtra, timestamp, client_id };
|
||||
const url = buildUrl(`${this.host}${subPath}`, query);
|
||||
const headers: Record<string, string> = {
|
||||
"X-APIKEY": this.apiKey,
|
||||
"Content-Type": "application/json",
|
||||
};
|
||||
const bodyStr = body !== null ? JSON.stringify(body) : null;
|
||||
return {
|
||||
method,
|
||||
subPath,
|
||||
url,
|
||||
headers,
|
||||
body: bodyStr,
|
||||
curlStr: formatCurl(method, url, headers, bodyStr),
|
||||
};
|
||||
}, true);
|
||||
}
|
||||
|
||||
private async criticalRequest(
|
||||
@@ -220,22 +368,65 @@ export class OpenApiClient {
|
||||
throw new Error("GMGN_PRIVATE_KEY is required for swap/order commands");
|
||||
}
|
||||
|
||||
const { timestamp, client_id } = buildAuthQuery();
|
||||
const query: Record<string, string | number> = { ...queryExtra, timestamp, client_id };
|
||||
return this.executePreparedRequest(() => {
|
||||
const { timestamp, client_id } = buildAuthQuery();
|
||||
const query: Record<string, string | number> = { ...queryExtra, timestamp, client_id };
|
||||
const bodyStr = body !== null ? JSON.stringify(body) : "";
|
||||
const message = buildMessage(subPath, query, bodyStr, timestamp);
|
||||
const signature = sign(message, this.privateKeyPem!, detectAlgorithm(this.privateKeyPem!));
|
||||
|
||||
const bodyStr = body !== null ? JSON.stringify(body) : "";
|
||||
const message = buildMessage(subPath, query, bodyStr, timestamp);
|
||||
const signature = sign(message, this.privateKeyPem, detectAlgorithm(this.privateKeyPem));
|
||||
const url = buildUrl(`${this.host}${subPath}`, query);
|
||||
const headers: Record<string, string> = {
|
||||
"X-APIKEY": this.apiKey,
|
||||
"X-Signature": signature,
|
||||
"Content-Type": "application/json",
|
||||
};
|
||||
return {
|
||||
method,
|
||||
subPath,
|
||||
url,
|
||||
headers,
|
||||
body: bodyStr || null,
|
||||
curlStr: formatCurl(method, url, headers, bodyStr || null),
|
||||
};
|
||||
}, method !== "POST");
|
||||
}
|
||||
|
||||
const url = buildUrl(`${this.host}${subPath}`, query);
|
||||
const headers: Record<string, string> = {
|
||||
"X-APIKEY": this.apiKey,
|
||||
"X-Signature": signature,
|
||||
"Content-Type": "application/json",
|
||||
};
|
||||
const curlStr = formatCurl(method, url, headers, bodyStr || null);
|
||||
const res = await this.doFetch(method, subPath, url, headers, bodyStr || null, curlStr);
|
||||
return this.parseResponse(method, subPath, res, curlStr);
|
||||
private async executePreparedRequest(
|
||||
prepare: () => PreparedRequest,
|
||||
autoRetryOnRateLimit: boolean
|
||||
): Promise<unknown> {
|
||||
const maxAttempts = autoRetryOnRateLimit ? 2 : 1;
|
||||
|
||||
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
|
||||
const request = prepare();
|
||||
const res = await this.doFetch(
|
||||
request.method,
|
||||
request.subPath,
|
||||
request.url,
|
||||
request.headers,
|
||||
request.body,
|
||||
request.curlStr
|
||||
);
|
||||
|
||||
try {
|
||||
return this.parseResponse(request.method, request.subPath, res, request.curlStr);
|
||||
} catch (err) {
|
||||
const retryDelayMs = getRateLimitRetryDelayMs(err, attempt, maxAttempts, autoRetryOnRateLimit);
|
||||
if (retryDelayMs == null) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
if (process.env.GMGN_DEBUG) {
|
||||
console.error(
|
||||
`[gmgn-cli] ${request.method} ${request.subPath} hit rate limit, retrying once in ${Math.ceil(retryDelayMs / 1000)}s`
|
||||
);
|
||||
}
|
||||
await sleep(retryDelayMs);
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error("Unexpected retry loop exit");
|
||||
}
|
||||
|
||||
private async doFetch(
|
||||
@@ -278,6 +469,8 @@ export class OpenApiClient {
|
||||
throw new Error(msg);
|
||||
};
|
||||
|
||||
const resetAtUnix = parseRateLimitReset(res.headers.get("x-ratelimit-reset"));
|
||||
|
||||
let text!: string;
|
||||
try {
|
||||
text = await res.text();
|
||||
@@ -285,7 +478,7 @@ export class OpenApiClient {
|
||||
fail(`${method} ${path} failed: HTTP ${res.status} (failed to read response body: ${err})`);
|
||||
}
|
||||
|
||||
let json!: { code: number | string; data?: unknown; message?: string; error?: string };
|
||||
let json!: ResponseEnvelope;
|
||||
try {
|
||||
json = JSON.parse(text);
|
||||
} catch {
|
||||
@@ -293,16 +486,116 @@ export class OpenApiClient {
|
||||
}
|
||||
|
||||
if (json.code !== 0) {
|
||||
fail(
|
||||
`${method} ${path} failed: HTTP ${res.status} code=${json.code} error=${json.error ?? ""} message=${json.message ?? ""}`,
|
||||
text
|
||||
);
|
||||
if (process.env.GMGN_DEBUG) {
|
||||
console.error(`${curlStr}\n${formatResponse(res, text)}`);
|
||||
}
|
||||
throw new OpenApiError({
|
||||
method,
|
||||
path,
|
||||
status: res.status,
|
||||
apiCode: json.code,
|
||||
apiError: json.error,
|
||||
apiMessage: json.message,
|
||||
resetAtUnix,
|
||||
});
|
||||
}
|
||||
|
||||
return json.data;
|
||||
}
|
||||
}
|
||||
|
||||
function getRateLimitRetryDelayMs(
|
||||
err: unknown,
|
||||
attempt: number,
|
||||
maxAttempts: number,
|
||||
autoRetryOnRateLimit: boolean
|
||||
): number | null {
|
||||
if (!autoRetryOnRateLimit || attempt >= maxAttempts) {
|
||||
return null;
|
||||
}
|
||||
if (!(err instanceof OpenApiError)) {
|
||||
return null;
|
||||
}
|
||||
if (err.apiError !== "RATE_LIMIT_EXCEEDED" && err.apiError !== "RATE_LIMIT_BANNED") {
|
||||
return null;
|
||||
}
|
||||
if (err.resetAtUnix == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const waitMs = Math.max(err.resetAtUnix * 1000 - Date.now(), 0) + RATE_LIMIT_RETRY_BUFFER_MS;
|
||||
return waitMs <= getAutoRetryMaxWaitMs() ? waitMs : null;
|
||||
}
|
||||
|
||||
function parseRateLimitReset(raw: string | null): number | undefined {
|
||||
if (raw == null || raw.trim() === "") {
|
||||
return undefined;
|
||||
}
|
||||
const parsed = Number.parseInt(raw, 10);
|
||||
return Number.isFinite(parsed) && parsed > 0 ? parsed : undefined;
|
||||
}
|
||||
|
||||
function getAutoRetryMaxWaitMs(): number {
|
||||
const raw = process.env.GMGN_RATE_LIMIT_AUTO_RETRY_MAX_WAIT_MS;
|
||||
if (!raw) {
|
||||
return DEFAULT_RATE_LIMIT_AUTO_RETRY_MAX_WAIT_MS;
|
||||
}
|
||||
const parsed = Number.parseInt(raw, 10);
|
||||
return Number.isFinite(parsed) && parsed >= 0 ? parsed : DEFAULT_RATE_LIMIT_AUTO_RETRY_MAX_WAIT_MS;
|
||||
}
|
||||
|
||||
function buildOpenApiErrorMessage(params: OpenApiErrorParams): string {
|
||||
const parts = [`${params.method} ${params.path} failed: HTTP ${params.status}`];
|
||||
if (params.apiCode != null) parts.push(`code=${params.apiCode}`);
|
||||
if (params.apiError) parts.push(`error=${params.apiError}`);
|
||||
if (params.apiMessage) parts.push(`message=${params.apiMessage}`);
|
||||
|
||||
let message = parts.join(" ");
|
||||
|
||||
if (params.status !== 429) {
|
||||
return message;
|
||||
}
|
||||
|
||||
const resetText = params.resetAtUnix != null
|
||||
? formatRateLimitReset(params.resetAtUnix)
|
||||
: "an unknown time";
|
||||
|
||||
if (params.apiError === "ERROR_RATE_LIMIT_BLOCKED") {
|
||||
return `${message}. Repeated business errors triggered a temporary block until ${resetText}. Fix the underlying request before retrying.`;
|
||||
}
|
||||
|
||||
if (params.apiError === "RATE_LIMIT_EXCEEDED" || params.apiError === "RATE_LIMIT_BANNED") {
|
||||
return `${message}. Rate limit resets at ${resetText}. Stop sending requests before then; repeated requests can extend the ban by 5s up to 5 minutes.`;
|
||||
}
|
||||
|
||||
return `${message}. Received HTTP 429; retry after ${resetText}.`;
|
||||
}
|
||||
|
||||
function formatRateLimitReset(resetAtUnix: number): string {
|
||||
const resetAt = new Date(resetAtUnix * 1000);
|
||||
const remainingSeconds = Math.max(Math.ceil((resetAt.getTime() - Date.now()) / 1000), 0);
|
||||
return `${formatLocalTimestamp(resetAt)} (~${remainingSeconds}s remaining)`;
|
||||
}
|
||||
|
||||
function formatLocalTimestamp(date: Date): string {
|
||||
const year = date.getFullYear();
|
||||
const month = String(date.getMonth() + 1).padStart(2, "0");
|
||||
const day = String(date.getDate()).padStart(2, "0");
|
||||
const hours = String(date.getHours()).padStart(2, "0");
|
||||
const minutes = String(date.getMinutes()).padStart(2, "0");
|
||||
const seconds = String(date.getSeconds()).padStart(2, "0");
|
||||
const offsetMinutes = -date.getTimezoneOffset();
|
||||
const sign = offsetMinutes >= 0 ? "+" : "-";
|
||||
const absOffsetMinutes = Math.abs(offsetMinutes);
|
||||
const offsetHours = String(Math.floor(absOffsetMinutes / 60)).padStart(2, "0");
|
||||
const offsetMins = String(absOffsetMinutes % 60).padStart(2, "0");
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds} GMT${sign}${offsetHours}:${offsetMins}`;
|
||||
}
|
||||
|
||||
function sleep(ms: number): Promise<void> {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
function formatResponse(res: Response, body: string | null): string {
|
||||
const headerLines = [...res.headers.entries()].map(([k, v]) => ` ${k}: ${v}`).join("\n");
|
||||
return `[response] HTTP ${res.status}\n${headerLines}\n\n${body ?? "(no body)"}`;
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
import { Command } from "commander";
|
||||
import { OpenApiClient, CreateTokenParams } from "../client/OpenApiClient.js";
|
||||
import { getConfig } from "../config.js";
|
||||
import { exitOnError, printResult } from "../output.js";
|
||||
import { validateChain } from "../validate.js";
|
||||
|
||||
export function registerCookingCommands(program: Command): void {
|
||||
const cooking = program.command("cooking").description("Token creation and launchpad commands");
|
||||
|
||||
cooking
|
||||
.command("stats")
|
||||
.description("Get token creation statistics by launchpad (normal auth)")
|
||||
.option("--raw", "Output raw JSON")
|
||||
.action(async (opts) => {
|
||||
const client = new OpenApiClient(getConfig());
|
||||
const data = await client.getCookingStatistics().catch(exitOnError);
|
||||
printResult(data, opts.raw);
|
||||
});
|
||||
|
||||
cooking
|
||||
.command("create")
|
||||
.description("Create a token on a launchpad platform (requires private key)")
|
||||
.requiredOption("--chain <chain>", "Chain: sol / bsc / base / eth / ton")
|
||||
.requiredOption("--dex <dex>", "Launchpad: pump / raydium / pancakeswap / flap / fourmeme / bonk / bags / ...")
|
||||
.requiredOption("--from <address>", "Wallet address (must match API Key binding)")
|
||||
.requiredOption("--name <name>", "Token name")
|
||||
.requiredOption("--symbol <symbol>", "Token symbol")
|
||||
.requiredOption("--buy-amt <amount>", "Initial buy amount in native token (e.g. 0.01 SOL)")
|
||||
.option("--image <base64>", "Token logo as base64-encoded data (max 2MB decoded)")
|
||||
.option("--image-url <url>", "Token logo URL")
|
||||
.option("--website <url>", "Website URL")
|
||||
.option("--twitter <url>", "Twitter link")
|
||||
.option("--telegram <url>", "Telegram link")
|
||||
.option("--slippage <n>", "Slippage tolerance (e.g. 0.01 = 1%)", parseFloat)
|
||||
.option("--auto-slippage", "Enable automatic slippage")
|
||||
.option("--priority-fee <sol>", "Priority fee in SOL (SOL only)")
|
||||
.option("--tip-fee <amount>", "Tip fee")
|
||||
.option("--gas-price <amount>", "Gas price in wei (EVM chains)")
|
||||
.option("--anti-mev", "Enable anti-MEV protection")
|
||||
.option("--raw", "Output raw JSON")
|
||||
.action(async (opts) => {
|
||||
if (!opts.image && !opts.imageUrl) {
|
||||
console.error("[gmgn-cli] Either --image or --image-url must be provided");
|
||||
process.exit(1);
|
||||
}
|
||||
if (!opts.slippage && !opts.autoSlippage) {
|
||||
console.error("[gmgn-cli] Either --slippage or --auto-slippage must be provided");
|
||||
process.exit(1);
|
||||
}
|
||||
validateChain(opts.chain);
|
||||
const params: CreateTokenParams = {
|
||||
chain: opts.chain,
|
||||
dex: opts.dex,
|
||||
from_address: opts.from,
|
||||
name: opts.name,
|
||||
symbol: opts.symbol,
|
||||
buy_amt: opts.buyAmt,
|
||||
};
|
||||
if (opts.image) params.image = opts.image;
|
||||
if (opts.imageUrl) params.image_url = opts.imageUrl;
|
||||
if (opts.website) params.website = opts.website;
|
||||
if (opts.twitter) params.twitter = opts.twitter;
|
||||
if (opts.telegram) params.telegram = opts.telegram;
|
||||
if (opts.slippage != null) params.slippage = opts.slippage;
|
||||
if (opts.autoSlippage) params.auto_slippage = true;
|
||||
if (opts.priorityFee) params.priority_fee = opts.priorityFee;
|
||||
if (opts.tipFee) params.tip_fee = opts.tipFee;
|
||||
if (opts.gasPrice) params.gas_price = opts.gasPrice;
|
||||
if (opts.antiMev) params.is_anti_mev = true;
|
||||
const client = new OpenApiClient(getConfig(true));
|
||||
const data = await client.createToken(params).catch(exitOnError);
|
||||
printResult(data, opts.raw);
|
||||
});
|
||||
}
|
||||
+119
-1
@@ -1,5 +1,5 @@
|
||||
import { Command } from "commander";
|
||||
import { OpenApiClient, SwapParams } from "../client/OpenApiClient.js";
|
||||
import { OpenApiClient, SwapParams, StrategyCreateParams, StrategyCancelParams } from "../client/OpenApiClient.js";
|
||||
import { getConfig } from "../config.js";
|
||||
import { exitOnError, printResult } from "../output.js";
|
||||
import { validateAddress, validateChain, validatePercent, validatePositiveInt } from "../validate.js";
|
||||
@@ -24,6 +24,8 @@ export function registerSwapCommands(program: Command): void {
|
||||
.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("--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"}]\'')
|
||||
.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.percent == null && !opts.amount) {
|
||||
@@ -54,6 +56,15 @@ export function registerSwapCommands(program: Command): void {
|
||||
if (opts.gasPrice) params.gas_price = String(Math.round(parseFloat(opts.gasPrice) * 1e9));
|
||||
if (opts.maxFeePerGas) params.max_fee_per_gas = opts.maxFeePerGas;
|
||||
if (opts.maxPriorityFeePerGas) params.max_priority_fee_per_gas = opts.maxPriorityFeePerGas;
|
||||
if (opts.conditionOrders) {
|
||||
try {
|
||||
params.condition_orders = JSON.parse(opts.conditionOrders);
|
||||
} catch {
|
||||
console.error("[gmgn-cli] --condition-orders must be valid JSON");
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
if (opts.sellRatioType) params.sell_ratio_type = opts.sellRatioType;
|
||||
|
||||
const client = new OpenApiClient(getConfig(true));
|
||||
const data = await client.swap(params).catch(exitOnError);
|
||||
@@ -97,4 +108,111 @@ export function registerSwapCommands(program: Command): void {
|
||||
const data = await client.queryOrder(opts.orderId, opts.chain).catch(exitOnError);
|
||||
printResult(data, opts.raw);
|
||||
});
|
||||
|
||||
const strategy = order.command("strategy").description("Limit/strategy order management");
|
||||
|
||||
strategy
|
||||
.command("create")
|
||||
.description("Create a limit/strategy order (requires private key)")
|
||||
.requiredOption("--chain <chain>", "Chain: sol / bsc / base")
|
||||
.requiredOption("--from <address>", "Wallet address (must match API Key binding)")
|
||||
.requiredOption("--base-token <address>", "Base token contract address")
|
||||
.requiredOption("--quote-token <address>", "Quote token contract address")
|
||||
.requiredOption("--order-type <type>", "Order type: limit_order")
|
||||
.requiredOption("--sub-order-type <type>", "Sub-order type: buy_low / buy_high / stop_loss / take_profit")
|
||||
.requiredOption("--check-price <price>", "Trigger check price")
|
||||
.option("--amount-in <amount>", "Input amount (smallest unit)")
|
||||
.option("--amount-in-percent <pct>", "Input amount as a percentage (e.g. 50 = 50%)")
|
||||
.option("--limit-price-mode <mode>", "Price mode: exact / slippage (default: slippage)")
|
||||
.option("--expire-in <seconds>", "Order expiry in seconds", parseInt)
|
||||
.option("--sell-ratio-type <type>", "Sell ratio basis: buy_amount (default) / hold_amount")
|
||||
.option("--slippage <n>", "Slippage tolerance (e.g. 0.01 = 1%)", parseFloat)
|
||||
.option("--auto-slippage", "Enable automatic slippage")
|
||||
.option("--priority-fee <sol>", "Priority fee in SOL (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("--anti-mev", "Enable anti-MEV protection")
|
||||
.option("--raw", "Output raw JSON")
|
||||
.action(async (opts) => {
|
||||
if (!opts.amountIn && !opts.amountInPercent) {
|
||||
console.error("[gmgn-cli] Either --amount-in or --amount-in-percent must be provided");
|
||||
process.exit(1);
|
||||
}
|
||||
if (!opts.slippage && !opts.autoSlippage) {
|
||||
console.error("[gmgn-cli] Either --slippage or --auto-slippage must be provided");
|
||||
process.exit(1);
|
||||
}
|
||||
validateChain(opts.chain);
|
||||
const params: StrategyCreateParams = {
|
||||
chain: opts.chain,
|
||||
from_address: opts.from,
|
||||
base_token: opts.baseToken,
|
||||
quote_token: opts.quoteToken,
|
||||
order_type: opts.orderType,
|
||||
sub_order_type: opts.subOrderType,
|
||||
check_price: opts.checkPrice,
|
||||
};
|
||||
if (opts.amountIn) params.amount_in = opts.amountIn;
|
||||
if (opts.amountInPercent) params.amount_in_percent = opts.amountInPercent;
|
||||
if (opts.limitPriceMode) params.limit_price_mode = opts.limitPriceMode;
|
||||
if (opts.expireIn != null) params.expire_in = opts.expireIn;
|
||||
if (opts.sellRatioType) params.sell_ratio_type = opts.sellRatioType;
|
||||
if (opts.slippage != null) params.slippage = opts.slippage;
|
||||
if (opts.autoSlippage) params.auto_slippage = true;
|
||||
if (opts.priorityFee) params.priority_fee = opts.priorityFee;
|
||||
if (opts.tipFee) params.tip_fee = opts.tipFee;
|
||||
if (opts.gasPrice) params.gas_price = String(Math.round(parseFloat(opts.gasPrice) * 1e9));
|
||||
if (opts.antiMev) params.is_anti_mev = true;
|
||||
const client = new OpenApiClient(getConfig(true));
|
||||
const data = await client.createStrategyOrder(params).catch(exitOnError);
|
||||
printResult(data, opts.raw);
|
||||
});
|
||||
|
||||
strategy
|
||||
.command("list")
|
||||
.description("List strategy orders (normal auth)")
|
||||
.requiredOption("--chain <chain>", "Chain: sol / bsc / base")
|
||||
.option("--type <type>", "open (default) / history")
|
||||
.option("--from <address>", "Filter by wallet address")
|
||||
.option("--group-tag <tag>", "Filter by group: LimitOrder / STMix")
|
||||
.option("--base-token <address>", "Filter by token address")
|
||||
.option("--page-token <token>", "Pagination cursor from previous response")
|
||||
.option("--limit <n>", "Results per page", parseInt)
|
||||
.option("--raw", "Output raw JSON")
|
||||
.action(async (opts) => {
|
||||
validateChain(opts.chain);
|
||||
const extra: Record<string, string | number> = {};
|
||||
if (opts.type) extra["type"] = opts.type;
|
||||
if (opts.from) extra["from_address"] = opts.from;
|
||||
if (opts.groupTag) extra["group_tag"] = opts.groupTag;
|
||||
if (opts.baseToken) extra["base_token"] = opts.baseToken;
|
||||
if (opts.pageToken) extra["page_token"] = opts.pageToken;
|
||||
if (opts.limit != null) extra["limit"] = opts.limit;
|
||||
const client = new OpenApiClient(getConfig());
|
||||
const data = await client.getStrategyOrders(opts.chain, extra).catch(exitOnError);
|
||||
printResult(data, opts.raw);
|
||||
});
|
||||
|
||||
strategy
|
||||
.command("cancel")
|
||||
.description("Cancel a strategy order (requires private key)")
|
||||
.requiredOption("--chain <chain>", "Chain: sol / bsc / base")
|
||||
.requiredOption("--from <address>", "Wallet address (must match API Key binding)")
|
||||
.requiredOption("--order-id <id>", "Order ID to cancel")
|
||||
.option("--order-type <type>", "Order type: limit_order / smart_trade")
|
||||
.option("--close-sell-model <model>", "Sell model when closing")
|
||||
.option("--raw", "Output raw JSON")
|
||||
.action(async (opts) => {
|
||||
validateChain(opts.chain);
|
||||
const params: StrategyCancelParams = {
|
||||
chain: opts.chain,
|
||||
from_address: opts.from,
|
||||
order_id: opts.orderId,
|
||||
};
|
||||
if (opts.orderType) params.order_type = opts.orderType;
|
||||
if (opts.closeSellModel) params.close_sell_model = opts.closeSellModel;
|
||||
const client = new OpenApiClient(getConfig(true));
|
||||
const data = await client.cancelStrategyOrder(params).catch(exitOnError);
|
||||
printResult(data, opts.raw);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import { registerMarketCommands } from "./commands/market.js";
|
||||
import { registerPortfolioCommands } from "./commands/portfolio.js";
|
||||
import { registerTrackCommands } from "./commands/track.js";
|
||||
import { registerSwapCommands } from "./commands/swap.js";
|
||||
import { registerCookingCommands } from "./commands/cooking.js";
|
||||
|
||||
const proxy = process.env.HTTPS_PROXY ?? process.env.https_proxy
|
||||
?? process.env.HTTP_PROXY ?? process.env.http_proxy;
|
||||
@@ -58,6 +59,7 @@ registerMarketCommands(program);
|
||||
registerPortfolioCommands(program);
|
||||
registerTrackCommands(program);
|
||||
registerSwapCommands(program);
|
||||
registerCookingCommands(program);
|
||||
|
||||
program.parseAsync().catch((err) => {
|
||||
console.error(`[gmgn-cli] ${err.message}`);
|
||||
|
||||
Reference in New Issue
Block a user