From 48c03537783a209143baab132c2914169ae8e0d9 Mon Sep 17 00:00:00 2001 From: kp2pml30 Date: Fri, 12 Dec 2025 23:00:12 +0900 Subject: [PATCH 1/4] chore: update contract feature docs --- .../intelligent-contracts/_meta.json | 1 + .../intelligent-contracts/features/_meta.json | 15 +++ .../features/balances.mdx | 45 +++++++ .../features/calling-llms.mdx | 72 +++++++++++ .../features/debugging.mdx | 31 +++++ .../features/error-handling.mdx | 45 +++++++ .../features/features.mdx | 24 ++++ .../interacting-with-evm-contracts.mdx | 54 ++++++++ ...interacting-with-intelligent-contracts.mdx | 52 ++++++++ .../features/non-determinism.mdx | 56 +++++++++ .../features/storage.mdx | 115 ++++++++++++++++++ .../intelligent-contracts/features/types.mdx | 1 + .../features/upgradability.mdx | 28 +++++ .../features/vector-storage.mdx | 54 ++++++++ .../features/web-access.mdx | 43 +++++++ .../intelligent-contracts/storage.mdx | 2 +- 16 files changed, 637 insertions(+), 1 deletion(-) create mode 100644 pages/developers/intelligent-contracts/features/_meta.json create mode 100644 pages/developers/intelligent-contracts/features/balances.mdx create mode 100644 pages/developers/intelligent-contracts/features/calling-llms.mdx create mode 100644 pages/developers/intelligent-contracts/features/debugging.mdx create mode 100644 pages/developers/intelligent-contracts/features/error-handling.mdx create mode 100644 pages/developers/intelligent-contracts/features/features.mdx create mode 100644 pages/developers/intelligent-contracts/features/interacting-with-evm-contracts.mdx create mode 100644 pages/developers/intelligent-contracts/features/interacting-with-intelligent-contracts.mdx create mode 100644 pages/developers/intelligent-contracts/features/non-determinism.mdx create mode 100644 pages/developers/intelligent-contracts/features/storage.mdx create mode 100644 pages/developers/intelligent-contracts/features/types.mdx create mode 100644 pages/developers/intelligent-contracts/features/upgradability.mdx create mode 100644 pages/developers/intelligent-contracts/features/vector-storage.mdx create mode 100644 pages/developers/intelligent-contracts/features/web-access.mdx diff --git a/pages/developers/intelligent-contracts/_meta.json b/pages/developers/intelligent-contracts/_meta.json index 543dbaa81..b5242e778 100644 --- a/pages/developers/intelligent-contracts/_meta.json +++ b/pages/developers/intelligent-contracts/_meta.json @@ -1,5 +1,6 @@ { "introduction": "Introduction to Intelligent Contracts", + "features": "Feature List", "tooling-setup": "Tooling Setup", "first-contract": "Your First Contract", "types": "Types", diff --git a/pages/developers/intelligent-contracts/features/_meta.json b/pages/developers/intelligent-contracts/features/_meta.json new file mode 100644 index 000000000..e5a6e95bf --- /dev/null +++ b/pages/developers/intelligent-contracts/features/_meta.json @@ -0,0 +1,15 @@ +{ + "features": "Features", + "types": "Types", + "storage": "Storage", + "error-handling": "Error Handling", + "upgradability": "Upgradability", + "balances": "Balances", + "interacting-with-intelligent-contracts": "Interacting with Intelligent Contracts", + "interacting-with-evm-contracts": "Interacting with EVM Contracts", + "vector-storage": "Vector Storage", + "debugging": "Debugging", + "non-determinism": "Non-determinism", + "calling-llms": "Calling LLMs", + "web-access": "Web Access" +} diff --git a/pages/developers/intelligent-contracts/features/balances.mdx b/pages/developers/intelligent-contracts/features/balances.mdx new file mode 100644 index 000000000..368b99140 --- /dev/null +++ b/pages/developers/intelligent-contracts/features/balances.mdx @@ -0,0 +1,45 @@ +import { Callout } from "nextra-theme-docs"; + +# Balances + +## Getting Contract Balance + +Access your contract's balance using `self.balance` within contract methods: + +```python +class Contract(gl.Contract): + @gl.public.view + def get_balance(self): + return self.balance # Shows contract's current balance +``` + +## Getting Another Contract's Balance + +Get any contract's balance by address: + +```python +other_contract = gl.get_contract_at(contract_address) +print(other_contract.balance) +``` + +## Transferring Funds + +Transfer funds from sender to current contract: + +```python +other_contract = gl.get_contract_at(gl.message.sender_address) +other_contract.emit_transfer(value=u256(5)) +other_contract.emit(value=u256(5), on='finalized').transfer(Address("0x...")) +``` + +## Balance Context + +Balance behavior depends on execution context: +- In `write` methods: `self.balance` reflects actual contract balance after state changes +- In `view` methods: Balance shows current state without modifications +- Transfers can only occur in write context + + + If message is emitted on acceptance and previous transaction gets successfully appealed after the emit, + the balance will be decremented nonetheless + diff --git a/pages/developers/intelligent-contracts/features/calling-llms.mdx b/pages/developers/intelligent-contracts/features/calling-llms.mdx new file mode 100644 index 000000000..e644f1149 --- /dev/null +++ b/pages/developers/intelligent-contracts/features/calling-llms.mdx @@ -0,0 +1,72 @@ +# Calling LLMs + +## Basic LLM Calls + +Execute prompts using large language models: + +```python +def llm_call(): + response = gl.nondet.exec_prompt("Answer this question") + return response.strip().lower() + +# Consensus ensures consistent LLM responses +answer = gl.eq_principle.strict_eq(llm_call) +``` + +## 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') + +result = gl.eq_principle.strict_eq(structured_llm_call) +score = result['score'] # Access JSON fields +``` + +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] + ) + +description = gl.eq_principle.strict_eq(vision_analysis) +``` + +Limit of images is just two + +## Response Validation + +Validate and process LLM responses: + +```python +def validated_call(): + 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'] + +result = gl.eq_principle.strict_eq(validated_call) +``` diff --git a/pages/developers/intelligent-contracts/features/debugging.mdx b/pages/developers/intelligent-contracts/features/debugging.mdx new file mode 100644 index 000000000..5c3c750dc --- /dev/null +++ b/pages/developers/intelligent-contracts/features/debugging.mdx @@ -0,0 +1,31 @@ +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:test" } +# ] +# } +``` + +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 + diff --git a/pages/developers/intelligent-contracts/features/error-handling.mdx b/pages/developers/intelligent-contracts/features/error-handling.mdx new file mode 100644 index 000000000..32a49df89 --- /dev/null +++ b/pages/developers/intelligent-contracts/features/error-handling.mdx @@ -0,0 +1,45 @@ +# Error Handling + +## VMErrors + +```python +if invalid_condition: + exit(1) # Terminates with VMError +``` + +## User Errors + +Raise immediate user errors to interrupt execution of current sub-vm: + +```python +gl.advanced.user_error_immediate("Transaction not allowed") +``` + +## Try-Catch with UserError + +Handle user errors within contract execution: + +```python +class Contract(gl.Contract): + @gl.public.write + def safe_operation(self): + try: + gl.advanced.user_error_immediate("Something went wrong") + except gl.vm.UserError as e: + print('Handled error:', e.message) +``` + +## Python Exceptions + +Standard Python exceptions in non-deterministic contexts: + +```python +class Contract(gl.Contract): + @gl.public.write + def risky_operation(self): + def do_something(): + raise Exception("Operation failed") + + # Exception will be handled by consensus mechanism + gl.eq_principle.strict_eq(do_something) +``` diff --git a/pages/developers/intelligent-contracts/features/features.mdx b/pages/developers/intelligent-contracts/features/features.mdx new file mode 100644 index 000000000..daf549f5e --- /dev/null +++ b/pages/developers/intelligent-contracts/features/features.mdx @@ -0,0 +1,24 @@ +import { Card, Cards } from 'nextra-theme-docs' + +# Intelligent Contract Features + +## Deterministic Features + + + + + + + + + + + + +## Non-Deterministic Features + + + + + + diff --git a/pages/developers/intelligent-contracts/features/interacting-with-evm-contracts.mdx b/pages/developers/intelligent-contracts/features/interacting-with-evm-contracts.mdx new file mode 100644 index 000000000..bd8b0a052 --- /dev/null +++ b/pages/developers/intelligent-contracts/features/interacting-with-evm-contracts.mdx @@ -0,0 +1,54 @@ +import { Callout } from "nextra-theme-docs"; + +# Interacting with EVM Contracts + +## 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 + diff --git a/pages/developers/intelligent-contracts/features/interacting-with-intelligent-contracts.mdx b/pages/developers/intelligent-contracts/features/interacting-with-intelligent-contracts.mdx new file mode 100644 index 000000000..247d3893c --- /dev/null +++ b/pages/developers/intelligent-contracts/features/interacting-with-intelligent-contracts.mdx @@ -0,0 +1,52 @@ +# Interacting with Intelligent Contracts + +## 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") +``` + +## 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) +``` diff --git a/pages/developers/intelligent-contracts/features/non-determinism.mdx b/pages/developers/intelligent-contracts/features/non-determinism.mdx new file mode 100644 index 000000000..ac618580e --- /dev/null +++ b/pages/developers/intelligent-contracts/features/non-determinism.mdx @@ -0,0 +1,56 @@ +# Non-determinism + +## Equality Principle + +Execute non-deterministic operations using consensus: + +```python +def non_deterministic_function(): + # This code may produce different results on different nodes + return random.randint(1, 100) + +# Validators reach consensus on the result +result = gl.eq_principle.strict_eq(non_deterministic_function) +``` + +## Accessing External Data + +Use non-deterministic blocks for external API calls: + +```python +@gl.public.write +def fetch_external_data(self): + def fetch_data(): + # External API call - inherently non-deterministic + response = requests.get("https://api.example.com/data") + return response.json() + + # Consensus ensures all validators agree on the result + data = gl.eq_principle.strict_eq(fetch_data) + return data +``` + +## Random Values + +Generate consensus-based random values: + +```python +@gl.public.write +def generate_random(self): + def random_generator(): + import random + return random.random() + + # All validators will agree on this "random" value + consensus_random = gl.eq_principle.strict_eq(random_generator) + return consensus_random +``` + +## When to Use + +Non-deterministic operations are needed for: +- External API calls +- Random number generation +- Current timestamp access +- File system operations +- Any operation that might vary between nodes diff --git a/pages/developers/intelligent-contracts/features/storage.mdx b/pages/developers/intelligent-contracts/features/storage.mdx new file mode 100644 index 000000000..f1a94b7bf --- /dev/null +++ b/pages/developers/intelligent-contracts/features/storage.mdx @@ -0,0 +1,115 @@ +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` | `""` | +| `DynArray` | `[]` | +| `TreeMap` | `{}` | diff --git a/pages/developers/intelligent-contracts/features/types.mdx b/pages/developers/intelligent-contracts/features/types.mdx new file mode 100644 index 000000000..3b308433a --- /dev/null +++ b/pages/developers/intelligent-contracts/features/types.mdx @@ -0,0 +1 @@ +# Types \ No newline at end of file diff --git a/pages/developers/intelligent-contracts/features/upgradability.mdx b/pages/developers/intelligent-contracts/features/upgradability.mdx new file mode 100644 index 000000000..7d025fc05 --- /dev/null +++ b/pages/developers/intelligent-contracts/features/upgradability.mdx @@ -0,0 +1,28 @@ +# Upgradability + +During deployment contract may freeze certain storage slots. Frozen slots can be modified +only if sender of the transaction is in *upgraders* list. Hence frozen empty *upgraders* list +with frozen code slot makes contract non-upgradable. + +See [GenVM specification](https://sdk.genlayer.com/v0.2.7/spec/04-contract-interface/04-upgradability.html) for more details. + +## Example + + +```python +class Proxy(gl.Contract): + def __init__(self, upgrader: Address): + root = gl.storage.Root.get() + root.upgraders.append(upgrader) + # default bootloader freezes relevant slots + # so we need to modify only upgraders + + @gl.public.write + def update_code(self, new_code: bytes): + root = gl.storage.Root.get() + code_vla = root.code.get() + + # If message.sender is not in upgraders, below will issue a VMError + code_vla.truncate() # Clear existing code + code_vla.extend(new_code) # Put the new code +``` diff --git a/pages/developers/intelligent-contracts/features/vector-storage.mdx b/pages/developers/intelligent-contracts/features/vector-storage.mdx new file mode 100644 index 000000000..ac1022aaa --- /dev/null +++ b/pages/developers/intelligent-contracts/features/vector-storage.mdx @@ -0,0 +1,54 @@ +# Vector Storage + +## Vector Database + +Store and query high-dimensional vectors using VecDB: + +```python +import numpy as np +import genlayer_embeddings as gle + +class Contract(gl.Contract): + # VecDB[dtype, dimensions, metadata_type, distance_metric] + vectors: gle.VecDB[np.float32, typing.Literal[5], str, gle.EuclideanDistanceSquared] + + @gl.public.write + def store_vector(self, data: list[float], label: str): + vector = np.array(data, dtype=np.float32) + self.vectors.insert(vector, label) +``` + +## K-Nearest Neighbors + +Find similar vectors using KNN search: + +```python +@gl.public.view +def find_similar(self, query: list[float], k: int): + query_vector = np.ones(5, dtype=np.float32) + results = list(self.vectors.knn(query_vector, k)) + return results +``` + +## Text Embeddings + +Generate embeddings from text using sentence transformers: + +```python +@gl.public.write +def embed_text(self, text: str): + def generate_embedding(): + embeddings_generator = gle.SentenceTransformer('all-MiniLM-L6-v2') + return embeddings_generator(text) + + # Run in non-deterministic context + embedding = gl.eq_principle.strict_eq(generate_embedding) + return embedding.sum() # Process the embedding +``` + +## Distance Metrics + +Choose appropriate distance metrics for your use case: +- `gle.EuclideanDistanceSquared` - For general similarity +- `gle.CosineDistance` - For semantic similarity +- `gle.ManhattanDistance` - For sparse data \ No newline at end of file diff --git a/pages/developers/intelligent-contracts/features/web-access.mdx b/pages/developers/intelligent-contracts/features/web-access.mdx new file mode 100644 index 000000000..b48a9d0e2 --- /dev/null +++ b/pages/developers/intelligent-contracts/features/web-access.mdx @@ -0,0 +1,43 @@ +# Web Access + +## HTTP Requests + +Send data to external services: + +```python +def post_request(): + response = gl.nondet.web.request( + webhook_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(): + # 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(): + # Capture page as image + screenshot = gl.nondet.web.render(url, mode='screenshot') + return screenshot + +image_data = gl.eq_principle.strict_eq(take_screenshot) +``` diff --git a/pages/developers/intelligent-contracts/storage.mdx b/pages/developers/intelligent-contracts/storage.mdx index ab38c7272..4d0ffb322 100644 --- a/pages/developers/intelligent-contracts/storage.mdx +++ b/pages/developers/intelligent-contracts/storage.mdx @@ -197,7 +197,7 @@ memory_user = gl.storage.copy_to_memory(storage_user) def my_nondet_function(): # Storage objects cannot be used directly in nondet blocks # print('storage: not accessible', storage_user) # Error - storage not accessible! - + # But memory objects work fine print('copied out: ok', memory_user) # Works! return str(memory_user) # String conversion also works with memory objects From 34845c3b259f9af3979b12386228baa275c3e8b4 Mon Sep 17 00:00:00 2001 From: kp2pml30 Date: Fri, 12 Dec 2025 23:53:07 +0900 Subject: [PATCH 2/4] chore: remove obsolete files --- .../genlayer-js.mdx | 2 +- .../intelligent-contracts/_meta.json | 1 - .../advanced-features/_meta.json | 6 - .../contract-to-contract-interaction.mdx | 135 ------------------ .../advanced-features/error-handling.mdx | 40 ------ .../advanced-features/vector-store.mdx | 102 ------------- .../intelligent-contracts/features/_meta.json | 1 + .../features/error-handling.mdx | 84 +++++++---- .../features/features.mdx | 1 + .../special-methods.mdx} | 47 +----- .../features/vector-storage.mdx | 124 +++++++++++----- 11 files changed, 150 insertions(+), 393 deletions(-) delete mode 100644 pages/developers/intelligent-contracts/advanced-features/_meta.json delete mode 100644 pages/developers/intelligent-contracts/advanced-features/contract-to-contract-interaction.mdx delete mode 100644 pages/developers/intelligent-contracts/advanced-features/error-handling.mdx delete mode 100644 pages/developers/intelligent-contracts/advanced-features/vector-store.mdx rename pages/developers/intelligent-contracts/{advanced-features/working-with-balances.mdx => features/special-methods.mdx} (51%) diff --git a/pages/developers/decentralized-applications/genlayer-js.mdx b/pages/developers/decentralized-applications/genlayer-js.mdx index 95c8ec573..ec480ecda 100644 --- a/pages/developers/decentralized-applications/genlayer-js.mdx +++ b/pages/developers/decentralized-applications/genlayer-js.mdx @@ -49,7 +49,7 @@ npm install genlayer-js Here’s how to initialize the client and connect to the GenLayer Studio: -- **[Reading a transaction](../intelligent-contracts/advanced-features/contract-to-contract-interaction)**: Understand how to query transactions on the GenLayer network. +- **[Reading a transaction](../intelligent-contracts/features/interacting-with-intelligent-contracts)**: Understand how to query transactions on the GenLayer network. - **[Reading a contract](/developers/decentralized-applications/reading-data)**: Discover how to read data from intelligent contracts on GenLayer. - **[Writing a contract](../intelligent-contracts/tools/genlayer-studio/contract-state)**: Learn how to write data to intelligent contracts on GenLayer. diff --git a/pages/developers/intelligent-contracts/_meta.json b/pages/developers/intelligent-contracts/_meta.json index b5242e778..d996e6d3f 100644 --- a/pages/developers/intelligent-contracts/_meta.json +++ b/pages/developers/intelligent-contracts/_meta.json @@ -10,7 +10,6 @@ "debugging": "Debugging", "deploying": "Deploying", "crafting-prompts": "Crafting Prompts", - "advanced-features": "Advanced Features", "security-and-best-practices": "Security and Best Practices", "examples": "Examples", "tools": "Tools", diff --git a/pages/developers/intelligent-contracts/advanced-features/_meta.json b/pages/developers/intelligent-contracts/advanced-features/_meta.json deleted file mode 100644 index 707d1e82f..000000000 --- a/pages/developers/intelligent-contracts/advanced-features/_meta.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "contract-to-contract-interaction": "Contract-to-Contract Interaction", - "working-with-balances": "Working with balances", - "vector-store": "Vector Store", - "error-handling": "Error Handling" -} diff --git a/pages/developers/intelligent-contracts/advanced-features/contract-to-contract-interaction.mdx b/pages/developers/intelligent-contracts/advanced-features/contract-to-contract-interaction.mdx deleted file mode 100644 index cea434e48..000000000 --- a/pages/developers/intelligent-contracts/advanced-features/contract-to-contract-interaction.mdx +++ /dev/null @@ -1,135 +0,0 @@ -import { Callout } from 'nextra-theme-docs' - -# Contract-to-Contract Interaction - -Contract-to-Contract Interaction in GenLayer allows developers to create more complex and modular Intelligent Contracts by enabling communication between them. This feature is crucial for building scalable and composable decentralized applications, allowing contracts to read from and write to other contracts. - - - Please note that this feature is in progress and will be subject to changes in the near future. - -## Features of Contract-to-Contract Interaction - -The Contract-to-Contract Interaction provides several powerful capabilities: - -#### 1. Cross-Contract Reads -Contracts can read data from other contracts, allowing for the sharing of state and information across different parts of your application. - -#### 2. Cross-Contract Writes -Contracts can initiate state changes in other contracts, enabling complex multi-step operations that span multiple contracts. - -#### 3. Modular Design -By separating concerns into different contracts, developers can create more maintainable and upgradable systems. - -#### 4. Composability -Contracts can be designed to work together, creating more powerful and flexible applications by combining different functionalities. - -## Methods for Contract-to-Contract Interaction - -#### 1. Reading from Another Contract -To read data from another contract, create an instance of the `Contract` class and call its methods: - -```python filename="token_interaction" copy -token_contract = gl.ContractAt(self.token_contract_address) -balance = token_contract.view().get_balance_of(account_address) -``` - -#### 2. Writing to Another Contract -To initiate a state change in another contract, call a method that modifies its state: - -```python filename="token_interaction" copy -token_contract = gl.ContractAt(self.token_contract_address) -success = token_contract.emit().transfer(to_address, amount) -``` - -#### 3. Handling Multiple Contract Interactions -For more complex scenarios involving multiple contracts: - -```python filename="multi_contract_interaction" copy -class MultiContractInteraction(gl.Contract): - token_contract: Address - storage_contract: Address - - def __init__(self, token_contract: str, storage_contract: str): - self.token_contract = Address(token_contract) - self.storage_contract = Address(storage_contract) - - @gl.public.write - def complex_operation(self, account: str, amount: int, data: str) -> bool: - token = gl.ContractAt(self.token_contract) - storage = gl.ContractAt(self.storage_contract) - - # Read from token contract - balance = token.view().get_balance_of(account) - - if balance >= amount: - # Write to token contract - token.emit().transfer(self.address, amount) - # Write to storage contract - storage.emit().store_data(account, data) - return True - return False -``` -## How to Use Contract-to-Contract Interaction in Your Contracts - -As GenLayer calldata is dynamically typed users can send whatever they want to a contract, or use statically typed interface to facilitate type checking and autocompletion - - - Exact parameters of `.view()` and `.emit()` are subject to change - - -### Dynamically typed approach - -```py -address = Address("0x03FB09251eC05ee9Ca36c98644070B89111D4b3F") -result = gl.ContractAt(address).view().method_name(1, '234') -gl.ContractAt(address).emit().method_name(1, '234') -# ^ write methods do not return anything! -``` - -### Statically typed approach - -```py -@gl.contract_interface -class GenLayerContractIface: - class View: - def method_name(self, a: int, b: str): ... - - class Write: - pass - -### in your contract method ### - -address = Address("0x03FB09251eC05ee9Ca36c98644070B89111D4b3F") -result = GenLayerContractIface(address).view().method_name(1, '234') -``` - -## Interacting with Ghost Contracts - -Eth contracts have statically typed calldata format, which means that only statically typed approach with interfaces is applicable - - - This is not supported in the Studio right now - - -```py -@gl.ghost_contract -class GhostContractIface: - class View: - def method_name(self, param: str, /) -> tuple[u32, str]: ... - - class Write: - def bar(self, param: str, /): ... - -### in your contract method ### - -address = Address("0x03FB09251eC05ee9Ca36c98644070B89111D4b3F") -i, s = GhostContractIface(address).view().method_name('234') -``` - -## Best Practices for Contract-to-Contract Interaction - -1. **Security**: Always validate inputs and check permissions before allowing cross-contract interactions. -2. **Error Handling**: Implement proper error handling for cases where called contracts might revert or throw exceptions. -3. **Upgradability**: Consider using upgradable patterns if you need to change contract interactions in the future. -4. **Testing**: Thoroughly test all contract interactions, including edge cases and potential vulnerabilities. - diff --git a/pages/developers/intelligent-contracts/advanced-features/error-handling.mdx b/pages/developers/intelligent-contracts/advanced-features/error-handling.mdx deleted file mode 100644 index 3c369b95d..000000000 --- a/pages/developers/intelligent-contracts/advanced-features/error-handling.mdx +++ /dev/null @@ -1,40 +0,0 @@ -import { Callout } from 'nextra-theme-docs' - -# Error handling - -Sometimes contracts can produce errors. There are two kinds of GenVM errors: - -1. unrecoverable errors - - `exit(x)` where $x \neq 0$ (if $x = 0$ then return is considered to be `None`) - - unhandled `Exception`, which will result in `exit(1)` -2. rollbacks - - `raise Rollback("message")` --- raises regular Python exception that can be caught, - if it escapes your contract, then it will call the later - - `gl.rollback_immediate("message")` --- immediately terminates current sub-vm without unwinding the stack, - which is more cost-efficient, but can't be caught in the current sub-vm - -Built-in `gl.eq_principle_*` functions family will `raise Rollback(sub_vm_message)` in case non-deterministic block agreed on a rollback. -Which means that deterministic block can catch and inspect such error from a non-deterministic block and act on it. -Note that rollback message is compared for **strict** equality, which means that you should not include stacktrace there, but rather use some descriptive error codes. - -Difference between rollbacks and unrecoverable errors is that unrecoverable errors can't be handled by user code and their "message" contains only short description provided by the GenVM. -However, there are two exceptions of when a contract can observe unrecoverable errors: -1. If leaders non-deterministic block produced an error. It is discouraged to do that and indicates that some unexpected (by contract) corner case was reached -2. You can get one out of `gl.advanced.sandbox` method - -```python filename="catching_a_rollback" copy -# { "Depends": "py-genlayer:test" } -from genlayer import * - - -class Contract(gl.Contract): - @gl.public.write - def main(self): - def run(): - raise gl.Rollback('my message') - - try: - gl.eq_principle_strict_eq(run) - except Rollback as r: - print(r.msg) # will print `my message` -``` diff --git a/pages/developers/intelligent-contracts/advanced-features/vector-store.mdx b/pages/developers/intelligent-contracts/advanced-features/vector-store.mdx deleted file mode 100644 index 5c412c838..000000000 --- a/pages/developers/intelligent-contracts/advanced-features/vector-store.mdx +++ /dev/null @@ -1,102 +0,0 @@ -# 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:test" }, -# { "Depends": "py-genlayer:test" } -# ] -# } - -from genlayer import * -import genlayermodelwrappers -import numpy as np -from dataclasses import dataclass - - -@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() - -``` - - - diff --git a/pages/developers/intelligent-contracts/features/_meta.json b/pages/developers/intelligent-contracts/features/_meta.json index e5a6e95bf..3bc9cd074 100644 --- a/pages/developers/intelligent-contracts/features/_meta.json +++ b/pages/developers/intelligent-contracts/features/_meta.json @@ -9,6 +9,7 @@ "interacting-with-evm-contracts": "Interacting with EVM Contracts", "vector-storage": "Vector Storage", "debugging": "Debugging", + "special-methods": "Special Methods", "non-determinism": "Non-determinism", "calling-llms": "Calling LLMs", "web-access": "Web Access" diff --git a/pages/developers/intelligent-contracts/features/error-handling.mdx b/pages/developers/intelligent-contracts/features/error-handling.mdx index 32a49df89..34453f203 100644 --- a/pages/developers/intelligent-contracts/features/error-handling.mdx +++ b/pages/developers/intelligent-contracts/features/error-handling.mdx @@ -1,45 +1,81 @@ # Error Handling -## VMErrors +## Unrecoverable Errors + +Exit codes terminate execution immediately: ```python if invalid_condition: - exit(1) # Terminates with VMError + exit(1) +``` + +Unhandled exceptions also become unrecoverable: + +```python +raise Exception("Critical error") # Becomes exit(1) ``` -## User Errors +## UserError -Raise immediate user errors to interrupt execution of current sub-vm: +User-generated errors with UTF-8 encoded messages: ```python -gl.advanced.user_error_immediate("Transaction not allowed") +# Can be caught in current sub-vm +raise gl.UserError("Invalid input") + +# Immediate user error, more efficient but can't be caught +gl.user_error_immediate("Insufficient funds") ``` -## Try-Catch with UserError +## VMError -Handle user errors within contract execution: +VM-generated errors with predefined string codes: ```python -class Contract(gl.Contract): - @gl.public.write - def safe_operation(self): - try: - gl.advanced.user_error_immediate("Something went wrong") - except gl.vm.UserError as e: - print('Handled error:', e.message) +# 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) ``` -## Python Exceptions +## Catching UserError -Standard Python exceptions in non-deterministic contexts: +Handle user errors from sub-VMs: ```python -class Contract(gl.Contract): - @gl.public.write - def risky_operation(self): - def do_something(): - raise Exception("Operation failed") - - # Exception will be handled by consensus mechanism - gl.eq_principle.strict_eq(do_something) +def risky_operation(): + raise gl.UserError("Operation failed") + +try: + result = gl.eq_principle.strict_eq(risky_operation) +except gl.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.UserError("INVALID_STATE") + return "success" + +try: + gl.eq_principle.strict_eq(nondet_block) +except gl.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) diff --git a/pages/developers/intelligent-contracts/features/features.mdx b/pages/developers/intelligent-contracts/features/features.mdx index daf549f5e..6ad71ab2a 100644 --- a/pages/developers/intelligent-contracts/features/features.mdx +++ b/pages/developers/intelligent-contracts/features/features.mdx @@ -13,6 +13,7 @@ import { Card, Cards } from 'nextra-theme-docs' + ## Non-Deterministic Features diff --git a/pages/developers/intelligent-contracts/advanced-features/working-with-balances.mdx b/pages/developers/intelligent-contracts/features/special-methods.mdx similarity index 51% rename from pages/developers/intelligent-contracts/advanced-features/working-with-balances.mdx rename to pages/developers/intelligent-contracts/features/special-methods.mdx index e0a2cb4a7..03b53014b 100644 --- a/pages/developers/intelligent-contracts/advanced-features/working-with-balances.mdx +++ b/pages/developers/intelligent-contracts/features/special-methods.mdx @@ -1,48 +1,4 @@ -# Working With Balances - -## Core concepts - -Each contract has a `balance` property: both derivatives of `gl.Contract` and `gl.ContractAt` - -All transactions and messages (both internal and external) can have attached `value`: `ContractAt(...).emit(value=100).my_method()` - -To get `value` from the incoming message users can read `gl.message.value` - -There is a special method to call a "nameless" transfer: `ContractAt(...).emit_transfer(value=100)`. -You can receive such transfers by overriding `__receive__(self)` method. - -There are few simple rules: -1. If your method expects a `value`, annotate it with `@gl.public.write.payable` instead of just `write` -2. Emitting transaction to a non-payable method with non-zero value is an error -3. If transaction outcome is not *success*, attached value is sent back - -## Code example - -```python filename="working_with_balances" copy -class TokenForwarder(gl.Contract): - vault_contract: Address - - def __init__(self, vault_contract: str): - self.vault_contract = Address(vault_contract) - - @gl.public.write.payable - def forward(self) -> None: - vault = gl.ContractAt(self.vault_contract) - amount = gl.message.value - vault.emit_transfer(value=amount) - - - @gl.public.write.payable - def donate_twice(self) -> bool: - vault = gl.ContractAt(self.vault_contract) - amount = gl.message.value - if self.balance < amount: - return False - vault.emit(value=amount*2).save(self.address) - return True -``` - -## Special methods +# Special Methods There are two special methods allowed in each contract **definition**: ```python @@ -89,5 +45,4 @@ graph TD has_value -->|no| fallback_defined fallback_defined -->|yes| fallback2{{call __handle_undefined_method__}} fallback_defined -->|no| error2{{error}} - ``` diff --git a/pages/developers/intelligent-contracts/features/vector-storage.mdx b/pages/developers/intelligent-contracts/features/vector-storage.mdx index ac1022aaa..5c412c838 100644 --- a/pages/developers/intelligent-contracts/features/vector-storage.mdx +++ b/pages/developers/intelligent-contracts/features/vector-storage.mdx @@ -1,54 +1,102 @@ -# Vector Storage +# 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. -## Vector Database +## Key Features of Vector Store +The Vector Store provides several powerful features for managing text data: -Store and query high-dimensional vectors using VecDB: +#### 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. -```python -import numpy as np -import genlayer_embeddings as gle +#### 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. -class Contract(gl.Contract): - # VecDB[dtype, dimensions, metadata_type, distance_metric] - vectors: gle.VecDB[np.float32, typing.Literal[5], str, gle.EuclideanDistanceSquared] +#### 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. - @gl.public.write - def store_vector(self, data: list[float], label: str): - vector = np.array(data, dtype=np.float32) - self.vectors.insert(vector, label) -``` +#### 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. -## K-Nearest Neighbors +## 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. -Find similar vectors using KNN search: +#### Importing Vector Store +First, import the VectorStore class from the standard library in your contract: ```python -@gl.public.view -def find_similar(self, query: list[float], k: int): - query_vector = np.ones(5, dtype=np.float32) - results = list(self.vectors.knn(query_vector, k)) - return results +from backend.node.genvm.std.vector_store import VectorStore ``` -## Text Embeddings - -Generate embeddings from text using sentence transformers: +#### Creating a Contract with Vector Store +Here’s an example of a contract using the Vector Store for indexing and searching text logs: ```python -@gl.public.write -def embed_text(self, text: str): - def generate_embedding(): - embeddings_generator = gle.SentenceTransformer('all-MiniLM-L6-v2') - return embeddings_generator(text) - - # Run in non-deterministic context - embedding = gl.eq_principle.strict_eq(generate_embedding) - return embedding.sum() # Process the embedding +# { +# "Seq": [ +# { "Depends": "py-lib-genlayermodelwrappers:test" }, +# { "Depends": "py-genlayer:test" } +# ] +# } + +from genlayer import * +import genlayermodelwrappers +import numpy as np +from dataclasses import dataclass + + +@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() + ``` -## Distance Metrics -Choose appropriate distance metrics for your use case: -- `gle.EuclideanDistanceSquared` - For general similarity -- `gle.CosineDistance` - For semantic similarity -- `gle.ManhattanDistance` - For sparse data \ No newline at end of file + From 4e9894e715f96270762cd0b91b4a12e7f0ad255f Mon Sep 17 00:00:00 2001 From: kp2pml30 Date: Sun, 18 Jan 2026 23:50:50 +0900 Subject: [PATCH 3/4] chore: update docs --- .../intelligent-contracts/features/_meta.json | 2 +- .../features/calling-llms.mdx | 7 +- .../features/features.mdx | 1 + .../features/non-determinism.mdx | 109 ++++++++++++++---- .../intelligent-contracts/features/random.mdx | 23 ++++ .../features/storage.mdx | 2 + .../intelligent-contracts/features/types.mdx | 1 - 7 files changed, 118 insertions(+), 27 deletions(-) create mode 100644 pages/developers/intelligent-contracts/features/random.mdx delete mode 100644 pages/developers/intelligent-contracts/features/types.mdx diff --git a/pages/developers/intelligent-contracts/features/_meta.json b/pages/developers/intelligent-contracts/features/_meta.json index 3bc9cd074..3d524f286 100644 --- a/pages/developers/intelligent-contracts/features/_meta.json +++ b/pages/developers/intelligent-contracts/features/_meta.json @@ -1,6 +1,5 @@ { "features": "Features", - "types": "Types", "storage": "Storage", "error-handling": "Error Handling", "upgradability": "Upgradability", @@ -10,6 +9,7 @@ "vector-storage": "Vector Storage", "debugging": "Debugging", "special-methods": "Special Methods", + "random": "Random", "non-determinism": "Non-determinism", "calling-llms": "Calling LLMs", "web-access": "Web Access" diff --git a/pages/developers/intelligent-contracts/features/calling-llms.mdx b/pages/developers/intelligent-contracts/features/calling-llms.mdx index e644f1149..61386c916 100644 --- a/pages/developers/intelligent-contracts/features/calling-llms.mdx +++ b/pages/developers/intelligent-contracts/features/calling-llms.mdx @@ -1,3 +1,5 @@ +import { Callout } from 'nextra-theme-docs' + # Calling LLMs ## Basic LLM Calls @@ -49,7 +51,10 @@ def vision_analysis(): description = gl.eq_principle.strict_eq(vision_analysis) ``` -Limit of images is just two + + Limit of images is two + + ## Response Validation diff --git a/pages/developers/intelligent-contracts/features/features.mdx b/pages/developers/intelligent-contracts/features/features.mdx index 6ad71ab2a..90068600f 100644 --- a/pages/developers/intelligent-contracts/features/features.mdx +++ b/pages/developers/intelligent-contracts/features/features.mdx @@ -14,6 +14,7 @@ import { Card, Cards } from 'nextra-theme-docs' + ## Non-Deterministic Features diff --git a/pages/developers/intelligent-contracts/features/non-determinism.mdx b/pages/developers/intelligent-contracts/features/non-determinism.mdx index ac618580e..d58ab6963 100644 --- a/pages/developers/intelligent-contracts/features/non-determinism.mdx +++ b/pages/developers/intelligent-contracts/features/non-determinism.mdx @@ -1,8 +1,20 @@ # Non-determinism +## 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 + ## Equality Principle -Execute non-deterministic operations using consensus: +GenLayer provides three equality principles for handling non-deterministic operations. For detailed information, see [Equivalence Principle](/developers/intelligent-contracts/equivalence-principle). + +### Strict Equality + +Requires exact matches between validator outputs. Use for deterministic results or objective data: ```python def non_deterministic_function(): @@ -13,44 +25,93 @@ def non_deterministic_function(): result = gl.eq_principle.strict_eq(non_deterministic_function) ``` -## Accessing External Data +### Comparative Principle + +Validators perform the same task and compare results using predefined 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 Principle + +Validators evaluate outputs based on task and criteria without performing the same computation: + +```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 Equality Principles -Use non-deterministic blocks for external API calls: +For advanced use cases, create custom consensus logic using `gl.vm.run_nondet`: + +```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(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 = requests.get("https://api.example.com/data") - return response.json() + 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 ``` -## Random Values +### LLM Integration -Generate consensus-based random values: +Execute AI prompts using comparative principle. For more detailed examples, see [Calling LLMs](/developers/intelligent-contracts/features/calling-llms): ```python @gl.public.write -def generate_random(self): - def random_generator(): - import random - return random.random() - - # All validators will agree on this "random" value - consensus_random = gl.eq_principle.strict_eq(random_generator) - return consensus_random -``` +def ai_decision(self, prompt: str): + def call_llm(): + response = gl.nondet.exec_prompt(prompt) + return response.strip() -## When to Use - -Non-deterministic operations are needed for: -- External API calls -- Random number generation -- Current timestamp access -- File system operations -- Any operation that might vary between nodes + # 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 +``` diff --git a/pages/developers/intelligent-contracts/features/random.mdx b/pages/developers/intelligent-contracts/features/random.mdx new file mode 100644 index 000000000..4b6f9ab3e --- /dev/null +++ b/pages/developers/intelligent-contracts/features/random.mdx @@ -0,0 +1,23 @@ +# Random + +Getting random in deterministic part may be difficult. Trying to delegate it to non-deterministic block will cause +yout 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 + 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.hexdigest() + hash_obj.update(chunk) +``` diff --git a/pages/developers/intelligent-contracts/features/storage.mdx b/pages/developers/intelligent-contracts/features/storage.mdx index f1a94b7bf..51c5007c5 100644 --- a/pages/developers/intelligent-contracts/features/storage.mdx +++ b/pages/developers/intelligent-contracts/features/storage.mdx @@ -111,5 +111,7 @@ Storage is zero-initialized: | `bool` | `False` | | `float` | `+0.0` | | `str` | `""` | +| `bytes` | `b""` | +| `Address` | `0x0...` | | `DynArray` | `[]` | | `TreeMap` | `{}` | diff --git a/pages/developers/intelligent-contracts/features/types.mdx b/pages/developers/intelligent-contracts/features/types.mdx deleted file mode 100644 index 3b308433a..000000000 --- a/pages/developers/intelligent-contracts/features/types.mdx +++ /dev/null @@ -1 +0,0 @@ -# Types \ No newline at end of file From 52df39d1e731f0368824624ba46890cb39f62226 Mon Sep 17 00:00:00 2001 From: kp2pml30 Date: Mon, 26 Jan 2026 22:13:35 +0900 Subject: [PATCH 4/4] docs: address feedback --- .../intelligent-contracts/features/calling-llms.mdx | 2 +- .../developers/intelligent-contracts/features/features.mdx | 2 +- .../intelligent-contracts/features/non-determinism.mdx | 2 ++ pages/developers/intelligent-contracts/features/random.mdx | 5 +++-- .../intelligent-contracts/features/upgradability.mdx | 6 +++--- .../intelligent-contracts/features/web-access.mdx | 5 ++++- 6 files changed, 14 insertions(+), 8 deletions(-) diff --git a/pages/developers/intelligent-contracts/features/calling-llms.mdx b/pages/developers/intelligent-contracts/features/calling-llms.mdx index 61386c916..2e2c10286 100644 --- a/pages/developers/intelligent-contracts/features/calling-llms.mdx +++ b/pages/developers/intelligent-contracts/features/calling-llms.mdx @@ -42,7 +42,7 @@ Process images with vision models: ```python def vision_analysis(): prompt = "Describe what you see in this image" - image_data = b"\x89PNG...' + image_data = b"\x89PNG..." return gl.nondet.exec_prompt( prompt, images=[image_data] diff --git a/pages/developers/intelligent-contracts/features/features.mdx b/pages/developers/intelligent-contracts/features/features.mdx index 90068600f..230f313be 100644 --- a/pages/developers/intelligent-contracts/features/features.mdx +++ b/pages/developers/intelligent-contracts/features/features.mdx @@ -4,7 +4,7 @@ import { Card, Cards } from 'nextra-theme-docs' ## Deterministic Features - + diff --git a/pages/developers/intelligent-contracts/features/non-determinism.mdx b/pages/developers/intelligent-contracts/features/non-determinism.mdx index d58ab6963..4e0f6e0aa 100644 --- a/pages/developers/intelligent-contracts/features/non-determinism.mdx +++ b/pages/developers/intelligent-contracts/features/non-determinism.mdx @@ -17,6 +17,8 @@ GenLayer provides three equality principles for handling non-deterministic opera Requires exact matches between validator outputs. Use for deterministic results or objective data: ```python +import random + def non_deterministic_function(): # This code may produce different results on different nodes return random.randint(1, 100) diff --git a/pages/developers/intelligent-contracts/features/random.mdx b/pages/developers/intelligent-contracts/features/random.mdx index 4b6f9ab3e..cc6c966af 100644 --- a/pages/developers/intelligent-contracts/features/random.mdx +++ b/pages/developers/intelligent-contracts/features/random.mdx @@ -1,7 +1,7 @@ # Random Getting random in deterministic part may be difficult. Trying to delegate it to non-deterministic block will cause -yout contract to either never agree on that block or to trust the leader. For that reason it is advised to use seeded random +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 @@ -12,12 +12,13 @@ yout contract to either never agree on that block or to trust the leader. For th ```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.hexdigest() + return hash_obj.digest() hash_obj.update(chunk) ``` diff --git a/pages/developers/intelligent-contracts/features/upgradability.mdx b/pages/developers/intelligent-contracts/features/upgradability.mdx index 7d025fc05..cca2f89e0 100644 --- a/pages/developers/intelligent-contracts/features/upgradability.mdx +++ b/pages/developers/intelligent-contracts/features/upgradability.mdx @@ -1,8 +1,8 @@ # Upgradability During deployment contract may freeze certain storage slots. Frozen slots can be modified -only if sender of the transaction is in *upgraders* list. Hence frozen empty *upgraders* list -with frozen code slot makes contract non-upgradable. +only if sender of the transaction is in *upgraders* list. Therefore, an empty locked *upgraders* list +with a frozen code slot makes the contract non-upgradable. See [GenVM specification](https://sdk.genlayer.com/v0.2.7/spec/04-contract-interface/04-upgradability.html) for more details. @@ -22,7 +22,7 @@ class Proxy(gl.Contract): root = gl.storage.Root.get() code_vla = root.code.get() - # If message.sender is not in upgraders, below will issue a VMError + # If gl.message.sender_address is not in upgraders, below will issue a VMError code_vla.truncate() # Clear existing code code_vla.extend(new_code) # Put the new code ``` diff --git a/pages/developers/intelligent-contracts/features/web-access.mdx b/pages/developers/intelligent-contracts/features/web-access.mdx index b48a9d0e2..c24910f88 100644 --- a/pages/developers/intelligent-contracts/features/web-access.mdx +++ b/pages/developers/intelligent-contracts/features/web-access.mdx @@ -6,8 +6,9 @@ Send data to external services: ```python def post_request(): + url = "https://test-server.genlayer.com/body/echo" response = gl.nondet.web.request( - webhook_url, + url, method='POST', body={} ) @@ -22,6 +23,7 @@ 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 @@ -35,6 +37,7 @@ 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