Tutorial
Place a First Bet

Place a First Bet

In order to place a bet, the user needs to select the outcome of a sporting event and input the amount they want to bet. To facilitate this process in the UI, we will use a modal. When the user clicks on an outcome button, a modal will open. The user can then enter the amount they want to bet, view a summary of their bet, and submit the transaction.

Create a Modal

Create a PlaceBetModal.js file in the components folder with the following content

components/PlaceBetModal.js
import dayjs from 'dayjs'
import { getMarketName } from '@azuro-org/dictionaries'
 
const GameInfo = ({ game }) => (
  <div className="py-4 px-6 rounded-b-xl shadow-xl">
    <div className="flex justify-between text-md">
      <span>{game.sport.name}</span>
      <span>{dayjs(game.startsAt * 1000).format('DD MMM HH:mm')}</span>
    </div>
    <div className="mt-1 text-md text-gray-400">
      {game.league.country.name} &middot; {game.league.name}
    </div>
    <div className="mt-3 space-y-2">
      {
        game.participants.map(({ image, name }) => (
          <div key={name} className="flex items-center">
            <div className="flex items-center justify-center w-8 h-8 mr-2 border border-gray-300 rounded-full">
              <img className="w-4 h-4" src={image} alt={name} />
            </div>
            <span className="text-md">{name}</span>
          </div>
        ))
      }
    </div>
  </div>
)
 
const OutcomeInfo = ({ outcome }) => {
  const marketName = getMarketName({ outcomeId: outcome.outcomeId })
 
  return (
    <div className="grid lg:grid-cols-[auto_1fr] gap-y-3 mt-2 text-md">
      <span className="text-gray-400">Market</span>
      <span className="text-right font-semibold">{marketName}</span>
      <span className="text-gray-400">Selection</span>
      <span className="text-right font-semibold">{outcome.selectionName}</span>
      <span className="text-gray-400">Odds</span>
      <span className="text-right font-semibold">{outcome.odds}</span>
    </div>
  )
}
 
const PlaceBetModal = ({ game, outcome, closeModal }) => {
  const amountsNode = (
    <div className="mt-4 pt-4 border-t border-gray-300 space-y-2">
      <div className="flex items-center justify-between">
        <span className="text-md text-gray-400">Wallet balance</span>
        <span className="text-md font-semibold">-</span>
      </div>
      <div className="flex items-center justify-between">
        <span className="text-md text-gray-400">Bet amount</span>
        <input
          className="w-[121px] py-2 px-4 border border-gray-400 text-md text-right font-semibold rounded-md"
          type="number"
          placeholder="Bet amount"
        />
      </div>
    </div>
  )
 
  const button = (
    <button className="button w-full mt-6 py-2.5 text-center">
      Place bet
    </button>
  )
 
  return (
    <div
      className="fixed top-0 left-0 z-50 w-full h-full flex items-center justify-center bg-black bg-opacity-20"
      onClick={closeModal}
    >
      <div
        className="w-[400px] bg-white overflow-hidden rounded-xl shadow-2xl"
        onClick={(event) => event.stopPropagation()}
      >
        <GameInfo game={game} />
        <div className="pt-4 px-6 pb-6">
          <OutcomeInfo outcome={outcome} />
          {amountsNode}
          {button}
        </div>
      </div>
    </div>
  )
}
 
export default PlaceBetModal

Add the Modal to UI

Update pages/games/[id].js

pages/games/[id].js
...
import { useState } from 'react'
import PlaceBetModal from '@/components/PlaceBetModal'
 
...
 
const Markets = ({ game, markets }) => {
  const [ selectedOutcome, setSelectedOutcome ] = useState(null)
 
  const handleOutcomeClick = (outcome) => {
    setSelectedOutcome(outcome)
  }
 
  const handleModalClose = () => {
    setSelectedOutcome(null)
  }
 
  return (
    <>
      <div className="max-w-[600px] mx-auto mt-12 space-y-6">
        {
          markets.map(({ marketName, outcomes: row }) => (
            <div key={marketName} className="">
              <div className="mb-2 font-semibold">{marketName}</div>
              <div className="space-y-1">
                {
                  row.map((outcomes, index) => (
                    <div key={index} className="flex justify-between">
                      <div className="flex gap-1 w-full">
                        {
                          outcomes.map((outcome) => (
                            <div
                              key={outcome.selectionName}
                              className="flex justify-between py-2 px-3 bg-gray-200 rounded-md cursor-pointer hover:bg-gray-300 transition"
                              style={{ width: `calc(100% / ${outcomes.length})` }}
                              onClick={() => handleOutcomeClick(outcome)}
                            >
                              <span className="text-gray-500">{outcome.selectionName}</span>
                              <span className="font-medium">{parseFloat(outcome.odds).toFixed(2)}</span>
                            </div>
                          ))
                        }
                      </div>
                    </div>
                  ))
                }
              </div>
            </div>
          ))
        }
      </div>
      {
        Boolean(selectedOutcome) && (
          <PlaceBetModal
            game={game}
            outcome={selectedOutcome}
            closeModal={handleModalClose}
          />
        )
      }
    </>
  )
}
 
export default function Game() {
  const { loading, game, markets } = useSportEvent()
 
  if (loading) {
    return <div>Loading...</div>
  }
 
  return (
    <main>
      <GameInfo {...game} />
      <Markets game={game} markets={markets} />
    </main>
  )
}

Now click on a outcome button should open the modal

Adjust the Modal with Business Logic

Create a new file called usePlaceBet.js in the hooks. Let's add the business logic step by step.

Balance and amount

hooks/usePlaceBet.js
import { useState } from 'react'
import * as ethers from 'ethers'
import { Polygon, useEthers, useTokenBalance } from '@usedapp/core'
 
const USDT_DECIMALS = 6
const USDT_ADDRESS = '0xc2132D05D31c914a87C6611C10748AEb04B58e8F'
 
export default function usePlaceBet() {
  const [ amount, setAmount ] = useState('')
  const { account, chainId } = useEthers()
 
  const isRightChain = chainId === Polygon.chainId
  const rawBalance = useTokenBalance(USDT_ADDRESS, account)
  const balance = rawBalance ? ethers.utils.formatUnits(rawBalance, USDT_DECIMALS) : '0'
 
  return {
    isRightChain,
    balance,
    amount,
    setAmount,
  }
}

Approve allowance

If you're not familiar with what a "allowance" is, we recommend reading this article (opens in a new tab).

hooks/usePlaceBet.js
import { useState } from 'react'
import * as ethers from 'ethers'
import { Polygon, useEthers, useTokenBalance, useTokenAllowance, useContractFunction, ERC20Interface } from '@usedapp/core'
 
const USDT_DECIMALS = 6
const USDT_ADDRESS = '0xc2132D05D31c914a87C6611C10748AEb04B58e8F'
const LP_ADDRESS = '0x7043E4e1c4045424858ECBCED80989FeAfC11B36'
 
const USDTContract = new ethers.Contract(USDT_ADDRESS, ERC20Interface)
 
export default function usePlaceBet() {
  ...
 
  const rawAllowance = useTokenAllowance(USDT_ADDRESS, account, LP_ADDRESS)
  const isAllowanceFetching = rawAllowance === undefined
  const allowance = rawAllowance && ethers.utils.formatUnits(rawAllowance, USDT_DECIMALS)
  const isApproveRequired = +allowance < +amount
 
  const { state: approveState, send: _approve } = useContractFunction(USDTContract, 'approve', { transactionName: 'Approve' })
  const isApproving = approveState.status === 'PendingSignature' || approveState.status === 'Mining'
 
  const approve = () => {
    // to prevent the need to ask for approval before each bet, the user will be asked to approve a "maximum" amount
    const amount = ethers.constants.MaxUint256
 
    _approve(LP_ADDRESS, amount)
  }
 
  return {
    ...
    isAllowanceFetching,
    isApproveRequired,
    approve,
    isApproving,
  }
}

Place bet

hooks/usePlaceBet.js
import { useState } from 'react'
import * as ethers from 'ethers'
import { Polygon, useEthers, useTokenBalance, useTokenAllowance, useContractFunction, ERC20Interface } from '@usedapp/core'
 
const USDT_DECIMALS = 6
const USDT_ADDRESS = '0xc2132D05D31c914a87C6611C10748AEb04B58e8F'
const LP_ADDRESS = '0x7043E4e1c4045424858ECBCED80989FeAfC11B36'
const CORE_ADDRESS = '0x3B182e9FbF50398A412d17D7969561E3BfcC4fA4'
const LP_ABI = [{"inputs":[],"name":"BetExpired","type":"error"},{"inputs":[{"internalType":"uint64","name":"waitTime","type":"uint64"}],"name":"ClaimTimeout","type":"error"},{"inputs":[],"name":"CoreNotActive","type":"error"},{"inputs":[],"name":"GameAlreadyCanceled","type":"error"},{"inputs":[],"name":"GameAlreadyCreated","type":"error"},{"inputs":[],"name":"GameCanceled_","type":"error"},{"inputs":[],"name":"GameNotExists","type":"error"},{"inputs":[],"name":"IncorrectCoreState","type":"error"},{"inputs":[],"name":"IncorrectFee","type":"error"},{"inputs":[],"name":"IncorrectGameId","type":"error"},{"inputs":[],"name":"IncorrectMinBet","type":"error"},{"inputs":[],"name":"IncorrectMinDepo","type":"error"},{"inputs":[],"name":"IncorrectPercent","type":"error"},{"inputs":[],"name":"IncorrectReinforcementAbility","type":"error"},{"inputs":[],"name":"IncorrectTimestamp","type":"error"},{"inputs":[],"name":"LeafNotExist","type":"error"},{"inputs":[],"name":"LiquidityIsLocked","type":"error"},{"inputs":[],"name":"LiquidityNotOwned","type":"error"},{"inputs":[],"name":"NoLiquidity","type":"error"},{"inputs":[],"name":"NotEnoughLiquidity","type":"error"},{"inputs":[],"name":"OnlyFactory","type":"error"},{"inputs":[{"internalType":"enum SafeCast.Type","name":"to","type":"uint8"}],"name":"SafeCastError","type":"error"},{"inputs":[],"name":"SmallBet","type":"error"},{"inputs":[],"name":"SmallDepo","type":"error"},{"inputs":[],"name":"UnknownCore","type":"error"},{"inputs":[{"internalType":"uint64","name":"waitTime","type":"uint64"}],"name":"WithdrawalTimeout","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"affiliate","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"AffiliateRewarded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"core","type":"address"},{"indexed":true,"internalType":"address","name":"bettor","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"BettorWin","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"newClaimTimeout","type":"uint64"}],"name":"ClaimTimeoutChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"core","type":"address"},{"indexed":false,"internalType":"enum ILP.CoreState","name":"state","type":"uint8"},{"indexed":false,"internalType":"uint64","name":"reinforcementAbility","type":"uint64"},{"indexed":false,"internalType":"uint128","name":"minBet","type":"uint128"}],"name":"CoreSettingsUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newDataProvider","type":"address"}],"name":"DataProviderChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"enum ILP.FeeType","name":"feeType","type":"uint8"},{"indexed":false,"internalType":"uint64","name":"fee","type":"uint64"}],"name":"FeeChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"gameId","type":"uint256"}],"name":"GameCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"gameId","type":"uint256"},{"indexed":false,"internalType":"uint64","name":"newStart","type":"uint64"}],"name":"GameShifted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"version","type":"uint8"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"uint48","name":"leaf","type":"uint48"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"LiquidityAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"uint48","name":"leaf","type":"uint48"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"LiquidityRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"core","type":"address"},{"indexed":false,"internalType":"uint128","name":"newMinBet","type":"uint128"}],"name":"MinBetChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint128","name":"newMinDepo","type":"uint128"}],"name":"MinDepoChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"gameId","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"ipfsHash","type":"bytes32"},{"indexed":false,"internalType":"uint64","name":"startsAt","type":"uint64"}],"name":"NewGame","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint128","name":"newReinforcementAbility","type":"uint128"}],"name":"ReinforcementAbilityChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"newWithdrawTimeout","type":"uint64"}],"name":"WithdrawTimeoutChanged","type":"event"},{"inputs":[],"name":"access","outputs":[{"internalType":"contract IAccess","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"gameId","type":"uint256"}],"name":"addCondition","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"core","type":"address"}],"name":"addCore","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint128","name":"amount","type":"uint128"}],"name":"addLiquidity","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"addLiquidityNative","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"gameId","type":"uint256"},{"internalType":"uint128","name":"lockedReserve","type":"uint128"},{"internalType":"uint128","name":"finalReserve","type":"uint128"},{"internalType":"uint48","name":"leaf","type":"uint48"}],"name":"addReserve","outputs":[{"internalType":"uint128","name":"affiliatesReward","type":"uint128"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"core","type":"address"},{"internalType":"uint128","name":"amount","type":"uint128"},{"internalType":"uint64","name":"expiresAt","type":"uint64"},{"components":[{"internalType":"address","name":"affiliate","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct IBet.BetData","name":"betData","type":"tuple"}],"name":"bet","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"bettor","type":"address"},{"internalType":"address","name":"core","type":"address"},{"internalType":"uint128","name":"amount","type":"uint128"},{"internalType":"uint64","name":"expiresAt","type":"uint64"},{"components":[{"internalType":"address","name":"affiliate","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct IBet.BetData","name":"betData","type":"tuple"}],"name":"betFor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"core","type":"address"},{"internalType":"uint64","name":"expiresAt","type":"uint64"},{"components":[{"internalType":"address","name":"affiliate","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct IBet.BetData","name":"betData","type":"tuple"}],"name":"betNative","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"gameId","type":"uint256"}],"name":"cancelGame","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64","name":"newClaimTimeout","type":"uint64"}],"name":"changeClaimTimeout","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newDataProvider","type":"address"}],"name":"changeDataProvider","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"enum ILP.FeeType","name":"feeType","type":"uint8"},{"internalType":"uint64","name":"newFee","type":"uint64"}],"name":"changeFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"gameId","type":"uint256"},{"internalType":"int128","name":"deltaReserve","type":"int128"}],"name":"changeLockedLiquidity","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint128","name":"newMinDepo","type":"uint128"}],"name":"changeMinDepo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64","name":"newWithdrawTimeout","type":"uint64"}],"name":"changeWithdrawTimeout","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"address","name":"target","type":"address"},{"internalType":"bytes4","name":"selector","type":"bytes4"}],"name":"checkAccess","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"core","type":"address"}],"name":"checkCore","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"checkOwner","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"core","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"claimAffiliateReward","outputs":[{"internalType":"uint256","name":"claimedAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimReward","outputs":[{"internalType":"uint256","name":"claimedAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimTimeout","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"coreAffRewards","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"cores","outputs":[{"internalType":"enum ILP.CoreState","name":"state","type":"uint8"},{"internalType":"uint64","name":"reinforcementAbility","type":"uint64"},{"internalType":"uint128","name":"minBet","type":"uint128"},{"internalType":"uint128","name":"lockedLiquidity","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"gameId","type":"uint256"},{"internalType":"bytes32","name":"ipfsHash","type":"bytes32"},{"internalType":"uint64","name":"startsAt","type":"uint64"}],"name":"createGame","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"dataProvider","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"factory","outputs":[{"internalType":"contract IOwnable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"fees","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"games","outputs":[{"internalType":"bytes32","name":"ipfsHash","type":"bytes32"},{"internalType":"uint128","name":"lockedLiquidity","type":"uint128"},{"internalType":"uint64","name":"startsAt","type":"uint64"},{"internalType":"bool","name":"canceled","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"gameId","type":"uint256"}],"name":"getGameInfo","outputs":[{"internalType":"uint64","name":"","type":"uint64"},{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLeaf","outputs":[{"internalType":"uint48","name":"leaf","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"core","type":"address"}],"name":"getLockedLiquidityLimit","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getReserve","outputs":[{"internalType":"uint128","name":"reserve","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"access_","type":"address"},{"internalType":"address","name":"dataProvider_","type":"address"},{"internalType":"address","name":"token_","type":"address"},{"internalType":"uint128","name":"minDepo_","type":"uint128"},{"internalType":"uint64","name":"daoFee","type":"uint64"},{"internalType":"uint64","name":"dataProviderFee","type":"uint64"},{"internalType":"uint64","name":"affiliateFee","type":"uint64"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"gameId","type":"uint256"}],"name":"isGameCanceled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lockedLiquidity","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minDepo","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nextNode","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint48","name":"leaf","type":"uint48"}],"name":"nodeWithdrawView","outputs":[{"internalType":"uint128","name":"withdrawAmount","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"rewards","outputs":[{"internalType":"int128","name":"amount","type":"int128"},{"internalType":"uint64","name":"claimedAt","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"gameId","type":"uint256"},{"internalType":"uint64","name":"startsAt","type":"uint64"}],"name":"shiftGame","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"token","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"tokenByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"tokenOfOwnerByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint48","name":"","type":"uint48"}],"name":"treeNode","outputs":[{"internalType":"uint64","name":"updateId","type":"uint64"},{"internalType":"uint128","name":"amount","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"core","type":"address"},{"internalType":"enum ILP.CoreState","name":"state","type":"uint8"},{"internalType":"uint64","name":"reinforcementAbility","type":"uint64"},{"internalType":"uint128","name":"minBet","type":"uint128"}],"name":"updateCoreSettings","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"updateId","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"core","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"viewPayout","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint48","name":"","type":"uint48"}],"name":"withdrawAfter","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint48","name":"depNum","type":"uint48"},{"internalType":"uint40","name":"percent","type":"uint40"},{"internalType":"bool","name":"isNative","type":"bool"}],"name":"withdrawLiquidity","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"core","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bool","name":"isNative","type":"bool"}],"name":"withdrawPayout","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawTimeout","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]
 
const USDTContract = new ethers.Contract(USDT_ADDRESS, ERC20Interface)
const lpContract = new ethers.Contract(LP_ADDRESS, LP_ABI)
 
export default function usePlaceBet({ outcome, onBetPlace }) {
  ...
 
  const { send: _placeBet } = useContractFunction(lpContract, 'bet', { transactionName: 'Bet' })
 
  const placeBet = () => {
    const { conditionId, outcomeId, odds } = outcome
 
    const slippage = 5 // 5%
    const minOdds = 1 + (odds - 1) * (100 - slippage) / 100 // the minimum value at which a bet should be made
    const oddsDecimals = 12 // current protocol version odds has 12 decimals
    const rawMinOdds = ethers.utils.parseUnits(minOdds.toFixed(oddsDecimals), oddsDecimals)
    const rawAmount = ethers.utils.parseUnits(amount, USDT_DECIMALS)
    const deadline = Math.floor(Date.now() / 1000) + 2000 // the time (in seconds) within which the transaction should be submitted
    const affiliate = "0x0000000000000000000000000000000000000000" // your affiliate wallet address
 
    const data = ethers.utils.defaultAbiCoder.encode(
      [ 'uint256', 'uint64', 'uint64' ],
      [ conditionId, outcomeId, rawMinOdds ]
    )
 
    _placeBet(CORE_ADDRESS, rawAmount, deadline, {
      affiliate,
      data,
    })
 
    onBetPlace()
  }
 
  return {
    ...
    placeBet,
  }
}

Here's what the final code for the file should look like

hooks/usePlaceBet.js
import { useState } from 'react'
import * as ethers from 'ethers'
import { Polygon, useEthers, useTokenBalance, useTokenAllowance, useContractFunction, ERC20Interface } from '@usedapp/core'
 
const USDT_DECIMALS = 6
const USDT_ADDRESS = '0xc2132D05D31c914a87C6611C10748AEb04B58e8F'
const LP_ADDRESS = '0x7043E4e1c4045424858ECBCED80989FeAfC11B36'
const CORE_ADDRESS = '0x3B182e9FbF50398A412d17D7969561E3BfcC4fA4'
 
const LP_ABI = [{"inputs":[],"name":"BetExpired","type":"error"},{"inputs":[{"internalType":"uint64","name":"waitTime","type":"uint64"}],"name":"ClaimTimeout","type":"error"},{"inputs":[],"name":"CoreNotActive","type":"error"},{"inputs":[],"name":"GameAlreadyCanceled","type":"error"},{"inputs":[],"name":"GameAlreadyCreated","type":"error"},{"inputs":[],"name":"GameCanceled_","type":"error"},{"inputs":[],"name":"GameNotExists","type":"error"},{"inputs":[],"name":"IncorrectCoreState","type":"error"},{"inputs":[],"name":"IncorrectFee","type":"error"},{"inputs":[],"name":"IncorrectGameId","type":"error"},{"inputs":[],"name":"IncorrectMinBet","type":"error"},{"inputs":[],"name":"IncorrectMinDepo","type":"error"},{"inputs":[],"name":"IncorrectPercent","type":"error"},{"inputs":[],"name":"IncorrectReinforcementAbility","type":"error"},{"inputs":[],"name":"IncorrectTimestamp","type":"error"},{"inputs":[],"name":"LeafNotExist","type":"error"},{"inputs":[],"name":"LiquidityIsLocked","type":"error"},{"inputs":[],"name":"LiquidityNotOwned","type":"error"},{"inputs":[],"name":"NoLiquidity","type":"error"},{"inputs":[],"name":"NotEnoughLiquidity","type":"error"},{"inputs":[],"name":"OnlyFactory","type":"error"},{"inputs":[{"internalType":"enum SafeCast.Type","name":"to","type":"uint8"}],"name":"SafeCastError","type":"error"},{"inputs":[],"name":"SmallBet","type":"error"},{"inputs":[],"name":"SmallDepo","type":"error"},{"inputs":[],"name":"UnknownCore","type":"error"},{"inputs":[{"internalType":"uint64","name":"waitTime","type":"uint64"}],"name":"WithdrawalTimeout","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"affiliate","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"AffiliateRewarded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"core","type":"address"},{"indexed":true,"internalType":"address","name":"bettor","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"BettorWin","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"newClaimTimeout","type":"uint64"}],"name":"ClaimTimeoutChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"core","type":"address"},{"indexed":false,"internalType":"enum ILP.CoreState","name":"state","type":"uint8"},{"indexed":false,"internalType":"uint64","name":"reinforcementAbility","type":"uint64"},{"indexed":false,"internalType":"uint128","name":"minBet","type":"uint128"}],"name":"CoreSettingsUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newDataProvider","type":"address"}],"name":"DataProviderChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"enum ILP.FeeType","name":"feeType","type":"uint8"},{"indexed":false,"internalType":"uint64","name":"fee","type":"uint64"}],"name":"FeeChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"gameId","type":"uint256"}],"name":"GameCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"gameId","type":"uint256"},{"indexed":false,"internalType":"uint64","name":"newStart","type":"uint64"}],"name":"GameShifted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"version","type":"uint8"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"uint48","name":"leaf","type":"uint48"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"LiquidityAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"uint48","name":"leaf","type":"uint48"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"LiquidityRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"core","type":"address"},{"indexed":false,"internalType":"uint128","name":"newMinBet","type":"uint128"}],"name":"MinBetChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint128","name":"newMinDepo","type":"uint128"}],"name":"MinDepoChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"gameId","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"ipfsHash","type":"bytes32"},{"indexed":false,"internalType":"uint64","name":"startsAt","type":"uint64"}],"name":"NewGame","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint128","name":"newReinforcementAbility","type":"uint128"}],"name":"ReinforcementAbilityChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"newWithdrawTimeout","type":"uint64"}],"name":"WithdrawTimeoutChanged","type":"event"},{"inputs":[],"name":"access","outputs":[{"internalType":"contract IAccess","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"gameId","type":"uint256"}],"name":"addCondition","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"core","type":"address"}],"name":"addCore","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint128","name":"amount","type":"uint128"}],"name":"addLiquidity","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"addLiquidityNative","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"gameId","type":"uint256"},{"internalType":"uint128","name":"lockedReserve","type":"uint128"},{"internalType":"uint128","name":"finalReserve","type":"uint128"},{"internalType":"uint48","name":"leaf","type":"uint48"}],"name":"addReserve","outputs":[{"internalType":"uint128","name":"affiliatesReward","type":"uint128"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"core","type":"address"},{"internalType":"uint128","name":"amount","type":"uint128"},{"internalType":"uint64","name":"expiresAt","type":"uint64"},{"components":[{"internalType":"address","name":"affiliate","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct IBet.BetData","name":"betData","type":"tuple"}],"name":"bet","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"bettor","type":"address"},{"internalType":"address","name":"core","type":"address"},{"internalType":"uint128","name":"amount","type":"uint128"},{"internalType":"uint64","name":"expiresAt","type":"uint64"},{"components":[{"internalType":"address","name":"affiliate","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct IBet.BetData","name":"betData","type":"tuple"}],"name":"betFor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"core","type":"address"},{"internalType":"uint64","name":"expiresAt","type":"uint64"},{"components":[{"internalType":"address","name":"affiliate","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct IBet.BetData","name":"betData","type":"tuple"}],"name":"betNative","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"gameId","type":"uint256"}],"name":"cancelGame","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64","name":"newClaimTimeout","type":"uint64"}],"name":"changeClaimTimeout","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newDataProvider","type":"address"}],"name":"changeDataProvider","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"enum ILP.FeeType","name":"feeType","type":"uint8"},{"internalType":"uint64","name":"newFee","type":"uint64"}],"name":"changeFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"gameId","type":"uint256"},{"internalType":"int128","name":"deltaReserve","type":"int128"}],"name":"changeLockedLiquidity","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint128","name":"newMinDepo","type":"uint128"}],"name":"changeMinDepo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64","name":"newWithdrawTimeout","type":"uint64"}],"name":"changeWithdrawTimeout","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"address","name":"target","type":"address"},{"internalType":"bytes4","name":"selector","type":"bytes4"}],"name":"checkAccess","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"core","type":"address"}],"name":"checkCore","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"checkOwner","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"core","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"claimAffiliateReward","outputs":[{"internalType":"uint256","name":"claimedAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimReward","outputs":[{"internalType":"uint256","name":"claimedAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimTimeout","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"coreAffRewards","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"cores","outputs":[{"internalType":"enum ILP.CoreState","name":"state","type":"uint8"},{"internalType":"uint64","name":"reinforcementAbility","type":"uint64"},{"internalType":"uint128","name":"minBet","type":"uint128"},{"internalType":"uint128","name":"lockedLiquidity","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"gameId","type":"uint256"},{"internalType":"bytes32","name":"ipfsHash","type":"bytes32"},{"internalType":"uint64","name":"startsAt","type":"uint64"}],"name":"createGame","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"dataProvider","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"factory","outputs":[{"internalType":"contract IOwnable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"fees","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"games","outputs":[{"internalType":"bytes32","name":"ipfsHash","type":"bytes32"},{"internalType":"uint128","name":"lockedLiquidity","type":"uint128"},{"internalType":"uint64","name":"startsAt","type":"uint64"},{"internalType":"bool","name":"canceled","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"gameId","type":"uint256"}],"name":"getGameInfo","outputs":[{"internalType":"uint64","name":"","type":"uint64"},{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLeaf","outputs":[{"internalType":"uint48","name":"leaf","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"core","type":"address"}],"name":"getLockedLiquidityLimit","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getReserve","outputs":[{"internalType":"uint128","name":"reserve","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"access_","type":"address"},{"internalType":"address","name":"dataProvider_","type":"address"},{"internalType":"address","name":"token_","type":"address"},{"internalType":"uint128","name":"minDepo_","type":"uint128"},{"internalType":"uint64","name":"daoFee","type":"uint64"},{"internalType":"uint64","name":"dataProviderFee","type":"uint64"},{"internalType":"uint64","name":"affiliateFee","type":"uint64"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"gameId","type":"uint256"}],"name":"isGameCanceled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lockedLiquidity","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minDepo","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nextNode","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint48","name":"leaf","type":"uint48"}],"name":"nodeWithdrawView","outputs":[{"internalType":"uint128","name":"withdrawAmount","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"rewards","outputs":[{"internalType":"int128","name":"amount","type":"int128"},{"internalType":"uint64","name":"claimedAt","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"gameId","type":"uint256"},{"internalType":"uint64","name":"startsAt","type":"uint64"}],"name":"shiftGame","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"token","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"tokenByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"tokenOfOwnerByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint48","name":"","type":"uint48"}],"name":"treeNode","outputs":[{"internalType":"uint64","name":"updateId","type":"uint64"},{"internalType":"uint128","name":"amount","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"core","type":"address"},{"internalType":"enum ILP.CoreState","name":"state","type":"uint8"},{"internalType":"uint64","name":"reinforcementAbility","type":"uint64"},{"internalType":"uint128","name":"minBet","type":"uint128"}],"name":"updateCoreSettings","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"updateId","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"core","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"viewPayout","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint48","name":"","type":"uint48"}],"name":"withdrawAfter","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint48","name":"depNum","type":"uint48"},{"internalType":"uint40","name":"percent","type":"uint40"},{"internalType":"bool","name":"isNative","type":"bool"}],"name":"withdrawLiquidity","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"core","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bool","name":"isNative","type":"bool"}],"name":"withdrawPayout","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawTimeout","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]
 
const USDTContract = new ethers.Contract(USDT_ADDRESS, ERC20Interface)
const lpContract = new ethers.Contract(LP_ADDRESS, LP_ABI)
 
export default function usePlaceBet({ outcome, onBetPlace }) {
  const [ amount, setAmount ] = useState('')
  const { account, chainId } = useEthers()
 
  const isRightChain = chainId === Polygon.chainId
  const rawBalance = useTokenBalance(USDT_ADDRESS, account)
  const balance = rawBalance ? ethers.utils.formatUnits(rawBalance, USDT_DECIMALS) : '0'
 
  const rawAllowance = useTokenAllowance(USDT_ADDRESS, account, LP_ADDRESS)
  const isAllowanceFetching = rawAllowance === undefined
  const allowance = rawAllowance && ethers.utils.formatUnits(rawAllowance, USDT_DECIMALS)
  const isApproveRequired = +allowance < +amount
 
  const { state: approveState, send: _approve } = useContractFunction(USDTContract, 'approve', { transactionName: 'Approve' })
  const isApproving = approveState.status === 'PendingSignature' || approveState.status === 'Mining'
 
  const approve = () => {
    // to prevent the need to ask for approval before each bet, the user will be asked to approve a "maximum" amount
    const amount = ethers.constants.MaxUint256
 
    _approve(LP_ADDRESS, amount)
  }
 
  const { send: _placeBet } = useContractFunction(lpContract, 'bet', { transactionName: 'Bet' })
 
  const placeBet = () => {
    const { conditionId, outcomeId, odds } = outcome
 
    const slippage = 5 // 5%
    const minOdds = 1 + (odds - 1) * (100 - slippage) / 100 // the minimum value at which a bet should be made
    const oddsDecimals = 12 // current protocol version odds has 12 decimals
    const rawMinOdds = ethers.utils.parseUnits(minOdds.toFixed(oddsDecimals), oddsDecimals)
 
    const amountDecimals = 6 // USDT decimals
    const rawAmount = ethers.utils.parseUnits(amount, amountDecimals)
 
    const deadline = Math.floor(Date.now() / 1000) + 2000 // the time (in seconds) within which the transaction should be submitted
    const affiliate = "0x0000000000000000000000000000000000000000" // your affiliate wallet address
 
    const data = ethers.utils.defaultAbiCoder.encode(
      [ 'uint256', 'uint64', 'uint64' ],
      [ conditionId, outcomeId, rawMinOdds ]
    )
 
    _placeBet(CORE_ADDRESS, rawAmount, deadline, {
      affiliate,
      data,
    })
 
    onBetPlace()
  }
 
  return {
    isRightChain,
    balance,
    amount,
    setAmount,
    isAllowanceFetching,
    isApproveRequired,
    approve,
    isApproving,
    placeBet,
  }
}

Use the hook inside the PlaceBetModal.js

components/PlaceBetModal.js
...
import usePlaceBet from '@/hooks/usePlaceBet'
 
...
 
const PlaceBetModal = ({ game, outcome, closeModal }) => {
  const {
    isRightChain,
    balance,
    amount,
    setAmount,
    isAllowanceFetching,
    isApproveRequired,
    approve,
    isApproving,
    placeBet,
  } = usePlaceBet({ outcome, onBetPlace: closeModal })
 
  const amountsNode = (
    <div className="mt-4 pt-4 border-t border-gray-300 space-y-2">
      <div className="flex items-center justify-between">
        <span className="text-md text-gray-400">Wallet balance</span>
        <span className="text-md font-semibold">{balance} USDT</span>
      </div>
      <div className="flex items-center justify-between">
        <span className="text-md text-gray-400">Bet amount</span>
        <input
          className="w-[121px] py-2 px-4 border border-gray-400 text-md text-right font-semibold rounded-md"
          type="number"
          placeholder="Bet amount"
          disabled={isApproving}
          value={amount}
          onChange={(event) => setAmount(event.target.value)}
        />
      </div>
    </div>
  )
 
  const button = !isRightChain ? (
    <div className="mt-6 py-2.5 text-center bg-red-200 rounded-md">
      Switch network in your wallet to <b>Polygon</b>
    </div>
  ) : (
    <button
      className="button w-full mt-6 py-2.5 text-center"
      disabled={isAllowanceFetching || isApproving}
      onClick={isApproveRequired ? approve : placeBet}
    >
      {
        isApproveRequired ? (
          isApproving ? 'Approving...' : 'Approve'
        ) : (
          'Place bet'
        )
      }
    </button>
  )
 
  ...
}