将签名的交易发送到 Ropsten
Sending signed transactions to Ropsten
使用 web3js 很容易调用不需要签名的函数(例如,不更新合约状态的函数)。但是,除了手动解锁我的 MetaMask 钱包并在 Remix 环境中调用函数外,如何调用需要签名的函数尚不清楚。
第一次将我的 dapp 部署到 Ropsten 后,我需要调用 createItem(string name, uint price)
100 次来初始填充 items
数组。由于我不想在 Remix 中手动执行此操作,因此我想编写一个自动执行此操作的脚本。
看起来我需要 ethereumjs-tx
除了 web3js
才能在没有 MetaMask 的情况下以编程方式签署交易。我还需要 account
和 privateKey
。有了所有这些信息和官方 web3js 文档,我得出以下结论:
// Call an external function programatically
const web3 = new Web3(new Web3.providers.HttpProvider("https://ropsten.infura.io"))
const account = "ACCOUNT_ADDRESS"
const privateKey = new Buffer('PRIVATE_KEY', 'hex')
const contract = new web3.eth.Contract(abi, CONTRACT_ADDRESS, {
from: account,
gas: 3000000,
})
const functionAbi = contract.methods.myFunctionName(myArgument).encodeABI()
let estimatedGas
contract.methods.myFunctionNAme(myArgument).estimateGas({
from: account,
}).then((gasAmount) => {
estimatedGas = gasAmount.toString(16)
})
const txParams = {
gasPrice: '0x' + estimatedGas,
to: CONTRACT_ADDRESS,
data: functionAbi,
from: account,
}
const tx = new Tx(txParams)
tx.sign(privateKey)
const serializedTx = tx.serialize()
web3.eth.sendSignedTransaction('0x' + serializedTx.toString('hex')).
on('receipt', console.log)
代码运行s,但是txParams
其实少了一个键:nonce
。当你 运行 这样做时,你会收到以下错误:
Unhandled rejection Error: Returned error: nonce too low
这是我的问题:
- 这通常是做我想做的事情的正确方法吗?
- 如果 1 为真,我如何获取已部署合约的
nonce
参数?
参考文献:
- http://web3js.readthedocs.io/en/1.0/
- https://github.com/ethereumjs/ethereumjs-tx
- https://ethereum.stackexchange.com/questions/21402/web3-eth-call-how-can-i-set-data-param
- https://ethereum.stackexchange.com/questions/6368/using-web3-to-sign-a-transaction-without-connecting-to-geth
更新:
感谢 Adam,现在我学会了如何获得 nonce
。所以我添加了以下代码:
let nonce
web3.eth.getTransactionCount(account).then(_nonce => {
nonce = _nonce.toString(16)
})
const txParams = {
gasPrice: '0x' + gasPrice,
to: CONTRACT_ADDRESS,
data: functionAbi,
from: account,
nonce: '0x' + nonce,
}
但现在我一直运行宁到这个异常:
Unhandled rejection Error: Returned error: rlp: input string too long
for uint64, decoding into
(types.Transaction)(types.txdata).AccountNonce
Google 搜索没有帮助,只是让我找到了这个具有异常处理程序的文件 (https://github.com/ethereum/go-ethereum/blob/master/rlp/decode.go)。有谁知道如何解决这个问题?
大体看起来是正确的。我唯一的问题是你打算如何加载私钥?您将需要提示输入私钥,或者在密钥库中 import/read 并提示输入密码。您可以使用 keythereum to accomplish this (See the key import 部分作为示例代码)。
nonce
只是一个递增的数字,用于为一个账户订购交易。要获得正确的值,只需使用 web3.eth.getTransactionCount(account)
编辑 - 示例 运行 使用锁定帐户的 Ganache(--secure
选项):
SimpleContract.sol
pragma solidity ^0.4.19;
contract SimpleContract {
uint public val = 4;
function increment(uint amt) public {
val += amt;
}
function get() public constant returns (uint) {
return val;
}
}
test.js
const Web3 = require('web3');
const Tx = require('ethereumjs-tx');
const provider = new Web3.providers.HttpProvider("http://localhost:8545");
const web3 = new Web3(provider);
const account = '0x9a6d82ef3912d5ab60473124bccd2f2a640769d7'; // Ganache
const privateKey = Buffer.from('70f1384b24df3d2cdaca7974552ec28f055812ca5e4da7a0ccd0ac0f8a4a9b00', 'hex');
const contractAddress = '0x6dd7c1c13df7594c27e0d191fd8cc21efbfc98b4'; // Deployed manually
const abi = [{"constant":true,"inputs":[],"name":"val","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"get","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"amt","type":"uint256"}],"name":"increment","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}];
const contract = new web3.eth.Contract(abi, contractAddress, {
from: account,
gasLimit: 3000000,
});
const contractFunction = contract.methods.increment(3);
const functionAbi = contractFunction.encodeABI();
let estimatedGas;
let nonce;
console.log("Getting gas estimate");
contractFunction.estimateGas({from: account}).then((gasAmount) => {
estimatedGas = gasAmount.toString(16);
console.log("Estimated gas: " + estimatedGas);
web3.eth.getTransactionCount(account).then(_nonce => {
nonce = _nonce.toString(16);
console.log("Nonce: " + nonce);
const txParams = {
gasPrice: '0x09184e72a000',
gasLimit: 3000000,
to: contractAddress,
data: functionAbi,
from: account,
nonce: '0x' + nonce
};
const tx = new Tx(txParams);
tx.sign(privateKey);
const serializedTx = tx.serialize();
contract.methods.get().call().then(v => console.log("Value before increment: " + v));
web3.eth.sendSignedTransaction('0x' + serializedTx.toString('hex')).on('receipt', receipt => {
console.log(receipt);
contract.methods.get().call().then(v => console.log("Value after increment: " + v));
})
});
});
结果:
$ npm run my_test
> hello_world_dapp@1.0.0 my_test C:\cygwin\home\adamk\eth\hello_world_dapp
> node ./test.js
Getting gas estimate
Estimated gas: 6919
Nonce: 5
Value before increment: 19
{ transactionHash: '0xb6fdfc36cc32cb01a2be8832a387da586a44a37c1241bbf2979745536f206ed4',
transactionIndex: 0,
blockHash: '0xb6ee8d4e45585010d9a12d48aa37a478eb6021aad78599f1719cb424ab364bb5',
blockNumber: 10,
gasUsed: 26905,
cumulativeGasUsed: 26905,
contractAddress: null,
logs: [],
status: 1 }
Value after increment: 22
这是一个代码片段,用于在 rinkeby.In 上以类似的方式发送签名交易,您可以继续进行 ropsten 测试网络:
const Tx = require('ethereumjs-tx')
const Web3 = require('web3')
const web3 =new
Web3('https://rinkeby.infura.io/v3/9ce80a86c6c54d22aa821d0486a1a47d')
var account1 = '0xa00c70B72150D627cf53872eefD077079116B6a6'
var account2 = '0xD2a8aa318Fbc56995Db8C415BE6E40329DB1C56C'
const privateKey1 = Buffer.from(process.env.PRIVATE_KEY_1,'hex')
const privateKey2 = Buffer.from(process.env.PRIVATE_KEY_2,'hex')
web3.eth.getTransactionCount(account1,(err,txCount)=>{
const txObject = {
nonce:web3.utils.toHex(txCount),
to:account2,
value:web3.utils.toHex(web3.utils.toWei('0.1','ether')),
gasLimit:web3.utils.toHex(21000),
gasPrice:web3.utils.toHex(web3.utils.toWei('10','gwei')),
}
console.log(txObject)
const tx = new Tx(txObject)
tx.sign(privateKey1)
const serializedTransaction = tx.serialize()
const raw = '0x'+serializedTransaction.toString('hex')
web3.eth.sendSignedTransaction(raw,(err,txHash)=>{
console.log(txHash)
})
})
使用 web3js 很容易调用不需要签名的函数(例如,不更新合约状态的函数)。但是,除了手动解锁我的 MetaMask 钱包并在 Remix 环境中调用函数外,如何调用需要签名的函数尚不清楚。
第一次将我的 dapp 部署到 Ropsten 后,我需要调用 createItem(string name, uint price)
100 次来初始填充 items
数组。由于我不想在 Remix 中手动执行此操作,因此我想编写一个自动执行此操作的脚本。
看起来我需要 ethereumjs-tx
除了 web3js
才能在没有 MetaMask 的情况下以编程方式签署交易。我还需要 account
和 privateKey
。有了所有这些信息和官方 web3js 文档,我得出以下结论:
// Call an external function programatically
const web3 = new Web3(new Web3.providers.HttpProvider("https://ropsten.infura.io"))
const account = "ACCOUNT_ADDRESS"
const privateKey = new Buffer('PRIVATE_KEY', 'hex')
const contract = new web3.eth.Contract(abi, CONTRACT_ADDRESS, {
from: account,
gas: 3000000,
})
const functionAbi = contract.methods.myFunctionName(myArgument).encodeABI()
let estimatedGas
contract.methods.myFunctionNAme(myArgument).estimateGas({
from: account,
}).then((gasAmount) => {
estimatedGas = gasAmount.toString(16)
})
const txParams = {
gasPrice: '0x' + estimatedGas,
to: CONTRACT_ADDRESS,
data: functionAbi,
from: account,
}
const tx = new Tx(txParams)
tx.sign(privateKey)
const serializedTx = tx.serialize()
web3.eth.sendSignedTransaction('0x' + serializedTx.toString('hex')).
on('receipt', console.log)
代码运行s,但是txParams
其实少了一个键:nonce
。当你 运行 这样做时,你会收到以下错误:
Unhandled rejection Error: Returned error: nonce too low
这是我的问题:
- 这通常是做我想做的事情的正确方法吗?
- 如果 1 为真,我如何获取已部署合约的
nonce
参数?
参考文献:
- http://web3js.readthedocs.io/en/1.0/
- https://github.com/ethereumjs/ethereumjs-tx
- https://ethereum.stackexchange.com/questions/21402/web3-eth-call-how-can-i-set-data-param
- https://ethereum.stackexchange.com/questions/6368/using-web3-to-sign-a-transaction-without-connecting-to-geth
更新:
感谢 Adam,现在我学会了如何获得 nonce
。所以我添加了以下代码:
let nonce
web3.eth.getTransactionCount(account).then(_nonce => {
nonce = _nonce.toString(16)
})
const txParams = {
gasPrice: '0x' + gasPrice,
to: CONTRACT_ADDRESS,
data: functionAbi,
from: account,
nonce: '0x' + nonce,
}
但现在我一直运行宁到这个异常:
Unhandled rejection Error: Returned error: rlp: input string too long for uint64, decoding into (types.Transaction)(types.txdata).AccountNonce
Google 搜索没有帮助,只是让我找到了这个具有异常处理程序的文件 (https://github.com/ethereum/go-ethereum/blob/master/rlp/decode.go)。有谁知道如何解决这个问题?
大体看起来是正确的。我唯一的问题是你打算如何加载私钥?您将需要提示输入私钥,或者在密钥库中 import/read 并提示输入密码。您可以使用 keythereum to accomplish this (See the key import 部分作为示例代码)。
nonce
只是一个递增的数字,用于为一个账户订购交易。要获得正确的值,只需使用 web3.eth.getTransactionCount(account)
编辑 - 示例 运行 使用锁定帐户的 Ganache(--secure
选项):
SimpleContract.sol
pragma solidity ^0.4.19;
contract SimpleContract {
uint public val = 4;
function increment(uint amt) public {
val += amt;
}
function get() public constant returns (uint) {
return val;
}
}
test.js
const Web3 = require('web3');
const Tx = require('ethereumjs-tx');
const provider = new Web3.providers.HttpProvider("http://localhost:8545");
const web3 = new Web3(provider);
const account = '0x9a6d82ef3912d5ab60473124bccd2f2a640769d7'; // Ganache
const privateKey = Buffer.from('70f1384b24df3d2cdaca7974552ec28f055812ca5e4da7a0ccd0ac0f8a4a9b00', 'hex');
const contractAddress = '0x6dd7c1c13df7594c27e0d191fd8cc21efbfc98b4'; // Deployed manually
const abi = [{"constant":true,"inputs":[],"name":"val","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"get","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"amt","type":"uint256"}],"name":"increment","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}];
const contract = new web3.eth.Contract(abi, contractAddress, {
from: account,
gasLimit: 3000000,
});
const contractFunction = contract.methods.increment(3);
const functionAbi = contractFunction.encodeABI();
let estimatedGas;
let nonce;
console.log("Getting gas estimate");
contractFunction.estimateGas({from: account}).then((gasAmount) => {
estimatedGas = gasAmount.toString(16);
console.log("Estimated gas: " + estimatedGas);
web3.eth.getTransactionCount(account).then(_nonce => {
nonce = _nonce.toString(16);
console.log("Nonce: " + nonce);
const txParams = {
gasPrice: '0x09184e72a000',
gasLimit: 3000000,
to: contractAddress,
data: functionAbi,
from: account,
nonce: '0x' + nonce
};
const tx = new Tx(txParams);
tx.sign(privateKey);
const serializedTx = tx.serialize();
contract.methods.get().call().then(v => console.log("Value before increment: " + v));
web3.eth.sendSignedTransaction('0x' + serializedTx.toString('hex')).on('receipt', receipt => {
console.log(receipt);
contract.methods.get().call().then(v => console.log("Value after increment: " + v));
})
});
});
结果:
$ npm run my_test
> hello_world_dapp@1.0.0 my_test C:\cygwin\home\adamk\eth\hello_world_dapp
> node ./test.js
Getting gas estimate
Estimated gas: 6919
Nonce: 5
Value before increment: 19
{ transactionHash: '0xb6fdfc36cc32cb01a2be8832a387da586a44a37c1241bbf2979745536f206ed4',
transactionIndex: 0,
blockHash: '0xb6ee8d4e45585010d9a12d48aa37a478eb6021aad78599f1719cb424ab364bb5',
blockNumber: 10,
gasUsed: 26905,
cumulativeGasUsed: 26905,
contractAddress: null,
logs: [],
status: 1 }
Value after increment: 22
这是一个代码片段,用于在 rinkeby.In 上以类似的方式发送签名交易,您可以继续进行 ropsten 测试网络:
const Tx = require('ethereumjs-tx')
const Web3 = require('web3')
const web3 =new
Web3('https://rinkeby.infura.io/v3/9ce80a86c6c54d22aa821d0486a1a47d')
var account1 = '0xa00c70B72150D627cf53872eefD077079116B6a6'
var account2 = '0xD2a8aa318Fbc56995Db8C415BE6E40329DB1C56C'
const privateKey1 = Buffer.from(process.env.PRIVATE_KEY_1,'hex')
const privateKey2 = Buffer.from(process.env.PRIVATE_KEY_2,'hex')
web3.eth.getTransactionCount(account1,(err,txCount)=>{
const txObject = {
nonce:web3.utils.toHex(txCount),
to:account2,
value:web3.utils.toHex(web3.utils.toWei('0.1','ether')),
gasLimit:web3.utils.toHex(21000),
gasPrice:web3.utils.toHex(web3.utils.toWei('10','gwei')),
}
console.log(txObject)
const tx = new Tx(txObject)
tx.sign(privateKey1)
const serializedTransaction = tx.serialize()
const raw = '0x'+serializedTransaction.toString('hex')
web3.eth.sendSignedTransaction(raw,(err,txHash)=>{
console.log(txHash)
})
})