API Reference

Complete reference for the QTUM Dice backend API. All endpoints return JSON. The base URL depends on your deployment (e.g., https://dice.ziesto.ca).

Base URL & Authentication

The API has no authentication — it's designed for public access. Rate limiting is applied at the server level:

  • General: 500 requests per 15 minutes per IP
  • House seed generation: 20 requests per minute per IP

All responses follow this format:

{
  "success": true | false,
  "data": { ... },        // present on success
  "error": "message"      // present on failure
}

General

GET /health

Health check endpoint. Returns server status.

Response:

{
  "status": "ok",
  "uptime": 12345,
  "qtumConnected": true
}

GET /api/info

Game metadata and configuration.

Response:

{
  "success": true,
  "data": {
    "name": "QTUM Dice",
    "version": "1.0.0",
    "network": "testnet",
    "contractAddress": "9aac22498a..."
  }
}

Dice Game Endpoints

GET /api/game/status

Get the current game status including contract balance, pending bets, and recent history.

Response:

{
  "success": true,
  "data": {
    "contractBalance": "10000000000",  // in satoshis (100 QTUM)
    "pendingBets": 2,
    "recentBets": [
      {
        "betId": 42,
        "player": "7926223070...",
        "amount": "10000000",
        "target": 50,
        "isOver": true,
        "roll": 73,
        "won": true,
        "payout": "19800000",
        "resolvedAt": 1706123456789
      }
    ]
  }
}

GET /api/game/house-seed

Generate a new house seed and return its keccak256 hash. The actual seed is stored on the backend for later reveal. Seeds expire after 2 hours.

Rate limit: 20 requests per minute per IP

Response:

{
  "success": true,
  "data": {
    "houseSeedHash": "0x3a7f8c...",   // bytes32 keccak256 hash
    "expiresAt": 1706130656789       // Unix timestamp (ms)
  }
}
Workflow

Call this endpoint before placing a bet. Pass the houseSeedHash to the smart contract's placeBet() function. The backend will later reveal the original seed during revealAndResolve().

POST /api/game/submit-player-seed

Submit the player's revealed seed after the bet is confirmed on-chain. This triggers the backend to call revealAndResolve() on the contract.

Request Body:

ParameterTypeDescription
betIdnumberThe on-chain bet ID
playerSeedstringThe original seed (bytes32, 0x + 64 hex chars)

Request:

POST /api/game/submit-player-seed
Content-Type: application/json

{
  "betId": 42,
  "playerSeed": "0xabcdef0123456789..."
}

Response:

{
  "success": true,
  "data": {
    "betId": 42,
    "playerSeedReceived": true,
    "revealTxid": "abc123..."  // or null if failed
  }
}

Errors:

CodeErrorCause
400Missing required fieldsbetId or playerSeed not provided
400Invalid player seed formatSeed not in bytes32 format
404Bet not found or not pendingbetId doesn't match a pending bet

GET /api/game/bet/:betId

Get details of a specific bet by ID. Works for both pending and resolved bets.

URL Parameter:

ParameterTypeDescription
betIdnumberThe on-chain bet ID

Response (resolved):

{
  "success": true,
  "data": {
    "betId": 42,
    "player": "7926223070...",
    "amount": "10000000",       // satoshis
    "target": 50,
    "isOver": true,
    "roll": 73,
    "won": true,
    "payout": "19800000",      // satoshis
    "status": "resolved",
    "resolvedAt": 1706123456789
  }
}

Response (pending):

{
  "success": true,
  "data": {
    "betId": 43,
    "player": "7926223070...",
    "amount": "10000000",
    "target": 75,
    "isOver": false,
    "hasPlayerSeed": false,
    "status": "pending"
  }
}

GET /api/game/find-bet?houseSeedHash=0x...

Find a pending bet by its house seed hash. Used by the frontend to discover the on-chain bet ID after placing a transaction.

ParameterTypeDescription
houseSeedHashstring (query)The house seed hash used when placing the bet

Response:

{
  "success": true,
  "data": {
    "betId": 42,
    "player": "7926223070...",
    "amount": "10000000",
    "target": 50,
    "isOver": true,
    "hasPlayerSeed": false
  }
}

GET /api/game/multiplier?target=50&isOver=true

Calculate the payout multiplier for given bet parameters.

ParameterTypeDescription
targetnumber (query)Target number (2-98)
isOverstring (query)true or false

Response:

{
  "success": true,
  "data": {
    "target": 50,
    "isOver": true,
    "multiplier": "1.9600",
    "winChance": "50%"
  }
}

GET /api/game/history?limit=20

Get recent resolved bets in reverse chronological order.

ParameterTypeDefaultDescription
limitnumber (query)20Max number of bets to return

Response:

{
  "success": true,
  "data": [
    {
      "betId": 42,
      "player": "7926223...",
      "amount": "10000000",
      "target": 50,
      "isOver": true,
      "roll": 73,
      "won": true,
      "payout": "19800000",
      "resolvedAt": 1706123456789
    }
  ]
}

WebSocket Events

Connect to the WebSocket server at ws://<host>:3002 (or wss:// for SSL). The server broadcasts game events in real-time.

betPlaced

Emitted when a new bet is detected on-chain.

{
  "type": "betPlaced",
  "data": {
    "betId": 42,
    "player": "7926223070...",
    "amount": "10000000",
    "target": 50,
    "isOver": true
  }
}

betResolved

Emitted when a bet is resolved on-chain.

{
  "type": "betResolved",
  "data": {
    "betId": 42,
    "player": "7926223070...",
    "amount": "10000000",
    "target": 50,
    "isOver": true,
    "roll": 73,
    "won": true,
    "payout": "19800000",
    "resolvedAt": 1706123456789
  }
}

WebSocket Client Example

const ws = new WebSocket('wss://dice.ziesto.ca/ws');

ws.addEventListener('message', (event) => {
  const msg = JSON.parse(event.data);

  switch (msg.type) {
    case 'betPlaced':
      console.log(`New bet #${msg.data.betId}`);
      break;
    case 'betResolved':
      console.log(`Bet #${msg.data.betId}: roll=${msg.data.roll}`);
      break;
  }
});

Smart Contract Interface

These are the contract functions your dApp interacts with. All functions are called via QTUM RPC (sendtocontract or callcontract) or through the MetaMask Snap.

Write Functions

FunctionParametersDescription
placeBet (bytes32 playerSeedHash, uint8 target, bool isOver, bytes32 houseSeedHash) Place a bet with QTUM value. Payable.
revealAndResolve (uint256 betId, bytes32 houseSeed, bytes32 playerSeed) Reveal both seeds and resolve the bet.
claimRefund (uint256 betId) Claim refund if bet not resolved after 1 hour.
deposit (none) Fund the contract bankroll. Payable.
withdraw (uint256 amount) Owner-only: withdraw from bankroll.

Read Functions

FunctionParametersReturns
getBet (uint256 betId) Full bet details (player, amount, target, result, etc.)
calculateMultiplier (uint8 target, bool isOver) Payout multiplier in basis points
calculatePayout (uint256 amount, uint8 target, bool isOver) Potential payout for a bet
getBalance (none) Contract balance in satoshis

Events

EventParametersWhen Emitted
BetPlaced (uint256 indexed betId, address indexed player, uint256 amount, uint8 target, bool isOver, bytes32 playerSeedHash, bytes32 houseSeedHash) When placeBet() succeeds
BetResolved (uint256 indexed betId, address indexed player, uint8 roll, bool won, uint256 payout) When revealAndResolve() or claimRefund() succeeds

Function Selectors

Pre-computed keccak256 selectors for ABI encoding:

FunctionSelector
placeBet(bytes32,uint8,bool,bytes32)keccak256(...).slice(0,10)
revealAndResolve(uint256,bytes32,bytes32)keccak256(...).slice(0,10)
claimRefund(uint256)keccak256(...).slice(0,10)
getBalance()0x12065fe0
getBet(uint256)keccak256(...).slice(0,10)
Computing Selectors

You can compute function selectors using ethers.js:

import { keccak256, toUtf8Bytes } from 'ethers';
const sel = keccak256(
  toUtf8Bytes('placeBet(bytes32,uint8,bool,bytes32)')
).slice(0, 10);
// e.g., "0x1a2b3c4d"

Error Handling

All error responses follow the standard format:

{
  "success": false,
  "error": "Human-readable error message"
}
HTTP CodeMeaningCommon Causes
400Bad RequestMissing or invalid parameters
404Not FoundBet ID doesn't exist
429Too Many RequestsRate limit exceeded
500Internal ErrorQTUM node unreachable, contract call failed