Developer Hub
🔮 For applications
Guides & Tutorials
Advanced integration
Tutorial: Add Live to Existing dApp
Step 3: Realtime Market Updates

Adding Live. Step 3: Realtime market updates

Add WebSocket

As for prematch games, live game's markets are constantly updated:

  • Market can be stopped and unstopped.
  • Outcomes odds can be changed.

To receive updates, utilize the Socket API. Begin by creating a new socket instance.

The simplest approach is to utilize or duplicate the SocketContext from the SDK:
https://github.com/Azuro-protocol/sdk/blob/v3.1.0/src/contexts/socket.tsx (opens in a new tab)

The main components here are the event-emitters:

These events are triggered by the prematch contract event listeners (opens in a new tab) and the socket message handler:

socket.current.onmessage = (message: MessageEvent<SocketData>) => {
  JSON.parse(message.data.toString()).forEach((data: SocketData[0]) => {
    const { id: conditionId, reinforcement, margin, winningOutcomesCount } = data
 
    if (data.outcomes) {
      const eventData: OddsChangedData = {
        conditionId: conditionId,
        reinforcement: +reinforcement,
        margin: +margin,
        winningOutcomesCount: +winningOutcomesCount,
        outcomes: {},
      }
 
      eventData.outcomes = data.outcomes.reduce((acc, { id, odds, clearOdds, maxStake }) => {
        acc[id] = {
          odds,
          clearOdds,
          maxBet: maxStake,
        }
 
        return acc
      }, {} as Record<number, OddsChangedData['outcomes'][0]>)
 
      oddsWatcher.dispatch(conditionId, eventData)
    }
 
    if (data.state) {
      conditionStatusWatcher.dispatch(conditionId, data.state)
    }
  })
}

Use Created WebSocket Context

You can subscribe to updates by providing condition IDs to listen to.

Here's how it's implemented in the SDK:

part of useSelection.ts
import { isLiveEntity } from '@/config'
 
export const useSelection = ({ selection, initialOdds, initialStatus }: Props) => {
  const { coreAddress, conditionId, outcomeId } = selection
 
  const { contracts } = useChain()
  const { prematchClient } = useApolloClients()
  const { isSocketReady, subscribeToUpdates, unsubscribeToUpdates } = useSocket()
 
  const isLive = isLiveEntity(selection)
 
  useEffect(() => {
    if (!isLive || !isSocketReady) {
      return
    }
 
    subscribeToUpdates([ conditionId ])
 
    return () => {
      unsubscribeToUpdates([ conditionId ])
    }
  }, [ isSocketReady ])
 
  useEffect(() => {
    const unsubscribe = oddsWatcher.subscribe(`${conditionId}`, `${outcomeId}`, async (oddsData?: OddsChangedData) => {
      let odds: string | number | undefined = oddsData?.outcomes?.[String(outcomeId)]?.odds
 
      if (!odds) {
        const rawOdds = await publicClient!.readContract({
          address: contracts.prematchCore.address,
          abi: contracts.prematchCore.abi,
          functionName: 'calcOdds',
          args: [
            BigInt(conditionId),
            BigInt(1),
            BigInt(outcomeId),
          ],
        })
 
        odds = formatUnits(rawOdds, ODDS_DECIMALS)
      }
      else {
        setOddsFetching(false)
      }
 
      setOdds(+odds)
    })
 
    return () => {
      unsubscribe()
    }
  }, [ publicClient ])
 
  useEffect(() => {
    const unsubscribe = conditionStatusWatcher.subscribe(`${conditionId}`, (newStatus: ConditionStatus) => {
      setStatusFetching(false)
      setStatus(newStatus)
    })
 
    return () => {
      unsubscribe()
    }
  }, [])
/* ... */
/* See full code https://github.com/Azuro-protocol/sdk/blob/v3.1.0/src/hooks/useSelection.ts */