Tap every funnel step of the submit and cash-out flows with the ParlayEvent union, and map it to PostHog or any analytics backend.
Both write hooks (useSubmitParlay, useCashOut) accept a ParlayCallbacks object with an onEvent tap. The SDK fires a typed event at every funnel step; your app maps them to whatever analytics backend it uses. The SDK itself never talks to analytics.
Raw amounts (stakeRaw, payoutRaw, cashValueRaw) are 6-decimal bigint. Convert to display dollars only at the analytics edge with Number(formatUsdc(raw)); never feed raw bigints to JSON-based trackers directly (JSON.stringify throws on bigint).
The second host concern in ParlayCallbacks. By default, every write hook calls wagmi’s switchChain to client.config.chainId before touching the wallet (failures are swallowed; the write itself will surface a wrong-network error). Hosts with their own network model (a testnet/mainnet toggle, a modal-based switcher) inject theirs instead:
import {useSwitchChain} from "wagmi";function useNetworkGuard() { const {switchChainAsync} = useSwitchChain(); return { ensure: async () => { // your own logic: check the toggle, prompt a modal, then switch await switchChainAsync({chainId: 998}); }, };}const {ensure} = useNetworkGuard();const submit = useSubmitParlay({ensureChain: ensure, onEvent: onParlayEvent});
If ensureChain throws, the flow stops with status: "error" and a failed event whose stage is the step it died in.
slip.quote_requested -> slip.quote_received -> parlay.submitted -> parlay.confirmed: the core conversion funnel; the gap between the first two is pricing rejections and HL latency.
slip.quote_failed grouped by reason: separates correlation refusals (“mutually exclusive”) from risk limits (“stake above maximum”) from infrastructure (“could not fetch HL prices”).
parlay.failed grouped by stage: signing failures are wallet rejections (UX), submitting failures are reverts or relayer issues (ops).