robinhood-chain-x402 0.6.0 โ†’ 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,220 +1,245 @@
1
- # robinhood-chain-x402
2
-
3
- [![npm version](https://img.shields.io/npm/v/robinhood-chain-x402?style=flat-square)](https://www.npmjs.com/package/robinhood-chain-x402)
4
- [![npm downloads](https://img.shields.io/npm/dm/robinhood-chain-x402?style=flat-square)](https://www.npmjs.com/package/robinhood-chain-x402)
5
- [![TypeScript](https://img.shields.io/badge/TypeScript-5+-blue?style=flat-square&logo=typescript&logoColor=white)](https://www.typescriptlang.org/)
6
- [![License: MIT](https://img.shields.io/badge/License-MIT-blue?style=flat-square)](LICENSE)
7
-
8
- > ๐Ÿ“‚ **[Examples](./examples/)** ยท ๐Ÿ“š **[API docs](https://madeonsol.com/api-docs)** ยท ๐Ÿค– **[Robinhood Chain](https://madeonsol.com/robinhood)** ยท ๐Ÿ’ฐ **[Free API key](https://madeonsol.com/pricing)**
9
-
10
- **TypeScript SDK for the [MadeOnSol](https://madeonsol.com/robinhood) Robinhood Chain API โ€” EVM-native on-chain trading intelligence for Robinhood Chain, chain id 4663.**
11
-
12
- Robinhood Chain is an Arbitrum Orbit L2. This SDK gives you the same intel you get on Solana โ€” live KOL trades, a DEX trade tape, token discovery, launch-bundle detection, early-buyer quality, deployer reputation and smart-money wallets โ€” but EVM-native: lowercase `0x` addresses, `eth_amount`, `tx_hash`, `block_number`, `net_flow_eth`. Data comes from our **self-hosted RHC node**. The KOLโ†’EVM mapping is recovered by tracing each Solana KOL's bridge deposits (deBridge / Relay / Mayan / Wormhole) โ€” a dataset unique to MadeOnSol.
13
-
14
- RHC coverage is **bundled into every tier at no extra cost** โ€” same API key, same base URL. Get a free key (200 req/day, no card) at [madeonsol.com/pricing](https://madeonsol.com/pricing).
15
-
16
- > **Key-mode only.** This package authenticates with an `msk_` Bearer API key and calls the keyed Robinhood Chain v1 routes listed below. A keyless x402 pay-per-call rail for Robinhood Chain **does** exist โ€” a deliberately narrow 6-endpoint subset with dual-accept payment (USDG on Robinhood Chain, or USDC on Solana), discoverable at [`/api/x402/rhc`](https://madeonsol.com/api/x402/rhc) and documented at [madeonsol.com/robinhood/x402](https://madeonsol.com/robinhood/x402) โ€” but the EVM signing path is **not** bundled in this SDK yet; agents pay it directly from the self-describing 402 challenge. For keyless USDC-per-call on the Solana API, use [`madeonsol-x402`](https://www.npmjs.com/package/madeonsol-x402).
17
-
18
- > **New in 0.6.0 โ€” wallet intelligence.** Ten new operations covering the Robinhood Chain wallet surface, which had no SDK binding at all until now: `wallet()` (90-day profile with reputation flags), `walletPnl()` (FIFO PnL with daily curve, closed and open positions), `walletPositions()` (open book marked to market), `walletTrades()` (per-wallet keyset-paginated tape), plus the watchlist โ€” `walletTrackerList()`, `walletTrackerAdd()`, `walletTrackerRemove()`, `walletTrackerRelabel()`, `walletTrackerTrades()` and `walletTrackerSummary()`. Everything is **ETH**-denominated, and cost basis is FIFO over a rolling 90-day window โ€” `cost_basis_observable_from` names the date the window opens, so a position opened before it reads as a sell with no matching buy. The profile / PnL / positions trio shares ONE snapshot cache server-side, so calling all three on an address costs roughly one computation rather than three; `cache_hit` says which call paid for it. Watchlist quotas are **per chain** (PRO 50 / ULTRA 100 / BUSINESS 500 RHC wallets), independent of your Solana list. Dependency ranges are now bounded to the versions actually tested (`@x402/*` `^2.x`, `@solana/kit` `^5.5.1`) instead of open-ended `>=0.0.1`, and the lazily-imported x402 peers are marked optional โ€” a keyed install no longer pulls the whole Solana stack.
19
-
20
- ## Install
21
-
22
- ```bash
23
- npm install robinhood-chain-x402
24
- ```
25
-
26
- > Zero required runtime dependencies. The live stream will use the optional [`ws`](https://www.npmjs.com/package/ws) package on Node when present (recommended on Node โ‰ฅ 22 for a clean process exit); the browser uses the native `WebSocket`.
27
-
28
- ## Quick start (10 seconds)
29
-
30
- ```ts
31
- import { createClient } from "robinhood-chain-x402";
32
-
33
- const client = createClient("msk_your_api_key_here"); // free tier at madeonsol.com/pricing
34
-
35
- // Live KOL buys/sells on Robinhood Chain (chain id 4663)
36
- const { trades } = await client.kolFeed({ limit: 10, action: "buy" });
37
- console.log(trades);
38
- ```
39
-
40
- ### Advanced initialization
41
-
42
- ```ts
43
- import { RobinhoodChainX402 } from "robinhood-chain-x402";
44
-
45
- const client = new RobinhoodChainX402({
46
- apiKey: "msk_...",
47
- baseUrl: "https://madeonsol.com", // optional override
48
- });
49
- ```
50
-
51
- ## Endpoints โ€” all 41 Robinhood Chain routes
52
-
53
- Every method maps 1:1 to an /api/v1/rhc/โ€ฆ route. Fields are EVM-native. Everything is a GET except the four rule engines at the bottom, which are full CRUD.
54
-
55
- ### KOL intelligence
56
-
57
- | Method | Route | Tier | Description |
58
- |---|---|---|---|
59
- | `kolFeed(params?)` | `/api/v1/rhc/kol/feed` | BASIC | Real-time KOL trade feed โ€” every buy/sell from tracked KOLs' verified EVM wallets, enriched with MC/peak and `mc_multiple_since_trade` |
60
- | `kolLeaderboard(params?)` | `/api/v1/rhc/kol/leaderboard` | BASIC | KOLs ranked by trade count then net ETH flow over `24h`/`7d`/`30d` |
61
- | `kolHotTokens(params?)` | `/api/v1/rhc/kol/hot-tokens` | BASIC | Consensus tokens bought by 2+ distinct KOLs in the window |
62
- | `kol(wallet)` | `/api/v1/rhc/kol/{wallet}` | BASIC | Single KOL profile โ€” aggregate stats + 50 most recent trades |
63
-
64
- ### DEX trade tape
65
-
66
- | Method | Route | Tier | Description |
67
- |---|---|---|---|
68
- | `trades(params?)` | `/api/v1/rhc/trades` | PRO+ | Every Uniswap v2/v3/v4 swap with the effective `trader_eoa`, gas/ordering for MEV, and KOL/deployer flags |
69
-
70
- > **`trader_eoa` is the effective trading account**, not simply `tx.from`. On an ordinary transaction it *is* `tx.from`; when the trade was bundled through ERC-4337 it is the userOp sender (`UserOperationEvent`), never the bundler that relayed it. It is still an EOA either way โ€” on Robinhood Chain a userOp sender is a normal EOA carrying an EIP-7702 delegation. Use `trader` only for the swap-log recipient (the router on aggregated swaps).
71
-
72
- ### Token discovery + intelligence
73
-
74
- | Method | Route | Tier | Description |
75
- |---|---|---|---|
76
- | `tokens(params?)` | `/api/v1/rhc/tokens` | PRO+ | Live-priced token discovery โ€” MC, liquidity, peak MC + drawdown, launchpad, deployer tier |
77
- | `token(address)` | `/api/v1/rhc/tokens/{address}` | BASIC | Full token snapshot โ€” price/MC/FDV, graduation, deployer block, KOL activity, pools |
78
- | `tokenCandles(address, params?)` | `/api/v1/rhc/tokens/{address}/candles` | PRO+ | 1-minute OHLC candles โ€” price + MC OHLC, close liquidity, volume with buy/sell split |
79
- | `tokenKolConsensus(address)` | `/api/v1/rhc/tokens/{address}/kol-consensus` | PRO+ | KOL positioning โ€” buyers vs sellers, exit rate, `net_flow_eth`, median entry MC (ULTRA adds wallet lists) |
80
- | `tokenBuyerQuality(address)` | `/api/v1/rhc/tokens/{address}/buyer-quality` | BASIC | 0โ€“100 early-buyer quality with bundle-buyer + dump-cluster legs |
81
- | `tokenBundle(address)` | `/api/v1/rhc/tokens/{address}/bundle` | BASIC | Launch-bundle detection (`same_block`) + how much the cohort still holds |
82
- | `tokenTopTraders(address, params?)` | `/api/v1/rhc/tokens/{address}/top-traders` | PRO+ | Traders ranked by REALIZED ETH (`sell โˆ’ buy`) โ€” **not PnL**; a wallet still holding ranks last |
83
- | `tokenFlow(address, window?)` | `/api/v1/rhc/tokens/{address}/flow` | PRO+ | Net flow split by cohort (kol โ†’ bot โ†’ dump_cluster โ†’ early_buyer โ†’ โ€ฆ); positive `net_eth` means that cohort distributed |
84
- | `tokenPeakHistory(address, params?)` | `/api/v1/rhc/tokens/{address}/peak-history` | PRO+ | Two peaks because they disagree โ€” `peak_mc_usd_recorded` (stored high-water) vs `peak_mc_usd_observed` (candle highs) |
85
- | `tokenRisk(address)` | `/api/v1/rhc/tokens/{address}/risk` | PRO+ | EVM-native risk computed live on-chain โ€” proxy upgradeability, LP custody, simulated **sellability** (never cached) |
86
- | `tokenHolders(address, params?)` | `/api/v1/rhc/tokens/{address}/holders` | PRO+ | Exact holder set from `Transfer` logs + concentration. **Check `verified` first**; `balance` is a raw uint256 string |
87
-
88
- ### Deployer hunter + smart money
89
-
90
- | Method | Route | Tier | Description |
91
- |---|---|---|---|
92
- | `deployerLeaderboard(params?)` | `/api/v1/rhc/deployer-hunter/leaderboard` | BASIC | 99k+ deployers ranked by reputation โ€” `graduation_rate` ($40K+ peak MC), `runner_rate` ($100K+) |
93
- | `deployer(address)` | `/api/v1/rhc/deployer-hunter/{address}` | BASIC | Single deployer profile + 50 most recent tokens (unknown wallets โ†’ `is_deployer: false`) |
94
- | `alphaWallets(params?)` | `/api/v1/rhc/alpha-wallets` | PRO+ | Smart-money wallets ranked by realized performance โ€” `net_eth`, `win_rate`, `memecoin_share`, `likely_bot` |
95
-
96
- ### Rule engines โ€” push, not polling
97
-
98
- Four server-side rule engines that watch the RHC tape for you and deliver over webhook or WebSocket. **Every quota is per chain** โ€” configuring RHC rules never consumes your Solana budget. A `webhook_secret` is returned exactly once on create; payloads are signed HMAC-SHA256 over `` `<timestamp>.<body>` `` in the `X-MadeOnSol-Signature` header.
99
-
100
- | Method | Route | Tier | Description |
101
- |---|---|---|---|
102
- | `copyTradeList()` | `GET /api/v1/rhc/copytrade/subscriptions` | PRO+ | Your copy-trade rules |
103
- | `copyTradeCreate(params)` | `POST /api/v1/rhc/copytrade/subscriptions` | PRO+ | Follow up to 250 wallets; sizes are **ETH**, and there is no MC band (the RHC notify payload carries no market cap) |
104
- | `copyTradeGet(id)` | `GET /api/v1/rhc/copytrade/subscriptions/{id}` | PRO+ | One rule (numeric id) |
105
- | `copyTradeUpdate(id, params)` | `PATCH /api/v1/rhc/copytrade/subscriptions/{id}` | PRO+ | Partial update; the wallet cap is re-checked so a rule cannot be PATCHed past its tier |
106
- | `copyTradeDelete(id)` | `DELETE /api/v1/rhc/copytrade/subscriptions/{id}` | PRO+ | Delete a rule (signals cascade) |
107
- | `copyTradeSignals(params?)` | `GET /api/v1/rhc/copytrade/signals` | PRO+ | Fire history โ€” the catch-up path for a missed webhook. Retained 7 days |
108
- | `priceAlertsList()` | `GET /api/v1/rhc/price-alerts` | PRO+ | Your price alerts |
109
- | `priceAlertsCreate(params)` | `POST /api/v1/rhc/price-alerts` | PRO+ | Baseline MC is captured at creation; token must already be tracked with an MC |
110
- | `priceAlertsGet(id)` | `GET /api/v1/rhc/price-alerts/{id}` | PRO+ | One alert (numeric id) |
111
- | `priceAlertsUpdate(id, params)` | `PATCH /api/v1/rhc/price-alerts/{id}` | PRO+ | Only `name`, `delivery_mode`, `webhook_url`, `is_active` are mutable |
112
- | `priceAlertsDelete(id)` | `DELETE /api/v1/rhc/price-alerts/{id}` | PRO+ | Delete an alert (events cascade) |
113
- | `priceAlertsEvents(params?)` | `GET /api/v1/rhc/price-alerts/events` | PRO+ | Dip/recovery fire history. Retained 30 days |
114
- | `coordinationAlertsList()` | `GET /api/v1/rhc/kol/coordination/alerts` | PRO+ | Your coordination rules |
115
- | `coordinationAlertsCreate(params)` | `POST /api/v1/rhc/kol/coordination/alerts` | PRO+ | Fire when N+ tracked KOLs buy the same token inside a rolling window |
116
- | `coordinationAlertsGet(id)` | `GET /api/v1/rhc/kol/coordination/alerts/{id}` | PRO+ | One rule (UUID) |
117
- | `coordinationAlertsUpdate(id, params)` | `PATCH /api/v1/rhc/kol/coordination/alerts/{id}` | PRO+ | Partial update |
118
- | `coordinationAlertsDelete(id)` | `DELETE /api/v1/rhc/kol/coordination/alerts/{id}` | PRO+ | Delete a rule (cooldown state + signals cascade) |
119
- | `firstTouchSubscriptionsList()` | `GET /api/v1/rhc/kol/first-touches/subscriptions` | ULTRA+ | Your first-touch subscriptions |
120
- | `firstTouchSubscriptionsCreate(params)` | `POST /api/v1/rhc/kol/first-touches/subscriptions` | ULTRA+ | Push when a token gets its FIRST tracked-KOL buy |
121
- | `firstTouchSubscriptionsGet(id)` | `GET /api/v1/rhc/kol/first-touches/subscriptions/{id}` | ULTRA+ | One subscription (UUID) |
122
- | `firstTouchSubscriptionsUpdate(id, params)` | `PATCH /api/v1/rhc/kol/first-touches/subscriptions/{id}` | ULTRA+ | `filters` is a whole-object **replace**, not a merge |
123
- | `firstTouchSubscriptionsDelete(id)` | `DELETE /api/v1/rhc/kol/first-touches/subscriptions/{id}` | ULTRA+ | Delete a subscription |
124
-
125
- > **RHC price alerts are polled (~15s), not live.** `rhc_token_prices` is written by the RHC ingester on a separate box and emits no `pg_notify`, so there is nothing to react to. Effective latency is that interval plus the token's own price-update cadence โ€” **do not assume parity with the Solana alerts, which are sub-second.** The create response spells this out in its `evaluation` block.
126
-
127
- > **Coordination scoring is comparable to Solana, but not identical.** The shared v1 scorer runs, `quality` is a real KOL win-rate, and `earliness` is **defaulted** โ€” RHC has no early-entry equivalent. Every fired signal records which components were real in `score_inputs`.
128
-
129
- > **First-touch filters are not the Solana set.** RHC has no scout score, so `min_scout_tier` and `min_n_touches` do not exist here rather than silently matching nothing; `min_kol_winrate` and `strategy` are the quality gates. Unknown filter keys are rejected with a 400, not ignored.
130
-
131
- ```ts
132
- // Follow three wallets, 0.05 ETH per copy, pushed over WebSocket
133
- const { subscription, webhook_secret } = await client.copyTradeCreate({
134
- name: "degen desk",
135
- source_wallets: ["0xaaa...", "0xbbb...", "0xccc..."],
136
- min_trade_eth: 0.01,
137
- sizing_mode: "fixed",
138
- sizing_amount: 0.05,
139
- delivery_mode: "websocket",
140
- });
141
-
142
- // Catch up on anything the webhook missed in the last hour
143
- const since = new Date(Date.now() - 3_600_000).toISOString();
144
- const { signals } = await client.copyTradeSignals({ subscription_id: subscription.id, since });
145
-
146
- // Alert me if this token drops 30% from where it is right now
147
- await client.priceAlertsCreate({ token_address: "0xToken...", drop_pct: 30, recovery_pct: 15, webhook_url: "https://example.com/hook" });
148
- ```
149
-
150
- ### Examples
151
-
152
- > **Deployer tiers ride `runner_rate`, not `graduation_rate`.** Since migrations 267 + 269, `elite` = 5+ tokens, 24h+ of deployer history, `runner_rate >= 0.50` ($100K+ peak MC); `good` = same with `>= 0.25`. `graduation_rate` still means the $40K bar and is still returned on every row โ€” it just no longer sets the tier (it proved farmable by operators rotating wallets). Only `spammer` still keys off it (20+ tokens, `graduation_rate < 0.05`).
153
-
154
- ```ts
155
- // Deployer reputation leaderboard โ€” elite deployers first
156
- const { deployers } = await client.deployerLeaderboard({ sort: "runner_rate", tier: "elite" });
157
-
158
- // Is this token's early cohort a launch bundle that's still holding?
159
- const { bundle } = await client.tokenBundle("0xToken...");
160
- console.log(bundle.bundle_kind, bundle.held_pct_of_supply);
161
-
162
- // Smart-money memecoin traders only, biggest net ETH first
163
- const { wallets } = await client.alphaWallets({ classification: "smart_money", min_memecoin_share: 0.7 });
164
- ```
165
-
166
- ## Streaming
167
-
168
- Managed WebSocket stream over ws-streaming (`wss://madeonsol.com/ws/v1/stream`). Handles the token fetch + 24h refresh, auto-reconnect with backoff, and heartbeat liveness. Six RHC channels:
169
-
170
- | Channel | Emits | Tier | Scope |
171
- |---|---|---|---|
172
- | `rhc:kol_trades` | `rhc:kol_trade` | PRO+ | broadcast โ€” the live KOL tape |
173
- | `rhc:dex_trades` | `rhc:dex_trade` | **ULTRA+** | broadcast โ€” the full DEX firehose |
174
- | `rhc:copytrade:signals` | `rhc:copytrade:signal` | PRO+ | user-scoped โ€” only **your** rules' fires |
175
- | `rhc:price_alert:events` | `rhc:price_alert:dip`, `rhc:price_alert:recovery` | PRO+ | user-scoped; ~15s polled, not sub-second |
176
- | `rhc:kol:coordination` | `rhc:kol:coordination` | PRO+ | user-scoped โ€” only **your** rules' fires |
177
- | `rhc:kol:first_touches` | `rhc:kol:first_touch` | PRO+ | broadcast โ€” ULTRA gates only the first-touch *subscription CRUD*, not this channel |
178
-
179
- > **Deprecated:** `rhc:trades` was never a real channel โ€” 0.4.0 subscribers got a `channels_rejected` warning and silence. The server now accepts it as an alias of `rhc:dex_trades` (and acks it under the canonical name), and the SDK keeps the literal marked `@deprecated` so 0.4.0 code compiles. Use `rhc:dex_trades`.
180
-
181
- ```ts
182
- const stream = client.stream();
183
-
184
- stream.on("rhc:kol_trade", (t) => console.log("KOL trade", t));
185
- stream.on("rhc:dex_trade", (t) => console.log("DEX trade", t)); // ULTRA+
186
- stream.on("open", () => console.log("connected to chain 4663"));
187
- // New in 0.5.0 โ€” a refused channel (typo or tier gate) is surfaced instead of
188
- // leaving the stream silently quiet.
189
- stream.on("warning", (w) => console.warn("rejected:", w.code, w.rejected, w.valid_channels));
190
-
191
- stream.subscribe(["rhc:kol_trades", "rhc:dex_trades"]);
192
-
193
- // later
194
- stream.close();
195
- ```
196
-
197
- ### New in 0.5.0 โ€” stream fixes
198
-
199
- - **Channel names corrected.** `StreamChannel` now lists the six real RHC channels above. 0.4.0's `rhc:trades` never existed server-side; it is now a server-accepted deprecated alias of `rhc:dex_trades` and stays in the union as `@deprecated`.
200
- - **Event names corrected.** The firehose broadcasts `rhc:dex_trade` โ€” a 0.4.0 `on("rhc:trade", โ€ฆ)` handler never fired, and is now a **compile error** so you find it. `StreamEventName` covers all six channels' events.
201
- - **Server warnings surfaced.** `channels_rejected` frames used to be silently dropped; they now emit a typed `"warning"` lifecycle event (`StreamWarning`: `code`, `rejected`, `valid_channels`, `message`).
202
-
203
- ## Rate limits
204
-
205
- Every successful response updates `client.lastRateLimit` from the `X-RateLimit-*` headers:
206
-
207
- ```ts
208
- await client.kolFeed({ limit: 5 });
209
- console.log(client.lastRateLimit); // { limit, remaining, reset, requestId }
210
- ```
211
-
212
- ## Links
213
-
214
- - ๐Ÿค– Robinhood Chain overview โ€” https://madeonsol.com/robinhood
215
- - ๐Ÿ’ฐ Pricing & free API key โ€” https://madeonsol.com/pricing
216
- - ๐Ÿ“š API docs โ€” https://madeonsol.com/api-docs
217
-
218
- ## License
219
-
220
- MIT ยฉ MadeOnSol
1
+ # robinhood-chain-x402
2
+
3
+ [![npm version](https://img.shields.io/npm/v/robinhood-chain-x402?style=flat-square)](https://www.npmjs.com/package/robinhood-chain-x402)
4
+ [![npm downloads](https://img.shields.io/npm/dm/robinhood-chain-x402?style=flat-square)](https://www.npmjs.com/package/robinhood-chain-x402)
5
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5+-blue?style=flat-square&logo=typescript&logoColor=white)](https://www.typescriptlang.org/)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue?style=flat-square)](LICENSE)
7
+
8
+ > ๐Ÿ“‚ **[Examples](./examples/)** ยท ๐Ÿ“š **[API docs](https://madeonsol.com/api-docs)** ยท ๐Ÿค– **[Robinhood Chain](https://madeonsol.com/robinhood)** ยท ๐Ÿ’ฐ **[Free API key](https://madeonsol.com/pricing)**
9
+
10
+ **TypeScript SDK for the [MadeOnSol](https://madeonsol.com/robinhood) Robinhood Chain API โ€” EVM-native on-chain trading intelligence for Robinhood Chain, chain id 4663.**
11
+
12
+ Robinhood Chain is an Arbitrum Orbit L2. This SDK gives you the same intel you get on Solana โ€” live KOL trades, a DEX trade tape, token discovery, launch-bundle detection, early-buyer quality, deployer reputation and smart-money wallets โ€” but EVM-native: lowercase `0x` addresses, `eth_amount`, `tx_hash`, `block_number`, `net_flow_eth`. Data comes from our **self-hosted RHC node**. The KOLโ†’EVM mapping is recovered by tracing each Solana KOL's bridge deposits (deBridge / Relay / Mayan / Wormhole) โ€” a dataset unique to MadeOnSol.
13
+
14
+ RHC coverage is **bundled into every tier at no extra cost** โ€” same API key, same base URL. Get a free key (200 req/day, no card) at [madeonsol.com/pricing](https://madeonsol.com/pricing).
15
+
16
+ > **Two auth modes.** **Key mode** โ€” an `msk_` Bearer API key calls every Robinhood Chain v1 route below (49 methods, all tiers). **Keyless x402 mode** (since 0.7.0) โ€” pass an EVM `privateKey` instead and the client pays per call in **USDG on Robinhood Chain** on the 10-endpoint x402 rail (from $0.04/call, no signup, wallet needs USDG but no ETH โ€” our facilitator relays gas). It handles the 402 โ†’ sign EIP-3009 `transferWithAuthorization` โ†’ retry flow itself; the rail is discoverable at [`/api/x402/rhc`](https://madeonsol.com/api/x402/rhc) and documented at [madeonsol.com/robinhood/x402](https://madeonsol.com/robinhood/x402). For keyless USDC-per-call on the Solana API, use [`madeonsol-x402`](https://www.npmjs.com/package/madeonsol-x402).
17
+
18
+ > **New in 0.8.0 โ€” `holder_growth`: who arrived and who left.** `client.tokenHolders(address)` (key mode and the keyless USDG rail alike โ€” same handler) now returns `holder_growth` on `GET /rhc/tokens/{address}/holders`: `{ "1h", "24h", "7d" }` ร— `{ cutoff_block, entered, entered_still_holding, exited, net }`. *entered* = addresses whose first `Transfer` of the token landed at-or-after the window's cutoff block (any current balance); *entered_still_holding* = those still non-zero; *exited* = pre-existing holders whose last movement in the window left them at zero; *net* โ‰ˆ the change in `holder_count`. Pools and burn addresses are excluded from every count. This exists because RHC balances are folded from ERC-20 Transfer logs on our own node โ€” the fold keeps first-seen and last-moved blocks per address and retains zero-balance rows โ€” so it is a direct read, not an estimate; the Solana census is a point-in-time ledger scan with no history and cannot answer this. A window is `null` (never 0) only when the chain had no ingested trades in it; the whole block is `null` only if the growth read failed. Sanity check from ship day: a token launched that morning showed 593 entered / 560 still holding over 24h, and `holder_count` was exactly 560.
19
+
20
+ > **New in 0.7.0 โ€” keyless x402 mode.** `createKeylessClient("0xโ€ฆ")` / `new RobinhoodChainX402({ privateKey })`: any EVM wallet holding USDG on chain 4663 can call `kolFeed`, `kolHotTokens`, `kolLeaderboard`, `token`, `tokenBuyerQuality`, `tokenKolConsensus`, `tokenRisk`, `tokenHolders`, `walletPnl` and `deployerAlerts` with no API key. The signature is EIP-712 over the USDG domain `{ Global Dollar, 1, 4663 }`, one payment attempt per call, `client.lastPayment` exposes the on-chain settlement (`transaction`, `payer`). Requires the optional peer dependency `viem` (`npm i viem`); key mode still has zero runtime deps. Calling any other method on a keyless client throws `KeylessNotAvailableError` โ€” it names the rail, it does not silently downgrade. Also new on the server this release: `/rhc/equities` (beacon-verified tokenized stocks/ETFs), `/rhc/tokens?sort=newest&since=`, `/rhc/lp-events` โ€” key-mode bindings for those follow in the next minor.
21
+
22
+ > **New in 0.6.0 โ€” wallet intelligence.** Ten new operations covering the Robinhood Chain wallet surface, which had no SDK binding at all until now: `wallet()` (90-day profile with reputation flags), `walletPnl()` (FIFO PnL with daily curve, closed and open positions), `walletPositions()` (open book marked to market), `walletTrades()` (per-wallet keyset-paginated tape), plus the watchlist โ€” `walletTrackerList()`, `walletTrackerAdd()`, `walletTrackerRemove()`, `walletTrackerRelabel()`, `walletTrackerTrades()` and `walletTrackerSummary()`. Everything is **ETH**-denominated, and cost basis is FIFO over a rolling 90-day window โ€” `cost_basis_observable_from` names the date the window opens, so a position opened before it reads as a sell with no matching buy. The profile / PnL / positions trio shares ONE snapshot cache server-side, so calling all three on an address costs roughly one computation rather than three; `cache_hit` says which call paid for it. Watchlist quotas are **per chain** (PRO 50 / ULTRA 100 / BUSINESS 500 RHC wallets), independent of your Solana list. Dependency ranges are now bounded to the versions actually tested (`@x402/*` `^2.x`, `@solana/kit` `^5.5.1`) instead of open-ended `>=0.0.1`, and the lazily-imported x402 peers are marked optional โ€” a keyed install no longer pulls the whole Solana stack.
23
+
24
+ ## Install
25
+
26
+ ```bash
27
+ npm install robinhood-chain-x402
28
+ # keyless x402 mode additionally needs viem:
29
+ npm install viem
30
+ ```
31
+
32
+ > Zero required runtime dependencies in key mode. The live stream will use the optional [`ws`](https://www.npmjs.com/package/ws) package on Node when present (recommended on Node โ‰ฅ 22 for a clean process exit); the browser uses the native `WebSocket`.
33
+
34
+ ## Quick start (10 seconds)
35
+
36
+ ```ts
37
+ import { createClient } from "robinhood-chain-x402";
38
+
39
+ const client = createClient("msk_your_api_key_here"); // free tier at madeonsol.com/pricing
40
+
41
+ // Live KOL buys/sells on Robinhood Chain (chain id 4663)
42
+ const { trades } = await client.kolFeed({ limit: 10, action: "buy" });
43
+ console.log(trades);
44
+ ```
45
+
46
+ ### Advanced initialization
47
+
48
+ ```ts
49
+ import { RobinhoodChainX402 } from "robinhood-chain-x402";
50
+
51
+ const client = new RobinhoodChainX402({
52
+ apiKey: "msk_...",
53
+ baseUrl: "https://madeonsol.com", // optional override
54
+ });
55
+ ```
56
+
57
+ ### Keyless x402 mode โ€” pay per call in USDG, no API key
58
+
59
+ ```ts
60
+ import { createKeylessClient } from "robinhood-chain-x402";
61
+
62
+ // An EVM wallet that holds USDG on Robinhood Chain (chain 4663). No ETH needed.
63
+ // Read the key from the environment โ€” never hard-code it.
64
+ const agent = createKeylessClient(process.env.RHC_PAYER_KEY!);
65
+
66
+ const risk = await agent.tokenRisk("0xd0601ce157db5bdc3162bbac2a2c8af5320d9eec"); // NVDA, $0.02
67
+ console.log(risk.score, risk.sellability, agent.lastPayment?.transaction); // settlement tx on Robinhood Chain
68
+
69
+ // Keyless rail = 10 endpoints; anything else throws KeylessNotAvailableError:
70
+ console.log(agent.constructor.KEYLESS_ENDPOINTS);
71
+ ```
72
+
73
+ How it works: the first request gets a `402` with `accepts[]`; the client picks the `eip155:4663` leg, signs an EIP-3009 `transferWithAuthorization` (EIP-712 domain `{ name: "Global Dollar", version: "1", chainId: 4663 }`, 5-minute validity, random 32-byte nonce) with `viem`, and retries with `PAYMENT-SIGNATURE`. Our facilitator verifies balance + nonce and settles on-chain (`transferWithAuthorization`, gas paid by us); the `PAYMENT-RESPONSE` header comes back decoded on `client.lastPayment`. Prices: from **$0.04** on the USDG leg (the relayer's gas floor); the same endpoints also accept USDC on Solana via [`madeonsol-x402`](https://www.npmjs.com/package/madeonsol-x402).
74
+
75
+ ## Endpoints โ€” all 52 Robinhood Chain routes
76
+
77
+ Every method maps 1:1 to an /api/v1/rhc/โ€ฆ route. Fields are EVM-native. Everything is a GET except the four rule engines at the bottom, which are full CRUD.
78
+
79
+ ### KOL intelligence
80
+
81
+ | Method | Route | Tier | Description |
82
+ |---|---|---|---|
83
+ | `kolFeed(params?)` | `/api/v1/rhc/kol/feed` | BASIC | Real-time KOL trade feed โ€” every buy/sell from tracked KOLs' verified EVM wallets, enriched with MC/peak and `mc_multiple_since_trade` |
84
+ | `kolLeaderboard(params?)` | `/api/v1/rhc/kol/leaderboard` | BASIC | KOLs ranked by trade count then net ETH flow over `24h`/`7d`/`30d` |
85
+ | `kolHotTokens(params?)` | `/api/v1/rhc/kol/hot-tokens` | BASIC | Consensus tokens bought by 2+ distinct KOLs in the window |
86
+ | `kol(wallet)` | `/api/v1/rhc/kol/{wallet}` | BASIC | Single KOL profile โ€” aggregate stats + 50 most recent trades |
87
+
88
+ ### DEX trade tape
89
+
90
+ | Method | Route | Tier | Description |
91
+ |---|---|---|---|
92
+ | `trades(params?)` | `/api/v1/rhc/trades` | PRO+ | Every Uniswap v2/v3/v4 swap with the effective `trader_eoa`, gas/ordering for MEV, and KOL/deployer flags |
93
+
94
+ > **`trader_eoa` is the effective trading account**, not simply `tx.from`. On an ordinary transaction it *is* `tx.from`; when the trade was bundled through ERC-4337 it is the userOp sender (`UserOperationEvent`), never the bundler that relayed it. It is still an EOA either way โ€” on Robinhood Chain a userOp sender is a normal EOA carrying an EIP-7702 delegation. Use `trader` only for the swap-log recipient (the router on aggregated swaps).
95
+
96
+ ### Token discovery + intelligence
97
+
98
+ | Method | Route | Tier | Description |
99
+ |---|---|---|---|
100
+ | `tokens(params?)` | `/api/v1/rhc/tokens` | PRO+ | Live-priced token discovery โ€” MC, liquidity, peak MC + drawdown, launchpad, deployer tier |
101
+ | `token(address)` | `/api/v1/rhc/tokens/{address}` | BASIC | Full token snapshot โ€” price/MC/FDV, graduation, deployer block, KOL activity, pools |
102
+ | `tokenCandles(address, params?)` | `/api/v1/rhc/tokens/{address}/candles` | PRO+ | 1-minute OHLC candles โ€” price + MC OHLC, close liquidity, volume with buy/sell split |
103
+ | `tokenKolConsensus(address)` | `/api/v1/rhc/tokens/{address}/kol-consensus` | PRO+ | KOL positioning โ€” buyers vs sellers, exit rate, `net_flow_eth`, median entry MC (ULTRA adds wallet lists) |
104
+ | `tokenBuyerQuality(address)` | `/api/v1/rhc/tokens/{address}/buyer-quality` | BASIC | 0โ€“100 early-buyer quality with bundle-buyer + dump-cluster legs |
105
+ | `tokenBundle(address)` | `/api/v1/rhc/tokens/{address}/bundle` | BASIC | Launch-bundle detection (`same_block`) + how much the cohort still holds |
106
+ | `tokenTopTraders(address, params?)` | `/api/v1/rhc/tokens/{address}/top-traders` | PRO+ | Traders ranked by REALIZED ETH (`sell โˆ’ buy`) โ€” **not PnL**; a wallet still holding ranks last |
107
+ | `tokenFlow(address, window?)` | `/api/v1/rhc/tokens/{address}/flow` | PRO+ | Net flow split by cohort (kol โ†’ bot โ†’ dump_cluster โ†’ early_buyer โ†’ โ€ฆ); positive `net_eth` means that cohort distributed |
108
+ | `tokenPeakHistory(address, params?)` | `/api/v1/rhc/tokens/{address}/peak-history` | PRO+ | Two peaks because they disagree โ€” `peak_mc_usd_recorded` (stored high-water) vs `peak_mc_usd_observed` (candle highs) |
109
+ | `tokenRisk(address)` | `/api/v1/rhc/tokens/{address}/risk` | PRO+ | EVM-native risk computed live on-chain โ€” proxy upgradeability, LP custody, simulated **sellability** (never cached) |
110
+ | `tokenHolders(address, params?)` | `/api/v1/rhc/tokens/{address}/holders` | PRO+ | Exact holder set from `Transfer` logs + concentration. **Check `verified` first**; `balance` is a raw uint256 string. `holder_growth` (`"1h"` / `"24h"` / `"7d"`) = `entered`, `entered_still_holding`, `exited`, `net` โ‰ˆ ฮ” `holder_count` per window (pools/burns excluded; a window is `null` only when the chain had no ingested trades in it) |
111
+
112
+ ### Deployer hunter + smart money
113
+
114
+ | Method | Route | Tier | Description |
115
+ |---|---|---|---|
116
+ | `deployerLeaderboard(params?)` | `/api/v1/rhc/deployer-hunter/leaderboard` | BASIC | 99k+ deployers ranked by reputation โ€” `graduation_rate` ($40K+ peak MC), `runner_rate` ($100K+) |
117
+ | `deployer(address)` | `/api/v1/rhc/deployer-hunter/{address}` | BASIC | Single deployer profile + 50 most recent tokens (unknown wallets โ†’ `is_deployer: false`) |
118
+ | `deployerAlerts(params?)` | `/api/v1/rhc/deployer-hunter/alerts` | BASIC ยท keyless $0.01 | Launch alerts from tracked (graded) deployers โ€” tier, lifetime bond rate, MC at alert; `since` = polling cursor (feed back `next_since`) |
119
+ | `alphaWallets(params?)` | `/api/v1/rhc/alpha-wallets` | PRO+ | Smart-money wallets ranked by realized performance โ€” `net_eth`, `win_rate`, `memecoin_share`, `likely_bot` |
120
+
121
+ ### Rule engines โ€” push, not polling
122
+
123
+ Four server-side rule engines that watch the RHC tape for you and deliver over webhook or WebSocket. **Every quota is per chain** โ€” configuring RHC rules never consumes your Solana budget. A `webhook_secret` is returned exactly once on create; payloads are signed HMAC-SHA256 over `` `<timestamp>.<body>` `` in the `X-MadeOnSol-Signature` header.
124
+
125
+ | Method | Route | Tier | Description |
126
+ |---|---|---|---|
127
+ | `copyTradeList()` | `GET /api/v1/rhc/copytrade/subscriptions` | PRO+ | Your copy-trade rules |
128
+ | `copyTradeCreate(params)` | `POST /api/v1/rhc/copytrade/subscriptions` | PRO+ | Follow up to 250 wallets; sizes are **ETH**, and there is no MC band (the RHC notify payload carries no market cap) |
129
+ | `copyTradeGet(id)` | `GET /api/v1/rhc/copytrade/subscriptions/{id}` | PRO+ | One rule (numeric id) |
130
+ | `copyTradeUpdate(id, params)` | `PATCH /api/v1/rhc/copytrade/subscriptions/{id}` | PRO+ | Partial update; the wallet cap is re-checked so a rule cannot be PATCHed past its tier |
131
+ | `copyTradeDelete(id)` | `DELETE /api/v1/rhc/copytrade/subscriptions/{id}` | PRO+ | Delete a rule (signals cascade) |
132
+ | `copyTradeSignals(params?)` | `GET /api/v1/rhc/copytrade/signals` | PRO+ | Fire history โ€” the catch-up path for a missed webhook. Retained 7 days |
133
+ | `priceAlertsList()` | `GET /api/v1/rhc/price-alerts` | PRO+ | Your price alerts |
134
+ | `priceAlertsCreate(params)` | `POST /api/v1/rhc/price-alerts` | PRO+ | Baseline MC is captured at creation; token must already be tracked with an MC |
135
+ | `priceAlertsGet(id)` | `GET /api/v1/rhc/price-alerts/{id}` | PRO+ | One alert (numeric id) |
136
+ | `priceAlertsUpdate(id, params)` | `PATCH /api/v1/rhc/price-alerts/{id}` | PRO+ | Only `name`, `delivery_mode`, `webhook_url`, `is_active` are mutable |
137
+ | `priceAlertsDelete(id)` | `DELETE /api/v1/rhc/price-alerts/{id}` | PRO+ | Delete an alert (events cascade) |
138
+ | `priceAlertsEvents(params?)` | `GET /api/v1/rhc/price-alerts/events` | PRO+ | Dip/recovery fire history. Retained 30 days |
139
+ | `coordinationAlertsList()` | `GET /api/v1/rhc/kol/coordination/alerts` | PRO+ | Your coordination rules |
140
+ | `coordinationAlertsCreate(params)` | `POST /api/v1/rhc/kol/coordination/alerts` | PRO+ | Fire when N+ tracked KOLs buy the same token inside a rolling window |
141
+ | `coordinationAlertsGet(id)` | `GET /api/v1/rhc/kol/coordination/alerts/{id}` | PRO+ | One rule (UUID) |
142
+ | `coordinationAlertsUpdate(id, params)` | `PATCH /api/v1/rhc/kol/coordination/alerts/{id}` | PRO+ | Partial update |
143
+ | `coordinationAlertsDelete(id)` | `DELETE /api/v1/rhc/kol/coordination/alerts/{id}` | PRO+ | Delete a rule (cooldown state + signals cascade) |
144
+ | `firstTouchSubscriptionsList()` | `GET /api/v1/rhc/kol/first-touches/subscriptions` | ULTRA+ | Your first-touch subscriptions |
145
+ | `firstTouchSubscriptionsCreate(params)` | `POST /api/v1/rhc/kol/first-touches/subscriptions` | ULTRA+ | Push when a token gets its FIRST tracked-KOL buy |
146
+ | `firstTouchSubscriptionsGet(id)` | `GET /api/v1/rhc/kol/first-touches/subscriptions/{id}` | ULTRA+ | One subscription (UUID) |
147
+ | `firstTouchSubscriptionsUpdate(id, params)` | `PATCH /api/v1/rhc/kol/first-touches/subscriptions/{id}` | ULTRA+ | `filters` is a whole-object **replace**, not a merge |
148
+ | `firstTouchSubscriptionsDelete(id)` | `DELETE /api/v1/rhc/kol/first-touches/subscriptions/{id}` | ULTRA+ | Delete a subscription |
149
+
150
+ > **RHC price alerts are polled (~15s), not live.** `rhc_token_prices` is written by the RHC ingester on a separate box and emits no `pg_notify`, so there is nothing to react to. Effective latency is that interval plus the token's own price-update cadence โ€” **do not assume parity with the Solana alerts, which are sub-second.** The create response spells this out in its `evaluation` block.
151
+
152
+ > **Coordination scoring is comparable to Solana, but not identical.** The shared v1 scorer runs, `quality` is a real KOL win-rate, and `earliness` is **defaulted** โ€” RHC has no early-entry equivalent. Every fired signal records which components were real in `score_inputs`.
153
+
154
+ > **First-touch filters are not the Solana set.** RHC has no scout score, so `min_scout_tier` and `min_n_touches` do not exist here rather than silently matching nothing; `min_kol_winrate` and `strategy` are the quality gates. Unknown filter keys are rejected with a 400, not ignored.
155
+
156
+ ```ts
157
+ // Follow three wallets, 0.05 ETH per copy, pushed over WebSocket
158
+ const { subscription, webhook_secret } = await client.copyTradeCreate({
159
+ name: "degen desk",
160
+ source_wallets: ["0xaaa...", "0xbbb...", "0xccc..."],
161
+ min_trade_eth: 0.01,
162
+ sizing_mode: "fixed",
163
+ sizing_amount: 0.05,
164
+ delivery_mode: "websocket",
165
+ });
166
+
167
+ // Catch up on anything the webhook missed in the last hour
168
+ const since = new Date(Date.now() - 3_600_000).toISOString();
169
+ const { signals } = await client.copyTradeSignals({ subscription_id: subscription.id, since });
170
+
171
+ // Alert me if this token drops 30% from where it is right now
172
+ await client.priceAlertsCreate({ token_address: "0xToken...", drop_pct: 30, recovery_pct: 15, webhook_url: "https://example.com/hook" });
173
+ ```
174
+
175
+ ### Examples
176
+
177
+ > **Deployer tiers ride `runner_rate`, not `graduation_rate`.** Since migrations 267 + 269, `elite` = 5+ tokens, 24h+ of deployer history, `runner_rate >= 0.50` ($100K+ peak MC); `good` = same with `>= 0.25`. `graduation_rate` still means the $40K bar and is still returned on every row โ€” it just no longer sets the tier (it proved farmable by operators rotating wallets). Only `spammer` still keys off it (20+ tokens, `graduation_rate < 0.05`).
178
+
179
+ ```ts
180
+ // Deployer reputation leaderboard โ€” elite deployers first
181
+ const { deployers } = await client.deployerLeaderboard({ sort: "runner_rate", tier: "elite" });
182
+
183
+ // Is this token's early cohort a launch bundle that's still holding?
184
+ const { bundle } = await client.tokenBundle("0xToken...");
185
+ console.log(bundle.bundle_kind, bundle.held_pct_of_supply);
186
+
187
+ // Smart-money memecoin traders only, biggest net ETH first
188
+ const { wallets } = await client.alphaWallets({ classification: "smart_money", min_memecoin_share: 0.7 });
189
+ ```
190
+
191
+ ## Streaming
192
+
193
+ Managed WebSocket stream over ws-streaming (`wss://madeonsol.com/ws/v1/stream`). Handles the token fetch + 24h refresh, auto-reconnect with backoff, and heartbeat liveness. Six RHC channels:
194
+
195
+ | Channel | Emits | Tier | Scope |
196
+ |---|---|---|---|
197
+ | `rhc:kol_trades` | `rhc:kol_trade` | PRO+ | broadcast โ€” the live KOL tape |
198
+ | `rhc:dex_trades` | `rhc:dex_trade` | **ULTRA+** | broadcast โ€” the full DEX firehose |
199
+ | `rhc:copytrade:signals` | `rhc:copytrade:signal` | PRO+ | user-scoped โ€” only **your** rules' fires |
200
+ | `rhc:price_alert:events` | `rhc:price_alert:dip`, `rhc:price_alert:recovery` | PRO+ | user-scoped; ~15s polled, not sub-second |
201
+ | `rhc:kol:coordination` | `rhc:kol:coordination` | PRO+ | user-scoped โ€” only **your** rules' fires |
202
+ | `rhc:kol:first_touches` | `rhc:kol:first_touch` | PRO+ | broadcast โ€” ULTRA gates only the first-touch *subscription CRUD*, not this channel |
203
+
204
+ > **Deprecated:** `rhc:trades` was never a real channel โ€” 0.4.0 subscribers got a `channels_rejected` warning and silence. The server now accepts it as an alias of `rhc:dex_trades` (and acks it under the canonical name), and the SDK keeps the literal marked `@deprecated` so 0.4.0 code compiles. Use `rhc:dex_trades`.
205
+
206
+ ```ts
207
+ const stream = client.stream();
208
+
209
+ stream.on("rhc:kol_trade", (t) => console.log("KOL trade", t));
210
+ stream.on("rhc:dex_trade", (t) => console.log("DEX trade", t)); // ULTRA+
211
+ stream.on("open", () => console.log("connected to chain 4663"));
212
+ // New in 0.5.0 โ€” a refused channel (typo or tier gate) is surfaced instead of
213
+ // leaving the stream silently quiet.
214
+ stream.on("warning", (w) => console.warn("rejected:", w.code, w.rejected, w.valid_channels));
215
+
216
+ stream.subscribe(["rhc:kol_trades", "rhc:dex_trades"]);
217
+
218
+ // later
219
+ stream.close();
220
+ ```
221
+
222
+ ### New in 0.5.0 โ€” stream fixes
223
+
224
+ - **Channel names corrected.** `StreamChannel` now lists the six real RHC channels above. 0.4.0's `rhc:trades` never existed server-side; it is now a server-accepted deprecated alias of `rhc:dex_trades` and stays in the union as `@deprecated`.
225
+ - **Event names corrected.** The firehose broadcasts `rhc:dex_trade` โ€” a 0.4.0 `on("rhc:trade", โ€ฆ)` handler never fired, and is now a **compile error** so you find it. `StreamEventName` covers all six channels' events.
226
+ - **Server warnings surfaced.** `channels_rejected` frames used to be silently dropped; they now emit a typed `"warning"` lifecycle event (`StreamWarning`: `code`, `rejected`, `valid_channels`, `message`).
227
+
228
+ ## Rate limits
229
+
230
+ Every successful response updates `client.lastRateLimit` from the `X-RateLimit-*` headers:
231
+
232
+ ```ts
233
+ await client.kolFeed({ limit: 5 });
234
+ console.log(client.lastRateLimit); // { limit, remaining, reset, requestId }
235
+ ```
236
+
237
+ ## Links
238
+
239
+ - ๐Ÿค– Robinhood Chain overview โ€” https://madeonsol.com/robinhood
240
+ - ๐Ÿ’ฐ Pricing & free API key โ€” https://madeonsol.com/pricing
241
+ - ๐Ÿ“š API docs โ€” https://madeonsol.com/api-docs
242
+
243
+ ## License
244
+
245
+ MIT ยฉ MadeOnSol