6

Calls work but transactions throw the error -

Error: Invalid JSON RPC response: ""

I am using web3 v0.19.0 & Truffle v3.4.9. Deploying contract using truffle,
truffle migrate --network ropsten
which successfully provides api and contract address.

My web3 provider and ropsten network (infura node) are defined in truffle.js on top of react-auth-box project.

I open truffle console --network ropsten, and define web3 -

var Web3 = require('web3')
let web3 = new Web3()
web3.setProvider(new web3.providers.HttpProvider('https://ropsten.infura.io/my_access_token_here')) 

There is no default account
web3.eth.defaultAccount (returns null)
web3.eth.accounts (returns [])

Set default account,
web3.eth.defaultAccount = '0xpersonalaccount'

Define contract instance,
let contract = web3.eth.contract(abi).at(address)

All good so far and Calls work -
contract.checkIdExists.call(1, {'from': account, 'to': address})
(Returns '0x0000000000')

contract.fetchDataById.call(1, {'from': account, 'to': address})
(Returns '0x')

1. Transactions fail -
contract.addRecord.sendTransaction(1, 'fjdnjsnkjnsd', '03:00:21 12-12-12', 'true', '', {'from': account, 'to': address})

Error: Invalid JSON RPC response: "" at Object.InvalidResponse (/var/www/html/react-auth-box/node_modules/web3/lib/web3/errors.js:38:16) at HttpProvider.send (/var/www/html/react-auth-box/node_modules/web3/lib/web3/httpprovider.js:91:22) at RequestManager.send (/var/www/html/react-auth-box/node_modules/web3/lib/web3/requestmanager.js:58:32) at Eth.send [as sendTransaction] (/var/www/html/react-auth-box/node_modules/web3/lib/web3/method.js:145:58) at SolidityFunction.sendTransaction (/var/www/html/react-auth-box/node_modules/web3/lib/web3/function.js:167:26) at evalmachine.:1:20 at ContextifyScript.Script.runInContext (vm.js:53:29) at Object.runInContext (vm.js:108:6) at TruffleInterpreter.interpret (/home/shivam/.npm-global/lib/node_modules/truffle/build/cli.bundled.js:213786:17) at bound (domain.js:301:14)

2. Makes me think I probably have to unlock account first (do I?)
web3.personal.unlockAccount(account, password)

Error: Invalid JSON RPC response: "" at Object.InvalidResponse (/var/www/html/react-auth-box/node_modules/web3/lib/web3/errors.js:38:16) at HttpProvider.send (/var/www/html/react-auth-box/node_modules/web3/lib/web3/httpprovider.js:91:22) at RequestManager.send (/var/www/html/react-auth-box/node_modules/web3/lib/web3/requestmanager.js:58:32) at Personal.send [as unlockAccount] (/var/www/html/react-auth-box/node_modules/web3/lib/web3/method.js:145:58) at evalmachine.:1:15 at ContextifyScript.Script.runInContext (vm.js:53:29) at Object.runInContext (vm.js:108:6) at TruffleInterpreter.interpret (/home/shivam/.npm-global/lib/node_modules/truffle/build/cli.bundled.js:213786:17) at bound (domain.js:301:14) at REPLServer.runBound [as eval] (domain.js:314:12)

Running pretty clueless by now. Any support will be appreciated. Thanks!

  • Infura doesn't support sendTransaction, you have to manage the keys on your side and send signed transaction using sendRawTransaction. To sign a transaction you can use ethereumjs-tx github.com/ethereumjs/ethereumjs-tx – Ismael Sep 24 '17 at 18:18
  • Thanks Ismael, that helped! I'm assuming infura node does not have the defaultAccount (or some such thing) - a little confused about the exact reasoning. – Shivam D Sep 27 '17 at 5:24
  • @ShivamD I believe the reason for this is because Infura doesn't hold your private keys. This means that they can't sign a transaction on your behalf. See this post for more information: ethereum.stackexchange.com/questions/6905/… – hhppj Mar 7 at 22:26
3

Let me post the complete answer here (Credits to @Ismael).

Relevant packages -
web3@0.18.2
ethereumjs-tx@1.3.3
crypto-js

const Web3 = require('web3')  
let web3 = new Web3()  
web3.providers.HttpProvider('https://ropsten.infura.io/my_access_token_here'))  
let contract = web3.eth.contract(abi).at(address)  
var coder = require('web3/lib/solidity/coder')  
var CryptoJS = require('crypto-js')  
var privateKey = new Buffer(myPrivateKey, 'hex')  

var functionName = 'addRecord'  
var types = ['uint','bytes32','bytes20','bytes5','bytes']  
var args = [1, 'fjdnjsnkjnsd', '03:00:21 12-12-12', 'true', '']  
var fullName = functionName + '(' + types.join() + ')'  
var signature = CryptoJS.SHA3(fullName,{outputLength:256}).toString(CryptoJS.enc.Hex).slice(0, 8)  
var dataHex = signature + coder.encodeParams(types, args)  
var data = '0x'+dataHex  

var nonce = web3.toHex(web3.eth.getTransactionCount(account))  
var gasPrice = web3.toHex(web3.eth.gasPrice)  
var gasLimitHex = web3.toHex(300000) (user defined)  
var rawTx = { 'nonce': nonce, 'gasPrice': gasPrice, 'gasLimit': gasLimitHex, 'from': account, 'to': address, 'data': data}  
var tx = new Tx(rawTx)  
tx.sign(privateKey)  
var serializedTx = '0x'+tx.serialize().toString('hex')  
web3.eth.sendRawTransaction(serializedTx, function(err, txHash){ console.log(err, txHash) })   

(Returns '0xf802614fd6a53cb372752634630265063d0b48fec12ea8f5ed363de1d4bd372d')

web3.eth.getTransaction('0xf802614fd6a53cb372752634630265063d0b48fec12ea8f5ed363de1d4bd372d', console.log)

(Prints transaction data)
(Refer here)

  • where is myPrivateKey typically stored on ubuntu when making it with geth – Barney Chambers Sep 2 at 12:05
0

Greater than web1.0 change.

var myPrivateKey = "xxxxxx";
var privateKey = new Buffer(myPrivateKey, 'hex')

var functionName = 'add token';
var types = ['uint', 'bytes32', 'string', 'bool', 'bytes'];
var args = [123, '0xdf3234', '03:00:21 12-12-12', true, '0xdf3234'];
var fullName = functionName + '(' + types.join() + ')';
var signature = CryptoJS.SHA3(fullName, { outputLength: 256 }).toString(CryptoJS.enc.Hex).slice(0, 8)
var dataHex = signature + Web3.eth.abi.encodeParameters(types, args)
var data = '0x' + dataHex;
// console.log(99, dataHex)

var rawTx = {
    nonce: Web3.utils.toHex(await Web3.eth.getTransactionCount(cfg.addr.accountA).then(data => data)),
    gasPrice: Web3.utils.toHex(await Web3.eth.getGasPrice().then(data => data)),
    gasLimit: Web3.utils.toHex(300000), // Web3.toHex(300000)
    // from: '',
    to: cfg.addr.accountB,
    value: Web3.utils.toHex(10 ** 16),
    data
}

var tx = new Tx(rawTx)
tx.sign(privateKey)
var serializedTx = '0x' + tx.serialize().toString('hex')

const res = await Web3.eth.sendSignedTransaction(serializedTx)
    .on('transactionHash', function (hash) {
        console.log(100, hash)
    })
    .on('receipt', function (receipt) {
        console.log(101, receipt)
        return receipt;
    })
    .on('confirmation', function (confirmationNumber, receipt) {
        // console.log(102, confirmationNumber, receipt)
    })
    .on('error', (e) => {
        console.log(103, e)
    })
0

Edit: Yes, when sending a transanction to Infura node it must be created and signed beforehand as it supports only eth_sendRawTransaction JSON RPC method.

It is not necessary to use ethereumjs-tx library anymore to make transactions, you can do it only with core web3.js 1.0, you don't need else,

Signing transactions automatically using only web3.js 1.0, using web3.eth.sendTransaction() with provided private key of the sending account:

When you specify web3.eth.defaultAccount add its private key in web3.eth.accounts.wallet (also referred to as a keystore).

...   
web3.eth.wallet.add('The private key of the sending account')
...

Otherwise web3.js API has no way to sign a transaction because it doesn't know where the defautlAccount private key is and will trow:

"Error: Returned error: The method eth_sendTransaction does not exist/is not available"

Note: You have to also consider the security implications of storing the private key of the user in his local browser storage.

So, for example, if you were to use a local TestRPC with locked accounts, to do the same thing, you could use unlockAccount function on the account that you want to use for signing and then sign a transaction, but in the variant explained above (adding a 'wallet') all you need to do is

...
web3.eth.defaultAccount = '0xpersonalaccount'
web3.eth.wallet.add('The private key of your personal account')
...

and now you can use web3.eth.sendTransaction() even without specifying a from: property and signing it afterwards since web3.js will sign it locally using the private key of that default account, and will send the transaction via web3.eth.sendSignedTransaction() automatically.

  • This doesn't work when calling a contract method via send or call – Paul Razvan Berg Feb 11 at 15:26
  • I can approve @PaulBerg's comment. I'm using methods.myMethod.send (see web3js.readthedocs.io/en/1.0/…) and it fails with "Error: Returned error: The method eth_sendTransaction does not exist/is not available" – Ronin Apr 5 at 8:52
  • @Ronin, @Paul, you can not use contract.methods.myMethod.send(), you will have to use web3.eth.sendTransaction() or web3.eth.sendSignedTransaction() to create and sign the transaction before sending it, as I described in my original answer. To use the eth_call you do not need to do anything related to account management except specifying the sender, as it is used only for calling read only (view) functions and does not modify state on the blockchain (basically not a transaction). – Nikolay Deshev Apr 8 at 6:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.