初学使用web3.js提交事务到以太坊时,会遇到:Error: Returned error: invalid sender
以太坊返回提示:发送者无效。
产生此问题的根本原因是事务提交操作的链信息没有指定,或者指定的不对。看下面例子
const Web3 = require('web3')
const web3 = new Web3("http://localhost:8545")
// 智能合约地址部署地址
let contractAddr = '0x......'
// 操作智能合约的账户,也就是要发送事务的账户
const accountAddress = '0x.....'
// 交易接收账户
const accountReceiveAddr = '0x....'
// 事务要执行的方法,取自abi中
const fs = require('fs')
// 通过remix编译得到的metadata文件
const meta = JSON.parse(fs.readFileSync('artifacts/MyToken_metadata.json').toString())
const abi = meta.output.abi;
// 根据智能合约接口定义文件abi和部署地址,实例化智能合约
const contract = new web3.eth.Contract(abi, contractAddr)
// 构造要执行的智能合约方法
let transferFn = contract.methods.transfer(accountReceiveAddr, 11)
// 将要执行的方法编码为16进制数据,该数据包含方法签名16进制值+参数16进制编码值
let transHexData = transferFn.encodeABI()
console.log('transHexData =>', transHexData)
// gas预估消耗
let estimateGasValue = await web3.eth.estimateGas({
from: accountAddress, // 必须加上from,否则抛出:Error: Returned error: execution reverted at Object.ErrorResponse (...
to: contractAddr, // 必传参数,通过事务消息要发送到的地址和data数据来估算gas值(每次调用不一定相同,gas值还和当前链上的任务数有关)
data: transHexData
}, (err, egas) => {
console.log(err, 'estimateGas =>', egas)
})
// 引入事务对象 需要先安装模块 cnpm install -g @ethereumjs/tx
// 文档:https://ethereumjs.readthedocs.io/en/latest/
const Tx = require('@ethereumjs/tx').Transaction
let rawTx = {
// nonce值为当前要提交事务的账户已提交的事务数量
nonce: web3.utils.toHex(await web3.eth.getTransactionCount(accountAddress)),
// 1gwei web3.eth.getGasPrice() 可以获取最近几个区块的价格中位数, 默认不设置gasPrice使用该值
gasPrice: '0x3B9ACA00',
gasLimit: estimateGasValue,
to: contractAddr, // 合约地址
value: '0x00',
data: transHexData
}
// web3.eth.defaultAccount = accountAddress
// 账户密钥(创建时候保存的,或者从metamask导出密钥)
const privateKey = Buffer.from('a7870a8da63dcf8fbb3d17e70de85846b9bbb54a957cb8d7ca454ae67fbcae7d', 'hex')
// 私有链、自定义链必须指定链信息,默认mainnet,web3版本4.0后默认undefined,不指定导致 invalid sender
// 安装common模块:cnpm install -g @ethereumjs/common
// 配置自定义链信息,创建事务时需要指定,否则出现invalid sender问题
const Common = require('@ethereumjs/common')
let commOpt = Common.Common.custom({chainId: 1337802, networkId: 1337802})
let tx = new Tx(rawTx, {common: commOpt})
let signedTx = tx.sign(privateKey)
let serializeTx = signedTx.serialize()
console.log("serialize => ", serializeTx)
web3.eth.sendSignedTransaction('0x' + serializeTx.toString('hex'))
.on('receipt', console.log)
注意:invalid sender问题出现的原因就是在new Tx时候没有设置正确的txOptions参数:
const Common = require('@ethereumjs/common')
// 配置自定义链信息,链id和networkid为初始化链时的值,可以通过geth console获取eth.getChainId()
// 通常chainId和networkId一样,初始化创建创世区块时侯也可以设置的不一样
let commOpt = Common.Common.custom({chainId: 1337802, networkId: 1337802})
// 第二个参数为txOptions参数,用于配置链信息
let tx = new Tx(rawTx, {common: commOpt})
测试方式:
通过geth启动一个自定义链,如果没有先初始化一个链,然后通过node demo.js测试即可。
本测试启动:geth console --kiln --rpc.gascap 0 --rpc.txfeecap 0 --http -http.api web3,eth,net,personal,debug --http.corsdomain '*' --ws --ws.api web3,net,eth,personal,debug --allow-insecure-unlock --vmdebug 2>~/geth.log
参考