XChain Platform Decoder — Database Schema
The decoder writes to a single MariaDB database. The indexer reads from this database (read-only from the indexer’s perspective).
Database Naming
XChain_{CHAIN}_{NETWORK}_Decoder
Examples: XChain_BTC_Mainnet_Decoder, XChain_LTC_Regtest_Decoder, XChain_DOGE_Testnet_Decoder
The database and all tables are auto-created on startup if they don’t exist. Schema definitions live in src/sql/*.sql.
Table Overview
| Table | Purpose | Primary Key |
|---|---|---|
blocks |
One row per parsed block | block_index |
transactions |
Confirmed XChain transactions | tx_index |
mempool_transactions |
Unconfirmed XChain transactions | tx_hash_id (unique) |
dispensers |
Active (unexpired) dispensers | (tx_index, address_id) |
transaction_outputs |
Outputs that paid to a dispenser address | (tx_index, vout) |
index_addresses |
Address string → integer ID lookup | id (auto-increment) |
index_transactions |
Transaction/block hash → integer ID lookup | id (auto-increment) |
events |
System events (reorgs, errors) | id (auto-increment) |
Core Tables
blocks
Stores one row per block that the decoder has processed.
| Column | Type | Description |
|---|---|---|
block_index |
INTEGER UNSIGNED |
Block height (primary key) |
block_time |
INTEGER UNSIGNED |
Block timestamp (unix epoch) |
block_hash_id |
INTEGER UNSIGNED |
FK → index_transactions.id |
previous_block_hash_id |
INTEGER UNSIGNED |
FK → index_transactions.id |
Indexes: block_hash_id, previous_block_hash_id
transactions
Stores confirmed XChain transactions (transactions whose deobfuscated payload starts with XCHN).
| Column | Type | Description |
|---|---|---|
tx_index |
INTEGER UNSIGNED |
Transaction index (primary key, auto-assigned) |
tx_hash_id |
INTEGER UNSIGNED |
FK → index_transactions.id (unique) |
block_index |
INTEGER UNSIGNED |
Block height containing this transaction |
source_id |
INTEGER UNSIGNED |
FK → index_addresses.id (sender) |
destination_id |
INTEGER UNSIGNED |
FK → index_addresses.id (receiver) |
amount |
BIGINT |
BTC/LTC/DOGE amount in satoshis |
fee |
BIGINT |
Transaction fee in satoshis |
data |
MEDIUMTEXT |
Decoded ACTION string (e.g., SEND|0|TOKEN|1000) |
Indexes: tx_hash_id (unique), block_index, source_id, destination_id
mempool_transactions
Same structure as transactions but without tx_index or block_index. Rows are created when a transaction enters the mempool and deleted when it confirms (promoted to transactions) or drops out.
| Column | Type | Description |
|---|---|---|
tx_hash_id |
INTEGER UNSIGNED |
FK → index_transactions.id (unique) |
source_id |
INTEGER UNSIGNED |
FK → index_addresses.id |
destination_id |
INTEGER UNSIGNED |
FK → index_addresses.id |
amount |
BIGINT |
Amount in satoshis |
fee |
BIGINT |
Fee in satoshis |
data |
MEDIUMTEXT |
Decoded ACTION string |
Indexes: tx_hash_id (unique), source_id, destination_id
DISPENSER Tables
dispensers
Tracks active (unexpired) dispensers. A dispenser is created when the decoder encounters a DISPENSER|0|... action with valid fields. Expired dispensers are deleted at the start of each new block.
| Column | Type | Description |
|---|---|---|
tx_index |
INTEGER UNSIGNED |
Transaction that created the dispenser |
address_id |
INTEGER UNSIGNED |
FK → index_addresses.id (dispenser owner) |
expiration |
DATETIME |
Expiration time (from FROM_UNIXTIME()) |
Primary Key: (tx_index, address_id)
transaction_outputs
Records outputs that paid to an active dispenser’s address. These are detected during transaction parsing when isThereADispenserForAddress() returns true.
| Column | Type | Description |
|---|---|---|
tx_index |
INTEGER UNSIGNED |
Transaction containing the output |
vout |
INTEGER UNSIGNED |
Output index within the transaction |
destination_id |
INTEGER UNSIGNED |
FK → index_addresses.id (dispenser address) |
amount |
VARCHAR(250) |
Coin amount in decimal (e.g., 0.01000000) |
Primary Key: (tx_index, vout)
Index Tables
Index tables normalize frequently-repeated strings (addresses, transaction hashes) into integer IDs for storage efficiency and join performance. Both tables reserve id=1 for a blank/null sentinel value.
index_addresses
| Column | Type | Description |
|---|---|---|
id |
INTEGER UNSIGNED AUTO_INCREMENT |
Address ID (primary key) |
address |
VARCHAR(120) |
Address string |
Indexes: address (partial index on first 10 characters)
index_transactions
| Column | Type | Description |
|---|---|---|
id |
INTEGER UNSIGNED AUTO_INCREMENT |
Hash ID (primary key) |
hash |
VARCHAR(250) |
Transaction or block hash |
Indexes: hash (partial index on first 20 characters)
Events Table
events
Records system events such as chain reorganizations.
| Column | Type | Description |
|---|---|---|
id |
BIGINT UNSIGNED AUTO_INCREMENT |
Event ID (primary key) |
time |
DATETIME |
Event timestamp |
code |
VARCHAR(32) |
Event type code (e.g., REORG) |
data |
TEXT |
Event payload (JSON) |
Data Flow
Coin Node → BlockchainConnector → XChainDecoder.parseTransaction()
│
┌──────┴──────┐
│ │
blocks table transactions table
│ │
index_transactions index_addresses
│ │
dispensers transaction_outputs
│
events (reorgs)
Satoshi Conversion
The bigIntSatoshiToDecimalsString() method converts satoshi values (integers or BigInts) to decimal strings with 8 decimal places:
| Input | Output |
|---|---|
100000000 |
"1.00000000" |
50000000 |
"0.50000000" |
1 |
"0.00000001" |
0 |
"0.00000000" |
-100000000 |
"-1.00000000" |
This is used for the amount column in transaction_outputs.
Copyright © 2025–2026 Dankest, LLC
Based on XChain Platform by Dankest, LLC – https://dankest.llc
Licensed under the GNU Affero General Public License v3.0 (AGPL-3.0-or-later) with a commercial license available for proprietary use.
You may use, modify, and distribute this material under the terms of the License. See LICENSE and NOTICE for full terms. See the licensing overview.