Overview
MouthBetFactory is the core protocol contract. It deploys individual MouthBet escrow contracts and stores protocol-wide configuration.
Deployed behind a UUPS proxy so the address remains fixed across upgrades. Inherits from Initializable, UUPSUpgradeable, and OwnableUpgradeable (OpenZeppelin v5).
State Variables
| Variable | Type | Description |
|---|---|---|
resolver | address | Address authorized to resolve bets |
treasury | address | Address receiving protocol fees |
feePercentage | uint256 | Protocol fee in basis points (200 = 2%), snapshotted per-bet at creation and collected per-deposit |
usdc | address | USDC token address on Base |
betCount | uint256 | Total number of bets created |
bets | mapping(uint256 => address) | Bet ID → Bet contract address |
isMouthBet | mapping(address => bool) | Verify if an address is a legitimate Mouth bet |
owner is inherited from OwnableUpgradeable and is not listed separately. Storage layout must be preserved across upgrades — new variables are only appended at the end. The MAX_FEE (500 = 5%) and MIN_POT (10e6 = 10 USDC) constants are also defined.Functions
initialize
Replaces the constructor. Called once at deployment via the proxy.
- Initialize
OwnableUpgradeablewithmsg.senderas owner - Initialize
UUPSUpgradeable - Set
resolver,treasury,usdc - Set
feePercentageto 200 (2%)
createBet
Deploys a new MouthBet contract, calls its initialize(), and transfers the Challenger’s USDC deposit to the bet contract.
- Validate parameters:
_challengerAmountmust be greater than 0_oddsmust be between 10 and 90 (inclusive)_expirationDatemust be in the future_acceptanceDeadlinemust be in the future (> block.timestamp) and before_expirationDate
- Calculate
totalOpponentAmount:_challengerAmount * (100 - _odds) / _odds - Validate minimum pot:
_challengerAmount + totalOpponentAmount >= 10 USDC(10e6 with 6 decimals) - Deploy new
MouthBetcontract:MouthBet bet = new MouthBet() - Call
bet.initialize(...)with all parameters including_disputeable(setschallengerDeposited = true) - Transfer Challenger’s USDC to the bet contract:
IERC20(usdc).safeTransferFrom(_challenger, address(bet), _challengerAmount) - Store bet address in
betsmapping - Mark address in
isMouthBet - Emit
BetCreatedevent - Return bet ID and contract address
The Challenger’s USDC is transferred in the same transaction as bet creation. The Challenger must approve the Factory contract (not the bet contract) to spend their USDC. The opponent address can be a pre-generated Privy wallet for users who haven’t signed up yet — see Identity & Wallets.
updateResolver
Updates the resolver address.
updateTreasury
Updates the treasury address.
updateFeePercentage
Updates the protocol fee.
_authorizeUpgrade
UUPS hook — restricts upgrades to the contract owner.
View Functions
Events
Upgrade Path
The UUPS proxy allows upgrading the Factory implementation without changing its address or losing state:mouthBetImplementation state variable and update createBet() to use Clones.clone(). No changes needed to MouthBet itself.