Privacy-first poker on Solana using Arcium’s privacy (MPC). Sensitive steps (shuffle, deal, hand evaluation) are intended to run through confidential MPC. A demo fallback keeps the game playable if devnet MPC callbacks are delayed.
This is an MVP prototype built for the Solana Hackathon, focused on demonstrating privacy-first gameplay and rapid iteration. It is not a final production release.
Online poker has a trust problem. Players have to trust the house to shuffle fairly, deal cards honestly, and evaluate hands correctly. Even "provably fair" systems still require trusting a central authority at some point.
We use Arcium's Multi-Party Computation to handle the sensitive parts of the game in a way that nobody (not even us) can see or manipulate. The deck shuffle, card dealing, and hand evaluation happen through encrypted computations. The blockchain only stores encrypted data; the actual game logic runs securely off-chain through MPC.
- Confidential Card Shuffling: Deck shuffle is queued through MPC and encrypted on-chain (localnet only; devnet uses fallback)
- Private Card Dealing: Player cards are encrypted with client keys and decrypted locally
- Secure Hand Evaluation: Winner determination queued through MPC (localnet only; devnet uses fallback)
- Demo Fallback: Deterministic flow keeps the game playable when MPC callbacks are unavailable
- Bot Workers: Optional automated players for testing and demos
On devnet, MPC transactions are queued but callbacks may not arrive due to executor availability. The fallback ensures the game remains demoable. For full MPC with real callbacks, run localnet (see below).
The latest devnet transactions for the demo wallet show the explicit fallback instructions used when MPC callbacks are unavailable:
- DevnetBypassShuffleAndDeal: https://explorer.solana.com/tx/2T83GVUqFukhC1EaLkjTYTHKyTfhpG3y2obwGDtsJxgc1fSjripBQWe8FB8SVqxPe49MBt5ZiqQMJsVQ5Qwsky92?cluster=devnet
- StartGame: https://explorer.solana.com/tx/api1MC2GUPKVST18yGq5DYDFtn6PCGTe1ZuAQniif4q5xwzRDVco2hwz2Uchis346ZErXNMExxN2sNDY937gpUJ?cluster=devnet
- CreateGame: https://explorer.solana.com/tx/551C9aLXDB2SADt3pwLpr7WtvJHJEGnEHZYwXUVAwNC523sne7zSmsJK7jQNnvdLaTGZggbKps5juBYLWfQoeXSW?cluster=devnet
These confirm the devnet fallback path. Full MPC callbacks are currently verifiable on localnet runs only.
These devnet transactions show MPC enqueue instructions in program logs (e.g., QueueShuffleAndDealCardsV3, QueueProveEntryFee, QueueComputation):
- QueueShuffleAndDealCardsV3: https://explorer.solana.com/tx/5b2WuZHeHEqubMrha6hWbPcuST46aFCDwgqSXr2BAQrhuZVu8Xm4syqopRsA1yr37xkUbyULHGQ1869WHeK2de3i?cluster=devnet
- QueueProveEntryFee: https://explorer.solana.com/tx/4ySUuYUuPDGL496anXmaetvwZz62Gk3xjeTNuvasvTDc8uevvx4dRrZoedbmLijHqua1K9K6GmFfZ3cP3FTcMS31?cluster=devnet
- QueueShuffleAndDealCardsV3: https://explorer.solana.com/tx/3gCpzo1C3VxZMBEmVXvm29FEmtuhG9YSG6TgtXq8nwsxCeNHUz2hXcp1qSTweC8dUz6KLEy4MHrJY1cBHjKq74kp?cluster=devnet
- programs/crypto-bluff - Anchor on-chain program with MPC integration
- encrypted-ixs - Arcium confidential instruction sources
- frontend - React + Vite UI (game table + wallet integration)
- scripts/bot-worker.ts - Optional bot runner for automated seats
- artifacts - Localnet/MXE artifacts and configs
- Program ID: 3JPRmpPsWuCAb6KiudBaPG2o5t7duJ55gobsEgK1WDZa
- Cluster Offset:
456 - RPC (default):
https://api.devnet.solana.com
- Offchain UI: https://arciumpoker.vercel.app (offchain only)
- Presentation Video: https://www.youtube.com/watch?v=HuGYFlWgFEI
- If you want onchain play, multiple players can connect on devnet using the same
gameId. - Bots are not enabled onchain; onchain is currently manual players only.
The UI creates a demo chip token on the current network the first time a wallet buys chips.
- By default, the mint is per-wallet and stored in local storage.
- For a fixed, shared chip mint, set
VITE_CHIP_MINTin your local frontend env file.
Current shared devnet mint (demo):
Ew7gouQS6xNTtmoTVBvcNyHSW44g3KFR8dPdNZh8iT3W
Example:
VITE_CHIP_MINT=<YOUR_DEVNET_MINT_ADDRESS>
- Node.js 18+
- A Solana wallet extension (Phantom or Backpack)
From the repository root:
npm install
cd frontend
npm install
cd ..Create a local env file in the frontend folder (this file is intentionally not committed).
Example:
VITE_SOLANA_RPC_URL=https://api.devnet.solana.com
VITE_SOLANA_CLUSTER=devnet
VITE_PROGRAM_ID=3JPRmpPsWuCAb6KiudBaPG2o5t7duJ55gobsEgK1WDZa
VITE_ARCIUM_CLUSTER_OFFSET=456
# Optional: fixed chip mint for all wallets
VITE_CHIP_MINT=Ew7gouQS6xNTtmoTVBvcNyHSW44g3KFR8dPdNZh8iT3W
cd frontend
npm run devThis will start the dev server and print the local URL (typically http://localhost:3000).
We run the demo with automatic bots, so you don’t need to create multiple wallets manually.
-
Prepare bot.env
- Copy
bot.env.example→bot.env - Fill in your bot keypairs as BASE64 (BOT_1/2/3)
- Set
BOT_HOST_KEYPAIR_PATH(host keypair file) - (Optional) Set
BOT_HOST_PUBLIC_KEYandBOT_GAME_IDto lock the worker to a specific game
- Copy
-
Choose your bot mode
- Trigger mode (default in
bot.env.example):BOT_REQUIRE_TRIGGER=true- The worker opens port 8787 and waits for the UI trigger
- You must click “Bots On” in the game screen, otherwise bots will NOT start
- Auto-detect mode:
- Set
BOT_REQUIRE_TRIGGER=false - No port is required; the worker auto-detects the latest
WaitingForPlayersgame
- Set
- Trigger mode (default in
-
Fund the host + bot keypairs
- Generate host + bot keypairs and fill
bot.env:# create keypairs mkdir -p .keys solana-keygen new -o .keys/host.json solana-keygen new -o .keys/bot_1.json solana-keygen new -o .keys/bot_2.json solana-keygen new -o .keys/bot_3.json # pubkeys solana-keygen pubkey .keys/bot_1.json solana-keygen pubkey .keys/bot_2.json solana-keygen pubkey .keys/bot_3.json # base64 secrets (paste into BOT_*_SECRET_BASE64) cat .keys/bot_1.json | python3 - <<'PY' import sys, json, base64 data = json.load(sys.stdin) print(base64.b64encode(bytes(data)).decode()) PY
- Set
BOT_HOST_KEYPAIR_PATH=.keys/host.jsoninbot.env. - Airdrop SOL to host + bots (devnet):
solana airdrop 2 $(solana-keygen pubkey .keys/host.json) --url https://api.devnet.solana.com solana airdrop 2 $(solana-keygen pubkey .keys/bot_1.json) --url https://api.devnet.solana.com solana airdrop 2 $(solana-keygen pubkey .keys/bot_2.json) --url https://api.devnet.solana.com solana airdrop 2 $(solana-keygen pubkey .keys/bot_3.json) --url https://api.devnet.solana.com
- For required envs and behavior, see scripts/bot-worker.ts.
- Generate host + bot keypairs and fill
-
Run the bot worker (keep it running)
npm run bot:workerNotes:
- Recommended: run this in a separate terminal so the process keeps running while you use the UI.
- If
BOT_REQUIRE_TRIGGER=true(orBOT_WORKER_PORTis set), the worker opens an HTTP trigger on port 8787. The UI calls this when you click “Bots On”. - If
BOT_REQUIRE_TRIGGER=false, no port is required; the worker auto-detects the latest game. - If you’re on Codespaces or a remote host, set
VITE_BOT_WORKER_URLto your forwarded URL for port 8787.
- Run the UI and create a game
- The bot worker auto-detects the new game (WaitingForPlayers) and joins it.
- It bypasses entry fees and progresses the hand automatically.
- Important: If your UI requires manual trigger, you must click “Bots On” on the game screen, otherwise bots will NOT start.
- If you enable a “Bots On” button in the UI, it simply triggers the worker (when trigger mode is enabled).
If bots don’t move, double-check bot.env values and that the bot worker is running.
The UI uses devnet by default. If MPC callbacks do not arrive in time, the fallback keeps the game progressing. This behavior is controlled via VITE_MPC_MODE (default: devnet-fallback).
For a complete MPC setup with real callbacks, you can run the full stack locally. This requires a local Solana validator and Arcium MXE nodes.
- Docker and Docker Compose
- Solana CLI tools
-
Start a local Solana validator
solana-test-validator --reset
-
Deploy the program to localnet
arcium deploy --cluster-offset 0
This will deploy the program and initialize MXE on localnet.
-
Start the MXE nodes
docker compose -f artifacts/docker-compose-arx-env.yml up -d
-
Configure the frontend for localnet Create or update your local frontend env file:
VITE_SOLANA_RPC_URL=http://localhost:8899 VITE_SOLANA_CLUSTER=localnet VITE_PROGRAM_ID=<YOUR_LOCALNET_PROGRAM_ID> VITE_ARCIUM_CLUSTER_OFFSET=0 VITE_MPC_MODE=localnet -
Run the frontend
cd frontend npm run dev
With this setup, all MPC callbacks will be processed by your local executor nodes. The game will use real encrypted card shuffling and hand evaluation instead of the fallback.
- 403 from RPC: your RPC endpoint is blocked or rate-limited. Switch
VITE_SOLANA_RPC_URLin your local frontend env file to a public devnet RPC or your own provider. - 429 Too Many Requests: your RPC is rate-limiting. Set
BOT_RPC_URLto a dedicated RPC (Helius/QuickNode), or pause and retry after a minute. - No chip balance: the chip mint is network-specific. If the mint is missing, mint chips again using the UI.
- Anchor.toml - Anchor configuration
- programs/crypto-bluff/src/lib.rs - On-chain program
- encrypted-ixs - MPC circuits
- frontend - UI and configuration
- Solana - Blockchain layer
- Anchor - Solana program framework
- Arcium MPC - Confidential computation
- React + Vite - Frontend
- TypeScript - Type safety throughout
MIT


