robinhood-chain-sdk 0.1.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/LICENSE +21 -0
- package/README.md +248 -0
- package/dist/index.d.ts +800 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +332 -0
- package/dist/index.js.map +1 -0
- package/dist/stream.d.ts +72 -0
- package/dist/stream.d.ts.map +1 -0
- package/dist/stream.js +225 -0
- package/dist/stream.js.map +1 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +4 -0
- package/dist/version.js.map +1 -0
- package/llms.txt +71 -0
- package/package.json +74 -0
package/dist/stream.js
ADDED
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Real-time WebSocket streaming client for Robinhood Chain (chain id 4663).
|
|
3
|
+
*
|
|
4
|
+
* Wraps the connect → token → subscribe → event loop with auto-reconnect,
|
|
5
|
+
* 24h-token auto-refresh, heartbeat liveness, and typed events, so consumers
|
|
6
|
+
* never hand-roll connection management. Obtain one via `client.stream.connect()`.
|
|
7
|
+
*
|
|
8
|
+
* Works in Node (global `WebSocket` on Node 22+, else lazily imports the
|
|
9
|
+
* optional `ws` package) and the browser (native WebSocket). Zero required deps.
|
|
10
|
+
*/
|
|
11
|
+
async function resolveWebSocket(override) {
|
|
12
|
+
if (override)
|
|
13
|
+
return override;
|
|
14
|
+
// Prefer the `ws` package in Node: it exposes terminate() for an immediate
|
|
15
|
+
// socket teardown, so close() lets short-lived processes exit. The global
|
|
16
|
+
// (undici) WebSocket on Node 22+ keeps its TLS socket open after close() —
|
|
17
|
+
// it has no terminate() and no reachable socket handle — which hangs the
|
|
18
|
+
// event loop. In the browser this import rejects and we fall back to the
|
|
19
|
+
// platform's native WebSocket.
|
|
20
|
+
try {
|
|
21
|
+
const mod = (await import("ws"));
|
|
22
|
+
const impl = mod.default ?? mod.WebSocket;
|
|
23
|
+
if (impl)
|
|
24
|
+
return impl;
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
/* not Node, or `ws` not installed — fall back to the platform WebSocket */
|
|
28
|
+
}
|
|
29
|
+
const g = globalThis.WebSocket;
|
|
30
|
+
if (g)
|
|
31
|
+
return g;
|
|
32
|
+
throw new Error("No WebSocket implementation available. On Node < 22, install `ws` (npm i ws) or pass { WebSocketImpl }.");
|
|
33
|
+
}
|
|
34
|
+
const OPEN = 1;
|
|
35
|
+
export class RobinhoodStream {
|
|
36
|
+
constructor(opts) {
|
|
37
|
+
this.ws = null;
|
|
38
|
+
this.listeners = new Map();
|
|
39
|
+
this.desired = { channels: new Set(), filters: {} };
|
|
40
|
+
this.closedByUser = false;
|
|
41
|
+
this.attempt = 0;
|
|
42
|
+
this.hbTimer = null;
|
|
43
|
+
this.reconnectTimer = null;
|
|
44
|
+
this.connecting = false;
|
|
45
|
+
this.opts = {
|
|
46
|
+
getToken: opts.getToken,
|
|
47
|
+
autoReconnect: opts.autoReconnect ?? true,
|
|
48
|
+
maxBackoffMs: opts.maxBackoffMs ?? 30000,
|
|
49
|
+
heartbeatTimeoutMs: opts.heartbeatTimeoutMs ?? 90000,
|
|
50
|
+
WebSocketImpl: opts.WebSocketImpl,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
/** Register a handler. Use an event name, `"*"` for every event, or a lifecycle event. */
|
|
54
|
+
on(event, fn) {
|
|
55
|
+
if (!this.listeners.has(event))
|
|
56
|
+
this.listeners.set(event, new Set());
|
|
57
|
+
this.listeners.get(event).add(fn);
|
|
58
|
+
return this;
|
|
59
|
+
}
|
|
60
|
+
/** Remove a handler (or all handlers for an event when `fn` is omitted). */
|
|
61
|
+
off(event, fn) {
|
|
62
|
+
if (!fn)
|
|
63
|
+
this.listeners.delete(event);
|
|
64
|
+
else
|
|
65
|
+
this.listeners.get(event)?.delete(fn);
|
|
66
|
+
return this;
|
|
67
|
+
}
|
|
68
|
+
emit(event, data, evt) {
|
|
69
|
+
const set = this.listeners.get(event);
|
|
70
|
+
if (set)
|
|
71
|
+
for (const fn of set) {
|
|
72
|
+
try {
|
|
73
|
+
fn(data, evt);
|
|
74
|
+
}
|
|
75
|
+
catch { /* user handler */ }
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/** Subscribe to one or more channels (connects on first call). Optional server-side filters. */
|
|
79
|
+
subscribe(channels, filters) {
|
|
80
|
+
for (const c of channels)
|
|
81
|
+
this.desired.channels.add(c);
|
|
82
|
+
if (filters)
|
|
83
|
+
this.desired.filters = { ...this.desired.filters, ...filters };
|
|
84
|
+
if (this.ws && this.ws.readyState === OPEN)
|
|
85
|
+
this.sendSubscribe();
|
|
86
|
+
else
|
|
87
|
+
void this.connect();
|
|
88
|
+
return this;
|
|
89
|
+
}
|
|
90
|
+
/** Stop receiving the given channels. */
|
|
91
|
+
unsubscribe(channels) {
|
|
92
|
+
for (const c of channels)
|
|
93
|
+
this.desired.channels.delete(c);
|
|
94
|
+
if (this.ws && this.ws.readyState === OPEN) {
|
|
95
|
+
this.ws.send(JSON.stringify({ type: "unsubscribe", channels }));
|
|
96
|
+
}
|
|
97
|
+
return this;
|
|
98
|
+
}
|
|
99
|
+
/** Open the connection (also called implicitly by subscribe). */
|
|
100
|
+
async connect() {
|
|
101
|
+
if (this.connecting || (this.ws && this.ws.readyState === OPEN))
|
|
102
|
+
return;
|
|
103
|
+
this.closedByUser = false;
|
|
104
|
+
this.connecting = true;
|
|
105
|
+
try {
|
|
106
|
+
const [WS, token] = await Promise.all([
|
|
107
|
+
resolveWebSocket(this.opts.WebSocketImpl),
|
|
108
|
+
this.opts.getToken(),
|
|
109
|
+
]);
|
|
110
|
+
const url = `${token.ws_url}?token=${encodeURIComponent(token.token)}`;
|
|
111
|
+
const ws = new WS(url);
|
|
112
|
+
this.ws = ws;
|
|
113
|
+
ws.onopen = () => {
|
|
114
|
+
this.attempt = 0;
|
|
115
|
+
this.resetHeartbeat();
|
|
116
|
+
if (this.desired.channels.size > 0)
|
|
117
|
+
this.sendSubscribe();
|
|
118
|
+
this.emit("open", undefined);
|
|
119
|
+
};
|
|
120
|
+
ws.onmessage = (ev) => this.handleMessage(ev.data);
|
|
121
|
+
ws.onerror = (err) => this.emit("error", err instanceof Error ? err : new Error("WebSocket error"));
|
|
122
|
+
ws.onclose = (ev) => {
|
|
123
|
+
this.clearHeartbeat();
|
|
124
|
+
this.ws = null;
|
|
125
|
+
this.emit("close", { code: ev?.code, reason: ev?.reason });
|
|
126
|
+
if (!this.closedByUser && this.opts.autoReconnect)
|
|
127
|
+
this.scheduleReconnect();
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
catch (err) {
|
|
131
|
+
this.emit("error", err);
|
|
132
|
+
if (!this.closedByUser && this.opts.autoReconnect)
|
|
133
|
+
this.scheduleReconnect();
|
|
134
|
+
}
|
|
135
|
+
finally {
|
|
136
|
+
this.connecting = false;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
/** Close the connection and stop reconnecting. */
|
|
140
|
+
close() {
|
|
141
|
+
this.closedByUser = true;
|
|
142
|
+
if (this.reconnectTimer) {
|
|
143
|
+
clearTimeout(this.reconnectTimer);
|
|
144
|
+
this.reconnectTimer = null;
|
|
145
|
+
}
|
|
146
|
+
this.clearHeartbeat();
|
|
147
|
+
const sock = this.ws;
|
|
148
|
+
this.ws = null;
|
|
149
|
+
try {
|
|
150
|
+
// `ws` package: terminate() destroys the underlying socket immediately so
|
|
151
|
+
// the process can exit. Native/undici WebSocket has no terminate() — fall
|
|
152
|
+
// back to a graceful close().
|
|
153
|
+
if (typeof sock?.terminate === "function")
|
|
154
|
+
sock.terminate();
|
|
155
|
+
else
|
|
156
|
+
sock?.close(1000, "client closed");
|
|
157
|
+
}
|
|
158
|
+
catch { /* ignore */ }
|
|
159
|
+
}
|
|
160
|
+
sendSubscribe() {
|
|
161
|
+
const channels = Array.from(this.desired.channels);
|
|
162
|
+
if (channels.length === 0)
|
|
163
|
+
return;
|
|
164
|
+
const msg = { type: "subscribe", channels };
|
|
165
|
+
if (Object.keys(this.desired.filters).length > 0)
|
|
166
|
+
msg.filters = this.desired.filters;
|
|
167
|
+
this.ws?.send(JSON.stringify(msg));
|
|
168
|
+
}
|
|
169
|
+
handleMessage(raw) {
|
|
170
|
+
let msg;
|
|
171
|
+
try {
|
|
172
|
+
const text = typeof raw === "string" ? raw : String(raw);
|
|
173
|
+
msg = JSON.parse(text);
|
|
174
|
+
}
|
|
175
|
+
catch {
|
|
176
|
+
this.emit("error", new Error("Failed to parse stream message"));
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
if (msg.type === "heartbeat") {
|
|
180
|
+
this.resetHeartbeat();
|
|
181
|
+
this.emit("heartbeat", msg.ts);
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
if (msg.type === "connected") {
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
if (msg.type === "subscribed") {
|
|
188
|
+
this.emit("subscribed", msg.channels);
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
if (msg.channel && msg.event) {
|
|
192
|
+
const evt = msg;
|
|
193
|
+
this.emit(evt.event, evt.data, evt);
|
|
194
|
+
this.emit("*", evt.data, evt);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
scheduleReconnect() {
|
|
198
|
+
if (this.reconnectTimer)
|
|
199
|
+
return;
|
|
200
|
+
const base = Math.min(1000 * 2 ** this.attempt, this.opts.maxBackoffMs);
|
|
201
|
+
const delay = base / 2 + Math.floor((base / 2) * Math.random());
|
|
202
|
+
this.attempt++;
|
|
203
|
+
this.emit("reconnect", { attempt: this.attempt, delayMs: delay });
|
|
204
|
+
this.reconnectTimer = setTimeout(() => {
|
|
205
|
+
this.reconnectTimer = null;
|
|
206
|
+
void this.connect();
|
|
207
|
+
}, delay);
|
|
208
|
+
}
|
|
209
|
+
resetHeartbeat() {
|
|
210
|
+
this.clearHeartbeat();
|
|
211
|
+
this.hbTimer = setTimeout(() => {
|
|
212
|
+
try {
|
|
213
|
+
this.ws?.close(4000, "heartbeat timeout");
|
|
214
|
+
}
|
|
215
|
+
catch { /* ignore */ }
|
|
216
|
+
}, this.opts.heartbeatTimeoutMs);
|
|
217
|
+
}
|
|
218
|
+
clearHeartbeat() {
|
|
219
|
+
if (this.hbTimer) {
|
|
220
|
+
clearTimeout(this.hbTimer);
|
|
221
|
+
this.hbTimer = null;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
//# sourceMappingURL=stream.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stream.js","sourceRoot":"","sources":["../src/stream.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,OAAO,UAAU,CAAC"}
|
package/dist/version.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,mEAAmE;AACnE,0EAA0E;AAC1E,MAAM,CAAC,MAAM,OAAO,GAAG,OAAO,CAAC"}
|
package/llms.txt
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# robinhood-chain-sdk
|
|
2
|
+
|
|
3
|
+
> Official TypeScript SDK for the Robinhood Chain (chain id 4663) API — EVM-native on-chain trading intelligence.
|
|
4
|
+
|
|
5
|
+
## What is Robinhood Chain?
|
|
6
|
+
|
|
7
|
+
Robinhood Chain (RHC) is an Arbitrum Orbit L2, chain id 4663. MadeOnSol (https://madeonsol.com/robinhood) runs a self-hosted node and exposes EVM-native intelligence over the same paid v1 API and the same `msk_` API key as the Solana product — bundled into every tier at no extra cost. All addresses are lowercase `0x`, amounts are `eth_amount`, transactions are `tx_hash`, and blocks are `block_number`.
|
|
8
|
+
|
|
9
|
+
- **KOL Trades**: live buy/sell feed from tracked Solana KOLs' verified EVM wallets, a KOL activity leaderboard, consensus "hot tokens" (2+ KOLs), and per-KOL profiles.
|
|
10
|
+
- **Tokens**: token discovery, per-token snapshot (price/MC/FDV/peak/drawdown, deployer reputation, KOL activity, pools), 1-minute OHLC candles, KOL consensus (`net_flow_eth`), early-buyer quality (0–100), and same-block launch-bundle detection.
|
|
11
|
+
- **DEX Trade Tape**: every Uniswap v2/v3/v4 swap with the real trader EOA (tx.from), gas/ordering for MEV analysis, and KOL/deployer flags.
|
|
12
|
+
- **Deployer Hunter**: reputation leaderboard + per-deployer profiles (graduation = $40K peak MC, runner = $100K).
|
|
13
|
+
- **Alpha Wallets**: smart-money ranking by realized on-chain performance (net ETH, win rate, memecoin share), with bot-fleet flagging.
|
|
14
|
+
- **Streaming**: managed WebSocket for `rhc:kol_trades` and `rhc:trades`.
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
npm install robinhood-chain-sdk
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Authentication
|
|
23
|
+
|
|
24
|
+
Get a free API key at https://madeonsol.com/developer (starts with `msk_`).
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
import { RobinhoodClient } from "robinhood-chain-sdk";
|
|
28
|
+
const client = new RobinhoodClient({ apiKey: "msk_your_api_key_here" });
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Methods (all 14 endpoints)
|
|
32
|
+
|
|
33
|
+
### KOL (client.kol) — BASIC
|
|
34
|
+
- `client.kol.feed(params?)` — GET /rhc/kol/feed — live KOL trade feed
|
|
35
|
+
- `client.kol.leaderboard(params?)` — GET /rhc/kol/leaderboard — KOL activity leaderboard
|
|
36
|
+
- `client.kol.hotTokens(params?)` — GET /rhc/kol/hot-tokens — tokens bought by 2+ KOLs
|
|
37
|
+
- `client.kol.wallet(wallet)` — GET /rhc/kol/{wallet} — single KOL profile
|
|
38
|
+
|
|
39
|
+
### DEX tape (client.trades) — PRO+
|
|
40
|
+
- `client.trades(params?)` — GET /rhc/trades — Uniswap v2/v3/v4 trade tape
|
|
41
|
+
|
|
42
|
+
### Tokens (client.tokens)
|
|
43
|
+
- `client.tokens.list(params?)` — GET /rhc/tokens — token discovery (PRO+)
|
|
44
|
+
- `client.tokens.get(address)` — GET /rhc/tokens/{address} — token snapshot (BASIC)
|
|
45
|
+
- `client.tokens.candles(address, params?)` — GET /rhc/tokens/{address}/candles — 1m OHLC (PRO+)
|
|
46
|
+
- `client.tokens.kolConsensus(address)` — GET /rhc/tokens/{address}/kol-consensus (PRO+)
|
|
47
|
+
- `client.tokens.buyerQuality(address)` — GET /rhc/tokens/{address}/buyer-quality (BASIC)
|
|
48
|
+
- `client.tokens.bundle(address)` — GET /rhc/tokens/{address}/bundle — launch-bundle detection (BASIC)
|
|
49
|
+
|
|
50
|
+
### Deployer Hunter (client.deployerHunter) — BASIC
|
|
51
|
+
- `client.deployerHunter.leaderboard(params?)` — GET /rhc/deployer-hunter/leaderboard
|
|
52
|
+
- `client.deployerHunter.profile(address)` — GET /rhc/deployer-hunter/{address}
|
|
53
|
+
|
|
54
|
+
### Alpha Wallets (client.alphaWallets) — PRO+
|
|
55
|
+
- `client.alphaWallets(params?)` — GET /rhc/alpha-wallets — smart-money ranking
|
|
56
|
+
|
|
57
|
+
### Streaming (client.stream) — PRO+
|
|
58
|
+
- `client.stream.getToken()` — POST /stream/token — 24h WS token
|
|
59
|
+
- `client.stream.connect()` — managed WebSocket (channels: rhc:kol_trades, rhc:trades)
|
|
60
|
+
|
|
61
|
+
## Error handling
|
|
62
|
+
|
|
63
|
+
All methods throw `RobinhoodError` with `.message`, `.status`, `.body`, and `.requestId` (`_rid`). 429 and 5xx responses are retried automatically with backoff.
|
|
64
|
+
|
|
65
|
+
## Links
|
|
66
|
+
|
|
67
|
+
- Robinhood Chain: https://madeonsol.com/robinhood
|
|
68
|
+
- Pricing: https://madeonsol.com/pricing
|
|
69
|
+
- API docs: https://madeonsol.com/api-docs
|
|
70
|
+
- npm: https://www.npmjs.com/package/robinhood-chain-sdk
|
|
71
|
+
- GitHub: https://github.com/madeonsol/robinhood-chain-sdk
|
package/package.json
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "robinhood-chain-sdk",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Official TypeScript SDK for the Robinhood Chain (chain id 4663) API — EVM-native on-chain trading intelligence: live KOL trades, token discovery & bundles, the DEX trade tape, OHLC candles, deployer reputation, and smart-money wallets. Zero dependencies.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"default": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist",
|
|
16
|
+
"llms.txt"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"gen-version": "node scripts/gen-version.mjs",
|
|
20
|
+
"prebuild": "node scripts/gen-version.mjs",
|
|
21
|
+
"build": "tsc",
|
|
22
|
+
"test": "tsc -p examples/tsconfig.json",
|
|
23
|
+
"route-parity": "node scripts/route-parity-check.mjs",
|
|
24
|
+
"preflight": "bash ./scripts/preflight-publish.sh",
|
|
25
|
+
"prepublishOnly": "npm run preflight && npm run build"
|
|
26
|
+
},
|
|
27
|
+
"keywords": [
|
|
28
|
+
"robinhood chain",
|
|
29
|
+
"robinhood-chain",
|
|
30
|
+
"rhc",
|
|
31
|
+
"robinhood chain api",
|
|
32
|
+
"robinhood chain sdk",
|
|
33
|
+
"robinhood-chain-sdk",
|
|
34
|
+
"chain-4663",
|
|
35
|
+
"chain-id-4663",
|
|
36
|
+
"evm",
|
|
37
|
+
"arbitrum-orbit",
|
|
38
|
+
"kol",
|
|
39
|
+
"kol-tracker",
|
|
40
|
+
"deployer-hunter",
|
|
41
|
+
"smart-money",
|
|
42
|
+
"alpha-wallets",
|
|
43
|
+
"memecoin",
|
|
44
|
+
"memecoin-tracker",
|
|
45
|
+
"defi",
|
|
46
|
+
"web3",
|
|
47
|
+
"trading",
|
|
48
|
+
"trade-stream",
|
|
49
|
+
"dex",
|
|
50
|
+
"sdk",
|
|
51
|
+
"api",
|
|
52
|
+
"madeonsol"
|
|
53
|
+
],
|
|
54
|
+
"license": "MIT",
|
|
55
|
+
"repository": {
|
|
56
|
+
"type": "git",
|
|
57
|
+
"url": "https://github.com/madeonsol/robinhood-chain-sdk.git"
|
|
58
|
+
},
|
|
59
|
+
"homepage": "https://madeonsol.com/robinhood",
|
|
60
|
+
"bugs": {
|
|
61
|
+
"url": "https://github.com/madeonsol/robinhood-chain-sdk/issues"
|
|
62
|
+
},
|
|
63
|
+
"engines": {
|
|
64
|
+
"node": ">=18"
|
|
65
|
+
},
|
|
66
|
+
"optionalDependencies": {
|
|
67
|
+
"ws": "^8.0.0"
|
|
68
|
+
},
|
|
69
|
+
"devDependencies": {
|
|
70
|
+
"@types/node": "^20.0.0",
|
|
71
|
+
"typescript": "^5.4.0"
|
|
72
|
+
},
|
|
73
|
+
"funding": "https://madeonsol.com"
|
|
74
|
+
}
|