Overview
This skill covers five focused use cases where Dune excels over RPC:
- Token transfers from/to an address
- Contract creation transaction
- All txs that called a specific function
- Filter logs by contract or event
- Labels for an address
Prerequisites
Assume Dune CLI is installed and authenticated. Run commands directly.
If a command fails:
"command not found"→ CLI not installed401 / "unauthorized"→ Auth failure (setDUNE_API_KEYenv var or rundune auth --api-key <key>)- Always use
-o jsonfor all commands
Critical DuneSQL rules:
- Addresses are
varbinary— use0xprefix without quotes:WHERE "from" = 0xabc...(NOT'0xabc...') - Reserve words
fromandtomust be quoted:"from","to" - Always include a partition filter (
block_time,evt_block_time) to avoid scanning the full table - For
tokens.transfersspell table, the partition column isblock_time
Chain names used as table prefixes and blockchain column values:
ethereum, base, arbitrum, optimism, polygon, bnb, avalanche_c, gnosis, fantom, scroll, zksync, linea, mantle, blast, celo
When the user doesn't specify a chain, ask — or default to querying across all chains using spell tables (tokens.transfers, dex.trades) which already include a blockchain column.
1. Token Transfers From/To an Address
Use the tokens.transfers spell table — it aggregates ERC-20/721/1155 transfers across all tokens, already decoded with symbol, amount, and USD value.
dune query run-sql --sql "
SELECT
block_time,
blockchain,
symbol,
amount,
amount_usd,
\"from\",
\"to\",
tx_hash,
contract_address
FROM tokens.transfers
WHERE (
\"from\" = 0xTARGET_ADDRESS
OR \"to\" = 0xTARGET_ADDRESS
)
AND blockchain = 'ethereum'
AND block_time >= NOW() - INTERVAL '30' DAY
ORDER BY block_time DESC
LIMIT 100
" -o json
Customize:
- Change
blockchaintobase,arbitrum,optimism,polygon,bnb, etc. - Remove the
blockchainfilter to search all chains - Adjust the
INTERVALfor time range - Add
AND symbol = 'USDC'to filter by token - Add
AND amount_usd > 1000to filter by USD value
For a specific token contract on a specific chain (if you know both):
# Replace erc20_<chain> with e.g. erc20_base, erc20_arbitrum, erc20_optimism, erc20_polygon, erc20_bnb, etc.
dune query run-sql --sql "
SELECT
evt_block_time,
\"from\",
\"to\",
CAST(value AS double) / POWER(10, 18) AS amount,
evt_tx_hash
FROM erc20_<chain>.evt_Transfer
WHERE (\"from\" = 0xTARGET OR \"to\" = 0xTARGET)
AND contract_address = 0xTOKEN_CONTRACT
AND evt_block_time >= NOW() - INTERVAL '30' DAY
ORDER BY evt_block_time DESC
LIMIT 100
" -o json
2. Contract Creation Transaction
Find when and how a contract was deployed. Replace <chain> with the appropriate chain name.
# <chain> = ethereum | base | arbitrum | optimism | polygon | bnb | avalanche_c | etc.
dune query run-sql --sql "
SELECT
block_time,
block_number,
tx_hash,
\"from\" AS deployer,
address AS contract_address
FROM <chain>.creation_traces
WHERE address = 0xCONTRACT_ADDRESS
" -o json
No time filter needed — filtering on
addressis lightweight. If you don't know which chain, check a few or use a block explorer first.
3. All Txs That Called a Specific Function
Option A — Use decoded call tables (preferred if available)
First, find if the contract has decoded tables:
dune dataset search-by-contract --contract-address 0xCONTRACT_ADDRESS --include-schema -o json
Look for call_<FunctionName> tables (e.g. call_swap, call_transfer). The namespace includes the chain (e.g. uniswap_v3_ethereum, uniswap_v3_base). Then query:
dune query run-sql --sql "
SELECT
call_block_time,
call_tx_hash,
call_success,
output_0,
-- add decoded input columns as needed
call_from
FROM <namespace>.call_<FunctionName>
WHERE call_block_time >= NOW() - INTERVAL '30' DAY
ORDER BY call_block_time DESC
LIMIT 100
" -o json
Option B — Filter by function selector from raw traces (works for any contract, any chain)
The function selector is the first 4 bytes of calldata (keccak256 of the function signature).
# <chain> = ethereum | base | arbitrum | optimism | polygon | bnb | avalanche_c | etc.
dune query run-sql --sql "
SELECT
block_time,
tx_hash,
\"from\",
\"to\",
SUBSTR(input, 1, 4) AS selector,
input
FROM <chain>.traces
WHERE \"to\" = 0xCONTRACT_ADDRESS
AND SUBSTR(input, 1, 4) = 0xSELECTOR -- e.g. 0xa9059cbb for transfer(address,uint256)
AND block_time >= NOW() - INTERVAL '30' DAY
AND call_type = 'call'
ORDER BY block_time DESC
LIMIT 100
" -o json
Common selectors:
transfer(address,uint256)=0xa9059cbb,approve(address,uint256)=0x095ea7b3
Option C — Direct calls only (transaction-level):
dune query run-sql --sql "
SELECT
block_time,
hash AS tx_hash,
\"from\",
\"to\",
value
FROM <chain>.transactions
WHERE \"to\" = 0xCONTRACT_ADDRESS
AND SUBSTR(data, 1, 4) = 0xSELECTOR
AND block_time >= NOW() - INTERVAL '30' DAY
ORDER BY block_time DESC
LIMIT 100
" -o json
4. Filter Logs by Contract or Event
Replace <chain> with the target chain name.
All logs for a contract (raw):
dune query run-sql --sql "
SELECT
block_time,
tx_hash,
topic0,
topic1,
topic2,
topic3,
data,
index
FROM <chain>.logs
WHERE contract_address = 0xCONTRACT_ADDRESS
AND block_time >= NOW() - INTERVAL '7' DAY
ORDER BY block_time DESC
LIMIT 100
" -o json
Filter by specific event (topic0 = keccak256 of event signature):
dune query run-sql --sql "
SELECT
block_time,
tx_hash,
topic1, -- indexed param 1 (e.g. 'from' in Transfer)
topic2, -- indexed param 2 (e.g. 'to' in Transfer)
data -- non-indexed params (e.g. 'value' in Transfer)
FROM <chain>.logs
WHERE contract_address = 0xCONTRACT_ADDRESS
AND topic0 = 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef -- Transfer(address,address,uint256)
AND block_time >= NOW() - INTERVAL '7' DAY
ORDER BY block_time DESC
LIMIT 100
" -o json
Common topic0 values:
Transfer(address,address,uint256)=0xddf252ad...b3efApproval(address,address,uint256)=0x8c5be1e5...1a11Swap(Uniswap V2) =0xd78ad95f...6b87
Use decoded event tables when available (cleaner, named columns):
# First discover decoded tables for the contract
dune dataset search-by-contract --contract-address 0xCONTRACT_ADDRESS -o json
# The namespace already encodes the chain (e.g. uniswap_v3_base, aave_v3_arbitrum)
dune query run-sql --sql "
SELECT * FROM <namespace>.evt_<EventName>
WHERE contract_address = 0xCONTRACT_ADDRESS
AND evt_block_time >= NOW() - INTERVAL '7' DAY
LIMIT 100
" -o json
5. Labels for an Address
Dune maintains curated label tables that map addresses to human-readable names, categories, and ownership info. This is useful for identifying unknown wallets, contracts, or entities.
Primary lookup — use labels.addresses (consolidated, multi-chain, richest data):
dune query run-sql --sql "
SELECT blockchain, address, name, category, label_type, model_name, contributor
FROM labels.addresses
WHERE address = 0xTARGET_ADDRESS
ORDER BY blockchain, category
LIMIT 50
" -o json
This single table aggregates labels from all sources: ENS names, DEX personas, NFT activity, CEX deposit addresses, sanctions, and more. It typically returns many rows per address — one per label per chain.
Reverse lookup — find all addresses for a known entity (e.g. all Binance wallets):
dune query run-sql --sql "
SELECT blockchain, address, name, category, label_type
FROM labels.addresses
WHERE LOWER(name) LIKE '%binance%'
ORDER BY blockchain
LIMIT 100
" -o json
If you need ownership/custody details specifically (who controls an address):
dune query run-sql --sql "
SELECT address, blockchain, owner_key, custody_owner, account_owner, contract_name
FROM labels.owner_addresses
WHERE address = 0xTARGET_ADDRESS
" -o json
If you need OFAC sanctions check specifically:
dune query run-sql --sql "
SELECT address, name, category, label_type
FROM labels.ofac_sanctioned_ethereum
WHERE address = 0xTARGET_ADDRESS
" -o json
What the key tables provide:
labels.addresses— Primary table. All labels in one place: ENS names, behavioral personas (DEX trader, NFT user), protocol affiliations, exchange labels. Columns:blockchain,address,name,category,label_type,model_name,contributorlabels.owner_addresses— Ownership/custody data: who custodies/owns an address, contract names, EOA vs factory flagslabels.ofac_sanctioned_ethereum— OFAC-sanctioned addresses (e.g. Tornado Cash contracts)
Error Recovery
| Error | Fix |
|---|---|
Column cannot be resolved |
Check schema: dune dataset search --query "<table>" --include-schema -o json |
Type mismatch |
Addresses must use 0x literal, not string quotes |
mismatched input near from/to |
Must quote reserved words: "from", "to" |
| Query timeout / resource limit | Add or tighten block_time filter; try --performance large |
Table does not exist |
Search for correct name: dune dataset search --query "<keyword>" -o json |