Smart Contract

The on-chain program powering trustless rock-paper-scissors duels on Solana.

Overview

The Janken program is deployed on Solana and implements a trustless, commit-reveal based rock-paper-scissors game. The contract guarantees fairness through cryptographic commitments and enforces deterministic settlement logic on-chain.

All game state is stored in program-derived accounts (PDAs) with transparent, auditable transitions. Token transfers are handled via Solana's SPL Token program, ensuring atomic execution.

Instructions

The program exposes five core instructions that manage the complete game lifecycle.

create_game

Core

Initializes a new game instance. The creator stakes tokens and submits a cryptographic commitment hash of their move. Creates a game account and associated treasury PDA to hold staked funds.

Arguments

  • stake_amount: u64Amount of tokens to stake (must meet minimum)
  • commitment_hash: [u8; 32]keccak256 hash of (move || nonce || salt)

Key Accounts

  • • game (writable, signer)
  • • creator (writable, signer)
  • • creator_token (writable)
  • • game_treasury (writable, PDA)

join_game

Core

Allows a second player to join an existing game. Stakes an equal amount and submits their own commitment hash. After this instruction, the game transitions to the reveal phase.

Arguments

  • commitment_hash: [u8; 32]keccak256 hash of joiner's (move || nonce || salt)

Key Accounts

  • • game (writable)
  • • joiner (writable, signer)
  • • joiner_token (writable)
  • • game_treasury (writable, PDA)

reveal

Core

Players reveal their actual moves by providing the plaintext move and salt. The program verifies the reveal against the stored commitment. Once both players reveal, the game is settled automatically with funds distributed according to the outcome.

Arguments

  • move_choice: MoveRock | Paper | Scissors
  • salt: stringRandom nonce used in commitment

Key Accounts

  • • game (writable)
  • • player (signer)
  • • player0_token (writable)
  • • player1_token (writable)
  • • platform_fee_account (writable)

Settlement Logic: Win (90% to winner), Loss (0%), Tie (100% refund, no burn). The 10% burn is sent to the platform fee account on decisive outcomes.

claim_on_timeout

Timeout

Allows the revealing player to claim the full pot if their opponent fails to reveal within the deadline. Prevents griefing by penalizing non-revealing players. No burn occurs—winner takes all.

Requirements

  • • Reveal deadline must have passed
  • • Caller must have already revealed
  • • Opponent must not have revealed

Key Accounts

  • • game (writable)
  • • caller (signer)
  • • player0_token (writable)
  • • player1_token (writable)

close_game

Cleanup

Closes a finished game account and reclaims rent. Can only be called after the game is fully settled. Transfers any remaining SOL rent to the closer.

Key Accounts

  • • game (writable)
  • • closer (writable, signer)
  • • game_treasury (writable, PDA)

Account Structures

Game

The primary account type storing all game state.

players[Pubkey; 2] — Player addresses
commitment_hashes[[u8; 32]; 2] — Commitment hashes
revealed_moves[Option<Move>; 2] — Revealed moves
stake_amountu64 — Stake per player
stateGameState — Current state
reveal_deadlinei64 — Unix timestamp
created_ati64 — Creation timestamp
bumpu8 — PDA bump

Type Definitions

GameState (Enum)

  • WaitingForPlayerInitial state, awaiting second player
  • WaitingForRevealBoth players committed, awaiting reveals
  • FinishedGame settled, ready for cleanup

Move (Enum)

  • RockBeats Scissors
  • PaperBeats Rock
  • ScissorsBeats Paper

Error Codes

The program defines custom errors for precise failure handling.

6000InvalidGameStateOperation not valid for current game state
6001RevealTimeoutReveal deadline exceeded
6002NotAPlayerCaller is not a player in this game
6003InvalidRevealReveal does not match commitment hash
6004NotTimedOutTimeout claim invalid, deadline not reached
6005CallerNotRevealedCaller must reveal before claiming timeout
6006InvalidPlatformWalletPlatform fee account invalid
6007InvalidGameTokenToken mint does not match expected
6008InvalidTimeoutClaimTimeout claim conditions not met

Security Guarantees

The contract enforces fairness through a two-phase commit-reveal scheme. Commitments are validated using keccak256, providing collision resistance and preimage security.

Timeout mechanisms prevent griefing: if a player refuses to reveal, their opponent can claim the full pot after the deadline. All state transitions are atomic and deterministic, eliminating race conditions.

The program uses PDAs for treasury accounts, ensuring only the program can authorize token transfers. No external authority can drain funds or manipulate outcomes.

Technical Specifications

Version0.1.0
Spec0.1.0
Instructions5
Account Types1
Custom Errors9