Skip to content

Chain of Custody Phase 1

Date: 2026-04-15 Status: Implemented

Decision

Align all three layers (contract, admin-server, game-server) around a single unit: integer NUMERO. Eliminate conversion layers, game-specific settlement, and fire-and-forget patterns on fund paths.

The Token

NUMERO is a zero-decimal ERC-20 with ERC-2612 Permit. 1 NUMERO = 1 chip. No conversion anywhere.

The Table Pool

Contract tracks one total per table (pools[tableId][token]), not per-player. Game-server tracks the per-player split via PlayerLedger. Deposits are admin-only via depositFor with ERC-2612 permit.

The Settlement

Single SettlementFacet replaces per-game functions:

  • settle(receipt, signature) — verify signer, record PnL + nonce
  • withdrawFor(wallet, player, tableId, amount, historyHash) — transfer from pool to wallet
  • settleAndWithdraw(receipt, signature, amount) — both in one call

gameId in the EIP-712 receipt distinguishes games. The contract has no game-specific logic.

The Boundary

  • Game-server = the table. Manages chips (integers). Zero blockchain awareness.
  • Admin-server = the cage (the money-only bank, now called evm-bank). Manages money (all on-chain ops). Looks up gameId and token from table config by roomCode.
  • gameType never crosses the boundary. It's a game-server routing concept.

Sequential Operations

Every fund-affecting operation is awaited. No .catch(() => {}) on money paths:

  • Session recovery completes before accepting connections
  • Deposit verified before first game_state
  • Concurrent sit-down attempts rejected
  • Runtime config loaded before server starts

Tradeoffs

  • Per-player on-chain tracking lost — can't query per-player balance on-chain. Admin-server tracks deposits in its own DB.
  • Token migration — zero-decimal requires redeployment. Manageable on testnet.
  • Admin-server API breaking change — no gameType, integer chips instead of wei.

Key Files

  • packages/contracts/contracts/facets/TableFacet.solregisterTable, depositFor
  • packages/contracts/contracts/facets/SettlementFacet.sol — unified settlement
  • apps/admin-server/src/routes/settlement.ts — integer passthrough, table config lookup
  • apps/game-server/src/shared/player-ledger.ts — pure integer counter
  • apps/game-server/src/shared/settlement.ts — sends chip integers

Full ADR

The full ADR is retained in the project's internal planning archive.