A fair 50/50 coin flip game built on Sui blockchain, utilizing Sui's native on-chain randomness for secure and trustless gameplay. Players bet on Heads or Tails with provably fair outcomes, powered by Move smart contracts and a React frontend with integrated Enoki sponsorship.
V2 introduces significant architectural improvements:
- Sui On-Chain Randomness: Uses Sui's built-in
random::Randommodule for secure, trustless random number generation - eliminating the need for external VRF services - Enoki Sponsorship Integration: Implements @mysten/enoki for gas-free transactions via Vercel API routes
- Type-Safe Contract Bindings: Auto-generated TypeScript bindings using @mysten/codegen for type-safe smart contract interactions
- Simplified Architecture: Single-transaction game flow with no backend signing service required
Creates a singleton house data object that manages the game's treasury and configuration.
- Initializes house balance and game parameters (min/max stake, fees)
- Operates as the house's treasury, holding staking balance and collected fees
- Provides methods for house administration (top-up, withdraw, update parameters)
Defines the core game logic for a single-player coin flip game.
start_game(): Creates a new game with player's guess (H/T) and stakefinish_game(): Determines winner using Sui's on-chain randomness and distributes fundsdispute_and_win(): Allows players to reclaim funds if game is not finished withinEPOCHS_CANCEL_AFTER(7 epochs)
Key features:
- Uses dynamic object fields to store games within
HouseData - Implements fee collection on winning games
- Emits events for game creation (
NewGame) and completion (Outcome)
Note: This module is no longer necessary with on-chain randomness. It was designed to prevent MEV attacks when using backend VRF services. With Sui's native randomness, the standard single_player_satoshi module provides equivalent security with better UX.
sequenceDiagram
participant Player as Player (Frontend)
participant Enoki as Enoki Sponsorship
participant API as Vercel API Routes
participant Sui as Sui Blockchain
participant Random as Sui On-Chain Random
Note over Player, Random: Game Creation
Player->>Enoki: Request sponsorship for start_game
Enoki-->>Player: Sponsored transaction bytes
Player->>Sui: Execute start_game (guess + stake, sponsored)
Sui-->>Player: Game created with ID
Note over Player, Random: Game Execution
Player->>Enoki: Request sponsorship for finish_game
Enoki-->>Player: Sponsored transaction bytes
Player->>Sui: Execute finish_game (sponsored)
Sui->>Random: Request on-chain randomness
Random-->>Sui: Cryptographically secure random bool
Note over Sui: Compare player guess with random result
Sui-->>Player: Funds distributed to winner
Note over Player, Sui: All randomness generated on-chain, trustless & verifiable
| Feature | V1 (Backend VRF) | V2 (On-Chain Randomness) |
|---|---|---|
| Randomness Source | Backend BLS signature | Sui on-chain random::Random |
| Backend Service | Required (signing service) | Optional (only for sponsorship) |
| Transactions per Game | 2 (create + finish) | 2 (create + finish) |
| Trust Model | Trust backend with house key | Trustless, cryptographically secure |
| MEV Resistance | Required 2-step flow | Built-in via on-chain randomness |
| Gas Fees | Player pays | Sponsored by Enoki |
- Node.js 18+ and pnpm installed
- Sui CLI installed and configured
- Enoki API key (for sponsorship)
-
Navigate to setup directory:
cd setup/ npm i -
Configure environment variables based on
setup/README.md:ADMIN_SECRET_KEY: Admin wallet private keyNETWORK: Sui network (testnet/mainnet)- Other configuration per setup README
-
Publish contracts and initialize house:
./publish.sh testnet npm run init-house # Requires admin account to have 10+ SUI -
Setup frontend:
cd ../app/ pnpm i pnpm codegen # Generate TypeScript bindings pnpm run dev
Create app/.env.local with:
VITE_ENOKI_PUBLIC_KEY=your_enoki_public_key
VITE_ENOKI_PRIVATE_KEY=your_enoki_private_key
VITE_NETWORK=testnet
VITE_PACKAGE_ID=<published_package_id>
VITE_HOUSE_DATA_ID=<house_data_object_id>- Connect Wallet: Player connects their Sui-compatible wallet
- Place Bet:
- Choose Heads (H) or Tails (T)
- Submit stake (between
min_stakeandmax_stake) - Transaction is sponsored by Enoki (gas-free)
- Finish Game:
- Player (or anyone) calls
finish_game - On-chain randomness determines outcome
- Winner receives stake (minus fees if applicable)
- Player (or anyone) calls
- Dispute (Optional):
- If game is not finished within 7 epochs, player can call
dispute_and_winto reclaim full stake
- If game is not finished within 7 epochs, player can call
With on-chain randomness, fairness is cryptographically guaranteed:
- Unpredictable Randomness: The random value is generated by Sui's secure randomness beacon, which cannot be predicted or manipulated
- Transparent Verification: All randomness generation happens on-chain and is verifiable in the transaction effects
- Time-Lock Protection: Games that aren't finished within 7 epochs can be disputed, ensuring funds aren't locked indefinitely
- No Trust Required: Unlike V1, no backend service holds cryptographic keys or can influence outcomes
.
├── satoshi_flip/ # Move smart contracts
│ ├── sources/
│ │ ├── house_data.move
│ │ ├── single_player_satoshi.move
│ │ └── mev_attack_resistant_single_player_satoshi.move (legacy)
│ └── tests/
├── app/ # Frontend application
│ ├── src/
│ │ ├── __generated__/ # Auto-generated contract bindings (@mysten/codegen)
│ │ ├── components/
│ │ ├── hooks/
│ │ ├── services/
│ │ └── pages/
│ └── api/ # Vercel API routes for Enoki sponsorship
│ ├── sponsor/
│ │ ├── prepare.ts # Prepare sponsored transaction
│ │ └── execute.ts # Execute sponsored transaction
│ └── lib/
│ ├── enokiClient.ts
│ └── config.ts
└── setup/ # Deployment scripts
└── publish.sh
From single_player_satoshi.move:108-114:
// Step 1: Generate secure randomness using Sui's native random.
let mut generator = random::new_generator(random_state, ctx);
let random_result = random::generate_bool(&mut generator);
// Step 2: Determine winner.
let player_guess_bool = map_guess(guess) == 0; // H = 0 = false, T = 1 = true
let player_won = player_guess_bool == random_result;The random::Random shared object provides cryptographically secure randomness through Sui's randomness beacon, eliminating the need for external VRF services.
Transaction sponsorship is handled via Vercel API routes:
/api/sponsor/prepare: Prepares a sponsored transaction with allowed move call targets/api/sponsor/execute: Executes the sponsored transaction on behalf of the player
Sponsored move calls include:
single_player_satoshi::start_gamesingle_player_satoshi::finish_game- Standard coin operations (
split,join,transfer)
Generated using @mysten/codegen:
pnpm codegen # Reads sui-codegen.config.ts and generates TypeScript typesThis creates type-safe functions for all Move contract interactions in src/__generated__/.
- No Key Management: No backend private keys to secure
- No MEV Risk: Randomness is generated after the guess is committed on-chain
- Verifiable: All randomness sources are recorded in transaction effects
- Censorship Resistant: No backend can refuse to sign or delay transactions
- Epoch Delay: On-chain randomness requires a small delay (typically sub-second) for generation
- Gas Costs: While sponsored by Enoki, on-chain randomness does add computational cost
- Dispute Window: 7-epoch window for disputes means funds can be locked briefly
If you're migrating from V1 (backend VRF):
- Remove Backend Service: No signing service needed
- Update Contract Calls: Replace VRF-based
finish_gamewith randomness-based version - Remove Counter NFT: No longer needed for VRF input
- Simplify Flow: Use single-transaction
single_player_satoshimodule instead of MEV-resistant variant
"Satoshi Coin Flip" is intended to serve as a general reference and is provided for informational purposes only. It does not provide gambling endorsement, advice, or recommendations. Users are responsible for their own gambling activities and decisions, including complying with applicable laws and regulations relating to gambling. We make no representations or warranties of any kind, express or implied, about the completeness, accuracy, reliability, suitability, or availability of the information provided. We are not responsible for any legal consequences users may face.
Released under the Apache 2.0 License.
- Original theory presented by Kostas Chalkias at GAM3R 2022
- Built with @mysten/sui, @mysten/dapp-kit, and @mysten/enoki