🏁 这是以太坊地址监控系列的最后一篇,前三篇已分别讲述基础调用、区块监听与 Token 变动捕捉。本文聚焦 Python 重构后的最终落地经验,帮你避开作者踩过的坑,并给出可直接复用的代码片段。
为什么选择 Web3.py 重构?
早期版本直接用 requests 发 HTTP 调用,代码量大、调试痛苦、格式不统一。作者尝试过:
web3.js:功能全但项目栈为 Python,调用麻烦;web3.py:与 Python 业务深度契合,pip install即可即用,生态也一直在更新;- 结论:用高效开发抵消 纯 JS API 方便性 的劣势,重构顺理成章。
核心关键词:web3.py、Python 以太坊开发、地址监听、智能合约监听。
三大核心对象拆解
EthAddrWatcher:监控以太币流动
职责一目了然:
add_address()一键添加目标地址;on_balance_change()回调传出变更金额;- 已内置 错误重试、RPC 超时复连 两个小彩蛋,主网长期跑一周不死机 👉 点击获取核心代码灵感与调试要诀。
关键示例
watcher = EthAddrWatcher(endpoint="https://mainnet.infura.io/v3/YOUR_KEY")
watcher.add_address("0xA0b86a33E6441b0...") # 关注热钱包
watcher.on_balance_change(lambda addr, delta:
print(f"{addr} 金额变动 {Web3.from_wei(delta,'ether')} ETH"))
watcher.loop_forever()EthAddrWatcherDB:把变更写进 DB
将增量数据落表,防止 窗口宕机漏数。选型简单暴力:
- SQLite:单机足够;
- SQLAlchemy ORM 自动建表,字段仅
address, block_number, delta_wei, created_at。
关键词:数据库监听表、SQLite、增量落库。
EthBlockSrv:命令行守护进程
一条命令拉起:
python eth_block_srv.py --conf /etc/eth_watcher.yaml- 从 DB 读 观察列表;
- 每块对比余额 → 将变更回写;
--once参数方便做 离线补偿。
四大雷区 & 避坑清单
1. 合约销毁后 getCode() 返回 0x
现象:
调用 w3.eth.get_code(contract_addr) 得到空字符串,于是脚本将其误判为普通 EOA(内部账户)。
解决思路:查看该地址历史交易 input 字段,只要存在 SELFDESTRUCT 操作即可断定已销毁,无需额外判断 合约接口是否存在。
核心关键词:以太坊合约销毁、SELFDESTRUCT、getCode。
2. 地址大小写不敏感却暗坑
测试网转账两次:
to=0x068a7A0022a0e20d78682F39aA75547A0A260A2A // 大写A
to=0x068a7A0022a0e20d78682F39aA75547A0A260A2a // 小写a结果链上都解析成 同一个收款地址。但 off-chain 工具却严格区分大小写,数据库唯一索引会崩溃。
对策:
- 一律调用
Web3.to_checksum_address()统一格式后再参与比对。
3. 主网 getCode() 意外返回 0x
哪怕地址明显是合约,偶尔也会拿到0x。
作者调试 3 小时发现:Infura 节点延迟导致调用命中空缓存。方案:
- 将
get_code()套一层 指数退避重试; - 同时扫描
input字段,若出现0xa9059cbb(ERC-20 transfer)直接标记 合约,无需再等。
4. 节点未同步,blockNumber 与 syncing 均取不到
在 --syncmode fast 的节点下,偶发的网络抖动会触发:
eth_getBlockByNumber("latest")返回null;eth_syncing也失败。
哈克方法:
- 循环等待 1 秒;
- 设置最大重试阈值 120;
- 达到阈值直接报警,防止脚本假死。
独立可运行最小示例
把核心流程压缩到 50 行,供同行快速测试(Python 3.9+):
from web3 import Web3, HTTPProvider
import sqlite3, time, os
w = Web3(HTTPProvider('https://mainnet.infura.io/v3/KEY'))
conn = sqlite3.connect('addr_watch.db')
addr = Web3.to_checksum_address('0x...')
cursor = conn.execute('SELECT balance FROM accounts WHERE address=?', (addr,))
old = int(cursor.fetchone()[0]) if cursor.arraysize > 0 else 0
while True:
try:
new = w.eth.get_balance(addr)
if new != old:
conn.execute('INSERT OR REPLACE INTO accounts(address,balance) VALUES (?,?)',
(addr, new))
conn.commit()
print('余额变化:', (new-old)/1e18, 'ETH')
old = new
time.sleep(5)
except Exception as e:
print('重连中', e)
time.sleep(1)关键词:Python 脚本 实时轮询余额、轻量级架构。
常见问题与解答(FAQ)
| 问 | 答 |
|---|---|
| 为什么我调用 eth_getBalance 一直在变? | 如果同一地址有未打包交易,RPC 节点可能在 Pending 状态 与 确认状态 间波动。给 block_identifier="latest" 再加 1 个确认即可。 |
| Infura 免费套餐够地址监控吗? | 每天 100k 次调用,仅监控几十地址完全够用。一旦调用量上涨,自建 geth light 节点 每月成本 <5 美元。 |
| 如何区分 ERC-20 转账与普通转账? | 若 tx.input 长度 ≥ 10 且前 10 位为 0xa9059cbb,大概率是 ERC-20 的 transfer(address,uint256),可据此再解码 to 与 value。 |
| SQLite 写入并发会阻塞吗? | 官方 WAL 模式已解决并发写。配合 check_same_thread=False 在 Python 中即可,多个 watchdog 进程同时写无明显锁表。 |
| 我还能监控哪些参数? | 除余额与 Token 流向外,还可以拉取 Nonce 变动、Gas Price 波动、智能事件日志——只需把 EthBlockSrv 解耦成插件式架构即可。 |
下一步计划
- 日志上报: 接入 Prometheus,获取
balance_change_total指标,配合 Grafana 生成大盘; - 邮件+短信报警: 当金额变动大于阈值及时提醒;
- 本地热缓存: 把余额信息写到 Redis,查询接口降到 1 ms。
关键词一览(自然融入文中)
- 以太坊地址监控
- web3.py
- Python 以太坊开发
- SELFDESTRUCT
- SQLite 日志表
- Infura 请求限制
- 合约销毁判断
- 余额监听
- ETH 主网实时更新
如果你觉得这套代码对你有帮助,欢迎 Star & Fork(去 Github 搜索同名主题即可)。更多优化细节,未来也会持续分享到社区~