Click-Me Games
The Core Problem
Solana's account model creates write-lock contention when multiple transactions touch the same account. For a global competition where thousands of users need to update shared state (pot size, last clicker), this becomes a bottleneck at ~50 TPS.
Our solution: temporal sharding with deterministic ordering.
Architectural Decisions
Why Sharding?
We evaluated three approaches:
- Single account with optimistic concurrency - Failed at 50+ concurrent users
- Merkle tree aggregation - Too complex for real-time gaming, 2-3 second finality
- Temporal sharding - Linear scalability, sub-second UX
Sharding won. Each shard holds exactly one click, eliminating write contention entirely. A crank transaction atomically consumes all shards, determines the winner, and distributes funds.
Trade-offs We Accepted
- Crank dependency: Someone must call consume_all_shards. We solve this economically with guaranteed crank rewards.
- Account proliferation: 16 shards = 32 PDAs (state + vault). Worth it for 400 TPS.
- Slightly higher latency: Click → crank → settlement adds ~1 second. Users don't notice.
Non-Obvious Implementation Details
The Rent Problem
Solana's rent exemption is a footgun. When you transfer SOL from a PDA, dropping below rent exemption kills the account.
Solution: Buffer enforcement per transaction on all vault operations.
Click Ordering Without Timestamps
Block times lie. MEV bots can manipulate slot ordering. Our three-tier resolution:
- Clock timestamp (rough ordering)
- Slot number (finer ordering)
- Deterministic hash (tiebreaker)
The hash incorporates user pubkey + slot + shard, making pre-computation impossible while ensuring global consistency.
Direct Lamport Manipulation
We bypass the System Program for vault transfers. Why? Speed. Direct lamport writes save ~5000 CUs per transfer. In a transaction processing 16 shards with refunds, that's 80k CUs saved.
The pattern: direct mutation of account lamports through borrow_mut_lamports(). No System Program. No CPI overhead.
Security Learnings
Economic DoS is Real
Initial design: no rate limits. Result: bots clicking 1000x per second, burning money but clogging the network.
Current design:
- 2-slot cooldown (economic: can't spam faster than blockchain)
- 100 clicks/epoch max (practical: real users never hit this)
- Escalating costs (asymmetric: defenders win long-term)
Why Shard Griefing Fails: The Real Economic Analysis
The critical insight: priority fees scale with pot size.
Game dynamics:
- Each click extends the timer by 3 minutes
- Pot grows with each click (90% of click cost added)
- Rational priority fee = f(pot size, probability of winning)
Griefing requirements: For a 10,000 SOL pot scenario:
- Attacker must block all 16 shards for 3 full minutes (~450 slots)
- With 2-slot cooldown, attacker needs 225 wallets per shard (3,600 total wallets)
- Must outbid ALL legitimate players for EVERY slot
The impossible economics:
When pot = 10,000 SOL:
- Legitimate player EV for winning = 10,000 SOL
- Rational priority spend = up to 50+ SOL to secure one click
- Attacker must outbid this on 16 shards × 450 slots = 7,200 transactions
- Attacker cost: >50 SOL × 7,200 = 360,000+ SOL minimum
- Attacker profit: 0 SOL (pure loss)
Why the attacker always loses:
- Positive vs negative EV: Legitimate players have +EV (can win pot), attacker has -EV (guaranteed loss)
- Escalating defender motivation: As pot grows, defenders rationally spend more on priority fees
- Wallet management overhead: Managing 3,600 wallets under time pressure is operationally infeasible
- Capital efficiency: Defenders need 1/7,200th the capital of an attacker
The design creates a beautiful economic trap: the more valuable the pot becomes, the more expensive it becomes to grief, while the more motivated legitimate players become to break through. The attacker is betting against players who have 10,000x upside while having 0x upside themselves.
State Cleanup Matters
Early versions leaked state. Shard accounts grew unbounded. Now:
- Fixed-size click records
- Circular buffer for recent clickers
- Shards reset after each game atomically
Memory efficiency translates ensures compute efficiency and 100% scalability.