betBlox developers guide
Welcome to the developer documentation for betBlox the decentralized betting infrastucture protocol. betBlox allows anyone to build and run decentralised non-custodial onchain betting dapps and services This guide will help you understand the architecture, interact with the smart contracts, and build applications on top of the betBlox protocol. Additionally the guide covers the use of betBase community services like the betBase API, betBase oracle, betBlox subgraph and the bloxTester dev test tool.
Introduction
betBlox is a decentralized protocol designed to facilitate decentralized fully onchain non-custodial peer-to-peer betting markets. It uses EVM smart contracts to ensure transparency, security, and trustless execution of bets and markets. The protocol implements a virtual Onchain Betting Market (OBM) that allows users to place bets onchain or to become market makers and create markets for other bettors.
Protocol Architecture
The betBlox protocol is built on a modular architecture, separating concerns into distinct contracts for better maintainability and upgradability. The first version of betBlox implements greenBlox for Sports betting and simple predictions. Future versions will implement blue, purple and orangeBlox for a complete suite of betting products.
Core Components
BetHandler: The primary entry point for placing bets. It validates bet parameters and interacts with the MarketHandler to record bets.
MarketHandler: Manages the lifecycle of betting markets. It handles liquidity, odds, and settlement logic.
EventHandler: Acts as the source of truth for event data (e.g., sports match results). It tracks the state of events (Active, Playing, Completed) and ensures markets are settled correctly based on these states.
MarketmakerBox (lite): The vault system where market makers deposit control their liquidity and earn fees.
ParlayHandler: A specialized handler for parlay bets, coordinating across multiple markets and events.
LockBox: The foundational contract for locking bets and ensuring secure deposit and withdrawal of funds.
Interaction Flow
Create a MarketMaker: Create a MarketMakerBox via the MarketMakerBoxFactory.
Create a Market: A Market Maker creates a market via
MarketHandler, specifying the event and odds. Liquidity is reserved from theirMarketmakerBox.Place a Bet: A user calls
addBetonBetHandler. The contract verifies the market and odds, transfers the user's stake, and records the bet.Resolve an Event: An Oracle or authorized entity updates the event state in
EventHandlerto "Completed" and provides the result.Settle a Market:
MarketHandleruses the event result to determine winning outcomes. Note: Settlement is a permissionless action; once the event is completed, anyone (Market Maker, Bettor, or a third party) can callsettleMarketto finalize the market.Claim Payouts: Winners can claim their payouts via
payoutBetinBetHandler, which releases the funds from thebet-lockbox(LockBox) and the locked stakes.
Smart Contract Reference
This section provides a detailed reference for the core smart contracts in the betBlox protocol.
BetHandler.sol
Introduction: This contract handles the betting logic. It communicates with the MarketHandler to add bets to markets and manages the lifecycle of a bet.
External Functions:
setBetAgent(address inBetAgent): Sets a bet agent for the caller, allowing the agent to place bets on their behalf.addBet(bytes32 marketHash, uint256 betAmount, uint256 odds, uint16 outcomeId): Adds a regular bet to a market on behalf of the caller.agentBet(bytes32 marketHash, address bettor, uint256 betAmount, uint256 odds, uint16 outcomeId): Adds a bet to a market on behalf of another user (requires being their agent).payoutBet(bytes32 betHash): Pays out a winning bet to the bettor. Settles the market first if necessary.payoutBets(bytes32[] calldata betHashes): Pays out a list of winning bets. Efficient for batch processing.
View Only Functions:
token(): Returns the address of the betting token.tokenDecimals(): Returns the number of precision decimals in the betting token.
EventHandler.sol
Introduction: This contract manages events, including storing event data, tracking states (Active, Playing, Completed), and firing chain events. It acts as a central registry for events.
External Functions:
checkOrAddEvent(bytes32 eventHash, uint32 marketType, address marketmaker): Checks if an event exists for a new market, or adds it if it doesn't.addEvent(bytes32 eventHash, uint32 eventType): Adds a marketmaker-controlled event.setEventActive(bytes32 eventHash, uint32 eventType): Updates an event to the Active state (called by Oracle).setEventPlaying(bytes32 eventHash): Updates an event to the Playing state.completeEvent(bytes32 eventHash, EventResult calldata result): Updates an event to the Completed state and records the result.cancelEvent(bytes32 eventHash): Cancels an event.invalidateEvent(bytes32 eventHash): Invalidates an event.
View Only Functions:
getEventData(bytes32 eventHash): Returns the full data of an event.getEventState(bytes32 eventHash): Returns the current state of an event.getEventResult(bytes32 eventHash): Returns the result of a completed event.isOracleEvent(bytes32 eventHash): Checks if an event is controlled by an Oracle.owner(bytes32 eventHash): Returns the owner of an event.isValidMarket(uint32 inEventType, uint32 inMarketType): Checks if a market type is valid for a given event type.isValidResult(uint32 inEventType, EventResult calldata inResult): Checks if a result is valid for a given event type.assertEventIsActive(bytes32 eventHash): Reverts if the event is not in the Active state.assertEventIsActiveOrPlaying(bytes32 eventHash): Reverts if the event is not Active or Playing.
MarketHandler.sol
Introduction: This contract handles markets, including storing market data, managing liquidity, and processing settlements. It interacts with EventHandler and BetHandler.
External Functions:
addMarket(...): Adds a new market with liquidity, odds, and other parameters.addBet(BetData calldata bet): Adds a bet to a market (restricted toBETS_ROLE, i.e.,BetHandler).addLiquidity(bytes32 marketHash, uint256 inAmount): Adds additional liquidity to an existing market.removeLiquidity(bytes32 marketHash, uint256 inAmount): Removes unused liquidity from a market.setOdds(bytes32 marketHash, uint16 outcomeId, uint256 inOdds): Updates the odds for a specific outcome in a market.settleMarket(bytes32 marketHash): Settles a market to prepare it for payouts.payoutAll(BetSettleData[] calldata bets): Pays the marketmaker and all listed bettors (restricted toBETS_ROLE).payoutMarketmaker(bytes32 marketHash): Pays the marketmaker of a market.payoutBettor(BetSettleData calldata bet): Pays a specific bettor (restricted toBETS_ROLE).setVIPGroup(bytes32 marketHash, uint8 inGroup): Changes the VIP group access for a market.
View Only Functions:
getMarketData(bytes32 marketHash): Returns the full data of a market.getEventState(bytes32 marketHash): Returns the event state associated with a market.getOwner(bytes32 marketHash): Returns the owner of a market.hasOutcome(bytes32 marketHash, uint16 outcomeId): Checks if a specific outcome exists in a market.getOutcome(bytes32 marketHash, uint16 outcomeId): Returns data for a specific outcome.getProcessor(uint32 id): Returns the processor contract address for a market type.isValidOutcome(uint32 inMarketType, uint16 inOutcomeId): Checks if an outcome ID is valid for a market type.getWinningOutcome(...): Determines the winning outcome based on the event result.isValidOffset(uint32 inMarketType, int32 inOffset): Checks if an offset value is valid for a market type.getValidOutcomes(uint32 inMarketType): Returns all valid outcome IDs for a market type.
LockBox.sol
Introduction: Simple contract for storing tokens in a locked state. It keeps a list of locked tokens and can own the locked tokens. It inherits from LockBoxAbstract and TokenValidator.
External Functions:
lockAmount(address owner, address token, uint256 amount): Increases the users locked amount for a token. (Restricted toLOCKBOX_ROLE)unlockAmount(address owner, address token, uint256 amount): Decreases the users locked amount. (Restricted toLOCKBOX_ROLE)unlockAmountTo(address owner, address to, address token, uint256 amount): Decreases an owners locked amount and sets allowance to another address. (Restricted toLOCKBOX_ROLE)transferLockAmount(address owner, address receiver, address token, uint256 amount): Transfers a lock amount from one owner to another. (Restricted toLOCKBOX_ROLE)
View Only Functions:
getLockedAmount(address owner, address token): Returns the amount currently locked for a user.hasLockedAmount(address owner, address token, uint256 amount): Checks if a user has at least the specified amount locked.
MarketmakerBoxLite.sol
Introduction: A lightweight vault contract where marketmakers deposit tokens. It inherits from LockBoxAbstract to provide secure fund management without exposing raw lock/unlock functionality, ensuring all fund movements are accounted for by the protocol.
External Functions:
deposit(uint256 inAmount): Deposits tokens into the box.withdraw(uint256 inAmount): Withdraws tokens from the box to the owner.withdrawMax(): Withdraws all available (unlocked) tokens.reserveForMarket(uint256 inAmount): Reserves tokens for a market (restricted toMARKETS_ROLE).returnToOwner(uint256 inAmount): Returns tokens from a market back to the owner (restricted toMARKETS_ROLE).grantVIP(address inAddress, uint8 inGroup): Grants a VIP role to an address.revokeVIP(address inAddress, uint8 inGroup): Revokes a VIP role.grantSigner(address inAddress): Grants a signer role.revokeSigner(address inAddress): Revokes a signer role.
View Only Functions:
isVIP(address inAddress, uint8 inGroup): Checks if an address has a specific VIP role.getOwner(): Returns the owner of the box.getLockedAmount(address owner, address token): Returns the amount currently locked for a user.hasLockedAmount(address owner, address token, uint256 amount): Checks if a user has at least the specified amount locked.
MarketmakerFactory.sol
Introduction: A factory contract for deploying MarketmakerBoxLite contracts. It ensures that new boxes are correctly initialized and registered.
External Functions:
createMarketmakerBox(): Creates and deploys a newMarketmakerBoxLitefor the caller.createMarketmakerBoxForUser(address inOwner): Creates a box for a specific user (admin only).
View Only Functions:
getMarketmakerList(): Returns a list of all created marketmaker boxes.isMarketmaker(address inMarketmaker): Checks if an address has created a marketmaker box.
ParlayHandler.sol
Introduction: COMING SOON! Not deployed in current version of betBlox. This contract will handle parlay bets, which are combined bets on multiple outcomes. It manages the creation, settlement, and payout of parlays. Like MarketHandler, it relies on EventHandler for retrieving event status and results.
External Functions:
setMaxOutcomes(uint8): Sets the max number outcomes that can be combined when a bettor makes a parlay bet. (Restricted toOPERATION_ADMIN_ROLE).setParlayAgent(address): Assigns an agent that is allowed to parlay bet for the bettor making the call.addParlay(ParlayInput calldata, bytes calldata): The regular way to create a parlay bet.agentParlay(ParlayInput calldata, bytes calldata): Adding a parlay bet as agent, on behalf of another bettor.settleParlay(bytes32): Settles a parlay bet, when all corresponding events are settled. There are only 2 accounts involved in a parlay bet, an MM and a bettor, and both are automatically paid from the settle.
View Only Functions:
getMaxOutcomes(): Returns the max allowed outcomes to combine.getParlayData(bytes32): Returns the full data of a parlay.getEventStates(bytes32): Returns a list of the event states, one for each outcome in the parlay bet.
AccessHandler.sol
Introduction: Not deployed as a separate contract, but inherited by all other contracts. This contract provides functionalty to restrict function calls by assigned roles and to Pause transactions entirely.
External Functions:
addAdmin(address): Add another super-admin. (Restricted toDEFAULT_ADMIN_ROLE).removeAdmin(address): Remove a super-admin. (Restricted toDEFAULT_ADMIN_ROLE).grantRole(bytes32 role, address account): Grantroletoaccount. (Restricted toDEFAULT_ADMIN_ROLE).revokeRole(bytes32 role, address account): Revokeroleforaccount. (Restricted toDEFAULT_ADMIN_ROLE).renounceRole(bytes32 role, address callerConfirmation): Lets a user revoke one of their own roles.pause(): Pauses the contract. (Restricted toPAUSER_ROLE).unpause(): Unpauses the contract. (Restricted toPAUSER_ROLE).
View Only Functions:
getRoleAdmin(bytes32 role): Returns the admin role that controlsrole(super-admin).hasRole(bytes32 role, address account): Returnstrueifaccountis assignedrole.paused(): Returnstrueif the contract is paused.
Role Access Control
The betBlox protocol uses a robust role-based access control system to secure sensitive operations. Roles are identified by calculating a keccak256 hash of the roles name.
System Roles
DEFAULT_ADMIN_ROLE: The super-admin role. Admins can grant and revoke other roles, including adding other admins. This particular role hash is always the zero hex hash.
OPERATION_ADMIN_ROLE: Allows for operational tasks, such as creating
MarketmakerBoxcontracts for users via the factory.PAUSER_ROLE: Grants the ability to pause and unpause contracts. This is an emergency control mechanism to stop protocol activity in case of a critical issue.
Functional Roles
MARKETS_ROLE: Assigned to
MarketHandlerand comingParlayHandler. This role allows these contracts to reserve and return tokens in aMarketmakerBoxwhen markets are created or settled.BETS_ROLE: Assigned to
BetHandler. This role authorizes the contract to call sensitive functions inMarketHandler, such asaddBetandpayoutBettor.ORACLE_ROLE: Assigned to trusted oracle addresses. Oracles with this role can control the state and results of events they manage.
SIGNER_ROLE: Assigned to trusted signers within a
MarketmakerBox.ParlayHandlerwill then check the assigned role by callingmmBox.hasRole(SIGNER_ROLE, [signer_address]). This is used to validate off-chain agreements, such as the terms of a parlay bet.
Oracle Integration
Oracles play an important role in the betBlox ecosystem by bridging real-world data (sports results) to the blockchain. Markets can be either Controlled (Oracle-managed) or Non-Controlled (Market Maker-managed).
Controlled Markets (Oracle Managed)
In a controlled market, the lifecycle of the underlying event is managed by an approved Oracle.
Market Creation: A Market Maker creates a market linked to a specific
eventHash.Initialization: When the first market is created for this
event_hash, the event is created and the event state is set toInit.Activation: The Oracle server constantly monitors for markets in the
Initstate. If it recognizes theeventHashas one it tracks, it callssetEventActiveon theEventHandler. This transitions the event toActive.Lifecycle Management: The Oracle updates the event state as the real-world event progresses (e.g., setting it to
Playingwhen the match starts).Resolution: Once the event concludes, the Oracle calls
completeEventwith the final result. This allows theMarketHandlerto settle markets and enable payouts.
Non-Controlled Markets (Market Maker Managed)
If a market is not set to be controlled by an Oracle, the responsibility for lifecycle management falls on the Market Maker.
The Market Maker must manually update the event state and provide the final result.
This requires trust in the Market Maker, as they have direct control over the outcome resolution.
Market Components & Types
Markets in betBlox are structured around a few key components that define how bets are placed and settled.
Market Components
A market consists of the following parameters:
eventHash: Identifies the corresponding event.
liquidity: Is the amount of tokens the marketmaker has provided (laid) on the market.
betVolume: Is the combined amount of tokens the bettors have bet (backed) on the market.
paid: Is the combined amount settled, to both marketmaker and bettors, ignoring fee details.
marketType: Is a 32 bit unsigned (nonzero) number that identifies the marketType.
b2MmFeePermille: Is a market specific win fee rate, the bettors will pay to the marketmaker.
owner: Is the wallet address of the marketmaker, that "owns" this market.
vipGroup: Is a number between 0 and 255, that limits the access to who can bet. 0 means no VIP limitations.
offset: Is a number value used for some market types like "totals-goals", "spread" etc., to define the handicap or total line (e.g. -1.5 goals).
settled: Is a boolean stating if the market has been settled (event results processed and payout ready).
mmPaid: Is a Boolean stating if the marketmaker is paid yet.
bettorsPaid: Is a Boolean stating if all bettors are paid yet.
outcomes: Is an array of outcome data, holding specific data for each bettable outcome (se below).
MarketOutcomeData has these parameters:
outcomeId: Is a number from 0 - 65535 to define the outcome, as seen from the bettor (e.g. Home Win, Away Win, Draw).
odds: Is the current odds as decimal-odds, offered by the marketMaker, to bet on this outcome (is stored with 10 precision digits in contract).
available: Is the amount of the marketmakers volume, that is still available to bettors (unmatched).
betAmount: Is the combined amount betted by bettors in this outcome.
betsWagered: Is the number of bets on this outcome by bettors.
betsSettled: Is the number of bets settled to the bettors combined, or to the mm if its a loss.
result: Is an enum that defines if the result is a Win, HalfWin, Loss etc., after the market is settled.
Single Outcome vs. Book Markets
Markets can be configured in two primary ways regarding how they accept bets:
Single Outcome Markets: These markets are set up for a specific outcome (e.g., "Home Win" only). The Market Maker backs only this specific result.
Book Markets: These markets cover all possible outcomes for a specific market type (e.g., Home, Draw, and Away).
Balanced Book Mechanics: In a book market, the Market Maker can accept bets up to 100% of the assigned liquidity on each outcome side.
This allows for capital efficiency, as the liquidity is shared across the potential outcomes, assuming a balanced book where losing bets on one side fund the winning bets on the other.
Example: Balanced Moneyline Book
Scenario:
Market: Moneyline (Home vs. Away)
Liquidity Provided: 1000 USDC
Odds: Home @ 2.0, Away @ 2.0
Action 1: Bettor A bets 1000 USDC on Home.
Potential Payout: 2000 USDC (1000 Stake + 1000 Profit).
Liquidity Usage: The market reserves 1000 USDC of the MM's liquidity to cover the potential profit.
State: Market is fully exposed on the Home side (100% of liquidity used).
Action 2: Bettor B bets 1000 USDC on Away.
Potential Payout: 2000 USDC (1000 Stake + 1000 Profit).
Liquidity Usage: Because this is a book market, the MM can also use the same 1000 USDC liquidity to back this side. The losing side's stake will cover the winning side's profit.
State: Market is fully exposed on the Away side as well.
Resolution:
If Home Wins:
Bettor A receives 2000 USDC (1000 Stake + 1000 Profit).
The 1000 Profit comes from Bettor B's lost stake.
The MM retains their original 1000 USDC liquidity.
Result: The MM facilitated 2000 USDC in volume with only 1000 USDC in capital, effectively doubling their capital efficiency compared to a single-outcome market.
Market Types and Parameters
The protocol supports various market types, each with specific behaviors:
Moneyline (1x2)
Description: The simplest bet on who will win the match.
Outcomes: Home Win, Away Win, Draw (for sports like Soccer).
Parameters:
kMarketType_MoneyLine: Standard 2-way or 3-way moneyline (Multiple Outcomes / Book Market).kMarketType_MoneyLine_Home/_Away: Single outcome variants.
Spread (Asian Handicap)
Description: Bets on the margin of victory. One team receives a virtual head start (handicap).
Outcomes: Home Win, Away Win (after applying the spread).
Parameters:
Offset: The handicap value (e.g., -150 for -1.5 goals). Stored with 2 decimal precision.
kMarketType_Spread: Standard spread market.
Total (Over/Under)
Description: Bets on whether the total score will be over or under a specified amount.
Outcomes: Over, Under.
Parameters:
Offset: The total line (e.g., 250 for 2.5 goals). Stored with 2 decimal precision.
kMarketType_Total: Standard total market.
Fulltime Result
Description: Similar to Moneyline but specifically for full-time results in sports like Soccer.
Outcomes: Home Win, Draw, Away Win.
Parameters:
kMarketType_FulltimeResult: Standard 3-way market.
Prediction
Description: Generic prediction markets with a fixed number of outcomes.
Outcomes: 1 to 5 specific prediction outcomes. Note: Outcomes are mutually exclusive, meaning there can only be one winning outcome per market.
Parameters:
kMarketType_Prediction1tokMarketType_Prediction5: Defines the number of possible outcomes.
Fee Structure
betBlox implements a flexible fee structure that incentivizes Market Makers while ensuring protocol sustainability. Fees are calculated based on the net winnings (profit) and are deducted automatically during payouts.
1. Market Maker Fees (Bettor Wins)
When a bettor wins a bet, the Market Maker who created the market can charge a fee on the bettor's profit.
Fee Type:
b2MmFeePermille(Bettor to Market Maker Fee).Payer: The winning Bettor.
Receiver: The Market Maker.
Calculation:
Bettor Profit * b2MmFeePermille / 1000.Purpose: Compensates the Market Maker for providing liquidity and taking on risk.
2. Protocol Fees (Bettor Wins)
The protocol can also charge a fee on a bettor's profit.
Fee Type:
feePermilleBettor.Payer: The winning Bettor.
Receiver: The Protocol (Fee Receiver address).
Calculation:
Bettor Profit * feePermilleBettor / 1000.
3. Protocol Fees (Market Maker Wins)
When a Market Maker wins (i.e., the bettor loses), the protocol charges a fee on the Market Maker's profit. The rate depends on whether the market was controlled by an Oracle or managed manually by the Market Maker.
Fee Types:
feePermilleOracleMM: Fee rate for Oracle-managed events.feePermilleOwnMM: Fee rate for Market Maker-managed events.
Payer: The Market Maker.
Receiver: The Protocol (Fee Receiver address).
Calculation:
Market Maker Profit * Applicable Fee Rate / 1000.Purpose: Ensures the protocol earns revenue from trading activity, regardless of who wins.
Agent Betting & Automation
betBlox includes a native "Agent" system that allows users to delegate betting execution to another address (an "Agent") without giving up custody of their funds. This is designed specifically for automated trading, AI clients and bots, and copy-betting services.
How it Works
The system separates the Authorization (User) from the Execution (Agent).
Custody: The User (Bettor) keeps their funds in their own wallet. They do not deposit funds into a bot's wallet.
Approval: The User approves the
BetHandlercontract to spend their betting tokens (standard ERC20 approval).Delegation: The User calls
setBetAgent(agentAddress)on theBetHandlerto authorize a specific address (e.g., a bot's hot wallet) to place bets on their behalf.Execution: The Agent calls
agentBet(...)specifying the User's address as thebettor.Acceptance: The
BetHandlerverifies the relationship and pulls the bet amount directly from the User's wallet. Any winnings are sent back directly to the User.
Use Case: AI Betting Bot
This feature enables a secure setup for AI-driven betting services:
User Setup:
User holds 1000 USDC in their cold/warm wallet.
User approves
BetHandlerfor 1000 USDC.User authorizes the AI Bot's address as their agent.
Bot Operation:
The AI Bot scans markets for value bets.
When a specific opportunity arises (e.g., "Home Win" odds > 2.5), the Bot triggers
agentBetfor the User.The transaction gas is paid by the Bot, but the 100 USDC stake is pulled from the User.
Security:
The Bot cannot withdraw funds or transfer them elsewhere. It can only place bets into the protocol.
The User can revoke the Agent's access at any time by calling
setBetAgent(address(0)).
VIP System
betBlox includes a granular access control system known as the VIP System. This allows Market Makers to restrict who can place bets on their markets. This feature is powerful for creating private betting clubs, exclusive high-roller markets, or regulatory-compliant KYC pools.
How it Works
Every market has a vipGroup parameter (uint8) set during creation or updated via setVIPGroup.
Group 0: Public Market. Anyone can bet. (Default)
Groups 1-254: VIP Markets. Only addresses with the specific VIP role can bet.
Group 255: Closed Market. No one can bet (effectively pausing the market).
Managing Access
Access is managed at the MarketmakerBox level. A Market Maker grants VIP roles to specific user addresses. Once a user has a VIP role (e.g., Group 1), they can bet on any market created by that Market Maker that is set to vipGroup = 1.
Grant Access:
mmBox.grantVIP(userAddress, groupNumber)Revoke Access:
mmBox.revokeVIP(userAddress, groupNumber)
Use Cases
1. Private Betting Club
Scenario: You want to run a betting pool exclusively for your friends or colleagues.
Setup:
Create a
MarketmakerBox.Assign "Group 1" status to all your friends' addresses using
grantVIP.When creating markets, set
vipGroupto1.
Result: Only your friends can place bets. Random users on the blockchain will be rejected by the smart contract if they try to bet.
2. Exclusive Low-Fee Markets
Scenario: You are a professional Market Maker and want to offer reduced fees (e.g., 0% fee) to your high-volume VIP clients, while charging standard fees to the public.
Setup:
Define "Group 10" as your "VIP High Roller" tier.
Grant "Group 10" status to your qualifying large-volume customers.
Create two identical markets for the same event:
Market A (Public):
vipGroup = 0,Fee = 2%.Market B (VIP):
vipGroup = 10,Fee = 0%.
Result: Your VIP clients can bet on Market B with zero fees, while the general public can only access Market A. This allows for tiered service levels on-chain.
Token Gating
In addition to role-based access control, betBlox uses a Token Gating system. This feature allows the protocol to restrict access to specific functions based on whether the caller holds a minimum balance of BET tokens.
In betBlox v1.0 only MarketMaking is token gated. This means that only users with a minimum balance of BET tokens can create markets.
Token Gated Functions
The following contracts and functions support token gating:
MarketHandler
addMarket: Creating a new market.addLiquidity: Adding liquidity to an existing market.removeLiquidity: Removing liquidity from a market.
EventHandler
addEvent: Creating a new market maker-controlled event.
MarketmakerBoxLite
deposit: Depositing funds into the market maker box.withdraw: Withdrawing funds from the box.withdrawMax: Withdrawing all available funds.
Finding Data & Indexing
To interact with the protocol effectively (e.g., placing a bet on a specific market), you need to know the unique identifiers (hashes) for Events, Markets, and Bets. Since all core actions emit events on the blockchain, you can retrieve this data in several ways.
1. Transaction Logs (Direct)
When you execute a transaction (like addMarket or addBet), the smart contract emits an event containing the generated hash.
Event Created: Emits
EventAdded(bytes32 eventHash, ...)Market Created: Emits
MarketAdded(bytes32 marketHash, ...)Bet Placed: Emits
BetAdded(bytes32 betHash, ...)
You can parse these logs from the transaction receipt immediately after execution to store the hashes in your local database.
2. Block Explorers (Etherscan)
If you need to find a hash manually or verify data, you can use a block explorer like Etherscan.
Manual Lookup:
Navigate to the relevant contract address (e.g.,
MarketHandler).Click on the "Events" tab.
Filter by the event topic (e.g.,
MarketAdded).The
marketHashwill be listed in the data fields.
API Access: Etherscan also provides a robust API that allows you to programmatically fetch these logs instead of doing it manually. For high-volume applications, Etherscan offers a free and a paid version with significantly higher rate limits, allowing for almost unlimited calls.
3. TheGraph
For building scalable applications (dApps) that need to display lists of markets or user betting history, querying the blockchain directly can be inefficient.
betBase provides a hosted Subgraph on TheGraph protocol. This indexes selected betBlox protocol events (those critical for process flow) and makes them queryable via GraphQL. Note that many other events are emitted for debugging and manual verification purposes but may not be indexed.
Anyone can host their own subgraph on TheGraph protocol.
Aggregated Entities: The betBase subgraph goes beyond simple event indexing. It uses special constructed entities that assemble data from multiple contract events into single, easy-to-query objects. For example, a Market entity in the subgraph might combine data from MarketAdded, MarketOutcomeUpdated, and MarketSettled events, giving you the complete state of a market in one request.
Query Markets: Get all active markets for a specific sport.
Query Bets: Get all bets placed by a specific user.
Query Stats: Get volume and liquidity data.
See the betblox subgraph reference guide below for detailed information.
betBase API
betBase provides a REST API to facilitate easy integration of betBlox AIoracle real-world sports data with the betBlox protocol. While betBlox handles the on-chain execution of bets and markets, the betBase API is the source for finding meta data for upcoming sports events handled by the AIoracle and their corresponding on-chain identifiers (eventHash).
Core Concepts
The Event Hash
The eventHash is the critical link between the off-chain world (a football match happening in real life) and the on-chain protocol.
On-Chain: contracts like
MarketHandlerandEventHandlerrequire aneventHashto create markets or track status.Off-Chain: You use the betBase API to discover upcoming games (e.g., "Liverpool vs. Arsenal") and retrieve their unique
eventHash.
API Endpoints
Base URL: https://api.betbase.xyz
1. Get Sport Event Count
Endpoint: /get_sport_event_count_betblox
Description: Returns the number of active sport events within each sport category.
2. Get Sports & Leagues
Endpoint: /get_sports_leagues_betblox
Description: Returns a list of all sports and their IDs and the Leagues and IDs for each sport.
3. Get Events
Endpoint: /get_events_betblox?sport_id={id}&league_id={id}
Description: Returns a full list of events for all sports that are within the time range set to 48 hours before start. The parameters sport_id and league_id are optional event filters.
4. Get Event Info
Endpoint: /get_event_info_betblox?event_hash[]={hash}
Description: Returns information about one or more event_hashes. Input is one or more event_hash[]= appended.
Integration Guide
Getting Started
To start developing with betBlox, you will need to interact with the deployed contracts on the relevant network.
Connect to the Network: Ensure your application is connected to the correct blockchain network (Base).
Get Contract Addresses: Obtain the addresses of the core contracts (
BetHandler,MarketHandler, etc.) from the official deployment list.Approve Tokens: Before placing bets or creating markets, you must approve the relevant contracts to spend your tokens.
Placing a Bet
To place a bet, follow these steps:
Identify the
marketHashof the market you wish to bet on.Call
approveon the betting token contract to allowBetHandlerto spend your bet amount.Call
addBetonBetHandlerwith themarketHash,betAmount,odds, andoutcomeId.
Becoming a Market Maker
Create a
MarketmakerBoxusing theMarketmakerFactory.Deposit tokens into your
MarketmakerBox.Call
addMarketonMarketHandlerto create a new market using your deposited liquidity.
bloxTester Dev Tool
The betBlox protocol provides a bloxTester dev tool to help you test the betBlox protocol directly on Base Mainnet.
You can find the bloxTester dev tool here: betBlox bloxTester (URL placeholder).
betBlox Subgraph reference guide
1. Introduction
The betBlox subgraph indexes events, markets, bets, and parlays from the betBlox smart contracts on the Base blockchain. It provides a queryable interface to access historical and real-time data about betting activities.
Key Features
Events & Markets: Query detailed information about sports events and their associated betting markets.
Betting History: Access individual bets and parlay bets linked to specific users and outcomes.
Settlement Data: Retrieve results and payout information for settled markets and bets.
Endpoint
You can access the subgraph via The Graph Gateway. You will need an API key from the Subgraph Studio.
https://gateway.thegraph.com/api/[YOUR_API_KEY]/subgraphs/id/7MsG12oTAxnvLz1KyRyLFNm5RjrkYyXeYN9ww6wWunEM
Data Structure Overview
The subgraph is built around a few core entities:
EventData: The root entity representing a sports match or game.
MarketData: A specific betting market (e.g., "Match Winner") within an event.
MarketOutcomeData: A specific outcome (e.g., "Home Team Wins") within a market.
BetData: An individual user's bet on an outcome.
ParlayData: A multi-leg bet combining outcomes from different events. [Coming Soon]
ParlayOutcomeData: The leg specific data holding market/event relevant info. [Coming Soon]
PayoutInfo: A common entity used to detail all payouts.
2. Entities
Here are the primary entities available in the betBlox subgraph, based on the schema.
EventData
The EventData entity represents a unique sports event or match.
id:
Bytes!- (32 bytes) EventHash, unique identifier for the event.state:
Int!- (uint8) Current state of the event (e.g., scheduled, active, completed).owner:
Bytes- (address, 20 bytes) Address of the event creator/manager.eventType:
BigInt- (uint32) Type of event (e.g. Basketball, Football).firstMarketType:
BigInt!- (uint32) Identifier for the first market type. The type is saved for the market that created the event, so it can be used to check type compatibility, when the event type is later set.controlled:
Boolean- After being set Active this indicates if the event is Oracle controlled.result_homeScore:
Int- (uint16) The fulltime score of the home team.result_awayScore:
Int- (uint16 The fulltime score of the away team.result_prediction:
Int- (uint8) The winning outcome of the prediction: 1-5.result_desc:
String- (string) Optional description of the result. Could explain an overtime result, difference from the fulltime result, for instance.markets:
[MarketData!]!- List of associated markets.parlayOutcomes:
[ParlayOutcomeData!]!- List of associated parlay outcomes.blockNumber:
BigInt!- (BigInt) Number/id of the last modification block.blockTimestamp:
BigInt!- (BigInt) Timestamp of the last modification block.transactionHash:
Bytes!- (32 bytes) Hash of the last transaction that modified the entity.
MarketData
The MarketData entity represents a betting market within an event (e.g., "Match Winner").
id:
Bytes!- (32 bytes) MarketHash, unique identifier for the market.eventHash:
Bytes!- (32 bytes) EventHash, unique identifier for the parent event.owner:
Bytes- (address, 20 bytes) Address of the Marketmaker.marketType:
BigInt- (uint32) Type of market (e.g. 1x2, moneyline, total-goals).b2MmFeePermille:
Int- (uint16) Is the market specific win fee rate, the bettors will pay to the marketmaker.vipGroup:
Int- (uint8) Is the VIP group (0-255), that limits the access to who can bet. 0 means no VIP limitations, 255 means all are restricted so betting is paused and all other values are assignable to bettors.offset:
Int- (int32) Is a number value used for some market types like "totals-goals", "spread" etc., to define the handicap or total line (e.g. -1.5 goals).liquidity:
BigInt!- (BigInt) Total liquidity available in the market.betVolume:
BigInt!- (BigInt) Total volume of bets placed.allPaidAmount:
BigInt!- (BigInt) Full paid amount, is only set after full payout.settled:
Boolean!- Whether the market has been settled.mmPayout:
PayoutInfo- Detail amounts of the Marketmaker payoutoutcomes:
[MarketOutcomeData!]!- List of possible outcomes in this market.bets:
[BetData!]!- List of bets placed on this market.event:
EventData- Link to the parent event.blockNumber:
BigInt!- (BigInt) Number/id of the last modification block.blockTimestamp:
BigInt!- (BigInt) Timestamp of the last modification block.transactionHash:
Bytes!- (32 bytes) Hash of the last transaction that modified the entity.
MarketOutcomeData
The MarketOutcomeData entity represents a specific outcome within a market (e.g. "Home Win").
id:
Bytes!- (32+4 bytes) Unique identifier (MarketHash + OutcomeId).marketHash:
Bytes!- (32 bytes) MarketHash, unique identifier for the parent market.odds:
BigInt!- (BigInt) Is the current odds as decimal-odds, offered by the marketMaker, to bet on this outcome (is stored with 10 precision digits, eg.19000000000for 1.900 decimal odds).outcomeId:
Int!- (uint16) Numeric ID of the outcome (e.g. 1 for "Home Win", 2 for "Away Win").available:
BigInt!- (BigInt) Is the liquidity from the Marketmaker, still available for new bets. Is updated for every bet and add/remove liquidity.betVolume:
BigInt!- (BigInt) Is the total amount of bets from bettors, on this outcome. Is updated for every bet.outcomeResult:
Int!- (uint8) Is an enum to express the result of this outcome:{ Undefined, Win, HalfWin, Void, HalfLoss, Loss }.market:
MarketData!- Link to the parent market.bets:
[BetData!]!- List of bets placed on this specific outcome.blockNumber:
BigInt!- (BigInt) Number/id of the last modification block.blockTimestamp:
BigInt!- (BigInt) Timestamp of the last modification block.transactionHash:
Bytes!- (32 bytes) Hash of the last transaction that modified the entity.
BetData
The BetData entity represents a single bet placed by a user.
id:
Bytes!- (32 bytes) BetHash, unique identifier for the bet.marketHash: -
Bytes!(32 bytes) MarketHash, unique identifier for the parent market.bettor:
Bytes!- (address) Address of the user who placed the bet.outcomeId:
Int!- (uint16) Numeric ID of the bet outcome (e.g. 1 for "Home Win", 2 for "Away Win").betType:
BigInt- (uint8) Enum to determine bet type (to recognize agent bets):{ Undefined, Market, Agent }.betAmount:
BigInt!- (BigInt) Amount wagered.odds:
BigInt!- (BigInt) Is the odds as decimal-odds, at the time the bet was placed.settled:
Boolean!- Whether the bet has been settled.payout:
PayoutInfo- Details about the payout (if settled).outcome:
MarketOutcomeData!- The specific outcome chosen.market:
MarketData!- The parent market the bet was placed on.blockNumber:
BigInt!- (BigInt) Number/id of the last modification block.blockTimestamp:
BigInt!- (BigInt) Timestamp of the last modification block.transactionHash:
Bytes!- (32 bytes) Hash of the last transaction that modified the entity.
ParlayData
The ParlayData entity represents a multi-leg bet (parlay).
id:
Bytes!(32 bytes) ParlayHash, unique identifier for the parlay.liquidity:
BigInt!- (BigInt) Liquidity to the parlay by the Marketmaker.betAmount:
BigInt!- (BigInt) Amount wagered by the bettor.odds:
BigInt!- Combined odds for the parlay.parlayType:
BigInt- (uint8) Enum to determine parlay type (to recognize agent parlays):{ Undefined, Parlay, Agent }.b2MmFeePermille:
BigInt!- (BigInt) Is a paraly specific win fee rate, the bettors will pay to the marketmaker.mm:
Bytes- (address, 20 bytes) Address of the Marketmaker.bettor:
Bytes!- (address) - Address of the user who placed the parlay bet.settled:
Boolean!- Whether the parlay has been settled.parlayResult:
Int!- (uint8) Is an enum to express the result of this parlay, as seen from the bettor:{ Undefined, Win, Void, Loss }.outcomes:
[ParlayOutcomeData!]!- List of individual legs in the parlay.bettorPayout:
PayoutInfo- Detail amounts of the bettor payout.mmPayout:
PayoutInfo- Detail amounts of the Marketmaker payout.blockNumber:
BigInt!- (BigInt) Number/id of the last modification block.blockTimestamp:
BigInt!- (BigInt) Timestamp of the last modification block.transactionHash:
Bytes!- (32 bytes) Hash of the last transaction that modified the entity.
ParlayOutcomeData
The ParlayOutcomeData entity represents a single leg within a parlay bet.
id:
Bytes!- (32+4 bytes) Unique identifier (txHash + logIndex). This is never updated, and does not need be easily recreated.parlayHash:
Bytes!(32 bytes) ParlayHash, unique identifier for the parent parlay.eventHash:
Bytes!- (32 bytes) EventHash, unique identifier for corresponding event.marketType:
BigInt- (uint32) Type of market (e.g. 1x2, moneyline, total-goals).offset:
Int- (int32) Is a number value used for some market types like "totals-goals", "spread" etc., to define the handicap or total line (e.g. -1.5 goals).outcomeId:
Int!- (uint16) The specific outcome selected.parlay:
ParlayData!- Link to the parent parlay bet.event:
EventData!- Link to the event associated with this leg.blockNumber:
BigInt!- (BigInt) Number/id of the last modification block.blockTimestamp:
BigInt!- (BigInt) Timestamp of the last modification block.transactionHash:
Bytes!- (32 bytes) Hash of the last transaction that modified the entity.
PayoutInfo
The PayoutInfo entity contains detailed payout information for settled bets and markets.
amountPaid:
BigInt!- (BigInt) Amount paid out to the bettor/Marketmaker.amountWon:
BigInt!- (BigInt) Amount won by the receiver (profit).amountReturned:
BigInt!- (BigInt) Amount returned to the receiver (fee free amount).amountFeesToMm:
BigInt!- (BigInt) Fees paid to the Marketmaker.amountFeesProto:
BigInt!- (BigInt) Fees paid to the protocol.
3. Querying Guide
This guide explains how to construct queries to fetch data from the betBlox subgraph.
Basic Query Structure
GraphQL queries allow you to fetch exactly the data you need.
Filtering
By ID
Fetch a specific event by its ID (Hash).
By State
Fetch only active events (e.g., state 1).
By Owner
Fetch events created by a specific address.
Pagination
Use first and skip to paginate through results.
Sorting
Use orderBy and orderDirection to sort results.
Nested Queries (Relationships)
The power of the subgraph lies in traversing relationships.
Events with Markets and Outcomes
Fetch events along with their markets and the outcomes for those markets.
Bets within a Market
Fetch the last 10 bets placed on a specific market.
4. TypeScript Examples
Here are some practical examples of how to query the betBlox subgraph using TypeScript. We'll use graphql-request for simplicity, but you can use Apollo Client or fetch as well.
Setup
First, install the necessary package:
Example 1: Fetching Active Events with Markets
This example fetches all active events (state 1) along with their markets and outcomes.
Example 2: Fetching Recent Bets
Fetch the most recent bets placed in the system.
Example 3: Market Liquidity Analysis
Analyze liquidity and bet volume for specific markets.
Parlay Betting Guide
Parlay Betting is a special case of betting in the protocol which requires off-chain interaction with a market maker server process to approve a combined bet of onchain markets. This guide explains the technical workflow of the ParlayHandler.sol smart contract, detailing how parlay bets are constructed, placed on-chain, and settled.
1. The Parlay Input Data
A parlay bet is defined by the ParlayInput struct, which serves as the signed agreement between the Bettor (Client) and the Market Maker (Server).
Data Structure
The ParlayInput contains:
Participants:
bettor(User address) andmm(Market Maker address).Bet Terms:
betAmount: The user's stake.liquidity: The amount the Market Maker is risking (the payout).odds: The combined decimal odds for the parlay.b2MmFeePermille: The fee rate paid to the MM on a win.
Outcomes: An array of selected predictions (
outcomeId,marketType,eventHash) that must all occur.
The "Optimistic" Flow
Client: Constructs the proposed bet (outcomes, stake).
Server (MM): The market maker has assigned the server, via its public key, a parlay signer role in his marketMaker box. The server Validates the risk/odds off-chain. If accepted, the MM server process signs a hash of the
ParlayInputwith its private key.Client: Submit the
ParlayInputdata + the MM's serversignatureto the blockchain.
2. On-Chain Placement (addParlay)
addParlay)When the client calls addParlay(), the smart contract performs strict validation before locking funds.
Verification Steps
Signature Check: The contract hashes the input data and uses
ECDSA.recoverto verify that the signature belongs to the Market Maker server. This ensures the terms haven't been tampered with.Validation:
Amounts: Checks if
betAmountandliquidityalign with the providedodds.Uniqueness: Checks that the same event isn't used twice in one parlay.
Leg Validity: Verifies that every
eventHashexists and themarketType/outcomeIdare valid valid options.Authorization: Checks if the bettor has approved the contract to spend their tokens.
Fund Locking
If valid, the contract:
Transfers the
betAmountfrom the Bettor.Reserves the
liquidityfrom the Market Maker's vault (MarketMakerBox).Locks both sums into the
BetBox(a secure LockBox contract) under the specificparlayHash.
3. Settlement & Resolution (settleParlay)
settleParlay)Once the real-world events have concluded, the settleParlay function resolves the bet.
The Logic
The contract iterates through every single outcome in the parlay and checks its status via the EventHandler and MarketHandler.
Winning a Leg: An outcome is only a "Win" if the event is completed (
Activestate removed) and the resulting outcome matches the user's prediction.Losing a Leg: If the prediction is wrong.
Void/Canceled Events: If an event is canceled or a market is voided.
The "All-or-Nothing" Rule
Crucially, this contract implements a strict All-or-Nothing rule:
WIN: Every single outcome must result in a
Win.LOSS: If ANY outcome is a
Loss, OR matches areCanceledorVoid, the entire parlay is marked as a LOSS.
Note: Unlike some traditional bookmakers that might remove a voided leg and reduce the odds, this contract treats a Void/Canceled leg as a failure of the parlay condition, resulting in a loss for the bettor.
Payout
If Parlay Wins:
Bettor receives: Their original
betAmount+ the MM'sliquidity.(Fees are deducted from the winnings).
If Parlay Loses:
Market Maker receives: Their reserved
liquidity(unlocked) + the Bettor'sbetAmount(profit).(Fees are deducted from the profit).
Last updated