Base Bets (MBC Hackathon)
Overview
Base Bets is a peer-to-peer USDC escrow on Base that settles based on Polymarket outcomes. We built it at the 2025 Midwest Blockchain Conference hackathon. Two people make a conditional bet, like “I’ll pay you $50 if Bitcoin hits $100k by year-end.” Both stakes are locked in a contract, and the winner is paid automatically when the market resolves.
Prediction markets already decide real-world outcomes and have money riding on getting them right, so Base Bets uses a Polymarket market as the oracle instead of a single trusted arbiter.
I led smart contract development.
How It Works
High level. A depositor creates an escrow that links to a Polymarket market and picks which outcome they’re betting on. The beneficiary accepts by staking their side. An offchain resolver watches Polymarket, and when the market resolves it sends the outcome to the contract. The contract then pays the full pot to the winner. If the market never resolves, the stakes can be refunded after a timeout.
Low level: the escrow lifecycle.
- Create.
createEscrowvalidates the inputs (no zero address, no self-escrow, no zero amounts, no empty market ID), pullsamountAUSDC from the depositor withsafeTransferFrom, and stores anEscrowstruct with the market ID and the depositor’s expected outcome. - Accept.
acceptEscrowcan only be called by the named beneficiary. It pullsamountBand marks the escrow accepted, so the contract now holds both stakes. - Resolve.
resolveEscrowcan only be called by the resolver. It compares the market result withexpectedOutcomeYes, sendsamountA + amountBto the winner, and updates each user’s win/loss stats onchain. - Batch resolve.
resolveEscrowBatchsettles many escrows in one transaction when several markets resolve together, which saves gas. Escrows that aren’t eligible are skipped instead of reverting. - Refund. If the market hasn’t resolved after
ESCROW_TIMEOUT(7 days), the depositor or the contract owner can callemergencyRefund, which returns each side’s original stake. Nobody wins in this case.
The resolver is the only centralized part. It can trigger settlement, but the contract only ever pays the depositor or the beneficiary, so the resolver can’t send funds anywhere else. Every resolution is onchain and can be checked against Polymarket.
What I Built
I wrote the escrow contract in Solidity:
- A two-sided escrow struct with separate stake amounts, so the odds can be uneven ($100 against $20)
- The five-step lifecycle above: create, accept, resolve, batch resolve, timeout refund
ReentrancyGuardon every function that moves funds, plusSafeERC20transfers- Custom errors (
CannotEscrowToSelf,OnlyResolver,TimeoutNotReached, etc.) instead of string reverts - Per-user win/loss stats stored onchain for a live leaderboard
- Deployment to Base Sepolia
Stack
- Onchain: Solidity on Base, OpenZeppelin
- Wallet and gas: Coinbase Smart Wallet (ERC-4337) and CDP Paymaster through OnchainKit, so users only need USDC and never pay gas
- Oracle: an offchain resolver that polls Polymarket’s Gamma API
Team
- Brock Benton: smart contracts
- Tyler Martin: frontend
- Sri Vamsi Andavarapu: backend and resolver