GenLayer Architecture, Layer by Layer
A visual walkthrough of how everything connects — from your dApp frontend down to the external world of LLMs and live web data.
Why architecture matters
Most developer docs jump straight to "here's how to write a contract." That's fine if you already have a mental model of what's happening under the hood. But if you don't, you end up with a lot of confusion about why things behave the way they do.
Why does it take 38 seconds for a simple contract to finalize? Why can't you just call an LLM from anywhere in your code? Why does changing the equivalence principle affect performance?
These questions make a lot more sense once you understand the architecture. GenLayer is built in layers — here's what each one does.
The full stack
Layer 1 — The user / dApp frontend
This is where your users interact. A web app, mobile app, or even a CLI. From the user's perspective, they connect a wallet, read contract state, and submit transactions.
The key distinction: reading is instant, writing is not. Reading contract state (view methods) is a direct query — no consensus needed, returns immediately. Writing triggers the full consensus flow and takes 30–60+ seconds.
Design your UX around this. If your app needs to show the result of a write transaction, you'll need polling or WebSocket updates — don't expect a synchronous response.
Layer 2 — GenLayerJS SDK / GenLayer CLI
The SDK abstracts the raw JSON-RPC calls into a clean TypeScript API:
import { createClient } from 'genlayer-js'
const client = createClient({ chain: studionet })
// Read — instant, no gas
const data = await client.readContract({
address: contractAddress,
functionName: 'get_stats',
args: [userAddress]
})
// Write — triggers consensus, takes time
const hash = await client.writeContract({
address: contractAddress,
functionName: 'cast',
args: []
})
// Poll until finalized
const receipt = await client.waitForTransactionReceipt({
hash,
status: 'FINALIZED'
})
Layer 3 — GenLayer Node
The node is the entry point to the network. It receives transactions, coordinates validator selection, manages the mempool, and exposes the JSON-RPC interface.
The node is also responsible for validator selection — when a transaction comes in, it randomly picks a group of validators from the active pool to process it. This randomness is a core security property.
Layer 4 — The validator network
Validators are participants who stake tokens to earn the right to validate transactions. They run GenVM with a connected LLM provider. Currently on Studio testnet, 115 validators run simultaneously across 8 different models — GPT-5.2, Claude Sonnet 4.5, Gemini 3 Flash, DeepSeek V3.2, Llama 4 Maverick, and others.
The diversity of models is intentional. If all validators ran the same LLM, a single model's biases or errors could consistently dominate consensus. Running different models reduces correlation risk.
Layer 5 — GenVM (Python runtime)
GenVM is the execution environment where your Intelligent Contract runs. It's a sandboxed Python runtime that supports two modes:
Deterministic mode — regular Python code. Runs identically on all validators. No surprises.
Non-deterministic mode — calls through gl.nondet.*. These are sandboxed and their outputs go through the Equivalence Principle before validators can agree.
# { "Depends": "py-genlayer:1jb45aa8ynh2a9c9xn3b7qqh8sm5q93hwfp7jqmwsfhh8jpz09h6" }
from genlayer import *
class MyContract(gl.Contract):
data: TreeMap[str, str] # not dict — TreeMap for persistent storage
def __init__(self):
self.data = TreeMap[str, str]()
@gl.public.view
def get(self, key: str) -> str:
return self.data.get(key, "")
@gl.public.write
def set(self, key: str, value: str) -> None:
self.data[key] = value
TreeMap[K, V] and DynArray[T], not Python's native dict and list. All persistent fields must be declared at the class level with type annotations.
Layer 6 — The external world
Unlike traditional smart contracts, GenLayer's Intelligent Contracts can reach outside the blockchain:
Web data — gl.nondet.web.get(url) fetches live HTTP data. No oracle required.
LLM inference — gl.nondet.exec_prompt(prompt) calls an LLM through the validator's configured provider.
EVM contracts — gl.evm.contract_interface() lets you interact with Ethereum-compatible contracts on other chains.
All of these go through gl.nondet.* because they're non-deterministic. The Equivalence Principle is what makes it safe to use them in a consensus-based system.
Putting it all together — a real example
Here's what happens when a user clicks "Cast" in a fishing game built on GenLayer:
1. Frontend calls client.writeContract({ functionName: 'cast', args: [] })
2. SDK sends a signed transaction to the GenLayer Node via JSON-RPC
3. Node selects 5 validators randomly; designates one as Leader
4. Leader's GenVM instance executes the cast() function
5. Since we use deterministic randomness (no LLM), all validators agree immediately
6. Majority consensus → ACCEPTED
7. Finality window passes with no appeal → FINALIZED
8. Frontend polls waitForTransactionReceipt and updates the UI
That full flow takes about 38–45 seconds. Most of that is steps 4–7 — validators independently executing and voting.
What this means for how you build
Separate reads from writes. View functions are fast and free. Design your data model so the UI can display state via reads, and only trigger writes for actual state changes.
Non-deterministic calls add latency. Each gl.nondet.* call adds overhead because validators need to independently resolve it. Use them only when you actually need external data or AI judgment.
Test locally before deploying. GLSim starts in about 1 second and doesn't require Docker. Use direct mode for rapid iteration, GLSim for integration testing, Studio for final validation before testnet.
This is part of a series of visual guides to GenLayer's core concepts. Previous pieces covered Optimistic Democracy and the Equivalence Principle.