Polygon mainnet ERC-721 tickets Permissionless progression Chainlink → Supra → blockhash

Windfall Lotto Documentation

Windfall Lotto is a weekly, token-denominated lottery protocol centered on transferable ticket NFTs, deterministic jackpot accounting, public draw progression, and a donor-powered fee-sharing layer. The verified deployed contract keeps gameplay logic in the core lottery contract while delegating ticket rendering, archival draw NFTs, and shareholder accounting to dedicated companion contracts.

Current deployed logic. The verified mainnet version uses a streak-based, order-sensitive matching model, a fixed ticket price of 1e18 token units, a 10% host fee, a 10% transferred-winner royalty to the original minter, and a normal jackpot distribution ladder of 80% / 20% / 5% for tiers 5 / 4 / 3.
1 token
Fixed ticket price in 18-decimal units
50
Maximum tickets per batch purchase
500
Maximum tickets processed per count call
1B
Windfall trigger in whole tokens

Deployment Reference

Reference metadata for the verified contract instance this docs page is aligned to.

ContractWindfallLotto

Verified exact match on PolygonScan.

Address0x9650D206c6e0093FBc1D623b2A1e03984D24d3f1

Main deployed contract documented here.

CompilerSolidity v0.8.31

Optimizer enabled.

Optimizer200 runs

Default EVM version shown on verification page.

Decentralization Concept

Windfall Lotto is not ownerless, but the operational path for each draw is intentionally public.

What is permissionless

  • Anyone can close a finished draw and request randomness through the next valid source.
  • Anyone can push the fallback chain forward when a randomness source times out.
  • Anyone can process ticket batches and anyone can finalize the active winning tier.
  • Winners claim directly from the contract without host mediation.
  • Anyone can open the next draw once the current one is in COUNTING_DONE.

What remains host-controlled

  • Deployment, initial dependency wiring, and the choice of immutable external addresses.
  • Bounded tuning of callback gas and confirmation parameters, restricted to the host treasury.
  • The child contracts are linked once through setLotto(address) by the host treasury.
  • The host treasury remains the permanent fee-share beneficiary and receives archival draw NFTs.

Protocol Architecture

The deployed system separates state, collectibles, fee sharing, and rendering into dedicated contracts.

WindfallLotto

Core draw state machine, jackpot accounting, randomness routing, batch counting, finalization, claims, and next-draw opening.

WindfallTicket

ERC721Enumerable ticket NFTs storing draw linkage, end time, original minter, packed numbers, and processed tier state.

WindfallDrawNFT

Archival draw-result NFTs minted in pairs: one to the host treasury and one to the lotto contract itself.

WindfallFeeShare

Tracks donor qualification, active shareholder expiry, host-fee claimable balances, and pruning of stale inactive entries.

WindfallSVG

Provides on-chain SVG generation used by NFT metadata layers.

Immutable wiring

The core contract stores immutable references to the payment token, ticket NFT, draw NFT, fee-share contract, Chainlink config, and Supra router wallet pair.

Tickets and Matching Logic

The deployed version uses order-sensitive, consecutive streak matching.

Ticket model

  • Each ticket contains five uint8 values packed into a bytes32.
  • Every value must stay in the 0–99 range.
  • Duplicates are allowed; there is no uniqueness rule across the five positions.
  • Position matters. This is not an unordered matching system.
  • The ticket also records its draw id, end timestamp, and original minter.

How _matchTier() works

The algorithm scans left to right and returns the longest exact-position consecutive streak.

Longest streakTier result
5 consecutive exact matchesTier 5
4 consecutive exact matchesTier 4
3 consecutive exact matchesTier 3
0, 1, or 2 matches as max streakNon-winner

Draw Lifecycle

The contract is designed so a draw can keep moving even if the host is offline.

1

Open

A new draw opens with its end time set to the next Friday at 23:00 UTC. Any remainder from the previous draw seeds the new jackpot.

2

Sales

Users buy one ticket or up to 50 in a batch. Ticket purchases mint ERC-721 tickets and split value between jackpot and host fee.

3

Donations

Users may donate directly to the jackpot. Donations do not pay a fee, but qualifying donations are forwarded to the fee-share logic for shareholder tracking.

4

Close and request randomness

After endTime, anyone can close the sale and request randomness through the current valid path.

5

Reveal

The draw stores a five-number winning array, records the randomness source used, and moves to REVEALED.

6

Count

Anyone can process tickets in batches of up to 500, increment tier counters, and push processed ticket metadata to the ticket NFT contract.

7

Finalize

Once all tickets are processed, anyone can lock the active tier, winner count, payout-per-winner, and remainder that will roll forward.

8

Claim

Winning owners claim individually. If a ticket was transferred, 10% of the claim routes to the original minter and 90% to the current owner.

9

Open next draw

The standard path mints draw NFTs and distributes stored host fees. An emergency light path exists that skips those side effects to prioritize liveness.

Randomness Pipeline

The deployed contract hardcodes a staged fallback model to reduce the chance of a stuck draw.

Stage 1 — Chainlink VRF v2.5

Primary source. A draw first moves from OPEN to CHAINLINK_REQUESTED and waits for the VRF callback.

Stage 2 — Supra

If Chainlink exceeds the configured timeout, the same public function can escalate the draw to SUPRA_REQUESTED.

Stage 3 — Blockhash

If Supra also times out, the contract arms a blockhash fallback with a target block delayed by seven blocks, then later reveals from that block hash.

Timeout and fallback notes

  • CHAINLINK_TIMEOUT = 1 hours
  • SUPRA_TIMEOUT = 2 hours
  • BLOCKHASH_DELAY = 7 blocks after arming
  • The draw state machine is OPEN → CHAINLINK_REQUESTED → SUPRA_REQUESTED → BLOCKHASH_PENDING → REVEALED → COUNTING_DONE.
  • RandomnessUsed emits the selected source and random word for auditability.

Jackpot, Tier Selection, and Windfall

The deployed version pays only one active tier per draw, with a special windfall override once the jackpot becomes very large.

ConditionTier 5 winners existTier 4 winners existTier 3 winners existActive winning tierDistributable share
Below windfall triggerYesIrrelevantIrrelevantTier 5 only80%
Below windfall triggerNoYesIrrelevantTier 4 only20%
Below windfall triggerNoNoYesTier 3 only5%
Below windfall triggerNoNoNoNo winning tier0%, full rollover
At or above windfall triggerYesAnyAnyTier 5 only80%
At or above windfall triggerNoYesAnyTier 4 only80%
At or above windfall triggerNoNoYesTier 3 only80%
Windfall override. Once jackpot >= WINDFALL_TRIGGER, the contract still selects the highest available tier in the normal 5 → 4 → 3 order, but the distributable jackpot share becomes 80% even when the active winning tier is only tier 4 or tier 3.

Payout locking and rollover

  • payoutPerWinner is locked at finalization time.
  • Any undistributed dust from integer division stays in remainder.
  • If no winning tier exists, the whole jackpot rolls to the next draw as remainder.
  • The normal ticket purchase split is 90% to the jackpot and 10% to host-fee accounting.

Shareholders Concept

Host fees do not go straight to the treasury. They feed a separate donor-share economy in WindfallFeeShare.

Becoming active

  • THE_MIN = 1000e18 sets the base minimum donation unit.
  • A donor is only stored once the incoming amount reaches the current save threshold.
  • The save threshold starts at one tenth of the active qualification threshold.
  • Once a donor accumulates at least the current qualification threshold, the donor becomes active for 365 days, plus proportional extension for any excess amount.
  • Later qualifying donations extend expiry proportionally when the donor is already active.

Lifecycle rules

  • Expired shareholders lose active status and their stored accumulation resets to zero.
  • If continuity breaks for more than 30 days while inactive, unfinished accumulation can reset.
  • Inactive entries can be pruned after 30 days from the last state change.
  • The shareholder registry is capped at MAX_SHAREHOLDERS = 200.
  • The host treasury is always treated as active on the fee-share side.
Active donor countQualification thresholdSave threshold
0 to 351,000 tokens100 tokens
36 to 7010,000 tokens1,000 tokens
71 to 105100,000 tokens10,000 tokens
106 to 1401,000,000 tokens100,000 tokens
141 to 17510,000,000 tokens1,000,000 tokens
176 and above100,000,000 tokens10,000,000 tokens

How host-fee distribution works

Each active donor gets one share. The host treasury receives ceil(activeDonors / 20) shares, with a minimum of one. Any remainder from integer division also goes to the host treasury.

activeDonors = count(active donor addresses) hostShares = max(1, ceil(activeDonors / 20)) activeShares = activeDonors + hostShares sharePerMember = distributedHostFee / activeShares

NFT Layer

Windfall Lotto uses NFTs both as player assets and as protocol memory.

Ticket NFTs

  • WindfallTicket is ERC721Enumerable and supports on-chain portfolio lookup.
  • Metadata is on-chain and includes SVG rendering.
  • Processed tier state is reflected in ticket appearance after counting.
  • Winning tier visuals are color-coded for tiers 5, 4, and 3.

Draw-result NFTs

  • The standard settlement path mints two draw NFTs per completed draw.
  • One result NFT goes to the host treasury and one to the lotto contract.
  • Each stores draw id, reveal timestamp, jackpot snapshot, packed winning numbers, active tier, and winner count.
  • The emergency light path skips draw-NFT minting.

Trust Model and Risks

The protocol narrows operational dependence, but still has bounded trust assumptions.

  • Users still trust the original deployment, immutable addresses, and correct one-time linking of helper contracts.
  • The host treasury can tune confirmation and callback parameters within bounded ranges, which can affect UX and liveness quality.
  • openNextDrawLight() preserves liveness but deliberately skips fee distribution and result-NFT minting for that transition.
  • The blockhash fallback improves recovery from oracle failure, but it is still a weaker fallback than the oracle paths.

Key Constants Snapshot

A compact reference for the deployed mainnet rules.

Constant / ruleValueMeaning
Ticket price1e18 base unitsOne whole token assuming 18 decimals
Max tickets per buy50Upper bound in buyTickets()
Max process batch500Upper bound for one counting call
Host fee on buys10%Accumulated and later distributed through WindfallFeeShare
Jackpot contribution on buys90%Added immediately to the current draw jackpot
Tier 5 share80%Normal distributable share when tier 5 is active
Tier 4 share20%Normal distributable share when tier 4 is active
Tier 3 share5%Normal distributable share when tier 3 is active
Windfall trigger1e27 base units1,000,000,000 whole tokens
Windfall payout share80%Applied to whichever tier becomes active once trigger is reached
Transferred winner royalty10%Redirected to the original minter when the winning ticket changed hands
ScheduleWeeklyDraw end targets next Friday at 23:00 UTC
Shareholder base minimum1000e18Base unit used by the fee-share qualification ladder
Shareholder duration365 daysBase active duration once threshold is reached
Max shareholders stored200Registry cap before pruning is attempted