> ## Documentation Index
> Fetch the complete documentation index at: https://docs.blockdb.chaintable.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Block tables

> A deep dive into BlockDB's two core logical table models: model definitions, logical types, start and consensus height, and core features.

# Block tables

BlockDB abstracts on-chain data into two core logical models: the **Block State Table** and the **Block Event Table**. Both use the block as their unit of data and understand on-chain semantics, so you do not manage block heights, data overwrites, or reorg rollbacks yourself.

## 1. Model definitions

### Block State Table: single-row state model

**Definition**: stores the latest state of on-chain entities plus their state at every historical height. Whenever data for a new height arrives, BlockDB stores a snapshot at that height and asynchronously updates the latest-state table.

* **Design philosophy**: keyed by a unique entity (an address, a contract). Every write overwrites the previous state, while the storage layer retains historical snapshots.
* **Typical scenarios**: token balances of user addresses, current contract state variables, current NFT owner.
* **System-managed columns**: `block_id`, `block_height`, and `block_timestamp` are maintained automatically to support historical version lookups.

### Block Event Table: fact record model

**Definition**: stores every event that happened on chain.

* **Design philosophy**: data is read-only and append-only. Each row represents a fact that definitively happened on chain and cannot be modified.
* **Typical scenarios**: token transfer logs (token\_transfer), contract events, individual matched trades on an exchange.
* **System-managed columns**: `block_id`, `block_height`, and `block_timestamp` are maintained automatically.

**Difference from the State table**: an Event table **does not maintain an `_archive` table**. Once an on-chain event happens at a given height it is unique and immutable — there is no notion of "update", only "append".

<Note>
  The derived tables underneath a business abstraction table (`_archive`, `_height`, and so on) can be read directly with `GetRow` / `BatchGetRows` / `FilterRows` just like regular tables, which helps with troubleshooting. However, **modifying derived tables with regular-table write APIs such as `UpsertRows` is forbidden** — all writes must go through the Block table's own block write interface.
</Note>

## 2. Logical types

When you create a table, every business column besides the `id` primary key can be declared with any logical type. For the complete list, see the [reference](/reference/value-types). This section covers the types that are closely tied to blockchain data and follow EVM length conventions:

| Logical type | Description | Length constraint |
| - | - | - |
| `ADDRESS` | EVM address | `0x` prefix + 20 bytes (40 hex chars) |
| `TXID` | Transaction hash | `0x` prefix + 32 bytes (64 hex chars) |
| `BLOCKID` | Block hash | `0x` prefix + 32 bytes (64 hex chars) |
| `HASH` | Generic hash | No prefix, 16 bytes (32 hex chars) |
| `TOKENID` | Token / NFT token id | `0x` prefix + 20 bytes |
| `BLOCKHEIGHT` | Block height | Integer |
| `CHAINID` | Chain ID | Identifies which chain the data belongs to |
| `HEXSTRING` | Variable-length hex string | `0x` prefix, up to about 16 MB |

<Note>
  Fixed-length hexadecimal types such as `ADDRESS`, `TXID`, `BLOCKID`, and `TOKENID` are represented as `0x`-prefixed hexadecimal strings at the BlockDB API boundary and stored as fixed-length bytes underneath. Their lengths follow the EVM conventions and BlockDB's own definitions; treat the [reference](/reference/value-types) and the implementation as authoritative.
</Note>

**Schema recommendations**:

* **ID (primary key)**: must be named `id`.
* **Business columns**: add as many as you need, with any logical type.
* **CHAINID**: identifies which chain the data belongs to.

## 3. Start height and consensus height

### Start height

The start height is the **first block height** of on-chain data that the table manages, and you set it yourself.

* Your table often does not need to maintain data from height 0. For example, if a contract only appeared at a certain height, set that height as the start height and only maintain data from there on.
* **If you do not set it, the start height defaults to `0`.**

### Consensus height

The consensus height is the **right boundary of the contiguous height range covered from the start height onward**.

* As long as data is written contiguously from the start height with no gaps, the consensus height keeps advancing.
* It is the **highest height that `GetState`, `GetEvent`, and similar interfaces can safely query** — querying within it guarantees you will not get an incorrect result caused by missing data.

<Note>
  The `current_block.height` you pass when reading must be ≤ the table's consensus height (the highest height contiguously covered in the `_height` table from the start height) for the result to be correct.
</Note>

## 4. Core features

### ACID guarantees for block-granular writes

BlockDB writes use the **block as the smallest atomic unit**:

* **Atomicity (A)**: all data in one block is either fully written or fully rejected. There is never "half a block" of dirty data.
* **Consistency (C)**: a write **always replaces the data at that height completely, at block granularity** — the data at that height is one complete, deterministic snapshot, and readers never see an intermediate or partially written state.
* **Isolation / durability**: concurrent writes are coordinated at block granularity, and committed data is durably readable.

Downstream consumers that use [block subscription](/concepts/write-subscribe) always receive a complete block, so they naturally avoid reading partially written data.

### Time-travel queries

The read APIs natively support `AS OF BLOCK <height>` semantics. Even though a State table stores the latest balance, you can query its value at any historical block height in a single call.

### Real-time event subscription

Every successful block-granular write makes BlockDB publish a block write event, so downstream tasks can process the data produced at each block height incrementally and in real time, building multi-stage cascaded event-driven pipelines.

### Transparent automatic reorg handling

When the underlying chain reorgs and N blocks must be rolled back, BlockDB uses the implicitly maintained block columns to automatically undo writes for the forked blocks, delete the corresponding events from Block Event tables, and keep Block State tables eventually consistent. **Reorgs require no maintenance on your side.**

## Summary

| Dimension | Block State Table | Block Event Table |
| - | - | - |
| Data shape | State snapshot (latest result) | Log stream (historical facts) |
| Query focus | "What are this address's assets right now?" | "Which transfers happened in the last 24 hours?" |
| Physical mapping | main + `_archive` + `_height` + `_bundle` | main + `_height` + `_bundle` |

For on-chain data production, prefer Block tables to manage on-chain data.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.