# Normalized Event Schema v1 **Last Updated:** 2026-01-31 **Document Version:** 1.0 **Status:** Active Documentation --- **Purpose:** Canonical schema for EII (Event Ingestion + Indexing), SAL (State & Accounting Ledger), and Mirroring Service. All chains (138, 651940, public mainnets) use this schema for blocks, transactions, receipts, logs, decoded_events, and execution_steps. **Version:** 1.0 **Status:** Active --- ## 1. Design principles - Append-only event store; no in-place updates. - Every entity has `chain_id`, `created_at` (ingestion time). - Hashes and addresses are lowercase hex with `0x` prefix. - Optional fields may be null; required fields are non-null. --- ## 2. Blocks | Field | Type | Required | Description | |-------|------|----------|-------------| | chain_id | integer | yes | EVM chain ID (138, 651940, 1, etc.) | | number | bigint | yes | Block number | | hash | string(66) | yes | Block hash (0x + 64 hex) | | parent_hash | string(66) | yes | Parent block hash | | state_root | string(66) | no | State root | | receipts_root | string(66) | no | Receipts root (for commitment leaves) | | transactions_root | string(66) | no | Transactions root | | miner | string(42) | no | Miner/validator address | | difficulty | string | no | Block difficulty (numeric string) | | total_difficulty | string | no | Total difficulty | | size | bigint | no | Block size in bytes | | gas_limit | bigint | yes | Gas limit | | gas_used | bigint | yes | Gas used | | timestamp | bigint | yes | Unix timestamp (seconds) | | base_fee_per_gas | bigint | no | EIP-1559 base fee | | transaction_count | integer | no | Number of transactions | | extra_data | string | no | Extra data (hex) | | created_at | string (ISO8601) | yes | Ingestion timestamp | **Unique:** `(chain_id, number)` **Indexes:** `(chain_id, number)`, `(chain_id, hash)`, `(chain_id, timestamp)` --- ## 3. Transactions | Field | Type | Required | Description | |-------|------|----------|-------------| | chain_id | integer | yes | EVM chain ID | | hash | string(66) | yes | Transaction hash | | block_number | bigint | yes | Block number | | block_hash | string(66) | yes | Block hash | | transaction_index | integer | yes | Index within block | | from_address | string(42) | yes | Sender | | to_address | string(42) | no | Recipient (null for contract creation) | | value | string | yes | Value in wei (decimal string) | | gas_price | bigint | no | Legacy gas price | | max_fee_per_gas | bigint | no | EIP-1559 max fee | | max_priority_fee_per_gas | bigint | no | EIP-1559 priority fee | | gas_limit | bigint | yes | Gas limit | | gas_used | bigint | no | Gas used (from receipt) | | nonce | bigint | yes | Sender nonce | | input_data | string | no | Calldata (hex) | | status | integer | no | 0 = failed, 1 = success | | contract_address | string(42) | no | Created contract (if creation) | | cumulative_gas_used | bigint | no | From receipt | | effective_gas_price | bigint | no | Actual gas price paid | | created_at | string (ISO8601) | yes | Ingestion timestamp | **Unique:** `(chain_id, hash)` **Indexes:** `(chain_id, hash)`, `(chain_id, block_number, transaction_index)`, `(chain_id, from_address)`, `(chain_id, to_address)` --- ## 4. Receipts | Field | Type | Required | Description | |-------|------|----------|-------------| | chain_id | integer | yes | EVM chain ID | | transaction_hash | string(66) | yes | Transaction hash | | transaction_index | integer | yes | Index in block | | block_number | bigint | yes | Block number | | block_hash | string(66) | yes | Block hash | | from_address | string(42) | yes | Sender | | to_address | string(42) | no | Recipient | | gas_used | bigint | no | Gas used | | cumulative_gas_used | bigint | no | Cumulative gas | | contract_address | string(42) | no | Created contract | | logs_bloom | string | no | Logs bloom (hex) | | status | integer | no | 0 = failed, 1 = success | | root | string(66) | no | Pre-Byzantium state root | | created_at | string (ISO8601) | yes | Ingestion timestamp | **Unique:** `(chain_id, transaction_hash)` **Indexes:** `(chain_id, transaction_hash)`, `(chain_id, block_number)` --- ## 5. Logs | Field | Type | Required | Description | |-------|------|----------|-------------| | chain_id | integer | yes | EVM chain ID | | transaction_hash | string(66) | yes | Transaction hash | | block_number | bigint | yes | Block number | | block_hash | string(66) | yes | Block hash | | log_index | integer | yes | Index within transaction | | address | string(42) | yes | Contract address | | topic0 | string(66) | no | Event signature hash | | topic1 | string(66) | no | First indexed parameter | | topic2 | string(66) | no | Second indexed parameter | | topic3 | string(66) | no | Third indexed parameter | | data | string | no | Non-indexed data (hex) | | created_at | string (ISO8601) | yes | Ingestion timestamp | **Unique:** `(chain_id, transaction_hash, log_index)` **Indexes:** `(chain_id, transaction_hash)`, `(chain_id, address)`, `(chain_id, topic0)`, `(chain_id, block_number)`, `(chain_id, address, topic0)` --- ## 6. Decoded events Decoded view of logs when ABI is available. Used for indexing and for commitment leaf payload hash. | Field | Type | Required | Description | |-------|------|----------|-------------| | chain_id | integer | yes | EVM chain ID | | transaction_hash | string(66) | yes | Transaction hash | | block_number | bigint | yes | Block number | | log_index | integer | yes | Log index | | address | string(42) | yes | Contract address | | event_signature | string | yes | e.g. Transfer(address,address,uint256) | | event_name | string | yes | e.g. Transfer | | decoded_params | object | yes | Key-value of decoded parameters | | payload_hash | string(66) | no | keccak256(canonical_json(decoded_params)) for commitment leaf | | created_at | string (ISO8601) | yes | Ingestion timestamp | **Unique:** `(chain_id, transaction_hash, log_index)` **Indexes:** `(chain_id, address)`, `(chain_id, event_name)`, `(chain_id, block_number)` **Canonical encoding for payload_hash:** Sort keys of `decoded_params` alphabetically; encode as JSON without whitespace; hash with keccak256. --- ## 7. Execution steps Links intents/executions to on-chain transactions. Used by EO and for SAL reconciliation. | Field | Type | Required | Description | |-------|------|----------|-------------| | execution_id | string(UUID) | yes | Execution run ID | | intent_id | string(UUID) | yes | Intent ID | | step_index | integer | yes | Order of step (0-based) | | step_type | string | yes | transfer, swap, bridge, message_send, message_receive, mint, burn | | chain_id | integer | yes | Chain where tx was submitted | | transaction_hash | string(66) | no | Tx hash once submitted | | status | string | yes | pending, submitted, confirmed, finalized, failed | | preconditions | string | no | JSON array of precondition IDs | | postconditions | string | no | JSON array of postcondition IDs | | gas_used | bigint | no | Filled after confirmation | | created_at | string (ISO8601) | yes | Created timestamp | | updated_at | string (ISO8601) | yes | Last update | **Unique:** `(execution_id, step_index)` **Indexes:** `(execution_id)`, `(intent_id)`, `(chain_id, transaction_hash)`, `(status)` --- ## 8. SAL journal entry hash (optional for commitment leaf) For Merkle commitment leaves, a leaf may include the hash of the SAL journal entry that corresponds to this transaction (if any). Schema for the hash input: - **Input to hash:** `ledger_id || entry_id || debit_account_id || credit_account_id || amount || currency_code || reference_id || timestamp_utc` - **Encoding:** Concatenate as UTF-8 strings with a single delimiter (e.g. `|`); then keccak256. - **Field in commitment leaf:** `sal_journal_entry_hash` (bytes32), optional; null if no ledger entry. --- ## 9. Commitment leaf (Merkle tree) Each leaf in the mirroring Merkle tree is built from: | Field | Source | Description | |-------|--------|-------------| | tx_hash | transactions.hash | Transaction hash | | block_number | blocks.number | Block number | | receipt_root or logs_bloom | receipts / blocks | Receipt root or logs bloom (chain-dependent) | | normalized_event_payload_hash | decoded_events.payload_hash | Hash of decoded event payload (or logs hash if no decode) | | sal_journal_entry_hash | ledger_entries | Optional; from SAL if applicable | **Leaf encoding (canonical):** `keccak256(abi.encodePacked(chain_id, tx_hash, block_number, receipt_root_or_logs_bloom, normalized_event_payload_hash, sal_journal_entry_hash))` Schema version and chain_id are also stored at commit level (startBlock, endBlock, root, chain_id, schema_version). --- ## 10. Changelog | Version | Date | Change | |---------|------|--------| | 1.0 | 2026-01-28 | Initial normalized event schema v1 for EII, SAL, and Mirroring. |