Developer Hub
📦 Releases
05/24 Combo Bets

05/24 Combo Bets

Azuro Protocol recently launched a new type of bet called combo bets.

The combo bet is a type of bet that enables users to combine outcomes from different conditions and place them as a single bet. This bet wins only if each of the selected outcomes in the set wins, with the payout coefficient being the product of the selected odds, taking into account the applied margin.

One of the advantages of combo bets is that users can get higher odds. In contrast, betting apps can get a higher margin from the bets made by users. This is because a combo bet contains the margin of all the included sub-bets on ordinaries, and it also increases turnover and profits by attracting players to combo bets.

Contracts

Change Log

Azuro Factory now offers support for a new BettingEngine called BetExpress.

With BetExpress, users can place combo bets on events from one of the previously connected PrematchCore instances. The operation of the BetExpress contract generally does not affect the operation of PrematchCore, except that the odds in the PrematchCore also change as a result of making combo bets, but more subtly compared to if the bet were placed on one of the PrematchCore Conditions.

Migration Guide

To connect BetExpress to your pool, perform the following four simple steps:

  1. The pool owner must call the plugExpress method of the Azuro Factory smart contract and pass the following parameters:

    • lp - The pool address for which you want to connect BetExpress.
    • core - The address of the PrematchCore smart contract on which combo bets will be placed within the deployed contract. The core smart contract must belong to the pool whose address is passed in the lp parameter.
    • coreType - "express".

    As a result, a new BetExpress smart contract will be deployed. You can obtain the address of the deployed BetExpress contract from the core parameter emitted by the Factory NewBet event.

  2. To configure your Pool for the deployed BetExpress contract, you need to set the reinforcementAbility and minBet parameters. These parameters determine the maximum share of Pool's liquidity that the BetExpress contract can use and the minimum bet size.

    To enable your BettingEngine, call the updateCoreSettings method in your LP smart contract and pass the following parameters:

    • core - The address of the deployed BetExpress contract.
    • state - Set this parameter to 1. If set to 2, your BettingEngine will be disabled, and users will not be able to place bets, although they can still withdraw their rewards.
    • reinforcementAbility - Set this parameter to the desired maximum share of Pool's liquidity that the BetExpress smart contract can use. The value should be represented as a fraction of 10^12. For example, if you want the BetExpress to use only 10% of the liquidity available in the Pool, set this parameter to 100000000000.
    • minBet - Set this parameter to the desired minimum bet size.

    For other BettingEngines, these parameters determine the maximum share of Pool's liquidity that the deployed BetExpress smart contract can use and the minimum bet size. You can change these settings by calling the updateCoreSettings method again.

    By default, the reinforcement ability for the deployed BettingEngine is 10^12 (100%), and the minimum bet is 1.

    To set the maxReinforcementShare parameter inside the deployed BetExpress contract, which determines how much of the funds the BetExpress smart contract has at its disposal within the Pool can be lost by a single Condition across all combo bets, follow these steps:

  3. Call the changeMaxReinforcementShare method in the deployed BetExpress smart contract, passing the following parameter:

    • newMaxReinforcementShare - the desired maximum share where 10^12 is 100% of the current reinforcementAbility. You can change these settings in the future.

    Note that the Pool owner must perform this action.

  4. Grant the BetExpress smart contract address access to the changeOdds method of the PrematchCore smart contract, which was passed to the plugExpress function in step 1. How to grant access depends on how your Pool's Access smart contract is configured.

How To Bet on Combo Bets

To place a combo bet, you must call the bet function of the LP smart contract and provide the following parameters:

  • core - The address of the BetExpress smart contract associated with the LP.
  • amount - The amount of the bet.
  • expiresAt - The timestamp after which the bet becomes invalid.
  • betData - A tuple that includes:
    • affiliate - The address of the affiliate (frontend) through which you make the combo bet. This address will receive the affiliate's reward for your bet. If you are making a bet independently, you can specify your address to receive affiliate rewards after resolving the condition.
    • data - The data of the sub-bets included in the combo bet. The data is represented as an ABI-encoded array of tuples (conditionId, outcomeId), where:
    • conditionId - The ID of the condition for the sub-bet.
    • outcomeId - The ID of the outcome of the condition with ID conditionId for the sub-bet.

Here is an example of data for placing a combo bet that includes two sub-bets: on outcome 1 of Condition 101 and on outcome 2 of Condition 202.

  • core - "0x7a73EF9087C6E7043ec2125DB159E908B2267B11"
  • amount - 1000000000000000000
  • expiresAt - 1684385472
  • betData - ("0x860d20A09a980C5253a5dBA178E11ec3e1836a22", data), where data represents encoded data of the form [ "tuple(uint256 conditionId, uint64 outcomeId)[]", "uint64" ].

Potential Issues

Users are unable to place bets due to the TooLargeReinforcement custom error.

This message indicates that the user is trying to place a combo bet that would cause one of the conditions to exceed the maxReinforcementShare limit. To increase the limit on how much a single condition can lose across all combo bets, call the changeMaxReinforcementShare function in the BetExpress smart contract with a new limit (refer to step 3 of the Migration Guide).

Users are unable to place bets due to the AccessNotGranted custom error.

This means that you still need to grant the BetExpress smart contract access to the changeOdds function of PrematchCore. The smart contract retrieves information about Conditions from this function (see step 4 of the Migration Guide).

Users cannot place bets because of the TooFewSubbets custom error.

This means that the user is attempting to place a combo bet with fewer than two sub-bets, which is forbidden.

GraphQL

Motivation

The main goal of the major release is to allow support for combo bets to exist within the same entity as single bets. In the initial version, all single bets were associated with a single Outcome entity. This needed to be clarified because the Outcome entity also had the odds field. GraphQL users might have mistakenly assumed that this field represented the bet odds.

To address this confusion, we are introducing the Selection entity. It combines the bet, bet odds, and outcome. Now, all bets contain a selections: Selection[] field that merges single and combo bets into a single entity. For a single bet, this field will contain only one selection entity item, while for a combo bet, it will contain two or more.

Change Log

In order to keep the current implementation, a new version of Subgraph has been added. Use the following endpoints to implement combo bets:

A new entity called ExpressPrematchRelation has been added. It contains the definition of the pre-match core to which the combo contract is attached.

In addition, the oracles field has been removed from LiquidityPoolContract. Accesses in v2 contracts are granted differently, and this field has become outdated.

Changes to the entities:

AzuroBetContract

  • The oracleGameId field has been removed. Please use gameId instead.

Game

  • The following fields have been marked as service fields with _ prefix:

    • activeConditionsEntityIds_activeConditionsEntityIds
    • resolvedConditionsEntityIds_resolvedConditionsEntityIds
    • canceledConditionsEntityIds_canceledConditionsEntityIds
    • pausedConditionsEntityIds_pausedConditionsEntityIds
    • updatedAt_updatedAt
  • The following fields have been renamed:

    • canceledBlockNumberresolvedBlockNumber
    • canceledBlockTimestampresolvedBlockTimestamp
    • canceledTxHashresolvedTxHash

Condition

  • A new field wonOutcomeId is added.

  • The oracleConditionId field has been removed - use conditionId instead.

  • The following fields have been marked as service fields with _ prefix:

    • updatedAt_updatedAt
  • The following fields have been renamed:

    • canceledBlockNumberresolvedBlockNumber
    • canceledBlockTimestampresolvedBlockTimestamp
    • canceledTxHashresolvedTxHash

Outcome

  • The fields potentialPayout, payout, and betsAmount have been removed.
  • The following fields have been marked as service fields with _ prefix:
    • betsIds_betsEntityIds
  • The following fields have been renamed:
    • rawOdds should be renamed to rawCurrentOdds.
    • odds should be replaced with currentOdds. This field contains the current odds for the outcome, and updates with each bet. Do not use this field for bets that have already been place.
    • bets should be replaced with selections.

Selection

This entity describes the selected outcome of the game within a single condition. Combo bets contain several selections, while single bets can contain only one selection.

  • The fields:
    • id - entity id, a composite string {betEntityId}_{conditionId}
    • bet: Bet! - a reference to the selection’s bet entity
    • odds - the odds at which the sub-bet was placed
    • rawOdds - the odds, raw value
    • result - the result of the sub-bet - Won or Lost
    • outcome: Outcome! - the entity of the outcome on which the sub-bet was placed
    • _outcomeId - id of the outcome on which the sub-bet was made. This is a service field, use outcome.outcomeId
    • _oddsDecimals - the odds decimals

Bet

  • The following fields have been marked as service fields with _ prefix:

    • tokenDecimals_tokenDecimals
    • oddsDecimals_oddsDecimals
    • isFreebet_isFreebet
    • updatedAt_updatedAt
  • The following fields have been renamed:

    • affilateaffiliate
  • New fields have been added:

    • type - type of bet. Ordinar for single bet or Express for combo bet
    • payout - actual payout, formatted taking into account decimals. 0 in case of loss, from 1 to potentialPayout value for a full win or a payout for a cancellation of part of the conditions
    • approxSettledAt - approximate time of bet settlement - time of settlement of the latest condition from all sub-bets + 2 hours
    • rawPayout - actual payout, raw value
    • settledOdds - the odds value calculated after all sub-bets was settled. If some of condition of any sub-bet is canceled then this value will be different from the odds field’s value. In other cases the field will be equal to odds field. Before all sub-bets are settled the value is null.
    • selections: [Selection!] - the Selection entity is described above.
    • resolvedBlockNumber - the block of settlement of the entire bet - the block of settlement of the last condition from all sub-bets
    • resolvedBlockTimestamp - time of the block of settlement of the entire bet
    • resolvedTxHash - transaction of settlement of the entire bet
    • redeemedBlockNumber - the block of the withdrawPayout transaction (when Bet NFT was redeemed)
    • redeemedBlockTimestamp - time of the block of the withdrawPayout transaction (when Bet NFT was redeemed)
    • redeemedTxHash - withdrawPayout transaction hash
    • _conditions - a list of all conditions from sub-bets
    • _games - a list of all games from sub-bets
    • _subBetsCount - total number of sub-bets
    • _wonSubBetsCount - number of winning sub-bets
    • _lostSubBetsCount - number of losing sub-bets
    • _canceledSubBetsCount - number of canceled sub-bets
  • The fields have been removed:

    • conditionId - in extreme cases, you can use the service field _conditionIds
    • oracleConditionId
    • outcome - use selections
    • condition - in extreme cases, you can use the service field _conditions
    • game - use selections[].outcome.condition.game
    • gameStartsAt - use approxSettledAt

Freebet

  • The following fields have been marked as service fields with _ prefix:
    • updatedAt_updatedAt

Migration Guide

Update the bet and bets queries for GraphQL. Refer to the "Change Log" section above to see which fields have been modified.

Example:

from
query Bets {
  bets {
    ...
    odds
    conditionId
    game {
      gameId
    }
    outcome {
      outcomeId
      ...
    }
  }
}
to
query Bets {
  bets(where: { type: Ordinar }) {
    ...
    odds
    settledOdds
    selections {
      odds
      outcome {
        outcomeId
        condition {
          conditionId
          game {
            gameId
          }
        }
        ...
      }
    }
  }
}

Recommendations:

  1. To define the type of a bet, use the type field from the Bet entity, which can be either Ordinary or Express. Do not rely on the count of items in the selections field.

Deprecation Notice

🚨

We strongly recommend not delaying the migration, as Azuro will stop supporting the current version of Subgraph in the next release.

Client

Migration Guide

  1. Change the GraphQL endpoints.
  2. Change the GraphQL queries (take a look at the "Migrate Guide" from the section above).
  3. Update contract ABIs. You can find them here (opens in a new tab).

Bet on Combo Bets

minOdds = '12000000000000'
 
selections = [
  [
    '101', // conditionId
    '1' // outcomeId
  ],
  [
    '202', // conditionId
    '2' // outcomeId
  ]
]
 
data = ethers.utils.defaultAbiCoder.encode(
  [
    "tuple(uint256 conditionId, uint64 outcomeId)[]",
    "uint64"
  ],
  [
    selections,
    minOdds
  ]
)
 
// Gnosis, XDAI
betNative(coreAddress, deadline, {
  affiliate,
  data
}, { value: rawAmount })
 
// Polygon, USDT
bet(coreAddress, rawAmount, deadline, {
  affiliate,
  data
})