如何使用Python和Web3.py获取ETH智能合约的ERC-20交易金额
基于Web3.py获取ERC-20转账金额的实现方法
原理说明
0xa9059cbb是ERC-20标准中transfer(address recipient, uint256 amount)方法的固定函数选择器,对应你提到的这类转账交易- 交易的
input字段结构为:0x + 8位方法选择器 + 64位收款地址参数(补零到32字节) + 64位转账金额参数(补零到32字节) - 直接解析input或者解析交易触发的Transfer事件都可以拿到转账金额,后者更准确,可排除交易执行失败的无效场景
方法1:直接解码input字段
实现步骤:
- 先过滤符合条件的交易:
value为0,且input以0xa9059cbb开头、长度≥138位 - 调用Web3内置的ABI解码工具解析input参数
- 调用对应ERC20合约的
decimals()方法获取精度,换算得到用户可见的实际转账金额
代码示例:
from web3 import Web3, HexBytes # 初始化Web3实例,替换为你的RPC节点地址 w3 = Web3(Web3.HTTPProvider("https://你的RPC节点地址")) # 仅定义transfer方法的ABI片段即可,无需完整ERC20 ABI ERC20_ABI = [ { "constant": False, "inputs": [ {"name": "_to", "type": "address"}, {"name": "_value", "type": "uint256"} ], "name": "transfer", "outputs": [{"name": "", "type": "bool"}], "payable": False, "stateMutability": "nonpayable", "type": "function" }, { "constant":True, "inputs":[], "name":"decimals", "outputs":[{"name":"","type":"uint8"}], "payable":False, "stateMutability":"view", "type":"function" } ] # 你的交易数据示例 tx = {'blockHash': HexBytes('0x439f47a300d021223aeee060c0daabc03bedb250600890faeea7b27c0d30e0e4'), 'blockNumber': 13395924, 'from': '0x18EEfb2E363B54d9F76d347c4aAACF79553c7dBf', 'gas': 106052, 'gasPrice': 89000000000, 'hash': HexBytes('0x173f992db0b9c96f57799afdec51c7a418b7fa36c5ba8b16acb5d45619f03f3f'), 'input': '0xa9059cbb000000000000000000000000a1d8d972560c2f8144af871db508f0b0b10a3fbf00000000000000000000000000000000000000000000010f0cf064dd59200000', 'nonce': 14, 'r': HexBytes('0x9aba92daac3b8b2858552d0e88a3a3cbf904759942830d1d615a8d98af972fd1'), 's': HexBytes('0x7f5228eba9607f0d2a6cb1c6dc97bc37056a7274fa5ee9283d5962de41a86955'), 'to': '0xaA7a9CA87d3694B5755f213B5D04094b8d0F0A6F', 'transactionIndex': 145, 'type': '0x0', 'v': 38, 'value': 0} # 解码input contract = w3.eth.contract(abi=ERC20_ABI) func_obj, func_params = contract.decode_function_input(tx['input']) # 原始转账金额(未除精度) raw_amount = func_params['_value'] # 收款地址 recipient = func_params['_to'] # 获取对应ERC20代币的精度,换算为实际金额 erc20_contract = w3.eth.contract(address=tx['to'], abi=ERC20_ABI) decimals = erc20_contract.functions.decimals().call(block_identifier=tx['blockNumber']) actual_amount = raw_amount / (10 ** decimals) print(f"原始金额:{raw_amount},实际转账金额:{actual_amount}")
方法2:解码交易的Transfer事件(更准确)
交易执行成功后会触发ERC20标准的Transfer(address from, address to, uint256 value)事件,解码该日志得到的金额是实际链上生效的金额:
# Transfer事件的topic固定值 TRANSFER_EVENT_TOPIC = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef" TRANSFER_EVENT_ABI = [{ "anonymous": False, "inputs": [ {"indexed": True, "name": "from", "type": "address"}, {"indexed": True, "name": "to", "type": "address"}, {"indexed": False, "name": "value", "type": "uint256"} ], "name": "Transfer", "type": "event" }] # 获取交易收据 tx_receipt = w3.eth.get_transaction_receipt(tx['hash']) for log in tx_receipt['logs']: if log['topics'][0].hex() == TRANSFER_EVENT_TOPIC: # 解码日志参数 decoded_log = w3.eth.contract(abi=TRANSFER_EVENT_ABI).events.Transfer().process_log(log) raw_amount = decoded_log['args']['value'] # 后续换算精度逻辑同方法1 break
内容的提问来源于stack exchange,提问作者taga
相关产品推荐
相关产品推荐

