无需Web3库,如何通过JSON-RPC直接调用以太坊智能合约函数?
不借助Web3工具直接通过HTTP调用以太坊JSON-RPC的eth_call方法
要直接用HTTP请求调用以太坊JSON-RPC的eth_call方法,核心是手动完成ABI编码(生成函数调用的data字段)和返回结果的ABI解码——这两步原本是Web3库自动帮我们做的,现在需要手动实现。
核心步骤拆解
1. 生成函数调用的Data字段
以太坊合约调用的data由两部分组成:
- 函数选择器:对函数签名(如
balanceOf(address))做Keccak256哈希后取前4字节,用于告诉合约要执行哪个函数。 - 编码后的参数:按ABI规则对函数参数进行编码,比如地址需要左补零到32字节长度。
以ERC20的balanceOf(address)为例:
- 函数签名:
balanceOf(address) - 函数选择器:
keccak256("balanceOf(address)")的前4字节,即0x70a08231 - 参数编码:目标地址
0x7Ba83AC9243Ff520A28FC218758608F900E2d958补零到32字节,得到0x0000000000000000000000007ba83ac9243ff520a28fc218758608f900e2d958 - 最终
data:拼接选择器和编码参数,即0x70a082310000000000000000000000007ba83ac9243ff520a28fc218758608f900e2d958
2. 发送HTTP请求调用eth_call
构造JSON-RPC格式的请求体,通过POST请求发送到以太坊节点:
Python示例(无Web3库依赖)
import requests import json # 以太坊节点地址 rpc_url = "https://eth-pokt.nodies.app" # 合约地址与目标地址 contract_addr = "0x40E64405F18e4FB01c6fc39f4F0c78df5eF9D0E0" target_addr = "0x7Ba83AC9243Ff520A28FC218758608F900E2d958" # 构造balanceOf的data字段 function_selector = "0x70a08231" # balanceOf(address)的函数选择器 encoded_param = "0x" + target_addr[2:].zfill(64) # 地址补零到32字节 data = function_selector + encoded_param[2:] # 拼接完整data # JSON-RPC请求体 payload = { "jsonrpc": "2.0", "method": "eth_call", "params": [ { "to": contract_addr, "data": data }, "0x12B8E9E" # 对应区块号19649654的十六进制,也可填"latest"取最新区块 ], "id": 1 } headers = {"Content-Type": "application/json"} # 发送请求并获取结果 response = requests.post(rpc_url, json=payload, headers=headers) result = response.json()
3. 解码返回结果
eth_call返回的是十六进制字符串,需要根据函数输出类型解码:
解码uint256类型(如balanceOf的返回值)
# 提取返回的十六进制余额并转十进制 hex_balance = result["result"] balance = int(hex_balance, 16) print(balance) # 输出:976055241381593241650
解码字符串类型(如name()的返回值)
对于无参数的name()函数,函数选择器是0x06fdde03,返回结果解码方式如下:
# 构造name()的请求 payload_name = { "jsonrpc": "2.0", "method": "eth_call", "params": [ { "to": contract_addr, "data": "0x06fdde03" }, "latest" ], "id": 2 } response_name = requests.post(rpc_url, json=payload_name, headers=headers) hex_name = response_name.json()["result"] # 十六进制转字符串,去除末尾空字符 name_bytes = bytes.fromhex(hex_name[2:]) name = name_bytes.decode("utf-8").rstrip("\x00") print(name)
内容的提问来源于stack exchange,提问作者sirjay
相关产品推荐
相关产品推荐

