mirror of
https://github.com/GMGNAI/gmgn-skills.git
synced 2026-08-16 17:48:06 +00:00
Add robinhood as a supported chain across the CLI and skills, and stop advertising chains that are not publicly supported yet. robinhood: - validate.ts: add to VALID_CHAINS and treat as EVM for address checks - reject guards for commands that do not support it: track kol, track smartmoney, cooking create (mirrors market signal) - --chain help updated for supported commands (token/portfolio/swap/ market kline·trending·trenches·hot-searches/track follow-*/order get/ gas-price); OpenApiClient chain comment - docs: all SKILL.md, Readme.md, Readme.zh.md (split-out row for unsupported commands), cli-usage.md, plugin/marketplace descriptions disable monad/megaeth/hyperevm/tron: - comment monad out of VALID_CHAINS (kept for quick re-enable) - strip these chains from all --chain help, SKILL.md, cli-usage.md (hot-searches default now 4 chains: sol/bsc/base/eth) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
135 lines
6.5 KiB
TypeScript
135 lines
6.5 KiB
TypeScript
import { Command } from "commander";
|
|
import { OpenApiClient } from "../client/OpenApiClient.js";
|
|
import { getConfig } from "../config.js";
|
|
import { exitOnError, printResult } from "../output.js";
|
|
import { validateAddress, validateChain } from "../validate.js";
|
|
|
|
export function registerPortfolioCommands(program: Command): void {
|
|
const portfolio = program.command("portfolio").description("Wallet portfolio commands");
|
|
|
|
portfolio
|
|
.command("holdings")
|
|
.description("Get wallet token holdings")
|
|
.requiredOption("--chain <chain>", "Chain: sol / bsc / base / eth / robinhood")
|
|
.requiredOption("--wallet <address>", "Wallet address")
|
|
.option("--limit <n>", "Page size (default 20, max 50)", parseInt, 20)
|
|
.option("--cursor <cursor>", "Pagination cursor")
|
|
.option("--order-by <field>", "Sort field: usd_value / last_active_timestamp / realized_profit / unrealized_profit / total_profit / history_bought_cost / history_sold_income", "usd_value")
|
|
.option("--direction <dir>", "Sort direction: asc / desc", "desc")
|
|
.option("--interval <interval>", "Stats interval (default 24h)")
|
|
.option("--hide-abnormal <bool>", "Hide abnormal positions (default: false)", "false")
|
|
.option("--hide-airdrop <bool>", "Hide airdrop positions (default: true)", "true")
|
|
.option("--hide-closed <bool>", "Hide closed positions (default: true)", "true")
|
|
.option("--hide-open", "Hide open positions")
|
|
.option("--tx30d", "Only show positions with trades in last 30 days")
|
|
.option("--raw", "Output raw JSON")
|
|
.action(async (opts) => {
|
|
validateChain(opts.chain);
|
|
validateAddress(opts.wallet, opts.chain, "--wallet");
|
|
const extra: Record<string, string | number> = {};
|
|
if (opts.limit != null) extra["limit"] = opts.limit;
|
|
if (opts.cursor) extra["cursor"] = opts.cursor;
|
|
if (opts.orderBy) extra["order_by"] = opts.orderBy;
|
|
if (opts.direction) extra["direction"] = opts.direction;
|
|
if (opts.interval) extra["interval"] = opts.interval;
|
|
extra["hide_abnormal"] = opts.hideAbnormal;
|
|
extra["hide_airdrop"] = opts.hideAirdrop;
|
|
extra["hide_closed"] = opts.hideClosed;
|
|
if (opts.hideOpen) extra["hide_open"] = "true";
|
|
if (opts.tx30d) extra["tx30d"] = "true";
|
|
|
|
const client = new OpenApiClient(getConfig());
|
|
const data = await client.getWalletHoldings(opts.chain, opts.wallet, extra).catch(exitOnError);
|
|
printResult(data, opts.raw);
|
|
});
|
|
|
|
portfolio
|
|
.command("activity")
|
|
.description("Get wallet transaction activity")
|
|
.requiredOption("--chain <chain>", "Chain: sol / bsc / base / eth / robinhood")
|
|
.requiredOption("--wallet <address>", "Wallet address")
|
|
.option("--token <address>", "Filter by token contract address")
|
|
.option("--limit <n>", "Page size", parseInt)
|
|
.option("--cursor <cursor>", "Pagination cursor")
|
|
.option("--type <type...>", "Activity type filter, repeatable: buy / sell / transferIn / transferOut / add / remove")
|
|
.option("--raw", "Output raw JSON")
|
|
.action(async (opts) => {
|
|
validateChain(opts.chain);
|
|
validateAddress(opts.wallet, opts.chain, "--wallet");
|
|
if (opts.token) validateAddress(opts.token, opts.chain, "--token");
|
|
const extra: Record<string, string | number | string[]> = {};
|
|
if (opts.token) extra["token_address"] = opts.token;
|
|
if (opts.limit != null) extra["limit"] = opts.limit;
|
|
if (opts.cursor) extra["cursor"] = opts.cursor;
|
|
if (opts.type?.length) extra["type"] = opts.type;
|
|
|
|
const client = new OpenApiClient(getConfig());
|
|
const data = await client.getWalletActivity(opts.chain, opts.wallet, extra).catch(exitOnError);
|
|
printResult(data, opts.raw);
|
|
});
|
|
|
|
portfolio
|
|
.command("stats")
|
|
.description("Get wallet trading statistics (supports multiple wallets)")
|
|
.requiredOption("--chain <chain>", "Chain: sol / bsc / base / eth / robinhood")
|
|
.requiredOption("--wallet <address...>", "Wallet address(es), repeatable")
|
|
.option("--period <period>", "Stats period: 7d / 30d", "7d")
|
|
.option("--raw", "Output raw JSON")
|
|
.action(async (opts) => {
|
|
validateChain(opts.chain);
|
|
for (const w of opts.wallet as string[]) validateAddress(w, opts.chain, "--wallet");
|
|
const client = new OpenApiClient(getConfig());
|
|
const data = await client.getWalletStats(opts.chain, opts.wallet, opts.period).catch(exitOnError);
|
|
printResult(data, opts.raw);
|
|
});
|
|
|
|
portfolio
|
|
.command("info")
|
|
.description("Get wallets and main currency balances bound to the API Key")
|
|
.option("--raw", "Output raw JSON")
|
|
.action(async (opts) => {
|
|
const client = new OpenApiClient(getConfig());
|
|
const data = await client.getUserInfo().catch(exitOnError);
|
|
printResult(data, opts.raw);
|
|
});
|
|
|
|
portfolio
|
|
.command("token-balance")
|
|
.description("Get wallet token balance for a single token")
|
|
.requiredOption("--chain <chain>", "Chain: sol / bsc / base / eth / robinhood")
|
|
.requiredOption("--wallet <address>", "Wallet address")
|
|
.requiredOption("--token <address>", "Token contract address")
|
|
.option("--raw", "Output raw JSON")
|
|
.action(async (opts) => {
|
|
validateChain(opts.chain);
|
|
validateAddress(opts.wallet, opts.chain, "--wallet");
|
|
validateAddress(opts.token, opts.chain, "--token");
|
|
const client = new OpenApiClient(getConfig());
|
|
const data = await client.getWalletTokenBalance(opts.chain, opts.wallet, opts.token).catch(exitOnError);
|
|
printResult(data, opts.raw);
|
|
});
|
|
|
|
portfolio
|
|
.command("created-tokens")
|
|
.description("Get tokens created by a developer wallet")
|
|
.requiredOption("--chain <chain>", "Chain: sol / bsc / base / eth / robinhood")
|
|
.requiredOption("--wallet <address>", "Developer wallet address")
|
|
.option("--order-by <field>", "Sort field: market_cap / token_ath_mc")
|
|
.option("--direction <dir>", "Sort direction: asc / desc")
|
|
.option("--migrate-state <state>", "Filter: migrated / non_migrated")
|
|
.option("--raw", "Output raw JSON")
|
|
.action(async (opts) => {
|
|
validateChain(opts.chain);
|
|
validateAddress(opts.wallet, opts.chain, "--wallet");
|
|
const extra: Record<string, string | number> = {};
|
|
if (opts.orderBy) extra["order_by"] = opts.orderBy;
|
|
if (opts.direction) extra["direction"] = opts.direction;
|
|
if (opts.migrateState) extra["migrate_state"] = opts.migrateState;
|
|
const client = new OpenApiClient(getConfig());
|
|
const data = await client.getCreatedTokens(opts.chain, opts.wallet, extra).catch(exitOnError);
|
|
printResult(data, opts.raw);
|
|
});
|
|
|
|
}
|
|
|