robinhood-chain-sdk 0.11.0 → 0.13.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 CHANGED
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 MadeOnSol
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
1
+ MIT License
2
+
3
+ Copyright (c) 2026 MadeOnSol
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -15,6 +15,14 @@ Robinhood Chain (RHC) is an **Arbitrum Orbit L2, chain id 4663**. This SDK wraps
15
15
 
16
16
  The KOL→EVM mapping is unique to MadeOnSol: each tracked Solana KOL's Robinhood-Chain wallet is recovered by tracing their Solana→EVM bridge deposits (deBridge / Relay / Mayan / Wormhole), then attributed on-chain to the effective trading account (`tx.from`, or the ERC-4337 userOp sender when the trade was bundled). Robinhood Chain coverage is **bundled into every MadeOnSol tier at no extra cost — same `msk_` API key, same base URL** as the Solana product.
17
17
 
18
+ > **New in 0.13.0 (API parity 2026-10-03).** **Verified holdings:** `client.wallet.profile()` gains `holdings` (`RhcHoldingsSummary`, null when the snapshot was unavailable) and `client.wallet.positions()` rows are now `RhcVerifiedOpenPosition` (`current_onchain_balance`, `holding_status`: `HELD` / `PARTIALLY_REDUCED` / `TRANSFERRED_OR_DISPOSED` / `EXTERNAL_INFLOW` / `BALANCE_UNVERIFIED`, `holding_unverified_reason`, held/not-held cost basis) with `summary.holdings`. FIFO-open is a trading position, not a balance: only `holdings.verified_value_eth` counts proven balances. **Copy-trade:** `operational_state` (`eligible` / `no_tracked_sources` / `unknown`, separate from `is_active`), `source_wallets_tracked` / `source_wallets_untracked`, `warnings`. **New methods:** `client.tokens.locksFeed()` → `GET /rhc/tokens/locks`, `client.tokens.locks(address)` → `GET /rhc/tokens/{address}/locks`, `client.tokens.unlocks()` → `GET /rhc/tokens/unlocks` (PRO+; rows carry `provider` identity verified / compatible / unverified, Blockscout `explorer` links, `price_usd`, `seconds_until_end` / `seconds_until_next_unlock`; create-only, withdrawals not tracked; raw amounts are strings), and `client.tokens.earlyBuyers(address)` → `GET /rhc/tokens/{address}/early-buyers` (PRO+, untyped body).
19
+
20
+ > **New in 0.13.0, continued (API parity 2026-10-03, second pass).** **Pagination:** `client.kol.feed()`, `client.kol.firstTouches()` and `client.deployer.alerts()` accept `cursor` (opaque strict keyset, preferred over the legacy `before`) and return `next_cursor`, `has_more`, `scan` (`FeedScanInfo`, on post-filtered pages) and the free-tier delay fields (`FreeTierDelayMeta`). `kol.feed()` also accepts `exclude_sells`, `min_kol_winrate` (0–1), `strategy`, `token_age_max_min`, `min_mc_usd` / `max_mc_usd` and returns `filtered_by` / `matched_kols`; alert rows carry `liquidity_basis` and the response `liquidity_note`. **Token snapshot:** `RhcTokenDeployer` reputation fields are nullable (null while the deployer is not yet in the reputation view) and gain `identity_status` / `history_status`; `RhcTokenKolActivity` gains `distinct_wallets`, `buy_eth`, `sell_eth`, `participants`, `identity`, `window`, `basis`, `complete` (+ `sample_size` on the fallback sample); `RhcTokenSnapshot` gains `deployer_identity_status`, `price_observed_at`, `price_age_seconds`, `price_is_stale`, `price_updated_at`, `liquidity_basis`, `liquidity_note`, `degraded_fields`. **Buyer quality:** `QualityConfidence` adds `insufficient_data`; breakdown gains `wallets_with_history` / `qualified_win_rate_wallets`; responses carry `cohort_selection` and `coverage.earliest_buyers_caveat`. **Stream token:** `StreamToken.dex_ws_url` (ULTRA / BUSINESS / ENTERPRISE only) plus `usage`, `subscribe_example`, `channels`, `rhc_token_prices`, `named_subscriptions`. **Early buyers:** `client.tokens.earlyBuyers()` is now typed (`RhcEarlyBuyersResponse` / `RhcEarlyBuyer`). Nullability changes on `RhcTokenDeployer` can break strict-null code that read those fields as non-null.
21
+
22
+ > **New in 0.12.0 — WS Phase 4 stream types.** `rhc:token_candles`, `rhc:token_risk` and `rhc:wallet_scores` channels, event names, payload and filter types (README example).
23
+
24
+ > **New in 0.11.0 — WS Phase 3 stream types.** `rhc:lp_events` channel, `rhc:lp_event` and unlock-schedule event names, `RhcLpStreamEvent` + filters, `/rhc/lp-events` `action` and depth fields.
25
+
18
26
  > **New in 0.10.0 — named subscriptions: several independent subscriptions per socket.** `subscribe({ subId, channels, filters })`, `updateSubscription(subId, filters)`, `unsubscribe(subId)`, `getSubscriptions()` / `listSubscriptions()`. Each named subscription has its own channels and filters (the server caps the total per connection, default included: PRO 5, ULTRA 10, BUSINESS 20); frames carry `evt.sub_id`; an event matching several subscriptions is delivered once per subscription (dedupe per `(sub_id, id)`). Resume is per subscription with one commit for the connection. The plain `subscribe(channels, filters)` API is unchanged. See "Named subscriptions" in the stream section.
19
27
 
20
28
  > **Also in 0.10.0 — `rhc:token_prices` and enriched RHC trade payloads (WS Phase 2).** A tenth RHC channel, `rhc:token_prices` (PRO+, address-scoped): subscribe with `filters.addresses` (25 / 100 / 250 per connection on PRO / ULTRA / BUSINESS, rejected above the cap) and receive one `snapshot: true` frame per address, then ticks derived from the RHC trade feed at most once per address per 250 ms, each typed `RhcTokenPriceTick` with `quality` fresh | stale | unreliable and a `quality_reason`; a stale or unreliable price is never delivered as fresh. `rhc:dex_trade` / `rhc:dex_trade_unattributed` frames now carry additive enrichment, typed `RhcDexTradeEvent` / `RhcDexTradeUnattributedEvent`: exact `amount_in_raw` / `amount_out_raw` decimal strings (never floats), `token` + `quote` identity with decimals, `metadata_status`, `price_status` / `price_source` / `price_observed_at`, `mc_status` (why `mc_usd` is null) and `side` / `side_reason`. Every existing field keeps its name.
@@ -585,7 +593,7 @@ await client.kol.firstTouchSubscriptions.update(subscription.id, { filters: {} }
585
593
 
586
594
  ## Streaming — `client.stream` (PRO+)
587
595
 
588
- Managed WebSocket with token fetch on every (re)connect, auto-reconnect with backoff, heartbeat liveness, and typed events. Stream tokens **never expire** (since 2026-08-27) — there is no refresh timer; `client.stream.getToken()` returns the same token every call (`expires_at` / `next_refresh_at` are always `null`), and `getToken({ rotate: true })` replaces it (the old one keeps working for 60 s). Eleven RHC channels:
596
+ Managed WebSocket with token fetch on every (re)connect, auto-reconnect with backoff, heartbeat liveness, and typed events. Stream tokens **never expire** (since 2026-08-27) — there is no refresh timer; `client.stream.getToken()` returns the same token every call (`expires_at` / `next_refresh_at` are always `null`), and `getToken({ rotate: true })` replaces it (the old one keeps working for 60 s). Fourteen RHC channels:
589
597
 
590
598
  | Channel | Emits | Tier | Scope |
591
599
  |---|---|---|---|
@@ -600,6 +608,9 @@ Managed WebSocket with token fetch on every (re)connect, auto-reconnect with bac
600
608
  | `rhc:token_locks` | `rhc:token_lock` (+ `rhc:token_unlock_upcoming`, `rhc:token_unlock_available` with `filters.lifecycle: true`) | PRO+ | broadcast — a token lock / vesting contract created on chain; opt-in unlock-schedule events (claims / cancels are not observable on RHC) |
601
609
  | `rhc:token_prices` | `rhc:token_price` | PRO+ | **address-scoped** — `filters.addresses` required (25 / 100 / 250 per connection); one `snapshot: true` frame per address, then ≤ 1 tick per address per 250 ms with `quality` fresh / stale / unreliable + reason (`RhcTokenPriceTick`); no `seq` / `id` |
602
610
  | `rhc:lp_events` | `rhc:lp_event` | **ULTRA+** | broadcast — liquidity `add` / `remove` / `pool_created` on tracked Uniswap v2/v3/v4 pools with `in_range`, `active_share`, `share_of_reserves`, `material` (`RhcLpStreamEvent`); filters `RhcLpEventsFilters`; durable resume |
611
+ | `rhc:token_candles` | `rhc:candle_closed`, `rhc:candle_revised` (+ `rhc:candle_update` with `filters.updates: true`) | PRO+ | scoped — live 1-minute candles for `filters.addresses` (`RhcCandleClosedEvent` / `RhcCandleUpdateEvent`); durable resume |
612
+ | `rhc:token_risk` | `rhc:risk_verdict_changed` (+ `rhc:risk_verdict` snapshot) | PRO+ | scoped — risk-verdict changes for `filters.addresses` (`RhcRiskVerdictChangedEvent`); score HIGHER = SAFER |
613
+ | `rhc:wallet_scores` | `rhc:deployer_tier_changed` | PRO+ | scoped — deployer tier changes for `filters.wallets` (0x deployers, `RhcDeployerTierChangedEvent`) |
603
614
 
604
615
  **Liquidity events and lock schedule (server 2026-09-23).** `rhc:lp_events` frames carry raw `amount0` / `amount1` strings (null on v4), the position range and, for v3/v4, `in_range` / `active_liquidity_delta` / `active_share` — a share of liquidity at the current price, not of TVL (an out-of-range removal is `active_share: 0`); v2 carries `share_of_reserves`; `material: true` = a removal of ≥ 25 %. When the pool's tick was not known the active fields are `null` with `active_share_reason: "pool_state_unknown"` — never guessed. `provider` is usually the router / position manager. No USD field. On `rhc:token_locks`, `filters.lifecycle: true` adds `rhc:token_unlock_upcoming` (within 24 h) and `rhc:token_unlock_available` (passed within 30 min — **claimable per the schedule, not claimed**; `withdrawals_tracked: false`).
605
616
 
@@ -609,6 +620,18 @@ lp.subscribe({ subId: "rugs", channels: ["rhc:lp_events"], filters: { actions: [
609
620
  lp.on("rhc:lp_event", (e: RhcLpStreamEvent) => console.log(e.dex, e.pool, e.action, e.active_share ?? e.share_of_reserves));
610
621
  ```
611
622
 
623
+ **Candles, risk verdicts and deployer tiers (server 2026-09-23).** All three are PRO+ and **scoped**: per-connection cap PRO 25 / ULTRA 100 / BUSINESS 250 across named subscriptions; over the cap or without a scope the channel is rejected, never truncated. `rhc:token_candles` (`filters.addresses`, a budget separate from `rhc:token_prices`): `rhc:candle_closed` is the stored 1-minute row (id `candle:robinhood:<address>:<bucket epoch s>`), `rhc:candle_revised` the same row rewritten (`revision` n > 0, id suffix `:r<n>`); `filters.updates: true` adds `rhc:candle_update`, the in-progress minute (≤ 1 per address per second, a state stream: no id, never replayed, no snapshot). `rhc:token_risk` (`filters.addresses`, optional `risk_events`, `risk_snapshot` default `true`): `rhc:risk_verdict_changed` when a sweep recheck stores a different verdict — the change happened somewhere in `(previous_checked_at, checked_at]` — plus one `rhc:risk_verdict` snapshot frame per address (`frame.snapshot === true`); `score` is **higher = safer**, the opposite of Solana's `risk_score`. `rhc:wallet_scores` (`filters.wallets`, 0x deployers, optional `score_events`): `rhc:deployer_tier_changed` after each 5-min refresh ("recomputed at T", not "changed at T").
624
+
625
+ ```ts
626
+ const s = client.stream.connect();
627
+ s.subscribe({ subId: "candles", channels: ["rhc:token_candles"], filters: { addresses: [TOKEN], updates: true } });
628
+ s.subscribe({ subId: "risk", channels: ["rhc:token_risk"], filters: { addresses: [TOKEN] } });
629
+ s.subscribe({ subId: "tiers", channels: ["rhc:wallet_scores"], filters: { wallets: [DEPLOYER] } });
630
+ s.on("rhc:candle_closed", (c: RhcCandleClosedEvent) => console.log(c.bucket_start, c.open_price_usd, c.close_price_usd, c.volume_usd));
631
+ s.on("rhc:risk_verdict_changed", (e: RhcRiskVerdictChangedEvent) => console.log(e.token_address, e.changed, e.before.score, "→", e.after.score));
632
+ s.on("rhc:deployer_tier_changed", (e: RhcDeployerTierChangedEvent) => console.log(e.address, e.tier_before, "→", e.tier_after));
633
+ ```
634
+
612
635
  > **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`.
613
636
 
614
637
  ```ts