Provably Fair

Every outcome. Verifiable on-chain.

Oni Games doesn't ask you to trust us. Every wager, payout, and score is governed by open smart contracts on OneChain โ€” not our servers.

View ContractsHow to Verify โ†“๐Ÿ“„ Whitepaper โ†—
โšก
0%
On-Chain Logic
๐ŸŽฒ
0 servers
RNG Control
๐Ÿ”’
0 hidden fees
Transparency
๐Ÿ”—
โˆž
Audit History
Randomness

We don't control
the dice roll.

Instant games use OneChain's native one::random module โ€” a core blockchain primitive outside Oni Games' control. We cannot influence, predict, or change any result.

Seeded from on-chain context
Block hash, epoch data, and transaction digest seed every RNG call โ€” determined by the network, not Oni Games.
Generated after wager is locked
The outcome is computed after your bet is committed. Nobody knows the result in advance.
Validator manipulation is impossible
Unlike older chains, OneChain's RNG is generated alongside consensus โ€” validators cannot withhold blocks to influence results.
PTB Rollback Protection
All instant-play functions use the entry keyword โ€” not public. This prevents composing them in a Programmable Transaction Block, completely blocking the rollback exploit where a player sees the result and aborts if they lose.
oni_games::casinoplay_instant_wager()
1entry fun play_instant_wager(
2 bankroll: &mut HouseBankroll,
3 rng: &Random,
4 wager: Coin<OCT>,
5 guess: u64, game_range: u64,
6 multiplier_bps: u64,
7) {
8 // OneChain native RNG โ€” not our server
9 let mut generator =
10 random::new_generator(rng, ctx);
11
12 let result =
13 random::generate_u64_in_range(
14 &mut generator, 0, game_range - 1
15 );
16
17 let won = (result == guess);
18 // win/loss handled transparently below...
19}
๐Ÿ’ก The entry keyword makes this the terminal step of any transaction โ€” a player cannot inspect the result and abort.
Session Games

Two-step escrow.
Zero trust required.

Crash, Minesweeper, and High-Low use a cryptographically secure escrow model โ€” funds are locked on-chain before you play a single move.

Game Flow
1
lock_wager() on-chain
Your wager is deposited into HouseBankroll immediately. A SessionReceipt NFT proves your bet exists โ€” it cannot be backdated or forged.
2
Play the game
You play Crash, Minesweeper, or High-Low in the frontend. The backend tracks session state.
3
Backend signs the outcome
The server calculates your multiplier and signs a tamper-evident payload with its Ed25519 private key.
4
resolve_session() verifies on-chain
The contract verifies the Ed25519 signature before releasing any payout. Invalid signature = transaction aborted.
Signed Message Payload
1// BCS payload the server signs
2let mut msg = vector::empty<u8>();
3
4vector::append(&mut msg,
5 bcs::to_bytes(&session_id)); // unique ID
6vector::append(&mut msg,
7 bcs::to_bytes(&player)); // wallet locked
8vector::append(&mut msg,
9 bcs::to_bytes(&multiplier_bps)); // outcome
10vector::append(&mut msg,
11 bcs::to_bytes(&nonce)); // single-use
12
13assert!(ed25519::ed25519_verify(
14 &signature, &server_key, &msg
15), E_INVALID_SIGNATURE);
session_idUnique per game โ€” the server can't reuse a winning resolution
playerYour wallet is encoded โ€” nobody can redirect your payout
multiplier_bpsThe exact outcome in BPS โ€” cannot be swapped after signing
nonceSingle-use, tracked on-chain forever โ€” replay is impossible
Anti-Cheat

Can someone call the mint function directly
and fake a score?

This is the #1 question skeptics raise โ€” and the answer is solid. Here's exactly why direct contract calls cannot produce fake NFTs.

Short answer: No. The function mint_verified_score is public โ€” anyone can call it directly from any wallet or script. But it immediately demands a valid Ed25519 signature from the server's private key. Without it, the transaction aborts. Full stop.

Gate 1 โ€” Cryptographic (mint_verified_score)
Server private key required
Forging a valid Ed25519 signature without the private key is computationally infeasible. The key never touches the frontend.
Intercepted signatures can't be replayed
Your wallet address is encoded in the signed payload. A signature issued for Wallet A fails when submitted from Wallet B โ€” the message won't match.
Score is baked into the signature
A signature for a 5,000-point Tetris score cannot mint a 5,000,000-point score or a score in any other game.
Every nonce is single-use
Nonces are permanently recorded in the used_nonces table on-chain. Even a valid captured signature is rejected on resubmission.
Gate 2 โ€” Object Capability (admin_mint_score_nft)

The admin minting path requires passing a reference to the AdminCap object โ€” a unique on-chain object held exclusively by the Oni Games deployer wallet. Move's object model makes it impossible to spoof or borrow from another account. Non-admins are rejected at the VM level, before any contract logic executes.

Cannot Fake
Minting a fake score requires either breaking Ed25519 cryptography or stealing the Oni Games server private key. Neither is achievable through any smart contract interaction.
mint_verified_scorepublic fun
1public fun mint_verified_score(
2 store: &mut GameStore,
3 game_id: String, score: u64,
4 nonce: u64, signature: vector<u8>,
5 ctx: &mut TxContext,
6) {
7 // โ‘  Server key must be set
8 assert!(vector::length(
9 &store.server_public_key) == 32,
10 E_SERVER_KEY_NOT_SET);
11
12 // โ‘ก Nonce must be unused
13 assert!(!table::contains(
14 &store.used_nonces, nonce),
15 E_NONCE_ALREADY_USED);
16
17 // โ‘ข Ed25519 โ€” no bypass exists
18 assert!(ed25519::ed25519_verify(
19 &signature,
20 &store.server_public_key,
21 &msg // player|game|score|nonce
22 ), E_INVALID_SIGNATURE);
23
24 // Only THEN: mint the NFT โœ“
25}
What happens if you try to cheat?
๐Ÿ”ด Submit a fake signatureโ†’ E_INVALID_SIGNATURE
๐Ÿ”ด Replay another player's signatureโ†’ Address mismatch โ†’ abort
๐Ÿ”ด Reuse a valid signatureโ†’ E_NONCE_ALREADY_USED
๐Ÿ”ด Change score in the payloadโ†’ Signature invalid โ†’ abort
๐Ÿ”ด Call admin_mint without AdminCapโ†’ Move VM rejects โ€” not owned
Full Transparency

Nothing is hidden.

House Edge is Public
The multiplier_bps on every game encodes the house edge directly. A coin flip paying 1.96x (19,600 bps) instead of 2.0x represents 2% โ€” both are publicly readable on-chain, always.
Payout Caps Enforced On-Chain
No single payout can exceed 5% of the total bankroll. This is enforced by the smart contract before every bet โ€” not a policy document that can be quietly changed.
Leaderboards Can't Be Edited
Score rankings live inside the GameStore contract and are updated automatically on every mint. Our team has no function to manually alter, delete, or reorder any entry.
Replay Attacks Are Impossible
Every session nonce is permanently stored in the used_nonces table on-chain. A valid signature resubmitted after its first use is always rejected โ€” no exceptions, no backdoors.
Full Audit Trail
Every game emits an on-chain event with your address, guess, random result, and payout. Events are permanent and immutable โ€” verify your entire history on any blockchain explorer.
Admin Power Has Hard Limits
Admins can adjust bet limits and fees. They cannot alter any outcome, redirect any payout, or modify event history. Every admin action is a signed transaction โ€” permanently attributable.
๐Ÿ” Verify Yourself

Don't trust. Verify.

Everything on this page is publicly checkable on-chain. No account, API key, or special access required.

01
Read the contracts
Look up the oni_games package on the OneChain explorer. View full source of both modules โ€” casino and game_portal.
02
Check bankroll status
Look up the HouseBankroll shared object. Read balance, bet limits, payout cap, and lifetime stats โ€” all public fields.
03
Verify your game history
Query InstantWagerPlayed and SessionResolved events filtered by your wallet. Every result is permanently on-chain.
04
Inspect leaderboards
Look up the GameStore object and inspect the leaderboards table for any game_id. The ranking logic is in the contract.
Open OneChain Explorer
Oni Games ยท Transparency & Provably Fair ยท Built on OneChain