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
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} · {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
...
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
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).
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
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
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
...
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>
)
...
}