PERMISSIONLESS
ON-CHAIN LOTTERY
The most transparent lottery protocol ever built. Verifiable randomness, permissionless deployment, and provably fair winner selection — all on-chain.
WHITEPAPER
A fully on-chain, permissionless lottery protocol with verifiable randomness, elastic tokenomics, and provably fair outcomes.
Elastic Tokenomics
Target supply is always 1B SORS. When the RewardsPool can't cover cashback, the factory mints the shortfall. Future creation fees burn this surplus first, returning supply to 1B.
- 200M initial RewardsPool (cashback source)
- 160M Treasury ops (Gnosis Safe)
- 250M Reserve (1yr cliff + 2yr linear)
- 150M Team (PinkSale lock)
- 80M Marketing/KOL vesting contract
- 60M Investor presale + 40M Operator presale
- 60M DEX liquidity provision
Unbiasable Randomness
Two independent entropy sources ensure no single party — not the operator, not the protocol, not any validator — can predict or influence winners.
- Rolling hash: keccak256(hash, buyer, block.number, prevrandao, ticketId)
- drand evmnet: Threshold BLS signatures from decentralized nodes
- Target round set 250 rounds (~750s) ahead — cannot be cherry-picked
- Final seed: keccak256(rollingHashSnapshot, sha256(drandSignature))
Permissionless Operators
Anyone with 1,000 SORS can stake, register a unique name, and deploy lottery rounds. Operators earn commissions on ticket sales but face slashing if they miss deadlines.
- MIN_OPERATOR_STAKE: 1,000 SORS
- Commission: Up to 30% (3000 bps) of prize pot
- Custom ticket currency (any ERC-20) and duration
- Up to 20 winner slots with configurable percentages
Slashing & Safety
Economic security through staking. Operators lose 5% of their stake for missing close (24h) or drand submission (48h) deadlines. Unstaking requires a 7-day cooldown.
- 5% slash for late close or missing beacon
- 7-day unstake delay; blocked if active rounds exist
- Emergency refund after 60 days (pull-payment model)
- Reentrancy guards on all critical functions
Economic Model
Creation Fee Formula
Operators pay a dynamic fee in SORS to create rounds. The fee decreases as the RewardsPool fills up and as the operator's stake increases.
poolFillRatio = min(poolBalance / 200M, 1.0)
baseFee = 10,000 SORS × (1 − poolFillRatio)
stakeRatio = operatorStake / SORS.totalSupply()
finalFee = baseFee × (1 − stakeRatio)- • Pool full (200M+): Creation is free
- • Pool empty: Base fee = 10,000 SORS
- • High-stake operators pay significantly less
- • Fees burn mintedSurplus first, then go to RewardsPool
Owner Cut (Protocol Revenue)
The protocol takes a percentage of the operator's commission. This percentage slides based on the operator's share of total staked SORS.
operatorShare = operatorStake / totalStaked
ownerBps = 5000 − 4500 × operatorShare
ownerCut = operatorTake × ownerBps / 10000
operatorNet = operatorTake − ownerCutCashback Mechanics
How Cashback Works
Players earn SORS cashback on every ticket they purchase. The cashback rate depends on how full the RewardsPool is.
cashbackRate = poolFillRatio × 5% (max 5%)
cashbackPool = operatorStakeSnapshot × cashbackRate
cashbackPerTicket = cashbackPool / totalCappedTickets- • Each wallet earns cashback on up to 10 tickets per round
- • Cashback is reserved at settlement to prevent over-commitment
- • If pool is insufficient, factory mints shortfall into pool
- • Unclaimed cashback after 180 days returns to pool
RewardsPool Dynamics
The RewardsPool starts with 200M SORS at genesis. It is funded by creation fees and depleted by cashback claims.
- +Inflows: Initial 200M genesis allocation, creation fees (after surplus burn), slashed operator stakes
- −Outflows: Player cashback claims
- ↻Self-correcting: When pool can't cover cashback, factory mints shortfall. Next creation fees burn it back to 1B supply.
fillRatio = min(balance / 200M, 1.0)How Winners Are Chosen
Ticket Sales Close
Rolling hash snapshot locked. Target drand round calculated as current + 250 (~750s ahead).
drand Beacon Submitted
BLS signature verified on-chain via BN254 pairing. Randomness derived as sha256(signature).
Fisher-Yates Selection
Final seed = keccak256(rollingHash, drandRandomness). Winners selected via bitmap-deduplicated indexing.
Decentralization Guarantees
- ✓Permissionless operators: Anyone can stake 1,000 SORS and launch rounds. No whitelist, no approval needed.
- ✓drand network: Threshold BLS from independent nodes. No single party controls randomness output.
- ✓EIP-1167 clones: Each round is an independent contract. No central point of failure or fund commingling.
- ✓Governance timelock: 48h proposal window for template, verifier, and treasury updates. Community can react.
- ✓Treasury multisig: 2-of-3 Gnosis Safe controls protocol parameters. No single-key admin access.
- ✓Entropy chain continuity: Previous round's finalSeed seeds the next round. Cross-round entropy linkage.
Safety Mechanisms
- ✓Operator slashing: 5% penalty for missing close (24h grace) or drand submission (48h deadline). Each fires once per round.
- ✓Emergency refund: After 60 days stuck, anyone triggers. Ticket holders claim refunds individually via pull-payment.
- ✓Pull-payment claims: All payouts (prizes, cashback, operator/treasury cuts) are self-serve. No push-based vulnerabilities.
- ✓Reentrancy guards: All critical functions (buy, claim, settle) protected against reentrant calls.
- ✓Immutable round params: Once initialize() is called, operators cannot change ticket price, duration, or winner percentages.
- ✓Under-subscribed protection: If fewer tickets than winner slots, revenue redistributes proportionally among filled slots. No funds locked.
- ✓Cashback reservation: At settlement, pool balance is locked to prevent over-commitment across concurrent rounds.
TECHNOLOGY
Built with Foundry and Solidity 0.8.24. Deployed on Polygon PoS for low-cost, high-speed transactions. Paris EVM (pre-PUSH0) for compatibility.
EIP-1167 Minimal Proxies
Each round is an independent clone sharing the template's bytecode but with isolated storage. The factory deploys via `clone()` and tracks all instances in a registry array. Clones cannot interfere with each other's state.
Entropy Chain Continuity
Previous round's finalSeed seeds the next round's rolling hash at initialization. This creates cross-round entropy linkage: `rollingHash = keccak256(previousFinalSeed, block.prevrandao, block.number, address(this))`.
Pull-Payment Model
All payouts are self-serve. Prizes, cashback, operator cuts, and treasury cuts sit in the round contract until claimed. This eliminates push-based reentrancy risks and gas griefing attacks.
Timelock Governance
Sensitive updates (template, drand verifier, treasury) use a 48h proposal → apply pattern. Community can monitor proposals and react before they take effect. Ownership transfer is two-step: transferOwnership → acceptOwnership.
Slashing Deadlines
Operators have 24h grace after roundEndTime to close (or anyone slashes them). They have 48h after close to submit drand beacon (or anyone slashes them). Each slash fires at most once per round via boolean guards.
Emergency Refund
If a round is stuck (no drand submission, settlement failure), anyone can trigger emergencyRefund() after 60 days. Ticket holders then claim refunds individually via claimEmergencyRefund(). Round goes DEAD, activeRounds decremented.
Core Contracts
LotteryFactory
Central hub managing operator staking, EIP-1167 clone deployment, fee routing, slashing enforcement, and seed chain continuity.
- Operator registry with unique names (bytes32)
- Stake tracking with MIN_OPERATOR_STAKE = 1,000 SORS
- Unstake queue with 7-day cooldown
- Creation fee calculation: poolFillRatio × stakeRatio
- EIP-1167 minimal proxy deployment per round
- Cross-round entropy: lastSeedByOperator mapping
- Timelock governance: 48h proposal → apply pattern
PolysorsLottery
Round template deployed as EIP-1167 clones. Full lifecycle: OPEN → CLOSED → SETTLED/DEAD with pull-payment claims.
- Immutable parameters after initialize() call
- Rolling hash accumulation per ticket purchase
- drand beacon verification via BN254 pairing
- Fisher-Yates winner selection with bitmap deduplication
- Pull-payment prize/cashback/operator-cut claims
- Emergency refund after 60 days (two-step process)
- ReentrancyGuard on all state-changing functions
SORSToken
ERC-20 with elastic supply targeting 1B. Factory-only mint (cashback shortfalls) and burn (fee surplus).
- Genesis supply: 1,000,000,000 SORS (1B)
- 200M allocated to RewardsPool at deploy
- mint(to, amount) — callable only by LotteryFactory
- burn(amount) — factory burns from its own balance
- setFactory(addr) — one-time irreversible wiring
- Supply self-corrects to 1B via mint/burn cycles
RewardsPool
Holds 200M SORS for cashback. Tracks reserved vs available balances. Pool fullness drives creation fees.
- GENESIS_BALANCE = 200,000,000e18
- getPoolFillRatio() returns min(balance/200M, 1.0) in 1e18
- reserveCashback(amount) — locks balance at settlement
- distributeCashback(to, amount) — releases reservation
- releaseCashback(amount) — frees unclaimed reservations after 180 days
- availableForCashback() = balance − reserved
DrandVerifier
BN254 BLS signature verification for drand evmnet beacons. Public key stored as immutables for zero-SLOAD efficiency.
- evmnet chain: bls-bn254-unchained-on-g1
- Public key: 4 uint256 constants (X0, X1, Y0, Y1)
- verifyBeaconSignature(round, signature) — 64-byte BLS G1 sig
- Message: H2C(DST, keccak256(abi.encodePacked(uint64(round))))
- Randomness derived on-chain as sha256(signature)
- Zero storage reads per verification (immutables)
MarketingVesting
KOL/marketing token vesting with custom cliff + linear schedules per recipient. Revocable by owner.
- 80M SORS allocated for marketing/KOL distribution
- addRecipient(addr, amount, cliffDays, vestingDays)
- Per-recipient vesting: cliffEnd = startTime + cliffDuration
- Linear unlock after cliff: amount × elapsed / vestingDuration
- revoke(addr) — caps vesting at revocation time
- withdrawUnallocated() — owner pulls unused tokens
Randomness Deep Dive
Rolling Hash Accumulation
Every ticket purchase contributes entropy to a rolling hash. This hash accumulates throughout the ticket sale window, making it impossible for any party to predict the final outcome.
// Per ticket purchase:
rollingHash = keccak256(
rollingHash,
buyer,
block.number,
block.prevrandao,
ticketId
)
// At initialization (seed chain):
rollingHash = keccak256(
previousFinalSeed,
block.prevrandao,
block.number,
address(this)
)Snapshot locked at close — no further manipulation possible. Previous round's finalSeed ensures cross-round entropy continuity.
drand Beacon Verification
The drand evmnet network produces threshold BLS signatures every 3 seconds. The contract verifies these on-chain using BN254 pairing checks against an immutable public key.
// Target round calculation:
currentRound = (closeTime - 1727521075) / 3
targetRound = currentRound + 250
// Final seed derivation:
finalSeed = keccak256(
rollingHashSnapshot,
sha256(drandSignature)
)
// Winner selection:
ticketIndex = uint256(
keccak256(finalSeed, i)
) % totalTicketsPublic key stored as 4 immutables — zero SLOADs per verification. Signature must match exact targetDrandRound — cherry-picking is impossible.
Access Control Matrix
| Function | Who Can Call | Conditions |
|---|---|---|
| createRound() | Registered operator | Stake ≥ 1,000 SORS, no pending unstake |
| buyTicket(buyer) | buyer only | msg.sender == buyer, status OPEN |
| closeRound() | Operator (24h) / Anyone (after) | Time expired or tickets full |
| submitDrandBeacon() | Operator (48h) / Anyone (after) | Exact targetDrandRound match |
| claimPrize(idx) | Winner at that index | Status SETTLED |
| claimCashback() | Any ticket holder | Within 180 days of settlement |
| emergencyRefund() | Anyone | 60 days after close/end |
| applyTemplate() | Owner only | 48h after proposeTemplate() |
ROADMAP
From token launch to multi-chain expansion — our journey to redefine on-chain gaming.
Token Launch & Presale
- PinkSale presale listing for early supporters
- Operator presale allocation (40M SORS)
- Investor presale allocation (60M SORS)
- DEX liquidity pool initialization (60M SORS)
- Initial operator onboarding and staking
- First lottery rounds go live on mainnet
Polysors Hub Launch
- Unified dashboard for all active lottery rounds
- Operator leaderboard with stake rankings
- Real-time ticket sales tracking
- Live drand beacon submission monitoring
- Winner announcement feed
- Historical round analytics and statistics
- Cashback claim notifications
Protocol Expansion
- Multi-chain deployment (Arbitrum, BSC)
- Advanced lottery templates (jackpot rollovers, instant wins)
- NFT ticket integration for collectible entries
- Cross-round jackpot pooling
- Mobile app beta launch
- Community governance token utilities
- Partnership integrations with DeFi protocols
The Polysors Hub
The Polysors Hub is your central command center for everything happening in the protocol. Think of it as the mission control for the entire lottery ecosystem — where operators manage their rounds, players discover active games, and everyone can verify outcomes in real-time.
For Players
Browse active rounds, track your tickets, claim prizes and cashback instantly.
For Operators
Monitor your rounds, track commissions, manage stake, and optimize performance.
For Everyone
Full transparency into every round's lifecycle, from creation to settlement.