Introduction
Blink reads the bytecode of every contract deployed on the chains it indexes and
works out what each one is: its token standard, whether it is a proxy, what compiled
it, how large it is, and whether its source is verified.
It never sees an ABI or uploaded source. Every signal comes from static analysis of
onchain bytecode, so when a contract is labelled, it is because the bytecode
says so. The rule behind each label lives in
Tracked labels.
The dashboard turns that into a handful of charts (deployments, verification, size,
compilers, standards) plus a read-only SQL explorer for asking your own questions.
Chains
Two for now: Ethereum and Gnosis. The switcher in
the topbar sets the active chain.
Tracked labels
Every label comes from one static pass over a contract's
runtime bytecode. The scanner walks real opcodes and skips over push
data, so a function selector only counts when it is an actual PUSH4
operand, not four stray bytes sitting inside another instruction. Each rule below is
the exact one Blink applies.
Token standards (function selectors)
A standard is flagged only when all of its defining 4-byte function
selectors appear as PUSH4 operands in the runtime code.
| Label | Column | Requires ALL selectors |
| ERC-20 |
is_erc20 |
0x18160ddd totalSupply() · 0xa9059cbb transfer() · 0xdd62ed3e allowance() |
| ERC-721 |
is_erc721 |
0x6352211e ownerOf() · 0x42842e0e safeTransferFrom() |
| ERC-1155 |
is_erc1155 |
0x4e1273f4 balanceOfBatch() · 0x2eb2c2d6 safeBatchTransferFrom() |
Mutually exclusive. Classification is prioritized
ERC-1155 > ERC-721 > ERC-20. A contract that satisfies more
than one selector mask is labelled only as the highest-priority standard, so
is_erc20, is_erc721, and is_erc1155 are never
both true on the same row, and an ERC-1155 that also exposes ERC-20 selectors counts
only as ERC-1155.
EIP-1167 minimal proxy
Column is_proxy_minimal. Matches the canonical 45-byte clone
exactly: runtime length must be 45 bytes, with a fixed 10-byte prefix, a
variable 20-byte implementation address, and a fixed 15-byte suffix.
36 3d 3d 37 3d 3d 3d 36 3d 73 <20-byte implementation address> 5a f4 3d 82 80 3e 90 3d 91 60 2b 57 fd 5b f3
└──────── prefix (10) ────────┘ └──────── address ────────┘ └───────────── suffix (15) ─────────────┘
Canonical only. Optimized or variant minimal proxies (different
push layout, or any length other than 45 bytes) are not matched.
EIP-1967 proxy
Column is_proxy_eip1967. Flagged when a PUSH32 operand in
the runtime code equals the EIP-1967 implementation slot constant
(keccak256("eip1967.proxy.implementation") - 1):
0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc
Implementation slot only. The EIP-1967 admin slot and beacon slot
are not tracked.
PUSH0 opcode
Column uses_push0. True when the PUSH0 opcode
(0x5f) occurs as a real instruction in the runtime code. Because the
scanner skips push immediates, a 0x5f byte that merely sits inside
another push's data does not count. This is a genuine PUSH0 usage
signal (a proxy for a Solidity 0.8.20+ / Shanghai-era compile).
Source metadata hash
Column has_source_hash. Solidity and Vyper append a CBOR metadata
trailer to the runtime bytecode. Blink reads the last two bytes as the trailer
length, parses the CBOR map, and sets has_source_hash = true when that
map contains an ipfs, bzzr0, or bzzr1 key,
meaning the contract embeds a content hash of its source metadata.
The same CBOR trailer is where language (Solidity / Vyper / Other) and
compiler_version come from: a solc or vyper
key carries the version bytes, formatted major.minor.patch.
SQL guide
The explorer runs your query as read-only DuckDB SQL and shows the
result as a table you can export to CSV. Queries are scoped to whichever chain is
selected in the topbar. Below are the rules, the schema, and a stack of examples to
copy.
Rules & limits
- Statements must begin with
SELECT or WITH. Only a
single statement is allowed (one optional trailing ;).
- Read-only. Anything that writes or changes state is rejected, including
INSERT, UPDATE, DELETE, CREATE,
DROP, ALTER, COPY, ATTACH,
PRAGMA, SET, INSTALL/LOAD.
- File / external scanners are blocked (
read_csv,
read_parquet, read_json, read_blob,
*_scan, httpfs).
- Results are capped at 1000 rows; a
LIMIT is applied
for you (and any larger LIMIT you set is clamped down). Query text is
capped at 20,000 characters.
- Blobs (addresses, hashes, bytecode) are returned as
0x… hex strings.
The main table: contract_metadata
This is the table the default query uses and the one you'll want most of the time.
| Column | Type | Meaning |
address | text | contract address, 0x… |
block_number | int | deployment block |
create_index | int | ordering within a block |
deployer_address | text | deployer, 0x… |
code_hash_hex | text | runtime code hash, 0x… |
n_code_bytes | int | runtime bytecode length |
language | text | solidity / vyper / other |
compiler_version | text | e.g. 0.8.24 |
is_erc20 | bool | ERC-20 selector mask (see labels) |
is_erc721 | bool | ERC-721 |
is_erc1155 | bool | ERC-1155 |
is_proxy_eip1967 | bool | EIP-1967 proxy |
is_proxy_minimal | bool | EIP-1167 minimal proxy |
uses_push0 | bool | runtime uses PUSH0 |
has_source_hash | bool | CBOR source-metadata hash present |
is_verified | bool | matched in the verification registry |
contract_name | text | verified name, if any |
verification_source | text | where the verification came from |
decoded_at | timestamp | when bytecode was decoded |
Chain scoping. A query against contract_metadata only
sees the chain you've selected. To query across all chains, use
contract_metadata_all (same columns plus chain_id) and filter
chain_id yourself.
Other queryable views
bytecodes: distinct runtime bytecodes, with code_hash_hex,
n_code_bytes, code (the runtime bytecode blob), and
contract_count (deployments sharing this hash).
decoded_bytecodes: one decode row per code_hash
(language, compiler_version, the is_* label
flags, uses_push0, has_source_hash).
contracts: the only view carrying creation / deployment
bytecode (init_code) alongside runtime code, with
its own chain_id.
Bytecode availability. code and init_code
are populated only when the underlying bytecode snapshot / extraction data is loaded
for that chain; otherwise they are NULL. The label flags
(is_erc20, uses_push0, …) are always available on
contract_metadata regardless.
Examples: filter by what a contract is
Each of these runs against the chain selected in the topbar. Every label is just a
boolean column, so you filter on it directly.
ERC-20 tokens
SELECT address, compiler_version, is_verified
FROM contract_metadata
WHERE is_erc20
ORDER BY block_number DESC
LIMIT 100
NFTs (ERC-721 or ERC-1155)
SELECT address, contract_name, is_verified
FROM contract_metadata
WHERE is_erc721 OR is_erc1155
ORDER BY block_number DESC
LIMIT 100
Proxies (either kind)
SELECT address, is_proxy_eip1967, is_proxy_minimal
FROM contract_metadata
WHERE is_proxy_eip1967 OR is_proxy_minimal
ORDER BY block_number DESC
LIMIT 100
Contracts using the PUSH0 opcode
SELECT address, compiler_version, n_code_bytes
FROM contract_metadata
WHERE uses_push0
ORDER BY block_number DESC
Recently verified, with a name
SELECT block_number, address, contract_name, compiler_version
FROM contract_metadata
WHERE is_verified AND contract_name IS NOT NULL
ORDER BY block_number DESC
LIMIT 50
Carries a source hash but is not verified
Contracts that embedded a source-metadata hash yet never matched the verification
registry: good candidates to go verify.
SELECT address, compiler_version, language
FROM contract_metadata
WHERE has_source_hash AND NOT is_verified
ORDER BY block_number DESC
LIMIT 100
Largest contracts, near the size cap
The EVM caps runtime bytecode at 24,576 bytes; these sit right up against it.
SELECT address, n_code_bytes, compiler_version
FROM contract_metadata
ORDER BY n_code_bytes DESC
LIMIT 20
Examples: counts and breakdowns
Contracts by language
SELECT language, COUNT(*) AS n
FROM contract_metadata
GROUP BY language
ORDER BY n DESC
Top compiler versions
SELECT compiler_version, COUNT(*) AS n
FROM contract_metadata
WHERE compiler_version IS NOT NULL
GROUP BY compiler_version
ORDER BY n DESC
LIMIT 20
PUSH0 adoption by compiler version
COUNT(*) FILTER (WHERE ...) counts a subset alongside the total in one
pass.
SELECT compiler_version,
COUNT(*) AS total,
COUNT(*) FILTER (WHERE uses_push0) AS with_push0
FROM contract_metadata
WHERE compiler_version IS NOT NULL
GROUP BY compiler_version
ORDER BY total DESC
LIMIT 25
Verification coverage by compiler
SELECT compiler_version,
COUNT(*) AS total,
COUNT(*) FILTER (WHERE is_verified) AS verified
FROM contract_metadata
WHERE compiler_version IS NOT NULL
GROUP BY compiler_version
ORDER BY total DESC
LIMIT 25
Contracts per chain (all chains)
Reach past the selected chain with contract_metadata_all, which keeps the
chain_id column.
SELECT chain_id, COUNT(*) AS contracts
FROM contract_metadata_all
GROUP BY chain_id
ORDER BY contracts DESC
Examples: working with bytecode
Distinct first-20-byte prefixes of deployment bytecode
Deployment (creation) bytecode is init_code on the
contracts view. DuckDB's substr() works on blobs, so you can
take a byte prefix and count distinct values. This view keeps its own
chain_id, so scope it explicitly:
SELECT COUNT(DISTINCT substr(init_code, 1, 20)) AS distinct_prefixes
FROM contracts
WHERE chain_id = 1
AND init_code IS NOT NULL
Same idea on runtime bytecode (usually cheaper, one row per distinct
code hash):
SELECT COUNT(DISTINCT substr(code, 1, 20)) AS distinct_runtime_prefixes
FROM bytecodes
WHERE code IS NOT NULL
Heads up. init_code and code exist only when
bytecode data has been loaded for the chain. If they are NULL everywhere,
these counts come back 0.
Most deployed bytecodes
SELECT code_hash_hex, contract_count, n_code_bytes
FROM bytecodes
ORDER BY contract_count DESC
LIMIT 20
Implementation address behind minimal proxies
In an EIP-1167 clone the 20-byte implementation address sits at bytes 11 to 30 of the
runtime code. Pull it out with substr(); blobs come back as
0x… hex. Join the decode flags to the raw bytecode on
code_hash:
SELECT b.code_hash_hex,
substr(b.code, 11, 20) AS implementation
FROM bytecodes b
JOIN decoded_bytecodes d ON d.code_hash = b.code_hash
WHERE d.is_proxy_minimal
AND b.code IS NOT NULL
LIMIT 50