Quick Start
Deploy your first Intelligent Contract in about 10 minutes.
Option A: Zero setup β try in the browser
The fastest path to a running contract is studio.genlayer.com (opens in a new tab) β open it, pick a built-in example, and click Deploy. No installation needed. You can also import any deployed contract by address to interact with it directly.
When you're ready to build locally, continue with Option B below.
Option B: Local development
Before you start: pick your environment
GLSim β a lightweight simulator that implements the GenLayer JSON-RPC protocol. Starts in ~1 second, no Docker required. Ideal for getting started quickly.
You'll need: Python 3.12+ and Node.js 18+
Both paths use the same contracts, the same CLI, and the same tests. You can switch at any time.
Set up your project
Clone the official boilerplate β it includes a contract template, testing infrastructure, and a frontend scaffold.
git clone https://github.com/genlayerlabs/genlayer-project-boilerplate.git my-project
cd my-project
pip install -r requirements.txtThis installs two tools:
genlayer-testβ testing framework (direct mode, GLSim, integration tests)genvm-lint(from thegenvm-linterpackage) β static analysis that catches contract errors before you deploy
Your project layout:
contracts/ # Your Intelligent Contracts
tests/
direct/ # Fast in-memory tests β no server needed
integration/ # Full end-to-end tests against a GenLayer environment
frontend/ # Next.js frontend with GenLayerJS pre-wired
deploy/ # Deployment scripts
gltest.config.yaml # Network and account configurationStart your environment
pip install 'genlayer-test[sim]'
glsim --port 4000 --validators 5GLSim runs the Python runner natively β not inside GenVM β so there can be minor incompatibilities. Always validate against Studio before deploying to testnet.
Write your first contract
Create contracts/hello.py:
# { "Depends": "py-genlayer:1jb45aa8ynh2a9c9xn3b7qqh8sm5q93hwfp7jqmwsfhh8jpz09h6" }
from genlayer import *
class Hello(gl.Contract):
name: str
def __init__(self, name: str):
self.name = name
@gl.public.view
def greet(self) -> str:
return f'Hello, {self.name}!'
@gl.public.write
def set_name(self, name: str):
self.name = nameA few things every contract needs:
- Line 1 β the version comment pins the GenVM version. Required.
gl.Contractβ marks this class as an Intelligent Contract.@gl.public.viewβ read-only method, no state change.@gl.public.writeβ method that modifies on-chain state.- Class-level type annotations β persistent fields must be declared in the class body. Fields set via
self.field = valueoutside the class body are not persisted.
The GenLayer Studio auto-detects constructor parameters from your contract code and generates a UI for them β no ABI configuration needed.
Lint and test
Catch issues before deploying:
# Static analysis β catches forbidden imports, invalid types, missing decorators
genvm-lint check contracts/hello.py
# Fast in-memory tests β no server, no Docker
pytest tests/direct/ -vA minimal direct-mode test looks like this:
def test_greet(direct_vm, direct_deploy, direct_alice):
contract = direct_deploy("contracts/hello.py", args=["World"])
result = contract.greet()
assert result == "Hello, World!"
direct_vm.sender = direct_alice
contract.set_name("GenLayer")
assert contract.greet() == "Hello, GenLayer!"Available fixtures: direct_vm, direct_deploy, direct_alice, direct_bob, direct_charlie, direct_owner.
Deploy and call
- Open http://localhost:8080 (opens in a new tab) (or studio.genlayer.com (opens in a new tab) for zero-setup)
- Load
contracts/hello.py - Click Deploy β Studio detects the
nameparameter and shows an input field - Call
greetfrom the UI to see the output
You should see Hello, World! (or whatever name you passed). That's your first contract running. β
Make it intelligent
Now add what no regular smart contract can do β call an LLM to decide on-chain:
# { "Depends": "py-genlayer:1jb45aa8ynh2a9c9xn3b7qqh8sm5q93hwfp7jqmwsfhh8jpz09h6" }
from genlayer import *
import json
class SentimentChecker(gl.Contract):
last_result: str
def __init__(self):
self.last_result = ""
@gl.public.write
def analyze(self, text: str) -> None:
# Non-deterministic operations must run inside a function
# passed to gl.eq_principle.* β this is how GenLayer reaches
# consensus across validators that may get slightly different results.
def run():
result = gl.nondet.exec_prompt(
f'Classify the sentiment of this text as POSITIVE, NEGATIVE, or NEUTRAL. '
f'Text: "{text}". '
f'Respond ONLY with valid JSON: {{"sentiment": "<POSITIVE|NEGATIVE|NEUTRAL>"}}',
response_format="json"
)
return json.dumps(result, sort_keys=True)
raw = gl.eq_principle.strict_eq(run)
self.last_result = json.loads(raw)["sentiment"]
@gl.public.view
def get_result(self) -> str:
return self.last_resultAlways use json.dumps(..., sort_keys=True) before returning from a strict_eq block.
Without it, validators may produce differently-ordered JSON and fail to reach consensus.
The key concept: gl.eq_principle.strict_eq tells GenLayer that all validators must return byte-identical results. It works best when output is tightly constrained β like a fixed JSON schema with a small set of possible values.
For web data access (like fetching live prices), validators may get slightly different values depending on timing. See the Price Oracle example in the Equivalence Principle docs for the correct pattern.
β Deep dive: Equivalence Principle
What's next?
Go deeper on Intelligent Contracts
- Equivalence Principle β how validators reach consensus on non-deterministic outputs
- Calling LLMs β
exec_prompt, response formats, and choosing the right eq principle - Web Access β
web.getvsweb.render, modes, and when to use each - Prompt & Data Techniques β prompt patterns that maximize validator agreement
Write proper tests
- Testing β direct mode, mocking web/LLM calls, integration tests
- Debugging β reading validator execution logs, tracing failed transactions
Connect a frontend
- DApp Development Workflow β full-stack architecture overview
- GenLayerJS SDK β reading and writing contracts from JavaScript
Ship to testnet
- Deploying β deployment methods, Testnet Bradbury, network configuration