Skip to main content

Reading Chain State

Aleo programs keep two kinds of state: public mappings, which anyone can read, and private records, which only their owner can decrypt. This guide covers the public half. For records, see Working with Records.

Every read in this guide goes through a public client — no account, wallet, or private key is involved. A public client wraps a transport pointed at an Aleo node and exposes read-only actions over it.

import { createPublicClient, http } from '@provablehq/veil-core'

const publicClient = createPublicClient({
transport: http('https://api.provable.com/v2', { network: 'mainnet' }),
})

Mappings

A mapping is a program's on-chain key-value store, maintained by validators and updated each block a transaction writes to it. credits.aleo's account mapping, for example, holds every address's public credits balance — reading it is analogous to reading a contract's storage slot on Ethereum. Fetch a value with readContract (or its Aleo-native alias, readMapping):

import { parseValue } from '@provablehq/veil-core'

const raw = await publicClient.readContract({
programId: 'credits.aleo',
mapping: 'account',
key: 'aleo1q6qstg8q8shwqf5m6q5fcenuwsdqsvp4hhsgfnx5chzjm3secyzqt9mxm8',
})
// '5000000u64'

const balance = parseValue(raw)

The value comes back as the raw Aleo literal the node stores — a number, boolean, or struct literal with its type suffix still attached (u64, in the example above). parseValue decodes suffixed integers of every width (including field, scalar, and group), booleans, and aleo1... addresses, and throws on anything else — a struct literal needs manual parsing, or ABI-based decoding through a contract instance. Skip parseValue when the raw string is all that is needed.

A key that has never been written does not resolve to an empty value: the node answers 404 and the call rejects with a transport error (HTTP 404: ...). Wrap the read in try/catch when the key may not exist — a mapping only holds entries a transaction has actually inserted.

For a typed read bound to a program's ABI instead of a raw mapping name and key, see Contract instances.

Discovering a program's mappings

Before reading a mapping, getMappingNames lists what a program exposes:

const mappings = await publicClient.getMappingNames({ programId: 'credits.aleo' })
// ['account', 'committee', 'bonded', 'unbonding', ...]

Blocks and transactions

getBlockNumber returns the current chain height, widened to bigint to match viem's return type even though the underlying value is a u32 on chain. Convert it with Number() before passing a height into an action such as getBlock:

const height = await publicClient.getBlockNumber()
const block = await publicClient.getBlock({ height: Number(height) })

getBlock returns the full block — header, ratifications, solutions, and confirmed transactions. Look up a specific transaction by its at1... id with getTransaction:

const tx = await publicClient.getTransaction({ id: 'at1...' })

See Types for the full Block and Transaction field reference, and Transaction Lifecycle for tracking a transaction from submission through acceptance.

Programs

getCode fetches a deployed program's Aleo instructions source — Veil's analogue of viem's getCode, which returns bytecode. Inspect the source to see a program's functions, mappings, and record types before calling it:

const source = await publicClient.getCode({ programId: 'credits.aleo' })

getDeploymentTransaction finds the transaction that deployed a program, including the deployer's address and signature:

const deployTx = await publicClient.getDeploymentTransaction({
programId: 'credits.aleo',
})