Skip to content

Sources ​

from names the contracts to read, whether you want their state or their events, at which blocks, with which options, and under what name.

from <contracts> [events <name>] [at <range>] [with { <options> }] [as <alias>]

Only from and the contracts are required; every other part has a default.

The contracts ​

One address, a let name, or a list in brackets. A list gives one row per contract per block, and every contract in it must offer the functions and events the query uses.

cql
let stETH  = ethereum:0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84;
let wstETH = ethereum:0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0;

from [stETH, wstETH] with { abi: erc20 } as t
| project { address: $address, symbol: t.symbol(), supply: format(t.totalSupply(), 18) }

Open in workbench →

addresssymbolsupply
ethereum:0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84stETH9761717.887788954079605074
ethereum:0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0wstETH3675170.451161002783092282

The chains in a list need not agree. Each contract is read on its own chain, and the rows arrive chain by chain, then block by block; Compare a contract across chains reads one list on Ethereum and Base.

State or events ​

Without events, the source reads state: one row per contract per block, and the alias gives you the contract's functions to call. With events, the source reads logs: one row per event emitted in the range, with the event's arguments as columns. Name the event, or write * for all of them and tell them apart by $event.

cql
let stETH = ethereum:0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84;

from stETH events * at latest-7200..latest as e
| project { block: $block, name: $event }

Open in workbench →

An event may also be given as a signature, when the ABI does not declare it: events 'TransferShares(address indexed from, address indexed to, uint256 sharesValue)'.

The range ​

A state source with no at reads the newest block. An event source with no at reads a recent window — 10 000 blocks on Ethereum, more on faster chains — and reports it as CQL3901.

FormReads
at latestthe newest block
at finalizedthe newest block the chain will not reorganise
at 26_000_000one block, by number
at latest-7200..latesta range ending at the head
at 25_800_000..26_000_000 every 7200the range, at every multiple of 7 200 plus the end
at time '2026-09-20'..'2026-09-23' every 1da range by timestamp, at day boundaries plus the end

Write the lower bound first; a range that ends below its start is refused (CQL3017). A block beyond the head is refused too (CQL3001); only a time range's end is clamped to it. A time range is resolved on each chain a source reads, so a list across chains is sampled at each chain's own blocks, for the same instants. Blocks and time explains every and what a long range without one does.

The options ​

with { abi: …, caller: …, block: …, value: … } applies to every call made through the alias. with options covers each one; the one you will use most is abi.

The alias ​

as s is the name the pipeline uses for the source: s.getTotalShares() calls a function, s.$address reads a system column of that source, e.reportTimestamp reads an event argument. Without an alias a source can still be read through its system columns, but a call needs a name to hang on.

See also ​