# index.mdx
import Image from 'next/image'
import { Card, Cards } from 'nextra-theme-docs'
import CustomCard from '../components/card'
## Trust Infrastructure for the AI Age
GenLayer is a blockchain where validator nodes powered by diverse AI models reach consensus on subjective decisions — a synthetic jurisdiction on-chain.
Intelligent Contracts interpret language, process unstructured data, and pull live web inputs. No oracles, no intermediaries.
- **Bitcoin** — Trustless Money
- **Ethereum** — Trustless Computation
- **GenLayer** — Trustless Decision-Making
Explore [typical use cases](/understand-genlayer-protocol/typical-use-cases) or jump straight into building.
## Get Started
## Community and Careers
Join the community on [Discord](https://discord.gg/8Jm4v89VAu) or [Telegram](https://t.me/genlayer) to ask questions, share feedback, and showcase your builds.
We're hiring — [see open roles](https://x.com/GenLayer/jobs).
# understand-genlayer-protocol.mdx
## What is GenLayer?
GenLayer is the first AI-native blockchain built for AI-powered smart contracts—called Intelligent Contracts—capable of reasoning and adapting to real-world data. Its foundation is the Optimistic Democracy consensus mechanism, an enhanced Delegated Proof of Stake (dPoS) model where validators connect directly to Large Language Models (LLMs). This setup allows for non-deterministic operations—such as processing text prompts, fetching live web data, and executing AI-based decision-making—while preserving the reliability and security of a traditional blockchain.
## Core Technology
At the heart of GenLayer lies Optimistic Democracy—an enhanced Delegated Proof of Stake (dPoS) consensus mechanism that integrates AI models directly into validator operations. This synergy delivers three capabilities traditional blockchains cannot match:
1. **On-Chain AI Processing**
Validators connect to leading AI models (GPT, LLaMA, Meta, etc.) to execute complex reasoning on-chain, from natural language comprehension to data-driven predictions.
2. **Consensus-Backed Security**
Multiple validators vote on outcomes, ensuring collective agreement and robust reliability for every transaction—even those involving non-deterministic AI outputs.
3. **Intelligent Contracts**
Smart contracts in GenLayer gain reasoning abilities, allowing them to understand natural language, process real-world data, and adapt to evolving conditions.
## Technical Implementation
To integrate AI seamlessly with the blockchain, GenLayer employs a distributed neural consensus network, wherein validators run specialized software connected via API to advanced AI models. This approach unifies:
- Delegated Proof of Stake (dPoS) for efficient block production and governance.
- Neural Consensus for non-deterministic transactions requiring advanced AI reasoning.
This architecture supports autonomous DAOs, self-executing prediction markets, and dynamic DeFi protocols that react to real-world data in real time.
# Optimistic Democracy: How Consensus Works
Inspired by **[Condorcet's Jury Theorem](https://jury-theorem.genlayer.com/)** (click the link to check out our interactive model), Optimistic Democracy merges probabilistic AI systems with deterministic blockchain rules, ensuring secure and accurate consensus at scale.
1. **User Submits a Transaction**
A user sends a transaction request to the network (see the diagram's Step 1).
2. **Leader (Validator) Proposes Result**
The network selects a Leader, who processes the request and proposes an outcome (Step 2).
3. **Validators Recompute**
A group of Validators independently re-compute the transaction (Step 3). If the output aligns with the Leader's proposal, they approve; otherwise, they deny.
This multi-layer validation ensures majority agreement, adding a safety net for AI-driven computations.
# Validator Selection Mechanism
Token holders bolster network security by delegating tokens to validator candidates. A deterministic function f(x) then randomly designates Leader-Validator and Validators for each transaction. This process not only promotes fairness but also helps decentralize validation power, strengthening GenLayer's security and trustlessness.
# Validator Operational Framework
Each GenLayer validator node integrates:
- **Validator Software**
Handles core blockchain functions: networking, block production, and transaction management.
- **AI Model Integration**
Connects to Large Language Models (LLMs) or other AI services for complex reasoning, natural language processing, and real-time data retrieval.
Validators seamlessly manage both:
1. **Deterministic Transactions** typical of traditional blockchains.
2. **Non-Deterministic Transactions** that leverage AI-driven logic (e.g., searching the internet, analyzing data, making probabilistic inferences).
By splitting tasks between standard deterministic and advanced AI-powered transactions, GenLayer ensures high performance without compromising on security.
# Putting It All Together
With Optimistic Democracy guiding consensus and validators empowered by AI, GenLayer enables a new class of blockchain applications. From DAOs that self-govern based on real-time data to DeFi protocols that dynamically adjust parameters in response to market changes, developers can now build truly intelligent decentralized solutions.
# understand-genlayer-protocol/what-is-genlayer.mdx
import { Callout } from "nextra-theme-docs";
# What is GenLayer
## Trust Infrastructure for the AI Age
GenLayer is a blockchain where validator nodes powered by diverse AI models reach consensus on subjective decisions — a synthetic jurisdiction on-chain.
Intelligent Contracts interpret language, process unstructured data, and pull live web inputs. No oracles, no intermediaries.
- **Bitcoin** — Trustless Money
- **Ethereum** — Trustless Computation
- **GenLayer** — Trustless Decision-Making
## What Intelligent Contracts Can Do
### Subjective Decisions
Evaluate context and nuance. Turn judgment calls into enforceable on-chain outcomes — content moderation, claim assessment, quality evaluation.
### Internet Access
Fetch live web data directly on-chain. Contracts can read websites, call APIs, and verify real-world information without oracles or intermediaries.
### Natural Language Processing
Interpret human-readable inputs via LLMs. Contracts can analyze text, extract meaning, and make decisions based on qualitative criteria.
### Image & Visual Processing
Pass images to LLMs for analysis — screenshot a webpage and verify its content, check visual evidence for claims, analyze receipts or documents. Contracts can capture screenshots via `gl.nondet.web.render()` and send them to LLMs via `gl.nondet.exec_prompt(images=[...])`.
### Unstructured Data
Process text, images, audio transcripts, and qualitative evidence. Handle real-world complexity that traditional smart contracts cannot.
## How It Compares
| Feature | Traditional Smart Contracts | Intelligent Contracts |
|---|---|---|
| **Language** | Solidity, Rust | Python |
| **Data sources** | On-chain only (or oracles) | On-chain + live web data |
| **Decision logic** | Deterministic only | Deterministic + subjective |
| **AI integration** | Not possible | Native LLM access (text + images) |
| **Consensus** | All nodes must agree on exact output | Validators assess equivalence of results |
## Architecture: Two Layers
GenLayer operates as two integrated layers:
**GenLayer Chain** — an EVM-compatible L2 (zkSync Elastic Chain). Holds account balances via ghost contracts, handles standard Ethereum operations (`eth_*` methods), and anchors to Ethereum's security model.
**GenVM** — the execution environment for Intelligent Contracts. A WebAssembly-based VM (built on [Wasmtime](https://wasmtime.dev)) that runs a Python interpreter with native access to LLMs, web data, and non-deterministic operations. Can also execute compiled native code.
Every Intelligent Contract has a corresponding **ghost contract** on the chain layer at the same address. Ghost contracts hold the contract's GEN balance, relay transactions to consensus, and execute external messages. See [Messages](/developers/intelligent-contracts/features/messages#ghost-contracts) for details.
Transactions enter via `addTransaction` on the chain layer. GenVM executes the contract logic. Results settle back on-chain.
## Develop in Python
Intelligent Contracts are Python classes extending `gl.Contract`:
```python
# { "Depends": "py-genlayer:1jb45aa8ynh2a9c9xn3b7qqh8sm5q93hwfp7jqmwsfhh8jpz09h6" }
from genlayer import *
import json
class WizardOfCoin(gl.Contract):
has_coin: bool
def __init__(self):
self.has_coin = True
@gl.public.write
def ask_for_coin(self, request: str) -> None:
if not self.has_coin:
raise gl.vm.UserError("I don't have a coin!")
prompt = f"""
You are a wizard guarding a gold coin.
An adventurer says: {request}
Should you give them the coin?
Respond as JSON: {{"give_coin": true/false}}
"""
def leader_fn():
return gl.nondet.exec_prompt(prompt, response_format="json")
def validator_fn(leaders_res) -> bool:
if not isinstance(leaders_res, gl.vm.Return):
return False
my_result = leader_fn()
return my_result["give_coin"] == leaders_res.calldata["give_coin"]
result = gl.vm.run_nondet_unsafe(leader_fn, validator_fn)
if result["give_coin"]:
self.has_coin = False
@gl.public.view
def get_has_coin(self) -> bool:
return self.has_coin
```
Full SDK available: [genlayer-js](/api-references/genlayer-js) (TypeScript), [genlayer-py](/api-references/genlayer-py) (Python), [CLI](/api-references/genlayer-cli).
[Get started →](/developers/intelligent-contracts/first-contract)
# understand-genlayer-protocol/optimistic-democracy-how-genlayer-works.mdx
import { Callout } from "nextra-theme-docs";
# How GenLayer Works
## Optimistic Democracy
GenLayer uses **Optimistic Democracy** for consensus — a mechanism where validators running diverse AI models independently evaluate transactions and vote on outcomes. It applies [Condorcet's Jury Theorem](https://en.wikipedia.org/wiki/Condorcet%27s_jury_theorem): a group of independent reasoners is more likely to reach the correct answer than any individual.
Transactions are accepted if a majority of validators agree. Anyone can appeal an accepted result, triggering re-evaluation by a new, larger validator set. This process can escalate through multiple rounds until a final decision is reached.
## Transaction Lifecycle
Every transaction moves through these stages:
1. **Pending** — queued, waiting to be picked up
2. **Proposing** — a leader validator executes the contract and proposes a result
3. **Committing** — other validators execute independently and submit encrypted votes
4. **Leader Revealing** — the leader reveals execution data and decryption keys
5. **Revealing** — validators reveal their votes
6. **Accepted** — majority consensus reached; transaction enters the appeal window
7. **Finalized** — appeal window closed, result is permanent and irreversible
If consensus is not reached, the transaction may be marked **Undetermined** or rotate to a new leader.
See [Transaction Execution](/understand-genlayer-protocol/core-concepts/transactions/transaction-execution) for the full state machine.
## Non-Determinism and Consensus
Because Intelligent Contracts use LLMs and web data, validators may produce different outputs for the same input. GenLayer provides several strategies for reaching consensus on non-deterministic results:
- **Strict equality** — all validators must produce the exact same output (for deterministic operations)
- **LLM-based comparison** — an LLM compares validator outputs against developer-defined criteria
- **Custom validation** — developers write explicit leader/validator function pairs with full control over consensus logic
See [Non-determinism](/developers/intelligent-contracts/features/non-determinism) for implementation details.
## Appeals and Finality
After a transaction is accepted, it enters a **finality window** during which anyone can appeal the result.
- An appeal triggers a new round with a fresh, larger validator set
- Appeals can escalate through multiple rounds
- The final round's decision is binding
Once the finality window closes without appeal (or after the final appeal round), the transaction is **finalized** — permanent and irreversible.
See [Appeal Process](/understand-genlayer-protocol/core-concepts/optimistic-democracy/appeal-process) and [Finality](/understand-genlayer-protocol/core-concepts/optimistic-democracy/finality) for details.
# understand-genlayer-protocol/typical-use-cases.mdx
# Use Cases
GenLayer enables applications that require subjective judgment — decisions that traditional smart contracts can't make because they need to interpret language, evaluate evidence, or assess quality.
## Dispute Resolution
AI validators evaluate evidence and deliver verdicts in minutes. Applies to:
- **Agentic commerce** — AI agents transacting autonomously ([ERC-8183](https://eips.ethereum.org/EIPS/eip-8183)) need trustless arbitration for SLA compliance, delivery verification, service quality
- **Chargebacks** — buyer/seller disputes resolved by analyzing shipping records, communication logs, and transaction history
- **Freelance & gig work** — was the deliverable satisfactory? AI consensus replaces subjective back-and-forth
## Rule & Constitution Verification
Check whether something meets a set of criteria defined in natural language:
- Does a new prediction market meet listing guidelines?
- Does a DAO proposal comply with the organization's charter?
- Does a content submission follow community standards?
- Does a transaction comply with regulatory requirements?
This is a foundational primitive — many applications are instances of "evaluate X against rules Y."
## Prediction Markets
Markets that resolve automatically by fetching outcomes from primary sources. No centralized oracle or authority needed.
→ [Example contract](/developers/intelligent-contracts/examples/prediction)
## Social Content Verification
AI validators assess content quality, detect plagiarism, and distribute rewards based on originality and engagement. Replaces centralized content moderation with consensus-driven evaluation.
## Code & Work Quality Assurance
Staked submissions where reviewers are economically incentivized to find issues. AI validators assess code quality, deliverable completeness, or compliance with specifications.
## AI-Governed Organizations
DAOs where proposals are written in natural language, evaluated against real-time data, and executed automatically when conditions align.
## Compliance Automation
Real-time screening against sanctions lists, KYC/AML requirements, and changing regulations. Contracts read authoritative sources directly — no manual updates needed.
## Insurance
Parametric and evidence-based insurance where claims are evaluated by AI validators. Contracts fetch weather data, flight statuses, or photographic evidence to assess claims and trigger payouts automatically — no adjusters, no weeks of waiting.
## Argumentation & Debate Markets
Structured debates where participants stake positions and AI consensus determines outcomes — a new primitive for information markets.
## What Makes These Possible
All of these share a common pattern: they require **subjective judgment** that traditional smart contracts can't perform.
GenLayer's Intelligent Contracts can:
- Fetch and interpret live web data
- Process natural language and unstructured inputs
- Make subjective decisions through multi-validator AI consensus
- Execute outcomes on-chain with full finality
See [projects building on GenLayer](https://portal.genlayer.foundation/#/) for live examples.
[Start building →](/developers/intelligent-contracts/first-contract)
# understand-genlayer-protocol/core-concepts.mdx
import { Card, Cards } from 'nextra-theme-docs'
# Core Concepts
Dive into GenLayer’s fundamental building blocks. These core concepts elucidate how Intelligent Contracts remain secure, efficient, and reliable in a non-deterministic environment:
# understand-genlayer-protocol/core-concepts/validators-and-validator-roles.mdx
# Validators and Validator Roles
## Overview
Validators are essential participants in the GenLayer network. They are responsible for validating transactions and maintaining the integrity and security of the blockchain. Validators play a crucial role in the Optimistic Democracy consensus mechanism, ensuring that both deterministic and non-deterministic transactions are processed correctly.
## Key Responsibilities
- **Transaction Validation**: Validators verify the correctness of transactions proposed by the leader, using mechanisms like the Equivalence Principle for non-deterministic operations.
- **Leader Selection**: Validators participate in the process of randomly selecting a leader for each transaction, ensuring fairness and decentralization.
- **Consensus Participation**: Validators cast votes on proposed transaction outcomes, contributing to the consensus process.
- **Staking and Incentives**: Validators stake tokens to earn the right to validate transactions and receive rewards based on their participation and correctness.
## Validator Selection and Roles
- **Leader Validator**: For each transaction, a leader is randomly selected among the validators. The leader is responsible for executing the transaction and proposing the result to other validators.
- **Consensus Validators**: Other validators assess the leader's proposed result and vote to accept or reject it based on predefined criteria.
## Becoming a Validator
- **Staking Requirement**: Participants must stake a certain amount of tokens to become validators.
- **Validator Configuration**: Validators must configure their nodes with the appropriate LLM providers and models, depending on the network's requirements.
- **Reputation and Slashing**: Validators must act honestly to avoid penalties such as slashing of their staked tokens.
# understand-genlayer-protocol/core-concepts/genvm.mdx
# GenVM (GenLayer Virtual Machine)
The GenVM is the execution environment for Intelligent Contracts in the GenLayer protocol. It serves as the backbone for processing and managing contract operations within the GenLayer ecosystem.
[Source code at GitHub](https://github.com/genlayerlabs/genvm)
## Purpose of GenVM
The only purpose of the GenVM is to execute Intelligent Contracts, which can have non-deterministic code while maintaining blockchain security and consistency.
In summary, the GenVM plays a crucial role in enabling GenLayer's unique features, bridging the gap between traditional smart contracts and AI-powered, web-connected Intelligent Contracts.
## Key Features That Make the GenVM Different
Unlike traditional blockchain virtual machines such as Ethereum Virtual Machine (EVM), the GenVM has some advanced features.
- **Integration with LLMs**: the GenVM facilitates seamless interaction between Intelligent Contracts and Large Language Models
- **Web access**: the GenVM provides access to the Internet
- **User friendliness**: Intelligent Contracts can be written in Python, which makes the learning curve much more shallow
## How the GenVM Works
1. **Contract Deployment**: When an Intelligent Contract is deployed, the GenVM compiles and executes the contract code.
2. **Transaction Processing**: As transactions are submitted to the network, the GenVM executes the relevant contract functions and produces the contract's next state.
## Developer Considerations
When developing Intelligent Contracts for the GenVM:
- Utilize Python's robust libraries and features
- Consider potential non-deterministic outcomes when integrating LLMs
- Implement proper error handling for web data access
- Optimize code for efficient execution within the rollup environment
# understand-genlayer-protocol/core-concepts/optimistic-democracy.mdx
import { Callout } from 'nextra-theme-docs'
# Optimistic Democracy
Optimistic Democracy is the consensus method used by GenLayer to validate transactions and operations of Intelligent Contracts. This approach is especially good at handling unpredictable outcomes from transactions involving web data or AI models, which is important for keeping the network reliable and secure.
## Key Components
- **Validators:** Participants who stake tokens to earn the right to validate transactions. They play a crucial role in both the initial validation and any appeals process if needed.
- **Leader Selection:** A process that randomly picks one validator to propose the outcome for each transaction, ensuring fairness and reducing potential biases.
## How It Works
Optimistic Democracy relies on a mix of trust and verification to ensure transaction integrity:

1. **Initial Validation:** When a transaction is submitted, a small group of randomly selected validators checks its validity. One is chosen as the leader. The leader executes the transaction, and the other validators assess the leader's proposal using the Equivalence Principle.
2. **Majority Consensus:** If most validators accept the leader's proposal, the transaction is provisionally accepted. However, this decision is not final yet, allowing for possible appeals during a limited window of time, known as the **Finality Window**.
If any validator fails to vote within the specified timeframe, they are replaced, and a new validator is selected to cast a vote.
3. **Initiating an Appeal:** If a participant disagrees with the initial validation (if it's incorrect or fraudulent), they can appeal during the Finality Window. They must submit a request and provide a bond. After the appeal starts, a new group of validators joins the original ones. This group first votes on whether the transaction should be re-evaluated. If they agree, a new leader is chosen to reassess the transaction, and all validators then review this new evaluation.
4. **Appeal Evaluation:** The new leader re-evaluates the transaction, while the other validators assess the leader's proposal using the Equivalence Principle. This step involves more validators, increasing the chances of an accurate decision.
5. **Escalating Appeals:** If the appealing party is still not satisfied, the process can escalate, with each round involving more validators. Each round doubles the number of validators. A new leader is only chosen if the transaction is overturned.
6. **Final Decision:** The appeals process continues until a majority consensus is reached or until all validators have participated. The final decision is recorded, and the transaction's state is updated accordingly. If the appealing party is correct, they receive a reward for their efforts, while incorrect appellants lose their bond.
# understand-genlayer-protocol/core-concepts/optimistic-democracy/equivalence-principle.mdx
# Equivalence Principle Mechanism
The Equivalence Principle mechanism is a cornerstone in ensuring that Intelligent Contracts function consistently across various validators when handling non-deterministic outputs like responses from Large Language Models (LLMs) or data retrieved through web browsing. It plays a crucial role in how validators assess and agree on the outcomes proposed by the Leader.
The Equivalence Principle protects the network from manipulations or errors by ensuring that only suitable, equivalent outcomes influence the blockchain state.
## Key Features of the Equivalence Principle
The Equivalence Principle is fundamental to how Intelligent Contracts operate, ensuring they work reliably across different network validators.
- **Consistency in Decentralized Outputs:** The Equivalence Principle allows outputs from various sources, such as LLMs or web data, to be different yet still considered valid as long as they meet predefined standards. This is essential to maintain fairness and uniform decision-making across the blockchain, despite the natural differences in AI-generated responses or web-sourced information.
- **Security Enhancement:** To protect the integrity of transactions, the Equivalence Principle requires that all validators check each other’s work. This mutual verification helps prevent errors and manipulation, ensuring that only accurate and agreed-upon data affects the blockchain.
- **Output Validation Flexibility:** Intelligent Contracts often need to handle complex and varied data. This part of the principle allows developers to set specific rules for what counts as "equivalent" or acceptable outputs. This flexibility helps developers tailor the validation process to suit different needs, optimizing either for accuracy or efficiency depending on the contract's requirements.
## Types of Equivalence Principles
Validators work to reach a consensus on whether the result set by the Leader is acceptable, which might involve direct comparison or qualitative evaluation, depending on the contract’s design. If the validators do not reach a consensus due to differing data interpretations or an error in data processing, the result might be challenged or an appeal process might be initiated.
### Comparative Equivalence Principle
In the Comparative Equivalence Principle, both the Leader and the validators perform identical tasks and then directly compare their respective results with the predefined criteria in the Equivalence Principle to ensure consistency and accuracy. This method uses an acceptable margin of error to handle slight variations in results between validators and is suitable for quantifiable outputs. However, since multiple validators perform the same tasks as the Leader, it increases computational demands and associated costs.
For example, if an Intelligent Contract is tasked with calculating the average rating of a product based on user reviews, the Equivalence Principle specifies that the average ratings should not differ by more than 0.1 points. Here's how it works:
1. **Leader Calculation**: The Leader validator calculates the average rating from the user reviews and arrives at a rating of 4.5.
2. **Validators' Calculations**: Each validator independently calculates the average rating using the same set of user reviews. Suppose one validator calculates an average rating of 4.6.
3. **Comparison**: The validators compare their calculated average (4.6) with the Leader's average (4.5). According to the Equivalence Principle, the ratings should not differ by more than 0.1 points.
4. **Decision**: Since the difference (0.1) is within the acceptable margin of error, the validators accept the Leader's result as valid.
### Non-Comparative Equivalence Principle
In contrast, the Non-Comparative Equivalence Principle does not require validators to replicate the Leader's output, which makes the validation process faster and less costly. Instead, validators assess the accuracy of the Leader’s result against the criteria defined in the Equivalence Principle. This method is particularly useful for qualitative outputs like text summaries.
For example, in an Intelligent Contract designed to summarize news articles, the process works as follows:
1. **Leader Summary**: The Leader validator generates a summary of a news article.
2. **Evaluation Criteria**: The Equivalence Principle defines criteria for an acceptable summary, such as accuracy, relevance, and length.
3. **Validators' Assessment**: Instead of generating their own summaries, validators review the Leader’s summary and check if it meets the predefined criteria.
- **Accuracy**: Does the summary accurately reflect the main points of the article?
- **Relevance**: Is the summary relevant to the content of the article?
- **Length**: Is the summary within the acceptable length?
4. **Decision**: If the Leader’s summary meets all the criteria, it is accepted by the validators.
## Key Points for Developers
- **Setting Equivalence Criteria:** Developers must define what 'equivalent' means for each non-deterministic operation in their Intelligent Contract. This guideline helps validators judge if different outcomes are close enough to be treated as the same.
- **Ensuring Contract Reliability:** By clearly defining equivalence, developers help maintain the reliability and predictability of their contracts, even when those contracts interact with the unpredictable web or complex AI models.
# understand-genlayer-protocol/core-concepts/optimistic-democracy/appeal-process.mdx
# Appeals Process
The appeals process in GenLayer is a critical component of the Optimistic Democracy consensus mechanism. It provides a means for correcting errors or disagreements in the validation of Intelligent Contracts. This process ensures that non-deterministic transactions are accurately evaluated, contributing to the robustness and fairness of the platform.
## How It Works
- **Initiating an Appeal**: Participants can appeal the initial decision by submitting a request and a required bond during the Finality Window. A new set of validators is then added to the original group to reassess the transaction.
- **Appeal Evaluation**: The new validators first review the existing transaction to decide if it needs to be overturned. If they agree it should be re-evaluated, a new leader re-evaluates the transaction. The combined group of original and new validators then review this new evaluation to ensure accuracy.
- **Escalating Appeals**: If unresolved, the appeal can escalate, doubling the number of validators each round until a majority consensus is reached or all validators have participated.
Once a consensus is reached, the final decision is recorded, and the transaction's state is updated. Correct appellants receive a reward, while those who are incorrect may lose their bond.
## Gas Costs for Appeals
The gas costs for an appeal can be covered by the original user, the appellant, or any third party. When submitting a transaction, users can include an optional tip to cover potential appeal costs. If insufficient gas is provided, the appeal may fail to be processed, but any party can supply additional gas to ensure the appeal proceeds.
# understand-genlayer-protocol/core-concepts/optimistic-democracy/finality.mdx
# Finality
Finality refers to the state in which a transaction is considered settled and unchangeable. In GenLayer, once a transaction achieves finality, it cannot be appealed or altered, providing certainty to all participants in the system. This is particularly important for applications that rely on accurate and definitive outcomes, such as financial contracts or decentralized autonomous organizations (DAOs).
## Finality Window
The Finality Window is a time frame during which a transaction can be challenged or appealed before it becomes final. This window serves several purposes:
1. **Appeals**: During the Finality Window, any participant can appeal a transaction if they believe the validation was incorrect. This allows for a process of checks and balances, ensuring that non-deterministic transactions are evaluated properly.
2. **Re-computation**: If a transaction is appealed, the system can re-evaluate the transaction with a new set of validators. The Finality Window provides the time necessary for this process to occur.
3. **Security**: The window also acts as a security feature, allowing the network to correct potential errors or malicious activity before finalizing a transaction.
import Image from 'next/image'
## Deterministic vs. Non-Deterministic Transactions
In GenLayer, Intelligent Contracts are classified as either deterministic or non-deterministic.
### Deterministic Contracts
These contracts have a shorter Finality Window because their validation process is straightforward and not subject to appeals. However, it is essential that all interactions with the contract remain deterministic to maintain this efficiency.
### Non-Deterministic Contracts
Non-deterministic contracts involve Large Language Model (LLM) calls or web data retrieval, which introduce variability in their outcomes. These contracts require a longer Finality Window to account for potential appeals and re-computation.
import { Callout } from 'nextra-theme-docs'
If a specific transaction within the contract is deterministic but interacts with a non-deterministic part of the contract, it will be treated as non-deterministic. This ensures that any appeals or re-computations of previous transactions are handled consistently, maintaining the integrity of the contract's overall state.
## Fast Finality
For scenarios requiring immediate finality, such as emergency decisions in a DAO, it is possible to pay for all validators to validate the transaction immediately. This approach, though more costly, allows for fast finality, bypassing the typical Finality Window.
Fast finality only works if there are no previous non-deterministic transactions still within their Finality Window. Even if your transaction is considered final, if a previous transaction is reverted, your transaction will have to be recomputed as it might depend on the same state.
## Appealability and Gas
When submitting a transaction, users can include additional gas to cover potential appeals. If a transaction lacks sufficient gas for appeals, third parties can supply additional gas during the Finality Window. Developers of Intelligent Contracts can also set minimum gas requirements for appealability, ensuring that critical transactions have adequate coverage.
# understand-genlayer-protocol/core-concepts/optimistic-democracy/staking.mdx
# Staking in GenLayer
Participants, known as validators, commit a specified amount of tokens to the network by locking them up on the rollup layer. This commitment supports the network's consensus mechanism and enables validators to actively participate in processing transactions and managing the network.
## Validators vs Delegators
| Role | Minimum Stake | Infrastructure | Rewards |
|------|--------------|----------------|---------|
| **Validator** | 42,000 GEN | Must run a node | 10% operational fee + stake rewards |
| **Delegator** | 42 GEN | None required | Passive stake rewards |
**Validators** run the consensus infrastructure and are responsible for executing intelligent contracts and validating transactions. They receive a 10% operational fee from rewards before distribution.
**Delegators** stake their tokens with validators without running infrastructure. They earn passive rewards proportional to their stake, minus the validator's operational fee.
## How Staking Works
- **Stake Deposit**: To become a validator on GenLayer, participants must deposit GEN tokens on the rollup layer. This deposit acts as a security bond and qualifies them to join the pool of active validators.
- **Validator Participation**: Only a maximum of 1000 validators with the highest stakes can be part of the active validator set. Once staked, validators take on the responsibility of validating transactions and executing Intelligent Contracts. Their role is crucial for ensuring the network's reliability and achieving consensus on transaction outcomes.
- **Delegated Proof of Stake (DPoS)**: GenLayer enhances accessibility and network security through a Delegated Proof of Stake system. This allows token holders who are not active validators themselves to delegate their tokens to trusted validators. By delegating their tokens, users increase the total stake of the validator and share in the rewards. Typically, the validator takes a configurable fee (around 10%), with the remaining rewards (90%) going to the delegating user.
- **Earning Rewards**: Validators, and those who delegate their tokens to them, earn rewards for their contributions to validating transactions, paid in GEN tokens. These rewards are proportional to the amount of tokens staked and the transaction volume processed.
- **Risk of Slashing**: Validators, and by extension their delegators, face the risk of having a portion of their staked tokens [slashed](/understand-genlayer-protocol/core-concepts/optimistic-democracy/slashing) if they fail to comply with network rules or if the validator supports fraudulent transactions.
## Owner, Operator, and ValidatorWallet
When a validator joins, the system creates three distinct entities:
**ValidatorWallet**: A separate smart contract wallet created automatically on `validatorJoin()`. This is the primary validator identifier and holds staked GEN tokens.
**Owner Address**: The address that creates the validator (msg.sender). It controls staking operations and can change the operator address. Should use a cold wallet for security.
**Operator Address**: Used for consensus operations. Can differ from the owner (hot wallet recommended). It can be changed by the owner but cannot be the zero address or reused across validators.
## Epoch System
The network operates in epochs (1 day):
- **Epoch +2 Activation Rule**: All deposits become active 2 epochs after they are made
- Epoch finalization requires all transactions to be finalized
- Cannot advance to epoch N+1 until epoch N-1 is finalized
- Validators are "primed" via `validatorPrime()` each epoch (permissionless - anyone can call it)
**Critical**: If `validatorPrime()` isn't called, the validator is excluded from the next epoch's selection.
### Genesis Epoch 0
Epoch 0 is the **genesis bootstrapping period** with special rules designed to facilitate network launch. The normal staking rules are relaxed to allow rapid network bootstrapping.
#### What is Epoch 0?
Epoch 0 is the **bootstrapping period** before the network becomes operational. During epoch 0:
- **No transactions are processed** - the network is not yet active
- **No consensus occurs** - validators are not yet participating
- Stakes are registered and prepared for activation in epoch 2
**Important**: The network transitions directly from epoch 0 to epoch 2 (epoch 1 is skipped). Validators and delegators who stake in epoch 0 become active in epoch 2, but only if they meet the minimum stake requirements.
#### Special Rules for Epoch 0
| Rule | Normal Epochs (2+) | Epoch 0 |
|------|-------------------|---------|
| Validator minimum stake | 42,000 GEN | No minimum to join |
| Delegator minimum stake | 42 GEN | No minimum to join |
| Activation delay | +2 epochs | Active in epoch 2 |
| validatorPrime required | Yes, each epoch | Not required |
| Share calculation | Based on existing ratio | 1:1 (shares = input) |
| Transaction processing | Yes | No (bootstrapping only) |
**Activation requires meeting minimums**: While you can join with any amount during epoch 0, your stake will only be **activated in epoch 2** if it meets the minimum requirements (42,000 GEN for validators, 42 GEN for delegators). Stakes below the minimum remain registered but inactive.
#### Validators in Epoch 0
**Key behaviors:**
1. **No minimum stake to join**: Validators can join with any non-zero amount during epoch 0
2. **Registered for epoch 2**: Stakes are recorded and will become active when epoch 2 begins
3. **No priming required**: `validatorPrime()` is not needed during epoch 0
4. **No consensus participation**: Validators do not process transactions in epoch 0
**Do validators need to take any action in epoch 0 to be active in epoch 2?**
No. Validators who join in epoch 0:
- Have their stake registered during epoch 0
- Become active automatically in epoch 2 (epoch 1 is skipped) **only if they have at least 42,000 GEN staked**
- Must start calling `validatorPrime()` in epoch 2 for continued participation in epoch 4+
**Important**: Validators who joined in epoch 0 with less than 42,000 GEN will **not be active** in epoch 2. They must deposit additional funds to meet the minimum requirement before epoch 2 begins.
#### Delegators in Epoch 0
**Key behaviors:**
1. **No minimum delegation**: Any non-zero amount accepted during epoch 0
2. **Registered for epoch 2**: Delegation is recorded and will become active when epoch 2 begins
3. **No rewards in epoch 0**: Since no transactions are processed, no rewards are earned during epoch 0
**Is a delegation made in epoch 0 active in epoch 2?**
Yes. Delegations made in epoch 0 become active in epoch 2 (epoch 1 is skipped). Unlike normal epochs where you wait +2 epochs, epoch 0 delegations activate as soon as the network becomes operational.
#### Activation Timeline Comparison
**Normal Epochs (2+):**
```
Epoch N: validatorJoin() or delegatorJoin() called
Epoch N+1: validatorPrime() stages the deposit
Epoch N+2: validatorPrime() activates the deposit → NOW ACTIVE
```
**Epoch 0 (Bootstrapping):**
```
Epoch 0: validatorJoin() or delegatorJoin() called → stake registered (not yet active)
No transactions processed, no consensus
Epoch 2: Stakes become active (if minimum met), network operational, validatorPrime() required
```
#### Share Calculation in Epoch 0
In epoch 0, shares are calculated at a 1:1 ratio with the input amount:
```
Shares = Input Amount
Example: Deposit 1,000 GEN → Receive 1,000 shares
```
This is because there's no existing stake pool to calculate a ratio against. Starting from epoch 2, shares are calculated based on the current stake-to-share ratio.
#### Transitioning from Epoch 0 to Epoch 2
When the network advances from epoch 0 to epoch 2 (epoch 1 is skipped):
1. **Epoch 0 stakes that meet minimums become active** - validators need 42,000 GEN, delegators need 42 GEN
2. **Normal minimum requirements apply** for new joins/deposits
3. **+2 epoch activation delay** applies to all new deposits
4. **validatorPrime() becomes mandatory** for validators to remain in the selection pool
5. **Existing validators** must ensure their nodes begin calling `validatorPrime()` in epoch 2
#### FAQ: Epoch 0 Special Cases
**Q: Can I join as a validator with less than 42,000 GEN in epoch 0?**
A: Yes, any non-zero amount is accepted during epoch 0. However, you will **not be active** in epoch 2 unless you have at least 42,000 GEN staked by then.
**Q: If I delegate in epoch 0, when does it become active?**
A: In epoch 2. Unlike normal epochs with a +2 delay, epoch 0 delegations activate when the network becomes operational.
**Q: Do I need to call validatorPrime() in epoch 0?**
A: No. Priming is not required during epoch 0. Your node should start calling it automatically when epoch 2 begins.
**Q: Will my epoch 0 stake still be active after epoch 0 ends?**
A: Yes, if you meet the minimum requirements. Stakes from epoch 0 carry forward and remain active in all subsequent epochs.
**Q: What happens to my stake if I joined in epoch 0 but my node doesn't call validatorPrime() in epoch 2?**
A: You'll be excluded from validator selection in epoch 4, but your stake remains. Once priming resumes, you'll be eligible for selection again.
## Shares vs Stake
The staking system uses shares to track ownership:
**Shares** are fixed quantities that never change. You receive shares when depositing and exit by burning shares. They represent immutable claims on the stake pool.
**Stake** is the dynamic GEN token amount. It increases with rewards/fees and decreases with slashing. The exchange rate is calculated as:
```
stake_per_share = total_stake / total_shares
```
**Example**: 100 shares representing 1,000 GEN (10 GEN per share). After rewards are distributed, the same 100 shares might represent 1,050 GEN (10.5 GEN per share). Rewards automatically compound without user action.
## Validator Selection and Weight
Validators are selected for consensus based on their weight, calculated using:
```
Weight = (ALPHA × Self_Stake + (1-ALPHA) × Delegated_Stake)^BETA
```
**Parameters:**
- **ALPHA = 0.6**: Self-stake counts 50% more than delegated stake
- **BETA = 0.5**: Square-root damping prevents whale dominance
**Effects:**
- Higher stake leads to higher weight and higher selection probability
- Doubling stake only increases weight by approximately 41%
- Encourages distribution across validators
- Smaller validators often provide higher returns per GEN staked
## Reward Distribution
**Sources:**
1. Transaction Fees
2. Inflation (starting at 15% APR, decreasing to 4% APR over time)
**Distribution Pattern:**
- **10%** → Validator owners (operational fee)
- **75%** → Total validator stake (validators + delegators)
- **10%** → Developers
- **5%** → Locked future allocation for the DeepThought AI-DAO
Within the 75% stake allocation:
- Self-stake receives a portion based on the validator's own staked amount
- Delegated stake is split among delegators proportionally to their shares
Rewards automatically increase the stake-per-share ratio without requiring user action.
## Unbonding Period
Both validators and delegators face a **7-epoch unbonding period** when withdrawing:
- Prevents rapid stake movements that could destabilize the network
- Exit is not processed immediately - validator remains active until next `validatorPrime()` call at next epoch
- Exited tokens stop earning rewards only after the exit is processed in the next epoch
- Countdown starts from the exit epoch
- Funds become claimable when: `current_epoch >= exit_epoch + 7`
**Detailed Exit Flow**:
```
Epoch N: validatorExit(all_shares) called
→ Exit scheduled in contract state
→ Validator STILL ACTIVE and earning rewards
→ Can still be selected for consensus participation
Epoch N+1: validatorPrime() called (by anyone)
→ Exit processed: stake reduced to 0
→ Removed from validator tree
→ No longer active or earning rewards
→ Cannot be selected for new transactions
Epoch N+2: epochAdvance() called
→ Validator officially not in active validator set
→ Fully excluded from consensus operations
Epoch N+7: validatorClaim() callable
→ 7 epochs have passed since exit call (epoch N)
→ Tokens released to validator owner
```
## Validator Priming
`validatorPrime(address validator)` is a critical function that:
- Activates pending deposits
- Processes pending withdrawals
- Distributes previous epoch rewards
- Applies pending slashing penalties
- Sorts the validator into the selection tree
**Key Properties:**
- **Monitoring Required**: Ensure correct execution
- **Permissionless**: Anyone can call it
- **Incentivized**: Caller receives 1% of any slashed amount
- **Critical**: If the node fails to prime, the validator is excluded from the next epoch
- **No Loss**: Missing priming doesn't lose rewards, but the validator can't be selected
## Unstaking and Withdrawing
Both validators and delegators can withdraw their staked tokens, but must follow the unbonding process.
### For Validators
To stop validating or retrieve staked tokens, validators must:
1. **Calculate shares to exit**: Determine how many shares to withdraw (partial or full)
2. **Call `validatorExit(shares)`**: Initiate the unbonding process
3. **Wait 7 epochs**: Tokens are locked during the unbonding period
4. **Call `validatorClaim()`**: Retrieve tokens after unbonding completes
Validators can perform partial exits while remaining active, as long as their stake stays above the 42,000 GEN minimum.
### For Delegators
Delegators follow a similar process:
1. **Calculate shares to exit**: Use `sharesOf(delegator, validator)` to check current shares
2. **Call `delegatorExit(validator, shares)`**: Initiate unbonding for a specific validator
3. **Wait 7 epochs**: Tokens are locked during the unbonding period
4. **Call `delegatorClaim(delegator, validator)`**: Retrieve tokens after unbonding
**Important for delegators:**
- Exit each validator separately if delegating to multiple validators
- Claims are permissionless—anyone can trigger them on your behalf
- Tokens stop earning rewards immediately upon calling exit
- Multiple exits create separate withdrawals that can be claimed together
For detailed step-by-step instructions and code examples, see the [Staking Guide](/developers/staking-guide).
## Governance and Safeguards
- **24-Hour Delay**: All slashing actions have a governance delay period
- Parameters like ALPHA, BETA, minimum stakes, and unbonding periods are adjustable through governance
- Maximum 1,000 active validators per epoch (adjustable)
## Next Steps
- [Staking Guide](/developers/staking-guide) - Practical guide for staking operations
- [Unstaking](/understand-genlayer-protocol/core-concepts/optimistic-democracy/unstaking) - Detailed unstaking process
- [Slashing](/understand-genlayer-protocol/core-concepts/optimistic-democracy/slashing) - Slashing conditions and penalties
# understand-genlayer-protocol/core-concepts/optimistic-democracy/slashing.mdx
# Slashing in GenLayer
Slashing is a mechanism used in GenLayer to penalize validators who engage in behavior detrimental to the network. This ensures that validators act honestly and effectively, maintaining the integrity of the platform and the Intelligent Contracts executed within it.
By penalizing undesirable behavior, slashing helps align validators' incentives with those of the network and its users.
## Slashing Process
1. **Violation Detection**: The network identifies a violation, such as missing an execution window.
2. **Slash Calculation**: The amount to be slashed is calculated based on the specific violation and platform rules.
3. **Stake Reduction**: The slashed amount is deducted from the validator's stake.
4. **Finality**: The slashing becomes final after the Finality Window closes, ensuring that the validator's balance is finalized and accounts for any potential appeals.
## When Slashing Occurs
Validators in GenLayer can be slashed for several reasons:
1. **Missing Transaction Execution Window**: Validators are expected to execute transactions within a specified time frame. If a validator misses this window, they are penalized, ensuring that validators remain active and responsive.
2. **Missing Appeal Execution Window**: During the appeals process, validators must respond within a set time frame. If they fail to do so, they are slashed, which motivates validators to participate in the appeals process.
### Amount Slashed
The amount slashed varies based on the severity of the violation and the specific rules set by the GenLayer platform. The slashing amount is designed to be substantial enough to deter malicious or negligent behavior while not being excessively punitive for honest mistakes.
# understand-genlayer-protocol/core-concepts/optimistic-democracy/unstaking.mdx
# Unstaking in GenLayer
Unstaking in GenLayer is the process by which validators disengage their staked tokens from the network, ending their active participation as validators. They must initiate an unstaking process, which includes a cooldown period to finalize all pending transactions. This procedure ensures that all obligations are fulfilled and pending issues resolved, maintaining the network's integrity and securing the platform’s operations.
## How Unstaking Works
The unstaking process includes several key steps:
1. **Initiating Unstaking**: Validators initiate their exit from active duties by submitting an unstaking transaction, signaling their intention to cease participation in validating transactions.
2. **Validator Removal**: Once the unstaking request is made, the validator is promptly removed from the pool of active validators, meaning they will no longer receive new transactions or be called upon for appeal validations.
3. **Finality Period**: During this period, validators must wait for all transactions they have participated in to reach full finality. This is crucial to ensure that validators do not exit while still having potential influence over unresolved transactions. This cooldown period helps prevent the situation where new transactions with new finality windows could prevent them from ever achieving full finality on all transactions they were involved in.
4. **Withdrawing Stake**: After all transactions have achieved finality and no outstanding issues remain, validators and their delegators can safely withdraw their staked tokens and any accrued rewards.
## Purpose of Unstaking
The unstaking process is designed to:
- **Ensure Accountability**: By enforcing a Finality Window, validators are held accountable for their actions until all transactions they influenced are fully resolved. This prevents premature exit from the network and ensures that all potential disputes are settled.
- **Align Incentives**: The requirement for validators to wait through the Finality Window aligns their incentives with the long-term security and reliability of the network, promoting responsible participation.
- **Maintain Network Security**: The unstaking process discourages abrupt departures and ensures that validators address any possible security concerns related to their past validations before leaving.
## Implications for Validators and Delegators
For validators, this process mandates careful planning regarding their exit strategy from the network, considering the need to wait out the Finality Window. Delegators must also be patient, understanding that their assets will remain locked until their validator has cleared all responsibilities, safeguarding their investments from potential liabilities caused by unresolved validations.
# understand-genlayer-protocol/core-concepts/rollup-integration.mdx
# Rollup Integration
GenLayer leverages Ethereum rollups, such as ZKSync or Polygon CDK, to ensure scalability and compatibility with existing Ethereum infrastructure. This integration is crucial for optimizing transaction throughput and reducing fees while maintaining the security guarantees of the Ethereum mainnet.
## Key Aspects of Rollup Integration
### Scalability
- **High Transaction Throughput**: Rollups allow GenLayer to process a much higher number of transactions per second compared to Layer 1 solutions.
- **Reduced Congestion**: By moving computation off-chain, GenLayer helps alleviate congestion on the Ethereum mainnet.
### Cost Efficiency
- **Lower Transaction Fees**: Users benefit from significantly reduced gas fees compared to direct Layer 1 transactions.
- **Batched Submissions**: Transactions are batched and submitted to the Ethereum mainnet, distributing costs across multiple operations.
### Security
- **Ethereum Security Inheritance**: While execution happens off-chain, the security of assets and final state is guaranteed by Ethereum's robust consensus mechanism.
- **Fraud Proofs/Validity Proofs**: Depending on the specific rollup solution (Optimistic or ZK), security is ensured through either fraud proofs or validity proofs.
## How Rollup Integration Works with GenLayer
1. **Transaction Submission**: Users submit transactions to the rollup.
2. **Transaction Execution**: Transactions are executed within the GenVM environment.
3. **Consensus**: The rollup layer implements the Optimistic Democracy mechanism to reach consensus on the state updates.
4. **State Updates**: The rollup layer maintains an up-to-date state of all accounts and contracts.
5. **Batch Submission**: Periodically, batches of transactions and state updates are submitted to the Ethereum mainnet.
6. **Verification**: The Ethereum network verifies the integrity of the submitted data, ensuring its validity.
## Benefits for Developers and Users
- **Ethereum Compatibility**: Developers can leverage existing Ethereum tools and infrastructure.
- **Improved User Experience**: Lower fees and faster transactions lead to a better overall user experience.
## Considerations
- **Withdrawal Periods**: Depending on the rollup solution, there might be waiting periods for withdrawing assets back to the Ethereum mainnet.
- **Rollup-Specific Features**: Different rollup solutions may offer unique features or limitations that developers should be aware of.
By integrating with Ethereum rollups, GenLayer combines the innovative capabilities of Intelligent Contracts with the scalability and efficiency of Layer 2 solutions, creating a powerful platform for next-generation decentralized applications.
# understand-genlayer-protocol/core-concepts/non-deterministic-operations-handling.mdx
# Non-Deterministic Operations Handling
## Overview
GenLayer extends traditional smart contracts by allowing Intelligent Contracts to perform non-deterministic operations, such as interacting with Large Language Models (LLMs) and accessing web data. Handling the variability inherent in these operations is crucial for maintaining consensus across the network.
## Challenges
- **Variability of Outputs**: Non-deterministic operations can produce different outputs when executed by different validators.
- **Consensus Difficulty**: Achieving consensus on varying outputs requires specialized mechanisms.
## Solutions in GenLayer
- **Equivalence Principle**: Validators assess whether different outputs are equivalent based on predefined criteria, allowing for consensus despite variability.
- **Optimistic Democracy**: The consensus mechanism accommodates non-deterministic operations by allowing provisional acceptance of transactions and providing an appeals process.
## Developer Considerations
- **Defining Equivalence Criteria**: Developers must specify what constitutes equivalent outputs in their Intelligent Contracts.
- **Testing and Validation**: Thorough testing is essential to ensure that non-deterministic operations behave as expected in the consensus process.
# understand-genlayer-protocol/core-concepts/large-language-model-llm-integration.mdx
# Large Language Model (LLM) Integration
## Overview
Intelligent Contracts in GenLayer can interact directly with Large Language Models (LLMs), enabling natural language processing and more complex decision-making capabilities within blockchain applications.
## Key Features
- **Natural Language Understanding**: Contracts can process and interpret instructions written in natural language.
- **Dynamic Decision Making**: Utilizing LLMs allows contracts to make context-aware decisions based on complex inputs.
## Implementation
1. **LLM Providers**: Validators are configured with LLM providers (e.g., OpenAI, Ollama) to process LLM requests.
2. **Equivalence Principle**: LLM outputs are validated using the Equivalence Principle to ensure consensus among validators.
3. **Prompt Design**: Developers craft prompts to interact effectively with LLMs, specifying expected formats and constraints.
## Considerations
- **Cost and Performance**: LLM interactions may incur additional computational costs and latency.
- **Security**: Care must be taken to prevent prompt injections and ensure the reliability of LLM responses.
# understand-genlayer-protocol/core-concepts/web-data-access.mdx
# Web Data Access in Intelligent Contracts
## Overview
GenLayer enables Intelligent Contracts to directly access and interact with web data, removing the need for oracles and allowing for real-time data integration into blockchain applications.
## Key Features
- **Direct Web Access**: Contracts can retrieve data from web sources.
- **Dynamic Applications**: Access to web data allows for applications that respond to external events and real-world data.
- **Equivalence Validation**: Retrieved data is validated across validators to ensure consistency.
## Implementation
1. **Data Retrieval Functions**: GenLayer provides mechanisms for fetching web data within contracts.
2. **Data Parsing and Validation**: Contracts must parse web data and validate it according to defined equivalence criteria.
3. **Security Measures**: Contracts should handle potential security risks such as untrusted data sources and ensure data integrity.
## Considerations
- **Network Dependencies**: Reliance on external web sources introduces dependencies that may affect contract execution.
- **Performance Impact**: Web data retrieval may introduce latency and affect transaction processing times.
# understand-genlayer-protocol/core-concepts/transactions.mdx
# Transactions
Transactions are the fundamental operations that drive the GenLayer protocol. Whether it's deploying a new contract, sending value between accounts, or invoking a function within an existing contract, transactions are the means by which state changes occur on the network.
Here is the general structure of a transaction:
```json
{
"consensus_data": {
"leader_receipt": {
"args": [
[
2,
"0x793Ae2CfF17462cc9f9D68e194b7b949d2080Ea2"
]
],
"class_name": "LlmErc20",
"contract_state": "gASVnAAAAAAAAACMF2JhY2tlbmQubm9kZS5nZW52bS5iYXNllIwITGxtRXJjMjCUk5QpgZR9lIwIYmFsYW5jZXOUfZQojCoweEQyNzFjNzRBNzgwODNGMzU3YTlmOGQzMWQ1YWRDNTlCMzk1Y2YxNmKUS2KMKjB4NzkzQWUyQ2ZGMTc0NjJjYzlmOUQ2OGUxOTRiN2I5NDlkMjA4MEVhMpRLAnVzYi4=",
"eq_outputs": {
"leader": {
"0": "{\"transaction_success\": true, \"transaction_error\": \"\", \"updated_balances\": {\"0xD271c74A78083F357a9f8d31d5adC59B395cf16b\": 98, \"0x793Ae2CfF17462cc9f9D68e194b7b949d2080Ea2\": 2}}"
}
},
"error": null,
"execution_result": "SUCCESS",
"gas_used": 0,
"method": "transfer",
"mode": "leader",
"node_config": {
"address": "0x185D2108D9dE15ccf6beEb31774CA96a4f19E62B",
"config": {},
"model": "gpt-4o",
"plugin": "openai",
"plugin_config": {
"api_key_env_var": "OPENAIKEY",
"api_url": null
},
"provider": "openai",
"stake": 1
},
"vote": "agree"
},
"validators": [
{
"args": [
[
2,
"0x793Ae2CfF17462cc9f9D68e194b7b949d2080Ea2"
]
],
"class_name": "LlmErc20",
"contract_state": "gASVnAAAAAAAAACMF2JhY2tlbmQubm9kZS5nZW52bS5iYXNllIwITGxtRXJjMjCUk5QpgZR9lIwIYmFsYW5jZXOUfZQojCoweEQyNzFjNzRBNzgwODNGMzU3YTlmOGQzMWQ1YWRDNTlCMzk1Y2YxNmKUS2KMKjB4NzkzQWUyQ2ZGMTc0NjJjYzlmOUQ2OGUxOTRiN2I5NDlkMjA4MEVhMpRLAnVzYi4=",
"eq_outputs": {
"leader": {
"0": "{\"transaction_success\": true, \"transaction_error\": \"\", \"updated_balances\": {\"0xD271c74A78083F357a9f8d31d5adC59B395cf16b\": 98, \"0x793Ae2CfF17462cc9f9D68e194b7b949d2080Ea2\": 2}}"
}
},
"error": null,
"execution_result": "SUCCESS",
"gas_used": 0,
"method": "transfer",
"mode": "validator",
"node_config": {
"address": "0x31bc9380eCbF487EF5919eBa7457F457B5196FCD",
"config": {},
"model": "gpt-4o",
"plugin": "openai",
"plugin_config": {
"api_key_env_var": "OPENAIKEY",
"api_url": null
},
"provider": "openai",
"stake": 1
},
"pending_transactions": [],
"vote": "agree"
},
...
],
"votes": {
"0x185D2108D9dE15ccf6beEb31774CA96a4f19E62B": "agree",
"0x2F04Fb1e5daf7DCbf170E4CB0e427d9b11aB96cA": "agree",
"0x31bc9380eCbF487EF5919eBa7457F457B5196FCD": "agree"
}
},
"created_at": "2024-10-02T20:32:50.469443+00:00",
"data": {
"function_args": "[2,\"0x793Ae2CfF17462cc9f9D68e194b7b949d2080Ea2\"]",
"function_name": "transfer"
},
"from_address": "0xD271c74A78083F357a9f8d31d5adC59B395cf16b",
"gaslimit": 66,
"hash": "0xb7486f70a3fec00af5f929fc1cf1078af9ff3a063afe8b6f370a44a96635505d",
"leader_only": false,
"nonce": 66,
"r": null,
"s": null,
"status": "FINALIZED",
"to_address": "0x5929bB548a2Fd7E9Ea2577DaC9c67A08BbC2F356",
"type": 2,
"v": null,
"value": 0
}
```
## Explanation of fields:
- consensus_data: Object containing information about the consensus process
- leader_receipt: Object containing details about the leader's execution of the transaction
- args: Arguments passed to the contract function
- class_name: Name of the contract class
- contract_state: Encoded state of the contract
- eq_outputs: Outputs from every equivalence principle in the execution of the contract method
- error: Any error that occurred during execution (null if no error)
- execution_result: Result of the execution (e.g., "SUCCESS" or "ERROR")
- gas_used: Amount of gas used in the transaction
- method: Name of the method called on the contract
- mode: Execution mode (e.g., "leader" or "validator")
- node_config: Configuration of the node executing the transaction
- address: Address of the node
- config: Configuration of the node
- model: Model of the node
- plugin: Plugin used for the LLM provider connection
- plugin_config: Configuration of the plugin
- api_key_env_var: Environment variable containing the API key for the given provider
- api_url: API URL for the given provider
- provider: Provider of the node
- stake: Stake of the validator
- vote: The leader's vote on the transaction (e.g., "agree")
- validators: Array of objects containing similar information for each validator
- votes: Object mapping validator addresses to their votes
- created_at: Timestamp of when the transaction was created
- data: Object containing details about the function call in the transaction
- from_address: Address of the account initiating the transaction
- gaslimit: Maximum amount of gas the transaction is allowed to consume
- hash: Unique identifier (hash) of the transaction
- leader_only: Boolean indicating whether the transaction is to be executed by the leader node only
- nonce: Number of transactions sent from the from_address (used to prevent double-spending)
- r: Part of the transaction signature (null if not yet signed)
- s: Part of the transaction signature (null if not yet signed)
- status: Current status of the transaction (e.g., "FINALIZED")
- to_address: Address of the contract or account receiving the transaction
- type: Internal type of the transaction (2 indicates a contract write call)
- v: Part of the transaction signature (null if not yet signed)
- value: Amount of native currency (GEN) being transferred in the transaction
# understand-genlayer-protocol/core-concepts/transactions/types-of-transactions.mdx
# Types of Transactions
There are three different types of transactions that users can send in GenLayer. All three types are sent through the same RPC method, but they differ in the data they contain and the actions they perform.
## 1. Deploy a Contract
Deploying a contract involves creating a new Intelligent Contract on the GenLayer network. This transaction initializes the contract's state and assigns it a unique address on the blockchain. The deployment process ensures that the contract code is properly validated and stored, making it ready to be called.
### Example
```json
{
"consensus_data": {
"leader_receipt": {
"args": [
{
"total_supply": 100
}
],
"class_name": "LlmErc20",
"contract_state": "gASVnAAAAAAAAACMF2JhY2tlbmQubm9kZS5nZW52bS5iYXNllIwITGxtRXJjMjCUk5QpgZR9lIwIYmFsYW5jZXOUfZQojCoweEQyNzFjNzRBNzgwODNGMzU3YTlmOGQzMWQ1YWRDNTlCMzk1Y2YxNmKUS2KMKjB4NzkzQWUyQ2ZGMTc0NjJjYzlmOUQ2OGUxOTRiN2I5NDlkMjA4MEVhMpRLAnVzYi4=",
...
},
"validators": [
...
],
...
},
"data": {
"constructor_args": "{\"total_supply\":100}",
"contract_address": "0x5929bB548a2Fd7E9Ea2577DaC9c67A08BbC2F356",
"contract_code": "import json\nfrom backend.node.genvm.icontract import IContract\nfrom backend.node.genvm.equivalence_principle import EquivalencePrinciple\n\n\nclass LlmErc20(IContract):\n def __init__(self, total_supply: int) -> None:\n self.balances = {}\n self.balances[contract_runner.from_address] = total_supply\n...",
},
...
}
```
## 2. Send Value
Sending value refers to transferring the native GEN token from one account to another. This is one of the most common types of transactions. Each transfer updates the balance of the involved accounts, and the transaction is recorded on the blockchain to ensure transparency and security.
### Example
```json
{
"consensus_data": null,
"created_at": "2024-10-02T21:21:04.192995+00:00",
"data": {},
"from_address": "0x0Bd6441CB92a64fA667254BCa1e102468fffB3f3",
"gaslimit": 0,
"hash": "0x6357ec1e86f003b20964ef3b2e9e072c7c9521f92989b08e04459b871b69de89",
"leader_only": false,
"nonce": 2,
"r": null,
"s": null,
"status": "FINALIZED",
"to_address": "0xf739FDe22E0C0CB6DFD8f3F8D170bFC07329489E",
"type": 0,
"v": null,
"value": 200
}
```
## 3. Call Contract Function
Calling a contract function is the process of invoking a specific method within an existing Intelligent Contract. This could involve anything from querying data stored within the contract to executing more complex operations like transferring tokens or interacting with other contracts. Each function call is a transaction that modifies the contract’s state based on the inputs provided.
### Example
```json
{
"consensus_data": {
"leader_receipt": {
"args": [
[
2,
"0x793Ae2CfF17462cc9f9D68e194b7b949d2080Ea2"
]
],
"class_name": "LlmErc20",
"contract_state": "gASVnAAAAAAAAACMF2JhY2tlbmQubm9kZS5nZW52bS5iYXNllIwITGxtRXJjMjCUk5QpgZR9lIwIYmFsYW5jZXOUfZQojCoweEQyNzFjNzRBNzgwODNGMzU3YTlmOGQzMWQ1YWRDNTlCMzk1Y2YxNmKUS2KMKjB4NzkzQWUyQ2ZGMTc0NjJjYzlmOUQ2OGUxOTRiN2I5NDlkMjA4MEVhMpRLAnVzYi4=",
"eq_outputs": {
"leader": {
"0": "{\"transaction_success\": true, \"transaction_error\": \"\", \"updated_balances\": {\"0xD271c74A78083F357a9f8d31d5adC59B395cf16b\": 98, \"0x793Ae2CfF17462cc9f9D68e194b7b949d2080Ea2\": 2}}"
}
},
...
},
"validators": [
...
],
...
},
"data": {
"function_args": "[2,\"0x793Ae2CfF17462cc9f9D68e194b7b949d2080Ea2\"]",
"function_name": "transfer"
},
...
}
```
* For a list of all the fields in a transaction, see [here](/core-concepts/transactions)
# understand-genlayer-protocol/core-concepts/transactions/transaction-statuses.mdx
# Transaction Processing
In GenLayer, transactions are processed through an account-based queue system that ensures orderliness. Here’s how transactions transition through different statuses:
## 1. Pending
When a transaction is first submitted, it enters the pending state. This means it has been received by the network but is waiting to be processed. Transactions are queued per account, ensuring that each account's transactions are processed in the order they were submitted.
## 2. Proposing
In this stage, the transaction is moved from the pending queue to the proposing stage. A leader and a set of voters are selected from the validator set via a weighted random selection based on total stake. The leader proposes a receipt for the transaction, which is then committed to by the validators.
## 3. Committing
The transaction enters the committing stage, where validators commit their votes and cost estimates for processing the transaction. This stage is crucial for reaching consensus on the transaction's execution.
## 4. Revealing
After the committing stage, validators reveal their votes and cost estimates, allowing the network to finalize the transaction's execution cost and validate the consensus.
## 5. Accepted
Once the majority of validators agree on the transaction's validity and cost, the transaction is marked as accepted. This status indicates that the transaction has passed through the initial validation process successfully.
## 6. Finalized
After all validations are completed and any potential appeals have been resolved, the transaction is finalized. In this state, the transaction is considered irreversible and is permanently recorded in the blockchain.
## 7. Undetermined
If the transaction fails to reach consensus after all voting rounds, it enters the undetermined state. This status indicates that the transaction's outcome is unresolved, and it may require further validation or be subject to an appeal process.
## 8. Canceled
A transaction can be canceled by the user or by the system if it fails to meet certain criteria (e.g., insufficient funds). Once canceled, the transaction is removed from the processing queue and will not be executed.
# understand-genlayer-protocol/core-concepts/transactions/transaction-execution.mdx
## Transaction execution
Once a transaction is received and properly verified by the `eth_sendRawTransaction` method on the RPC server, it is stored with a PENDING status and its hash is returned as the RPC method response. This means that the transaction has been validated for authenticity and format, but it has not yet been executed. From this point, the transaction enters the GenLayer consensus mechanism, where it is picked up for execution by the network's validators according to the consensus rules.
As the transaction progresses through various stages—such as proposing, committing, and revealing—its status is updated accordingly. Throughout this process, the current status and output of the transaction can be queried by the user. This is done by calling the `eth_getTransactionByHash` method on the RPC server, which retrieves the transaction's details based on its unique hash. This method allows users to track the transaction's journey from submission to finalization, providing transparency and ensuring that they can monitor the outcome of their transactions in real-time.
### Transaction Status Transitions
In the journey of a transaction within the GenLayer protocol, it begins its life when an Externally Owned Account (EOA) submits it, entering the `Pending` state. Here, it awaits further processing unless it encounters an `OutOfFee` state due to insufficient fees. If the fees are topped up, it returns to `Pending`. Alternatively, the user can cancel the transaction, moving it to the `Canceled` state.
From `Pending`, the transaction progresses to the `Proposing` stage, where a leader is selected to propose a receipt. Upon successful proposal, it advances to the `Committing` stage, where all validators must commit to the transaction. If all validators commit, the transaction moves to the `Revealing` stage.
In the `Revealing` stage, the transaction's fate is determined. If a majority agrees, it is `Accepted`. However, if there is no majority agreement, it returns to `Proposing`. If all leaders are rotated without agreement, it becomes `Undetermined`. In cases of disagreement, a successful appeal can revert it to `Pending`, while a failed appeal results in `Accepted`.
Once `Accepted`, the transaction awaits the passing of the appeal window to become `Finalized`. An `Undetermined` transaction also becomes `Finalized` after the appeal window passes. However, an appeal can initiate a return to `Committing` from `Accepted`, or automatically revert an `Undetermined` transaction to `Pending`.
```mermaid
graph TD;
Start(( )) -->|EOA submits| Pending(Pending)
Start -->|Insufficient fee| OutOfFee(OutOfFee)
OutOfFee -->|Fee topped up| Pending
Pending -->|User cancels| Canceled(Canceled)
Pending -->|Select leader| Proposing(Proposing)
Proposing -->|Leader proposes receipt| Committing(Committing)
Committing -->|All validators commit| Revealing(Revealing)
Revealing -->|Majority agrees| Accepted(Accepted)
Revealing -->|No majority agreement| Proposing
Revealing -->|All leaders rotated| Undetermined(Undetermined)
Revealing -.->|Disagreement, appeal successful| Pending
Revealing -.->|Agreement, appeal failed| Accepted
Accepted -->|Appeal window passes| Finalized(Finalized)
Undetermined -->|Appeal window passes| Finalized
Accepted -.->|Appeal initiated| Committing
Undetermined -->|Appeal automatically| Pending
```
# understand-genlayer-protocol/core-concepts/transactions/transaction-encoding-serialization-and-signing.mdx
## Transaction encoding, serialization, and signing
In GenLayer, all three types of transactions needs to be properly encoded, serialized, and signed on the client-side. This process ensures that the transaction data is packaged into a relieable cross-platform efficient format, and securely signed using the sender's private key to verify the sender's identity.
Once prepared, the transaction is sent to the network via the `eth_sendRawTransaction` method on the RPC Server. This method performs the inverse process: it decodes and deserializes the transaction data, and then verifies the signature to ensure its authenticity. By handling all transaction types through `eth_sendRawTransaction`, GenLayer ensures that transactions are processed securely and efficiently while maintaining compatibility with Ethereum’s specification.
# understand-genlayer-protocol/core-concepts/economic-model.mdx
# Economic Model
## Overview
GenLayer's economic model is designed to incentivize participants to maintain the network's security and functionality. It involves staking, rewards, transaction fees, and penalties.
## Key Components
- **Staking**: Validators must stake tokens to participate in the validation process, aligning their interests with the network's health.
- **Rewards**: Validators receive rewards for correctly validating transactions.
- **Transaction Fees**: Users pay fees for transaction processing, which are partly used to reward validators.
- **Slashing**: Validators acting maliciously or incompetently can have their staked tokens slashed as a penalty.
## Incentive Mechanisms
- **Positive Incentives**: Rewards and fees motivate validators to act in the network's best interest.
- **Negative Incentives**: Slashing and penalties deter malicious behavior.
## Economic Security
- **Stake-Based Security**: The amount staked by validators serves as a deterrent against attacks, as they risk losing their stake.
- **Balancing Supply and Demand**: The economic model aims to balance the supply of validation services with demand from users.
# understand-genlayer-protocol/core-concepts/accounts-and-addresses.mdx
# Accounts and Addressing
## Overview
Accounts are fundamental to interacting with the GenLayer network. They represent users or entities that can hold tokens, deploy Intelligent Contracts, and initiate transactions.
## Types of Accounts
1. **Externally Owned Accounts (EOAs)**:
- Controlled by private keys
- Can initiate transactions and hold tokens
2. **Contract Accounts**:
- Associated with deployed Intelligent Contracts
- Have their own addresses and code
## Account Addresses
- **Address Format**: GenLayer uses a specific address format, typically represented as a hexadecimal string prefixed with `0x`.
- **Public and Private Keys**: Addresses are derived from public keys, which in turn are generated from private keys kept securely by the account owner.
## Interacting with Intelligent Contracts
- **Transaction Sending**: Accounts initiate transactions to call functions on Intelligent Contracts or transfer tokens.
- **Gas Fees**: Transactions require gas fees to be processed.
## Account Management
- **Creating Accounts**: Users can create new accounts using wallets or development tools provided by GenLayer.
- **Security Practices**: Users must securely manage their private keys, as losing them can result in loss of access to their funds.
# developers.mdx
import { Card, Cards } from 'nextra-theme-docs'
# Getting Started with GenLayer
Welcome to the GenLayer getting started guide. This guide helps developers exploit the full potential of the GenLayer environment. This documentation will provide you with the tools and knowledge needed to build, deploy, and manage Intelligent Contracts on GenLayer.
The next topics focus on key areas of the development process. From setting up your development environment and writing your first Intelligent Contract to exploring advanced features and best practices, these resources are designed to support your journey in creating cutting-edge decentralized applications on GenLayer. Click on any link to delve into detailed guides and enhance your development skills with GenLayer.
import { Tabs } from 'nextra/components'
# developers/networks.mdx
import { Callout } from 'nextra-theme-docs'
import AddToWallet from '../../components/AddToWallet'
# Networks
GenLayer operates as two layers: the **GenLayer RPC** handles intelligent contract operations (`gen_*` methods), while the **GenLayer Chain** is the underlying L2 (zkSync Elastic Chain) that handles standard Ethereum operations (`eth_*` methods). The GenLayer RPC also passes through all `eth_*` and `zks_*` calls to the underlying chain, so you can use either endpoint for standard wallet operations.
For the full RPC reference, see [GenLayer Node API](/api-references/genlayer-node).
Your wallet connects to the **GenLayer RPC** — it handles both intelligent contract calls and standard Ethereum methods.
---
## Testnet Bradbury
Production-like testnet with real AI/LLM workloads.
| Setting | Value |
|:--|:--|
| **GenLayer RPC** | `https://rpc-bradbury.genlayer.com` |
| **GenLayer Chain RPC** | `https://rpc.testnet-chain.genlayer.com` |
| **Chain ID** | 4221 |
| **Currency** | GEN |
| **Explorer** | [explorer-bradbury.genlayer.com](https://explorer-bradbury.genlayer.com) |
| **Chain Explorer** | [explorer.testnet-chain.genlayer.com](https://explorer.testnet-chain.genlayer.com/) |
| **Faucet** | [testnet-faucet.genlayer.foundation](https://testnet-faucet.genlayer.foundation) |
---
## Testnet Asimov
Infrastructure and stress testing.
| Setting | Value |
|:--|:--|
| **GenLayer RPC** | `https://rpc-asimov.genlayer.com` |
| **GenLayer Chain RPC** | `https://rpc.testnet-chain.genlayer.com` |
| **Chain ID** | 4221 |
| **Currency** | GEN |
| **Explorer** | [explorer-asimov.genlayer.com](https://explorer-asimov.genlayer.com) |
| **Chain Explorer** | [explorer.testnet-chain.genlayer.com](https://explorer.testnet-chain.genlayer.com/) |
| **Faucet** | [testnet-faucet.genlayer.foundation](https://testnet-faucet.genlayer.foundation) |
---
## Studionet
Hosted development environment — no local setup required.
| Setting | Value |
|:--|:--|
| **GenLayer RPC** | `https://studio.genlayer.com/api` |
| **Chain ID** | 61999 |
| **Currency** | GEN |
| **Explorer** | [explorer-studio.genlayer.com](https://explorer-studio.genlayer.com) |
| **Faucet** | Built-in — use the 💧 button in the account selector |
---
## Localnet
Local development with full control. Requires [GenLayer Studio](/developers/intelligent-contracts/tools/genlayer-studio) or [GLSim](/api-references/genlayer-test/glsim).
| Setting | Value |
|:--|:--|
| **GenLayer RPC** | `http://localhost:4000/api` |
| **Chain ID** | 61127 |
| **Currency** | GEN |
| **Explorer** | Bundled with Studio at `http://localhost:8080` |
| **Faucet** | Built-in — use the 💧 button in the account selector |
---
## GenLayer Chain (L2)
The underlying zkSync Elastic Chain that GenLayer runs on. You typically don't need to interact with this directly — the GenLayer RPC passes through all `eth_*` calls. But if you need direct L2 access (e.g., token transfers, contract debugging at the EVM level), you can add this chain to your wallet.
| Setting | Value |
|:--|:--|
| **RPC** | `https://rpc.testnet-chain.genlayer.com` |
| **Chain ID** | 4221 |
| **Currency** | GEN |
| **Explorer** | [explorer.testnet-chain.genlayer.com](https://explorer.testnet-chain.genlayer.com/) |
---
## Network Comparison
| | Bradbury | Asimov | Studionet | Localnet |
|---|---|---|---|---|
| **Purpose** | Production-like testing | Infrastructure testing | Hosted dev | Local dev |
| **Setup** | None — connect and go | None — connect and go | None — browser only | Docker or GLSim |
| **Persistence** | Persistent | Persistent | Temporary | Local only |
| **LLM execution** | Real models | Real models | Real models | Configurable |
| **Faucet** | [Available](https://testnet-faucet.genlayer.foundation) | [Available](https://testnet-faucet.genlayer.foundation) | Built-in (💧 button) | Built-in (💧 button) |
## Recommended Flow
1. **Start on Studionet** — zero setup, open [studio.genlayer.com](https://studio.genlayer.com)
2. **Move to Localnet** — when you need full control and fast iteration
3. **Deploy to Bradbury** — when ready for production-like testing with real AI workloads
# developers/intelligent-contracts/introduction.mdx
# Introduction to Intelligent Contracts
## What are Intelligent Contracts?
Intelligent Contracts are an advanced evolution of smart contracts that combine traditional blockchain capabilities with natural language processing and web connectivity. Built in Python, they enable developers to create contracts that can understand human language, process external data, and make complex decisions based on real-world information.
## Key Features of Intelligent Contracts
### Natural Language Processing (NLP)
Intelligent Contracts leverage Large Language Models (LLMs) to process and understand human language inputs. This integration enables the contracts to interpret complex text-based instructions and requirements, moving beyond simple conditional logic.
Through NLP capabilities, these contracts can analyze qualitative criteria and make nuanced decisions based on contextual understanding, bringing a new level of intelligence to contract execution.
### Web Connectivity
These contracts can actively interact with web APIs to fetch real-time information, enabling dynamic decision-making based on current data. By incorporating external services for data verification, they maintain a reliable connection to real-world events and conditions, bridging the gap between on-chain and off-chain environments.
### Non-Deterministic Operations
Intelligent Contracts introduce a sophisticated approach to handling operations with unpredictable outputs, a significant advancement over traditional deterministic smart contracts. Through the implementation of a built-in equivalence principle, multiple validators can reach consensus even when processing non-deterministic results. This system supports both comparative validation, where outputs are directly matched, and non-comparative validation, where validators assess the reasonableness of results within defined parameters.
## How Do Intelligent Contracts Work?
### Contract Structure
Intelligent Contracts are written in Python using the GenVM SDK library. The basic structure consists of:
1. Dependencies Declaration: Specify required GenVM SDK modules
2. Declare Contract: Extend `gl.Contract` to define a contract class
3. State Variables: Declare with type annotations for strong typing
4. Methods:
- `@gl.public.view`: Read-only methods that don't modify state
- `@gl.public.write`: Methods that can modify contract state
- `@gl.public.write.payable`: Methods that can modify contract state *and* receive `value`
Here's an example:
```py
# { "Depends": "py-genlayer:1jb45aa8ynh2a9c9xn3b7qqh8sm5q93hwfp7jqmwsfhh8jpz09h6" }
from genlayer import *
class MyContract(gl.Contract):
# State variables with type annotations
variable: str
def __init__(self):
self.variable = "initial value"
@gl.public.view
def read_method(self) -> str:
return self.variable
@gl.public.write
def write_method(self, new_value: str):
self.variable = new_value
```
### Validation Process
- When transactions are submitted to Intelligent Contracts, they are automatically queued in a contract-specific order and marked with a "pending" status
- A randomly selected group of validators is assigned to process the transaction, with one designated as the leader to propose the outcome
- Once all validators evaluate the proposal and reach consensus using the equivalence principle, the transaction is accepted and enters the Finality Window
[Learn more about the validation process](/about-genlayer/core-concepts/optimistic-democracy)
## Advantages over Traditional Smart Contracts
### Enhanced Decision Making
Intelligent Contracts can process complex and qualitative criteria that traditional smart contracts cannot handle. Through their natural language understanding capabilities, they can interpret and act on human-readable inputs without requiring strict formatting or coding syntax.
This flexibility allows the contracts to dynamically adapt to changing conditions, making them more responsive and intelligent in their decision-making processes.
### External Data Integration
Intelligent Contracts can seamlessly integrate with external data sources, providing direct access to real-world information without intermediate layers. Their real-time data processing capabilities ensure that contract decisions are based on current and accurate information.
This direct connectivity significantly reduces the traditional reliance on oracle services, making the contracts more efficient and cost-effective.
### Flexible Programming
Development of Intelligent Contracts leverages Python's robust ecosystem, providing developers with a familiar and powerful programming environment.
The platform supports the data structures needed to handle complex business logic and requirements.
## Trade-offs
Intelligent Contracts introduce non-determinism and external dependencies that traditional smart contracts avoid. Key considerations:
- **Non-deterministic outputs** — LLM responses and web data vary across validators. The [Equivalence Principle](/developers/intelligent-contracts/features/non-determinism) lets you define how validators compare results to reach consensus.
- **External data reliability** — Web sources can be inconsistent or unavailable. Multiple validators independently fetch and cross-validate data as part of [Optimistic Democracy](/understand-genlayer-protocol/optimistic-democracy-how-genlayer-works).
- **Compute cost** — LLM calls and web requests add latency and cost compared to purely deterministic contracts. Design contracts to minimize non-deterministic calls where possible.
# developers/intelligent-contracts/features.mdx
import { Card, Cards } from 'nextra-theme-docs'
# Intelligent Contract Features
## Deterministic Features
## Non-Deterministic Features
# developers/intelligent-contracts/features/storage.mdx
import { Callout } from "nextra-theme-docs";
# Storage
## Basic Types
Store persistent data using class attributes:
```python
class Contract(gl.Contract):
counter: u32
name: str
active: bool
@gl.public.write
def set_data(self, count: u32, new_name: str):
self.counter = count
self.name = new_name
```
## DynArray
Use `DynArray[T]` instead of `list[T]` for persistent arrays:
```python
class Contract(gl.Contract):
items: DynArray[str]
scores: DynArray[u32]
@gl.public.write
def add_item(self, item: str):
self.items.append(item)
@gl.public.view
def get_all_items(self):
return [item for item in self.items]
```
## TreeMap
Use `TreeMap[K, V]` instead of `dict[K, V]` for persistent mappings:
```python
class Contract(gl.Contract):
balances: TreeMap[str, u32]
@gl.public.write
def update_balance(self, user: str, amount: u32):
self.balances[user] = amount
@gl.public.view
def get_balance(self, user: str):
return self.balances.get(user, u32(0))
```
## Storage Classes
Create custom storage types with `@allow_storage`:
```python
@allow_storage
@dataclass
class UserData:
scores: DynArray[u32]
username: str
class Contract(gl.Contract):
users: DynArray[UserData]
@gl.public.write
def add_user(self, name: str):
user = UserData(scores=DynArray[u32](), username=name)
self.users.append(user)
```
## Memory Operations
Copy storage objects to memory for non-deterministic operations:
```python
@gl.public.write
def process_user(self):
storage_user = self.users[0]
memory_user = gl.storage.copy_to_memory(storage_user)
def nondet_operation():
return f"User: {memory_user.username}"
result = gl.eq_principle.strict_eq(nondet_operation)
```
In future reading from storage directly in non-deterministic blocks will be supported.
## Type Restrictions
- Use `DynArray[T]` instead of `list[T]`
- Use `TreeMap[K, V]` instead of `dict[K, V]`
- Use sized integers (`u32`, `i64`) instead of `int`
- Use `bigint` only for arbitrary precision needs
- All generic types must be fully specified
## Default Values
Storage is zero-initialized:
| Type | Default value |
|------------|---------------|
| `u*`, `i*` | `0` |
| `bigint` | `0` |
| `bool` | `False` |
| `float` | `+0.0` |
| `str` | `""` |
| `bytes` | `b""` |
| `Address` | `0x0...` |
| `DynArray` | `[]` |
| `TreeMap` | `{}` |
# developers/intelligent-contracts/features/error-handling.mdx
# Error Handling
## Unrecoverable Errors
Exit codes terminate execution immediately:
```python
if invalid_condition:
exit(1)
```
Unhandled exceptions also become unrecoverable:
```python
raise Exception("Critical error") # Becomes exit(1)
```
## UserError
User-generated errors with UTF-8 encoded messages:
```python
# Can be caught in current sub-vm
raise gl.vm.UserError("Invalid input")
# Immediate user error, more efficient but can't be caught
gl.advanced.user_error_immediate("Insufficient funds")
```
## VMError
VM-generated errors with predefined string codes:
```python
# Non-zero exit codes become VMError
exit(1) # Results in VMError with specific code
# Resource limit violations also trigger VMError
# (handled automatically by the VM)
```
## Catching UserError
Handle user errors from sub-VMs:
```python
def risky_operation():
raise gl.vm.UserError("Operation failed")
try:
result = gl.eq_principle.strict_eq(risky_operation)
except gl.vm.UserError as e:
print(f"Caught user error: {e.message}")
```
## Error Propagation
Errors flow from non-deterministic to deterministic code:
```python
def nondet_block():
if some_condition:
raise gl.vm.UserError("INVALID_STATE")
return "success"
try:
gl.eq_principle.strict_eq(nondet_block)
except gl.vm.UserError as e:
if e.message == "INVALID_STATE":
# Handle specific error condition
pass
```
## VM Result Types
GenVM produces four result types:
- **Return** - Successful execution with encoded result
- **VMError** - VM errors (exit codes, resource limits)
- **UserError** - User-generated errors with UTF-8 messages
- **InternalError** - Critical VM failures (not visible to contracts)
## Error Patterns for Consensus
When using `run_nondet_unsafe`, errors affect how validators compare results. If the leader errors, the validator needs to decide: do I agree or disagree?
One approach is to classify errors by their nature, so validators can handle each type appropriately:
- **Deterministic errors** (business logic, client errors): should match exactly between leader and validator
- **Transient errors** (network issues, server errors): both hitting a transient failure is expected
- **LLM errors** (malformed output): disagreeing forces a rotation to a new leader, which is usually what you want
### Example: Error Classification
```python
def validator_fn(leaders_res: gl.vm.Result) -> bool:
if not isinstance(leaders_res, gl.vm.Return):
# Leader errored — run the same logic to see if we error too
leader_msg = leaders_res.message if hasattr(leaders_res, 'message') else ''
try:
leader_fn()
return False # Leader errored but we succeeded — disagree
except gl.UserError as e:
validator_msg = str(e)
# Both hit the same business logic error — agree
if validator_msg == leader_msg:
return True
return False
except Exception:
return False
# Leader succeeded — validate the result
validator_result = leader_fn()
return compare_results(leaders_res.calldata, validator_result)
```
This pattern ensures that consensus failures on broken LLM output or transient network issues lead to retries rather than locking in bad state.
# developers/intelligent-contracts/features/upgradability.mdx
import { Callout } from "nextra-theme-docs";
# Upgradability
GenVM provides a native contract upgradability system that allows contracts to be modified after deployment while maintaining security guarantees and clear access controls.
The system is built around the **Root Slot** (`gl.storage.Root`), which stores:
| Field | Description |
|---|---|
| `code` | The contract's source code |
| `locked_slots` | Storage slots that non-upgraders cannot write to |
| `upgraders` | Addresses authorized to modify locked slots (including code) |
## How It Works
1. At the start of a write transaction, GenVM reads the `upgraders` list
2. If the sender **is** in the `upgraders` list, no slot restrictions apply — the sender can modify any slot, including `code`
3. If the sender is **not** in the `upgraders` list, GenVM reads `locked_slots` and prevents writes to them
During deployment (`__init__`), after the constructor completes, the runtime automatically calls `root.lock_default()`, which locks four critical slots: the root slot, the code slot, the locked_slots slot, and the upgraders slot.
## Making a Contract Upgradable
To make a contract upgradable, you need to:
1. Add authorized upgrader addresses in `__init__`
2. Expose a method that replaces the contract code
```python
# v0.1.0
# { "Depends": "py-genlayer:1jb45aa8ynh2a9c9xn3b7qqh8sm5q93hwfp7jqmwsfhh8jpz09h6" }
from genlayer import *
class UpgradableStorage(gl.Contract):
storage: str
def __init__(self, initial_storage: str):
self.storage = initial_storage
# Set the deployer as an upgrader
root = gl.storage.Root.get()
root.upgraders.get().append(gl.message.sender_address)
# lock_default() is called automatically after __init__
# it locks: root slot, code, locked_slots, upgraders
@gl.public.view
def get_storage(self) -> str:
return self.storage
@gl.public.write
def update_storage(self, new_storage: str) -> None:
self.storage = new_storage
@gl.public.write
def upgrade(self, new_code: bytes) -> None:
root = gl.storage.Root.get()
code = root.code.get()
# If sender is not in upgraders, this will raise a VMError
code.truncate()
code.extend(new_code)
```
## Upgrading to a New Version
The upgraded contract code must maintain the **same storage layout** for compatibility. Only the code changes — all storage data, the upgraders list, and locked slots persist across upgrades.
```python
# v0.1.0
# { "Depends": "py-genlayer:1jb45aa8ynh2a9c9xn3b7qqh8sm5q93hwfp7jqmwsfhh8jpz09h6" }
from genlayer import *
class UpgradableStorage(gl.Contract):
# Storage layout must remain compatible with v1
storage: str
def __init__(self):
pass
@gl.public.view
def get_storage(self) -> str:
return self.storage
@gl.public.write
def update_storage(self, new_storage: str) -> None:
self.storage = new_storage
# New method added in v2
@gl.public.view
def get_storage_length(self) -> int:
return len(self.storage)
# Keep the upgrade method for future upgrades
@gl.public.write
def upgrade(self, new_code: bytes) -> None:
root = gl.storage.Root.get()
code = root.code.get()
code.truncate()
code.extend(new_code)
```
## What Happens During an Upgrade
When an authorized upgrader calls the `upgrade` method with new code:
| Component | After upgrade |
|---|---|
| `code` | **Replaced** with new code |
| Contract storage data (e.g. `storage: str`) | **Persists** unchanged |
| `locked_slots` | **Persists** unchanged |
| `upgraders` | **Persists** unchanged |
This means:
- **Upgrades are not one-shot** — since the upgraders list persists, the same addresses can push another upgrade later
- **Storage must be compatible** — the new code must understand the existing storage layout. There is no automatic migration mechanism
- **New methods can be added** — but existing storage field positions must not change
- **Upgraders can be modified** — an upgrader can add or remove addresses from the upgraders list
## Freezing a Contract
To make a contract permanently non-upgradable, either:
- Call `root.lock_default()` without adding any addresses to `upgraders` — the code and critical slots are locked and nobody can unlock them
- Remove all addresses from the `upgraders` list after locking
Once a contract is frozen (locked slots with no upgraders), it cannot be upgraded. This is irreversible.
## Testing Upgrades
Using the [GenLayer Testing Suite](/developers/intelligent-contracts/tools/genlayer-testing-suite), you can test the full upgrade lifecycle:
```python
from pathlib import Path
from gltest import get_contract_factory
from gltest.assertions import tx_execution_succeeded
CONTRACTS_DIR = Path(__file__).parent.parent / "contracts"
def test_upgradable_storage():
# Deploy v1
factory = get_contract_factory(
contract_file_path=CONTRACTS_DIR / "upgradable_storage.py"
)
contract = factory.deploy(args=["hello"])
# Use v1 methods
assert contract.get_storage(args=[]).call() == "hello"
# Read v2 code and upgrade the contract
v2_code = (CONTRACTS_DIR / "upgradable_storage_v2.py").read_bytes()
tx = contract.upgrade(args=[v2_code]).transact()
assert tx_execution_succeeded(tx)
# Rebuild the contract proxy from the v2 schema
v2_factory = get_contract_factory(
contract_file_path=CONTRACTS_DIR / "upgradable_storage_v2.py"
)
contract_v2 = v2_factory.build_contract(contract_address=contract.address)
# Storage persists across upgrades
assert contract_v2.get_storage(args=[]).call() == "hello"
# New v2 method works
assert contract_v2.get_storage_length(args=[]).call() == 5
```
See [GenVM specification](https://sdk.genlayer.com/v0.2.7/spec/04-contract-interface/04-upgradability.html) for the full technical details.
# developers/intelligent-contracts/features/value-transfers.mdx
import { Callout } from "nextra-theme-docs";
# Value Transfers
## Native Token (GEN)
GenLayer uses GEN as its native token. Values are denominated in wei (1 GEN = 10¹⁸ wei). Use the `u256` type for value amounts in contract code.
## Receiving Value
Mark a method as payable with `@gl.public.write.payable` to accept GEN:
```python
from genlayer import *
class TipJar(gl.Contract):
def __init__(self):
self.total_tips = u256(0)
@gl.public.write.payable
def tip(self) -> None:
v = gl.message.value
if v == u256(0):
raise gl.vm.UserError("send some value")
self.total_tips = self.total_tips + v
@gl.public.view
def get_tips(self) -> u256:
return self.total_tips
```
`gl.message.value` is a `u256` containing the GEN sent with the call. It is only available in methods decorated with `@gl.public.write.payable`.
## Sending Value to Another Intelligent Contract
Send value to another IC using [internal messages](/developers/intelligent-contracts/features/messages#internal-messages-ic--ic):
```python
other = gl.get_contract_at(recipient_address)
# Pure value transfer (triggers __receive__ on recipient)
other.emit_transfer(value=u256(amount), on='finalized')
# Value + method call (recipient method must be payable)
other.emit(value=u256(amount), on='finalized').deposit()
```
### How Value Flows
When a message with value is emitted, the value is **immediately deducted** from the sending contract's balance and held in the message. It is only credited to the recipient when the message's child transaction is activated. The flow is:
**Sender balance → message → recipient balance**
If the child transaction fails, the value is not automatically returned to the sender.
See [Messages](/developers/intelligent-contracts/features/messages) for details on timing (`on='accepted'` vs `on='finalized'`).
## Sending Value to an EOA or EVM Contract
Sending to an address on the GenLayer Chain (EOA or EVM contract) is an [external message](/developers/intelligent-contracts/features/messages#external-messages-ic--chain-layer). It goes through the IC's [ghost contract](/developers/intelligent-contracts/features/messages#ghost-contracts) and always executes on finalization.
```python
@gl.evm.contract_interface
class _Recipient:
class View:
pass
class Write:
pass
class Faucet(gl.Contract):
@gl.public.write.payable
def send(self, recipient: str) -> None:
v = gl.message.value
if v == u256(0):
raise gl.vm.UserError("send some value")
_Recipient(Address(recipient)).emit_transfer(value=v)
```
The syntax for sending to an EOA uses the EVM contract interface even though the recipient is not a contract. This is because EOAs live on the chain layer, making this an external message — the same mechanism used for calling EVM contracts. This will be simplified in a future version.
## Reading Balances
```python
# Own contract balance
my_balance = self.balance
# Another IC's balance
other_balance = gl.get_contract_at(addr).balance
# EVM contract balance
evm_balance = EthContract(addr).balance
```
In `write` methods, `self.balance` reflects the current state including any value received in the current call. In `view` methods, it shows a snapshot without modifications.
### Where Balance Lives
On the GenLayer network, an IC's GEN balance is held by its [ghost contract](/developers/intelligent-contracts/features/messages#ghost-contracts) on the chain layer. `self.balance` reads from this. The balance is visible on both layers.
**Studio:** Balances are simulated in a local database. There is no EVM layer or ghost contracts in Studio.
## Receiving Value Without a Method Call
When value is sent without specifying a method, the contract can handle it with special methods:
```python
class Contract(gl.Contract):
@gl.public.write.payable
def __receive__(self):
"""Called for value-only transfers with no method name."""
pass
@gl.public.write.payable
def __handle_undefined_method__(
self, method_name: str, args: list, kwargs: dict
):
"""Fallback for calls to undefined methods."""
pass
```
The dispatch logic:
```mermaid
graph TD
msg>incoming call] --> has_method[method exists?]
has_method -->|yes| call_method{{call method}}
has_method -->|no| has_value[has value?]
has_value -->|yes| has_method_name[has method name?]
has_method_name -->|no| has_receive[__receive__ defined?]
has_receive -->|yes| call_receive{{call __receive__}}
has_receive -->|no| has_fallback1[__handle_undefined_method__ defined and payable?]
has_fallback1 -->|yes| call_fallback1{{call __handle_undefined_method__}}
has_fallback1 -->|no| error1{{error}}
has_method_name -->|yes| has_fallback2[__handle_undefined_method__ defined and payable?]
has_fallback2 -->|yes| call_fallback2{{call __handle_undefined_method__}}
has_fallback2 -->|no| error2{{error}}
has_value -->|no| has_fallback3[__handle_undefined_method__ defined?]
has_fallback3 -->|yes| call_fallback3{{call __handle_undefined_method__}}
has_fallback3 -->|no| error3{{error}}
```
## Funding Accounts
| Environment | How to fund |
|---|---|
| Studio (Localnet) | Built-in faucet — 💧 button in the account selector |
| Studionet | Built-in faucet — 💧 button in the account selector |
| Testnet Asimov | [testnet-faucet.genlayer.foundation](https://testnet-faucet.genlayer.foundation/) |
| Testnet Bradbury | [testnet-faucet.genlayer.foundation](https://testnet-faucet.genlayer.foundation/) |
See [Networks](/developers/networks) for full details on each environment.
## Calling Payable Methods from JavaScript
Use the `value` parameter in `writeContract()` to send GEN:
```typescript
import { createClient, createAccount } from 'genlayer-js';
import { testnetAsimov } from 'genlayer-js/chains';
const client = createClient({
chain: testnetAsimov,
account: createAccount(),
});
const txHash = await client.writeContract({
address: contractAddress,
functionName: 'tip',
args: [],
value: BigInt(5) * BigInt(10 ** 18), // 5 GEN in wei
});
```
# developers/intelligent-contracts/features/messages.mdx
import { Callout } from "nextra-theme-docs";
# Messages
GenLayer has three types of interaction, each operating at a different layer of the architecture.
## Transactions (→ Intelligent Contract)
A transaction is the entry point for all IC execution. The caller signs an EVM transaction that calls `addTransaction()` on ConsensusMain or on the IC's ghost contract (which relays to ConsensusMain). This is submitted via `eth_sendRawTransaction`.
The caller can be an EOA or an EVM contract on GenLayer Chain.
- `gl.message.sender_address` = caller address
- `gl.message.origin_address` = caller address
The transaction enters the consensus pipeline: activation → proposal → commit → reveal → finalization.
**Studio:** The SDK sends the same signed `addTransaction` EVM transaction via `eth_sendRawTransaction`, but Studio intercepts and simulates the consensus pipeline rather than executing on a real EVM layer.
## Internal Messages (IC → IC)
An internal message is an **asynchronous** call from one Intelligent Contract to another. It stays within the GenVM layer. During execution, `emit()` records the message as part of the execution result — the message is not sent immediately. The actual child transaction is only created when the parent transaction reaches the specified state (`on='accepted'` or `on='finalized'`). The child transaction then goes through consensus independently.
```python
other = gl.get_contract_at(addr)
# Call a write method on another IC
other.emit(on='finalized').update_status("active")
# Call with value (recipient must be payable)
other.emit(value=u256(100), on='finalized').deposit()
# Pure value transfer (triggers __receive__ on recipient)
other.emit_transfer(value=u256(100), on='finalized')
# Deploy a child contract
addr = gl.deploy_contract(
code=contract_code,
args=[],
salt_nonce=u256(1),
on='finalized',
)
```
In the child transaction's execution context:
- `gl.message.sender_address` = calling contract's address
- `gl.message.origin_address` = original caller (preserved through the chain)
See [Interacting with Intelligent Contracts](/developers/intelligent-contracts/features/interacting-with-intelligent-contracts) for full syntax reference.
## External Messages (IC → Chain Layer)
An external message crosses from the GenVM layer back to the GenLayer Chain (EVM layer). This is how an IC sends value or calls an EOA or EVM contract.
External messages are executed by the IC's ghost contract via `handleOp()`, so the `msg.sender` seen by the recipient is the ghost contract address (which is the same as the IC's address).
```python
@gl.evm.contract_interface
class ERC20:
class View:
def balance_of(self, owner: Address) -> u256: ...
class Write:
def transfer(self, to: Address, amount: u256) -> None: ...
# Read from an EVM contract (gl.message.contract_address is your own address)
balance = ERC20(token_address).view().balance_of(gl.message.contract_address)
# Write to an EVM contract (value is optional, defaults to 0)
ERC20(token_address).emit().transfer(recipient, amount)
# Send value to an EOA
@gl.evm.contract_interface
class _Recipient:
class View:
pass
class Write:
pass
_Recipient(Address(eoa_address)).emit_transfer(value=u256(amount))
```
External messages can only be emitted `on='finalized'`. Using `on='accepted'` for external messages is not supported.
**Studio:** EVM contract interaction beyond value transfers to EOAs is not implemented. The `@gl.evm.contract_interface` calls are not functional in Studio.
## Ghost Contracts
Every Intelligent Contract has a corresponding **ghost contract** on GenLayer Chain (the EVM layer). The ghost and IC share the same address.
### What Ghost Contracts Do
- **Hold the IC's GEN balance** — the native balance on the ghost contract on-chain is the source of truth for `self.balance`
- **Relay transactions** — `addTransaction()` on the ghost forwards to ConsensusMain
- **Execute external messages** — on finalization, `handleOp()` forwards calls to recipients so `msg.sender` is the IC's address
- **Bridge the two layers** — ghost on EVM ↔ IC on GenVM, same address
### Lifecycle
1. A deploy transaction is submitted
2. GhostFactory deploys a ghost contract at address `0xABC` on GenLayer Chain
3. Consensus processes the deployment
4. If successful: IC deploys at the same address `0xABC` on GenVM
5. If deployment is appealed and reverted: the ghost remains but has no IC behind it — it has no purpose or effect, but may be reused by consensus if deployment succeeds later
Ghost contract existence does **not** guarantee the IC is deployed. A reverted deployment leaves an empty ghost.
**Studio:** Ghost contracts are not implemented. The IC address is used directly, and balance is stored in a database table.
## Message Context (gl.message)
Every contract execution has access to `gl.message`:
| Field | Type | Description |
|---|---|---|
| `sender_address` | `Address` | Immediate caller — EOA, EVM contract, or IC depending on how the call was initiated |
| `origin_address` | `Address` | Original transaction submitter — preserved through internal message chains |
| `contract_address` | `Address` | The current contract's own address |
| `value` | `u256` | GEN sent with the call (only in `@gl.public.write.payable` methods) |
| `chain_id` | `u256` | Current chain ID |
## Timing: Accepted vs Finalized
Internal messages and contract deployments can execute at two different points:
### `on='finalized'` (default)
The message executes after the parent transaction is fully finalized (appeal window has closed). This is the safe default.
### `on='accepted'`
The message executes as soon as the parent transaction is accepted by initial consensus, before the appeal window closes.
**Appeal risks with `on='accepted'`:**
- The re-execution may emit the message again — potentially multiple times across appeal rounds.
- If the appeal changes the outcome, the message may be "invalid" (would not have been emitted with the final outcome), but **it cannot be taken back** — it was already sent and executed.
- The receiving contract must be **idempotent** and must handle duplicate or unexpected messages gracefully.
- Contract design must account for the possibility that accepted messages may not reflect the final state.
External messages always use `on='finalized'` and cannot be emitted on acceptance.
## Interfaces
### IC Interfaces (`@gl.contract_interface`)
Define typed stubs for calling other Intelligent Contracts:
```python
@gl.contract_interface
class Token:
class View:
def balance_of(self, owner: Address) -> u256: ...
class Write:
def transfer(self, to: Address, amount: u256) -> None: ...
token = Token(token_address)
balance = token.view().balance_of(user) # typed view call
token.emit(on='finalized').transfer(to, amount) # typed write call
```
This is purely for type safety and IDE autocompletion — at runtime it behaves identically to `gl.get_contract_at()`. No overhead.
### EVM Interfaces (`@gl.evm.contract_interface`)
Define typed stubs matching Solidity function signatures:
```python
@gl.evm.contract_interface
class IERC20:
class View:
def balance_of(self, owner: Address) -> u256: ...
def total_supply(self) -> u256: ...
class Write:
def transfer(self, to: Address, amount: u256) -> bool: ...
def approve(self, spender: Address, amount: u256) -> bool: ...
```
Parameters are automatically ABI-encoded. Type mapping:
| GenLayer | Solidity |
|---|---|
| `u256`, `u128`, `u64`, ... | `uint256`, `uint128`, `uint64`, ... |
| `i256`, `i128`, `i64`, ... | `int256`, `int128`, `int64`, ... |
| `Address` | `address` |
| `bool` | `bool` |
| `str` | `string` |
| `bytes` | `bytes` |
# developers/intelligent-contracts/features/interacting-with-intelligent-contracts.mdx
# Interacting with Intelligent Contracts
> This page covers syntax for internal messages (IC → IC). See [Messages](/developers/intelligent-contracts/features/messages) for the conceptual model and [Value Transfers](/developers/intelligent-contracts/features/value-transfers) for sending GEN.
## Getting Contract References
Access other contracts by their address:
```python
contract_address = Address("0x03FB09251eC05ee9Ca36c98644070B89111D4b3F")
dynamically_typed_contract = gl.get_contract_at(contract_address)
@gl.contract_interface
class GenLayerContractIface:
class View:
def method_name(self, a: int, b: str): ...
class Write:
pass
statically_typed_contract = GenLayerContractIface(contract_address)
```
Both approaches result in the same runtime value,
however the statically typed approach provides type checking and autocompletion in IDEs.
## Calling View Methods
Call read-only methods on other contracts:
```python
addr: Address = ...
other = gl.get_contract_at(addr)
result = other.view().get_token_balance()
```
## Emitting Messages
Send asynchronous messages to other contracts:
```python
other = gl.get_contract_at(addr)
other.emit(on='accepted').update_status("active")
other.emit(on='finalized').update_status("active")
# With value (recipient method must be @gl.public.write.payable)
other.emit(value=u256(100), on='finalized').deposit()
```
## Deploying New Contracts
```python
gl.deploy_contract(code=contract_code)
salt: u256 = u256(1) # not zero
child_address = gl.deploy_contract(code=contract_code, salt=salt)
```
## View vs Emit
- **`view()`** is synchronous — it reads state from another contract and returns the result immediately. The target contract's state is read as of the current block.
- **`emit()`** is asynchronous — it queues a write call that executes *after* the current transaction completes. The call is not blocking.
### Accepted vs Finalized
```python
# Fast — executes after the transaction is accepted by initial consensus
other.emit(on='accepted').do_something()
# Safe — waits until the transaction is fully finalized (after appeal window)
other.emit(on='finalized').do_something()
```
**`on='accepted'` has important implications.** If the emitting transaction is appealed, two things can happen:
1. The appeal changes the contract state such that the message should not have been emitted — but it was already sent and cannot be recalled.
2. The transaction is re-executed during the appeal, and the message (or a similar one) is emitted again. This can repeat across multiple appeal rounds — up to ~6 times depending on the validator set size.
The receiving contract must be **idempotent** — it must handle duplicate or unexpected messages gracefully. If the receiving logic cannot tolerate duplicates or messages that "shouldn't have been sent" based on the final state, use `on='finalized'` instead.
## Factory Pattern
Deploy child contracts from a parent:
```python
def __init__(self, num_workers: int):
with open("/contract/Worker.py", "rt") as f:
worker_code = f.read()
for i in range(num_workers):
addr = gl.deploy_contract(
code=worker_code.encode("utf-8"),
args=[i, gl.message.contract_address],
salt_nonce=i + 1,
on="accepted",
)
self.worker_addresses.append(addr)
```
Child contracts are immutable after deployment. To update worker logic, redeploy through the factory.
## Complete Working Example
Here's a complete example showing a token contract interacting with another contract:
```python
from genlayer import *
@gl.contract_interface
class TokenInterface:
class View:
def balance_of(self, account: Address) -> u256: ...
def total_supply(self) -> u256: ...
class Write:
def transfer(self, to: Address, amount: u256): ...
def approve(self, spender: Address, amount: u256): ...
class TokenGatedVault(gl.Contract):
"""A vault that requires minimum token balance to access"""
token_address: Address
min_balance: u256
stored_value: u256
def __init__(self, token_addr: Address, min_bal: u256):
self.token_address = token_addr
self.min_balance = min_bal
self.stored_value = u256(0)
@gl.public.write.payable
def deposit(self):
"""Deposit GEN into the vault (requires token balance)"""
# Get token contract reference
token = TokenInterface(self.token_address)
# Check caller's token balance
balance = token.view().balance_of(gl.message.sender_address)
if balance < self.min_balance:
raise gl.UserError(f"Insufficient token balance. Need {self.min_balance}, have {balance}")
# Accept the deposit
self.stored_value += gl.message.value
@gl.public.write
def withdraw(self, amount: u256):
"""Withdraw GEN from the vault (requires token balance)"""
token = TokenInterface(self.token_address)
balance = token.view().balance_of(gl.message.sender_address)
if balance < self.min_balance:
raise gl.UserError("Insufficient token balance")
if amount > self.stored_value:
raise gl.UserError("Insufficient vault balance")
self.stored_value -= amount
gl.transfer(gl.message.sender_address, amount)
```
## Error Handling
Always handle errors when calling other contracts:
```python
@gl.public.write
def safe_transfer(self, token_addr: Address, to: Address, amount: u256):
"""Transfer tokens with proper error handling"""
try:
token = gl.get_contract_at(token_addr)
token.emit(on='finalized').transfer(to, amount)
except gl.UserError as e:
# Handle contract-specific errors
raise gl.UserError(f"Transfer failed: {e}")
except Exception as e:
# Handle unexpected errors
raise gl.UserError(f"Unexpected error: {e}")
```
## Common Mistakes
### ❌ Don't: Call view() on emit()
```python
# WRONG - view() is for reading, emit() is for writing
other.view().update_status("active") # This will fail!
```
```python
# CORRECT
other.emit(on='finalized').update_status("active")
```
### ❌ Don't: Forget to check return values
```python
# WRONG - Not checking if the call succeeded
other.view().transfer(recipient, amount)
```
```python
# CORRECT - Check the return value
success = other.view().transfer(recipient, amount)
if not success:
raise gl.UserError("Transfer failed")
```
### ❌ Don't: Use accepted when order matters
```python
# WRONG - These might execute out of order or multiple times
other.emit(on='accepted').step_one()
other.emit(on='accepted').step_two()
```
```python
# CORRECT - Use finalized for sequential operations
other.emit(on='finalized').step_one()
other.emit(on='finalized').step_two()
```
## Best Practices
### 1. Use Static Typing for Better IDE Support
```python
# Preferred: Define interface upfront
@gl.contract_interface
class MyContractInterface:
class View:
def get_balance(self, addr: Address) -> u256: ...
class Write:
def update_balance(self, addr: Address, amount: u256): ...
other = MyContractInterface(contract_address)
# Now you get autocomplete and type checking!
```
### 2. Validate Contract Addresses
```python
def __init__(self, oracle_addr: Address):
# Verify the address is valid
if oracle_addr == Address("0x0000000000000000000000000000000000000000"):
raise gl.UserError("Invalid oracle address")
# Verify the contract exists (optional but recommended)
oracle = gl.get_contract_at(oracle_addr)
try:
# Try calling a view method to verify it's the right contract
oracle.view().get_version()
except:
raise gl.UserError("Oracle contract not found or incompatible")
self.oracle_address = oracle_addr
```
### 3. Handle Reentrancy
```python
class Vault(gl.Contract):
balances: TreeMap[Address, u256] = TreeMap()
@gl.public.write
def withdraw(self, amount: u256):
# Check balance FIRST
balance = self.balances[gl.message.sender_address]
if amount > balance:
raise gl.UserError("Insufficient balance")
# Update state BEFORE external call
self.balances[gl.message.sender_address] = balance - amount
# External call last (prevents reentrancy)
gl.transfer(gl.message.sender_address, amount)
```
### 4. Use Finalized for Critical Operations
```python
@gl.public.write
def execute_payment(self, recipient: Address, amount: u256):
"""Critical payment - must be finalized"""
token = gl.get_contract_at(self.token_address)
# Use 'finalized' for important operations
# This ensures the transaction won't be appealed
token.emit(on='finalized').transfer(recipient, amount)
```
## Testing Contract Interactions
Example test for contract-to-contract calls:
```python
# In your test file
def test_token_gated_vault():
# Deploy token contract first
token = deploy_contract("Token", args=[u256(1000000)])
# Deploy vault with token requirement
vault = deploy_contract("TokenGatedVault", args=[
token.address,
u256(100) # minimum balance
])
# Give user some tokens
token.transfer(user_address, u256(500))
# Test deposit with sufficient balance
vault.deposit(value=u256(1000))
assert vault.view().stored_value() == u256(1000)
# Test deposit with insufficient balance
vault2 = deploy_contract("TokenGatedVault", args=[
token.address,
u256(10000) # very high requirement
])
with pytest.raises(gl.UserError, match="Insufficient token balance"):
vault2.deposit(value=u256(1000))
```
# developers/intelligent-contracts/features/interacting-with-evm-contracts.mdx
import { Callout } from "nextra-theme-docs";
# Interacting with EVM Contracts
> These are external messages — they cross from the GenVM layer back to the GenLayer Chain via [ghost contracts](/developers/intelligent-contracts/features/messages#ghost-contracts). See [Messages](/developers/intelligent-contracts/features/messages) for the conceptual model.
## Contract Interface Definition
Define EVM contract interfaces using decorators:
```python
@gl.evm.contract_interface
class TokenContract:
class View:
def balance_of(self, owner: Address) -> u256: ...
def total_supply(self) -> u256: ...
class Write:
def transfer(self, to: Address, amount: u256) -> bool: ...
def approve(self, spender: Address, amount: u256) -> bool: ...
```
## Calling EVM Contracts
Interact with EVM contracts through defined interfaces:
```python
token_address: Address = ...
# Read from EVM contract
token = TokenContract(token_address)
supply = token.view().total_supply()
# Write to EVM contract
token.emit().transfer(receiver_address, u256(100))
```
## Balance Access
Access EVM contract balances directly:
```python
evm_contract = TokenContract(address)
balance = evm_contract.balance # Get contract's ETH balance
```
## Message Emission
Send messages to EVM contracts:
```python
TokenContract(token_address).emit().approve(spender, amount)
```
Messages to EVM contract can be emitted only on finality
# developers/intelligent-contracts/features/special-methods.mdx
# Special Methods
> For how these methods fit into value transfer flows, see [Value Transfers](/developers/intelligent-contracts/features/value-transfers#receiving-value-without-a-method-call).
There are two special methods allowed in each contract **definition**:
```python
class Contract(gl.Contract):
@gl.public.write # .payable?
def __handle_undefined_method__(
self, method_name: str, args: list[typing.Any], kwargs: dict[str, typing.Any]
):
"""
Method that is called for undefined method calls,
must be either ``@gl.public.write`` or ``@gl.public.write.payable``
"""
...
@gl.public.write.payable
def __receive__(self):
"""
Method that is called for no-method transfers,
must be ``@gl.public.write.payable``
"""
...
```
Below is a diagram that shows how GenVM decides which method to pick, in case any regular method did not match:
```mermaid
graph TD
unhandled_message>unhandled message] ---> has_value[has value?]
has_value -->|yes| has_method_name[has method name?]
has_method_name -->|yes| fallback_defined1[__handle_undefined_method__ is defined?]
has_method_name -->|no| receive_defined[is __receive__ defined?]
receive_defined -->|no| fallback_defined1[is __handle_undefined_method__ defined?]
receive_defined -->|yes| receive{{call __receive__}}
fallback_defined1 -->|yes| fallback_is_payable[is __handle_undefined_method__ payable?]
fallback_defined1 -->|no| error3{{error}}
fallback_is_payable -->|yes| fallback1{{call __handle_undefined_method__}}
fallback_is_payable -->|no| error1{{error}}
has_value -->|no| fallback_defined
fallback_defined -->|yes| fallback2{{call __handle_undefined_method__}}
fallback_defined -->|no| error2{{error}}
```
# developers/intelligent-contracts/features/vector-storage.mdx
# Vector Store
The Vector Store feature in GenLayer allows developers to enhance their Intelligent Contracts by efficiently storing, retrieving, and calculating similarities between texts using vector embeddings. This feature is particularly useful for tasks that require natural language processing (NLP), such as creating context-aware applications or indexing text data for semantic search.
## Key Features of Vector Store
The Vector Store provides several powerful features for managing text data:
#### 1. Text Embedding Storage
You can store text data as vector embeddings, which are mathematical representations of the text, allowing for efficient similarity comparisons. Each stored text is associated with a vector and metadata.
#### 2. Similarity Calculation
The Vector Store allows you to calculate the similarity between a given text and stored vectors using cosine similarity. This is useful for finding the most semantically similar texts, enabling applications like recommendation systems or text-based search.
#### 3. Metadata Management
Along with the text and vectors, you can store additional metadata (any data type) associated with each text entry. This allows developers to link additional information (e.g., IDs or tags) to the text for retrieval.
#### 4. CRUD Operations
The Vector Store provides standard CRUD (Create, Read, Update, Delete) operations, allowing developers to add, update, retrieve, and delete text and vector entries efficiently.
## How to Use Vector Store in Your Contracts
To use the Vector Store in your Intelligent Contracts, you will interact with its methods to add and retrieve text data, calculate similarities, and manage vector storage. Below are the details of how to use this feature.
#### Importing Vector Store
First, import the VectorStore class from the standard library in your contract:
```python
from backend.node.genvm.std.vector_store import VectorStore
```
#### Creating a Contract with Vector Store
Here’s an example of a contract using the Vector Store for indexing and searching text logs:
```python
# {
# "Seq": [
# { "Depends": "py-lib-genlayermodelwrappers:1jb45aa8ynh2a9c9xn3b7qqh8sm5q93hwfp7jqmwsfhh8jpz09h6" },
# { "Depends": "py-genlayer:1jb45aa8ynh2a9c9xn3b7qqh8sm5q93hwfp7jqmwsfhh8jpz09h6" }
# ]
# }
from genlayer import *
import genlayermodelwrappers
import numpy as np
from dataclasses import dataclass
@allow_storage
@dataclass
class StoreValue:
log_id: u256
text: str
# contract class
class LogIndexer(gl.Contract):
vector_store: VecDB[np.float32, typing.Literal[384], StoreValue]
def __init__(self):
pass
def get_embedding_generator(self):
return genlayermodelwrappers.SentenceTransformer("all-MiniLM-L6-v2")
def get_embedding(
self, txt: str
) -> np.ndarray[tuple[typing.Literal[384]], np.dtypes.Float32DType]:
return self.get_embedding_generator()(txt)
@gl.public.view
def get_closest_vector(self, text: str) -> dict | None:
emb = self.get_embedding(text)
result = list(self.vector_store.knn(emb, 1))
if len(result) == 0:
return None
result = result[0]
return {
"vector": list(str(x) for x in result.key),
"similarity": str(1 - result.distance),
"id": result.value.log_id,
"text": result.value.text,
}
@gl.public.write
def add_log(self, log: str, log_id: int) -> None:
emb = self.get_embedding(log)
self.vector_store.insert(emb, StoreValue(text=log, log_id=u256(log_id)))
@gl.public.write
def update_log(self, log_id: int, log: str) -> None:
emb = self.get_embedding(log)
for elem in self.vector_store.knn(emb, 2):
if elem.value.text == log:
elem.value.log_id = u256(log_id)
@gl.public.write
def remove_log(self, id: int) -> None:
for el in self.vector_store:
if el.value.log_id == id:
el.remove()
```
# developers/intelligent-contracts/features/debugging.mdx
import { Callout } from "nextra-theme-docs";
# Debugging
## Print Statements
Use `print` calls for basic debugging, which will be present in node or studio output.
Use `gl.trace` to include time stamps in the output (it will be written to genvm_log instead of stdout)
## Profiling
There is also `gl.trace_time_micro` which returns timestamp in the GenVM debug mode. Default bootloader includes
possibility to enable profiling by setting environment variable `GENLAYER_ENABLE_PROFILER=true`.
```python
# {
# "Seq": [
# { "SetEnv": { "name": "GENLAYER_ENABLE_PROFILER", "value": "true" } },
# { "Depends": "py-genlayer:1jb45aa8ynh2a9c9xn3b7qqh8sm5q93hwfp7jqmwsfhh8jpz09h6" }
# ]
# }
```
This will output base-64 encoded gzip-compressed profiling data to stderr after every single VM execution
## Attaching a Debugger
Unfortunately, attaching a debugger to a running Intelligent Contract is not supported yet
# developers/intelligent-contracts/features/random.mdx
# Random
Getting random in deterministic part may be difficult. Trying to delegate it to non-deterministic block will cause
your contract to either never agree on that block or to trust the leader. For that reason it is advised to use seeded random.
## Seed acquisition methods
1. Use some field of `message`
2. Use current time (transaction time)
3. Use `stdin`, as shown below
```python3
def get_random_seed() -> bytes:
import os
import hashlib
f = os.fdopen(0, 'rb', buffering=0, closefd=False)
f.seek(0)
hash_obj = hashlib.sha256()
while True:
chunk = f.read(8192)
if not chunk:
return hash_obj.digest()
hash_obj.update(chunk)
```
# developers/intelligent-contracts/features/non-determinism.mdx
# Non-determinism
import { Callout } from 'nextra-theme-docs'
## When to Use
Non-deterministic operations are needed for:
- External API calls
- LLM and AI model calls
- Random number generation
- Any operation that might vary between nodes
## What Goes Inside vs Outside
Non-deterministic blocks (`leader_fn`, `validator_fn`, functions passed to `strict_eq`) run in a special execution context. The GenVM enforces strict rules about what can and cannot happen inside these blocks.
### Must be INSIDE nondet blocks
All `gl.nondet.*` calls — web requests, LLM prompts — must be inside a nondet block. They cannot run in regular contract code.
```python
@gl.public.write
def fetch_price(self):
def leader_fn():
response = gl.nondet.web.get(api_url) # ✓ inside nondet block
result = gl.nondet.exec_prompt(prompt) # ✓ inside nondet block
return parse_price(response)
# gl.nondet.web.get(api_url) # ✗ would fail here
self.price = gl.vm.run_nondet_unsafe(leader_fn, validator_fn)
```
### Must be OUTSIDE nondet blocks
Several operations must happen in the deterministic context — after the nondet block returns:
| Operation | Why |
|-----------|-----|
| **Storage writes** (`self.x = ...`) | Storage must only change based on consensus-agreed values |
| **Contract calls** (`gl.get_contract_at()`) | Cross-contract calls must use deterministic state |
| **Message emission** (`.emit()`) | Messages to other contracts/chains must be deterministic |
| **Nested nondet blocks** | Nondet blocks cannot contain other nondet blocks |
```python
@gl.public.write
def update_price(self, pair: str):
def leader_fn():
response = gl.nondet.web.get(api_url)
return json.loads(response.body.decode("utf-8"))["price"]
# self.price = price # ✗ no storage writes here
# other = gl.get_contract_at(addr) # ✗ no contract calls here
# other.emit().notify(price) # ✗ no message emission here
def validator_fn(leaders_res) -> bool:
if not isinstance(leaders_res, gl.vm.Return):
return False
my_price = leader_fn()
return abs(leaders_res.calldata - my_price) / leaders_res.calldata <= 0.02
price = gl.vm.run_nondet_unsafe(leader_fn, validator_fn)
# ✓ All side effects happen AFTER consensus, in deterministic context
self.prices[pair] = price
oracle = gl.get_contract_at(self.oracle_address)
oracle.emit().price_updated(pair, price)
```
The [GenVM linter](/developers/intelligent-contracts/tooling-setup) catches all of these violations statically — run `genvm-lint check` before deploying to avoid runtime errors.
### Why these rules exist
The leader and validators execute nondet blocks **independently** — each node runs its own `leader_fn` or `validator_fn`. If you wrote to storage inside a nondet block, each node would write a different value before consensus decides which one is correct. The same applies to contract calls and message emission: these must happen once, after consensus, using the agreed-upon result.
## Equivalence Principle
GenLayer provides `strict_eq` for exact-match consensus and custom validator functions (`run_nondet_unsafe`) for everything else. Convenience wrappers like `prompt_comparative` and `prompt_non_comparative` exist for common patterns. For detailed information, see [Equivalence Principle](/developers/intelligent-contracts/equivalence-principle).
### Strict Equality
Requires exact matches between validator outputs. Use when all nodes can converge on the same normalized value — e.g., fetching objective data from an API and extracting a structured result:
```python
def fetch_current_block():
response = gl.nondet.web.request("https://api.example.com/block/latest")
data = json.loads(response)
return json.dumps({"height": data["height"], "hash": data["hash"]}, sort_keys=True)
# All validators must return the exact same string
result = gl.eq_principle.strict_eq(fetch_current_block)
```
> **Note:** `strict_eq` is not suitable for random number generation or LLM calls, since those inherently produce different results on each node. Use a custom validator function or one of the convenience wrappers below for those cases.
### Comparative (Convenience Shortcut)
A convenience wrapper where both leader and validators perform the same task, then an LLM compares results using your criteria:
```python
def comparative_example():
return gl.nondet.web.request("https://api.example.com/count")
# Results are compared with acceptable margin of error
result = gl.eq_principle.prompt_comparative(
comparative_example,
"Results should not differ by more than 5%"
)
```
### Non-Comparative (Convenience Shortcut)
A convenience wrapper where validators evaluate the leader's output against criteria without repeating the task:
```python
result = gl.eq_principle.prompt_non_comparative(
input="This product is amazing!",
task="Classify the sentiment as positive, negative, or neutral",
criteria="""
Output must be one of: positive, negative, neutral
Consider context and tone
"""
)
```
### Custom Validator Functions
For full control over consensus logic, write a custom leader/validator pair with `run_nondet_unsafe`. This is the recommended approach for most contracts:
```python
def custom_consensus_example(self, data: str):
def leader_fn():
# Leader performs the operation
response = gl.nondet.exec_prompt(f"Rate this sentiment 1-10: {data}. Answer only with integer, without reasoning")
return int(response.strip())
def validator_fn(leader_result):
own_score = leader_fn()
if isinstance(leader_result, Exception):
return False
# Accept if within acceptable range
return abs(own_score - leader_result) <= 2
return gl.vm.run_nondet_unsafe(leader_fn, validator_fn)
```
## Operations
### Accessing External Data
Use non-deterministic blocks for external API calls. For more web access examples, see [Web Access](/developers/intelligent-contracts/features/web-access):
```python
@gl.public.write
def fetch_external_data(self):
def fetch_data():
# External API call - inherently non-deterministic
response = gl.nondet.web.request("https://example.com/data")
return response
# Consensus ensures all validators agree on the result
data = gl.eq_principle.strict_eq(fetch_data)
return data
```
### LLM Integration
Execute AI prompts using comparative principle. For more detailed examples, see [Calling LLMs](/developers/intelligent-contracts/features/calling-llms):
```python
@gl.public.write
def ai_decision(self, prompt: str):
def call_llm():
response = gl.nondet.exec_prompt(prompt)
return response.strip()
# Use comparative principle for LLM response consensus
decision = gl.eq_principle.prompt_comparative(
call_llm,
principle="Responses should be semantically equivalent in meaning"
)
return decision
```
# developers/intelligent-contracts/features/calling-llms.mdx
import { Callout } from 'nextra-theme-docs'
# Calling LLMs
## Basic LLM Calls
Execute prompts using large language models:
```python
def leader_fn():
response = gl.nondet.exec_prompt("Answer this question")
return response.strip().lower()
def validator_fn(leader_result) -> bool:
if not isinstance(leader_result, gl.vm.Return):
return False
my_result = leader_fn()
return my_result == leader_result.calldata
answer = gl.vm.run_nondet_unsafe(leader_fn, validator_fn)
```
## JSON Response Format
Request structured responses from LLMs:
```python
def structured_llm_call():
prompt = """
Return a JSON object with these keys:
- "score": random integer from 1 to 100
- "status": either "active" or "inactive"
"""
return gl.nondet.exec_prompt(prompt, response_format='json')
def validate_structured(leader_result) -> bool:
if not isinstance(leader_result, gl.vm.Return):
return False
data = leader_result.calldata
# Validate structure rather than exact match — LLM outputs are non-deterministic
return (
isinstance(data, dict)
and isinstance(data.get("score"), int)
and 1 <= data["score"] <= 100
and data.get("status") in ("active", "inactive")
)
result = gl.vm.run_nondet_unsafe(structured_llm_call, validate_structured)
score = result['score'] # Access JSON fields
```
Using `strict_eq` with LLM calls will fail consensus because LLM outputs are non-deterministic.
Always use `run_nondet_unsafe` with a custom validator that checks the *structure* and *validity*
of the response rather than requiring an exact match.
This approach guarantees that `exec_prompt` returns a valid JSON object, however
correspondence to the specified format depends on the underlying LLM.
## Image Processing
Process images with vision models:
```python
def vision_analysis():
prompt = "Describe what you see in this image"
image_data = b"\x89PNG..."
return gl.nondet.exec_prompt(
prompt,
images=[image_data]
)
def validate_description(leader_result) -> bool:
if not isinstance(leader_result, gl.vm.Return):
return False
# Accept any non-empty string description
return isinstance(leader_result.calldata, str) and len(leader_result.calldata.strip()) > 0
description = gl.vm.run_nondet_unsafe(vision_analysis, validate_description)
```
Limit of images is two
## Response Validation
Validate and process LLM responses:
```python
def generate_number():
response = gl.nondet.exec_prompt(
"Generate a number between 1 and 100",
response_format='json'
)
# Validate the response
if response['number'] < 1 or response['number'] > 100:
raise Exception(f"Invalid number: {response['number']}")
return response['number']
def validate_number(leader_result) -> bool:
if not isinstance(leader_result, gl.vm.Return):
return False
num = leader_result.calldata
return isinstance(num, int) and 1 <= num <= 100
result = gl.vm.run_nondet_unsafe(generate_number, validate_number)
```
## Defensive Response Parsing
LLMs return unpredictable formats. Even with `response_format='json'`, the response may not match your expected schema. Here are some patterns for handling this:
### Key Aliasing
LLMs sometimes use alternate key names. One approach is to check for common variations:
```python
def parse_score(response: dict) -> int:
"""Extract a numeric score, handling common LLM variations."""
raw = response.get("score")
if raw is None:
for alt in ("rating", "points", "value", "result"):
if alt in response:
raw = response[alt]
break
if raw is None:
raise gl.UserError(f"Missing 'score' key. Got keys: {list(response.keys())}")
# Coerce to int — handles "3", "3.5", floats, whitespace
try:
return max(0, int(round(float(str(raw).strip()))))
except (ValueError, TypeError):
raise gl.UserError(f"Non-numeric score: {raw}")
```
### JSON Cleanup
LLMs sometimes wrap JSON in markdown fences or add trailing commas:
```python
import re
def clean_llm_json(text: str) -> dict:
"""Extract and clean JSON from LLM output."""
import json
first = text.find("{")
last = text.rfind("}")
if first == -1 or last == -1:
raise gl.UserError(f"No JSON object found in response")
text = text[first:last + 1]
text = re.sub(r",(?!\s*?[\{\[\"'\w])", "", text) # Remove trailing commas
return json.loads(text)
```
### Always Use `response_format='json'`
```python
result = gl.nondet.exec_prompt(task, response_format="json")
```
This instructs the LLM to return JSON. It significantly improves reliability, but you should still validate and clean the output — LLMs don't always comply.
## Error Handling in LLM Calls
When using LLMs inside `run_nondet_unsafe`, consider how errors affect consensus. If the LLM returns garbage, you generally want the validator to *disagree* (return `False`) rather than agree on broken output — this forces a rotation to a new leader:
```python
def leader_fn():
result = gl.nondet.exec_prompt(prompt, response_format="json")
if not isinstance(result, dict):
raise gl.UserError(f"LLM returned non-dict: {type(result)}")
return result
```
See [Error Handling](/developers/intelligent-contracts/features/error-handling) for more on error classification patterns.
# developers/intelligent-contracts/features/image-processing.mdx
import { Callout } from "nextra-theme-docs";
# Image Processing
Intelligent Contracts can process images through LLMs — pass screenshots, photos, or any visual data alongside a prompt for analysis.
## Sending Images to LLMs
Use the `images` parameter in `gl.nondet.exec_prompt()`:
```python
from genlayer import *
class ReceiptVerifier(gl.Contract):
verified: bool
def __init__(self):
self.verified = False
@gl.public.write
def verify_receipt(self, image_data: bytes, expected_amount: str) -> None:
def leader_fn():
return gl.nondet.exec_prompt(
f"Does this receipt show a payment of {expected_amount}? "
"Respond as JSON: {{\"matches\": true/false, \"actual_amount\": \"...\"}}",
images=[image_data], # accepts raw bytes directly
response_format="json",
)
def validator_fn(leaders_res) -> bool:
if not isinstance(leaders_res, gl.vm.Return):
return False
my_result = leader_fn()
return my_result["matches"] == leaders_res.calldata["matches"]
result = gl.vm.run_nondet_unsafe(leader_fn, validator_fn)
self.verified = result["matches"]
```
The `images` parameter accepts a sequence of raw `bytes` (e.g., PNG/JPEG data) or `gl.nondet.Image` objects.
## Capturing Screenshots from the Web
Combine [web access](/developers/intelligent-contracts/features/web-access) with image processing to screenshot a webpage and analyze it:
```python
def check_website_status():
url = "https://example.com/status-page"
screenshot = gl.nondet.web.render(url, mode='screenshot')
return gl.nondet.exec_prompt(
"Is this status page showing all systems operational? "
"Respond as JSON: {{\"all_operational\": true/false}}",
images=[screenshot],
response_format="json",
)
result = gl.eq_principle.strict_eq(check_website_status)
```
## Use Cases
- **Visual evidence verification** — insurance claims with photo proof, damage assessment
- **Document analysis** — receipts, invoices, certificates
- **Web monitoring** — screenshot a page and verify its content matches expectations
- **Brand & content compliance** — check if visual content meets guidelines
- **UI verification** — screenshot an app and verify it renders correctly
Image processing requires vision-capable LLM models. On the GenLayer network, validators handle model selection — your contract just sends images. In Studio running locally, ensure your configured validators use a model that supports image inputs (e.g., GPT-5, Claude Sonnet).
# developers/intelligent-contracts/features/web-access.mdx
# Web Access
## HTTP Requests
Send data to external services:
```python
def post_request():
url = "https://test-server.genlayer.com/body/echo"
response = gl.nondet.web.request(
url,
method='POST',
body={}
)
return response.status_code
status_code = gl.eq_principle.strict_eq(post_request)
```
## Web Rendering
Render web page and extract content:
```python
def render_page():
url = "https://test-server.genlayer.com/static/genvm/hello.html"
# Render HTML content
html_content = gl.nondet.web.render(url, mode='html')
return html_content
page_html = gl.eq_principle.strict_eq(render_page)
```
## Screenshot Capture
Take screenshots of web pages:
```python
def take_screenshot():
url = "https://test-server.genlayer.com/static/genvm/hello.html"
# Capture page as image
screenshot = gl.nondet.web.render(url, mode='screenshot')
return screenshot
image_data = gl.eq_principle.strict_eq(take_screenshot)
```
## Handling HTTP Errors
External APIs can return error responses. Consider checking the status code:
```python
def fetch_data():
response = gl.nondet.web.request(api_url, method='GET')
if response.status_code >= 400 and response.status_code < 500:
raise gl.UserError(f"API returned client error: {response.status_code}")
elif response.status_code >= 500:
raise gl.UserError(f"API temporarily unavailable: {response.status_code}")
return json.loads(response.body.decode("utf-8"))
```
## Consensus-Friendly Web Requests
When using web data in non-deterministic blocks, remember that the leader and validators make **independent requests**. External APIs may return different data between calls — timestamps change, counts update, caches vary.
### Extract Stable Fields
One approach is to return only the fields that won't change between calls:
```python
def leader_fn():
res = gl.nondet.web.get(api_url)
data = json.loads(res.body.decode("utf-8"))
# Only return fields that are stable across requests
return {"id": data["id"], "login": data["login"], "status": data["status"]}
# NOT: follower_count, updated_at, online_status
```
### Derive Status from Variable Data
When raw data may differ between leader and validator (e.g., CI check counts change), consider comparing a derived summary instead of the raw values:
```python
def validator_fn(leaders_res: gl.vm.Result) -> bool:
validator_data = leader_fn()
def derive_status(checks):
if not checks:
return "pending"
for c in checks:
if c.get("conclusion") != "success":
return "failing"
return "success"
return derive_status(leaders_res.calldata) == derive_status(validator_data)
```
See the [Equivalence Principle](/developers/intelligent-contracts/equivalence-principle) page for more validation patterns.
# developers/intelligent-contracts/tooling-setup.mdx
import { Callout } from 'nextra-theme-docs'
import CustomCard from '../../../components/card'
# Development Setup
This guide covers everything you need to develop, test, and deploy Intelligent Contracts — from zero-setup exploration to a full local development environment with linting, testing, and agent-assisted workflows.
## Quick Start: GenLayer Studio
The fastest way to start is [studio.genlayer.com](https://studio.genlayer.com) — a web-based IDE where you can write, deploy, and interact with Intelligent Contracts with zero setup.
You can also import any deployed contract by address to interact with it directly in the Studio — useful for debugging or testing contracts you've deployed from your local environment.
For anything beyond exploration — local testing, CI, agent-assisted development — set up a local environment below.
## Local Environment Setup
### Prerequisites