Cirrus (Indexed Contract Data)¶
Cirrus is how you query contract data on STRATO with SQL-style filters. Every node runs:
- slipstream, which reads SolidVM execution output and writes contract state, mappings and events to the Postgres
cirrusdatabase; - PostgREST (v12), which serves that database read-only over HTTP at
/cirrus/search.
Use Cirrus to list contracts, read state across many instances, search mappings such as token balances, and read event and state history. For one contract's live state you can also use the Bloc API. For Ethereum-style log queries, see JSON-RPC.
Endpoint¶
GET https://app.strato.nexus/cirrus/search/<table>
GET https://app.testnet.strato.nexus/cirrus/search/<table>
- GET only. nginx rejects other methods.
- No token needed. Anonymous reads are allowed.
- Rows come back as a JSON array. An unknown table returns
404with PostgREST error code42P01./cirrus/search/with no table returns403. - Always pass
limit. The server caps a response at 1,000,000 rows, so an unfiltered query on a large table returns a very large response.
Table Naming¶
Table names include - and @. Use them unchanged in the URL path.
| Table | Name | Example |
|---|---|---|
| Contract state | <creator>-<Contract> |
BlockApps-Token |
| Mapping or array variable | <creator>-<Contract>-<variable> |
BlockApps-Token-_balances |
| Event | <creator>-<Contract>-<Event> |
BlockApps-Token-Transfer |
| Array parameter of an event | <creator>-<Contract>-<Event>-<parameter> |
|
| All contract storage (JSON) | storage |
|
| All mapping entries (JSON) | mapping |
|
| All events (JSON) | event |
|
| State history | history@storage |
|
| Mapping history | history@mapping |
<creator>is the name recorded with the contract's code when it was uploaded. Platform contracts deployed at genesis useBlockApps.- Parent contracts have tables too. Querying a parent contract's table returns every contract that inherits from it. For example,
BlockApps-ERC20returnsTokenrows, and thecontract_namecolumn tells you the concrete contract. - Proxied contracts appear in their logic contract's table. USDST at
937efa7e3a77e20bbdbd7c0d32b6514f368c1010is aProxyaccount, but its row is inBlockApps-Token. - Postgres limits identifiers to 63 characters, so very long table names are truncated.
Changes from older STRATO versions
Contract tables no longer have chainId, record_id, transaction_hash or transaction_sender columns. There are no per-contract history@<Contract> tables: history lives in history@storage and history@mapping. indexed@ event tables are no longer created.
Columns¶
Contract tables¶
| Column | Notes |
|---|---|
address |
Contract address (40 hex characters, no 0x) |
block_hash, block_number, block_timestamp |
Block of the last update. block_number is text. block_timestamp looks like 2026-09-13 19:39:10 UTC. |
creator |
Table-name prefix, such as BlockApps |
contract_name |
Concrete contract name |
| one column per state variable | Named as in the source, such as _name, _symbol, _totalSupply |
{
"address": "000000000000000000000000000000000000100f",
"block_hash": "ad2a825bb4ca6a2937e51d5ed2fe4d4559af5214783d9609bdcf158f4be84eb7",
"block_timestamp": "2026-09-13 19:39:10 UTC",
"block_number": "558986",
"creator": "BlockApps",
"contract_name": "Token",
"_name": "lendUSDST",
"_symbol": "lendUSDST",
"_totalSupply": 211633657347554922364834,
"customDecimals": 18
}
Mapping and array tables¶
Columns:
address,block_hash,block_number,block_timestampcollection_name,collection_type(for exampleMapping)key, pluskey2,key3, ... for nested mappingsvalue
If the value is a struct, its fields appear as columns in place of value. Entries whose value is null, 0, false or an empty string are left out.
Event tables¶
Columns:
idaddress,block_hash,block_number,block_timestamptransaction_hash,transaction_sender,event_indexcreator,contract_name- one column per event parameter
The global event table has event_name and attributes (a JSON object) in place of per-parameter columns.
History tables¶
history@storage has a row for each version of a contract's storage: address, block columns, data (JSON), valid_from and valid_to.
history@mapping has a row for each version of a mapping entry: address, collection_name, key (JSON, such as {"key": "<address>"}), path, value, valid_from and valid_to.
The current version has valid_to set to infinity.
Reserved column names¶
Cirrus uses these names for its own columns and does not rename clashing variables:
- All tables:
address,block_hash,block_number,block_timestamp,creator,contract_name - Event tables:
id,transaction_hash,transaction_sender,event_index - Collection tables:
collection_name,collection_type,key,key2, ...,value
Don't give state variables or event parameters these names if you want to query them in Cirrus.
Large numbers
uint values are returned as JSON numbers and are often larger than 2^53. Standard JSON.parse in JavaScript loses precision on them. Use a big-number-safe JSON parser.
Query Syntax¶
Cirrus uses PostgREST query parameters. The most common:
| Purpose | Syntax | Example |
|---|---|---|
| Choose columns | select= |
select=address,_name,_symbol |
| Filter | <column>=<op>.<value> |
_symbol=eq.USDST |
| Operators | eq, neq, gt, gte, lt, lte, like, ilike, in, is |
_symbol=in.(USDST,GOLDST) |
| Pattern match | * is the wildcard |
_symbol=ilike.*ust* |
| Negate | not. prefix |
status=not.eq.3 |
| OR / AND | or=(...), and=(...) |
or=(_symbol.eq.USDST,_symbol.eq.GOLDST) |
| JSON fields | -> and ->> |
key->>key=eq.<address> |
| Sort | order=<column>.asc or .desc |
order=block_timestamp.desc |
| Page | limit=, offset= |
limit=50&offset=100 |
| Total count | Prefer: count=exact header |
The total is in Content-Range (0-0/979) |
Tip
block_number is stored as text, so ordering or range filters on it compare strings. Order events by id, and contract rows by block_timestamp.
Examples¶
All of these were run against testnet. Change the host to query mainnet. USDST has the same address on both networks.
List tokens
curl -s "https://app.testnet.strato.nexus/cirrus/search/BlockApps-Token?select=address,_name,_symbol&limit=3"
[
{"address":"000000000000000000000000000000000000100f","_name":"lendUSDST","_symbol":"lendUSDST"},
{"address":"0000000000000000000000000000000000001016","_name":"safetyUSDST","_symbol":"safetyUSDST"},
{"address":"0000000000000000000000000000000000001018","_name":"ETH-USDST LP Token","_symbol":"ETH-USDST-LP"}
]
Find tokens by symbol
curl -s -g "https://app.testnet.strato.nexus/cirrus/search/BlockApps-Token?select=address,_symbol&or=(_symbol.eq.USDST,_symbol.eq.GOLDST)"
All non-zero token balances for one account
curl -s "https://app.testnet.strato.nexus/cirrus/search/BlockApps-Token-_balances?select=address,key,value&key=eq.0dbb9131d99c8317aa69a70909e124f2e02446e8&value=gt.0"
In each row, address is the token contract and key is the holder.
Largest USDST holders
curl -s "https://app.testnet.strato.nexus/cirrus/search/BlockApps-Token-_balances?address=eq.937efa7e3a77e20bbdbd7c0d32b6514f368c1010&select=key,value&order=value.desc&limit=2"
Latest token transfers
curl -s "https://app.testnet.strato.nexus/cirrus/search/BlockApps-Token-Transfer?select=id,block_number,address,from,to,value&order=id.desc&limit=5"
Balance history for one holder
curl -s -g "https://app.testnet.strato.nexus/cirrus/search/history@mapping?address=eq.937efa7e3a77e20bbdbd7c0d32b6514f368c1010&collection_name=eq._balances&key->>key=eq.1b7dc206ef2fe3aab27404b88c36470ccf16c0ce&select=value,valid_from,valid_to&order=valid_from.desc&limit=5"
Count rows
curl -s -o /dev/null -D - -H "Prefer: count=exact" \
"https://app.testnet.strato.nexus/cirrus/search/BlockApps-Token?select=address&limit=1" | grep -i content-range
curl and brackets
Pass -g to curl when a URL contains (, ), [ or ], so curl doesn't treat them as glob patterns.
Related Docs¶
- Core Platform API: blocks, transactions, Bloc contract state
- JSON-RPC:
eth_getLogsand other Ethereum-style reads - Transactions & Fees