Skip to content

docs: add complete examples and best practices for contract interactions#401

Open
HusseinAdeiza wants to merge 1 commit intogenlayerlabs:mainfrom
HusseinAdeiza:docs/enhance-contract-interaction-examples
Open

docs: add complete examples and best practices for contract interactions#401
HusseinAdeiza wants to merge 1 commit intogenlayerlabs:mainfrom
HusseinAdeiza:docs/enhance-contract-interaction-examples

Conversation

@HusseinAdeiza
Copy link
Copy Markdown

@HusseinAdeiza HusseinAdeiza commented Apr 30, 2026

Summary

Enhances the "Interacting with Intelligent Contracts" documentation with complete working examples, error handling patterns, common mistakes, and best practices.

Changes

  • ✅ Added complete working example: TokenGatedVault contract demonstrating real-world contract-to-contract interactions
  • ✅ Added error handling section with try/catch patterns
  • ✅ Added "Common Mistakes" section covering:
    • view() vs emit() confusion
    • Return value checking
    • Message ordering with accepted vs finalized
  • ✅ Added "Best Practices" section covering:
    • Static typing for IDE support
    • Contract address validation
    • Reentrancy protection
    • Critical operation finalization
  • ✅ Added testing examples with pytest

Impact

This PR significantly improves developer onboarding by providing complete, copy-paste ready examples instead of just syntax fragments. New developers can now see:

  • How to structure real contracts
  • How to handle errors properly
  • Common pitfalls to avoid
  • Testing patterns to follow

Files Changed

  • pages/developers/intelligent-contracts/features/interacting-with-intelligent-contracts.mdx (+226 lines)

Related Issues

Addresses part of the ecosystem improvements discussed in #346 (adding more complete examples and best practices guides).

Summary by CodeRabbit

  • Documentation
    • Expanded developer guide for intelligent contract interactions with comprehensive Python code examples
    • Added error handling patterns and exception conversion guidance
    • Documented common incorrect interaction patterns alongside recommended best-practice approaches
    • Included reentrancy-safe patterns and sample test implementation for contract deployment and validation

- Add complete working example: TokenGatedVault contract
- Add error handling section with try/catch examples
- Add common mistakes section (view/emit confusion, return values, ordering)
- Add best practices (static typing, address validation, reentrancy protection, finalization)
- Add testing examples with pytest

This enhancement provides developers with real-world examples and
patterns instead of just syntax fragments, greatly improving the
onboarding experience for contract-to-contract interactions.
@netlify
Copy link
Copy Markdown

netlify Bot commented Apr 30, 2026

Deploy Preview for genlayer-docs ready!

Name Link
🔨 Latest commit 3e074b5
🔍 Latest deploy log https://app.netlify.com/projects/genlayer-docs/deploys/69f2f1a67283b3000849dd57
😎 Deploy Preview https://deploy-preview-401--genlayer-docs.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 30, 2026

📝 Walkthrough

Walkthrough

The documentation page is enhanced with comprehensive Python code examples and best practices for interacting with intelligent contracts, including contract interface patterns, error handling, anti-patterns, and pytest test examples.

Changes

Cohort / File(s) Summary
Documentation Enhancement
pages/developers/intelligent-contracts/features/interacting-with-intelligent-contracts.mdx
Added end-to-end examples of typed contract interfaces, error handling patterns with try/except, common anti-patterns, best-practice guidance for reentrancy-safety and finalization, and sample pytest-style tests with contract deployment and state assertions.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Suggested reviewers

  • cristiam86
  • MuncleUscles

Poem

🐰 ✨ Our docs now hop with vibrant code,
Examples light the developer's road,
From vault to token, errors caught with grace,
Best practices lead the way to a better place! 📚

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The pull request title clearly summarizes the main change: adding comprehensive examples and best practices for contract interactions to documentation.
Description check ✅ Passed The pull request description is comprehensive, covering summary, detailed changes with checkmarks, impact analysis, files changed, and related issues. It follows the spirit of the template with all key information provided.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
Review rate limit: 0/1 reviews remaining, refill in 60 minutes.

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
pages/developers/intelligent-contracts/features/interacting-with-intelligent-contracts.mdx (1)

104-105: ⚡ Quick win

Consider adding one sentence that highlights the “Intelligent Blockchain” angle.

This new section is strong technically; adding a short note about internet-access + subjective-decision use cases would better align with GenLayer’s differentiator and onboarding narrative.

Based on learnings: "The documentation should emphasize GenLayer's unique aspects as an 'Intelligent Blockchain' that enables contracts to access the internet and make subjective decisions beyond traditional deterministic logic."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@pages/developers/intelligent-contracts/features/interacting-with-intelligent-contracts.mdx`
around lines 104 - 105, Add a single sentence after the line "Here's a complete
example showing a token contract interacting with another contract:" that
explicitly calls out GenLayer as an "Intelligent Blockchain" and notes that
contracts can access the internet and make subjective decisions (e.g., off-chain
data, human-in-the-loop or ML-based inputs) to enable richer, non-deterministic
workflows; place the sentence in the same section heading "Interacting with
intelligent contracts" to tie the example to GenLayer’s unique internet-access
and subjective-decision capabilities.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@pages/developers/intelligent-contracts/features/interacting-with-intelligent-contracts.mdx`:
- Around line 146-160: The withdraw function is unsafe because it lets any
address with token balance >= min_balance withdraw from the shared stored_value;
change it to track per-user deposits and enforce sender-specific limits: add a
mapping (e.g., deposits or balances) keyed by address to record each depositor's
deposited amount when they deposit, then in withdraw (the withdraw method using
TokenInterface and token.view().balance_of and reading stored_value) check that
deposits[gl.message.sender_address] >= amount, decrement both
deposits[gl.message.sender_address] and stored_value by amount, and only then
call gl.transfer(gl.message.sender_address, amount); this ties withdrawals to
who deposited and prevents any qualifying token holder from draining pooled
funds.
- Around line 141-178: The snippets use gl.UserError but the project standard is
gl.vm.UserError; update all occurrences to gl.vm.UserError (e.g., the raise in
the deposit/withdraw methods and the exception handlers in safe_transfer) so the
examples match the documented error type—search for symbols like withdraw,
deposit, safe_transfer and replace gl.UserError with gl.vm.UserError everywhere
(including the other referenced snippets at lines noted) to ensure consistency.
- Around line 197-207: The "CORRECT" example incorrectly uses
other.view().transfer(...) for a write operation; replace it with the documented
asynchronous write pattern using emit(), e.g. call
other.emit(on='finalized').transfer(recipient, amount) and then check the result
(assign to success and raise gl.UserError("Transfer failed") if not success) so
the example aligns with the Write interface and the Error Handling section.

---

Nitpick comments:
In
`@pages/developers/intelligent-contracts/features/interacting-with-intelligent-contracts.mdx`:
- Around line 104-105: Add a single sentence after the line "Here's a complete
example showing a token contract interacting with another contract:" that
explicitly calls out GenLayer as an "Intelligent Blockchain" and notes that
contracts can access the internet and make subjective decisions (e.g., off-chain
data, human-in-the-loop or ML-based inputs) to enable richer, non-deterministic
workflows; place the sentence in the same section heading "Interacting with
intelligent contracts" to tie the example to GenLayer’s unique internet-access
and subjective-decision capabilities.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 92940788-9edd-4513-b4a6-ddb867801e8a

📥 Commits

Reviewing files that changed from the base of the PR and between a567b5d and 3e074b5.

📒 Files selected for processing (1)
  • pages/developers/intelligent-contracts/features/interacting-with-intelligent-contracts.mdx

Comment on lines +141 to +178
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}")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify UserError naming consistency across intelligent-contract docs
rg -n "gl\.UserError|gl\.vm\.UserError" pages/developers/intelligent-contracts/features

Repository: genlayerlabs/genlayer-docs

Length of output: 3511


🏁 Script executed:

#!/bin/bash
# Check error-handling.mdx to see how errors are documented
cat pages/developers/intelligent-contracts/features/error-handling.mdx

Repository: genlayerlabs/genlayer-docs

Length of output: 3401


🏁 Script executed:

#!/bin/bash
# Search for import statements and SDK references that clarify the API
rg -n "from genvm|import.*genvm|import.*gl" pages/developers/intelligent-contracts/features -B2 -A2 | head -100

Repository: genlayerlabs/genlayer-docs

Length of output: 52


Standardize on the documented gl.vm.UserError type across all snippets.

This page uses gl.UserError while the error-handling documentation and other reference pages use gl.vm.UserError, creating inconsistency that breaks reader confidence when cross-referencing examples.

Suggested doc fix
-raise gl.UserError("...")
+raise gl.vm.UserError("...")

-except gl.UserError as e:
+except gl.vm.UserError as e:

-with pytest.raises(gl.UserError, match="..."):
+with pytest.raises(gl.vm.UserError, match="..."):

Also applies to: lines 206, 246, 254, 271, 323

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@pages/developers/intelligent-contracts/features/interacting-with-intelligent-contracts.mdx`
around lines 141 - 178, The snippets use gl.UserError but the project standard
is gl.vm.UserError; update all occurrences to gl.vm.UserError (e.g., the raise
in the deposit/withdraw methods and the exception handlers in safe_transfer) so
the examples match the documented error type—search for symbols like withdraw,
deposit, safe_transfer and replace gl.UserError with gl.vm.UserError everywhere
(including the other referenced snippets at lines noted) to ensure consistency.

Comment on lines +146 to +160
@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)
```
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

withdraw allows any qualifying token holder to drain pooled funds.

In this “complete working example,” withdrawal is not tied to who deposited. Any address meeting min_balance can withdraw up to stored_value, which is a serious security pitfall for readers who reuse this pattern.

Suggested doc fix (track per-user balances)
 class TokenGatedVault(gl.Contract):
@@
-    stored_value: u256
+    stored_value: u256
+    balances: TreeMap[Address, u256]
@@
     def deposit(self):
@@
         self.stored_value += gl.message.value
+        sender = gl.message.sender_address
+        self.balances[sender] = self.balances.get(sender, u256(0)) + gl.message.value
@@
     def withdraw(self, amount: u256):
@@
+        sender = gl.message.sender_address
+        user_balance = self.balances.get(sender, u256(0))
+        if amount > user_balance:
+            raise gl.vm.UserError("Insufficient user balance")
         if amount > self.stored_value:
-            raise gl.UserError("Insufficient vault balance")
+            raise gl.vm.UserError("Insufficient vault balance")
         
         self.stored_value -= amount
-        gl.transfer(gl.message.sender_address, amount)
+        self.balances[sender] = user_balance - amount
+        gl.transfer(sender, amount)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@pages/developers/intelligent-contracts/features/interacting-with-intelligent-contracts.mdx`
around lines 146 - 160, The withdraw function is unsafe because it lets any
address with token balance >= min_balance withdraw from the shared stored_value;
change it to track per-user deposits and enforce sender-specific limits: add a
mapping (e.g., deposits or balances) keyed by address to record each depositor's
deposited amount when they deposit, then in withdraw (the withdraw method using
TokenInterface and token.view().balance_of and reading stored_value) check that
deposits[gl.message.sender_address] >= amount, decrement both
deposits[gl.message.sender_address] and stored_value by amount, and only then
call gl.transfer(gl.message.sender_address, amount); this ties withdrawals to
who deposited and prevents any qualifying token holder from draining pooled
funds.

Comment on lines +197 to +207
```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")
```
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

find . -name "interacting-with-intelligent-contracts.mdx" -type f

Repository: genlayerlabs/genlayer-docs

Length of output: 163


🏁 Script executed:

wc -l ./pages/developers/intelligent-contracts/features/interacting-with-intelligent-contracts.mdx

Repository: genlayerlabs/genlayer-docs

Length of output: 167


🏁 Script executed:

sed -n '180,220p' ./pages/developers/intelligent-contracts/features/interacting-with-intelligent-contracts.mdx

Repository: genlayerlabs/genlayer-docs

Length of output: 969


🏁 Script executed:

sed -n '1,180p' ./pages/developers/intelligent-contracts/features/interacting-with-intelligent-contracts.mdx | tail -100

Repository: genlayerlabs/genlayer-docs

Length of output: 3175


🏁 Script executed:

sed -n '1,196p' ./pages/developers/intelligent-contracts/features/interacting-with-intelligent-contracts.mdx | grep -A 20 -B 5 "view\|emit" | head -80

Repository: genlayerlabs/genlayer-docs

Length of output: 2994


The "CORRECT" example uses view() for a write operation, contradicting the page's documented guidance.

transfer() is a write method (defined in the Write interface class). The page explicitly documents that view() is for synchronous reads and emit() is for asynchronous writes. The earlier mistake example correctly enforces this pattern; the "transfer" example violates it. The Error Handling section on the same page demonstrates the correct approach: token.emit(on='finalized').transfer(to, amount).

Suggested fix
-# CORRECT - Check the return value
-success = other.view().transfer(recipient, amount)
-if not success:
-    raise gl.UserError("Transfer failed")
+# CORRECT - Use emit() for write operations
+other.emit(on='finalized').transfer(recipient, amount)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
```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")
```
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@pages/developers/intelligent-contracts/features/interacting-with-intelligent-contracts.mdx`
around lines 197 - 207, The "CORRECT" example incorrectly uses
other.view().transfer(...) for a write operation; replace it with the documented
asynchronous write pattern using emit(), e.g. call
other.emit(on='finalized').transfer(recipient, amount) and then check the result
(assign to success and raise gl.UserError("Transfer failed") if not success) so
the example aligns with the Write interface and the Error Handling section.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant