BOR · Blockchain On-chain Raffle

National lotteries return about 50%. BOR returns 90%.

The 10% fee is a constant compiled into the contract. There is no function to change it, no proxy to swap the logic, and no path for anyone — including the admin — to move participant funds out. You do not have to take that on trust: it is readable on a block explorer.

Status
Testnet — Base Sepolia Not audited Expected value below ticket price
How a round works

Seven steps, and only one of them is a call

A round is a function of time. There is no procedure to open one and none to close one — so there is no procedure that can fail, stall, or be withheld.

1 · Buy a ticket

Fixed price, no cap on how many are sold. Ticket numbers are assigned in purchase order — there are no numbers to pick.

2 · Sales stop on schedule

The deadline closes sales because the time arrived, not because someone called a function.

3 · The beacon round is already set

The drand round that round will use was fixed at deployment. You can read the one a year out, today.

4 · Anyone submits the beacon

Once that drand round publishes, anyone may submit it. The contract verifies the BLS signature on-chain before it counts.

5 · Winners are derived

Winning numbers are keccak256(randomness, i) mod total tickets — one jackpot plus small prizes, all drawn from tickets that were actually sold.

6 · Claim by presenting your purchase

The winner claims against their own purchase record. There is no deadline to claim.

7 · Below the minimum, everyone is refunded

If a round does not reach its minimum ticket count, it refunds in full and each participant withdraws.

The only call is draw()

Settlement needs exactly one call. Because the deadline and the beacon round were both fixed in advance, the same tickets are drawn no matter who calls it or when.

The prize

90% of sales, paid out in full every round

Conventional lotteries hand back about half of what participants paid; the rest goes to operating organizations, retail networks and public funds. BOR has no retail network to pay, so the fee stays at 10% — and it stays there because it is a constant, not a policy.

Payout rate 90%Every settled round satisfies prize × 10 == sales × 9, exactly.
Jackpot is at least 80% of the prizeThe small-prize share is a 20% constant, so the top prize cannot fall below that line.
No ceilingThe pot is whatever the round sold, times 0.9. Nothing caps it.
No rolloverJackpot and small prizes both go out every round. Nothing is deferred to a later one.
Read the small prizes correctly
  • Small prizes pay 3× the ticket price, and they appear as a round grows — at 1,000 tickets, 60 of them.
  • More small winners does not make a ticket better. The expected value is 0.9 either way; the small share is carved out of the same 90%, not added to it.
  • Because there is no rollover, the expected value is the same whichever round you join. Splitting across several wallets gains nothing: halve your probability and the prize doubles, exactly cancelling out.
What you can check

Two claims anyone can copy, two they cannot

A payout rate and a prize ceiling are easy to announce. These two are different — they hold because of what the code does not contain, and that is visible from outside.

The fee cannot be moved

10% is a constant in the compiled contract. No setter exists to raise it, no proxy exists to replace the logic that reads it, and no path exists for anyone to withdraw participant funds. That is a property of the deployed bytecode, not a promise about future behaviour.

Future randomness is already assigned

Each future draw's drand round is computed from immutable values, so you can read the round a draw a year from now will use — today. It does not exist yet when sales close, which is precisely why the last buyer cannot see the result before buying.

The schedule cannot be shifted

No call sequence moves a deadline or a beacon round. Settlement is idempotent in the way that matters: whoever calls draw(), and whenever they call it, the same tickets win.

Verification

Not audited — here is what was done instead

No third party has audited this contract. Saying so is the first line of the section, not a footnote at the bottom. What follows is the self-testing that exists in its place, stated so you can reproduce it.

What was checkedResult
Test suite143 tests pass
Deep randomized runsFuzz 5,000 runs; invariants 512 runs × 128 depth — all pass
RFC 9380 conformanceOfficial vectors parsed straight out of the RFC (K.1, J.9.1) and passed — not transcribed by hand
Cross-implementation differentialThe RFC, noble-curves, py_ecc and the on-chain BLS.sol agree across 82 vectors
Symbolic executionhalmos proves the bounds, the schedule and purchase accounting over all inputs, not sampled ones
On-chain beacon verificationForged, tampered and round-mismatched signatures are all rejected
No funds can be strandedAfter random call sequences everyone withdraws and the balance reaches zero
Admin parameter spaceFuzzed — no combination of admin settings locks funds
Payout rate in settlementprize × 10 == sales × 9 holds in actual settlement, not just in the description

For developers: the contract verifies the drand quicknet beacon over BLS12-381 on EIP-2537 precompiles, at roughly 166k gas per verification, and exposes the chain hash and group public key it checks against — so you can compare them with what drand publishes without reading bytecode.

What this is not

The limits, in the same place as the claims

Marketing copy inflates when it is repeated. This list exists so the claims above stay attached to what is actually true.

Not an investment The expected value of a ticket is below what it costs. That is what a 90% payout rate means. Nothing here is a return, a yield, or a way to earn.
Not audited No third party has reviewed the contract, and the BLS verification code in particular has not been externally audited. The self-testing above is what stands in its place.
Not on mainnet BOR runs on the Base Sepolia testnet. It is there to collect feedback, not participants — no real money is at stake and none should be.
There is an admin An admin exists and adjusts economic parameters. What it cannot do is change the fee, replace the logic, or withdraw participant funds — that is the claim, and it is narrower than “nobody is running this”.
Not fully decentralized The contract has no operator functions, but it settles on a chain with a sequencer. Those are two different statements and only the first one is ours to make.
You do not pick numbers Ticket numbers are assigned in purchase order. Winners are derived from the beacon against tickets that were actually sold.