You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用web3py调用ropsten.infura.io触发405错误的问题求助

问题原因与解决方案

你遇到的405错误本质是Infura不支持personal命名空间的RPC方法,也不允许在其托管节点上解锁账户或代为签名交易。Infura作为公共节点服务,不会存储用户私钥,也禁用了这类需要访问用户私钥的操作,所以直接调用w3.personal.unlockAccount或让节点自动签名交易都会被拒绝。

为什么会报错?

  • w3.personal.unlockAccount属于personal系列RPC方法,Infura明确禁用了这类涉及私钥操作的接口,因此请求返回405 Method Not Allowed。
  • 当你调用contract_instance.giveToken(..., transact={'from': account_a})时,web3py默认会尝试让连接的Infura节点用本地存储的账户私钥签名交易,但Infura节点上没有你的私钥,也不提供签名服务,所以同样触发405错误。

正确的处理方式:本地签名交易

你需要在本地用自己的私钥完成交易签名,再将签名后的交易发送到Infura。具体步骤和修改后的代码如下:

1. 安全管理私钥

绝对不要把私钥硬编码在代码里,建议通过环境变量、加密配置文件等方式加载。下面示例中用环境变量来获取私钥和Infura密钥。

2. 修改后的web3py代码

import json
import os
from eth_utils import to_checksum_address
from web3 import Web3, HTTPProvider
from web3.contract import ConciseContract
from web3.middleware import geth_poa_middleware

# 从环境变量加载敏感信息(安全做法)
INFURA_API_KEY = os.getenv("INFURA_API_KEY")
ACCOUNT_PRIVATE_KEY = os.getenv("ACCOUNT_PRIVATE_KEY")

w3 = Web3(HTTPProvider(f'https://ropsten.infura.io/v3/{INFURA_API_KEY}'))
w3.middleware_stack.inject(geth_poa_middleware, layer=0)

# 合约地址与ABI(ABI可从Remix或Etherscan获取)
contract_address = to_checksum_address('0xFb294910d8193DeB9a294B51F22D8878ad15f2E8')
contract_abi = [
    {"constant":false,"inputs":[{"name":"dest","type":"address"},{"name":"amount","type":"uint256"}],"name":"giveToken","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},
    {"constant":true,"inputs":[{"name":"addr","type":"address"}],"name":"getBalance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},
    {"constant":true,"inputs":[],"name":"myBalance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},
    {"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"},
    {"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"receiver","type":"address"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"increaseToken","type":"event"}
]

# 实例化合约
contract_instance = w3.eth.contract(abi=contract_abi, address=contract_address, ContractFactoryClass=ConciseContract)

account_a = to_checksum_address("0xCa2d22Cb8ff54f2D1DCfDBb75DD6411a5A0ee6f1")
account_b = to_checksum_address("0x8F8d1bc97E8939e3932BfBeb923A1B2B972D5A9A")

# 打印交易前余额
print("Contract: {}".format(contract_address))
print("Before give token")
print('My Balance: {}'.format(contract_instance.myBalance()))
print('{} Balance: {}'.format(account_a, contract_instance.getBalance(account_a)))
print('{} Balance: {}'.format(account_b, contract_instance.getBalance(account_b)))

token_amount = 1000

# 1. 构建交易对象
nonce = w3.eth.getTransactionCount(account_a)  # 获取账户当前nonce,避免交易重复
gas_price = w3.eth.gasPrice  # 使用当前网络默认gas价格
gas_limit = contract_instance.giveToken.estimateGas(account_b, token_amount)  # 估算交易所需gas
tx_data = contract_instance.giveToken.encodeABI(args=[account_b, token_amount])  # 编码合约调用数据

tx = {
    'from': account_a,
    'to': contract_address,
    'value': 0,
    'gas': gas_limit,
    'gasPrice': gas_price,
    'nonce': nonce,
    'data': tx_data,
    'chainId': 3  # Ropsten测试网的chainId为3
}

# 2. 用私钥签名交易
signed_tx = w3.eth.account.sign_transaction(tx, private_key=ACCOUNT_PRIVATE_KEY)

# 3. 发送签名后的交易
tx_hash = w3.eth.sendRawTransaction(signed_tx.rawTransaction)
print(f"交易已发送,哈希: {w3.toHex(tx_hash)}")

# 等待交易上链确认
tx_receipt = w3.eth.waitForTransactionReceipt(tx_hash)
print(f"交易已确认,区块号: {tx_receipt.blockNumber}")

# 验证交易结果
try:
    assert contract_instance.getBalance(account_b) == token_amount
except AssertionError as e:
    print("Test Error: {}".format(e))
    print("Amount: {}	 Expected: {}".format(contract_instance.getBalance(account_b), token_amount))
else:
    print("After give token")
    print('My Balance: {}'.format(contract_instance.myBalance()))
    print('{} Balance: {}'.format(account_a, contract_instance.getBalance(account_a)))
    print('{} Balance: {}'.format(account_b, contract_instance.getBalance(account_b)))
finally:
    print('Done')

关键细节说明

  • 私钥安全:用环境变量加载私钥,避免泄露风险;也可以用加密钱包(如web3py支持的keystore文件)来管理私钥。
  • Nonce:确保每笔交易的nonce唯一,防止交易重复或失败。
  • Gas设置:estimateGas能自动估算合约调用所需gas,也可手动设置一个合理值;gasPrice可根据网络拥堵情况调整。
  • ChainId:指定正确的链ID,防止交易被发送到其他网络。

额外提示

你在Remix中能正常交易,是因为Remix会通过浏览器钱包(如MetaMask)在本地完成签名,再将交易发送到网络——这和我们上面的本地签名逻辑完全一致。

内容的提问来源于stack exchange,提问作者Henry Kim

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 06:39:01