Adoption Bonus & Cohorts

The adoption-bonus pool — also called the "virality bonus" — rewards early adopters when later cohorts of players join. It is funded by viralityBonusProportion of every entry, and it is paid out per-cohort in a way that creates a viral incentive to recruit new players.

Cohort assignment

Each entrant ID k (1-indexed) has a cohort membership (used for claim eligibility) of:

cohort(k) = floor(log_{B_v}(k)) + 1

where B_v is viralityBonusLogBase. Cohort sizes grow geometrically.

Note the distinction between membership and pool funding: the contribution from an entry is routed into a pool indexed by max(2, floor(log_{B_v}(k)) + 1) (see Pool routing below). As a result Pool[1] is always empty — cohort-1 members exist for eligibility, but their contributions are redirected into Pool[2], which therefore spans entrant IDs 1 to B_v² - 1.

Cohort d Entrant ID range Size
1 1 to B_v - 1 B_v - 1
2 B_v to B_v² - 1 B_v² - B_v
3 B_v² to B_v³ - 1 B_v³ - B_v²
d B_v^(d-1) to B_v^d - 1 B_v^d - B_v^(d-1)

Example: B_v = 3

Cohort IDs Size
1 1–2 2
2 3–8 6
3 9–26 18
4 27–80 54
5 81–242 162
6 243–728 486

Example: B_v = 10

Cohort IDs Size
1 1–9 9
2 10–99 90
3 100–999 900
4 1000–9999 9000

Pool routing

Each ticket contributes v = entryAmount * p_v / (p_g + p_v) wei (post fee) into a cohort pool. The routing rule is:

c = cohort(k)

if c == 1:  contribution → Pool[2]   (cohort 1 redirects forward)
if c >= 2:  contribution → Pool[c]

Why the redirect? Cohort 1 has no earlier cohort to reward. Sending its contributions to Pool[2] ensures cohort 1 itself gets paid when cohort 3 arrives and unlocks Pool[2].

Pool unlocking and claimability

A cohort's pool unlocks for claims the moment the next cohort starts:

Pool[j] is claimable  ⇔  C >= j + 1

where C = floor(log_{B_v}(N)) + 1 is the current max cohort

The claim distributes Pool[j] equally among the j-1 earlier cohorts ("teams"), and each team's share is then split equally among its members:

team_prize(j)        = floor(Pool[j] / (j - 1))
individual_prize(d, j) = floor(team_prize(j) / size(d))

What this looks like in practice

With B_v = 3 and N = 50:

Pool Funded by Balance Claimable? Goes to
Pool[2] IDs 1–8 8 * v ✅ (C=4 > 2) Cohort 1
Pool[3] IDs 9–26 18 * v ✅ (C=4 > 3) Cohorts 1, 2
Pool[4] IDs 27–50 24 * v ❌ (C=4 is the last cohort) Host leftover

The earlier you enter, the more pools you eventually claim from — every later cohort funds a slice of every earlier cohort's payout.

Leftover handling

The last cohort's pool is never unlocked for participant claims (no later cohort exists to trigger it). After the game ends the host calls clearLeftoverAdoptionBonus(gameId) to sweep Pool[C] to themselves. This is a one-time action per game.

The larger a game grows beyond a cohort boundary, the more this leftover accumulates — it functions as a built-in operator-revenue mechanism on top of the deposit fee.

Why this is "viral"

Each cohort can only earn from cohorts after it. So each member has a direct incentive to attract more entrants — it grows the next cohort, unlocks their current pool, and grows the future pools they'll later claim from.

Two claim paths

There are two functions for claiming adoption bonuses, designed for different recipient gas profiles:

Path Function Gas to recipient DOS-resistant
Batch (default) batchClaimAdoptionBonusPrize 30,000-gas cap on .call ✅ failed transfers don't revert the batch
Single-claim claimAdoptionBonusPrize full forwarding reverts on transfer failure

EOAs and standard smart wallets should use the batch path. Wallets with heavy receive/fallback logic that need more than 30k gas should use the single-claim path.

See Public Interface for both signatures.

results matching ""

    No results matching ""