Raffle Protocol Technical Details

The raffle uses deterministic, verifiable random selection. Everyone can reproduce the same winner list from the same inputs.

Tickets are based on your wallet holdings at the September 1, 2025 snapshot.

System Flow

Snapshot Inputs

Wallet asset snapshot + active multipliers

Ticket Pool

Each signup materializes a deterministic ticket list

Public Seed

Draw seed comes from a published block hash

Winners

Unique winner tickets are selected and stored by rank

1) Ticket Calculation

For each wallet, assets are grouped by type. Every type uses an active multiplier. Fractional results use round-half-up via round.

function buildTicketBreakdown(assets, multipliersByType) {
  const countByType = countAssetsByType(assets)
  const rows = []

  for each (type, count) in countByType {
    multiplier = multipliersByType[type]
    rawTickets = count * multiplier
    ticketsInt = round(rawTickets)
    rows.push({ type, count, multiplier, rawTickets, ticketsInt })
  }

  totalTickets = sum(rows.ticketsInt)
  return { rows, totalTickets }
}

2) Ticket Materialization

The wallet signature is hashed, then combined with each ticket index. Every ticket value is a deterministic keccak256 hash.

function materializeTickets(signature, totalTickets) {
  signatureHash = keccak256(signature)
  tickets = []

  for (index = 0; index < totalTickets; index += 1) {
    indexHex = pad32(toHex(index))
    ticketSeed = concat(signatureHash, indexHex)
    tickets.push(keccak256(ticketSeed))
  }

  return tickets
}

3) Winner Draw

The draw takes a published seed. Candidates are sampled by modulo index. Duplicate indexes are skipped to ensure unique winners.

function drawWinners(tickets, winnerCount, seed) {
  selectedIndexes = set()
  winners = []
  cursor = 0

  while (winners.length < winnerCount) {
    value = nextUint(seed, cursor)
    index = value mod tickets.length
    cursor += 1

    if (selectedIndexes.has(index)) continue
    selectedIndexes.add(index)
    winners.push(tickets[index])
  }

  return winners
}

4) Verification

Anyone can re-run the same algorithm with the published draw metadata and compare regenerated winners against stored results.

function verifyDraw(storedWinners, currentTickets, drawMeta) {
  regenerated = drawWinners(
    currentTickets,
    drawMeta.winnerCount,
    drawMeta.seed
  )

  return storedWinners == regenerated
}