[Ethereum] Need help signing a raw transaction with “ethereumjs-tx”

ethereumjs-txraw-transaction

Here's a NodeJS script where I'm trying to send a raw transaction. I've tried changing params in a lot of different ways and always get the same error. What am I missing? I'm following an example from here.

Script:

const Web3 = require('web3');
const Tx = require('ethereumjs-tx');
const config = require('./config');

const web3 = new Web3(new Web3.providers.HttpProvider(config.provider));
web3.eth.defaultAccount = "0x15568faf1cd21041acbb9f99d94dbe40e7f1a479";

const rawTx = {
  nonce: '0x00',                                     // 0
  gasPrice: '0x6FC23AC00',                           //30 GWei | 30 000 000 000 wei
  gasLimit: '0xF4240',                               //1 000 000
  to: '0x203D17B4a1725E001426b7Ab3193E6657b0dBcc6',
  value: '0x00',                                     // 0
  data: '0x00',                                      // 0
  chainId: '0x03'                                    // Ropsten
}

const tx = new Tx(rawTx);
const privateKey = Buffer.from("f7ef8432857eb502f9406282b6cb86219ea4973f5f9bb0605099cfc2c63a516a", 'hex');
tx.sign(privateKey);

const serializedTx = tx.serialize();
console.log(serializedTx.toString('hex')); //f865808506fc23ac00830f424094203d17b4a1725e001426b7ab3193e6657b0dbcc680002aa070f37febb101867fe6b5abb4b4a0bf6daadba43736164a08cd0413c4b29d4e05a0204248c3e9a826cac72e7be29a8cacce924e33b36a0a7e7096f6a86684884f18

web3.eth.sendRawTransaction(serializedTx.toString('hex'), function(err, hash) {
    console.log('Error:', err);
    console.log('Hash:', hash);
});

Error:

Error: Error: Invalid params
    at Object.InvalidResponse (/home/manid/Рабочий стол/test/Ethereum/send-raw-transaction/node_modules/web3/lib/web3/errors.js:35:16)
    at /home/manid/Рабочий стол/test/Ethereum/send-raw-transaction/node_modules/web3/lib/web3/requestmanager.js:86:36
    at XMLHttpRequest.request.onreadystatechange (/home/manid/Рабочий стол/test/Ethereum/send-raw-transaction/node_modules/web3/lib/web3/httpprovider.js:118:13)
    at XMLHttpRequestEventTarget.dispatchEvent (/home/manid/Рабочий стол/test/Ethereum/send-raw-transaction/node_modules/xhr2/lib/xhr2.js:64:18)
    at XMLHttpRequest._setReadyState (/home/manid/Рабочий стол/test/Ethereum/send-raw-transaction/node_modules/xhr2/lib/xhr2.js:354:12)
    at XMLHttpRequest._onHttpResponseEnd (/home/manid/Рабочий стол/test/Ethereum/send-raw-transaction/node_modules/xhr2/lib/xhr2.js:509:12)
    at IncomingMessage.<anonymous> (/home/manid/Рабочий стол/test/Ethereum/send-raw-transaction/node_modules/xhr2/lib/xhr2.js:469:24)
    at emitNone (events.js:91:20)
    at IncomingMessage.emit (events.js:185:7)
    at endReadableNT (_stream_readable.js:974:12)

When I try to broadcast this transaction using etherscan.io/pushTx I get the following error

Error! Unable to broadcast Tx : {"jsonrpc":"2.0","error":{"code":-32010,"message":"Invalid network id.","data":null},"id":1}.

Is it possible that the problem might be in that I use a private key generated for mainnet and specify ropsten as the chainId? I used keythereum to generate keys. Is there a difference in key pair generation for mainnet and ropsten?

When I change chainId field to "0x01" I get an error:

Error! Unable to broadcast Tx : {"jsonrpc":"2.0","error":{"code":-32010,"message":"Insufficient funds. The account you tried to send transaction from does not have enough funds. Required 30000000000000000 and got: 0.","data":null},"id":1}

This error makes sense, because this address has balance only on ropsten.

Best Answer

a few quick observations:

  • nonce is hard-coded

    • it should be calculated:
      let nonce = web3.eth.getTransactionCount(fromAccount)
      • where fromAccount is the address derived from the publicKey that is paired to the privateKey that is used to sign the raw transaction
    • on testnet, the starting nonce value is: 1048576
      so you would need to increment by this offset:
      if (rawTx.chainId && Number(rawTx.chainId) > 1) nonce += 1048576
    • then, converted to a hex-encoded string with '0x' prefix:
      nonce = web3.toHex(nonce)
  • your chainId is: 0x03.. which is testnet

    • you're submitting your raw transaction to a web service that transmits to mainnet
    • you should be using the alternate web service that transmits to testnet
  • web3.eth.defaultAccount shouldn't matter.. your privateKey determines the "from" address of sender

  • only indirectly related, but you also might want to check out another similar library: ethereumjs-tx-sign

update

corrected links for testnets:

chainId values:

  • list
  • 0x03 is ropsten