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 installRun 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 treeOr 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.