DEV Community

Cover image for I Built the First Utility Library for GenLayer — Here's What's Inside
0xgayyy
0xgayyy

Posted on

I Built the First Utility Library for GenLayer — Here's What's Inside

genlayer-utils provides reusable patterns to GenLayer Intelligent Contracts. Stop rewriting boilerplate — drop in battle-tested helpers for web access, AI prompts, access control, and more.

Every GenLayer contract I've built starts the same way.

Define an inner function. Call gl.nondet.web.render(). Build a prompt. Call gl.nondet.exec_prompt(). Serialize with json.dumps(sort_keys=True). Wrap everything in gl.eq_principle.strict_eq(). Deserialize the result.

That's 15–20 lines of code. And it's identical in structure across every single contract.

After building TruthPost, a decentralized AI fact-checker, I realized I'd written the same patterns three times in a single project. So I extracted them into a library.

genlayer-utils is the first utility library for GenLayer Intelligent Contracts. It's open source, MIT licensed, and ready to use today.


The Problem

GenLayer is an AI-native blockchain. Contracts can browse the internet and call LLMs natively, no oracles needed. It's genuinely powerful.

But there's a cost: every contract that uses these features repeats the same boilerplate. There are zero reusable libraries in the ecosystem. Every developer starts from scratch.

Here's what a typical web + AI call looks like in a GenLayer contract:

def _fact_check(self, claim_text, source_url):
    def check_claim():
        web_data = gl.nondet.web.render(source_url, mode="text")
        prompt = f"""You are a fact-checker. Based on the evidence,
        determine whether this claim is true or false.
        CLAIM: {claim_text}
        EVIDENCE: {web_data}
        Respond ONLY with JSON: {{"verdict": "<true|false>",
        "explanation": "..."}}"""
        result = gl.nondet.exec_prompt(prompt, response_format="json")
        return json.dumps(result, sort_keys=True)
    raw = gl.eq_principle.strict_eq(check_claim)
    return json.loads(raw)
Enter fullscreen mode Exit fullscreen mode

Now imagine writing this for every method that touches the web or AI. It adds up fast.


The Solution: genlayer-utils

Five modules. Copy what you need. That's it.

1. nondet — Non-Deterministic Block Helpers

The biggest time-saver. Turns the entire web → LLM → consensus pattern into a single function call.

Before (20 lines):

def _check(self, url):
    def _inner():
        data = gl.nondet.web.render(url, mode="text")
        result = gl.nondet.exec_prompt(
            f"Analyze: {data}", response_format="json"
        )
        return json.dumps(result, sort_keys=True)
    return json.loads(gl.eq_principle.strict_eq(_inner))
Enter fullscreen mode Exit fullscreen mode

After (1 line):

result = web_llm_strict(url=url, prompt_template="Analyze: {web_data}")
Enter fullscreen mode Exit fullscreen mode

Three functions cover most use cases:

Function When to use
web_llm_strict() Web + AI with exact consensus (facts, categories, JSON)
llm_strict() AI-only, no web fetch (classification, yes/no)
web_llm_comparative() Web + AI where outputs can vary (summaries, descriptions)

2. llm — Prompt Templates

Writing prompts that reliably pass strict_eq consensus is tricky. Validators need to independently produce the same output. These templates are pre-optimized for that:

# Classify text into categories
prompt = classify_prompt(
    text="This product is terrible",
    categories=["positive", "negative", "neutral"]
)
result = llm_strict(prompt)
# {"category": "negative", "confidence": "high", "reason": "..."}
Enter fullscreen mode Exit fullscreen mode
# Fact-check a claim against web evidence
prompt = fact_check_prompt(
    claim="Python was created in 1991",
    evidence="{web_data}"
)
result = web_llm_strict(url="https://en.wikipedia.org/wiki/Python", prompt_template=prompt)
# {"verdict": "true", "explanation": "..."}
Enter fullscreen mode Exit fullscreen mode
# Extract structured data from text
prompt = extract_prompt(
    text="John Smith, 32, works at Google",
    fields={"name": "full name", "age": "numeric age", "company": "employer"}
)
# {"name": "John Smith", "age": "32", "company": "Google"}
Enter fullscreen mode Exit fullscreen mode

Four templates: classify_prompt(), fact_check_prompt(), extract_prompt(), yes_no_prompt(). Plus validators to check the LLM's response format.

3. access_control — Owner & Role Guards

GenLayer has no built-in access control. Every contract that needs it checks gl.message.sender_address manually. genlayer-utils provides drop-in guards:

@gl.public.write
def admin_action(self):
    require_sender(self._owner)  # One line. Done.
    # ... only owner reaches here

@gl.public.write.payable
def purchase(self):
    require_value(100)  # Must send at least 100 GEN
    # ... process purchase
Enter fullscreen mode Exit fullscreen mode

Also includes documented patterns for Ownable (single owner) and Role-Based Access (admin, moderator, editor, etc.) that you can copy into your contract.

4. web_oracle — Web Data Extraction

Domain-specific helpers with consensus built in:

# Fetch an asset price from any web source
result = fetch_price(
    url="https://www.coingecko.com/en/coins/bitcoin",
    asset_name="Bitcoin"
)
# {"price": "67500.42", "currency": "USD", "timestamp": "..."}

# Fetch a sports score
result = fetch_score(
    url="https://www.espn.com/match/12345",
    team1="Arsenal", team2="Chelsea"
)
# {"score": "2:1", "winner": 1, "status": "finished"}

# Fetch any JSON API
data = fetch_json_api("https://api.example.com/data")
Enter fullscreen mode Exit fullscreen mode

5. storage — TreeMap & DynArray Helpers

Small but saves repetitive code:

# Before (3 lines)
if sender not in self.reputation:
    self.reputation[sender] = 0
self.reputation[sender] += 10

# After (1 line)
increment_or_init(self.reputation, sender, 10)
Enter fullscreen mode Exit fullscreen mode

Also: treemap_paginate() for paginated views, address_map_to_dict() for converting address-keyed maps, and treemap_count().


Example Contracts

The repo includes 4 complete, deployable contracts that demonstrate the patterns:

Contract What it does Modules used
fact_checker.py AI fact-checking dApp nondet, llm, access_control, storage
price_feed.py Decentralized price oracle web_oracle, access_control
content_moderator.py AI content classification nondet, llm, storage
voting.py On-chain voting with roles access_control, storage

Each one is a real contract you can deploy on GenLayer Studio right now.


How to Use

GenLayer contracts run as single Python files inside GenVM. There's no pip install. So you copy the functions you need into the top of your contract:

# { "Depends": "py-genlayer:test" }
import json
from genlayer import *

# ─── genlayer-utils: nondet ─────────────────
def web_llm_strict(url, prompt_template, *, mode="text", response_format="json"):
    # ... (paste from the repo)

# ─── genlayer-utils: llm ────────────────────
def fact_check_prompt(claim, evidence, verdicts=None):
    # ... (paste from the repo)

# ─── Your contract ──────────────────────────
class MyContract(gl.Contract):
    ...
Enter fullscreen mode Exit fullscreen mode

This is how all contract libraries start, even OpenZeppelin began as copy-paste patterns. Zero dependencies, full transparency, and you only include what you use.


What's Next

This is v0.1.0. Here's what I'm planning:

  • CLI inject tool - A script that reads # genlayer-utils: nondet, llm directives in your contract and auto-injects the relevant functions before deployment
  • More prompt templates - Sentiment analysis, summarization, comparison
  • More oracle patterns - Weather, news, social media data extraction
  • Community contributions - If you have patterns you reuse, submit a PR

Get Started

git clone https://github.com/luch91/genlayer-utils.git
Enter fullscreen mode Exit fullscreen mode

Browse the source, copy what you need, and start building.


Built as a contribution to the GenLayer Builder Program. If you're building on GenLayer, I'd love to hear what patterns you need, open an issue or drop a comment below.

Top comments (0)