/** * Type definitions for the MadeOnSol Robinhood Chain (chain id 4663) API. * * EVM-native throughout: token/wallet addresses are lowercase `0x` hex, * trade sizes are `eth_amount` / `*_eth`, on-chain refs are `tx_hash` + * `block_number`, and KOL positioning is denominated in `net_flow_eth`. * There are NO Solana field names here. Every field mirrors * GET /api/v1/rhc/… in the OpenAPI contract. */ /** Every RHC response echoes `chain: "robinhood"`. */ export type Chain = "robinhood"; /** Trade side. */ export type TradeAction = "buy" | "sell"; /** * Deployer reputation tier (elite → spammer). `elite`/`good` ride `runner_rate` * (the $100K peak-MC milestone) and require 24h of deployer history; `spammer` * keys off `graduation_rate` (the $40K bar). See `deployerLeaderboard()`. */ export type DeployerTier = "elite" | "good" | "neutral" | "spammer"; /** Deployer Hunter v2 (additive): every field below is present only when the * server runs RHC_DEPLOYER_REPUTATION=v2. The default v1 response keeps the * published v1 fields and omits these. */ /** Deployer Hunter v2 reputation summary embedded next to tokens / alerts. */ export interface RhcDeployerReputationSummary { tier: DeployerTier | null; /** "v2.0" (or "v1" only during an operator rollback). */ rule_version: string | null; resolved_launches: number | null; organic_runners: number | null; /** Organic runner rate over the last 10 resolved launches. */ recent_rate: number | null; survival_1h_rate: number | null; /** Launches behind survival_1h_rate. */ survival_sample: number | null; manipulation_status: "none" | "suspected" | "manipulated" | null; confidence: "high" | "medium" | "low" | null; } /** Deployer Hunter v2 per-launch outcome; null until the token is scored. */ export interface RhcTokenOutcome { rule_version: string | null; launched_at: string | null; detected_at: string | null; /** Market cap at detection: the only base a since-detected multiple may use. */ detected_mc_usd: number | null; /** Seconds between contract creation and detection. */ detection_delay_s: number | null; resolved: boolean | null; reached_100k: boolean | null; durable_runner: boolean | null; organic_runner: boolean | null; independent_traders: number | null; survived_1h: boolean | null; risk_status: "safe" | "unsafe" | "unknown" | null; manipulation_flags: string[]; manipulation_confidence: number | null; confidence: "high" | "medium" | "low" | null; } /** v2-only reputation fields on a full deployer object (additive, optional). */ export interface RhcDeployerV2Fields extends Partial> { lifetime_rate?: number | null; recent_resolved?: number | null; recent_organic?: number | null; recent_results?: Array<{ token: string; organic: boolean | null; peak_mc_usd: number | null; survived_1h: boolean | null; }> | null; independent_traders_median?: number | null; manipulation_confidence?: number | null; unsafe_recent_share?: number | null; cohort_7777_share?: number | null; instant_bundled_share?: number | null; history_days?: number | null; coverage?: Record | null; computed_at?: string | null; /** v2 gate codes explaining the tier (empty for elite; non_human_creator: for non-person creators). */ tier_reasons?: string[] | null; } /** Uniswap DEX versions live on Robinhood Chain. */ export type Dex = "uniswap-v2" | "uniswap-v3" | "uniswap-v4"; /** Standard API error envelope. */ /** * How a `liquidity_usd` figure was derived. uniswap-v4 has no per-pool * balances, so its figure is virtual reserves clamped to the PoolManager's total * holdings — a provable CEILING, not measured per-pool TVL. Every other venue is * `measured` from real balances. */ export type RhcLiquidityBasis = "v4_virtual_ceiling" | "measured"; /** * Where `liquidity_usd` comes from (additive, more precise than * `liquidity_basis`): pool_balances = uniswap-v2/v3 balances; v4_virtual_ceiling * = uniswap-v4 ceiling; bonding_curve_reserve = 2x a launchpad curve quote * reserve (pons-v2, flap, hood.fun, bags); unknown_venue = no recognised venue. */ export type RhcLiquiditySource = "pool_balances" | "v4_virtual_ceiling" | "bonding_curve_reserve" | "unknown_venue"; /** `unavailable` = liquidity_usd is null (unknown, never zero); `verified_zero` = explicit 0. */ export type RhcLiquidityStatus = "available" | "verified_zero" | "unavailable"; /** * Precomputed risk summary (same engine as `/rhc/tokens/{address}/risk`). * `null` means NOT ASSESSED, not safe. A snapshot: always read `checked_at`. */ export interface RhcTokenRiskSummary { /** `no` = a sell was simulated through the router and REVERTED. */ sellable: "yes" | "no" | "unknown" | null; upgradeable: boolean | null; /** 0–100, conservative. */ score: number | null; checked_at: string; } export interface RhcError { error: string; _rid?: string; } export interface KolFeedParams { /** Page size (1–100, default 50). */ limit?: number; /** LEGACY cursor: ISO timestamp; returns trades strictly older than this (skips same-timestamp rows). Prefer `cursor`. */ before?: string; /** PREFERRED pagination: `next_cursor` from the previous page — opaque strict keyset (no skipped/repeated rows at shared timestamps). Cannot be combined with `before`. */ cursor?: string; /** Only buys or only sells. */ action?: TradeAction; /** Filter to a single KOL by their EVM wallet (0x, 40 hex). */ kol?: string; /** Minimum trade size in ETH. */ min_eth?: number; /** Alias for `action=buy` — drops sells. Ignored when `action` is set. */ exclude_sells?: boolean; /** Only trades where the token's MC at trade time was at least this (USD). */ min_mc_usd?: number; /** Only trades where the token's MC at trade time was at most this (USD); must be >= min_mc_usd. */ max_mc_usd?: number; /** Only trades on tokens first seen at most this many minutes ago (1–43200). Unknown first-seen is excluded. */ token_age_max_min?: number; /** KOLs whose 7-day win rate (closed positions only) is at least this (0–1). Unscored KOLs are dropped, so 0 means "scored". */ min_kol_winrate?: number; /** KOL hold-time bucket (RHC-specific, not Solana's auto_strategy_tag). */ strategy?: "scalper" | "day_trader" | "swing" | "inactive" | "unscored"; } export interface RhcKolTrade { /** The KOL's Robinhood-Chain wallet (0x). */ evm_address: string; kol_name: string | null; kol_twitter: string | null; token_address: string; token_symbol: string | null; token_name: string | null; /** pons | flap | clanker | hood.fun | virtuals | null. */ launchpad: string | null; is_graduated: boolean | null; deployer_tier: DeployerTier | null; token_age_minutes: number | null; action: TradeAction; /** Trade size in ETH. */ eth_amount: number | null; token_amount: number | null; price_usd_at_trade: number | null; market_cap_usd_at_trade: number | null; current_mc_usd: number | null; peak_mc_usd: number | null; liquidity_usd: number | null; liquidity_basis: RhcLiquidityBasis; /** Precomputed risk snapshot; null = not assessed (NOT safe). */ risk: RhcTokenRiskSummary | null; /** current_mc_usd ÷ market_cap_usd_at_trade — how far the token ran after the trade. */ mc_multiple_since_trade: number | null; /** uniswap-v2/v3/v4 or the launchpad name for curve trades. */ dex: string; pool: string | null; tx_hash: string; block_number: number; traded_at: string; /** The KOL's score row; null = no row yet (too few closed positions), never "not looked up". */ kol_score: RhcKolScore | null; } export interface RhcKolScore { /** 0–1, closed positions only (sold ≥ 90 % of bought). */ winrate_7d: number | null; winrate_30d: number | null; /** Hold-time bucket: scalper | day_trader | swing | inactive | unscored. */ strategy: string | null; closed_positions_30d: number | null; } export interface KolFeedResponse { chain: Chain; trades: RhcKolTrade[]; count: number; /** Echo of the KOL-score filters (null = not applied). */ filtered_by: { min_kol_winrate: number | null; strategy: string | null; }; /** KOLs matching min_kol_winrate/strategy; null when neither filter is set. */ matched_kols: number | null; data_age_seconds: number | null; next_before: string | null; /** Pass as `cursor` for the next (older) page; null at the end. */ next_cursor?: string | null; /** false only when the feed is exhausted. */ has_more?: boolean; /** Present when a filter was applied after the candidate fetch; scan_truncated=true means more matches MAY exist past next_cursor. */ scan?: { post_filtered: boolean; scanned: number; scan_truncated: boolean; scan_budget: number; }; } export type KolLeaderboardPeriod = "24h" | "7d" | "30d"; export interface KolLeaderboardParams { /** Activity ranking only (sort "trades"). */ period?: KolLeaderboardPeriod; limit?: number; /** "trades" (default, BASIC) or a FIFO win-rate ranking "winrate_7d" | "winrate_30d" (PRO+). */ sort?: "trades" | "winrate_7d" | "winrate_30d"; /** Win-rate sorts only: minimum fully closed positions in the window (1-100, default 3). */ min_closed?: number; } export interface RhcKolLeaderboardEntry { kol_name: string | null; kol_twitter: string | null; trades: number; buys: number; sells: number; buy_eth: number; sell_eth: number; /** buy_eth − sell_eth (flow, not realized PnL). */ net_eth: number; tokens_traded: number; last_trade_at: string; /** * Win-rate sorts only (sort "winrate_7d" | "winrate_30d"). With those sorts the * activity fields above are NOT returned; rows carry the fields below instead. * Same numbers as `kol(wallet, { include_performance: true })`. */ evm_address?: string; status?: "available" | "partial"; winrate_7d?: number | null; winrate_30d?: number | null; closed_positions_7d?: number; closed_positions_30d?: number; pnl_7d_eth?: number | null; pnl_30d_eth?: number | null; median_hold_minutes_30d?: number | null; auto_strategy_tag?: "scalper" | "day_trader" | "swing_trader" | "hodler" | null; data_fingerprint?: string | null; computed_at?: string; } export interface KolLeaderboardResponse { chain: Chain; /** Activity ranking only. */ period?: KolLeaderboardPeriod; leaderboard: RhcKolLeaderboardEntry[]; count: number; /** Win-rate sorts only. */ sort?: "winrate_7d" | "winrate_30d"; min_closed?: number; scores_meta?: { source: string; fresh_within_minutes: number; definition: string; }; } export type KolHotTokensWindow = "5m" | "15m" | "1h" | "6h" | "24h"; export interface KolHotTokensParams { window?: KolHotTokensWindow; } export interface RhcHotToken { token_address: string; token_symbol: string | null; token_name: string | null; launchpad: string | null; is_graduated: boolean | null; deployer_tier: string | null; /** Distinct KOL buyers in the window (>= 2). */ kols_buying: number; buys: number; sells: number; buy_eth: number; net_eth: number; market_cap_usd: number | null; last_trade_at: string; } export interface KolHotTokensResponse { chain: Chain; window: KolHotTokensWindow; tokens: RhcHotToken[]; count: number; } export interface RhcKolProfileStats { trades: number; buys: number; sells: number; buy_eth: number; sell_eth: number; net_eth: number; tokens_traded: number; /** e.g. "last 200 trades". */ window: string; } /** One of a KOL's 50 most recent trades (token, action, eth_amount, price/MC, dex, tx_hash, traded_at). */ export type RhcKolProfileTrade = Record; export interface RhcKolProfileResponse { chain: Chain; evm_address: string; kol_name: string | null; kol_twitter: string | null; stats: RhcKolProfileStats; trades: RhcKolProfileTrade[]; /** Only with `include_performance: true` (PRO+). */ performance?: RhcKolPerformance; } export interface RhcKolPerformanceWindow { closed_positions: number; wins: number; losses: number; breakeven: number; winrate: number | null; realized_pnl_eth: number | null; } /** KOL win rate + strategy from the 90-day FIFO wallet snapshot. Empty windows are null, never 0. */ export interface RhcKolPerformance { status: "available" | "partial" | "unavailable"; /** Set when unavailable: source_error, no_wallet_trades_in_window, no_sized_trades. */ reason?: string | null; /** Percent (0-100) of fully closed positions (last sell in window) with positive realized PnL. */ winrate_7d?: number | null; winrate_30d?: number | null; closed_positions_7d?: number; closed_positions_30d?: number; pnl_7d_eth?: number | null; pnl_30d_eth?: number | null; windows?: { "7d": RhcKolPerformanceWindow; "30d": RhcKolPerformanceWindow; }; median_hold_minutes_30d?: number | null; auto_strategy_tag?: "scalper" | "day_trader" | "swing_trader" | "hodler" | null; strategy_reason?: string | null; window_days: number; definition?: string; notes?: Record; } export interface TradesParams { limit?: number; /** Filter to one token address (0x, 40 hex). */ token?: string; dex?: Dex; action?: TradeAction; min_eth?: number; /** Opaque (block_time, id) keyset cursor — pass back the previous page's `next_before` unchanged. */ before?: string; } export interface RhcTrade { block_number: number; block_time: string; tx_hash: string; log_index: number; dex: string; pool: string; /** Swap-log recipient — the ROUTER for aggregated swaps. Use trader_eoa for analytics. */ trader: string | null; /** Effective trading account — `tx.from`, or the ERC-4337 userOp sender when bundled. Never the router or the bundler. */ trader_eoa: string | null; /** Router/aggregator contract (tx.to). */ router: string | null; token_address: string | null; /** 2026-10 — pool-pair attribution state, present only when recorded (`attributed` sets token/action; the others leave them null). */ pair_status?: "attributed" | "quote_quote" | "equity_equity" | "unsupported_pair"; action: TradeAction | null; eth_amount: number | null; price_native: number | null; price_usd: number | null; mc_usd_at_trade: number | null; /** Effective gas price, gwei. */ gas_price: number | null; /** Transaction position within the block (ordering / sandwich detection). */ tx_index: number | null; /** 4-byte calldata selector. */ method_selector: string | null; /** v3/v4 in-range liquidity at the trade. */ liquidity: number | null; launchpad: string | null; is_kol: boolean; kol_name: string | null; deployer_tier: DeployerTier | null; } export interface TradesResponse { chain: Chain; trades: RhcTrade[]; count: number; /** True when another page exists beyond this one. */ has_more: boolean; /** Opaque (block_time, id) keyset cursor — pass back as `before`; null when has_more is false. */ next_before: string | null; } export interface LpEventsParams { /** 1–200. Default 50. */ limit?: number; /** Filter to one token address (0x, 40 hex). */ token?: string; /** Pool address (v2/v3) or bytes32 poolId (v4). */ pool?: string; /** Filter to one liquidity provider — the wallet that pulled (0x, 40 hex). */ provider?: string; dex?: Dex; /** * Which actions to return (2026-09-23). Default `"remove"` = the historical * removals-only feed, unchanged. `"add"` (kept 7 days), `"pool_created"` or * `"all"` opt into rows persisted since the WS Phase 3 producer. */ action?: RhcLpAction | "all"; /** Opaque cursor from `next_before` (same (block_time,id) keyset as /rhc/trades). */ before?: string; } /** A liquidity action on Robinhood Chain. */ export type RhcLpAction = "add" | "remove" | "pool_created"; /** * One liquidity event. Without the `action` param every row is * `event: "remove"` (the default feed is removals only). Amounts are raw * uint256 integers as decimal STRINGS; v4 rows carry `liquidity` only (the * pool manager emits no token amounts). */ export interface RhcLpEvent { event: RhcLpAction; pool: string; dex: Dex; fee_tier: number | null; token_address: string; token_symbol: string | null; token_name: string | null; token_decimals: number | null; launchpad: string | null; /** Wallet that removed liquidity. */ provider: string | null; /** True when the provider is the token's own deployer — the classic rug shape. */ provider_is_token_deployer: boolean; provider_deployer_tier: string | null; provider_kol_name: string | null; /** Raw liquidity units removed (v3/v4). uint256 as string. */ liquidity: string | null; /** Raw token0 amount (v2/v3 only). uint256 as string. */ amount0: string | null; /** Raw token1 amount (v2/v3 only). uint256 as string. */ amount1: string | null; token0: string | null; token1: string | null; /** amount0 or amount1, whichever is the token side. */ token_amount_raw: string | null; quote_token: string | null; quote_amount_raw: string | null; block_number: number; /** Exact block header timestamp (ISO 8601). */ block_time: string; tx_hash: string; log_index: number; /** Position range (v3/v4); null on v2 (full range). */ tick_lower?: number | null; tick_upper?: number | null; /** Signed liquidity change (v3/v4), int256 as string. */ liquidity_delta?: string | null; /** tick_lower <= current tick < tick_upper just before the event; null = tick unknown. */ in_range?: boolean | null; /** liquidity_delta when in range, "0" out of range, null when unknown. */ active_liquidity_delta?: string | null; /** |active delta| / active liquidity before (v3/v4); 0 out of range; adds can exceed 1. */ active_share?: number | null; /** v2 only: amount / reserve before (from the pair's Sync). */ share_of_reserves?: number | null; /** true = a removal of ≥ 25 % of reserves / active liquidity; null for adds, creations and unknown shares. */ material?: boolean | null; } export interface LpEventsResponse { chain: Chain; events: RhcLpEvent[]; count: number; has_more: boolean; next_before: string | null; /** Honesty block: the actions this response covers (["remove"] by default), adds_persisted, note, since. */ coverage: { events: string[]; adds_persisted: boolean | null; adds_retention_days?: number; note: string; since: string; }; } /** Why `active_share` is null / not the relevant share. */ export type RhcLpActiveShareReason = "pool_created" | "not_concentrated" | "pool_state_unknown" | "no_active_liquidity"; /** * `rhc:lp_event` frame `data` (channel `rhc:lp_events`, ULTRA / BUSINESS). * Frame id = `rhc:lp_event::`, identical live and on * resume. Every key is always present (null when unknown). No USD field. */ export interface RhcLpStreamEvent { chain: Chain; action: RhcLpAction; dex: Dex; /** Pool address (v2/v3) or bytes32 poolId (v4). */ pool: string; token_address: string | null; /** symbol ≤ 32 code points; symbol / decimals null until the token's metadata resolves. */ token: { address: string | null; symbol: string | null; decimals: number | null; }; token0: string | null; token1: string | null; /** The log's sender / owner — usually the router (v2) or position manager (v3/v4), NOT the beneficial owner. */ provider: string | null; /** Positive liquidity minted / burned (v3/v4); null on v2. uint as string. */ liquidity: string | null; /** Raw token amounts (v2/v3). NULL on v4 — ModifyLiquidity reports none (not zero). */ amount0: string | null; amount1: string | null; tick_lower: number | null; tick_upper: number | null; liquidity_delta: string | null; /** null = the pool's current tick was unknown (never guessed). */ in_range: boolean | null; active_liquidity_delta: string | null; active_share: number | null; active_share_reason: RhcLpActiveShareReason | null; share_of_reserves: number | null; /** true = a REMOVAL of ≥ 25 % of reserves (v2) or active liquidity (v3/v4); false = smaller known share; null otherwise. */ material: boolean | null; block_number: number | null; block_time: string | null; tx_hash: string; log_index: number | null; } /** * Per-subscription filters for `rhc:lp_events` (all optional, AND). They apply * only to `rhc:lp_event` frames. A malformed value rejects the channel * (`channels_rejected`) or refuses the update (`invalid_filters`). */ export interface RhcLpEventsFilters { /** Token addresses, case-insensitive, ≤ 250. */ addresses?: string[]; /** Pool addresses (0x + 40 hex) or v4 poolIds (0x + 64 hex), ≤ 250. */ pools?: string[]; dexes?: Dex[]; actions?: RhcLpAction[]; /** Only `material: true` events. */ material_only?: boolean; /** 0..1000 — only events whose relevant share (reserves on v2, active on v3/v4) is known and ≥ this. */ min_share?: number; } /** Unlock-schedule event names on `rhc:token_locks` (only with `filters.lifecycle: true`). */ export type RhcTokenLockLifecycleEventName = "rhc:token_unlock_upcoming" | "rhc:token_unlock_available"; /** * Filters that switch `rhc:token_locks` to lifecycle mode. Without * `lifecycle: true` the channel carries only the `rhc:token_lock` create * event, unchanged. Claims, extensions and cancels are NOT observable on * Robinhood Chain — only the unlock schedule is. */ export interface RhcTokenLockLifecycleFilters { lifecycle: true; /** Subset of the lifecycle event names; creates always pass. */ events?: RhcTokenLockLifecycleEventName[]; unlock_kinds?: Array<"cliff" | "final" | "tranche">; /** Token scope for creates AND lifecycle (case-insensitive, ≤ 500); applied only with `lifecycle: true`. */ addresses?: string[]; } /** * `rhc:token_unlock_upcoming` (the lock's NEXT unlock instant, when it is within 24 h) / * `rhc:token_unlock_available` (an unlock instant passed within the last * 30 min — claimable PER THE SCHEDULE, NOT claimed). Frame id = * `:`. */ export interface RhcTokenUnlockScheduleEvent { /** `::` — durable dedupe key. */ event_key: string; lock_id: string; token_address: string; family: RhcLockFamily; observed_at: string | null; unlock_at: string; unlock_kind: "cliff" | "final" | "tranche"; /** Discrete jump at that instant; null + amount_reason (e.g. `position_nft_no_token_amount`) when unknown. */ amount_raw: string | null; amount_reason: string | null; unlocked_total_raw: string | null; release_model: "at_end" | "linear" | "tranched"; /** Only on `rhc:token_unlock_available`. */ claimable?: true; chain: Chain; locker: string | null; kind: RhcLockKind | null; subject: RhcLockSubject | null; lp_kind: string | null; lp_pool: string | null; sender: string | null; recipient: string | null; locked_amount_raw: string | null; amount_unit: string | null; /** Always null for LP locks (amounts are liquidity units). */ decimals: number | null; /** Always false: withdrawals are not observable on RHC. */ withdrawals_tracked: false; } /** * `rhc:token_candles` subscribe filters (PRO+). `addresses` is REQUIRED (0x, * case-insensitive) and capped per CONNECTION across named subscriptions — * PRO 25 / ULTRA 100 / BUSINESS 250, a budget separate from * `rhc:token_prices`. Over the cap, a missing scope or a non-boolean * `updates` rejects the channel, never truncates. */ export interface RhcTokenCandlesFilters { addresses: string[]; /** Also receive the in-progress minute as `rhc:candle_update` (a state stream). Default false. */ updates?: boolean; } /** * `rhc:candle_closed` / `rhc:candle_revised` frame `data` — the STORED * 1-minute row (`rhc_ohlc_1m`), identical live and on a durable resume. * Every key is always present (null when unknown). Frame id = * `candle:robinhood:
:`, plus `:r` for the * n-th revision (not event-prefixed). `revision` 0 = the close as first * written; a rewrite (restart merge, spoofed-pool purge) bumps it and is * announced as `rhc:candle_revised` with the full rewritten row. */ export interface RhcCandleClosedEvent { chain: Chain; address: string; bucket_start: string; /** bucket_start + 60 s. */ bucket_end: string; /** When the row was first written. */ closed_at: string | null; revision: number; /** Latest rewrite; null for revision 0. */ revised_at: string | null; open_price_usd: number | null; high_price_usd: number | null; low_price_usd: number | null; close_price_usd: number | null; open_mc_usd: number | null; high_mc_usd: number | null; low_mc_usd: number | null; close_mc_usd: number | null; close_liquidity_usd: number | null; close_supply: number | null; volume_usd: number | null; buy_volume_usd: number | null; sell_volume_usd: number | null; trades: number | null; buy_count: number | null; sell_count: number | null; dex: string | null; pool_address: string | null; final: true; source: "rhc_ohlc_1m"; } /** * `rhc:candle_update` frame `data` (only with `filters.updates: true`) — the * producer's in-progress minute, ≤ 1 per address per second. A state stream: * no id / seq, never replayed, no snapshot on subscribe. */ export interface RhcCandleUpdateEvent { chain: Chain; address: string; bucket_start: string; bucket_end: string; open_price_usd: number | null; high_price_usd: number | null; low_price_usd: number | null; close_price_usd: number | null; close_mc_usd: number | null; volume_usd: number | null; trades: number | null; final: false; /** The producer's timestamp of this state (ISO). */ as_of: string | null; source: "rhc-dex-stream:open_candle"; } /** * `rhc:token_risk` subscribe filters (PRO+). `addresses` is REQUIRED (0x, * lowercased), capped per connection PRO 25 / ULTRA 100 / BUSINESS 250. */ export interface RhcTokenRiskFilters { addresses: string[]; /** Non-empty subset of the channel's event types. */ risk_events?: Array<"rhc:risk_verdict_changed">; /** Send one `rhc:risk_verdict` snapshot per address on subscribe / scope change / resume. Default true. */ risk_snapshot?: boolean; } /** A stored RHC risk verdict (event `before` / `after`, snapshot `verdict`). */ export interface RhcRiskVerdict { /** 100 − penalties: HIGHER = SAFER — the opposite of Solana's `risk_score`; never compare the two. */ score: number | null; sellable: boolean | null; sellable_reason: string | null; proxy_kind: string | null; upgradeable: boolean | null; owner_model: string | null; can_mint: boolean | null; can_pause: boolean | null; lp_custody: string | null; lp_burned_pct: number | null; code_size: number | null; flags: string[]; } /** * `rhc:risk_verdict_changed` — a sweep recheck stored a verdict that differs * in a diffed field. The change happened somewhere in * `(previous_checked_at, checked_at]` (days to weeks), never at `checked_at`. * A first assessment is never an event. Frame id = * `rhc:risk_verdict_changed:`. */ export interface RhcRiskVerdictChangedEvent { chain: Chain; token_address: string; /** `:verdict:`. */ event_key: string; changed: Array<"score" | "sellable" | "proxy_kind" | "upgradeable" | "owner_model" | "can_mint" | "can_pause" | "lp_custody" | "flags">; flags_added: string[]; flags_removed: string[]; before: RhcRiskVerdict; after: RhcRiskVerdict; checked_at: string; previous_checked_at: string | null; score_semantics: "higher_is_safer"; detection: { method: "recheck_sweep"; recheck_target_hours: number; sweep_interval_minutes: number; min_liquidity_usd: number; note: string; }; written_at: string; source: "rhc_token_risk"; } /** * `rhc:risk_verdict` snapshot frame `data` (frame `snapshot: true`, no id / * seq): the CURRENT stored verdict of one scoped address. */ export interface RhcRiskVerdictSnapshot { chain: Chain; token_address: string; /** false = never assessed (verdict and checked_at null). */ assessed: boolean; verdict: RhcRiskVerdict | null; checked_at: string | null; score_semantics: "higher_is_safer"; source: "rhc_token_risk"; } /** * Robinhood Chain developer activity: the token's RECORDED developer * (`deployer_wallet`, from the launchpad creation or, weaker, the first pool's * creator — see `provenance.attribution`) buying or selling its own token. * Same contract as Solana `dev:activity`, with `chain: "robinhood"`, * `signature` = tx hash, `slot` = block number, `block_time` = chain time and * `quote_amount` = the ETH value of the leg. * * NO-TRANSFER BOUNDARY (v1): only `dev_buy` / `dev_sell` are ever delivered. * A developer moving tokens to another wallet is NOT on this channel, and the * absence of an event is not evidence that no transfer happened. A developer * trading through a different wallet is not linked (no RHC identity builder). * Dedupe on `id` (`dx1::::`): legs of one tx collapse. */ export interface RhcDevActivityEvent { id: string; v: number; type: "dev_buy" | "dev_sell"; chain: "robinhood"; /** Token address (0x, lower-case). */ mint: string; deployer_wallet: string; actor_wallet: string; actor_role: "deployer"; action: "buy" | "sell"; /** Transaction hash. */ signature: string; /** Block number. */ slot: number | null; block_time: string | null; observed_at: string | null; region: null; /** Raw uint256 token amount as a decimal string. */ token_amount_raw: string | null; token_decimals: number | null; token_amount: number | null; /** ETH value of the leg. */ quote_amount: number | null; quote_mint: string | null; venue: string | null; counterparty_wallet: null; counterparty_class: null; supply_pct: number | null; identity_evidence: false; /** true when the trade was re-read by a reconnect catch-up (delivered late). */ catchup_replay?: boolean; provenance: { source: "rhc_dex_stream"; decoder: "rhc-swap"; map_source: "launchpad" | "pool_creator"; /** `creator` (launchpad saw the creation) or `pool_creator` (only the first pool's creator is known — weaker). */ attribution: "creator" | "pool_creator"; map_age_ms: number | null; mode: "on"; /** The developer was resolved after the trade (bounded point read), so the frame is late. */ late_classified: boolean; }; } /** `rhc:dev_activity` subscribe / webhook filters (all optional, AND). */ export interface RhcDevActivityFilters { types?: Array<"dev_buy" | "dev_sell">; /** 0x token addresses (case-insensitive). */ addresses?: string[]; /** 0x wallets matched against deployer_wallet or actor_wallet. */ deployers?: string[]; action?: "buy" | "sell"; } /** * `rhc:wallet_scores` subscribe filters (PRO+). `wallets` is REQUIRED — 0x * deployer addresses (lowercased) — capped per connection PRO 25 / ULTRA 100 / * BUSINESS 250. A subscription that also holds the Solana `wallet:scores` * channel may mix in base58 wallets; an entry of a chain the subscription does * not hold is refused. */ export interface RhcWalletScoresFilters { wallets: string[]; /** Optional subset of the held channels' event types. */ score_events?: Array<"rhc:deployer_tier_changed" | "deployer:tier_changed" | "kol:score_state_changed">; } /** * `rhc:deployer_tier_changed` — the 5-min `mv_rhc_deployers` refresh found a * different tier ("recomputed at T", not "changed at T"). A deployer appearing * `neutral` is not an event; entering a ranked tier is (`tier_before: * "neutral"`), a first appearance already ranked has `tier_before: null` + * `first_appearance: true`, vanishing from the matview has `tier_after: null`. * Frame id = `rhc:deployer_tier_changed:`. */ export interface RhcDeployerTierChangedEvent { /** `
:`. */ event_key: string; chain: Chain; address: string; tier_before: DeployerTier | null; tier_after: DeployerTier | null; first_appearance: boolean; /** * @deprecated v1 stats from the v1 rollup `mv_rhc_deployers` ($40K / $100K all-time * peak milestones). Kept with its v1 meaning after the Deployer Hunter v2 cutover; * it does not set the v2 tier, read `stats_v2`. null when the deployer has no v1 row. */ stats: { tokens_deployed: number | null; graduated: number | null; graduation_rate: number | null; runners: number | null; runner_rate: number | null; best_peak_mc_usd: number | null; first_deploy_at: string | null; last_deploy_at: string | null; } | null; computed_at: string; /** "recomputed at T by a periodic refresh" (v1 and v2 alike); where the tier came from is `tier_source`. */ source: "matview_refresh"; /** The rollup `stats` is read from. */ matview: "mv_rhc_deployers"; /** Deployer Hunter v2 events only (after the cutover): "v2.0". Absent on v1 events. */ rule_version?: "v2.0"; /** v2 only: the table the tier was computed from. */ tier_source?: "rhc_deployer_reputation_v2"; /** v2 only: the v2 facts behind the tier. null when the deployer left the v2 ranking. */ stats_v2?: { tokens_deployed: number | null; resolved_launches: number | null; organic_runners: number | null; lifetime_rate: number | null; recent_rate: number | null; manipulation_status: "none" | "suspected" | "manipulated" | null; first_deploy_at: string | null; } | null; } export type RhcLockFamily = "pinklock" | "teamfinance" | "teamfinance-nft" | "uncx-v2-lp" | "uncx-v3-lp" | "uncx-vesting" | "vesting-fork" | "goplus" | "titan" | "titan-position" | "titan-vesting" | "hoodlock" | "hoodlock-vesting" | "sablier" | "stonkbrokers-vesting"; export type RhcLockKind = "lock" | "vesting"; export type RhcLockSubject = "token" | "lp"; export type RhcLockStatus = "active" | "completed"; export interface TokenLocksParams { /** 1–100. Default 50. */ limit?: number; /** ISO instant — only locks created after it (poll cursor = pagination.next_since). */ since?: string; /** ISO instant — only locks created before it (page back = pagination.next_before). LEGACY strict bound — prefer `cursor`. */ before?: string; /** Opaque strict (created_at, id) cursor — `pagination.next_cursor`. Not combinable with `before`. */ cursor?: string; token?: string; /** Depositor / creator wallet. */ sender?: string; /** Beneficiary wallet. */ recipient?: string; /** Locker contract address. */ locker?: string; family?: RhcLockFamily; kind?: RhcLockKind; /** token (default) excludes LP locks; lp = only LP locks; all = both. */ subject?: RhcLockSubject | "all"; status?: RhcLockStatus; /** Post-filter on the deposited amount in USD. */ min_usd?: number; /** Post-filter on the deposited amount as % of supply. */ min_pct_of_supply?: number; } export interface RhcLockNextUnlock { at: string; kind: "cliff" | "final" | "tranche"; amount_raw: string; amount: number | null; amount_usd: number | null; } /** * Who runs the lock contract, and how sure the server is (2026-10-02). * Identity is decided by the locker CONTRACT ADDRESS, never by the family: * a family is only an event shape (ABI) and forks copy shapes byte-for-byte. * `verified` = a known provider deployment (HoodLock vault + vesting, Sablier * Lockup v4, Titan Locker V2.1); `compatible` = the events match a known * provider's ABI (`compatible_with`) but the operator is NOT identified, so * `id` / `website_url` / `lock_url` are null; `unverified` = unknown shape. * `lock_url` is set only where the per-lock page format is proven (the HoodLock * vault's `/proof/lock/{locker_lock_id}`) — never guessed. */ export interface RhcLockProvider { id: string | null; name: string | null; identity: "verified" | "compatible" | "unverified"; compatible_with: string | null; website_url: string | null; lock_url: string | null; } /** Independent on-chain evidence on Robinhood Chain Blockscout (2026-10-02). */ export interface RhcLockExplorer { locker_url: string | null; creation_tx_url: string | null; } /** * One lock / vesting contract. Amounts are raw base units as decimal STRINGS; * ui / usd / pct are null when decimals or price are unknown. `withdrawn_*` * is ALWAYS null — withdrawals are not tracked on RHC (create-only tape). */ export interface RhcTokenLock { /** `:` — the row identity. */ lock_id: string; locker: string; locker_name: string | null; /** 2026-10-02 — who runs the locker; compatible ≠ verified (see {@link RhcLockProvider}). */ provider: RhcLockProvider; /** 2026-10-02 — Blockscout links for the locker contract and the creation tx. */ explorer: RhcLockExplorer; family: RhcLockFamily; family_name: string; locker_lock_id: string | null; kind: RhcLockKind; subject: RhcLockSubject; status: RhcLockStatus; token_address: string; /** Only on subject=lp. */ lp: { kind: "v2_pair" | "v3_position" | "v4_position" | null; pool: string | null; token0: string | null; token1: string | null; token_id: string | null; } | null; /** Depositor / creator — the dev-lock comparison key. */ sender: string; recipient: string | null; tx_sender: string | null; name: string | null; amount_raw: string | null; amount: number | null; amount_usd: number | null; /** 2026-10-02 — the token price behind every `*_usd` field; always null on subject=lp rows. */ price_usd: number | null; /** % of CURRENT supply; null when unknown, above 100.5, or on subject=lp rows. */ amount_pct_of_supply: number | null; /** `token` | `lp_token` (v2 pair) | `liquidity` (uncx-v3) | null (NFT position). LP units never get usd / price / %. */ amount_unit: string | null; locked_raw: string | null; locked: number | null; locked_usd: number | null; locked_pct_of_supply: number | null; unlocked_raw: string | null; unlocked: number | null; withdrawn_raw: null; withdrawn: null; start_at: string | null; cliff_at: string | null; end_at: string | null; /** 2026-10-02 — seconds until fully unlocked (>= 0); 0 once completed; null without an end date (perpetual). */ seconds_until_end: number | null; /** 2026-10-02 — seconds until `next_unlock.at` (>= 0); null without a next unlock. */ seconds_until_next_unlock: number | null; cliff_amount_raw: string | null; cliff_amount: number | null; continuous: boolean; perpetual: boolean; schedule?: Array<{ release_at: string; amount_raw: string; amount: number | null; }>; next_unlock: RhcLockNextUnlock | null; cancelable: boolean | null; cancelable_by_sender: boolean | null; transferable: boolean | null; created_at: string; created_at_estimated: false; block_number: number; block_time: string; tx_hash: string; log_index: number; layout_verified: boolean; token?: { symbol: string | null; name: string | null; decimals: number | null; price_usd: number | null; market_cap_usd: number | null; liquidity_usd: number | null; }; } export interface RhcLockCoverage { families: readonly string[]; withdrawals_tracked: false; cancels_tracked: false; lp_locks: string; note: string; } export interface TokenLocksResponse { chain: Chain; locks: RhcTokenLock[]; pagination: { limit: number; count: number; has_more: boolean; next_cursor?: string | null; next_since: string | null; next_before: string | null; scan?: { post_filtered: boolean; scanned: number; scan_truncated: boolean; scan_budget: number; }; }; stream: { channel: string; note?: string; [k: string]: unknown; }; coverage: RhcLockCoverage; meta: { families: readonly string[]; note: string; }; } export interface TokenLockSummaryParams { status?: RhcLockStatus; family?: RhcLockFamily; /** Default all. */ subject?: RhcLockSubject | "all"; /** 1–500. Default 200 (the summary always covers every row). */ limit?: number; } export interface TokenLockSummaryResponse { chain: Chain; token_address: string; token: { symbol: string | null; name: string | null; decimals: number | null; price_usd: number | null; supply: number | null; market_cap_usd: number | null; liquidity_usd: number | null; facts_resolved: boolean; }; summary: { lock_count: number; complete: boolean; rows_considered: number; token_lock_count: number; lp_lock_count: number; lp_lock_active_count: number; active_count: number; by_family: Record; by_kind: Record; distinct_lockers: number; distinct_locker_contracts: number; locked_raw: string; locked: number | null; locked_usd: number | null; locked_pct_of_supply: number | null; deposited_raw: string; deposited: number | null; deposited_usd: number | null; unlocking_7d_raw: string; unlocking_7d: number | null; unlocking_7d_usd: number | null; unlocking_7d_pct_of_supply: number | null; unlocking_30d_raw: string; unlocking_30d: number | null; unlocking_30d_usd: number | null; unlocking_30d_pct_of_supply: number | null; next_unlock: (RhcLockNextUnlock & { lock_id?: string; }) | null; active_cancelable_by_sender: number; }; locks: RhcTokenLock[]; coverage: RhcLockCoverage; meta: { families: readonly string[]; note: string; source: string; }; } export type UnlocksWithin = "1h" | "6h" | "24h" | "3d" | "7d" | "14d" | "30d" | "90d"; export interface TokenUnlocksParams { /** Default 7d. */ within?: UnlocksWithin; token?: string; family?: RhcLockFamily; kind?: RhcLockKind; /** On the next-event amount. */ min_usd?: number; /** On the next-event amount. */ min_pct_of_supply?: number; /** Default soonest. */ sort?: "soonest" | "largest_usd" | "largest_pct"; /** 1–200. Default 50. */ limit?: number; } export interface RhcTokenUnlock { unlock_at: string; in_seconds: number; event: "cliff" | "final" | "tranche"; amount_raw: string; amount: number | null; amount_usd: number | null; amount_pct_of_supply: number | null; window_amount_raw: string; window_amount: number | null; window_amount_usd: number | null; window_amount_pct_of_supply: number | null; token_address: string; token: { symbol: string | null; name: string | null; decimals: number | null; price_usd: number | null; market_cap_usd: number | null; }; lock: { lock_id: string; locker: string; locker_name: string | null; family: RhcLockFamily; kind: RhcLockKind; name: string | null; sender: string; recipient: string | null; amount_raw: string; amount: number | null; amount_usd: number | null; locked_raw: string; locked: number | null; locked_usd: number | null; cliff_at: string | null; end_at: string | null; cancelable_by_sender: boolean | null; tx_hash: string; }; } export interface TokenUnlocksResponse { chain: Chain; window: { within: UnlocksWithin; from: string; to: string; }; unlocks: RhcTokenUnlock[]; pagination: { limit: number; count: number; total_in_window: number; has_more: boolean; candidates_capped: boolean; }; coverage: RhcLockCoverage; meta: { families: readonly string[]; note: string; source: string; }; } /** `newest` = first_seen_at DESC (launch feed, adds next_since); `oldest` = first_seen_at ASC (backfill walk, adds next_cursor/has_more). */ export type TokensSort = "last_trade" | "market_cap" | "liquidity" | "peak_mc" | "newest" | "oldest"; export interface TokensParams { limit?: number; sort?: TokensSort; min_mc_usd?: number; min_liquidity_usd?: number; /** pons, flap, clanker, hood.fun, noxa, virtuals. */ launchpad?: string; /** Only with sort=newest: tokens first seen at or after this ISO time (inclusive — dedupe on token_address). Feed back `next_since`. */ since?: string; /** LEGACY, only with sort=oldest: inclusive ISO lower bound. Prefer `cursor`. Cannot be combined with `cursor`. */ after?: string; /** Only with sort=oldest (preferred): `next_cursor` from the previous page — strict (first_seen_at, address) keyset. */ cursor?: string; /** Filter on whether the token has a discovered pool. */ has_pool?: boolean; /** equity = beacon-verified Robinhood tokenized stock/ETF; other = everything else. */ asset_class?: "equity" | "other"; /** Value sorts only (market_cap / liquidity / peak_mc): exclude tokens whose last trade is older than this (1–365, default 30). */ max_age_days?: number; } export interface RhcTokenListItem { token_address: string; symbol: string | null; name: string | null; decimals: number | null; /** "equity" = beacon-verified Robinhood tokenized stock/ETF; null = everything else. */ asset_class: "equity" | null; /** When our node first saw the token trade or get a pool (not the creation block). */ first_seen_at: string | null; launchpad: string | null; is_graduated: boolean | null; deployer_address: string | null; deployer_tier: DeployerTier | null; price_usd: number | null; market_cap_usd: number | null; fdv_usd: number | null; peak_mc_usd: number | null; peak_mc_at: string | null; /** Percent below all-time-high MC. */ drawdown_from_peak_pct: number | null; liquidity_usd: number | null; liquidity_basis: RhcLiquidityBasis; primary_dex: string | null; primary_pool: string | null; last_trade_time: string | null; } export interface TokensResponse { chain: Chain; tokens: RhcTokenListItem[]; count: number; sort: string; /** Present when a filter was scanned (audit F09); scan_truncated ⇒ more matches MAY exist. */ scan?: { post_filtered: true; scanned: number; scan_truncated: boolean; scan_budget: number; }; /** sort=newest only — echoes the request's `since`. */ since?: string | null; /** sort=newest only — feed back as `since` to poll for newer launches. */ next_since?: string | null; /** sort=newest / sort=oldest only — constant note on first_seen_at semantics and the cursor contract. */ note?: string; /** sort=oldest only — echoes the request's `after`. */ after?: string | null; /** sort=oldest only — echoes the request's `cursor`. */ cursor?: string | null; /** sort=oldest only — false ONLY when the candidates ran out. */ has_more?: boolean; /** sort=oldest only (preferred) — feed back as `cursor`; null = walk complete. */ next_cursor?: string | null; /** sort=oldest only, legacy inclusive cursor — feed back as `after`. */ next_after?: string | null; } export type EquitiesSort = "volume" | "trades" | "market_cap" | "last_trade" | "symbol"; export interface EquitiesParams { /** Default "volume" (24h ETH volume). */ sort?: EquitiesSort; /** 1–300. Default 100. */ limit?: number; /** Exact ticker, case-insensitive (NVDA). */ symbol?: string; /** Substring of symbol or name. */ q?: string; } /** * One official Robinhood tokenized equity (stock or ETF). Identity is the * issuer BEACON, never the name — listed only if the contract is an EIP-1967 * beacon proxy on Robinhood's issuer beacon. */ export interface RhcEquity { token_address: string; symbol: string | null; /** Underlying name with the "• Robinhood Token" suffix stripped (display only). */ name: string | null; onchain_name: string | null; asset_class: "equity"; /** Always true here — beacon-verified by construction. */ verified: boolean; issuer_beacon: string | null; decimals: number | null; listed_at: string | null; price_usd: number | null; price_native: number | null; market_cap_usd: number | null; fdv_usd: number | null; peak_mc_usd: number | null; liquidity_usd: number | null; liquidity_basis: string | null; primary_dex: string | null; primary_pool: string | null; last_trade_time: string | null; trades_24h: number; volume_eth_24h: number; buys_24h: number; sells_24h: number; buyers_24h: number; sellers_24h: number; } export interface EquitiesResponse { chain: Chain; equities: RhcEquity[]; count: number; total_equities: number; sort: string; identity: { method: string; issuer_beacon: string; note: string; }; stats_window: "24h"; stats_as_of: string; } /** v1.x (audit 2026-09-21) — the creator is returned whenever the token row names it; * reputation fields are null until the reputation view has a row (history_status * "pending") or when that lookup failed ("unavailable"). */ export interface RhcTokenDeployer extends Partial> { address: string; tier: DeployerTier | null; tokens_deployed: number | null; /** @deprecated Deployer Hunter v1 metric, kept with its v1 meaning and value (also after the v2 cutover); it does not set the v2 tier. Use organic_runners / resolved_launches / lifetime_rate / recent_rate. */ graduation_rate: number | null; /** @deprecated Deployer Hunter v1 metric, kept with its v1 meaning and value (also after the v2 cutover); it does not set the v2 tier. Use organic_runners / resolved_launches / lifetime_rate / recent_rate. */ runner_rate: number | null; /** @deprecated Deployer Hunter v1 metric, kept with its v1 meaning and value (also after the v2 cutover); it does not set the v2 tier. Use organic_runners / resolved_launches / lifetime_rate / recent_rate. */ runners: number | null; /** @deprecated Deployer Hunter v1 metric, kept with its v1 meaning and value (also after the v2 cutover); it does not set the v2 tier. Use organic_runners / resolved_launches / lifetime_rate / recent_rate. */ best_peak_mc_usd: number | null; /** @deprecated Deployer Hunter v1 metric, kept with its v1 meaning and value (also after the v2 cutover); it does not set the v2 tier. Use organic_runners / resolved_launches / lifetime_rate / recent_rate. */ launchpads: string[] | null; identity_status?: "known"; history_status?: "computed" | "pending" | "unavailable"; } export interface RhcKolParticipant { kol_id: string; name: string | null; twitter_url: string | null; buys: number; sells: number; last_trade_at: string | null; } export interface RhcTokenKolActivity { /** Distinct KOL identities (stable KOL id, not display names). All-time since the audit fix. */ distinct_kols: number; names: string[]; buys: number; sells: number; net_eth: number; distinct_wallets?: number; buy_eth?: number; sell_eth?: number; participants?: RhcKolParticipant[]; identity?: "kol_wallet_id"; window?: { kind: "all_time" | "latest_trades"; first_trade_at: string | null; last_trade_at: string | null; }; basis?: string; sample_size?: number; complete?: boolean; } export interface RhcTokenDetailResponse { chain: Chain; token_address: string; symbol: string | null; name: string | null; decimals: number | null; launchpad: string | null; is_graduated: boolean | null; graduated_pool: string | null; graduated_at: string | null; deployer_address: string | null; first_seen_at: string | null; token_age_minutes: number | null; price_usd: number | null; price_native: number | null; market_cap_usd: number | null; fdv_usd: number | null; peak_mc_usd: number | null; peak_mc_at: string | null; drawdown_from_peak_pct: number | null; total_supply_raw: string | null; liquidity_usd: number | null; liquidity_basis: RhcLiquidityBasis; /** Constant statement of the uniswap-v4 ceiling semantics. */ liquidity_note: string; liquidity_source: RhcLiquiditySource; liquidity_status: RhcLiquidityStatus; primary_dex: string | null; primary_pool: string | null; last_trade_time: string | null; /** v1.x — price freshness anchored to last_trade_time (stale > 15 min). */ price_observed_at?: string | null; price_age_seconds?: number | null; price_is_stale?: boolean; price_updated_at?: string | null; deployer_identity_status?: "known" | "unresolved" | "unavailable"; degraded_fields?: string[]; deployer: RhcTokenDeployer | null; /** Up to 10 other tokens by the same deployer (symbol or address). */ deployer_other_tokens: string[]; kol_activity: RhcTokenKolActivity; /** Up to 20 pools with reserves/liquidity/sqrt_price. */ pools: Array>; } export type CandleTimeframe = "1m" | "5m" | "15m" | "1h" | "4h" | "1d"; export interface CandlesParams { /** Candle timeframe, rolled up server-side from the 1-minute series. Default "1m". */ tf?: CandleTimeframe; /** Number of candles (1–1500, default 240). */ limit?: number; /** Lower bound on bucket_start (ISO). */ from?: string; /** Upper bound on bucket_start (ISO). */ to?: string; } export interface RhcCandle { bucket_start: string; open_price_usd: number; high_price_usd: number; low_price_usd: number; close_price_usd: number; open_mc_usd: number | null; high_mc_usd: number | null; low_mc_usd: number | null; close_mc_usd: number | null; close_liquidity_usd: number | null; close_supply: number | null; volume_usd: number; volume_buy_usd: number | null; volume_sell_usd: number | null; trades: number; buy_count: number | null; sell_count: number | null; dex: string | null; pool_address: string | null; } export interface CandlesResponse { chain: Chain; token_address: string; timeframe: string; /** Window actually served (ISO); null when the token has no candles. */ from: string | null; to: string | null; candles: RhcCandle[]; count: number; /** true when the page budget ran out before `limit` candles or `from` were reached. */ truncated: boolean; /** Oldest instant actually searched; equals `from` when the whole window was scanned. */ covered_from: string | null; } export interface RhcKolConsensus { total_kol_buyers: number; total_kol_sellers: number; /** Fraction of KOL buyers who also sold (0–1). */ kol_exit_rate: number; net_flow_eth: number; total_buy_eth: number; total_sell_eth: number; first_kol_buy_at: string | null; last_kol_buy_at: string | null; first_touch_wallet: string | null; first_touch_at: string | null; median_entry_mc_usd: number | null; entry_mc_samples: number; total_trades: number; /** ULTRA only — distinct KOL buyer wallets. */ buyers?: string[]; /** ULTRA only — KOL wallets that bought and sold. */ exited?: string[]; } export interface RhcKolConsensusResponse { chain: Chain; token_address: string; current_mc_usd: number | null; current_price_usd: number | null; consensus: RhcKolConsensus | null; } /** "insufficient_data" (audit 2026-09-21) when no buyer's win rate fed the score. */ export type BuyerQualityConfidence = "insufficient_data" | "low" | "medium" | "high"; export type BuyerQualitySignal = "positive" | "neutral" | "negative"; export interface RhcBuyerQualityBreakdown { early_buyers_analyzed: number; alpha_wallet_count: number; kol_count: number; /** Early buyers flagged as part of a same-block launch bundle. */ bundle_buyer_count: number; /** Early buyers on the rolling dump-cluster list. Informational — does not move the score. */ dump_cluster_count: number; recycled_early_buyer_count: number; /** Percent (0–100), non-bot buyers with ≥3 tokens of history. */ avg_historical_win_rate: number | null; bot_dominated: boolean; wallets_with_history?: number; qualified_win_rate_wallets?: number; } export interface RhcBuyerQuality { score: number; confidence: BuyerQualityConfidence; signal: BuyerQualitySignal; breakdown: RhcBuyerQualityBreakdown; } export interface RhcBuyerQualityCoverage { bundle_detection: "available"; dump_cluster_signal: "available"; note?: string; /** Pre-2026-07-18 launches: the cohort is the earliest 20 ATTRIBUTED buyers, not necessarily the first 20. */ earliest_buyers_caveat?: string; } /** Panels a terminal can ask for. There is no default set: name every module you render. */ export type RhcTokenIntelligenceModuleId = "snapshot" | "risk" | "buyer_quality" | "holders" | "holder_count" | "flow" | "kol" | "locks" | "top_traders"; /** Per-module outcome; a non-ready module never carries `data` (a failure is never a zero). */ export type RhcTokenIntelligenceModuleStatus = "ready" | "partial_history" | "unverified" | "unavailable" | "timeout"; /** Query params for GET /rhc/tokens/{address}/intelligence. */ export interface RhcTokenIntelligenceParams { /** * Required. Modules as an array or a comma-separated string. Budget: at most * 5 modules and total cost 8 (snapshot 2, risk 2, buyer_quality 2, holders 3, * holder_count 1, flow 3, kol 1, locks 1, top_traders 2); over budget is HTTP 400 * `include_budget_exceeded` before any read. `holders` is opt-in only. * `holder_count` (Solana) is the last complete holder census, stored: `ready` * when fresh, `unverified` (`stale_measurement`) with the count and its * `as_of` when older, `unavailable` (`not_measured`) when unknown (never 0). */ include: readonly RhcTokenIntelligenceModuleId[] | string; } export interface RhcTokenIntelligenceModule { status: RhcTokenIntelligenceModuleStatus; /** Machine reason for any non-ready status (tier_required, not_found, module_timeout, request_deadline, history_not_verified, not_captured, source_error, ...). */ reason: string | null; /** The source's own timestamp; null when the source gives none. Never the request time. */ as_of: string | null; provenance: { source: string; chain: string; upstream_status: number | null; cache: "hit" | "miss" | "none"; }; coverage: { kind: "point_in_time" | "trade_derived"; historical_completeness: { threshold: "verified_interval"; observed: "verified" | "not_verified" | "unknown"; threshold_met: boolean; } | null; }; limitations: string[]; /** The source endpoint's own body. Absent when unavailable / timed out. */ data?: Record; } /** GET /rhc/tokens/{address}/intelligence — Robinhood Chain (4663), lowercase 0x, ETH-native. */ export interface RhcTokenIntelligenceResponse { contract_version: 1; chain: "robinhood-chain"; chain_id: 4663; /** Lowercased 0x address. */ address: string; address_format: "evm_hex"; native_asset: "ETH"; requested: RhcTokenIntelligenceModuleId[]; /** Response assembly time, NOT an observation time (each module has its own `as_of`). */ generated_at: string; budget: { cost_used: number; cost_limit: number; max_modules: number; source_reads: number; module_timeout_ms: number; request_deadline_ms: number; concurrency: number; }; summary: Record; /** Only the requested modules are present. */ modules: Partial>; } export interface RhcBuyerQualityResponse { chain: Chain; token_address: string; current_mc_usd: number | null; /** distinct_first_buy = first 20 distinct EOAs by first buy; legacy_row_window = pre-rhc/011 fallback. */ cohort_selection?: "distinct_first_buy" | "legacy_row_window"; quality: RhcBuyerQuality; coverage: RhcBuyerQualityCoverage; /** 2026-10, only with include_balances=true (PRO+): the dump-cluster early buyers' CURRENT balanceOf; totals only when every member was read. */ dump_cluster_held_now?: { cohort_size: number; members: number; held_now: Record; wallets: Array<{ wallet: string; balance_now: Record | null; }>; note: string; }; /** Present only when buyer data is insufficient. */ note?: string; } /** `same_block` = 3+ early buyers first-bought in one block; `none` = no cluster. `atomic_tx` does not exist on EVM. */ export type BundleKind = "same_block" | "none"; export interface RhcBundleSummary { wallet_count: number; bundle_kind: BundleKind; /** Net tokens still held ÷ tokens bought, [0,1]. The primary signal. */ held_ratio: number | null; /** Net tokens held ÷ total supply, [0,1]; null when supply is unknown. */ held_pct_of_supply: number | null; fully_exited: boolean; buy_volume: number; tokens_held: number; /** 2026-10 (COV-23) — `swap_only` = token transfers are not applied: a member that transferred tokens out and sold elsewhere still reads as holding. Absent on older responses. */ holdings_basis?: "swap_only" | "swap_and_transfers"; } export interface RhcBundleWallet { /** Early-buyer rank (1 = first buyer). */ rank: number; wallet: string; held_ratio: number | null; has_sold: boolean; is_kol: boolean; /** ULTRA only — historical win-rate [0,1]. */ win_rate?: number | null; /** ULTRA only — bot heuristic from mv_rhc_alpha_wallets. */ likely_bot?: boolean; /** ULTRA only — net position, human-scaled. */ tokens_held?: number; /** 2026-10, only with include_balances=true: current balanceOf(token) from our node; unverified never means zero. */ balance_now?: { status: "verified" | "unverified"; balance_raw: string | null; decimals: number | null; balance: string | null; holding: boolean | null; reason: string | null; } | null; } export interface RhcBundleResponse { chain: Chain; token_address: string; bundle: RhcBundleSummary; /** 2026-10, only with include_balances=true (PRO+): balance-based held figures; totals only when complete. */ held_now?: { source: "rhc_node_balanceOf_latest"; checked_at: string; wallets: number; verified_wallets: number; holding_wallets: number; complete: boolean; incomplete_reason: string | null; tokens_held_raw: string | null; tokens_held: string | null; /** RATIO in [0, 1] of total_supply_raw. */ held_pct_of_supply: number | null; supply_basis: "total_supply_raw" | null; }; /** Empty for BASIC; top-10 for PRO; full cohort for ULTRA. */ wallets: RhcBundleWallet[]; } export type DeployerLeaderboardSort = "graduation_rate" | "runner_rate" | "tokens_deployed" | "best_peak_mc_usd" | "last_deploy_at" /** Accepted only when the server runs RHC_DEPLOYER_REPUTATION=v2. */ | "recent_rate" | "lifetime_rate" | "organic_runners" | "resolved_launches" | "survival_1h_rate" | "independent_traders_median"; /** GET /rhc/deployer-hunter/alerts — tracked-deployer launch alerts (0.7.0). */ export interface DeployerAlertsParams { deployer_tier?: DeployerTier; priority?: "high" | "medium"; alert_type?: "new_deploy" | "graduated"; launchpad?: string; /** Minimum market cap at alert (USD). */ min_mc?: number; /** Page size (1–500, default 50). */ limit?: number; offset?: number; /** Include alerts whose token has < $500 liquidity (default false). */ include_untradeable?: boolean; /** Only alerts newer than this ISO8601 time — the polling cursor (pass back next_since). */ since?: string; /** Only alerts older than this ISO8601 time — LEGACY backward pagination (skips same-event_at siblings). */ before?: string; /** Opaque strict (event_at, id) cursor — `next_cursor` from the previous page. Not combinable with before/offset. */ cursor?: string; } export interface DeployerAlert { token_address: string; token_symbol: string | null; token_name: string | null; deployer_address: string; deployer_tier: DeployerTier | string; alert_type: string; priority: string; market_cap_usd: number | null; liquidity_usd: number | null; event_at: string; [key: string]: unknown; } export interface DeployerAlertsResponse { chain: "robinhood"; alerts: DeployerAlert[]; limit: number; offset: number; next_since?: string | null; next_event_at?: string | null; next_before?: string | null; /** Pass as `cursor` for the next (older) page; null at the end. */ next_cursor?: string | null; /** false only when the feed is exhausted. */ has_more?: boolean; scan?: { post_filtered: boolean; scanned: number; scan_truncated: boolean; scan_budget: number; }; [key: string]: unknown; } export interface DeployerLeaderboardParams { sort?: DeployerLeaderboardSort; tier?: DeployerTier; /** Minimum tokens deployed (default 3). */ min_tokens?: number; /** Page size (1–50, default 20). */ limit?: number; /** Pagination offset (0–10000). */ offset?: number; } export interface RhcDeployer extends RhcDeployerV2Fields { deployer_address: string; tokens_deployed: number; /** Tokens that reached a $40K+ peak MC (the graduation milestone). @deprecated Deployer Hunter v1 metric, kept with its v1 meaning and value (also after the v2 cutover); it does not set the v2 tier. Use organic_runners / resolved_launches / lifetime_rate / recent_rate. */ graduated: number; /** graduated ÷ tokens_deployed. @deprecated Deployer Hunter v1 metric, kept with its v1 meaning and value (also after the v2 cutover); it does not set the v2 tier. Use organic_runners / resolved_launches / lifetime_rate / recent_rate. */ graduation_rate: number; /** Tokens that peaked ≥ $100K MC. @deprecated Deployer Hunter v1 metric, kept with its v1 meaning and value (also after the v2 cutover); it does not set the v2 tier. Use organic_runners / resolved_launches / lifetime_rate / recent_rate. */ runners: number; /** runners ÷ tokens_deployed. @deprecated Deployer Hunter v1 metric, kept with its v1 meaning and value (also after the v2 cutover); it does not set the v2 tier. Use organic_runners / resolved_launches / lifetime_rate / recent_rate. */ runner_rate: number; /** @deprecated Deployer Hunter v1 metric, kept with its v1 meaning and value (also after the v2 cutover); it does not set the v2 tier. Use organic_runners / resolved_launches / lifetime_rate / recent_rate. */ best_peak_mc_usd: number | null; /** @deprecated Deployer Hunter v1 metric, kept with its v1 meaning and value (also after the v2 cutover); it does not set the v2 tier. Use organic_runners / resolved_launches / lifetime_rate / recent_rate. */ launchpads: string[]; first_deploy_at: string | null; /** @deprecated Deployer Hunter v1 metric, kept with its v1 meaning and value (also after the v2 cutover); it does not set the v2 tier. Use organic_runners / resolved_launches / lifetime_rate / recent_rate. */ last_deploy_at: string | null; /** elite/good = `runner_rate` ($100K) + 24h of history; spammer = $40K `graduation_rate`. */ tier: DeployerTier; } export interface DeployerLeaderboardResponse { chain: Chain; /** v2 only. */ rule_version?: string; sort?: string; sort_requested?: string; sort_note?: string; deployers: RhcDeployer[]; total: number; limit: number; offset: number; has_more: boolean; } export interface RhcDeployerProfile extends RhcDeployerV2Fields { deployer_address: string; tokens_deployed: number; curve_tokens: number; /** @deprecated Deployer Hunter v1 metric, kept with its v1 meaning and value (also after the v2 cutover); it does not set the v2 tier. Use organic_runners / resolved_launches / lifetime_rate / recent_rate. */ graduated: number; bonding_rate: number | null; /** @deprecated Deployer Hunter v1 metric, kept with its v1 meaning and value (also after the v2 cutover); it does not set the v2 tier. Use organic_runners / resolved_launches / lifetime_rate / recent_rate. */ runners: number; /** @deprecated Deployer Hunter v1 metric, kept with its v1 meaning and value (also after the v2 cutover); it does not set the v2 tier. Use organic_runners / resolved_launches / lifetime_rate / recent_rate. */ runner_rate: number; /** @deprecated Deployer Hunter v1 metric, kept with its v1 meaning and value (also after the v2 cutover); it does not set the v2 tier. Use organic_runners / resolved_launches / lifetime_rate / recent_rate. */ best_peak_mc_usd: number | null; /** @deprecated Deployer Hunter v1 metric, kept with its v1 meaning and value (also after the v2 cutover); it does not set the v2 tier. Use organic_runners / resolved_launches / lifetime_rate / recent_rate. */ launchpads: string[]; first_deploy_at: string | null; /** @deprecated Deployer Hunter v1 metric, kept with its v1 meaning and value (also after the v2 cutover); it does not set the v2 tier. Use organic_runners / resolved_launches / lifetime_rate / recent_rate. */ last_deploy_at: string | null; /** elite/good = `runner_rate` ($100K) + 24h of history; spammer = $40K `graduation_rate`. */ tier: DeployerTier; } export interface RhcDeployerRecentToken { address: string; symbol: string | null; name: string | null; launchpad: string | null; is_graduated: boolean | null; graduated_at: string | null; graduated_pool: string | null; first_seen_at: string | null; market_cap_usd: number | null; peak_mc_usd: number | null; peak_mc_at: string | null; /** v2 only: per-launch outcome (null until scored). */ outcome?: RhcTokenOutcome | null; } export interface RhcDeployerProfileResponse { chain: Chain; /** False if this wallet has never deployed a tracked token (deployer is then null). */ is_deployer: boolean; address: string; deployer: RhcDeployerProfile | null; recent_tokens: RhcDeployerRecentToken[]; /** Rows returned (capped at 50) — the true total is deployer.tokens_deployed. */ recent_tokens_count: number; /** * Deployer Hunter v2 only (rhc/049): absent in the default v1 response. * `null` = NOT MEASURED (unavailable or not enabled yet); it never means dormant. */ activity?: RhcDeployerActivity | null; } /** Whether the deployer is launching now. Never changes the tier. */ export interface RhcDeployerActivity { /** active: last counted launch within window_days; dormant: older; no_launches: none counted. */ state: "active" | "dormant" | "no_launches"; /** Null only when state is "no_launches". */ last_launch_at: string | null; days_since_last_launch: number | null; launches_7d: number; launches_30d: number; /** 7 */ window_days: number; as_of: string | null; } export type AlphaClassificationFilter = "all" | "human" | "bot" | "smart_money"; export type AlphaIdentityFilter = "all" | "known_kol" | "unknown"; export type AlphaSort = "net_eth" | "win_rate" | "trades" | "tokens" | "buy_eth" | "memecoin_share" | "last_trade_at"; export type AlphaOrder = "desc" | "asc"; /** Per-wallet classification returned in the response. */ export type AlphaWalletClassification = "bot" | "smart_money" | "trader"; export interface AlphaWalletsParams { classification?: AlphaClassificationFilter; identity?: AlphaIdentityFilter; /** Minimum share of trades in launchpad memecoins (0–1). */ min_memecoin_share?: number; /** Maximum average market cap traded — filter to low-cap degens. */ max_avg_mc_usd?: number; min_net_eth?: number; min_win_rate?: number; max_win_rate?: number; min_trades?: number; min_tokens?: number; /** Minimum ETH deployed (whale/size filter). */ min_buy_eth?: number; /** Only wallets that traded within the last N hours (1–720). */ active_hours?: number; sort?: AlphaSort; order?: AlphaOrder; /** Page size (1–100, default 25). */ limit?: number; offset?: number; /** Include wallets whose net_eth/win_rate are ≥50% inflated by zero-cost-basis dumps (excluded by default). */ include_zero_cost_dumps?: boolean; } export interface RhcAlphaWallet { /** Trader EOA (lowercase 0x). */ wallet: string; classification: AlphaWalletClassification; is_known_kol: boolean; trades: number; tokens: number; buy_eth: number; sell_eth: number; /** Realized net flow (sell − buy). */ net_eth: number; win_rate: number | null; /** Share of trades in launchpad memecoins (vs tokenized stocks/stables). */ memecoin_share: number | null; avg_trade_mc_usd: number | null; last_trade_at: string | null; /** Share of gross ETH extracted from tokens with no recorded buy (deployer/insider dumps). Null = never sold. */ zero_cost_share: number | null; } export interface AlphaWalletsResponse { chain: Chain; wallets: RhcAlphaWallet[]; total: number; limit: number; offset: number; has_more: boolean; /** Interim disclosure: aggregates use attributed trades only, complete from `attribution_complete_from`. */ attribution: { attribution_complete_from: string; note: string; }; } /** * Where a fired rule is delivered. `websocket` requires no `webhook_url`; * anything else does. A `webhook_secret` is minted (once) only when a webhook * URL is set — payloads are signed HMAC-SHA256 over `.` in the * `X-MadeOnSol-Signature` header. */ export type DeliveryMode = "webhook" | "websocket" | "both"; /** Every rule-engine DELETE returns this. */ export interface RhcDeletedResponse { chain: Chain; deleted: boolean; } /** Which side of the tape a copy-trade rule reacts to. */ export type CopyTradeOnlyAction = "buy" | "sell" | "both"; /** How the suggested size is derived from the source trade. */ export type CopyTradeSizingMode = "fixed" | "proportional" | "percent_source"; export interface RhcCopyTradeSubscription { /** Numeric identity PK. */ id: number; name: string | null; /** Lowercase 0x wallets this rule follows (the API lowercases on write). */ source_wallets: string[]; /** Minimum source-trade size in ETH for the rule to fire. */ min_trade_eth: number; only_action: CopyTradeOnlyAction; sizing_mode: CopyTradeSizingMode; /** ETH when `sizing_mode` is `fixed`, else a multiplier of the source trade. */ sizing_amount: number; delivery_mode: DeliveryMode; webhook_url: string | null; is_active: boolean; created_at: string; updated_at: string; /** * Subset of `source_wallets` that are TRACKED Robinhood Chain KOL wallets * (`kol_evm_wallets`, the set behind `/rhc/kol/wallets`). Under * `source_admission: "any_wallet"` (production since 2026-10-04) this is KOL * enrichment only; under the legacy `"kol_only"` engine only these can fire. * @deprecated 2026-10-04, kept and still filled. * `null` when the server could not read the reference set (see `warnings`). * Added 2026-09-22; absent on older servers. */ source_wallets_tracked?: string[] | null; /** * Subset of `source_wallets` NOT in the tracked KOL set. Under * `source_admission: "any_wallet"` these fire like any other wallet; under the * legacy `"kol_only"` engine they never fire. * @deprecated 2026-10-04 — KOL membership is enrichment only; kept and still filled. */ source_wallets_untracked?: string[] | null; /** Present only when something needs attention, e.g. `untracked_source_wallets` (legacy `kol_only` engine only). */ warnings?: RhcCopyTradeRuleWarning[]; /** * v0.19 (server 2026-10-02) — whether the rule can fire at all, separate from * `is_active` (your switch): `eligible` (at least one tracked source wallet), * `no_tracked_sources` (kept, but can never fire) or `unknown` (the tracking * read failed; never assumed eligible). Absent on older servers. * Server 2026-10-04: under `source_admission: "any_wallet"` any valid source * wallet is followed and the non-eligible states are infrastructure only — * `monitoring_pending` (rule changed after the engine's last load, live within * seconds), `monitoring_unavailable` (engine / trade stream not reporting; it * fires nothing then — see `monitoring_reasons`), `source_capacity_unavailable`. */ operational_state?: "eligible" | "monitoring_pending" | "monitoring_unavailable" | "source_capacity_unavailable" | "no_tracked_sources" | "unknown"; /** Server 2026-10-04 — which trades the RUNNING engine admits. Absent = unknown (legacy kol_only semantics). */ source_admission?: "kol_only" | "any_wallet"; /** Server 2026-10-04 — present only with `monitoring_unavailable`: e.g. `dex_stream_stale`, `map_stale`, `engine_state_stale`. */ monitoring_reasons?: string[]; } export interface RhcCopyTradeRuleWarning { code: "untracked_source_wallets" | "source_wallet_tracking_unavailable"; message: string; } export interface CopyTradeListResponse { chain: Chain; subscriptions: RhcCopyTradeSubscription[]; } export interface CopyTradeCreateParams { name?: string; /** 1–250 EVM addresses (0x, 40 hex); the per-tier cap is enforced server-side. */ source_wallets: string[]; /** Default 0. */ min_trade_eth?: number; /** Default `buy`. */ only_action?: CopyTradeOnlyAction; /** Default `fixed`. */ sizing_mode?: CopyTradeSizingMode; sizing_amount: number; /** Default `webhook`. */ delivery_mode?: DeliveryMode; /** HTTPS only. Required unless `delivery_mode` is `websocket`. */ webhook_url?: string; } export interface CopyTradeCreateResponse { chain: Chain; subscription: RhcCopyTradeSubscription; /** Shown ONCE — null when `delivery_mode` is `websocket`. */ webhook_secret: string | null; note: string; /** Mirror of `subscription.warnings`: present when any source wallet is untracked (legacy `kol_only` engine only) or the tracking lookup was unavailable. */ warnings?: RhcCopyTradeRuleWarning[]; } export interface CopyTradeGetResponse { chain: Chain; subscription: RhcCopyTradeSubscription; /** Mirror of `subscription.warnings`: present when any source wallet is untracked (legacy `kol_only` engine only) or the tracking lookup was unavailable. */ warnings?: RhcCopyTradeRuleWarning[]; } export interface CopyTradeUpdateParams { /** `null` clears the label. */ name?: string | null; source_wallets?: string[]; min_trade_eth?: number; only_action?: CopyTradeOnlyAction; sizing_mode?: CopyTradeSizingMode; sizing_amount?: number; delivery_mode?: DeliveryMode; webhook_url?: string | null; is_active?: boolean; } export interface CopyTradeSignalsParams { /** Scope to one of your rules — 404 if you do not own it. */ subscription_id?: number; /** ISO 8601 lower bound on `fired_at`. */ since?: string; /** 1–500, default 50. */ limit?: number; } export interface RhcCopyTradeSignal { id: number; subscription_id: number; fired_at: string; /** The followed wallet whose trade triggered the fire. */ source_wallet: string; action: TradeAction; token_address: string; token_symbol: string | null; token_name: string | null; /** Size of the source trade, ETH. */ source_eth_amount: number | null; /** Size your rule's sizing_mode implies, ETH. */ suggested_eth_amount: number | null; price_usd: number | null; dex: string | null; tx_hash: string; delivered: boolean; delivered_at: string | null; } export interface CopyTradeSignalsResponse { chain: Chain; signals: RhcCopyTradeSignal[]; count: number; } /** Lifecycle of an alert. Terminal states are `recovered` and `expired`. */ export type PriceAlertStatus = "watching" | "dipped" | "recovered" | "expired"; export interface RhcPriceAlert { id: number; name: string | null; token_address: string; token_symbol: string | null; /** MC captured at creation — an alert is a delta from the moment you set it. */ baseline_mc_usd: number; drop_pct: number; /** Null for a dip-only, terminal alert. */ recovery_pct: number | null; status: PriceAlertStatus; /** Lowest MC seen since the dip fired. */ dip_low_mc_usd: number | null; dip_fired_at: string | null; delivery_mode: DeliveryMode; webhook_url: string | null; is_active: boolean; /** Alerts self-expire 30 days after creation. */ expires_at: string; created_at: string; updated_at: string; } export interface PriceAlertListResponse { chain: Chain; alerts: RhcPriceAlert[]; } export interface PriceAlertCreateParams { name?: string; /** Must already be tracked on RHC with a market cap, else 400. */ token_address: string; /** 0.01–99.99. */ drop_pct: number; /** 0.01–1000. Omit for a dip-only, terminal alert. */ recovery_pct?: number; /** Default `webhook`. */ delivery_mode?: DeliveryMode; webhook_url?: string; } /** * How RHC alerts are evaluated. Since 2026-09-15 they are **event-driven** off * the `rhc:dex_trade` feed (each trade re-evaluates the alerts on its token), * with a price-table poll (`fallback_poll_seconds`: fast while the feed is * degraded or a trade carried no market cap, slow otherwise) and a trade-tape * replay after a feed outage as safety nets. Latency is a few seconds (the * chain trade flush is ~2 s) — **not** parity with the sub-second Solana * alerts. Older servers answered `mode: "polled"` (a ~15 s poll). */ export interface PriceAlertEvaluation { mode: "event_driven" | "polled"; /** The feed that triggers evaluation (`"rhc:dex_trade"`); absent when `polled`. */ trigger?: string; /** Kept for compatibility: now the fast fallback poll (was the 15 s poll interval). */ interval_seconds: number; /** Safety-net price-table poll intervals in seconds. */ fallback_poll_seconds?: { fast: number; slow: number; }; note: string; } export interface PriceAlertCreateResponse { chain: Chain; alert: RhcPriceAlert; /** Shown ONCE — null when `delivery_mode` is `websocket`. */ webhook_secret: string | null; evaluation: PriceAlertEvaluation; note: string; } export interface PriceAlertGetResponse { chain: Chain; alert: RhcPriceAlert; } /** * `token_address`, `drop_pct` and `recovery_pct` are immutable — retuning a * threshold mid-flight would make the alert's recorded events uninterpretable. * Delete and recreate instead. */ export interface PriceAlertUpdateParams { name?: string | null; delivery_mode?: DeliveryMode; webhook_url?: string | null; is_active?: boolean; } export type PriceAlertEventType = "dip" | "recovery"; export interface PriceAlertEventsParams { /** Scope to one of your alerts — 404 if you do not own it. */ alert_id?: number; event_type?: PriceAlertEventType; /** ISO 8601 lower bound on `fired_at`. */ since?: string; /** 1–500, default 50. */ limit?: number; } export interface RhcPriceAlertEvent { id: number; alert_id: number; event_type: PriceAlertEventType; fired_at: string; token_address: string; baseline_mc_usd: number; current_mc_usd: number; /** Measured drop at fire time, percent. */ drop_pct_actual: number | null; dip_low_mc_usd: number | null; /** Measured bounce off the dip low, percent — recovery events only. */ recovery_pct_actual: number | null; delivered: boolean; delivered_at: string | null; } export interface PriceAlertEventsResponse { chain: Chain; events: RhcPriceAlertEvent[]; count: number; } /** One side of a swap as the server resolved it. `decimals` null = not yet enriched. */ export interface RhcSideIdentity { address: string | null; symbol: string | null; decimals: number | null; } /** known: decimals AND symbol resolved · pending: token seen, enrichment queued · unknown: never seen. */ export type RhcMetadataStatus = "known" | "pending" | "unknown"; /** fresh: swap-derived quote inside the producer's 60-min freshness rule · stale: priced through the v4 pool-state fallback · none: unpriced. */ export type RhcPriceStatus = "fresh" | "stale" | "none"; export type RhcPriceSource = "swap_quote" | "pool_state_quote"; /** Why `mc_usd` is null (or "ok"). `liquidity_gate` = MC exceeded pool TVL x 50,000, the same gate that nulls the stored MC. */ export type RhcMcStatus = "ok" | "no_price" | "no_supply" | "dust" | "ceiling" | "liquidity_gate" | "no_liquidity"; export type RhcSideReason = "both_sides_quote" | "no_recognized_quote"; /** * `rhc:dex_trade` frame `data` (channel `rhc:dex_trades`, ULTRA+). Every field * that existed before 2026-09-22 keeps its name; the block from * `amount_in_raw` down is the additive enrichment. `*_raw` amounts are uint256 * as EXACT decimal strings — never parse them into a float. A KOL trade also * appears on `rhc:kol_trades` with the same `tx_hash` + `log_index` (different * event id). The durable backfill rebuilds every key it can and lists the rest * in the frame's `missing` (liquidity_usd, quote.usd, quote.observed_at). */ export interface RhcDexTradeEvent { chain: Chain; token_address: string; token_symbol: string | null; action: TradeAction; dex: string; pool: string; eth_amount: number | null; price_usd: number | null; mc_usd: number | null; /** Pool TVL at broadcast (null = not yet tracked, never "zero"). */ liquidity_usd: number | null; trader: string | null; tx_hash: string; log_index: number; block_number: number; traded_at: string; amount_in_raw: string | null; amount_out_raw: string | null; token_amount_raw: string | null; quote_amount_raw: string | null; token: RhcSideIdentity; quote: RhcSideIdentity & { usd: number | null; observed_at: string | null; }; token_decimals: number | null; quote_decimals: number | null; metadata_status: RhcMetadataStatus; price_status: RhcPriceStatus; price_source: RhcPriceSource | null; /** ISO block time of the print the price was observed at; null when unpriced. */ price_observed_at: string | null; mc_status: RhcMcStatus | null; /** `action` when a canonical quote (WETH / ETH / USDG / VIRTUAL or an equity quote) defines "the token", else null. */ side: TradeAction | null; side_reason: RhcSideReason | null; launchpad: string | null; } /** `rhc:dex_trade_unattributed` frame `data` (channel `rhc:dex_trades_unattributed`, ULTRA+). Raw legs plus per-leg identity; never a buy/sell side. */ export interface RhcDexTradeUnattributedEvent { chain: Chain; dex: string; pool: string; token_in: string; token_out: string; amount_in: string | number | null; amount_out: string | number | null; liquidity_usd: number | null; trader: string | null; tx_hash: string; log_index: number; block_number: number; traded_at: string; amount_in_raw: string | null; amount_out_raw: string | null; token_in_meta: RhcSideIdentity; token_out_meta: RhcSideIdentity; metadata_status: RhcMetadataStatus; price_status: "none"; price_source: null; price_observed_at: null; side: null; side_reason: RhcSideReason; } /** Why a tick is not `fresh`. Every rule reuses a server-side pricing guard; none is client-tunable. */ export type RhcTickQualityReason = "no_price" | "dust_trade" | "curve_live" | "state_quote_fallback" | "low_liquidity" | "price_age" | "mc_ceiling"; /** * `rhc:token_price` frame `data` (channel `rhc:token_prices`, PRO+, address- * scoped). On subscribe every address first gets ONE frame with * `snapshot: true` (the current price row; `source: "rhc_token_prices"`), then * ticks derived from the RHC trade feed (`source: "rhc-dex-stream:trade"`), * at most one per address per 250 ms with the latest value. A `stale` or * `unreliable` price is only ever delivered with its flag — treat `quality` * as part of the price. State stream: no `seq` / `id`, not replayed; a resume * sends a fresh snapshot. */ export interface RhcTokenPriceTick { chain: Chain; address: string; symbol: string | null; price_usd: number | null; /** ETH-denominated price; present on snapshots, null on trade-derived ticks. */ price_native: number | null; market_cap_usd: number | null; liquidity_usd: number | null; source_pool: string | null; source_dex: string | null; /** ISO block time of the print (tick) or the last trade (snapshot). */ observed_at: string | null; /** Age of `observed_at` when the frame was built (ms). */ price_age_ms: number | null; quality: "fresh" | "stale" | "unreliable"; quality_reason: RhcTickQualityReason | null; price_source: RhcPriceSource | null; /** The print this tick came from; null on a snapshot. */ tx_hash: string | null; log_index: number | null; block_number: number | null; source: "rhc_token_prices" | "rhc-dex-stream:trade"; } export interface RhcCoordinationAlertRule { /** UUID. */ id: string; name: string | null; /** Distinct tracked KOL buyers needed to fire (2–50). */ min_kols: number; /** Rolling window those buys must land inside (1–60). */ window_minutes: number; min_score: number; /** Minutes before the same token can fire again (1–1440). */ cooldown_min: number; /** Score jump that breaks the cooldown early (0–100). */ score_jump_break: number; min_mc_usd: number | null; max_mc_usd: number | null; delivery_mode: DeliveryMode; webhook_url: string | null; is_active: boolean; created_at: string; updated_at: string; } export interface CoordinationAlertListResponse { chain: Chain; rules: RhcCoordinationAlertRule[]; } export interface CoordinationAlertCreateParams { name?: string; /** 2–50, default 3. */ min_kols?: number; /** 1–60, default 15. */ window_minutes?: number; /** 0–100, default 0. */ min_score?: number; /** 1–1440, default 30. */ cooldown_min?: number; /** 0–100, default 20. */ score_jump_break?: number; min_mc_usd?: number | null; max_mc_usd?: number | null; /** Default `websocket`. */ delivery_mode?: DeliveryMode; webhook_url?: string; } /** * Which scorer components are real on RHC. Scores are comparable to the Solana * coordination scorer, but `earliness` is defaulted (RHC has no early-entry * equivalent) while `quality` is a real KOL win-rate. Each fired signal records * the same breakdown in its `score_inputs`. */ export interface CoordinationAlertScoring { score_version: string; quality: string; earliness: string; note: string; } export interface CoordinationAlertCreateResponse { chain: Chain; rule: RhcCoordinationAlertRule; /** Shown ONCE — null when `delivery_mode` is `websocket`. */ webhook_secret: string | null; scoring: CoordinationAlertScoring; note: string; } export interface CoordinationAlertGetResponse { chain: Chain; rule: RhcCoordinationAlertRule; } export interface CoordinationAlertUpdateParams { name?: string | null; min_kols?: number; window_minutes?: number; min_score?: number; cooldown_min?: number; score_jump_break?: number; min_mc_usd?: number | null; max_mc_usd?: number | null; delivery_mode?: DeliveryMode; webhook_url?: string | null; is_active?: boolean; } /** Auto-classified trader style of the first-touching KOL. */ export type FirstTouchStrategy = "scalper" | "day_trader" | "swing" | "inactive" | "unscored"; /** * Push filters. Deliberately NOT the Solana set: RHC has no scout score, so * `min_scout_tier` / `min_n_touches` are absent rather than silently matching * nothing. Unknown keys are rejected with a 400. */ export interface FirstTouchFilters { /** Only this KOL's first touches (0x, 40 hex). */ kol?: string; /** Minimum first-buy size in ETH (0–100000). */ min_first_buy_eth?: number; /** 0–1. Win-rate on CLOSED positions; a KOL who has never sold is dropped, not counted as a loser. */ min_kol_winrate?: number; strategy?: FirstTouchStrategy; min_mc_usd?: number; max_mc_usd?: number; } export interface RhcFirstTouchSubscription { /** UUID. */ id: string; name: string | null; filters: FirstTouchFilters; delivery_mode: DeliveryMode; webhook_url: string | null; is_active: boolean; created_at: string; updated_at: string; } export interface FirstTouchSubscriptionListResponse { chain: Chain; subscriptions: RhcFirstTouchSubscription[]; } export interface FirstTouchSubscriptionCreateParams { name?: string; /** Default `{}` — every first touch. */ filters?: FirstTouchFilters; /** Default `websocket`. */ delivery_mode?: DeliveryMode; webhook_url?: string; } export interface FirstTouchSubscriptionCreateResponse { chain: Chain; subscription: RhcFirstTouchSubscription; /** Shown ONCE — null when `delivery_mode` is `websocket`. */ webhook_secret: string | null; note: string; } export interface FirstTouchSubscriptionGetResponse { chain: Chain; subscription: RhcFirstTouchSubscription; } export interface FirstTouchSubscriptionUpdateParams { name?: string | null; /** Whole-object REPLACE, not a merge — merging would make removing a filter inexpressible. */ filters?: FirstTouchFilters; delivery_mode?: DeliveryMode; webhook_url?: string | null; is_active?: boolean; } export interface RhcWalletStats { first_seen: string | null; last_seen: string | null; total_trades: number; /** Trades with a resolvable trader + size — the denominator for every PnL figure. */ analyzed_trades: number; /** Pre-2026-07-18 rows with a NULL `trader_eoa`; unattributable by design. */ unattributed_trades: number; unsized_trades: number; buys: number; sells: number; bought_eth: number; sold_eth: number; realized_pnl_eth: number; unrealized_pnl_eth: number; total_pnl_eth: number; /** FIFO-unmatched DEX buys x current price. NOT an on-chain balance: see `holdings.verified_value_eth` on the profile. */ held_value_eth: number; unique_tokens: number; /** FIFO-open positions (unmatched DEX buys). How many are actually still held is in the profile's `holdings`. */ open_positions: number; window_days: number; /** `true` when the wallet hit the per-wallet trade cap and the numbers cover only part of the window. */ partial: boolean; } export interface RhcWalletFlags { is_kol: boolean; kol_name: string | null; is_deployer: boolean; deployer_tier: DeployerTier | null; deployer_tokens: number | null; /** v1 runner rate; null when the server runs RHC_DEPLOYER_REPUTATION=v2. @deprecated Deployer Hunter v1 metric, kept with its v1 meaning and value (also after the v2 cutover); it does not set the v2 tier. Use organic_runners / resolved_launches / lifetime_rate / recent_rate. */ deployer_runner_rate: number | null; /** Additive (v2 only, else null): organic durable runners / resolved launches. */ deployer_organic_runner_rate?: number | null; deployer_resolved_launches?: number | null; is_alpha_tracked: boolean; alpha_win_rate: number | null; alpha_net_eth: number | null; alpha_tokens_traded: number | null; likely_bot: boolean | null; is_dumper: boolean; dump_cluster: Record; early_buyer_tokens: number; } export interface RhcWalletDerived { win_rate: number | null; wins: number; losses: number; avg_trade_size_eth: number | null; is_active: boolean; } export interface RhcWalletProfileResponse { chain: Chain; address: string; stats: RhcWalletStats; flags: RhcWalletFlags; top_tokens: Record[]; recent_trades: Record[]; derived: RhcWalletDerived; /** * v0.19 (server 2026-10-02) — on-chain verification of every FIFO-open * position. `top_tokens[].holding_status` carries the per-token status * ({@link RhcHoldingStatus}; null when the token is FIFO-closed), and * `top_tokens[].still_holding` keeps its FIFO meaning. null when the * snapshot was unavailable; absent on older servers. */ holdings?: RhcHoldingsSummary | null; /** `true` when the snapshot could not be computed (timeout) — flags still resolve. */ stats_unavailable: boolean; /** The wallet trio shares one snapshot cache; `true` means this call reused it. */ cache_hit: boolean; } export type RhcPositionResult = "win" | "loss" | "breakeven"; export interface RhcWalletPnlSummary { realized_eth: number; unrealized_eth: number; total_pnl_eth: number; total_bought_eth: number; total_sold_eth: number; wins: number; losses: number; win_rate: number | null; profit_factor: number | null; avg_hold_minutes: number | null; median_hold_minutes: number | null; max_drawdown_eth: number; open_positions_count: number; closed_positions_count: number; total_tokens_traded: number; best_realized: Record | null; worst_realized: Record | null; } export interface RhcPnlCurvePoint { date: string; day_pnl: number; cumulative_pnl: number; trades: number; } export interface RhcClosedPosition { token_address: string; token_symbol: string | null; buy_count: number; sell_count: number; bought_eth: number; sold_eth: number; pnl_eth: number; roi_pct: number | null; hold_minutes: number | null; result: RhcPositionResult; first_trade: string | null; last_trade: string | null; } export interface RhcWalletPnlNotes { denomination: "ETH"; /** ISO date the FIFO window opens — buys before it are invisible to cost basis. */ cost_basis_observable_from: string; data_through: string | null; trades_seen: number; trades_analyzed: number; trades_unattributed: number; trades_unsized: number; partial: boolean; partial_reason: string; } export interface RhcWalletPnlResponse { chain: Chain; address: string; window_days: number; summary: RhcWalletPnlSummary; pnl_curve: RhcPnlCurvePoint[]; closed_positions: RhcClosedPosition[]; open_positions: RhcOpenPosition[]; notes: RhcWalletPnlNotes; cache_hit: boolean; } export interface RhcOpenPosition { token_address: string; token_symbol: string | null; token_name: string | null; launchpad: string | null; is_graduated: boolean | null; token_amount: number; cost_basis_eth: number; avg_entry_price_eth: number; current_price_eth: number | null; current_value_eth: number | null; unrealized_eth: number | null; unrealized_pct: number | null; current_mc_usd: number | null; liquidity_usd: number | null; /** * `v4_virtual_ceiling` = the pool is a v4 bonding curve, so `liquidity_usd` is * a virtual ceiling, NOT withdrawable TVL. Do not size an exit against it. */ liquidity_basis: "v4_virtual_ceiling" | "measured"; buys_in_position: number; realized_so_far_eth: number; first_buy_at: string | null; last_buy_at: string | null; } /** * v0.19 (server 2026-10-02) — on-chain status of a FIFO-open position, from * `balanceOf` on our own Robinhood Chain node. `HELD` = balance within 0.5 % of * the FIFO amount; `PARTIALLY_REDUCED` = 0 < balance < FIFO; * `TRANSFERRED_OR_DISPOSED` = balance 0; `EXTERNAL_INFLOW` = balance > FIFO (the * excess has no cost basis, never trading profit); `BALANCE_UNVERIFIED` = the * read failed, the token is non-standard or decimals disagree (no value, never * assumed held). */ export type RhcHoldingStatus = "HELD" | "PARTIALLY_REDUCED" | "TRANSFERRED_OR_DISPOSED" | "EXTERNAL_INFLOW" | "BALANCE_UNVERIFIED"; export type RhcHoldingUnverifiedReason = "rpc_unavailable" | "call_failed" | "decimals_failed" | "over_cap" | "decimals_mismatch" | "decimals_unknown"; /** v0.19 — proven-holdings view over every FIFO-open position (`holdings` / `summary.holdings`). */ export interface RhcHoldingsSummary { balance_source: "rhc_node_multicall3"; checked_at: string; /** `false` when at least one position is BALANCE_UNVERIFIED (it contributes no value). */ complete: boolean; fifo_open_positions: number; held: number; partially_reduced: number; transferred_or_disposed: number; external_inflow: number; unverified: number; /** Proven balances x current price. Unverified and unpriced positions contribute nothing. */ verified_value_eth: number; /** Verified positions with a non-zero balance but no current price. */ unpriced_held: number; /** Cost basis of the FIFO-known portion still in the wallet. */ cost_basis_held_eth: number; /** Unrealized PnL on the known-cost, still-held portion only. */ unrealized_known_eth: number; /** Cost basis of FIFO lots no longer in the wallet. Outcome unknown: neither realized nor unrealized. */ cost_basis_not_held_eth: number; } /** * A `/positions` row: the FIFO position (unchanged meaning) plus its on-chain * check. The balance fields are absent on servers older than 2026-10-02. */ export interface RhcVerifiedOpenPosition extends RhcOpenPosition { /** Same as `token_amount`: DEX buys not matched by a DEX sell. A trading position, not a balance. */ fifo_unmatched_amount?: number | null; /** balanceOf(wallet) / 10^decimals from our node. null = not proven. */ current_onchain_balance?: number | null; holding_status?: RhcHoldingStatus; holding_unverified_reason?: RhcHoldingUnverifiedReason | null; /** min(balance, FIFO): the held part whose cost basis is known. */ held_known_amount?: number | null; external_inflow_amount?: number | null; /** current_onchain_balance x current price. 0 when transferred out; null when unverified or unpriced. */ current_holding_value_eth?: number | null; cost_basis_held_eth?: number | null; /** Unrealized on the known-cost, still-held portion only. */ unrealized_known_eth?: number | null; /** Cost basis of the FIFO portion no longer in the wallet. Outcome unknown. */ cost_basis_not_held_eth?: number | null; } export interface RhcWalletPositionsSummary { /** FIFO-open positions; how many are still held on-chain is in `holdings`. */ open_positions: number; total_cost_basis_eth: number; /** FIFO figure; the proven value is `holdings.verified_value_eth`. */ total_current_value_eth: number; total_unrealized_eth: number; /** Positions with no current price — excluded from the value/unrealized totals. */ unpriced_positions: number; /** v0.19 (server 2026-10-02) — on-chain verification of the positions; absent on older servers. */ holdings?: RhcHoldingsSummary; } export interface RhcWalletPositionsResponse { chain: Chain; address: string; window_days: number; summary: RhcWalletPositionsSummary; positions: RhcVerifiedOpenPosition[]; notes: Record; } export interface WalletTradesParams { /** 1–200, default 50. */ limit?: number; /** Opaque keyset cursor — pass the previous response's `next_before`. */ before?: string; /** ISO-8601 with offset. */ since?: string; action?: TradeAction; /** Filter to one token address (0x, 40 hex). */ token?: string; } export interface RhcWalletTrade { token_address: string | null; token_symbol: string | null; token_name: string | null; launchpad: string | null; action: TradeAction | null; eth_amount: number | null; token_amount: number | null; price_native: number | null; price_usd: number | null; mc_usd_at_trade: number | null; dex: string | null; pool: string | null; router: string | null; method_selector: string | null; tx_hash: string; log_index: number; block_number: number; block_time: string; } export interface RhcWalletTradesResponse { chain: Chain; address: string; trades: RhcWalletTrade[]; count: number; has_more: boolean; /** Opaque cursor for the next page — `null` when the tape is exhausted. */ next_before: string | null; } export interface RhcTrackedWallet { wallet_address: string; label: string | null; added_at: string; } export interface WalletTrackerWatchlistResponse { chain: Chain; wallets: RhcTrackedWallet[]; count: number; /** Per-tier cap for this chain. */ limit: number; remaining: number; } export interface WalletTrackerAddParams { wallet_address: string; label?: string; } export interface WalletTrackerWalletResponse { chain: Chain; wallet: RhcTrackedWallet; } export interface WalletTrackerRemovedResponse { chain: Chain; /** The lowercased address that was removed. */ removed: string; } export interface WalletTrackerTradesParams { limit?: number; before?: string; /** Restrict to one tracked wallet — must already be on the watchlist. */ wallet?: string; action?: TradeAction; token?: string; } export interface RhcTrackedWalletTrade extends RhcWalletTrade { /** Which tracked wallet made the trade. */ trader_eoa: string | null; /** Your watchlist label for that wallet. */ label: string | null; } export interface WalletTrackerTradesResponse { chain: Chain; trades: RhcTrackedWalletTrade[]; count: number; has_more: boolean; next_before: string | null; } export interface WalletTrackerSummaryParams { /** Lookback window, default `7d`. */ period?: string; /** Restrict the rollup to one tracked wallet. */ wallet?: string; } export interface RhcTrackedWalletStats { trades: number; buys: number; sells: number; buy_eth: number; sell_eth: number; net_eth: number; tokens_traded: number; last_trade_at: string | null; } export interface RhcTrackedWalletSummary extends RhcTrackedWallet { stats: RhcTrackedWalletStats; } export interface WalletTrackerSummaryResponse { chain: Chain; period: string; interval: string; /** `true` when the rollup query timed out — `wallets[].stats` are zeroed, not absent. */ stats_unavailable: boolean; wallets: RhcTrackedWalletSummary[]; } export interface StreamToken { token: string; ws_url: string; /** Present on some tiers — the all-DEX firehose URL. */ dex_ws_url?: string; /** * Always `null` since 2026-08-27 — stream tokens never expire. Kept for * wire compatibility only; never schedule a refresh from it. */ expires_at: string | null; /** Always `null` since 2026-08-27 (see `expires_at`). */ next_refresh_at?: string | null; /** * `true` when this call replaced the previous token (`{ rotate: true }`); * the previous value keeps working for 60 s. */ rotated?: boolean; /** Human-readable lifetime statement from the server (e.g. `"never expires"`). */ lifetime?: string; } /** Filters for the `rhc:token_bundles` channel (all optional, AND-combined; invalid values reject the channel). */ export interface RhcTokenBundlesFilters { /** Only these 0x token addresses (at most 250, case-insensitive). */ addresses?: string[]; /** Minimum distinct trading accounts in the cluster (3-50, count at detection). */ min_wallets?: number; /** Minimum accounts new to the token (3-50). */ min_new_wallets?: number; /** launch = within 5 minutes of the token's first sighting. */ phases?: Array<"launch" | "later" | "unknown">; /** Only clusters where one transaction carried 3+ of the buyers. */ atomic_only?: boolean; /** Only these event types (default both). */ bundle_events?: Array<"rhc:token_bundle_cluster" | "rhc:token_bundle_exit">; } /** * `rhc:token_bundle_cluster` on WS `rhc:token_bundles` (PRO+): one NEW * same-block buy cluster on a Robinhood Chain token, launch or later: >= 3 * effective trading accounts (ERC-4337 senders resolved) bought in one block * and >= 3 of them were new to the token. `same_tx_groups` is weaker evidence * than on Solana (a 4337 bundler batches unrelated userOps). Announced once; * evidence, not a verdict. Share held now: `tokenBundleShare()`. */ export interface RhcTokenBundleClusterEvent { /** bundle_cluster_events row id (dedupe key). */ id: number; chain: "robinhood"; token_address: string; block_number: number; at: string; minutes_after_launch: number | null; phase: "launch" | "later" | "unknown"; launch_at: string | null; wallet_count: number; new_wallets: number; same_tx_groups: number; eth_volume: number | null; /** The token's own leg, raw uint256 as a decimal string. */ token_volume_raw: string | null; /** Up to 50 buyer accounts (lowercase). */ wallets: string[]; detected_at: string; } /** * `rhc:token_bundle_exit` on WS `rhc:token_bundles` (PRO+): an announced * cluster's accounts have sold `threshold_pct` (25 / 50 / 90 / 100) % of what * they bought IN the cluster, each threshold once. Token amounts are the * token's raw uint256 as decimal strings. Only sells by the bundle accounts * themselves, followed for 7 days after detection. */ export interface RhcTokenBundleExitEvent extends Omit { cluster_id: number; cluster_detected_at: string | null; threshold_pct: 25 | 50 | 90 | 100; wallets_tracked: number; wallets_sold: number; wallets_exited: number; bought_tokens_raw: string | null; sold_tokens_raw: string; sold_pct: number | null; realized_eth: number; /** 2026-10 (migration 511) — "incomplete" when a recorded sell has unknown proceeds: its tokens count in sold_pct, its proceeds are not in realized_eth (never 0). null = not measured. */ realized_status?: "complete" | "incomplete" | null; unknown_proceeds_sells?: number | null; last_sell_at: string | null; recent_sells: Array<{ wallet: string; tx_id: string; tokens_raw: string | null; eth: number | null; at: string; }>; } //# sourceMappingURL=types.d.ts.map