Prize Pool System
Trustless, contract-held tournament prize pools with third-party funding support.
Purpose
The prize pool system allows anyone — game operators, sponsors, guilds, streamers, or individual players — to fund competitive events. It exists because:
- Players and sponsors must trust that prize money will be paid out
- Game developers holding prize funds creates a single point of failure
- Smart contract custody removes the need for trust in any individual party
- Third-party funding (guilds, streamers, sponsors) requires a neutral mechanism
Design Principles
| Principle | Implementation |
|---|---|
| Developer never holds funds | All funds go directly to/from the contract |
| Winners claim directly | No admin intermediary for payout |
| Multiple funding sources | Any address can deposit into a pool |
| Refundable on cancellation | If event is cancelled, depositors can reclaim |
| Transparent pool balance | Anyone can query the contract for current pool state |
Architecture
┌─────────────────────────────────────────────┐
│ Prize Pool Contract │
│ │
Sponsor A ──►│ pool_balance: 100 AVAX │
Sponsor B ──►│ status: ACTIVE │
Entry Fees ─►│ participants: [addr1, addr2, ...] │
│ prize_split: [60%, 30%, 10%] │
│ results: [] (pending) │
│ │
│ fund() ← anyone │
│ register() ← players │
│ submit() ← game server (authorized) │
│ claim() ← verified winners │
│ refund() ← depositors (if cancelled) │
└─────────────────────────────────────────────┘
Pool Lifecycle
┌───────────┐ ┌───────────┐ ┌───────────┐ ┌───────────┐
│ CREATED │────►│ FUNDED │────►│ ACTIVE │────►│ FINALIZED │
│ │ │ │ │ │ │ │
│ pool ID │ │ deposits │ │ matches │ │ results │
│ rules set │ │ accepted │ │ in │ │ submitted │
│ │ │ players │ │ progress │ │ claims │
│ │ │ register │ │ │ │ open │
└───────────┘ └───────────┘ └───────────┘ └─────┬─────┘
│ │
▼ ▼
┌───────────┐ ┌───────────┐
│ CANCELLED │ │ COMPLETED │
│ │ │ │
│ refunds │ │ all │
│ available │ │ claimed │
└───────────┘ └───────────┘
State Transitions
| From | To | Triggered By | Condition |
|---|---|---|---|
| CREATED | FUNDED | First fund() call | Pool receives minimum balance |
| FUNDED | ACTIVE | Game server | Start signal from authorized key |
| ACTIVE | FINALIZED | Game server submit() | Results array submitted |
| FINALIZED | COMPLETED | All claim() processed | All winners have claimed |
| FUNDED | CANCELLED | Admin / timeout | Event not started within window |
| ACTIVE | CANCELLED | Admin (emergency only) | Requires multi-sig or timelock |
Funding Sources
Entry Fees
Player pays entry fee (AVAX)
│
▼
Contract splits fee:
├── 90% → prize pool
└── 10% → treasury (configurable)
Sponsor Deposits
Sponsor calls fund(poolId) with AVAX
│
▼
Deposit added to pool_balance
│
▼
Sponsor address recorded for refund eligibility
Third-Party Funding Types
| Funder Type | Motivation | How They Fund |
|---|---|---|
| Guilds | Promote their members, brand visibility | Direct fund() call |
| Streamers | Content, community engagement | Via streamer tournament UI |
| Sponsors | Brand exposure, marketing | Direct fund() call |
| Players | Increased prize pool for their event | Via entry fee + optional extra |
Prize Distribution
Split Configuration
Defined at pool creation:
{
pool_id: 42,
prize_split: [
{ rank: 1, percent: 50 },
{ rank: 2, percent: 25 },
{ rank: 3, percent: 15 },
{ rank: 4, percent: 10 }
],
entry_fee: "2.0 AVAX",
fee_split: {
prize: 90, // % of entry fee added to prize pool
treasury: 10 // % to treasury
}
}
Claim Process
Game server submits results:
results = [(addr1, rank1), (addr2, rank2), ...]
│
▼
Contract calculates each winner's share:
share = pool_balance * prize_split[rank] / 100
│
▼
Winner calls claim(poolId)
│
▼
Contract verifies:
• caller is in results
• caller hasn't already claimed
• pool is in FINALIZED state
│
▼
AVAX transferred to winner
Security Properties
| Property | Implementation |
|---|---|
| No admin withdrawal of prizes | Only verified winners can call claim() |
| Double-claim prevention | hasClaimed[poolId][address] mapping |
| Depositor refund on cancel | refund(poolId) returns proportional deposit |
| Result submission authorization | Only authorized game server key can call submit() |
| Overflow protection | Split percentages must sum to 100% |
| No fund mixing | Each pool has isolated balance |
Contract Interface
interface IPrizePool {
// Pool management
function createPool(
uint256 entryFee,
uint8[] calldata prizeSplitPercents,
uint256 startDeadline
) external returns (uint256 poolId);
// Funding
function fund(uint256 poolId) external payable;
// Registration
function register(uint256 poolId) external payable;
// Results (authorized only)
function submitResults(
uint256 poolId,
address[] calldata winners,
uint8[] calldata ranks
) external;
// Claims
function claim(uint256 poolId) external;
// Refunds
function refund(uint256 poolId) external;
// View
function getPool(uint256 poolId) external view returns (PoolData memory);
function getBalance(uint256 poolId) external view returns (uint256);
}
Integration Guide for Other Games
Step 1: Deploy Prize Pool Contract
- Configure default fee split (prize vs treasury)
- Set authorized server address for result submission
- Set cancellation policy (timeout, multi-sig)
Step 2: Integrate with Game Server
- Game server creates pools when tournaments are scheduled
- Game server submits results when tournaments complete
- Provide player-facing UI for registration and claiming
Step 3: Enable Third-Party Funding
- Expose
fund()for sponsors, guilds, and streamers - Build a sponsor dashboard showing pool status and branding
- Optionally: create a widget streamers can embed
Step 4: Monitor
- Track pool balances and claim rates
- Alert on pools stuck in ACTIVE state (stale tournaments)
- Monitor treasury accumulation for economic health