⚠️
Important! We’ve moved to V3! This documentation is for V2 only and will be updated in May.
Developer Hub
Azuro Protocol V3
Migrate SDK from v5 to v6

Migrate SDK from v5 to v6

In this migration guide, we will explore each component of your application and highlight the key changes in the SDK and Toolkit.

⚠️

The current SDK version is v6.0.0-beta.16, and the Toolkit version is v5.0.0-beta.11.

Key changes

Dependency Removal: @apollo/client

The heavyweight @apollo/client dependency has been removed, and all requests now go through TanStack Query (opens in a new tab).

New query prop for TanStack Query parameters

Data hooks (such as useSports) now support a query prop, allowing TanStack Query parameters to be passed in. This enhancement provides greater flexibility and control over requests.

useSports({
  ...
  query: {
    refetchInterval: 10_000,
    enabled: false,
  },
})

Full Query Information

Data hooks (such as useSports) now provide full access to TanStack Query’s return values. This enhancement allows for better handling of loading states, errors, caching, and other query metadata, giving developers more control over data-fetching behavior. All return data information can be found here (opens in a new tab).

const {
  data,
  isPending,
  isFetching,
  refetch,
} = useSports({...})

deBridge Support Discontinued

The new version of the Azuro protocol no longer supports bets using deBridge. As a result, the corresponding hooks have been removed:

  • useDeBridgeBet
  • useDeBridgeSupportedChains
  • useDeBridgeSupportedTokens
  • useDeBridgeSupportedTokens

Feed

Navigation

Games counters have been added to the useNavigation and useSportsNavigation hooks:

  • activeGamesCount: Total count of active games
  • activeLiveGamesCount: Count of live games
  • activePrematchGamesCount: Count of pre-match games
useNavigation schema
query Navigation(
  $first: Int,
  $sportFilter: Sport_filter, 
  $countryFilter: Country_filter, 
  $leagueFilter: League_filter,
) {
  sports(
    where: $sportFilter,
    subgraphError: allow
  ) {
    id
    slug
    name
    sportId
    activeGamesCount
    activeLiveGamesCount
    activePrematchGamesCount
    countries(where: $countryFilter) {
      id
      slug
      name
      turnover
      activeGamesCount
      activeLiveGamesCount
      activePrematchGamesCount
      leagues(where: $leagueFilter) {
        id
        slug
        name
        turnover
        activeGamesCount
        activeLiveGamesCount
        activePrematchGamesCount
      }
    }
  }
}
useSportsNavigation schema
query SportsNavigation(
  $first: Int,
  $sportFilter: Sport_filter,
) {
  sports(
    where: $sportFilter,
    subgraphError: allow
  ) {
    id
    slug
    name
    sportId
    activeGamesCount
    activeLiveGamesCount
    activePrematchGamesCount
  }
}

Usage and props for useNavigation and useSportsNavigation hooks remain unchanged

Events

Usage and props for useSports, useGames, useGame hooks remain unchanged

Game Schema Changes

The country field has been moved to the same level as the league, and the status field has been renamed to state

Game schema
fragment GameInfo on Game {
  id
  gameId
  title
  startsAt
  state
  sport {
    sportId
    slug
    name
  }
  league {
    slug
    name
  }
  country {
    slug
    name
  }
  participants {
    image
    name
  }
}
enum GameState {
  Finished = 'Finished',
  Live = 'Live',
  Prematch = 'Prematch',
  Stopped = 'Stopped'
}

Game State Updates

The useGameStatus hook has been renamed to useGameState. To subscribe to game state updates, use useGameState

const game = {...} // game from subgraph
 
const { data: state } = useGameState({
  gameId,
  initialState: game.state,
})
⚠️

The useGameState hook only subscribes to game state updates and does not fetch the game’s state on mount

Markets

The GameMarkets structure has been changed:

type GameMarkets = Market[]
 
type Market = {
  marketKey: string
  name: string
  description: string
-  outcomeRows: MarketOutcome[][]
+  conditions: Condition[]
}
 
type Condition = {
  conditionId: string
  state: ConditionState
  isExpressForbidden: boolean
  outcomes: MarketOutcome[]
}
 
type MarketOutcome = {
  selectionName: string
  odds: number
  gameId: string
  isExpressForbidden: boolean
  isWon?: boolean
} & Selection
 
type Selection = {
  conditionId: string
  outcomeId: string
-  coreAddress: string
}
enum ConditionState {
  Active = 'Active',
  Canceled = 'Canceled',
  Removed = 'Removed',
  Resolved = 'Resolved',
  Stopped = 'Stopped'
}

useConditions

UseConditionsProps
type UseConditionsProps = {
  gameId: string | bigint
  filter?: Condition_Filter
-  prematchQuery?: QueryProps
-  liveQuery?: QueryProps
+  query?: QueryParameter<ConditionsQuery['conditions']>
}

Returns UseQueryResult<ConditionsQuery['conditions']>

- prematchConditions: prematchData?.conditions,
- liveConditions: liveData?.conditions,
- loading: isPrematchLoading || isLiveLoading,
- error: prematchError || liveError,

useActiveConditions

UseActiveConditionsProps
type UseActiveConditionsProps = {
  gameId: string | bigint
-  isLive: boolean
-  livePollInterval?: number
  filter?: {
    outcomeIds?: string[]
    maxMargin?: number
  }
-  fetchPolicy?: FetchPolicy
+  query?: QueryParameter<ConditionsQuery['conditions']>
}

Returns UseQueryResult<ConditionsQuery['conditions']>

useActiveMarkets

UseActiveMarketsProps
type UseActiveMarketsProps = {
  gameId: 
-  gameStatus: GameStatus
  filter?: {
    outcomeIds?: string[]
    maxMargin?: number
  }
-  livePollInterval?: number
-  fetchPolicy?: FetchPolicy
+  query?: QueryParameter<ConditionsQuery['conditions']>
}

Returns UseQueryResult<GameMarkets>

useResolvedMarkets

Returns UseQueryResult<GameMarkets>

Return
-  groupedMarkets,
-  prematchMarkets,
-  liveMarkets,
-  loading,
-  error,

Condition State

The status field in Condition has been renamed to state.

The useSelection hook has been removed. To subscribe to condition state updates, use useConditionState instead. Example can be found here (opens in a new tab).

const { data: state, isLocked, isFetching } = useConditionState({ conditionId })
⚠️

You need to use the useConditionState hook at the top level of outcomes and pass the state as a prop down to the outcomes. This ensures that outcomes receive the correct state information for proper rendering and updates

Odds

The useSelection hook has been removed. To subscribe to odds updates, use useSelectionOdds instead. Example can be found here (opens in a new tab).

const { data: odds, isFetching } = useSelectionOdds({ selection })

Betslip

The betslip item now stores only the selection information by default:

declare global {
  namespace AzuroSDK {
    interface BetslipItem extends Selection {
      gameId: string
      isExpressForbidden: boolean
    }
  }
}

You can extend the betslip item in your global.d.ts to suit your needs by adding additional fields to the BetslipItem interface. This allows you to customize the data structure and store extra information as required for your application:

global.d.ts
import { type GameQuery } from '@azuro-org/toolkit'
 
 
declare global {
  namespace AzuroSDK {
    interface BetslipItem {
      marketName: string
      game: NonNullable<GameQuery['game']>
    }
  }
}

Example can be found here (opens in a new tab).

After extending the BetslipItem interface with your additional fields, you can pass them to the addItem function. These custom fields will then be accessible from the Betslip context, allowing you to retrieve and manage the data as needed:

const { items, addItem } = useBaseBetslip()
 
addItem({
  marketName: '...',
  game: {...},
  gameId: '...',
  conditionId: '...',
  outcomeId: '...',
  isExpressForbidden: false,
})
 
const { game } = items[0]

Max Bet

To retrieve the maximum bet amount, use the useMaxBet hook.

const { data: maxBet, isFetching } = useMaxBet({ selections })
ℹ️

The useMaxBet hook is part of the Betslip context, allowing access to the maximum bet amount within the betslip functionality.

const { maxBet } = useDetailedBetslip()

Batch bet

Batch betting are temporarily unavailable, and all related fields have been removed from the Betslip context. You need to remove any code related to these features. We will soon announce their release when they become available.

BetslipProviderProps
type BetslipProviderProps = {
  children: React.ReactNode
  affiliate?: Address
-  isBatchBetWithSameGameEnabled?: boolean
}

Context Value

DetailedBetslipContextValue
export type DetailedBetslipContextValue = {
  betAmount: string
-  batchBetAmounts: Record<string, string>
  odds: Record<string, number>
  totalOdds: number
  maxBet: number | undefined
  minBet: number | undefined
-  selectedFreeBet: FreeBet | undefined
-  freeBets: FreeBet[] | undefined | null
+  selectedFreebet: Freebet | undefined
+  freebets: Freebet[] | undefined | null
-  statuses: Record<string, ConditionStatus>
+  states: Record<string, ConditionState>
  disableReason: BetslipDisableReason | undefined
  changeBetAmount: (value: string) => void
-  changeBatchBetAmount: (item: ChangeBatchBetAmountItem, value: string) => void
-  changeBatch: (value: boolean) => void
-  selectFreeBet: (value?: FreeBet) => void
+  selectFreebet: (value?: Freebet) => void
-  isLiveBet: boolean
-  isBatch: boolean
-  isStatusesFetching: boolean
+  isStatesFetching: boolean
  isOddsFetching: boolean
-  isFreeBetsFetching: boolean
+  isFreebetsFetching: boolean
+  isMaxBetFetching: boolean
  isBetAllowed: boolean
}

Bet

The usePrepareBet hook has been renamed to useBet.

UseBetProps
type UseBetProps = {
- betAmount: string | Record<string, string>
+  betAmount: string
  slippage: number
  affiliate: Address
  selections: Selection[]
  odds: Record<string, number>
  totalOdds: number
- freeBet?: FreeBet
+ freebet?: FreeBet
  liveEIP712Attention?: string
  deadline?: number
  onSuccess?(receipt?: TransactionReceipt): void
  onError?(err?: Error): void
}

Usage for useBet hook remain unchanged

Freebets

⚠️

Will be available in production from June 2nd.

To begin implementing freebets in development, upgrade @azuro-org/sdk to version 6.1.0-beta.1 and @azuro-org/toolkit to version 5.1.0-beta.1.

Types

The Freebet type inherits from the BonusBase type, which is designed to support future bonus features. Currently, the only available bonus type is freebet.

type Selection = {
  outcomeId: string
  conditionId: string
}
 
export enum BonusType {
  FreeBet = 'FreeBet',
}
 
export enum BonusStatus {
  Used = 'Used',
  Available = 'Available',
}
 
type BonusBase = {
  id: string
  type: BonusType,
  amount: string
  status: BonusStatus
  chainId: ChainId
  expiresAt: number
  usedAt: number
  createdAt: number
}
 
export type Freebet = {
  type: BonusType.FreeBet,
  params: {
    isBetSponsored: boolean,
    isFeeSponsored: boolean,
    isSponsoredBetReturnable: boolean
  }
} & BonusBase
 
export type Bonus = Freebet

Retrieve All Bonuses

To retrieve all of a user's bonuses (currently limited to freebets), use the useBonuses hook.

const { data: bonuses } = useBonuses({
  account: '0x...', // user's address
  affiliate: '0x...', // your affiliate address
})

Retrieve Available Freebets

The useFreeBets hook has been renamed to useAvailableFreebets. This updated hook returns all freebets that can be used based on the provided bet information (selections).

const { data: freebets, isFetching } = useAvailableFreebets({
  account: '0x...', // user's address
  affiliate: '0x...', // your affiliate address
  selections: [...], // bet information
})

Freebet Usage

To use freebet provide it's data to the useBet hook

 
const freebet: Freebet = {...}
 
const { submit } = useBet({
  ...,
  freebet
})
 
submit()
ℹ️

The isSponsoredBetReturnable field indicates whether the value of the freebet will be included in the winning amount. If it is true, the freebet amount will be paid as part of the winnings. If it is false, only the profit (excluding the freebet value) will be paid out.

Taking this into account, here’s an example of how to calculate a bet’s possible win:

  const diff = freebet && freebet.params.isSponsoredBetReturnable ? +freebet.amount : 0
 
  // bet possible win based on isSponsoredBetReturnable field
  const possibleWin = totalOdds * +betAmount - diff

Retrieve Placed Bets with Freebets

To identify bets placed using freebets, use the useBets hook. Each bet item includes a freebetId field—if this field is present, the bet was placed with a freebet. This allows you to differentiate between regular and free bet transactions

type Bet = {
  actor: Address
  affiliate: Address
  tokenId: string
+ freebetId: string | null
+ isFreebetAmountReturnable: boolean | null
+ paymaster: Address | null
  totalOdds: number
  coreAddress: Address
  lpAddress: Address
  outcomes: BetOutcome[]
  txHash: Hex
  redeemedTxHash: Hex | null
  status: GraphBetStatus
  amount: string
  possibleWin: number
  payout: number | null
  createdAt: number
  resolvedAt: number | null
  cashout?: string
  isWin: boolean
  isLose: boolean
  isRedeemable: boolean
  isRedeemed: boolean
  isCanceled: boolean
  isCashedOut: boolean
}
ℹ️

The isFreebetAmountReturnable field indicates whether the value of the freebet will be included in the winning amount. If it is true, the freebet amount will be paid as part of the winnings. If it is false, only the profit (excluding the freebet value) will be paid out.

Taking this into account, here’s an example of how to calculate a bet’s possible win:

  const { amount, settledOdds, odds, freebetId, isFreebetAmountReturnable } = bet
 
  const isFreebet = Boolean(freebetId)
  const betDiff = isFreebet && isFreebetAmountReturnable ? amount : 0
  const totalOdds = settledOdds ? +settledOdds : +odds
 
  // bet possible win based on isFreebetAmountReturnable field
  const possibleWin = +amount * totalOdds - +betDiff

Batch Bet

Batch bet feature will be enabled soon in April.

Cashout

usePrecalculatedCashouts

UsePrecalculatedCashoutsProps
type UsePrecalculatedCashoutsProps = {
- tokenId: string
- selections: Selection[]
- graphBetStatus: GraphBetStatus
- enabled?: boolean
+ bet: Pick<Bet, 'tokenId' | 'amount' | 'outcomes' | 'status' | 'totalOdds'>
+ query?: QueryParameter<PrecalculatedCashoutsQueryData>
}

Returns

UseQueryResult<{
  isAvailable: false,
  cashoutAmount: number,
}>

useCashout

UseCashoutProps
type UseCashoutProps = {
- tokenId: string
- selections: Selection[]
+ bet: Pick<Bet, 'tokenId' | 'outcomes'>
  EIP712Attention?: string
  onSuccess?(receipt?: TransactionReceipt): void
  onError?(err?: Error): void
}

Bets

The usePrematchBets and useLiveBets hooks have been removed. To retrieve v3 user bets, use the useBets hook instead.

const { data } = useBets({
  filter: {
    bettor: '...',
  },
  orderDir: OrderDirection.Desc,
})
ℹ️

The useBets hook leverages useInfiniteQuery (opens in a new tab) for pagination. To load additional pages, use the fetchNextPage function. If the hasNextPage flag is false, there are no more pages available for fetching

const { data, fetchNextPage, hasNextPage } = useBets({
  filter: {
    bettor: '...',
  },
  itemsPerPage: 10, // pagination settings
  orderDir: OrderDirection.Desc,
})
⚠️

The useBets hook does not fetch v2 user bets. If you need to display old bets, use the useLegacyBets hook instead.

const { data, fetchNextPage, hasNextPage } = useLegacyBets({
  filter: {
    bettor: '...',
  },
  itemsPerPage: 10, // pagination settings
  orderDir: OrderDirection.Desc,
})

Toolkit Changes

  • changed chain data type
type ChainData = {
  chain: Omit<Chain, 'id'> & { id: ChainId }
  graphql: {
-   prematch: string
-   live: string
+   bets: string // graph endpoint to fetch bets
+   feed: string // graph endpoint to fetch feed
  }
  socket: string
  api: string
  environment: Environment
  contracts: Contracts
  betToken: BetToken
}
 
type Contracts = {
  lp: {
    address: Address
    abi: typeof lpAbi
  }
+ core: {
+   address: Address
+   abi: typeof coreAbi
+ }
+ relayer: {
+   address: Address
+   abi: typeof relayerAbi
+ }
+ vault: {
+   address: Address
+   abi: typeof vaultAbi
+ }
+ paymaster: {
+   address: Address
+   abi: typeof paymasterAbi
+ }
  azuroBet: {
    address: Address
    abi: typeof azuroBetAbi
  },
  cashout?: {
    address: Address
    abi: typeof cashoutAbi
  }
- prematchCore: {}
- prematchComboCore {}
- proxyFront {}
- liveRelayer {}
- liveCore {}
}
  • return value has beed changed for groupConditionsByMarket
type GameMarkets = Market[]
 
type Market = {
  marketKey: string
  name: string
  description: string
-  outcomeRows: MarketOutcome[][]
+  conditions: Condition[]
}
 
type Condition = {
  conditionId: string
  state: ConditionState
  isExpressForbidden: boolean
  outcomes: MarketOutcome[]
}
 
type MarketOutcome = {
  selectionName: string
  odds: number
  gameId: string
  isExpressForbidden: boolean
  isWon?: boolean
} & Selection
 
type Selection = {
  conditionId: string
  outcomeId: string
-  coreAddress: string
}
  • getBetTypedData and getComboBetTypedData have been added to generate typed data for single and combo bets
  • createBet and createComboBet have been added to create single and combo bets
  • getAvailableFreebets and getBonuses have been added to retrieve bonuses

Removed

  • createLiveBet and getLiveBet
  • calcLiveOdds and calcPrematchOdds
  • getPrematchBetDataBytes and getLiveBetTypedData
  • getGameStatus
  • getFreeBets