blockchain¶
Pack: 100xOS shared skills
Category:
data-handlingField: economics
License:
private (curator-owned)Updated: 2026-05-20
Stages:
data-acquisitionCurator-private skill — copy text from 100xOS/shared/skills/data/blockchain.md.
Blockchain Data: Flipside and Allium SQL Patterns¶
Purpose¶
This skill covers SQL patterns for querying blockchain data through Flipside Crypto and Allium analytics platforms. Both provide structured, indexed blockchain data accessible via SQL, but with different schemas, naming conventions, and capabilities.
Common Table Structures¶
Blockchain data is organized around a set of core tables that appear across most EVM-compatible chains and many non-EVM chains.
Blocks¶
| Column | Description |
|---|---|
| block_number | Sequential integer block identifier |
| block_timestamp | UTC timestamp of block production |
| block_hash | Unique hash of the block |
| miner / validator | Address that produced the block |
| gas_used | Total gas consumed in the block |
| gas_limit | Maximum gas allowed in the block |
| base_fee_per_gas | EIP-1559 base fee (post-London) |
| transaction_count | Number of transactions in the block |
Transactions¶
| Column | Description |
|---|---|
| tx_hash | Unique transaction hash |
| block_number | Block containing the transaction |
| block_timestamp | Timestamp of the containing block |
| from_address | Sender address |
| to_address | Recipient address (null for contract creation) |
| value | Native token value transferred (in wei/raw units) |
| gas_price | Gas price paid |
| gas_used | Gas consumed by the transaction |
| input_data | Encoded function call data |
| status | Success (1) or failure (0) |
| nonce | Sender's transaction sequence number |
Events / Logs¶
| Column | Description |
|---|---|
| tx_hash | Transaction that emitted the event |
| log_index | Position of the log within the transaction |
| contract_address | Address of the emitting contract |
| event_name | Decoded event name (if ABI is known) |
| topic0 | Event signature hash |
| topic1, topic2, topic3 | Indexed event parameters |
| data | Non-indexed event parameters (encoded) |
| decoded_log | Parsed event data (platform-specific format) |
Token Balances / Transfers¶
| Column | Description |
|---|---|
| from_address | Sender of the token transfer |
| to_address | Recipient of the token transfer |
| contract_address | Token contract address |
| amount / raw_amount | Transfer amount (raw or adjusted) |
| token_address | Same as contract_address in some schemas |
| symbol | Token symbol (e.g., USDC, WETH) |
| decimals | Token decimal places for conversion |
SQL Patterns for DeFi Analysis¶
Total Value Locked (TVL)¶
Track deposited assets in a protocol over time:
SQL
-- Daily TVL for a lending protocol (e.g., Aave-style)
WITH deposits AS (
SELECT
block_timestamp::date AS dt,
SUM(CASE
WHEN event_name = 'Deposit' THEN amount_usd
WHEN event_name = 'Withdraw' THEN -amount_usd
ELSE 0
END) AS net_flow
FROM protocol_events
WHERE contract_address IN (/* pool addresses */)
GROUP BY 1
)
SELECT
dt,
SUM(net_flow) OVER (ORDER BY dt) AS cumulative_tvl
FROM deposits
ORDER BY dt;
Trading Volume¶
Aggregate DEX swap volume:
SQL
-- Daily swap volume on a Uniswap-style AMM
SELECT
block_timestamp::date AS dt,
pool_address,
SUM(ABS(amount0_usd)) AS volume_usd,
COUNT(*) AS swap_count
FROM dex_swaps
WHERE platform = 'uniswap-v3'
AND block_timestamp >= CURRENT_DATE - INTERVAL '30 days'
GROUP BY 1, 2
ORDER BY 1, 4 DESC;
Yield / APY Calculation¶
Calculate realized yields from on-chain data:
SQL
-- Weekly yield for a staking protocol
WITH weekly_rewards AS (
SELECT
DATE_TRUNC('week', block_timestamp) AS week,
SUM(reward_amount * token_price) AS rewards_usd,
AVG(staked_amount * token_price) AS avg_tvl_usd
FROM staking_events
WHERE block_timestamp >= CURRENT_DATE - INTERVAL '90 days'
GROUP BY 1
)
SELECT
week,
rewards_usd,
avg_tvl_usd,
(rewards_usd / NULLIF(avg_tvl_usd, 0)) * 52 AS annualized_apy
FROM weekly_rewards
ORDER BY week;
SQL Patterns for On-Chain Governance¶
Proposal Tracking¶
SQL
-- Governance proposals and their outcomes
SELECT
proposal_id,
proposer,
description,
start_block,
end_block,
for_votes / POWER(10, 18) AS for_votes,
against_votes / POWER(10, 18) AS against_votes,
CASE
WHEN for_votes > against_votes AND quorum_reached THEN 'Passed'
WHEN for_votes <= against_votes THEN 'Defeated'
ELSE 'No Quorum'
END AS outcome
FROM governance_proposals
ORDER BY start_block DESC;
Voter Participation¶
SQL
-- Voter participation rates and delegation patterns
WITH votes AS (
SELECT
proposal_id,
voter,
voting_power / POWER(10, 18) AS voting_power,
support -- 0 = against, 1 = for, 2 = abstain
FROM governance_votes
),
totals AS (
SELECT
proposal_id,
COUNT(DISTINCT voter) AS unique_voters,
SUM(voting_power) AS total_power_voted,
SUM(CASE WHEN support = 1 THEN voting_power ELSE 0 END) AS for_power,
SUM(CASE WHEN support = 0 THEN voting_power ELSE 0 END) AS against_power
FROM votes
GROUP BY 1
)
SELECT
t.proposal_id,
t.unique_voters,
t.total_power_voted,
t.total_power_voted / NULLIF(s.total_supply, 0) AS participation_rate,
t.for_power / NULLIF(t.total_power_voted, 0) AS approval_rate
FROM totals t
LEFT JOIN token_supply s ON s.token_address = '0x...'
ORDER BY t.proposal_id DESC;
Flipside-Specific Schemas and Conventions¶
Flipside organizes data by chain under a <chain>.core or <chain>.defi schema.
Schema structure¶
Text Only
ethereum.core.fact_transactions
ethereum.core.fact_event_logs
ethereum.core.dim_labels -- address labels
ethereum.core.ez_token_transfers -- simplified token transfer view
ethereum.defi.ez_dex_swaps -- decoded DEX swaps
ethereum.defi.ez_lending_borrows -- decoded lending events
ethereum.price.ez_prices_hourly -- token prices
-- Same pattern for other chains:
polygon.core.fact_transactions
arbitrum.core.fact_event_logs
optimism.defi.ez_dex_swaps
solana.core.fact_transactions
Flipside naming conventions¶
fact_prefix: raw fact tables (immutable event records)dim_prefix: dimension tables (labels, metadata)ez_prefix: convenience views with decoded and enriched data- Column names use snake_case
- Timestamps are in UTC
- Token amounts in
ez_views are typically already decimal-adjusted - Raw amounts in
fact_tables require division byPOWER(10, decimals)
Flipside-specific patterns¶
SQL
-- Using Flipside's ez_ tables for quick analysis
SELECT
block_timestamp::date AS dt,
platform,
SUM(amount_in_usd) AS volume_usd
FROM ethereum.defi.ez_dex_swaps
WHERE block_timestamp >= CURRENT_DATE - INTERVAL '7 days'
GROUP BY 1, 2
ORDER BY 1, 3 DESC;
-- Address labeling with dim_labels
SELECT
t.from_address,
l.label,
l.label_type,
COUNT(*) AS tx_count
FROM ethereum.core.fact_transactions t
LEFT JOIN ethereum.core.dim_labels l
ON t.from_address = l.address
WHERE t.block_timestamp >= CURRENT_DATE - INTERVAL '1 day'
GROUP BY 1, 2, 3
ORDER BY 4 DESC
LIMIT 20;
Allium-Specific Schemas and Conventions¶
Allium uses a <chain>.raw and <chain>.assets schema pattern with some differences
from Flipside.
Schema structure¶
Text Only
ethereum.raw.blocks
ethereum.raw.transactions
ethereum.raw.logs
ethereum.raw.traces
ethereum.assets.erc20_transfer
ethereum.assets.erc721_transfer
ethereum.dex.trades
ethereum.lending.events
-- Cross-chain unified tables:
cross_chain.address_labels
cross_chain.token_metadata
Allium naming conventions¶
raw.schema: minimally processed blockchain dataassets.schema: token transfer events (ERC20, ERC721, ERC1155)dex.schema: decoded DEX trade eventslending.schema: decoded lending protocol events- Column names use snake_case
- Timestamps are
block_timestampin UTC - Raw amounts require decimal adjustment using
token_metadata - Allium provides
unique_idcolumns for deduplication
Allium-specific patterns¶
SQL
-- Using Allium's unified cross-chain structure
SELECT
chain,
block_timestamp::date AS dt,
COUNT(*) AS transfer_count,
COUNT(DISTINCT from_address) AS unique_senders
FROM ethereum.assets.erc20_transfer
WHERE token_address = LOWER('0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48') -- USDC
AND block_timestamp >= CURRENT_DATE - INTERVAL '30 days'
GROUP BY 1, 2
ORDER BY 2;
-- Trace-level analysis (Allium provides full trace data)
SELECT
tx_hash,
trace_address,
from_address,
to_address,
value / POWER(10, 18) AS eth_value,
call_type
FROM ethereum.raw.traces
WHERE block_number = 18000000
AND status = 1
ORDER BY trace_address;
Best Practices¶
Time-series aggregation¶
- Always use
DATE_TRUNCor::datefor consistent time bucketing. - Specify timezone explicitly when it matters:
AT TIME ZONE 'UTC'. - For hourly data, use
DATE_TRUNC('hour', block_timestamp). - Fill gaps in time series with a date spine to avoid misleading charts:
SQL
WITH date_spine AS (
SELECT generate_series(
'2024-01-01'::date,
CURRENT_DATE,
'1 day'::interval
)::date AS dt
),
daily_data AS (
SELECT block_timestamp::date AS dt, SUM(amount_usd) AS volume
FROM dex_swaps
GROUP BY 1
)
SELECT
s.dt,
COALESCE(d.volume, 0) AS volume
FROM date_spine s
LEFT JOIN daily_data d ON s.dt = d.dt
ORDER BY s.dt;
Address filtering¶
- Always lowercase addresses for comparison:
LOWER(address). - Use
INclauses for multiple known addresses rather than multiple ORs. - For contract vs. EOA distinction, check the
to_addressof creation transactions or use platform-provided labels. - Be aware of proxy contracts: the address you interact with may differ from the implementation address.
Token decimals¶
- Never display raw token amounts without decimal adjustment.
- Standard decimals: ETH/WETH = 18, USDC/USDT = 6, WBTC = 8, DAI = 18.
- Always join with token metadata to get the correct decimals:
SQL
SELECT
t.from_address,
t.to_address,
t.raw_amount / POWER(10, m.decimals) AS amount,
m.symbol,
(t.raw_amount / POWER(10, m.decimals)) * p.price AS amount_usd
FROM token_transfers t
JOIN token_metadata m ON t.token_address = m.address
LEFT JOIN token_prices p ON t.token_address = p.token_address
AND DATE_TRUNC('hour', t.block_timestamp) = p.hour
Performance tips¶
- Filter on
block_timestamporblock_numberearly -- these columns are typically partitioned and indexed. - Avoid
SELECT *on large tables; specify only needed columns. - Use
LIMITduring development to avoid scanning entire tables. - For large aggregations, consider materializing intermediate results with CTEs rather than deeply nested subqueries.
- When joining large tables, ensure join keys are indexed (they usually are for
tx_hash,block_number,addresscolumns).