Skip to main content
When a SolBid game ends, the prize pool is automatically distributed according to a fair formula that rewards both the final winner and early bidders who contributed to building the prize pool.

Prize pool composition

The total prize pool accumulates throughout the game:
Prize pool = Sum of all bids
Each bid adds to the prize pool:
game_state.prize_pool += bid_amount;
Code reference: place_bid.rs:120

Example prize pool growth

Starting with a 0.014 SOL initial bid:
Bid #AmountPrize Pool
10.014 SOL0.014 SOL
20.028 SOL0.042 SOL
30.056 SOL0.098 SOL
40.112 SOL0.210 SOL
50.224 SOL0.434 SOL
60.448 SOL0.882 SOL
70.896 SOL1.778 SOL
The prize pool grows exponentially as each bid is 2x the previous.

Distribution scenarios

There are two different distribution scenarios depending on the number of bids:

Scenario 1: Less than 5 bids (early game end)

If the game ends with fewer than 5 total bids, the distribution is simplified:
1

Calculate platform fee

Platform fee = Total prize pool × 10%
2

Calculate winner prize

Winner receives = Total prize pool - Platform fee (90% of total)
3

Transfer funds

The platform fee goes to the platform account, and the remaining 90% goes entirely to the last bidder.
Code reference: place_bid.rs:166-196
if bid_history.len() < 5 {
  let total_amount = game_state.prize_pool;
  let platform_fee = total_amount * 10 / 100;
  let remaining_prize = total_amount - platform_fee;
  // Winner receives remaining_prize (90%)
}
In this scenario, there’s no royalty distribution to earlier bidders. The winner receives 90% of the entire prize pool.

Scenario 2: 5 or more bids (standard distribution)

When the game has 5 or more bids, the distribution includes royalties:
1

Calculate platform fee from last 5 bids

Platform fee = (Sum of last 5 bids) × Platform fee percentageThe platform fee percentage is configurable and set to 10% by default.
2

Determine royalty pool

Royalty pool = 4th bid from the endThis specific bid amount becomes the total royalty to distribute among eligible bidders.
3

Calculate and distribute royalties

All bidders except the last 5 receive weighted royalties based on their bid amount and position.
4

Winner receives the remainder

Winner = Remaining balance after platform fee and royalties are paid
Code reference: place_bid.rs:198-236
let last_5_bids: Vec<u64> = bid_history.iter().rev().take(5).map(|bid| bid.amount).collect();
let platform_fee = last_5_bids.iter().sum::<u64>() * game_state.platform_fee_percentage / 100;

let royalty_amount = bid_history[bid_history.len() - 4].amount;

Platform fee calculation

The platform fee structure differs based on game size:

For games with < 5 bids

Platform fee = Total prize pool × 10%

For games with ≥ 5 bids

Platform fee = (Last 5 bids sum) × Platform fee percentage
The platform fee percentage is set when the game is created:
platform_fee_percentage: 10  // 10% default
Code reference: create_game.rs:114
The platform fee is always deducted first before any other distributions. This ensures the platform’s operational costs are covered regardless of the game outcome.

Royalty distribution system

Eligibility

To receive royalties, you must:
  • ✅ Have placed a bid before the last 5 bids
  • ✅ Be part of a game with at least 6 total bids
  • ✅ Wait for the game to end
The last 5 bidders (including the winner) do NOT receive royalties. Only earlier bidders are eligible.

Weighted formula

Royalties are distributed using a weighted formula that considers both:
  1. Position weight: Earlier bids get higher weights
  2. Bid amount: Larger bids get proportionally more royalty
The formula:
Weight = (Total eligible bidders - Bidder index)
Share = Weight × Bid amount
Royalty share = (Share × Royalty pool) / (Total weight × Total bid amount)
Code reference: place_bid.rs:238-278
let eligible_bidders = &bid_history[0..bid_history.len() - 5];

for (i, bid) in eligible_bidders.iter().enumerate() {
  let weight = total_bidders as u64 - i as u64;
  let share = (weight * bid.amount) as u64;
  let royalty_share = (share * royalty_amount) / (total_weight * total_bid_amount);
  let total_share = bid.amount + royalty_share;
  // Transfer total_share back to bidder
}

Royalty calculation example

Game scenario: 8 total bids
Bid #BidderAmountStatus
1Alice0.014 SOLEligible for royalty
2Bob0.028 SOLEligible for royalty
3Carol0.056 SOLEligible for royalty
4Dave0.112 SOLRoyalty pool amount
5Eve0.224 SOLLast 5 - No royalty
6Frank0.448 SOLLast 5 - No royalty
7Grace0.896 SOLLast 5 - No royalty
8Henry1.792 SOLWinner - No royalty
Total prize pool: 3.570 SOLLast 5 bids sum: 0.112 + 0.224 + 0.448 + 0.896 + 1.792 = 3.472 SOLPlatform fee: 3.472 × 10% = 0.3472 SOLRoyalty pool: 0.112 SOL (from Dave’s bid)Eligible bidders: Alice, Bob, Carol (first 3 bidders)Weight calculation:
  • Alice (index 0): weight = 3 - 0 = 3
  • Bob (index 1): weight = 3 - 1 = 2
  • Carol (index 2): weight = 3 - 2 = 1
  • Total weight = 3 + 2 + 1 = 6
Total bid amount (eligible): 0.014 + 0.028 + 0.056 = 0.098 SOLRoyalty shares:
  • Alice: (3 × 0.014 × 0.112) / (6 × 0.098) = 0.0080 SOL
  • Bob: (2 × 0.028 × 0.112) / (6 × 0.098) = 0.0107 SOL
  • Carol: (1 × 0.056 × 0.112) / (6 × 0.098) = 0.0107 SOL
Total returned to eligible bidders (bid + royalty):
  • Alice: 0.014 + 0.0080 = 0.0220 SOL
  • Bob: 0.028 + 0.0107 = 0.0387 SOL
  • Carol: 0.056 + 0.0107 = 0.0667 SOL
Winner receives: Remaining balance after all deductions

Winner payout

The winner (last bidder) receives:
Winner payout = Prize pool - Platform fee - Total royalties
The winner’s payout is calculated as the remaining balance:
let current_balance = **game_account.try_borrow_lamports()?;
let rent_exempt_balance = rent.minimum_balance(game_account.data_len());
let amount = current_balance.checked_sub(rent_exempt_balance)?;

transfer_from_pda(game_account, winner_account, amount)?;
Code reference: place_bid.rs:219-225
The smart contract ensures the game account maintains rent-exempt balance. The winner receives everything above this minimum threshold.

Player state updates

When distribution occurs, each player’s state is updated:

For royalty recipients

player_state.safe = true;  // Marked as paid out
player_state.royalty_earned += royalty_share;  // Tracks earnings

For the winner

player_state.safe = true;  // Marked as paid out
player_state.royalty_earned += amount;  // Tracks total winnings
Code reference: place_bid.rs:227-230 and place_bid.rs:269-273
The safe flag indicates a player has been successfully paid out. This prevents double-payouts and serves as a permanent on-chain record.

Distribution flow diagram

Security guarantees

Atomic distribution

All distributions happen atomically within a single transaction:
  • ✅ Platform fee transfer
  • ✅ Royalty transfers to all eligible bidders
  • ✅ Winner transfer
  • ✅ Player state updates
If any step fails, the entire transaction reverts.

Rent exemption

The smart contract ensures the game account maintains rent-exempt balance:
let rent_exempt_balance = rent.minimum_balance(game_account.data_len());
let amount = current_balance.checked_sub(rent_exempt_balance)?;
This prevents the account from being deleted due to insufficient rent.

Overflow protection

All calculations use checked arithmetic:
let amount = current_balance.checked_sub(rent_exempt_balance)
  .ok_or(BiddingError::InsufficientFunds)?;
The smart contract will reject any distribution that would result in arithmetic overflow or underflow, protecting all players from potential exploits.

Viewing your earnings

You can check your earnings through:

On-chain player state

Query your player account to see:
const playerState = await getAllPlayersAndBids(gameId, yourPublicKey, bidCount);

console.log(playerState.royalty_earned);  // In lamports
console.log(playerState.safe);  // true if paid out

Game history

All distributions are recorded on-chain and visible in:
  • Transaction history on Solana explorers
  • Game account state changes
  • Player account balance changes

Next steps

How to play

Learn the complete gameplay flow

Bidding rules

Understand the bidding mechanics and requirements

Build docs developers (and LLMs) love