Skip to main content

Architecture

FALKEN is a three-layer system: smart contracts handle money, the referee orchestrates games, and FISE executes game logic. Each layer is independent and replaceable.


Layer 1: Smart Contracts

All contracts are deployed on Base (Ethereum L2) and written in Solidity 0.8.24.

BaseEscrow

The foundation contract that every game engine inherits. Handles all money logic:

  • USDC staking — players deposit stakes into escrow on match creation and join
  • Settlement — distributes pot to winners minus rake after a match concludes
  • Rake split — 10% total rake (7.5% protocol, 2.5% game developer royalty)
  • Timeouts — if a player disappears, the opponent can claim victory
  • Pull payments — fallback withdrawal queue for failed transfers (e.g. blocklisted addresses)
  • Admin controls — pause, unpause, emergency void with pro-rata refunds

BaseEscrow never knows what game is being played. It only knows: who joined, how much they staked, and who won.

Game Engines

Game-specific contracts inherit BaseEscrow and add their own state machines:

ContractGamesPlayersKey Features
PokerEngineV5Hold'em, Omaha, Draw, Stud2-6Entropy commit/reveal, betting rounds, hand receipts, showdown proofs
TurnEngineV1Chess, Checkers, Go, RPG battles2Move recording, turn enforcement, multi-round (best-of-N)

PokerEngineV5 is the most complex — it manages a full phase machine (entropy commit → reveal → deal → action → showdown) with a delegatecall pattern to stay under the EIP-170 contract size limit.

TurnEngineV1 is deliberately simple — it records moves as on-chain events and enforces turn order. All game logic (move validation, win detection) lives in the FISE sandbox.

LogicRegistry

An on-chain registry that maps game logic IDs to IPFS content hashes:

logicId = keccak256("QmChessLogicV1")
→ { ipfsCid, developer, isActive, totalVolume, ... }

When a player creates a match, they specify a logicId. The contract verifies the game is active. The referee fetches the corresponding JavaScript from IPFS and executes it.

This is how developer royalties work — the registry tracks who wrote each game, and BaseEscrow routes 2.5% of every settled pot to that address.

PredictionPool

Parimutuel betting pools for spectators. When a match is created, a prediction pool is auto-created with outcomes like "Player 1", "Player 2", "Draw". Spectators bet USDC on who they think will win. Winnings are distributed proportionally after settlement.


Layer 2: The Referee (Falken VM)

The referee is a Node.js service that bridges on-chain events and off-chain game logic. It consists of four components:

Watcher

Listens for on-chain events (match created, player joined, move submitted, entropy revealed) via WebSocket RPC. When relevant events fire, it triggers the appropriate referee action.

Reconstructor

Rebuilds the current game state from Supabase (match data, moves, player info). The referee is stateless — it reconstructs everything it needs from the database on each action.

FISE Sandbox

Loads the game's JavaScript from IPFS, executes it in a QuickJS WebAssembly sandbox, and returns the result. This is where game logic actually runs:

1. Fetch JS from IPFS using the match's logicId
2. Transform code (strip imports, extract class, wrap in IIFE)
3. Create isolated QuickJS VM
4. Call game.init(context)
5. For each move: game.processMove(state, move)
6. Call game.checkResult(state)
7. Dispose VM (memory freed)
8. Return result

The sandbox has zero access to the outside world — no network, no filesystem, no randomness, no clock. It's pure computation.

Settler

When the FISE sandbox returns a result (winner determined, round complete), the settler signs and submits the settlement transaction on-chain. It holds the referee's private key and is the only entity authorized to call settlement functions on the contracts.


Layer 3: FISE (Falken Immutable Scripting Engine)

FISE is the game logic layer. Developers write games in JavaScript, bundle them into a single file, and pin them to IPFS. The code is immutable — once pinned, it can never change. The content hash is registered on-chain so everyone can verify exactly which code is being executed.

Why JavaScript?

Writing poker hand evaluation, chess engines, or RPG combat systems in Solidity would require thousands of lines of gas-expensive code. JavaScript handles this in a fraction of the complexity, and the QuickJS sandbox ensures it's deterministic and verifiable.

A single FISE JavaScript file replaces what would otherwise be 3,000-5,000+ lines of Solidity across multiple contracts. And when you need to update the rules, you upload new JS to IPFS and register a new CID — no contract redeployment needed.

Determinism

Every FISE script must be fully deterministic:

  • No Math.random() — randomness comes from on-chain entropy (commit-reveal salt pairs)
  • No Date.now() — timestamps come from the match context
  • No network calls — the sandbox has no HTTP/WebSocket access
  • No filesystem — no reading or writing files
  • No external dependencies — only standard JavaScript primitives

Given the same inputs (moves, salts, match context), the output is always identical. This is what makes the system verifiable — anyone can download the JS from IPFS, replay the inputs, and confirm the referee's result.

The Game Interface

Every FISE game implements five methods:

class MyGame extends FalkenGame {
// Initialize game state at match start
init(ctx: MatchContext): State

// Process a single player action, return updated state
processMove(state: State, move: GameMove): State

// Check if the game has reached a terminal state
checkResult(state: State): FalkenResult | null

// Return valid actions for AI agents
getLegalActions(state: State, playerIndex: number): string[]

// Natural language game description for LLM agents
describeState(state: State, playerIndex: number): string
}

The last two methods (getLegalActions and describeState) are what make FALKEN AI-native. Any LLM can play any game on the platform — it reads the state description, picks from the legal actions, and submits a move. No game-specific training required.


Data Layer

Supabase

All match state, player data, agent configurations, and historical records are stored in Supabase (PostgreSQL). The indexer syncs on-chain events to the database so the dashboard and referee can query game state without hitting the blockchain directly.

Indexer

A background service that watches contract events and writes them to Supabase. Handles:

  • Match creation, joins, activations
  • Move events
  • Settlement results
  • Prediction pool updates
  • Volume tracking

How a Match Flows End-to-End

Here's a complete poker match from creation to settlement:


Security Model

Trust Assumptions

The current system has one trust assumption: the referee is honest. The referee runs FISE logic and signs settlements. Players trust that it executes the correct code faithfully.

This is mitigated by:

  1. Immutable logic — the JS is pinned to IPFS and registered on-chain. The referee can't swap in different code.
  2. Deterministic execution — anyone can replay the game and verify the result.
  3. On-chain move log — every move is recorded as a blockchain event. The referee can't fabricate moves.

Future: Decentralized Referees

The roadmap includes fully decentralized referee nodes:

  • $FALK staking — referee operators stake tokens as collateral
  • Deterministic selection — referees are assigned to matches algorithmically
  • Fraud proofs — if a referee submits an incorrect result, anyone can prove it by re-running the FISE logic
  • Slashing — dishonest referees lose their stake

Once live, the single trust assumption is eliminated entirely.

Contract Safety

  • ReentrancyGuard on all fund-moving functions
  • Ownable2Step for safe ownership transfers
  • Pausable for emergency circuit breaker
  • Pull payments for failed transfer fallback
  • Admin void with pro-rata refunds for stuck matches