Files
gmgn-skills/src/commands/track.ts
T
David LauandClaude Opus 4.8 0056fa7d6a feat(chains): add arc and stable across all commands and docs
Add EVM chains arc and stable everywhere robinhood is supported:
- src/validate.ts: add arc + stable to VALID_CHAINS and EVM address check
- src/commands/{token,market,portfolio,swap,track}.ts: extend --chain
  option help and the market-signal chain gate
- src/client/OpenApiClient.ts: HotSearchesParam chain comment
- Sync SKILL.md files, Readme.md, Readme.zh.md, docs/cli-usage.md;
  update the default hot-search set wording from 6-chain to 7-chain

Cooking/create_token remains sol/bsc/base/robinhood only (no launchpad
on arc or stable), matching the openapi service.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-29 20:22:02 +08:00

110 lines
5.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { Command } from "commander";
import { OpenApiClient } from "../client/OpenApiClient.js";
import { getConfig } from "../config.js";
import { exitOnError, printResult } from "../output.js";
import { validateChain } from "../validate.js";
export function registerTrackCommands(program: Command): void {
const track = program.command("track").description("On-chain tracking commands: follow-wallet trades, KOL trades, Smart Money trades");
track
.command("follow-tokens")
.description("Get the followed token list for a wallet on a given chain")
.requiredOption("--chain <chain>", "Chain: sol / bsc / base / eth / robinhood / arc / stable")
.requiredOption("--wallet <address>", "Wallet address")
.option("--group-id <id>", "Filter by group: all_group (all), default, or a user-defined group ID")
.option("--interval <interval>", "Time interval for price change stats (e.g. 1m, 5m, 1h, 6h, 24h)")
.option("--order-by <field>", "Sort field: created_at / swaps / volume / market_cap / liquidity / price / open_timestamp")
.option("--direction <dir>", "Sort direction: asc / desc")
.option("--limit <n>", "Page size", parseInt)
.option("--cursor <cursor>", "Pagination cursor")
.option("--search <text>", "Search by token name or address")
.option("--raw", "Output raw JSON")
.action(async (opts) => {
validateChain(opts.chain);
const extra: Record<string, string | number> = {};
if (opts.groupId) extra["group_id"] = opts.groupId;
if (opts.interval) extra["interval"] = opts.interval;
if (opts.orderBy) extra["order_by"] = opts.orderBy;
if (opts.direction) extra["direction"] = opts.direction;
if (opts.limit != null) extra["limit"] = opts.limit;
if (opts.cursor) extra["cursor"] = opts.cursor;
if (opts.search) extra["search_text"] = opts.search;
const client = new OpenApiClient(getConfig());
const data = await client.getFollowTokens(opts.chain, opts.wallet, extra).catch(exitOnError);
printResult(data, opts.raw);
});
track
.command("follow-token-groups")
.description("Get the follow token group names for a wallet on a given chain")
.requiredOption("--chain <chain>", "Chain: sol / bsc / base / eth / robinhood / arc / stable")
.requiredOption("--wallet <address>", "Wallet address")
.option("--raw", "Output raw JSON")
.action(async (opts) => {
validateChain(opts.chain);
const client = new OpenApiClient(getConfig());
const data = await client.getFollowGroupNames(opts.chain, opts.wallet).catch(exitOnError);
printResult(data, opts.raw);
});
track
.command("follow-wallet")
.description("Get follow-wallet trade records")
.requiredOption("--chain <chain>", "Chain: sol / bsc / base / eth / robinhood / arc / stable")
.option("--wallet <address>", "Filter by wallet address")
.option("--limit <n>", "Page size (1100, default 10)", parseInt)
.option("--side <side>", "Trade direction filter: buy / sell")
.option("--filter <tag...>", "Filter conditions, repeatable")
.option("--min-amount-usd <n>", "Minimum trade amount (USD)", parseFloat)
.option("--max-amount-usd <n>", "Maximum trade amount (USD)", parseFloat)
.option("--raw", "Output raw JSON")
.action(async (opts) => {
validateChain(opts.chain);
const extra: Record<string, string | number | string[]> = {};
if (opts.wallet) extra["wallet_address"] = opts.wallet;
if (opts.limit != null) extra["limit"] = opts.limit;
if (opts.side) extra["side"] = opts.side;
if (opts.filter?.length) extra["filters"] = opts.filter;
if (opts.minAmountUsd != null) extra["min_amount_usd"] = opts.minAmountUsd;
if (opts.maxAmountUsd != null) extra["max_amount_usd"] = opts.maxAmountUsd;
const client = new OpenApiClient(getConfig());
const data = await client.getFollowWallet(opts.chain, extra).catch(exitOnError);
printResult(data, opts.raw);
});
track
.command("kol")
.description("Get KOL trade records")
.requiredOption("--chain <chain>", "Chain: sol / bsc / base / eth / robinhood / arc / stable")
.option("--limit <n>", "Page size (1200, default 100)", parseInt)
.option("--side <side>", "Filter by trade direction: buy / sell (client-side filter)")
.option("--raw", "Output raw JSON")
.action(async (opts) => {
if (opts.chain) validateChain(opts.chain);
const client = new OpenApiClient(getConfig());
const data = await client.getKol(opts.chain, opts.limit).catch(exitOnError) as { list?: { side: string }[] };
if (opts.side && data?.list) {
data.list = data.list.filter((item) => item.side === opts.side);
}
printResult(data, opts.raw);
});
track
.command("smartmoney")
.description("Get Smart Money trade records")
.requiredOption("--chain <chain>", "Chain: sol / bsc / base / eth / robinhood / arc / stable")
.option("--limit <n>", "Page size (1200, default 100)", parseInt)
.option("--side <side>", "Filter by trade direction: buy / sell (client-side filter)")
.option("--raw", "Output raw JSON")
.action(async (opts) => {
if (opts.chain) validateChain(opts.chain);
const client = new OpenApiClient(getConfig());
const data = await client.getSmartMoney(opts.chain, opts.limit).catch(exitOnError) as { list?: { side: string }[] };
if (opts.side && data?.list) {
data.list = data.list.filter((item) => item.side === opts.side);
}
printResult(data, opts.raw);
});
}