将已签名的交易发送给Ropsten

时间:2018-02-17 07:07:33

标签: ethereum web3js

使用web3js来调用不需要签名的函数(例如不更新合同状态的函数)很容易。但是,除了手动解锁我的MetaMask钱包和调用Remix环境中的函数之外,还不清楚如何调用需要签名的函数。

首次将我的dapp部署到Ropsten之后,我需要先调用createItem(string name, uint price) 100次来填充items数组。由于我不想在Remix中手动执行,我想编写一个自动执行此操作的脚本。

看起来除了ethereumjs-tx之外我还需要web3js以编程方式签署事务而无需MetaMask。我还需要拥有accountprivateKey。有了所有这些信息和官方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)

代码会运行,但txParams实际上缺少一个密钥:nonce。运行此操作时,会出现以下错误:

Unhandled rejection Error: Returned error: nonce too low

以下是我的问题:

  1. 这通常是我正在做的事情的正确方法吗?
  2. 如果1为真,我如何获得部署合同的nonce参数?
  3. 参考文献:

    1. http://web3js.readthedocs.io/en/1.0/
    2. https://github.com/ethereumjs/ethereumjs-tx
    3. https://ethereum.stackexchange.com/questions/21402/web3-eth-call-how-can-i-set-data-param
    4. https://ethereum.stackexchange.com/questions/6368/using-web3-to-sign-a-transaction-without-connecting-to-geth
    5. 更新

      感谢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,
      }
      

      但现在我一直遇到这个例外:

        

      未处理拒绝错误:返回错误:rlp:输入字符串太长   对于uint64,解码成   (types.Transaction)(types.txdata).AccountNonce

      除了让我找到具有异常处理程序的此文件(https://github.com/ethereum/go-ethereum/blob/master/rlp/decode.go)之外,Google搜索没有帮助。有谁知道如何解决这个问题?

2 个答案:

答案 0 :(得分:2)

一般看起来都是正确的。我唯一的问题是你打算如何加载私钥?您将需要提示输入私钥,或者在密钥库中导入/读取并提示输入密码。您可以使用keythereum来完成此操作(请参阅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

答案 1 :(得分:0)

这是一个用于在rinkeby上发送已签名交易的代码段。以类似的方式,您可以继续进行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)
})

})