Seasons & Leagues
Time-bounded competitive periods with tiered skill brackets.
What Are Seasons?
A season is a time-bounded competitive period during which players compete for rankings, rewards, and achievements. When a season ends:
- Final standings are calculated
- Prize distributions are triggered
- Promotion/relegation between leagues is applied
- Achievement NFTs are minted for qualifying players
Seasons are the primary economic cycle of a competitive game.
What Are Leagues?
Leagues are skill tiers within a season. They:
- Segment the player population by skill level
- Determine matchmaking brackets
- Define separate prize pools and reward tiers
- Apply promotion and relegation at season boundaries
Season 1
├── League: Diamond
│ └── No promotion (top league)
│ └── Bottom 10% relegated to Gold
├── League: Gold
│ ├── Top 10% promoted to Diamond
│ └── Bottom 20% relegated to Silver
├── League: Silver
│ ├── Top 20% promoted to Gold
│ └── Bottom 20% relegated to Bronze
└── League: Bronze
└── Top 20% promoted to Silver
Season Configuration
{
season_id: 1,
name: "Season of Smoke",
start: "2026-04-01T00:00:00Z",
end: "2026-06-30T23:59:59Z",
leagues: [
{
id: "diamond",
name: "Diamond",
min_elo: 2000,
promotion_percent: 0, // top league
relegation_percent: 10,
reward_multiplier: 4.0
},
{
id: "gold",
name: "Gold",
min_elo: 1600,
promotion_percent: 10,
relegation_percent: 20,
reward_multiplier: 2.5
},
{
id: "silver",
name: "Silver",
min_elo: 1200,
promotion_percent: 20,
relegation_percent: 20,
reward_multiplier: 1.5
},
{
id: "bronze",
name: "Bronze",
min_elo: 0,
promotion_percent: 20,
relegation_percent: 0, // bottom league
reward_multiplier: 1.0
}
],
reward_pool: {
gameplay_token_total: 100000, // total gameplay currency per season
settlement_token_total: 0, // optional settlement currency pool
distribution: "proportional_rank" // or "tier_fixed", "winner_take_all"
}
}
All parameters are per-game. A casual puzzle game might have 2 leagues and a 2-week season. A hardcore competitive game might have 6 leagues and a 3-month season.
Season Lifecycle
┌───────────┐ ┌───────────┐ ┌───────────┐ ┌───────────┐
│ CONFIGURE │────►│ ACTIVE │────►│ LOCKING │────►│ FINALIZED │
│ │ │ │ │ │ │ │
│ leagues │ │ matches │ │ results │ │ rewards │
│ defined │ │ played │ │ locked │ │ claimable │
│ │ │ standings │ │ results │ │ promos / │
│ │ │ updated │ │ submitted │ │ relegation│
└───────────┘ └───────────┘ └───────────┘ └───────────┘
States
| State | Description |
|---|---|
| Configure | Game operator sets season parameters, league definitions, reward pools |
| Active | Players compete. Matches are played. Standings updated in real-time |
| Locking | Season deadline passed. No new matches accepted. Final standings computed |
| Finalized | Results submitted on-chain. Prizes claimable. Promotions/relegations applied |
On-Chain Settlement
Only the finalization step touches the blockchain:
struct SeasonResult {
uint256 seasonId;
address[] players; // ordered by rank
uint256[] rewards; // gameplay currency per player
uint8[] leagues; // league assignment for next season
uint256 timestamp; // finalization time
}
The game server signs the result payload, and either:
- Server submits the transaction directly (server pays gas)
- Players claim individually using a signed attestation (players pay gas)
Promotion & Relegation
At season end, the server applies league transitions:
function applyPromotionRelegation(season, standings) {
for (const league of season.leagues) {
const players = standings.filter(p => p.league === league.id);
const sorted = players.sort((a, b) => b.elo - a.elo);
const total = sorted.length;
// Promote top N%
const promoteCount = Math.floor(total * league.promotion_percent / 100);
sorted.slice(0, promoteCount).forEach(p => p.next_league = league.next_up);
// Relegate bottom N%
const relegateCount = Math.floor(total * league.relegation_percent / 100);
sorted.slice(total - relegateCount).forEach(p => p.next_league = league.next_down);
// Everyone else stays
sorted.slice(promoteCount, total - relegateCount).forEach(p => p.next_league = league.id);
}
}
Integration Guide
For Your Game
- Define leagues — how many tiers, what ELO/rating ranges
- Set season duration — weeks or months, based on your game's pace
- Configure rewards — total pool per season, distribution model
- Implement matchmaking — pair players within the same league tier
- Track results — server records wins/losses and updates standings
- Finalize on-chain — submit results when season ends, enable claims
- Apply transitions — promote/relegate and start next season