Skip to main content

Overview

The Mouth protocol uses a Factory pattern: a single MouthBetFactory 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 MouthBet contracts 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)
See Factory Contract for full specification.

MouthBet

An individual bet escrow contract deployed for each bet. Supports both PvP (1v1) and Open PvP (multiple opponents, partial fills). Uses initialize() 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 disputeable flag: when true, enforce the 48-hour dispute window; when false, 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)
See Bet Contract for full specification.

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
Cons:
  • Higher gas cost per bet creation (deploying a contract vs writing to storage)
  • More contracts to index
On Base, gas costs are extremely low, so the isolation benefits outweigh the cost.

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
The proxy is controlled by the protocol owner via _authorizeUpgrade().

Initialize Pattern (Clone-Ready)

Both the Factory and MouthBet use initialize() instead of constructors. This means:
  • v1 (now): Factory deploys full MouthBet contracts via new MouthBet(), then calls initialize()
  • v2 (future): Factory switches to Clones.clone(implementation) + initialize() — no MouthBet code changes needed, only the Factory is upgraded
This decision costs nothing upfront and saves a full re-audit when migrating to clones.

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

ParameterValue
ChainBase (Chain ID: 8453)
USDC Address0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913
Solidity Version^0.8.24
OpenZeppelinContracts Upgradeable v5.x
LicenseMIT

Security Considerations

  • Reentrancy protection: All state changes happen before external calls, ReentrancyGuard on 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 disputeable flag). 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