Reading from Intelligent Contracts
Reading from Intelligent Contracts means querying contract state or executing view functions without modifying blockchain state. These read-only operations are free, require no fees, and provide immediate access to contract data.
Understanding View Operations
In GenLayer, functions marked with the @gl.public.view decorator are read-only operations that:
- Don't modify the contract's state
- Can be executed without requiring a transaction
- Return data immediately
- Don't consume gas
- Can be called by any account
Basic Contract Reading
Here's how to read from an Intelligent Contract:
import { simulator } from 'genlayer-js/chains';
import { createClient, createAccount } from 'genlayer-js';
const account = createAccount();
const client = createClient({
chain: simulator,
account: account,
});
const result = await client.readContract({
address: contractAddress,
functionName: 'get_complete_storage',
args: [],
});Parameters Explained
address: The deployed contract's address on the GenLayer networkfunctionName: The name of the view function you want to callargs: An array of arguments that the function accepts (empty if none required)
Common View Operations
Intelligent Contracts typically include several types of view functions:
State Queries
// Reading a single value
const balance = await client.readContract({
address: contractAddress,
functionName: 'get_balance',
args: [accountAddress],
});
// Reading multiple values
const userInfo = await client.readContract({
address: contractAddress,
functionName: 'get_user_info',
args: [userId],
});Computed Values
// Getting calculated results
const totalSupply = await client.readContract({
address: contractAddress,
functionName: 'calculate_total_supply',
args: [],
});Validation Checks
// Checking permissions
const hasAccess = await client.readContract({
address: contractAddress,
functionName: 'check_user_permission',
args: [userId, 'ADMIN_ROLE'],
});Error Handling
When reading from contracts, you should handle potential errors:
try {
const result = await client.readContract({
address: contractAddress,
functionName: 'get_data',
args: [],
});
console.log('Data retrieved:', result);
} catch (error) {
if (error.message.includes('Contract not found')) {
console.error('Invalid contract address');
} else if (error.message.includes('Function not found')) {
console.error('Invalid function name');
} else {
console.error('Error reading contract:', error);
}
}Best Practices
- Batch Readings: When possible, use functions that return multiple values instead of making many separate calls.
- Type Safety: Use TypeScript interfaces to ensure type safety when handling returned data.
- Cache Shared Reads: Treat public RPC endpoints as shared infrastructure. Browser traffic can multiply
gen_callrequests quickly, so production DApps should avoid polling the chain independently from every tab or visitor.
Building Read-Heavy DApps
readContract calls are convenient for account pages, dashboards, and dispute views, but they still consume RPC capacity. If a frontend calls gen_call repeatedly from every browser session, the app can hit rate limits even though the operation is read-only and gasless.
For read-heavy DApps, use a layered pattern:
- On-chain source of truth: keep the Intelligent Contract as the authoritative state machine.
- Backend cache or sidecar: poll the contract at a controlled interval and serve many frontend users from the cached response.
- Batch view functions: expose view methods that return the full page state needed by the UI, instead of requiring many small reads.
- HTTP cache semantics: use
ETag,Cache-Control, or stale-while-revalidate behavior so clients do not refetch unchanged data. - Verify-on-chain fallback: keep a UI path that can re-read the contract directly for users who need to verify the cached value.
A minimal backend route can centralize reads like this:
let cachedPageState: unknown;
let cachedAt = 0;
const CACHE_TTL_MS = 12_000;
export async function getPageState() {
const now = Date.now();
if (cachedPageState && now - cachedAt < CACHE_TTL_MS) {
return cachedPageState;
}
cachedPageState = await client.readContract({
address: contractAddress,
functionName: 'get_page_state',
args: [],
});
cachedAt = now;
return cachedPageState;
}This does not replace on-chain verification. It reduces duplicate read load for normal browsing while preserving GenLayer as the source of truth.