使用Infura调用智能合约发送交易时遇eth_sendTransaction不可用错误
解决Infura下
eth_sendTransaction不存在的错误 错误原因
Infura作为公共以太坊RPC节点,不支持eth_sendTransaction方法。该方法要求节点托管用户私钥并完成交易签名,而Infura出于安全考虑不提供此类服务。读函数(如getAppliances().call())无需签名,因此可以正常运行。
修复方案:本地签名交易后发送
需要手动构建交易、使用私钥完成本地签名,再通过eth_sendRawTransaction将签名后的交易发送至Infura。以下是修改后的完整Node.js代码:
修改后的代码
const web3 = require('web3'); const fs = require("fs"); const CONTRACT_ABI = `[ { "anonymous": false, "inputs": [ { "indexed": true, "internalType": "address", "name": "previousOwner", "type": "address" }, { "indexed": true, "internalType": "address", "name": "newOwner", "type": "address" } ], "name": "OwnershipTransferred", "type": "event" }, { "inputs": [ { "internalType": "string", "name": "name", "type": "string" }, { "internalType": "bytes1", "name": "pin", "type": "bytes1" } ], "name": "addAppliance", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ], "name": "appliances", "outputs": [ { "internalType": "uint256", "name": "id", "type": "uint256" }, { "internalType": "string", "name": "name", "type": "string" }, { "internalType": "bool", "name": "status", "type": "bool" }, { "internalType": "bytes1", "name": "pin", "type": "bytes1" } ], "stateMutability": "view", "type": "function" }, { "inputs": [ { "internalType": "uint256", "name": "id", "type": "uint256" } ], "name": "getAppliance", "outputs": [ { "components": [ { "internalType": "uint256", "name": "id", "type": "uint256" }, { "internalType": "string", "name": "name", "type": "string" }, { "internalType": "bool", "name": "status", "type": "bool" }, { "internalType": "bytes1", "name": "pin", "type": "bytes1" } ], "internalType": "struct Appliance", "name": "", "type": "tuple" } ], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "getAppliances", "outputs": [ { "components": [ { "internalType": "uint256", "name": "id", "type": "uint256" }, { "internalType": "string", "name": "name", "type": "string" }, { "internalType": "bool", "name": "status", "type": "bool" }, { "internalType": "bytes1", "name": "pin", "type": "bytes1" } ], "internalType": "struct Appliance[]", "name": "", "type": "tuple[]" } ], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "getAppliancesCount", "outputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "owner", "outputs": [ { "internalType": "address", "name": "", "type": "address" } ], "stateMutability": "view", "type": "function" }, { "inputs": [ { "internalType": "uint256", "name": "id", "type": "uint256" } ], "name": "removeAppliance", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [], "name": "renounceOwnership", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ { "internalType": "uint256", "name": "id", "type": "uint256" }, { "internalType": "bool", "name": "status", "type": "bool" } ], "name": "toggleAppliance", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ { "internalType": "address", "name": "newOwner", "type": "address" } ], "name": "transferOwnership", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ { "internalType": "uint256", "name": "id", "type": "uint256" }, { "internalType": "string", "name": "name", "type": "string" }, { "internalType": "bytes1", "name": "pin", "type": "bytes1" } ], "name": "updateAppliance", "outputs": [], "stateMutability": "nonpayable", "type": "function" } ]`; const CONTRACT_ADDRESS = "0x13d9FA79D364070510B410c2FaC1976F21E3e218"; const web3js = new web3(new web3.providers.HttpProvider("https://sepolia.infura.io/v3/9e644c118b7c44068674e2d12a776536")); var myAddress = '0x46Be881Fa6935a8FC969A4ddDFC74d625c558996'; const privateKey = Buffer.from('82f98661ea7e05ac4bad0965da4b8a1fab93cf27e606d1185a275c91f96aac9b', 'hex') var contractABI = JSON.parse(CONTRACT_ABI); var contractAddress = CONTRACT_ADDRESS; const contract = new web3js.eth.Contract(contractABI, contractAddress); async function main() { // 1. 获取地址当前nonce(避免交易重复) const nonce = await web3js.eth.getTransactionCount(myAddress, 'pending'); // 2. 编码合约方法调用数据 const functionData = contract.methods.addAppliance("fan", web3.utils.numberToHex(1)).encodeABI(); // 3. 构建交易对象 const txObject = { nonce: web3js.utils.toHex(nonce), gasLimit: web3js.utils.toHex(200000), // 可根据实际情况调整gas上限 gasPrice: web3js.utils.toHex(await web3js.eth.getGasPrice()), to: contractAddress, data: functionData, from: myAddress }; // 4. 本地签名交易 const signedTx = await web3js.eth.accounts.signTransaction(txObject, privateKey); // 5. 发送签名后的原始交易 const txReceipt = await web3js.eth.sendSignedTransaction(signedTx.rawTransaction); console.log("交易回执:", txReceipt); // 调用读函数验证结果 let applianceOnOff = await contract.methods.getAppliances().call(); console.log("设备列表:", applianceOnOff); } main() .then(() => process.exit(0)) .catch(error => { console.error(error); process.exit(1); });
关键修改说明
- 放弃
contract.methods.addAppliance().send({from: myAddress})的自动签名逻辑,改为手动处理交易签名与发送 - 通过
getTransactionCount获取地址nonce,确保交易按顺序执行 - 使用
encodeABI()编码合约方法调用的字节数据 - 调用
accounts.signTransaction完成本地私钥签名 - 用
sendSignedTransaction提交签名后的原始交易
额外提示
- 合约中
toggleAppliance函数声明的第二个参数bool status未在函数体中使用(直接取反现有状态),可考虑移除冗余参数或调整逻辑 - 私钥请勿硬编码在代码中,建议使用环境变量或加密存储方式管理
内容的提问来源于stack exchange,提问作者Martin Smith
相关产品推荐
相关产品推荐

