Overview
The Mouth protocol uses a Factory pattern: a singleMouthBetFactory contract (behind a UUPS proxy) deploys individual MouthBet contracts for each bet. Each bet is a self-contained escrow.
Contract Architecture
Contracts
MouthBetFactory (UUPS Proxy)
The factory contract that deploys individual bet contracts. Deployed behind a UUPS proxy so the address never changes across upgrades. Key responsibilities:- Deploy new
MouthBetcontracts with the correct parameters - Maintain a registry of all deployed bet contracts
- Store protocol-level configuration (fee percentage, resolver address, treasury address)
- Allow the owner to update protocol parameters
- Allow the owner to upgrade the implementation (UUPS)
MouthBet
An individual bet escrow contract deployed for each bet. Supports both PvP (1v1) and Open PvP (multiple opponents, partial fills). Usesinitialize() instead of a constructor, making it clone-ready for a future migration to EIP-1167 Minimal Proxy without any code changes.
Key responsibilities:
- Hold USDC deposits from the Challenger and one or more Opponents
- Support asymmetric deposits based on odds (10-90)
- Enforce bet lifecycle (pending → filling → active → resolved → finalized → claimed)
- Handle partial fills for Open PvP bets with acceptance deadline
- Allow the Challenger to withdraw before acceptance, or the Resolver to withdraw deserted bets after deadline
- Accept resolution from the authorized resolver
- Support a configurable
disputeableflag: whentrue, enforce the 48-hour dispute window; whenfalse, skip directly to Finalized - Distribute funds to the winner(s) with proportional payouts (minus protocol fee)
- Support resolver batch distribution via
resolverClaim() - Support mutual cancellation (2% fee already collected at deposit time, not refunded)
- Allow the resolver to re-resolve during the dispute window (
reResolve, disputeable only)
Design Decisions
Why Factory Pattern (Not a Single Contract)?
Pros:- Each bet is isolated — a bug in one bet doesn’t affect others
- Clear separation of concerns
- Easy to upgrade the factory for new bets without affecting existing ones
- Users can verify their specific bet contract on-chain
- Higher gas cost per bet creation (deploying a contract vs writing to storage)
- More contracts to index
Why UUPS Proxy for Factory?
The Factory is the protocol’s single entry point — the backend, frontend, and indexer all reference its address. A UUPS proxy ensures:- The address never changes across upgrades
- All state (bet registry, config) persists across upgrades
- We can migrate from full deploys to Minimal Proxy Clones without redeploying
_authorizeUpgrade().
Initialize Pattern (Clone-Ready)
Both the Factory and MouthBet useinitialize() instead of constructors. This means:
- v1 (now): Factory deploys full MouthBet contracts via
new MouthBet(), then callsinitialize() - v2 (future): Factory switches to
Clones.clone(implementation)+initialize()— no MouthBet code changes needed, only the Factory is upgraded
Why USDC (Not ETH)?
- Stable value: When you bet $500, you bet $500. No volatility risk.
- Native on Base: USDC is issued natively on Base by Circle, no bridging needed.
- User expectation: Users think in dollar terms, not ETH terms.
Chain Details
| Parameter | Value |
|---|---|
| Chain | Base (Chain ID: 8453) |
| USDC Address | 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 |
| Solidity Version | ^0.8.24 |
| OpenZeppelin | Contracts Upgradeable v5.x |
| License | MIT |
Security Considerations
- Reentrancy protection: All state changes happen before external calls,
ReentrancyGuardon all fund-moving functions - Access control: Only the resolver can resolve bets, only the factory owner can update protocol params and upgrade
- USDC approval pattern: The Challenger approves the Factory to spend USDC (deposit happens at bet creation). Opponents approve the individual bet contract. This ensures the Challenger always deposits first.
- Fee per-deposit: The 2% protocol fee is collected proportionally as each opponent deposits — not at resolution. For PvP 1v1, this happens in a single transaction when the opponent deposits. For Open PvP, each deposit triggers a proportional fee transfer to the treasury.
- Pre-generated wallets: Opponents don’t need to be registered on Mouth. The backend uses Privy to pre-generate wallets for unregistered X accounts, enabling immediate bet creation against any X user.
- Dispute window: Optional 48-hour window enforced on-chain with timestamp checks (controlled by
disputeableflag). Non-disputeable bets skip directly to Finalized. - Acceptance deadline: Mandatory for Open PvP bets to prevent last-second sniping
- No ETH handling: Contract only deals with USDC (ERC20), reducing attack surface
- Immutable bets: Individual MouthBet contracts are NOT upgradeable — once deployed, the rules of a bet cannot change