Developer Hub
🔮 For applications
Guides & Tutorials
Advanced integration
Prematch
Place a Bet

Place a Bet

Place a single bet

All LP works with ERC20 tokens as a bet token

import { ethers } from 'ethers'
 
const data = ethers.utils.defaultAbiCoder.encode(
  [ 'tuple(uint256, uint64)' ],
  [ { conditionId, outcomeId } ]
)
 
lp.bet(
  prematchCoreAddress,
  rawAmount,
  deadline,
  {
    affiliate: '0x...', // Your wallet address to earn rewards
    data,
    minOdds: rawMinOdds,
  }
)
⚠️

Note that the placing bets in ERC20 tokens requires users to make an approve of tokens spending.

Place bet using native token

🚫

Deprecated. Liquidity Pool in Gnosis chain works on WXDAI (wrapped ERC20 version of native token). To place bets in Gnosis chain suggest customers to wrap and unwrap tokens.

Wrap/unwrap WXDAI

// wagmi/viem example
 
txDto.to = '0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d'
 
// XDAI -> WXDAI
if (isWrap) {
  txDto.value = rawAmount
  txDto.data = encodeFunctionData({
    abi: erc20ABI,
    functionName: 'deposit',
  })
}
// WXDAI -> XDAI
else {
  txDto.data = encodeFunctionData({
    abi: erc20ABI,
    functionName: 'withdraw',
    args: [ rawAmount ],
  })
}
 
sendTransaction(txDto)

Place a batch of single bets

To send a batch of single bets you should use ProxyFront contract.

ℹ️

Note that data structure differs from the structure of placing single bet on LP contract.

import { ethers } from 'ethers'
 
const data = ethers.utils.defaultAbiCoder.encode(
  [ 'tuple(uint256, uint64)' ],
  [ { conditionId, outcomeId } ]
)
 
const betData1 = {
  core: prematchCoreAddress,
  amount: rawAmount,
  expiresAt: deadline,
  extraData: {
    affiliate: '0x...', // Your wallet address to earn rewards
    data,
    minOdds: rawMinOdds,
  },
}
 
const betData2 = {...}
 
proxyFront.bet(
  lpAddress,
  [
    betData1,
    betData2,
  ],
)

Place a combo bet

To place a combo bet with several selections you can use any LP or ProxyFront contract. The difference with single bet is just in data field (array instead of single tuple) and core address.

import { ethers } from 'ethers'
 
const selections: { conditionId: string, outcomeId: string }[]
 
const data = ethers.utils.defaultAbiCoder.encode(
  [ 'tuple(uint256, uint64)[]' ],
  [ selections ]
)
 
lp.bet(
  prematchCoreAddress,
  rawAmount,
  deadline,
  {
    affiliate: '0x...', // Your wallet address to earn rewards
    data,
    minOdds: rawMinOdds,
  }
)
 
/* =============================== */
 
// OR via ProxyFrontContract
const betData = {
  core: expressCoreAddress,
  amount: rawAmount,
  expiresAt: deadline,
  extraData: {
    affiliate: '0x...', // Your wallet address to earn rewards
    data,
    minOdds: rawMinOdds,
  },
}
 
proxyFront.bet(
  lpAddress,
  [
    betData,
    // yes it's also possible to place a batch of combo bets 🙈
  ],
)
⚠️

Some conditions have restrictions and cannot be used in a combo bet, for example, eSports conditions. To avoid this error you need check the isExpressForbidden field in condition subgraph data, if this field is true than you should forbid such combo bet and show warning notification in your UI.

Place a freebet

If you're using freebets feature, then customer may place a bet using available freebet. See details on structure and how to fetch in Use Freebets section.

⚠️

To apply freebet, outcome.rawMinOdds should be greater than freebet.minOdds. The bet should be single - freebets aren't applicable to combo bets.

⚠️

Please note that we provide you with the flexibility to distribute Freebets without a budget limitation. However, if your customers claim Freebets for an amount exceeding the freebet contract balance, they will encounter an error during the smart contract execution call.

import { ethers } from 'ethers'
 
if (BigInt(outcome.rawMinOdds) <  BigInt(freebet.minOdds)) {
  // outcome minOdds should be greater than freebet.minOdds
  return
}
 
const data = ethers.utils.defaultAbiCoder.encode(
  [ 'tuple(uint256, uint64)' ],
  [ { conditionId, outcomeId } ]
)
 
const freebetContract = new Contract(freebet.contract.freebetContractAddress, freebetABI)
 
freebetContract.bet(
  {
    chainId: freebet.contract.chainId,
    expiresAt: freebet.expiresAt,
    amount: freebet.amount,
    freeBetId: freebet.id,
    minOdds: freebet.minOdds,
    owner: account,
  },
  freebet.signature,
  coreAddress,
  conditionId,
  outcomeId,
  deadline,
  outcome.rawMinOdds
)

Highlights

conditionId & outcomeId

To clarify, conditionId and outcomeId values should be taken from the specific outcome that the user is placing their bet on within the game. These ids can be obtained by iterating through the outcomes array of the Game object and finding the relevant outcome that the user has selected.

Additionally, the prematchCoreAddress value should be the address of the Core contract that is associated with the LP contract that the user is betting on. Each condition entity has prematchCoreAddress field containing this value.

rawMinOdds

The minOdds parameter is used to specify the minimum odds value at which the bet can be placed. If the current odds value is lower than the specified value, the bet cannot be placed and will be rejected by the contract.

To calculate the minimum odds, you can use the slippage factor. The slippage factor is the percentage by which the current odds can change in a smaller direction.

For example, if the current odds for an outcome is 2, and the user sets a slippage factor of 5%, then the minimum odds that the user will accept is 1.9 (2 * 0.95). This ensures that the user's bet is not accepted if the odds fall below the minimum acceptable value.

The slippage factor can be specified by the user in the interface and then passed as a parameter to the bet method.

const currentOdds = 1.5 // the odds value taken from contracts in the moment of bet placing
const slippage = 5 // 5%
const minOdds = 1 + (currentOdds - 1) * (100 - slippage) / 100

The betting methods accept BigNumberish value for minOdds.

const oddsDecimals = 12 // in current version of protocol odds has 12 decimals
const rawMinOdds = parseUnits(minOdds.toFixed(oddsDecimals), oddsDecimals)

deadline

This parameter specifies the deadline by which the transaction must be included in a block. If the transaction is not included in a block before the deadline, the transaction will revert. The value of this parameter should be passed in seconds.

const currentTime = Math.floor(Date.now() / 1000)
const deadline = currentTime + 2000 // the bet should be accepted within the next 2000 seconds

affiliate

This is the wallet address where the rewards will be distributed. Please be aware that the accumulation of rewards is predicated on them being associated with this specific wallet. To receive your rewards directly into this wallet, you must redeem them accordingly. Read about rewards distribution here.

Recommendations

It's important to ensure that the user has sufficient funds in their account before placing a bet, and to handle errors from contracts gracefully if the bet cannot be placed due to insufficient funds or other reasons.

Keeping the odds updated is essential for providing the best user experience to your customers. By subscribing to odds updates, you can keep the odds values up-to-date in real-time, which can help reduce the risk of user frustration or missed betting opportunities. Alternatively, you can fetch the odds values before a user submits a bet to ensure that the odds values are current at the time of the bet placement.