在区块链数据分析中,除了代币价格,代币的持有者数量、总供应量以及各账户余额也是关键指标。本文将详细介绍如何在链上查询这些数据,并提供实用的查询方法和技巧。
代币余额计算基础
与比特币使用的UTXO模型不同,以太坊及兼容EVM的区块链采用账户余额模型。每个地址的每种ERC20代币余额需要通过汇总所有转入和转出记录来计算。虽然Dune V2提供了魔法表(如erc20_day、erc20_latest)来简化这一过程,但目前仅支持以太坊链,且查询性能有限。因此,我们推荐直接查询原始转账记录来计算余额。
获取代币合约地址
查询前,需先获取代币的合约地址。可通过Dune的tokens.erc20表查询:
SELECT * FROM tokens.erc20
WHERE symbol = '代币符号' AND blockchain = '区块链名称';例如,查询FTT代币的合约地址:
SELECT * FROM tokens.erc20
WHERE symbol = 'FTT' AND blockchain = 'ethereum';查询持有者数量与总供应量
计算持有者数量和总供应量需合并所有转入和转出记录,并按地址汇总余额。以下示例以FTT代币为例:
合并转账记录
SELECT * FROM (
SELECT evt_block_time, evt_tx_hash, contract_address, `to` AS address, value::DECIMAL(38, 0) AS amount
FROM erc20_ethereum.evt_Transfer
WHERE contract_address = '0x50d1c9771902476076ecfc8b2a83ad6b9355a4c9'
UNION ALL
SELECT evt_block_time, evt_tx_hash, contract_address, `from` AS address, -1 * value::DECIMAL(38, 0) AS amount
FROM erc20_ethereum.evt_Transfer
WHERE contract_address = '0x50d1c9771902476076ecfc8b2a83ad6b9355a4c9'
) LIMIT 10;汇总余额并计算指标
WITH transfer_detail AS (
-- 同上合并转账记录
),
address_balance AS (
SELECT address, SUM(amount) AS balance_amount
FROM transfer_detail
GROUP BY address
)
SELECT COUNT(*) AS holder_count, SUM(balance_amount / 1e18) AS supply_amount
FROM address_balance
WHERE balance_amount > 0;注意:不同代币的小数位数不同,需根据tokens.erc20表调整除数(如1e18)。余额为0的地址应排除,且可忽略极小余额(如小于0.001)以优化查询精度。
查询顶级持有者地址
基于上述汇总数据,可查询持有量最多的地址:
WITH transfer_detail AS (
-- 同上合并转账记录
),
address_balance AS (
SELECT address, SUM(amount / 1e18) AS balance_amount
FROM transfer_detail
GROUP BY address
)
SELECT address, balance_amount
FROM address_balance
ORDER BY balance_amount DESC
LIMIT 100;建议将代币合约地址参数化(如{{token_contract_address}}),以便灵活查询不同代币。结果可可视化展示,如使用对数坐标轴柱状图对比金额差异。
分析持有者余额分布
余额分布分析有助于了解代币的集中程度。以下是两种常用方法:
经验法则分区
按预设金额区间统计持有者数量:
WITH address_balance AS (
-- 汇总余额CTE
)
SELECT
CASE WHEN balance_amount >= 10000 THEN '≥10000'
WHEN balance_amount >= 1000 THEN '≥1000'
-- 更多区间...
ELSE '<1.0' END AS amount_area_type,
COUNT(address) AS holder_count,
AVG(balance_amount) AS average_balance_amount
FROM address_balance
GROUP BY 1
ORDER BY MIN(balance_amount) DESC;对数分区法
对于金额差异大的情况,使用对数分区更合理:
WITH address_balance AS (
SELECT address,
FLOOR(LOG2(SUM(amount / 1e18))) AS log_balance_amount,
SUM(amount / 1e18) AS balance_amount
FROM transfer_detail
GROUP BY address
HAVING balance_amount >= POW(10, -4) -- 过滤极小余额
)
SELECT
CASE WHEN log_balance_amount <= 0 THEN 0
ELSE POW(2, log_balance_amount) * 1.0 END AS min_balance_amount,
COUNT(*) AS holder_count
FROM address_balance
GROUP BY 1
ORDER BY 1;跟踪持有者数量变化
要分析持有者数量随时间的变化,可使用代币的解析表(如ftt_ethereum.FTT_Token_evt_Transfer)提升查询性能。以下示例按周统计:
WITH transfer_detail AS (
-- 从解析表合并转账记录
),
holder_balance_weekly AS (
SELECT DATE_TRUNC('week', evt_block_time) AS block_date, address, SUM(value/1e18) AS balance_amount
FROM transfer_detail
GROUP BY 1, 2
),
-- 更多CTE用于计算累计余额和日期序列...
SELECT block_date, holder_count, balance_amount,
holder_count - LAG(holder_count, 1) OVER (ORDER BY block_date) AS holder_count_change
FROM holder_count_summary
ORDER BY block_date;查询指定地址余额
筛选特定地址即可查询其余额:
WITH address_balance AS (
-- 汇总余额CTE
)
SELECT address, balance_amount
FROM address_balance
WHERE address IN ('地址1', '地址2');原生代币(如ETH)余额查询
ETH不是ERC20代币,其转账记录存储在ethereum.traces表中。计算方式类似,但需包含所有交易类型(如合约调用、燃料费支付)。👉 获取进阶ETH余额查询方法
常见问题
如何获取代币的小数位数?
查询tokens.erc20表,字段decimals即小数位数。
为什么我的持有者数量与Etherscan有差异?
通常因过滤标准不同(如忽略极小余额)所致。调整余额阈值可减少差异。
查询性能慢怎么办?
使用代币解析表而非通用表,添加日期范围限制,并避免全表扫描。
是否支持非以太坊链?
当前部分魔法表仅支持以太坊,但手动查询可适配多链(如替换表名为erc20_bsc.evt_Transfer)。
如何可视化余额分布?
推荐使用直方图或饼图,并利用排序字段优化显示顺序。
能否查询历史余额?
可以,通过关联日期序列和累计余额计算实现,但查询复杂度较高。
总结
查询代币持有者数据需理解余额计算原理,灵活运用SQL汇总和过滤技巧。通过参数化查询和可视化分析,可高效获取链上洞察。