npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@toncast/sdk-react

v0.0.6

Published

React hooks + provider for @toncast/sdk (paris, bets, live streams, betting flow, optional TonConnect sync)

Readme

@toncast/sdk-react

React hooks for @toncast/sdk — built on top of @tanstack/react-query. REST methods become useQuery queries.

Two live-data paths: WS-backed list and single-pari snapshots (paris.streamList, paris.subscribe) use useLiveStreamQuery (useSyncExternalStore) so post-initial errors and updates stay live without treating the stream as a one-shot promise. Observable-style resources such as betting.subscribeSummary use useObservableQuery (TanStack cache + forced per-emission renders). Pick the hook the SDK already exposes; only reach for the low-level adapters when composing custom streams.

Use toncastQueryKeys for every prefetchQuery, setQueryData, and targeted invalidateQueries so keys match the built-in hooks exactly (including serializeKey for params with bigint).

Status: 0.0.1 (pre–1.0.0). Pin exact versions until 1.0.0. See CHANGELOG.md and repository AGENTS.md for betting and address handling.

Pattern lifted from @ston-fi/omniston-sdk-react — thin wrappers around TanStack Query, single Observable adapter for streaming endpoints.

The SDK layer does not sign transactions; hooks surface errors from the client — do not swallow ToncastError / ToncastApiError / ToncastWsError in production UIs.

Install

npm install @toncast/sdk @toncast/sdk-react @tanstack/react-query
# Optional, only if you use TonConnect for wallet auth:
npm install @tonconnect/ui-react

Peer dependencies: react ^18 || ^19, @tanstack/react-query ^5.

Quick start

import { TonClient, ToncastClient } from "@toncast/sdk";
import { ToncastProvider, useStreamList } from "@toncast/sdk-react";

const tonClient = new TonClient({
  endpoint: "https://toncenter.com/api/v2/jsonRPC",
  apiKey: import.meta.env.VITE_TONCENTER_API_KEY,
});
const client = new ToncastClient({ tonClient });

function App() {
  return (
    <ToncastProvider client={client}>
      <ParisFeed />
    </ToncastProvider>
  );
}

function ParisFeed() {
  const { data, isLoading } = useStreamList({ feed: "active" });
  // `data` is the latest `Pari[]` snapshot (re-emitted on every WS update)
  if (isLoading) return <p>Loading…</p>;
  return data?.map((p) => <div key={p.id}>{p.name}</div>);
}

<ToncastProvider> creates an internal QueryClient automatically. Pass queryClient={appQueryClient} to share with an existing TanStack-Query app.

import { toncastQueryKeys } from "@toncast/sdk-react";

// Prefetch must use the same builders as the hooks
void queryClient.prefetchQuery({
  queryKey: toncastQueryKeys.paris.detail(pariId),
  queryFn: ({ signal }) => client.paris.get(pariId, signal),
});

Hooks

Provider

  • <ToncastProvider client={...} queryClient?={...}> — wires both clients into context.
  • useToncastClient() — read the SDK client (throws outside a provider).

High-level (recommended)

  • useBet(params) — all-in-one hook for the full bet flow: summary stream, coin picker, quote, confirm. Returns { summary, coins, quote, confirm, side, mode, tickets, … }. Use this unless you need fine-grained control.

Read (REST → useQuery)

| Hook | Wraps | Notes | |---|---|---| | useParis(params) | paris.list | Single page (cursor-paginated). | | usePari(id) | paris.get | Disabled when id is falsy. | | useBets(params) | bets.listForUser / listForPariByUser | pariId optional → cross-pari history. | | useCategories() | categories.list | Raw { id, title } categories, staleTime: Infinity. | | useCategoryFilters() | categories.listFilters | UI-ready chips whose param goes into useStreamList. | | useCoins(opts) | coins.list | TON + jettons via toncenter v3. Requires tonClient. | | useBetQuote(params \| null) | betting.quoteFixedBet/quoteLimitBet/quoteMarketBet | Auto re-quotes on params change. |

Pass any TanStack UseQueryOptions (enabled, staleTime, select, refetchInterval, …) as a second arg to any read hook.

Live (useSyncExternalStore)

  • useStreamList(params, options?) — wraps paris.streamList. data is the latest Pari[] snapshot; updates on every WS broadcast. Also exposes hasNextPage, fetchNextPage, isFetchingNextPage for infinite scroll. Options: keepPreviousData (default true; set false to reset the list when category/filter changes).
  • useSubscribe(pariId) — wraps paris.subscribe. data is the latest PariStreamSnapshot ({ pari, oddsState, coefficientHistory }). Returns the same live state shape as useStreamList.
  • useBetSummary(pariId) — wraps betting.subscribeSummary. Emits two phases: TON-only (~200 ms) then full jetton pricing (STON.fi, warm: instant / cold: 3–8 s). Uses useObservableQuery internally, not a simple useQuery.

Mutations

  • useConfirmBet() — TanStack useMutation for betting.confirmQuote. Trigger right before signing and pass acknowledged params: mutateAsync({ quote, params: { ...quoteParams, financialRiskAcknowledged: true } }).

TonConnect bridge (optional peer-dep)

  • useTonConnectClient(userAddress) — mirrors any wallet-bridge address into client.userAddress. With @tonconnect/ui-react:
import { useTonAddress } from "@tonconnect/ui-react";
function WalletSync() {
  useTonConnectClient(useTonAddress()); // "" when disconnected
  return null;
}

Custom Observables

If you build your own Observable on top of the SDK (e.g. transformed paris.streamList), wrap it via the low-level adapter:

  • useObservableQuery({ queryKey, requestFn }) — same shape as useQuery, but requestFn returns a Subscribable<T> (anything with subscribe(observer)). Each next() updates data and forces a re-render via useSyncExternalStore (so you don't lose intermediate values to TanStack's microtask batching).

End-to-end bet flow

The simplest approach — use useBet, which composes summary, quote, and confirm into one ergonomic API:

import { useBet } from "@toncast/sdk-react";
import { useTonAddress, useTonConnectUI } from "@tonconnect/ui-react";

function BetCard({ pariId }: { pariId: string }) {
  const userAddress = useTonAddress();
  const [tc] = useTonConnectUI();
  const bet = useBet({ pariId: userAddress ? pariId : null, defaultSide: "yes" });

  return (
    <button
      disabled={!bet.quote.isFeasible || bet.confirm.isPending}
      onClick={async () => {
        const confirmed = await bet.confirmCurrent({ financialRiskAcknowledged: true });
        if (!confirmed) return;
        await tc.sendTransaction({
          messages: confirmed.messages,
          validUntil: Math.floor(Date.now() / 1000) + 5 * 60,
        });
      }}
    >
      Bet {bet.side.toUpperCase()}
    </button>
  );
}

For advanced use-cases (custom UI, partial control) you can compose the lower-level hooks directly — useBetSummaryuseBetQuoteuseConfirmBet. See the example app's BetCard.tsx for a full reference implementation using useBet.

License

MIT — see LICENSE.