1. 准备工作:合约地址、ABI 与私钥配置
要进行 ERC20 代币转账,首先需要三样东西:节点 RPC 地址、ERC20 合约地址,以及一份精简的 ABI。以示例中的 Hoo Smart Chain 为例——HTTP 端点为 https://http-mainnet.hoosmartchain.com。
本示范所用的两个代币分别映射到两个 ERC20 合约地址:
- iXT 合约:
0x80c6A3A493aFd7C52f89E6504C90cE6A639783FC - iUSDT 合约:
0x09e6030537f0582d51C00bdC2d590031d9b1c86c
只保留 balanceOf 与 transfer 两个函数,能显著减小 ABI 体积,降低代码复杂度,也能减少内存占用。
2. 项目结构与依赖
- Python 3.8+
web3.py:核心交互库xlrd:读取 Excel 私钥清单(或改用 CSV、JSON)time:避免高频请求触发节点限频
安装命令:
pip install web3==6.* xlrd==2.*3. 读取 Excel—批量导入地址与私钥
Excel 格式保持简单:
| 一栏:钱包地址 | 一栏:明文私钥 |
借助 defaultdict,可以把地址映射到私钥,实现“一键扫全部私钥”的批处理:
from collections import defaultdict
import xlrd
def init_address():
data = defaultdict(str)
xls = xlrd.open_workbook("3000_2.xlsx")
sheet = xls.sheets()[0]
for row_idx in range(sheet.nrows):
addr = sheet.cell_value(row_idx, 0).strip()
priv = sheet.cell_value(row_idx, 1).strip()
data[addr] = priv
return data小提示
Excel 中会出现空格或科学计数法问题,务必.strip()并在数据源头统一格式。
4. 查询余额——实时校验代币余额
web3.py 的 contract.functions.balanceOf().call() 查询完全离线,不产生费用。将其换算成 以太单位 方便人类阅读:
def get_ixt(address):
addr = Web3.toChecksumAddress(address)
return w3.fromWei(iXT_CONTRACT.functions.balanceOf(addr).call(), 'ether')
def get_iusdt(address):
addr = Web3.toChecksumAddress(address)
return w3.fromWei(iUSDT_CONTRACT.functions.balanceOf(addr).call(), 'ether')调用失败的大多是 地址未校验:toChecksumAddress() 是小写地址转 EIP-55 格式的关键步骤。
5. 签名与广播交易——三步完成转账
- 构造交易字典
- 本地签名
- 广播到节点
示例函数仅转账整张 iUSDT 余额:
def send_iusdt(from_addr, priv_key):
to_addr = Web3.toChecksumAddress('0xE8e069C47A45050DBA9D566CEfFA7bc7D453F0c5')
amount_wei = w3.toWei(get_iusdt(from_addr), 'ether')
if amount_wei == 0:
return # 余额 0 不可转账
nonce = w3.eth.getTransactionCount(from_addr)
tx = iUSDT_CONTRACT.functions.transfer(
to_addr, amount_wei
).buildTransaction({
'gas': 241838,
'gasPrice': w3.toWei('1', 'gwei'),
'nonce': nonce,
})
signed = w3.eth.account.signTransaction(tx, priv_key)
tx_hash = w3.eth.sendRawTransaction(signed.rawTransaction)
print(f'已广播:{Web3.toHex(tx_hash)}')gas 设置为 24 万以上略高,能应付多数 ERC20 合约;节点拥堵时可提升 gasprice 到 5 gwei。
6. 批处理脚本实践
把上述模块拼成 if __name__ == '__main__': 块,即可一次扫完所有私钥:
if __name__ == '__main__':
wallets = init_address()
for addr, priv in wallets.items():
send_iusdt(addr, priv)
time.sleep(0.3) # 稍微降速,防止节点限频7. 安全与自动化进阶
- 私钥加密保存:用
keyring或环境变量,千万别明文进 git。 - 日志持久化:将
tx_hash写入文本文件,便于后续核对区块浏览器。 - 错误捕获:加
try/except捕捉Insufficient Balance、nonce too low等异常,提高脚本鲁棒性。 - 并发:上千个账号可采用
ThreadPoolExecutor或 Celery。
8. 常见问题(FAQ)
Q1:为什么报错 Could not decode contract function call?
- ABI 中缺少对应函数,或函数签名与链上不一致。核对 ABI 与合约是否匹配即可。
Q2:转账卡在 pending?
- 提高 gasprice 再重发即可覆盖卡住的交易;也可用
web3.eth.replace_transaction。
Q3:批量转账如何保障私钥不泄露?
- 私钥全部离线签名,然后复制签名后的 RawTx 联机上链广播,能 99% 隔离私钥风险。
Q4:能否一次转账多笔地址节省 gas?
- 通过 multisender 合约 或 Gnosis Safe 的 批量交易 都可以,但需额外部署或授权 ERC20 approve。
Q5:USDT 汇率与手续费怎么算?
- USDT 本身与美元 1:1;手续费按 gas*gasprice 计算,链上可查精确值,主网约 2–5 美元一次。