TypeScript packages + working demos for building on the Toncast prediction-market protocol:
| Package | Path | What it does |
|---|---|---|
@toncast/sdk |
packages/sdk |
Universal, framework-agnostic SDK: REST, WebSocket streams, betting flow via @toncast/tx-sdk. Browser + Node 20+. |
@toncast/sdk-react |
packages/sdk-react |
Thin React wrapper on top of @tanstack/react-query: provider + hooks for every read / live / betting endpoint, plus optional useTonConnectClient bridge. Supports React 18 + 19. Pattern lifted from @ston-fi/omniston-sdk-react. |
@toncast/widget |
packages/widget |
Embeddable betting widget with standalone and integrated TonConnect modes, live market views, and white-label CSS variable theming. |
@toncast/widget-loader |
packages/widget-loader |
Lightweight CDN loader for apps that want to mount the hosted widget bundle at runtime. |
| Demo app | examples/react-app |
Vite + React 19 + Tailwind 4 + Radix UI. Browse paris (live), open one (live odds), connect TonConnect, bet in any wallet coin (TON or any jetton with a STON.fi route). |
.
├── packages/
│ ├── sdk/ # @toncast/sdk
│ │ ├── src/
│ │ ├── tests/
│ │ ├── scripts/ # smoke tests against the live API
│ │ ├── examples/
│ │ └── README.md # ← full SDK docs (start here)
│ ├── sdk-react/ # @toncast/sdk-react
│ │ ├── src/
│ │ ├── tests/
│ │ └── README.md # ← React hooks reference
│ ├── widget/ # @toncast/widget
│ └── widget-loader/ # @toncast/widget-loader
├── examples/
│ ├── react-app/ # Vite + React 19 demo
│ │ ├── src/ # pages, components, providers
│ │ ├── public/ # tonconnect-manifest.json
│ │ └── README.md # ← run + deploy guide
│ └── widget-constructor/ # configure, preview, export widget embeds
├── biome.json # shared lint + format config
├── package.json # npm workspaces root
└── README.md # you are here
The repo is an npm workspaces monorepo. One install handles both packages:
npm install
Run a script across every workspace:
npm run typecheck # tsc --noEmit in each package npm run build # tsup → dist/ in each package npm test # vitest in each package npm run lint # biome over the whole tree
Or scope to one package:
npm run test --workspace @toncast/sdk
npm run build --workspace @toncast/sdk-reactTwo ways to load the same hosted bundle; both expose the ToncastWidget class.
- Direct CDN script (no bundler): add one
<script>tag; in modern browsers the bundle attaches towindow.ToncastWidget. - npm loader (
@toncast/widget-loader): callload()to inject the CDN script and receive the constructor — handy for React apps and dynamic import.
CDN URLs are major-versioned (/v0/, /v1/, ...): you get non-breaking updates within a major; change the path for breaking releases.
Option A — CDN
Host a TON Connect manifest at {your-domain}/tonconnect-manifest.json. The widget’s standalone mode uses tonconnect.options.domain as that origin (manifest URL = domain + '/tonconnect-manifest.json').
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <title>Toncast widget (CDN)</title> </head> <body> <div id="toncast-widget"></div> <script src="https://widget.toncast.me/v0/index.iife.js"></script> <script> const Widget = window.ToncastWidget.ToncastWidget; const widget = new Widget({ tonconnect: { type: "standalone", options: { domain: "https://your-app.com" }, }, }); widget.mount(document.getElementById("toncast-widget")); </script> </body> </html>
Option B — npm loader (e.g. React + integrated TonConnect)
import { useEffect, useRef } from "react"; import { useTonConnectUI } from "@tonconnect/ui-react"; import ToncastWidgetLoader, { type ToncastWidgetInstance, } from "@toncast/widget-loader"; function ToncastBettingWidget() { const [tonconnect] = useTonConnectUI(); const containerRef = useRef<HTMLDivElement>(null); const widgetRef = useRef<ToncastWidgetInstance | null>(null); useEffect(() => { let active = true; ToncastWidgetLoader.load() .then((Widget) => { if (!active || !containerRef.current) return; widgetRef.current = new Widget({ tonconnect: { type: "integrated", instance: tonconnect }, }); widgetRef.current.mount(containerRef.current); }) .catch((err) => console.error("[ToncastWidget] load failed:", err)); return () => { active = false; widgetRef.current?.unmount(); widgetRef.current = null; }; }, [tonconnect]); return <div ref={containerRef} style={{ width: "100%" }} />; }
Wrap your tree in TonConnectUIProvider with a valid manifest URL for your domain when using integrated mode from a full app.
For visual setup and exported snippets, use examples/widget-constructor/.
Container
idconvention.mount(container)accepts anyElement. The#toncast-widgetid used in every snippet above is just a convention:widget-constructor’s exportedstyle.cssscopes per-instance overrides under#toncast-widget { ... }. Keep that id (or rewrite the CSS scope) when reusing the exported stylesheet.
The successful-bet event is exposed identically through both surfaces:
- Class:
widget.on("bet", ({ pariId, amount, side }) => ...)(and matchingoff). - React:
<Widget onBet={({ pariId, amount, side }) => ...} />from@toncast/widget/react.
Both deliver the same payload and fire after the wallet send call resolves. See packages/widget/README.md for the full snippets.
@toncast/widget accepts widget.cssVars for per-instance visual customization and
widget.layout.grid for responsive market-card columns.
Source tokens such as accent, bg, success, danger, warn, and density
are resolved into readable foregrounds, subtle backgrounds, hover/active states,
surface/chrome colors, order-book fills, shadows, borders, and spacing variables.
const widget = new ToncastWidget({ tonconnect: { type: "standalone", options: { domain: "https://your-app.com" }, }, widget: { theme: "system", cssVars: { accent: "#7c3aed", success: "#10b981", danger: "#ef4444", warn: "#f59e0b", density: "compact", light: { bg: "#ffffff" }, dark: { bg: "#0b1020" }, }, layout: { grid: { mobile: 1, tablet: 2, desktop: 3, }, }, }, });
Explicit values always win. For example, if you pass successBg or
successFillBg, the widget keeps that exact value instead of deriving one from
success. Set deriveCssVars: false
to disable all derivation, or use deriveCssVars: { colors: false } /
{ density: false } to disable one group.
- Building anything? Start with
packages/sdk/README.md— full API surface, betting flow, advanced jetton discovery. - Building a React UI? Read
packages/sdk-react/README.md— quick start + every hook in one page. - Public API & versioning (pre–1.0.0): see each package
README.mdand rootCHANGELOG.md. - Releases / changelog:
CHANGELOG.md. - Want to see it work? Run the demo:
npm run dev --workspace @toncast/react-app-example. Source inexamples/react-app/.
MIT — see LICENSE.