robinhood-chain-sdk 0.10.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -251,9 +251,11 @@ const { events, has_more, next_before, coverage } = await client.lpEvents({ toke
251
251
  for (const ev of events) {
252
252
  if (ev.provider_is_token_deployer) console.warn("deployer pulled liquidity:", ev.tx_hash, ev.dex, ev.token_amount_raw);
253
253
  }
254
- console.log(coverage.adds_persisted); // false — always
254
+ console.log(coverage.events); // ["remove"] — the default request is removals only
255
255
  ```
256
256
 
257
+ Since 2026-09-23 `action: "add" | "pool_created" | "all"` opts into liquidity adds (kept 7 days) and pool creations, and every row carries the depth fields `tick_lower` / `tick_upper`, `liquidity_delta`, `in_range`, `active_liquidity_delta`, `active_share` (v3/v4), `share_of_reserves` (v2) and `material` — null where the pool state was unknown and on older rows.
258
+
257
259
  > **Removals ONLY.** Liquidity adds are not persisted (v4 adds share the topic and are dropped at decode; v2/v3 `Mint` is not subscribed), so every row is `event: "remove"` and an empty page means "no removals seen", never "no liquidity activity" — the `coverage` block spells this out. Amounts are **raw uint256 decimal strings** (`liquidity`, `amount0` / `amount1`, plus pre-resolved `token_amount_raw` / `quote_token` / `quote_amount_raw`) — do not `Number()` them; v4 rows carry `liquidity` only because the pool manager emits no token amounts. Filters: `token`, `pool` (v2/v3 address or v4 bytes32 poolId), `provider`, `dex`. Data since 2026-08-05.
258
260
 
259
261
  ## Token intelligence — `client.tokens`
@@ -583,7 +585,7 @@ await client.kol.firstTouchSubscriptions.update(subscription.id, { filters: {} }
583
585
 
584
586
  ## Streaming — `client.stream` (PRO+)
585
587
 
586
- Managed WebSocket with token fetch on every (re)connect, auto-reconnect with backoff, heartbeat liveness, and typed events. Stream tokens **never expire** (since 2026-08-27) — there is no refresh timer; `client.stream.getToken()` returns the same token every call (`expires_at` / `next_refresh_at` are always `null`), and `getToken({ rotate: true })` replaces it (the old one keeps working for 60 s). Nine RHC channels:
588
+ Managed WebSocket with token fetch on every (re)connect, auto-reconnect with backoff, heartbeat liveness, and typed events. Stream tokens **never expire** (since 2026-08-27) — there is no refresh timer; `client.stream.getToken()` returns the same token every call (`expires_at` / `next_refresh_at` are always `null`), and `getToken({ rotate: true })` replaces it (the old one keeps working for 60 s). Fourteen RHC channels:
587
589
 
588
590
  | Channel | Emits | Tier | Scope |
589
591
  |---|---|---|---|
@@ -595,8 +597,32 @@ Managed WebSocket with token fetch on every (re)connect, auto-reconnect with bac
595
597
  | `rhc:price_alert:events` | `rhc:price_alert:dip`, `rhc:price_alert:recovery` | PRO+ | user-scoped; event-driven off each trade (a few seconds), not sub-second |
596
598
  | `rhc:kol:coordination` | `rhc:kol:coordination` | PRO+ | user-scoped — only **your** rules' fires |
597
599
  | `rhc:kol:first_touches` | `rhc:kol:first_touch` | PRO+ | broadcast — ULTRA gates only the first-touch *subscription CRUD*, not this channel |
598
- | `rhc:token_locks` | `rhc:token_lock` | PRO+ | broadcast — a token lock / vesting contract created on chain |
600
+ | `rhc:token_locks` | `rhc:token_lock` (+ `rhc:token_unlock_upcoming`, `rhc:token_unlock_available` with `filters.lifecycle: true`) | PRO+ | broadcast — a token lock / vesting contract created on chain; opt-in unlock-schedule events (claims / cancels are not observable on RHC) |
599
601
  | `rhc:token_prices` | `rhc:token_price` | PRO+ | **address-scoped** — `filters.addresses` required (25 / 100 / 250 per connection); one `snapshot: true` frame per address, then ≤ 1 tick per address per 250 ms with `quality` fresh / stale / unreliable + reason (`RhcTokenPriceTick`); no `seq` / `id` |
602
+ | `rhc:lp_events` | `rhc:lp_event` | **ULTRA+** | broadcast — liquidity `add` / `remove` / `pool_created` on tracked Uniswap v2/v3/v4 pools with `in_range`, `active_share`, `share_of_reserves`, `material` (`RhcLpStreamEvent`); filters `RhcLpEventsFilters`; durable resume |
603
+ | `rhc:token_candles` | `rhc:candle_closed`, `rhc:candle_revised` (+ `rhc:candle_update` with `filters.updates: true`) | PRO+ | scoped — live 1-minute candles for `filters.addresses` (`RhcCandleClosedEvent` / `RhcCandleUpdateEvent`); durable resume |
604
+ | `rhc:token_risk` | `rhc:risk_verdict_changed` (+ `rhc:risk_verdict` snapshot) | PRO+ | scoped — risk-verdict changes for `filters.addresses` (`RhcRiskVerdictChangedEvent`); score HIGHER = SAFER |
605
+ | `rhc:wallet_scores` | `rhc:deployer_tier_changed` | PRO+ | scoped — deployer tier changes for `filters.wallets` (0x deployers, `RhcDeployerTierChangedEvent`) |
606
+
607
+ **Liquidity events and lock schedule (server 2026-09-23).** `rhc:lp_events` frames carry raw `amount0` / `amount1` strings (null on v4), the position range and, for v3/v4, `in_range` / `active_liquidity_delta` / `active_share` — a share of liquidity at the current price, not of TVL (an out-of-range removal is `active_share: 0`); v2 carries `share_of_reserves`; `material: true` = a removal of ≥ 25 %. When the pool's tick was not known the active fields are `null` with `active_share_reason: "pool_state_unknown"` — never guessed. `provider` is usually the router / position manager. No USD field. On `rhc:token_locks`, `filters.lifecycle: true` adds `rhc:token_unlock_upcoming` (within 24 h) and `rhc:token_unlock_available` (passed within 30 min — **claimable per the schedule, not claimed**; `withdrawals_tracked: false`).
608
+
609
+ ```ts
610
+ const lp = client.stream.connect();
611
+ lp.subscribe({ subId: "rugs", channels: ["rhc:lp_events"], filters: { actions: ["remove"], material_only: true } });
612
+ lp.on("rhc:lp_event", (e: RhcLpStreamEvent) => console.log(e.dex, e.pool, e.action, e.active_share ?? e.share_of_reserves));
613
+ ```
614
+
615
+ **Candles, risk verdicts and deployer tiers (server 2026-09-23).** All three are PRO+ and **scoped**: per-connection cap PRO 25 / ULTRA 100 / BUSINESS 250 across named subscriptions; over the cap or without a scope the channel is rejected, never truncated. `rhc:token_candles` (`filters.addresses`, a budget separate from `rhc:token_prices`): `rhc:candle_closed` is the stored 1-minute row (id `candle:robinhood:<address>:<bucket epoch s>`), `rhc:candle_revised` the same row rewritten (`revision` n > 0, id suffix `:r<n>`); `filters.updates: true` adds `rhc:candle_update`, the in-progress minute (≤ 1 per address per second, a state stream: no id, never replayed, no snapshot). `rhc:token_risk` (`filters.addresses`, optional `risk_events`, `risk_snapshot` default `true`): `rhc:risk_verdict_changed` when a sweep recheck stores a different verdict — the change happened somewhere in `(previous_checked_at, checked_at]` — plus one `rhc:risk_verdict` snapshot frame per address (`frame.snapshot === true`); `score` is **higher = safer**, the opposite of Solana's `risk_score`. `rhc:wallet_scores` (`filters.wallets`, 0x deployers, optional `score_events`): `rhc:deployer_tier_changed` after each 5-min refresh ("recomputed at T", not "changed at T").
616
+
617
+ ```ts
618
+ const s = client.stream.connect();
619
+ s.subscribe({ subId: "candles", channels: ["rhc:token_candles"], filters: { addresses: [TOKEN], updates: true } });
620
+ s.subscribe({ subId: "risk", channels: ["rhc:token_risk"], filters: { addresses: [TOKEN] } });
621
+ s.subscribe({ subId: "tiers", channels: ["rhc:wallet_scores"], filters: { wallets: [DEPLOYER] } });
622
+ s.on("rhc:candle_closed", (c: RhcCandleClosedEvent) => console.log(c.bucket_start, c.open_price_usd, c.close_price_usd, c.volume_usd));
623
+ s.on("rhc:risk_verdict_changed", (e: RhcRiskVerdictChangedEvent) => console.log(e.token_address, e.changed, e.before.score, "→", e.after.score));
624
+ s.on("rhc:deployer_tier_changed", (e: RhcDeployerTierChangedEvent) => console.log(e.address, e.tier_before, "→", e.tier_after));
625
+ ```
600
626
 
601
627
  > **Deprecated:** `rhc:trades` was never a real channel — 0.4.0 subscribers got a `channels_rejected` warning and silence. The server now accepts it as an alias of `rhc:dex_trades` (and acks it under the canonical name), and the SDK keeps the literal marked `@deprecated` so 0.4.0 code compiles. Use `rhc:dex_trades`.
602
628
 
package/dist/index.d.ts CHANGED
@@ -369,17 +369,26 @@ export interface RhcLpEventsParams {
369
369
  provider?: string;
370
370
  /** Filter by DEX version. */
371
371
  dex?: UniswapVersion;
372
+ /**
373
+ * Which actions to return (2026-09-23). Default `"remove"` = the historical
374
+ * removals-only feed, unchanged. `"add"` (kept 7 days), `"pool_created"` or
375
+ * `"all"` opt into rows persisted since the WS Phase 3 producer.
376
+ */
377
+ action?: RhcLpAction | "all";
372
378
  /** Opaque cursor from `next_before` (same (block_time,id) keyset as /rhc/trades). */
373
379
  before?: string;
374
380
  }
381
+ /** A liquidity action on Robinhood Chain. */
382
+ export type RhcLpAction = "add" | "remove" | "pool_created";
375
383
  /**
376
- * One liquidity REMOVAL from `/rhc/lp-events`. Every row is `event: "remove"` —
377
- * adds are not persisted. Amounts are raw on-chain uint256 integers as decimal
378
- * STRINGS; v4 rows carry `liquidity` only (the pool manager emits no token
379
- * amounts), so `amount0` / `amount1` / `token_amount_raw` are null there.
384
+ * One liquidity event from `/rhc/lp-events`. Without `action` every row is
385
+ * `event: "remove"` (the default feed is removals only). Amounts are raw
386
+ * on-chain uint256 integers as decimal STRINGS; v4 rows carry `liquidity` only
387
+ * (the pool manager emits no token amounts), so `amount0` / `amount1` /
388
+ * `token_amount_raw` are null there.
380
389
  */
381
390
  export interface RhcLpEvent {
382
- event: "remove";
391
+ event: RhcLpAction;
383
392
  pool: string;
384
393
  dex: UniswapVersion;
385
394
  fee_tier: number | null;
@@ -411,6 +420,305 @@ export interface RhcLpEvent {
411
420
  block_time: string;
412
421
  tx_hash: string;
413
422
  log_index: number;
423
+ tick_lower?: number | null;
424
+ tick_upper?: number | null;
425
+ /** Signed liquidity change (v3/v4), int256 as string. */
426
+ liquidity_delta?: string | null;
427
+ /** tick_lower <= current tick < tick_upper just before the event; null = tick unknown. */
428
+ in_range?: boolean | null;
429
+ /** liquidity_delta when in range, "0" out of range. */
430
+ active_liquidity_delta?: string | null;
431
+ /** |active delta| / active liquidity before (v3/v4); 0 out of range; adds can exceed 1. */
432
+ active_share?: number | null;
433
+ /** v2 only: amount / reserve before (from the pair's Sync). */
434
+ share_of_reserves?: number | null;
435
+ /** true = a removal of ≥ 25 % of reserves / active liquidity; null for adds, creations and unknown shares. */
436
+ material?: boolean | null;
437
+ }
438
+ /** Why `active_share` is null / not the relevant share. */
439
+ export type RhcLpActiveShareReason = "pool_created" | "not_concentrated" | "pool_state_unknown" | "no_active_liquidity";
440
+ /**
441
+ * `rhc:lp_event` frame `data` (WS channel `rhc:lp_events`, ULTRA / BUSINESS,
442
+ * 2026-09-23). Frame id = `rhc:lp_event:<tx_hash>:<log_index>`. Every key is
443
+ * always present (null when unknown). `in_range` / `active_*` are null with
444
+ * `active_share_reason: "pool_state_unknown"` when the pool's tick was not
445
+ * known — never guessed. `provider` is usually a router / position manager,
446
+ * not the beneficial owner. No USD field.
447
+ */
448
+ export interface RhcLpStreamEvent {
449
+ chain: Chain;
450
+ action: RhcLpAction;
451
+ dex: UniswapVersion;
452
+ pool: string;
453
+ token_address: string | null;
454
+ token: {
455
+ address: string | null;
456
+ symbol: string | null;
457
+ decimals: number | null;
458
+ };
459
+ token0: string | null;
460
+ token1: string | null;
461
+ provider: string | null;
462
+ liquidity: string | null;
463
+ /** NULL on v4 — ModifyLiquidity reports no token amounts. */
464
+ amount0: string | null;
465
+ amount1: string | null;
466
+ tick_lower: number | null;
467
+ tick_upper: number | null;
468
+ liquidity_delta: string | null;
469
+ in_range: boolean | null;
470
+ active_liquidity_delta: string | null;
471
+ active_share: number | null;
472
+ active_share_reason: RhcLpActiveShareReason | null;
473
+ share_of_reserves: number | null;
474
+ material: boolean | null;
475
+ block_number: number | null;
476
+ block_time: string | null;
477
+ tx_hash: string;
478
+ log_index: number | null;
479
+ }
480
+ /** Per-subscription filters for `rhc:lp_events` (all optional, AND; invalid values reject the channel). */
481
+ export interface RhcLpEventsFilters {
482
+ addresses?: string[];
483
+ pools?: string[];
484
+ dexes?: UniswapVersion[];
485
+ actions?: RhcLpAction[];
486
+ material_only?: boolean;
487
+ /** 0..1000 — only events whose relevant share (reserves on v2, active on v3/v4) is known and ≥ this. */
488
+ min_share?: number;
489
+ }
490
+ /** Unlock-schedule event names on `rhc:token_locks` (only with `filters.lifecycle: true`). */
491
+ export type RhcTokenLockLifecycleEventName = "rhc:token_unlock_upcoming" | "rhc:token_unlock_available";
492
+ /**
493
+ * Filters that switch `rhc:token_locks` to lifecycle mode. Claims, extensions
494
+ * and cancels are NOT observable on Robinhood Chain — only the unlock schedule.
495
+ */
496
+ export interface RhcTokenLockLifecycleFilters {
497
+ lifecycle: true;
498
+ events?: RhcTokenLockLifecycleEventName[];
499
+ unlock_kinds?: Array<"cliff" | "final" | "tranche">;
500
+ /** Token scope for creates AND lifecycle (≤ 500); applied only with `lifecycle: true`. */
501
+ addresses?: string[];
502
+ }
503
+ /**
504
+ * `rhc:token_unlock_upcoming` (the lock's NEXT unlock, when it is within 24 h) /
505
+ * `rhc:token_unlock_available` (passed within the last 30 min — claimable PER
506
+ * THE SCHEDULE, NOT claimed). Frame id = `<event>:<event_key>`.
507
+ */
508
+ export interface RhcTokenUnlockScheduleEvent {
509
+ event_key: string;
510
+ lock_id: string;
511
+ token_address: string;
512
+ family: string;
513
+ observed_at: string | null;
514
+ unlock_at: string;
515
+ unlock_kind: "cliff" | "final" | "tranche";
516
+ amount_raw: string | null;
517
+ amount_reason: string | null;
518
+ unlocked_total_raw: string | null;
519
+ release_model: "at_end" | "linear" | "tranched";
520
+ claimable?: true;
521
+ chain: Chain;
522
+ locker: string | null;
523
+ kind: string | null;
524
+ subject: string | null;
525
+ lp_kind: string | null;
526
+ lp_pool: string | null;
527
+ sender: string | null;
528
+ recipient: string | null;
529
+ locked_amount_raw: string | null;
530
+ amount_unit: string | null;
531
+ decimals: number | null;
532
+ withdrawals_tracked: false;
533
+ }
534
+ /**
535
+ * `rhc:token_candles` subscribe filters (PRO+). `addresses` is REQUIRED (0x,
536
+ * case-insensitive) and capped per CONNECTION across named subscriptions —
537
+ * PRO 25 / ULTRA 100 / BUSINESS 250, a budget separate from
538
+ * `rhc:token_prices`. Over the cap, a missing scope or a non-boolean
539
+ * `updates` rejects the channel, never truncates.
540
+ */
541
+ export interface RhcTokenCandlesFilters {
542
+ addresses: string[];
543
+ /** Also receive the in-progress minute as `rhc:candle_update` (a state stream). Default false. */
544
+ updates?: boolean;
545
+ }
546
+ /**
547
+ * `rhc:candle_closed` / `rhc:candle_revised` frame `data` — the STORED
548
+ * 1-minute row (`rhc_ohlc_1m`), identical live and on a durable resume.
549
+ * Every key is always present (null when unknown). Frame id =
550
+ * `candle:robinhood:<address>:<bucket_start epoch s>`, plus `:r<n>` for the
551
+ * n-th revision (not event-prefixed). `revision` 0 = the close as first
552
+ * written; a rewrite (restart merge, spoofed-pool purge) bumps it and is
553
+ * announced as `rhc:candle_revised` with the full rewritten row.
554
+ */
555
+ export interface RhcCandleClosedEvent {
556
+ chain: Chain;
557
+ address: string;
558
+ bucket_start: string;
559
+ /** bucket_start + 60 s. */
560
+ bucket_end: string;
561
+ /** When the row was first written. */
562
+ closed_at: string | null;
563
+ revision: number;
564
+ /** Latest rewrite; null for revision 0. */
565
+ revised_at: string | null;
566
+ open_price_usd: number | null;
567
+ high_price_usd: number | null;
568
+ low_price_usd: number | null;
569
+ close_price_usd: number | null;
570
+ open_mc_usd: number | null;
571
+ high_mc_usd: number | null;
572
+ low_mc_usd: number | null;
573
+ close_mc_usd: number | null;
574
+ close_liquidity_usd: number | null;
575
+ close_supply: number | null;
576
+ volume_usd: number | null;
577
+ buy_volume_usd: number | null;
578
+ sell_volume_usd: number | null;
579
+ trades: number | null;
580
+ buy_count: number | null;
581
+ sell_count: number | null;
582
+ dex: string | null;
583
+ pool_address: string | null;
584
+ final: true;
585
+ source: "rhc_ohlc_1m";
586
+ }
587
+ /**
588
+ * `rhc:candle_update` frame `data` (only with `filters.updates: true`) — the
589
+ * producer's in-progress minute, ≤ 1 per address per second. A state stream:
590
+ * no id / seq, never replayed, no snapshot on subscribe.
591
+ */
592
+ export interface RhcCandleUpdateEvent {
593
+ chain: Chain;
594
+ address: string;
595
+ bucket_start: string;
596
+ bucket_end: string;
597
+ open_price_usd: number | null;
598
+ high_price_usd: number | null;
599
+ low_price_usd: number | null;
600
+ close_price_usd: number | null;
601
+ close_mc_usd: number | null;
602
+ volume_usd: number | null;
603
+ trades: number | null;
604
+ final: false;
605
+ /** The producer's timestamp of this state (ISO). */
606
+ as_of: string | null;
607
+ source: "rhc-dex-stream:open_candle";
608
+ }
609
+ /**
610
+ * `rhc:token_risk` subscribe filters (PRO+). `addresses` is REQUIRED (0x,
611
+ * lowercased), capped per connection PRO 25 / ULTRA 100 / BUSINESS 250.
612
+ */
613
+ export interface RhcTokenRiskFilters {
614
+ addresses: string[];
615
+ /** Non-empty subset of the channel's event types. */
616
+ risk_events?: Array<"rhc:risk_verdict_changed">;
617
+ /** Send one `rhc:risk_verdict` snapshot per address on subscribe / scope change / resume. Default true. */
618
+ risk_snapshot?: boolean;
619
+ }
620
+ /** A stored RHC risk verdict (event `before` / `after`, snapshot `verdict`). */
621
+ export interface RhcRiskVerdict {
622
+ /** 100 − penalties: HIGHER = SAFER — the opposite of Solana's `risk_score`; never compare the two. */
623
+ score: number | null;
624
+ sellable: boolean | null;
625
+ sellable_reason: string | null;
626
+ proxy_kind: string | null;
627
+ upgradeable: boolean | null;
628
+ owner_model: string | null;
629
+ can_mint: boolean | null;
630
+ can_pause: boolean | null;
631
+ lp_custody: string | null;
632
+ lp_burned_pct: number | null;
633
+ code_size: number | null;
634
+ flags: string[];
635
+ }
636
+ /**
637
+ * `rhc:risk_verdict_changed` — a sweep recheck stored a verdict that differs
638
+ * in a diffed field. The change happened somewhere in
639
+ * `(previous_checked_at, checked_at]` (days to weeks), never at `checked_at`.
640
+ * A first assessment is never an event. Frame id =
641
+ * `rhc:risk_verdict_changed:<event_key>`.
642
+ */
643
+ export interface RhcRiskVerdictChangedEvent {
644
+ chain: Chain;
645
+ token_address: string;
646
+ /** `<token>:verdict:<checked_at epoch ms>`. */
647
+ event_key: string;
648
+ changed: Array<"score" | "sellable" | "proxy_kind" | "upgradeable" | "owner_model" | "can_mint" | "can_pause" | "lp_custody" | "flags">;
649
+ flags_added: string[];
650
+ flags_removed: string[];
651
+ before: RhcRiskVerdict;
652
+ after: RhcRiskVerdict;
653
+ checked_at: string;
654
+ previous_checked_at: string | null;
655
+ score_semantics: "higher_is_safer";
656
+ detection: {
657
+ method: "recheck_sweep";
658
+ recheck_target_hours: number;
659
+ sweep_interval_minutes: number;
660
+ min_liquidity_usd: number;
661
+ note: string;
662
+ };
663
+ written_at: string;
664
+ source: "rhc_token_risk";
665
+ }
666
+ /**
667
+ * `rhc:risk_verdict` snapshot frame `data` (frame `snapshot: true`, no id /
668
+ * seq): the CURRENT stored verdict of one scoped address.
669
+ */
670
+ export interface RhcRiskVerdictSnapshot {
671
+ chain: Chain;
672
+ token_address: string;
673
+ /** false = never assessed (verdict and checked_at null). */
674
+ assessed: boolean;
675
+ verdict: RhcRiskVerdict | null;
676
+ checked_at: string | null;
677
+ score_semantics: "higher_is_safer";
678
+ source: "rhc_token_risk";
679
+ }
680
+ /**
681
+ * `rhc:wallet_scores` subscribe filters (PRO+). `wallets` is REQUIRED — 0x
682
+ * deployer addresses (lowercased) — capped per connection PRO 25 / ULTRA 100 /
683
+ * BUSINESS 250. A subscription that also holds the Solana `wallet:scores`
684
+ * channel may mix in base58 wallets; an entry of a chain the subscription does
685
+ * not hold is refused.
686
+ */
687
+ export interface RhcWalletScoresFilters {
688
+ wallets: string[];
689
+ /** Optional subset of the held channels' event types. */
690
+ score_events?: Array<"rhc:deployer_tier_changed" | "deployer:tier_changed" | "kol:score_state_changed">;
691
+ }
692
+ /**
693
+ * `rhc:deployer_tier_changed` — the 5-min `mv_rhc_deployers` refresh found a
694
+ * different tier ("recomputed at T", not "changed at T"). A deployer appearing
695
+ * `neutral` is not an event; entering a ranked tier is (`tier_before:
696
+ * "neutral"`), a first appearance already ranked has `tier_before: null` +
697
+ * `first_appearance: true`, vanishing from the matview has `tier_after: null`.
698
+ * Frame id = `rhc:deployer_tier_changed:<event_key>`.
699
+ */
700
+ export interface RhcDeployerTierChangedEvent {
701
+ /** `<address>:<refresh epoch ms>`. */
702
+ event_key: string;
703
+ chain: Chain;
704
+ address: string;
705
+ tier_before: DeployerTier | null;
706
+ tier_after: DeployerTier | null;
707
+ first_appearance: boolean;
708
+ /** null when the deployer left the matview. */
709
+ stats: {
710
+ tokens_deployed: number | null;
711
+ graduated: number | null;
712
+ graduation_rate: number | null;
713
+ runners: number | null;
714
+ runner_rate: number | null;
715
+ best_peak_mc_usd: number | null;
716
+ first_deploy_at: string | null;
717
+ last_deploy_at: string | null;
718
+ } | null;
719
+ computed_at: string;
720
+ source: "matview_refresh";
721
+ matview: "mv_rhc_deployers";
414
722
  }
415
723
  export interface RhcLpEventsResponse {
416
724
  chain: Chain;
@@ -419,10 +727,11 @@ export interface RhcLpEventsResponse {
419
727
  has_more: boolean;
420
728
  /** Opaque pagination cursor; pass back as `before`. */
421
729
  next_before: string | null;
422
- /** Honesty block: `events: ["remove"]`, `adds_persisted: false`, note, since. */
730
+ /** Honesty block: the actions this response covers (["remove"] by default), adds_persisted, note, since. */
423
731
  coverage: {
424
732
  events: string[];
425
- adds_persisted: boolean;
733
+ adds_persisted: boolean | null;
734
+ adds_retention_days?: number;
426
735
  note: string;
427
736
  since: string;
428
737
  };