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 gamesactiveLiveGamesCount
: Count of live gamesactivePrematchGamesCount
: Count of pre-match games
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
}
}
}
}
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
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
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
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
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>
- 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:
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.
type BetslipProviderProps = {
children: React.ReactNode
affiliate?: Address
- isBatchBetWithSameGameEnabled?: boolean
}
Context Value
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
.
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
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
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
andgetComboBetTypedData
have been added to generate typed data for single and combo betscreateBet
andcreateComboBet
have been added to create single and combo betsgetAvailableFreebets
andgetBonuses
have been added to retrieve bonuses
Removed
createLiveBet
andgetLiveBet
calcLiveOdds
andcalcPrematchOdds
getPrematchBetDataBytes
andgetLiveBetTypedData
getGameStatus
getFreeBets