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',
})