---
name: gmgn-auto-trading
description: What GMGN can and cannot do on Robinhood Chain — UI and Agent API, verified 13 Sep 2026, including the CLI setup already installed on Ben's Mac. Read before designing any auto-buy, take-profit ladder, sniper or position-management bot for Ben.
sources: [cowork]
aliases: [gmgn, gmgn api, gmgn-cli, migrated snipe, auto-buy bot, tp ladder, gmgn-robinhood]
---

# GMGN on Robinhood Chain

Companion to [[trenchbuy]], [[rh-launchpad-sector]], [[data-access]]. Ben asked 13 Sep 2026 for an agent that auto-buys on graduation and takes profit in tiers across dozens of positions.

## CORRECTION, SAME SESSION — I was wrong twice, he pushed twice, he was right both times
- I first said GMGN had no socials data and implied it was not the venue. Then I said **"there is NO GMGN trading API for Robinhood Chain"**, sourced from the docs page `cooperation-api-integrate-gmgn-eth-base-bsc-trading-api` (SOL/ETH/Base/BSC, `x-route-key`, one call per 5s) and from the Agent API page's **"Supported Chains: SOL / BSC / Base"** table. **That table is STALE and it is what misled me.**
- The live spec is the installed skills and the CLI binary, not the docs site. `gmgn-cli swap --help` prints: `Chain: sol / bsc / base / eth / arbitrum / hyperevm / robinhood / arc / stable`. **Robinhood Chain is fully supported for swap, and for attached take-profit / stop-loss orders.**
- **STANDING LESSON: for GMGN, read the installed SKILL.md and `--help`, never the gitbook docs.** The docs lag the product by at least one chain generation. Same class of error as the "never assert a feature is missing without opening it" rule.

## THE AGENT API / gmgn-cli — this IS the auto-trader Ben described
- `gmgn-cli swap --chain robinhood --from <wallet> --input-token <addr> --output-token <addr> --amount <smallest unit> --slippage <0-100> --condition-orders '<json>' --sell-ratio-type hold_amount`
- **`--condition-orders` supported on sol / bsc / base / eth / robinhood. Max 10 sub-orders per swap.** Each sub-order: `order_type` = `profit_stop` | `loss_stop` | `profit_stop_trace` | `loss_stop_trace`; `side` always `"sell"`; `price_scale` = gain% or drop% from entry; `sell_ratio` = % of position; `drawdown_rate` required for the two trailing types. That is the whole laddered exit, server-side, attached at buy time.
- `--sell-ratio-type`: `buy_amount` (fixed slice of the original buy) or `hold_amount` (percentage of whatever is held at trigger time). `hold_amount` is the right default for a ladder.
- Other sub-commands: `multi-swap` (**up to 100 wallets concurrently**), `order quote` (API key only, no private key), `order get`, `order strategy create|list|cancel`, `gas-price`.
- **Rate limit is a leaky bucket, rate=20 capacity=20, swap weight 5 — about 4 swaps/second sustained.** The "one call per 5 seconds" figure in the older cooperation-API doc does NOT apply to this path.
- `--yes` (skip the confirm prompt) is **rejected unless `GMGN_ALLOW_AUTOMATED_TRADES=1`** is in the environment. That env var is the switch between human-confirmed and fully automated, and it is Ben's to set, not mine.
- **OPEN ITEM: the Chain Currencies table in gmgn-swap/SKILL.md lists base tokens only for sol/bsc/base/eth. Robinhood is not listed.** So the correct `--input-token` for a robinhood buy (WETH? USDG?) is unverified — resolve it with `gmgn-cli order quote` before any live swap, and never guess a currency address (the skill warns that a wrong one fails silently).
- Query skills need only `GMGN_API_KEY`; `swap` and every `order` sub-command except `order quote` also need `GMGN_PRIVATE_KEY`, which must match the wallet bound to the API key.

## INSTALLED ON BEN'S MAC, 13 Sep 2026
- `gmgn-cli` **v1.6.2** at `~/.npm-global/bin/gmgn-cli`. Installed with a user prefix because `npm install -g` into `/usr/local` fails EACCES on his machine; `npm config set prefix ~/.npm-global` and a PATH line appended to `~/.zshrc`. **Any future global npm install on his Mac must use that prefix.**
- 14 GMGN skills at `~/.claude/skills/gmgn-*` via `npx skills add GMGNAI/gmgn-skills --agent claude-code -y -g`. **The agent slug is `claude-code`, not `claude`** (the latter is rejected). One skill, `gmgn-wallet-score`, is skipped by the installer on a YAML parse error in its own frontmatter.
- `gmgn-cli config` generated the canonical Ed25519 pair at **`~/.config/gmgn/keypair.pem`** (chmod 600) and printed a pre-filled key-creation link `https://gmgn.ai/ai/generateapi?pbk=<urlencoded public key>`. An earlier manual openssl pair was renamed to `gmgn_private.pem.unused` / `gmgn_public.pem.unused` so only one pair is live.
- `~/.config/gmgn/.env` exists as a skeleton (chmod 600). **`gmgn-cli config --apply <api_key>` writes the API key and private key into it and verifies.** `gmgn-cli config --check` exits 0 if configured.
- **Remaining step is Ben's: open the generateapi link, create the API Key, then run `gmgn-cli config --apply <key>`.** The private key was never printed into chat and must not be.
- **THIRD CONFIRMATION of robinhood support, at the credential level, 13 Sep 2026:** in the Create API Key drawer at gmgn.ai/ai, switching **Enable Trading** on (2FA required, Ben enabled 2FA on his account that day) reveals a **Trading Wallets** list of **SOL · BSC · Base · ETH · Robinhood · Arc · Stable · Arbitrum · HyperEVM**. Robinhood is selectable as a trading wallet. It also exposes an optional **Trusted IP For Trading** field, max 5 IPv4, comma or space separated — the droplet's IP 159.223.88.168 belongs there if the bot runs from kookwatch, and the Agent API is **IPv4 only, no IPv6**.
- Create API Key drawer flow: paste public key -> Label -> Enable Reading (on by default) -> Enable Trading (2FA) -> Trusted IP -> pick Trading Wallets -> Next. Max 3 API keys per account.
- **The generateapi deep link `https://gmgn.ai/ai?pbk=...` does NOT route** (it bounced to a random token page). Go to `https://gmgn.ai/ai` and paste the public key into the drawer instead. The textarea is React-controlled: set it with the native `HTMLTextAreaElement.prototype.value` setter plus an `input` event.

## THE UI, verified on gmgn.ai/robinhood/token/{address}
- **Auto Trading tab exists on Robinhood Chain**: Migrated Snipe · Migrated Sell · Sell on Dev Sell · Buy on Dev Sell · Follow Sell >=100%.
- **Migrated Snipe "Set" is ACTIVE on a pre-graduation Pons V2 token** (tested on RWORLD 0x1dc89db90336373885f437167101c129316470c1) and greyed on an already-migrated one (PONS 0x39dBED3a2bd333467115dE45665cC57F813C4571). So auto-buy-at-graduation is native, fired inside the migration, no latency race.
- Buy tab: Market / Limit / DCA / Adv., P1/P2/P3 presets, and an **Advanced Trading Strategy** checkbox that expands to `TP x% / Sell y%` rows plus `SL -x% / Sell y%` and a **+ Add** button. Defaults shown: TP 100% sell 50%, TP 300% sell 50%, SL -50% sell 100%. Same feature as `--condition-orders`, by hand.
- Funds sit in a GMGN wallet, not a connected external wallet. That custody is what lets the ladder fire while he sleeps. State it as the tradeoff.
- Docs index is container-reachable at `https://docs.gmgn.ai/llms.txt`, every page as `.md`. gmgn.ai itself 403s the container on every path including `/` — Chrome on his Mac only.

## STILL MISSING, and it is the only thing to build
- **CORRECTION, THIRD ONE: the API DOES return socials.** The 13-field `/api/v1/token_info/robinhood/{address}` I probed unauthenticated from the browser is the WRONG endpoint. The authenticated `gmgn-cli token info --chain robinhood --address <addr> --raw` returns a large object including a **`link` block** (`twitter_username`, `website`, `telegram`, `discord`, `description`, `github`, `youtube`, `reddit`, `instagram`, `tiktok`, `medium`, `verify_status`), a **`launchpad`** name, **`image_dup_count`** (a literal copied-artwork counter), `migrated_timestamp`, `migration_market_cap`, plus `stat.*` (`top_10_holder_rate`, `top70_sniper_hold_rate`, `dev_team_hold_rate`, `fresh_wallet_rate`, `bot_degen_rate`, `top_rat_trader_percentage`, `top_bundler_trader_percentage`) and `dev.*` (creator history, `twitter_create_token_count`). **Verified live: CASHCAT 0x020bfc650a365f8bb26819deaabf3e21291018b4 returned launchpad `noxa`, twitter `cashcat_token`, website `https://cashcat.cc/`, telegram, image_dup_count 5, top10 26.69%.** PONS and PAIR return empty link blocks, so coverage is not universal: **cross-check against the token's own on-chain `socials()` before treating an empty link block as "no socials".**
- `/defi/quotation/v1/rank/robinhood/...` answers `network not supported`. Trenches is websocket-fed with no REST list endpoint found. Those two gaps stand.
- **LIVE AS OF 13 Sep 2026: API key applied and working.** `gmgn-cli config --apply` reported "Configuration saved, but the verification request failed" — that message is harmless, it was an IP rate-limit ban from the verification attempt itself, and `token info` worked once the ban expired. `gmgn-cli config --check` exits 0.
- **RATE LIMIT BAN IS REAL AND ESCALATING:** HTTP 429 `RATE_LIMIT_BANNED`, "repeated requests can extend the ban by 5s up to 5 minutes". Space calls by ~8s when probing by hand. Never put a retry loop in front of it.
- CLI command set: `token` · `market` · `portfolio` · `track` (follow-wallet / KOL / smart-money trades) · `swap` · `multi-swap` · `order` · `gas-price` · `cooking` (token creation and launchpad commands) · `config`. `token info` needs `--address`, not `--token`.
- **Nothing decides WHICH coins to buy.** Migrated Snipe is per token, armed by hand. There is no rule engine.
- So the build is: droplet watches Pons V2 `TokenLaunched` (factory 0x7ed598bcef8bd9edd8c97a195c6d13f40801ec7e), reads `socials()` (selector 0x53cd512a), applies the website / twitter / original / not-a-copy gates from [[rh-launchpad-sector]], and fires `gmgn-cli swap --chain robinhood` with the ladder attached. **No browser automation needed any more** — that was the pre-correction design and it is superseded.
- Ben's own sizing and the `GMGN_ALLOW_AUTOMATED_TRADES=1` switch stay his decisions. Claude does not execute the trades or handle the private key; the standing boundary in [[trenchbuy]] still holds.

## token_kline PAGINATION — CORRECTION, 13 Sep 2026
- **`GET /v1/market/token_kline` ignores `to`, `from`, `end` and `before` entirely.** Passing any of them returns `{"code":0,"data":{"list":[]},"message":"success"}` — an empty list with a success code, which is what made the earlier "it paginates backwards with to=<oldest>" claim look plausible. It does not paginate. **The parameter that works is `limit`.** One call with `limit=1500` returns the token's whole life: verified on BLAST, 562 rows of 30s candles spanning 05:18 to 09:59 UTC.
- Default page size is 100 rows, which is why 30s looked like it only had 50 minutes of history. `limit` is capped by the token's actual age, not by a server maximum — `limit=5000` and `limit=1000` both returned the same 562 rows.
- Route is "exist auth": header `X-APIKEY` only, plus query `chain`, `address`, `resolution`, `limit`, `timestamp`, `client_id`. No Ed25519 signature. Host `https://openapi.gmgn.ai`.
- Lesson, same class as the gitbook one: when a paging parameter returns an empty success, test the alternatives before building retry logic around it.

## RUNNER BOARDS on the droplet, 13 Sep 2026
- Two pages served from `/opt/launchwatch/pub/` on 159.223.88.168, sharing `board.css` and `common.js`.
  - `runners.html` — own candles from `klines.py` via GMGN, lightweight-charts, market-cap y-axis, timeframe picker 30s/1m/5m/15m. This is the reliable one.
  - `ds.html` — DexScreener embeds, for drawing tools and indicators.
- **DexScreener embed at tile width uses a TABBED layout and opens on the Info tab** — that is the banner Ben kept seeing, not a loading failure. `chartDefaultOnMobile=1` makes Chart the default tab; `trades=0` drops the Txns tab. **`info=0&trades=0` is a dead route on Robinhood Chain: it hangs on "Loading pair…" forever, top-level as well as framed.** The desktop side-by-side layout only appears above ~1024px, so it is unreachable in a tile.
- **lightweight-charts always draws its time axis in UTC.** The only fix is to add the offset to every candle timestamp: `t + (-new Date().getTimezoneOffset()*60)`. Ben is in Bangkok, so the axis must read UTC+7.
- Moving a DOM node that contains an iframe reloads the iframe, so tiles are appended once and reordered with CSS `order`. Fullscreen uses `position:fixed` on the tile in place for the same reason.
- `navigator.clipboard` does not exist on plain http; the hidden-textarea + `execCommand('copy')` fallback is what works.
- Board features as of 13 Sep: sound alert on crossing the line (Web Audio, needs one click to unlock), threshold picker 300K/400K/500K, fresh crossings jump to the front and pulse, X to remove a tile, restore button, fullscreen toggle, Run DD button.
- `runwatch` config `runup_mcap` lowered 500000 -> 400000 -> **250000** (13 Sep 2026, Ben's instruction), `poll_seconds` 12. The board's alert dropdown offers 250K/300K/400K/500K and defaults to 250K.

## RUN DD BUTTON -> XDECK, 13 Sep 2026
- Pressing **Run DD** on a runner tile now does two things at once: it POSTs to xdeck and drops **two columns** on the deck immediately, and it copies the research prompt for Claude.
  - Column 1, the dev's own account: `type: "account"`, `handles: [handle]`, `no_replies: false` so his REPLIES are included, not just his posts.
  - Column 2, the project talk: `type: "raw"`, query `($TICKER OR #TICKER OR <full contract address> OR @handle) -filter:retweets` — what people say about it, plus everything said TO the account.
- The X handle is resolved server-side. **The GMGN route is `GET https://openapi.gmgn.ai/v1/token/info` — NOT `/v1/market/token_info`, which 404s.** It returns `link.twitter_username` plus `dev`, `stat`, `holder_count`, `image_dup_count`, `launchpad`, `migration_market_cap`. Verified: BLAST 0x724a163f0081fa1771590dda9b55edde2c5b823a -> `experimentblast`; THRONE 0x72dc556fff14115c077a540921e5f35499a4dca7 -> `32thrones`. Fallback if empty is the token's on-chain `socials()` (selector 0x53cd512a).
- Endpoint: `POST http://159.223.88.168:8088/api/dd?dk=<dd_key>` body `{addr, sym}`. It has its own narrow key in `/opt/xdeck/config.json` (`dd_key`) that can ONLY append these two columns, never read or rewrite the deck; `/api/config` still needs `ui_key`. xdeck now sends CORS headers and answers OPTIONS, because the board is on :80 and xdeck on :8088.
- Keeps the last three DD pairs (ids prefixed `dd`), drops older ones, never touches Ben's own columns. Deck cap is 24.
- **EXPOSURE, unresolved: the runners board on :80 has no password, and the page source now carries both the xdeck `dd_key` and the `ui_key` (for the "open deck" link). Anyone who can reach 159.223.88.168 can open and rewrite his deck.** Offer to put basic auth on the board.

## DD PROMPT, question 5 — his correction, 13 Sep 2026
- Ben: DD runs in the FIRST HOUR OR TWO. "There's no trust, there's no audience, and there's no visibility before we're front-running all of that." So question 5 must NOT be answered with day-one usage numbers.
- The question is: **will people use this, do people need this, does this make sense, is there product market fit** — answered from what is observable at t=0. Verdict one of NO DEMAND / DEMAND ALREADY CAPTURED / DEMAND EXISTS UNSERVED / PMF PROVEN. First flickers of usage are a bonus, never the answer.

## CLAUDE DESKTOP DEEP LINK — the supported way to start a Cowork session with a prompt, 13 Sep 2026
- **`claude://cowork/new?q=<url-encoded prompt>&source=desktop_action`** opens the Claude desktop app on a NEW Cowork session with the prompt already loaded. Found by reading `/Applications/Claude.app/Contents/Resources/app.asar`: the handler routes it to `/task/new?q=...`, and the Finder Quick Action uses the same route with `file=` and `folder=`.
- **`q` is sliced to 14336 characters** by the app (constant `Gk`). Board sends at most 14000. Also accepts repeated `folder=` and `file=` params to attach them.
- Other live routes in the same handler: `claude://cowork/shared-artifact?uuid=`, `claude://claude.ai/new?surface=chat`, `claude://code/new`, `claude://code/continue?session=last`, `claude://resume`.
- **The session takes its name from the first line of the prompt**, so the DD prompt now opens with `DD: $TICKER  (Robinhood Chain)` and the session is findable by ticker.
- **DEAD ENDS, do not retry:** the remote-devices computer-use bridge refuses to control Claude itself (`computer_resolve_access` on "Claude" returns no app, policy-denied), and `osascript` on his Mac has no assistive access (error -1719), so UI automation of the Claude app is not available from a session. The deep link replaces the whole idea of a local keystroke daemon.

## DD COLUMN RESOLVER — two bugs found and fixed, 13 Sep 2026
- **A twitter link can be a SEARCH url.** 奶蛙 (Milk Frog) had `link.twitter = x.com/search?q=$Milk%20Frog`, and taking the last path segment produced the column `@search?q=$Milk%20Frog`. Handles are now validated (`^[A-Za-z0-9_]{1,15}$`) and X reserved paths rejected: search, hashtag, i, intent, home, explore, share, compose, messages, notifications, settings, login, signup, status, web, privacy, tos.
- **A non-ASCII ticker stripped to nothing**, so the talk column lost the cashtag and fell back to the contract prefix. Cashtags are ASCII only, so a symbol like 奶蛙 is now quoted as a phrase and the GMGN `name` ("Milk Frog") is added as a second phrase.
- **`twitter_username` is often empty even when the project has an X account, and GMGN's record CHANGES:** BLAST 0x724a163f0081fa1771590dda9b55edde2c5b823a returned `experimentblast` in the morning and `""` an hour later, while keeping website `https://www.experimentblast.com/` and telegram `t.me/experimentblast`. When there is no handle, the domain and the telegram name go into the talk column as search terms instead. **Never invent a handle from the domain** even when they obviously match.
- The on-chain `socials()` fallback is mostly dead from the droplet: `https://rpc.mainnet.chain.robinhood.com` answers **HTTP 403** to it (WAF). GMGN is the working source.

## DD SPEED AND DRIP-FEED, 13 Sep 2026 — Ben: "the due diligence is often very time-sensitive"
- Two asks: make it faster, and drip-feed partial results, but "I don't want to get some results that then later turn out to be wrong, so it needs to be revealed in a way that I can rely on what I see."
- **The mechanism for speed: the ten questions are INDEPENDENT, so answering them in sequence makes the wall clock the SUM of ten look-ups when it only needs to be the slowest one.** Fan them out in parallel. And most coins die on a single fact, so a kill screen runs first and stops everything when it fires.
- **The mechanism for safe partials: order the output by REVERSIBILITY, not by speed.** A look-up (who deployed it, does the site load, what the name means, is there a GitHub) can never be overturned by more work, so it is safe to show immediately. A count (holders, buy/sell, buybacks in dollars) firms up over minutes, so it ships with its current value and a label. A judgement (moat, demand, buy/hold/avoid) is the only thing that can flip, so it is never shown before the end. **The one exception: a disqualifying fact IS a final verdict the moment it is found**, because nothing later reverses it.
- Shipped as four passes in the prompt: PASS 0 kill screen (~30s: impostor contract by ticker search, deployer's prior launches, scraped socials; if any fires print AVOID with the one receipt and STOP), PASS 1 settled facts (~2 min, questions 0/1/2/3/4/6 all in parallel), PASS 2 counts (holder trend, buy vs sell, dated shipping list, buyback dollars), PASS 3 judgement (5, 9, 10 and the verdict).
- Every line is labelled `[SETTLED]` / `[COUNTING]` / `[CALL]`, no verdict before pass 3 except the kill screen, and any later contradiction must open with **CORRECTION**, name the exact line and say which number misled him. Prompt is now ~7,800 chars, well under the 14,336 deep-link cap.

## THE REAL "COLUMNS ARE NOT BEING ADDED" BUG, 13 Sep 2026
- Ben reported Run DD was not adding the columns. **It was adding them correctly the whole time.** `/api/dd` returned 200 with both column titles from his own browser, in 431 ms. The bug was on the deck page: `index.html` calls `loadCfg()` exactly ONCE at startup and never re-reads `/api/config`, so a column added while the deck was open stayed invisible until a manual reload.
- Fix: a `CFGV` counter on the server, bumped by `dd_columns`, `dd_remove` and the hand-edit `/api/config` POST, returned on every `/api/poll`; the page keeps `let CFGV=-1` and calls `loadCfg()` when the value moves.
- **Second half of the same bug: `/api/poll` is a 25-second LONG POLL that only returns early when a tweet arrives.** So a config change could sit invisible for up to 25s. The wait loop now also breaks on `CFGV != cfgv0`, and every config change does `CV.notify_all()` to wake the waiter. Measured after the fix: removal visible on an open deck in 1.75 s, addition in 0.75 s.
- **Closing a tile now removes that token's two columns** (`POST /api/dd` with `{"remove": true, addr, sym}`), because Ben closing the chart means he has decided not to enter. The X button on both boards calls it before killing the tile.
- Columns are prepended, so a fresh DD always lands leftmost. That is deliberate: while the research runs, Ben reads the X columns to decide whether to buy.

## XDECK SAVE RACE — silent column loss, found and fixed 13 Sep 2026
- **The deck page saves by POSTing its ENTIRE column list.** So any save from a page holding a stale list silently DELETED every column added since that page last loaded. Caught live: THRONE was added by Run DD, the new per-column X then posted the page's stale list, and both THRONE columns disappeared from the server. This would have hit any deck edit (rename, resize, sound) made in the seconds after a DD landed.
- Fix 1: deleting one column uses a targeted `POST /api/column/delete {id}` that only removes that id, instead of rewriting the whole list.
- Fix 2: every full `/api/config` POST now carries the `cfgv` the page last saw. A mismatch returns **409 stale** with the current config, and the page reloads and toasts "deck refreshed, redo that edit" rather than overwriting.
- Verified after the fix: DD columns arrive on an open deck in under 200 ms, deleting one leaves the other nine untouched, and page and server lists match exactly.
- **Gotcha that cost a broken restart:** `do_POST` already contained a `global CFGV` further down, so reading CFGV in the new stale-guard above it raised `SyntaxError: name 'CFGV' is used prior to global declaration`. The declaration now sits at the top of `do_POST`.
- Talk query is **cashtag and contract only**, no hashtag: `($BLAST OR 0x724a163f0081fa1771590dda9b55edde2c5b823a OR @handle) -filter:retweets`. Ben, 13 Sep 2026: "I only want $ and then the project name, not the hashtag. The contract ID is okay."
- Every deck column now has an X in its header (far right, next to the gear) that deletes it.
