2

I am testing whether transactions that I'm signing offline are accepted by the Kovan network. I'm submitting the transactions using Web3.js (web3@1.20) as follows:

I connect to my Open Ethereum 3.0.1 node, and then use web3.eth.sendSignedTransaction. According to the documentation, this should return a PromiEvent. However, the events emitted by the submission are not captured and the promise never resolves. The transaction is submitted to the network nonetheless and is valid (it's a simple transaction sending funds from one non-contract account to another). One can find it in any blockexplorer, like for instance etherscan.

The code below does not behave as expected:

function sendTrans(_rawData,_txHash) {

    try {
            
            console.log("Before Asynch call");
                                    
              web3.eth.sendSignedTransaction(_rawData)
                .once('transactionHash', function(hash){ console.log("txHash", hash) })
                .once('receipt', function(receipt){ console.log("receipt", receipt) })
                .on('confirmation', function(confNumber, receipt){ console.log("confNumber",confNumber,"receipt",receipt) })
                .on('error', function(error){ console.log("error", error) })
                .then(function(receipt){
                    console.log("trasaction mined!", receipt);
                });
                
          console.log("After Asynch Call");
         }
    catch (error) {
      
      console.log("Error Sending Transaction", error.message);
         
    }
      return { response: "OK", transHash: _txHash };
  }

// result: the code does not trigger any emitter event. No logging to the console is made.

The code above just hangs, because the promise returned by sendSignedTransaction is never resolved. No event is received either, so the .on('receipt') is never triggered. However the transaction is successfully submitted to the network and mined. So the problem is not with the submission, but rather with the PromiEvent that web3.eth.sendSignedTransaction returns.

Anyone has any idea of why this behavior is happening?

  • Try const promiEvent = web3.eth.sendSignedTransaction(_rawData)... and then console.log(promiEvent) to ensure you get the right return object. – Mikko Ohtamaa Jun 22 at 8:26
  • @MikkoOhtamaa I took your advice. The promiEvent info is promiEvent Promise { <pending>, _events: Events <[Object: null prototype] {}> { transactionHash: EE { fn: [Function], context: [Circular], once: false } }, emit: [Function: emit], on: [Function: on], once: [Function: once], off: [Function: removeListener], listeners: [Function: listeners], addListener: [Function: on], removeListener: [Function: removeListener], removeAllListeners: [Function: removeAllListeners], _eventsCount: NaN } However, the eventEmitter still not firing. – Kevin Melberger Jun 22 at 14:52
  • @MikkoOhtamaa. All I want to do is get the transactionHash and return that to my client application. Let the transaction get broadcasted to the nodes, get picked up by a miner and then get mined. I can't wait around for 1+ minutes while the client app is blocked – Kevin Melberger Jun 22 at 14:54
  • Here is my PromiEvent + await code with Angular - known to be work gist.github.com/miohtama/… - I am hoping to release this as an open source library later, so hopefully you see if it is any different from your code. – Mikko Ohtamaa Jun 22 at 15:01
  • transaction hash comes from the wallet within 1-2 seconds. I think your promise might not be never resolved, so that's why any of the handlers are not firing. – Mikko Ohtamaa Jun 22 at 15:01
2

You can wrap web3.eth.sendSignedTransaction in a promise that will resolve when the transaction is calculated.

const hash = await new Promise(async (resolve) => {
  await web3.eth.sendSignedTransaction(serializedTx)
    .once('transactionHash', (hash) => {
      resolve(hash)
    })
  console.log('We\'ve finished')
})
console.log('Hash: ', hash)

Unfortunately due to https://github.com/ethereum/web3.js/issues/3204 web3 will wait for confirmation (if some task is pending node will wait until it is finished so removing await will not fix it).

If you want to skip web3 confirmation you have to use the eth_sendRawTransaction api and communicate directly with the provider's send (untested).

web3.currentProvider.send({
  jsonrpc: '2.0',
  method: 'eth_sendRawTransaction',
  params: ['0xf8648080....'],
  id: 253,
}, (err, result) => {
  if (err) {
    return reject(err)
  }
  return resolve(result)
})
| improve this answer | |
  • I get it. That's why it is so hard to get users to on-board using an Ethereum blockchain. The user experience is so difficult to manage (like waiting for minutes until the transaction is mined and a receipt is created). To implement this for a user would require some sophisticated polling code. – Kevin Melberger Jun 23 at 0:29
1

From the official documentation:

// using the promise
web3.eth.sendTransaction({
    from: '0xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe',
    to: '0x11f4d0A3c12e86B4b5F39B213F7E19D048276DAe',
    value: '1000000000000000'
})
.then(function(receipt){
    ...
});


// using the event emitter
web3.eth.sendTransaction({
    from: '0xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe',
    to: '0x11f4d0A3c12e86B4b5F39B213F7E19D048276DAe',
    value: '1000000000000000'
})
.on('transactionHash', function(hash){
    ...
})
.on('receipt', function(receipt){
    ...
})
.on('confirmation', function(confirmationNumber, receipt){ ... })
.on('error', console.error); // If a out of gas error, the second parameter is the receipt.

In your code, you seem to be attempting to use both the promise and the event emitter.

In addition to that, for the event emitter, you seem to be using once instead of on.

I'd start by fixing these two (getting rid of the then, and replacing once with on).

| improve this answer | |
  • I am using web3.js with both await and on in the same PromiEvent object and it works correctly. Furthermore once is supported. This is directly from web3.js documentation itself: web3js.readthedocs.io/en/v1.2.7/… – Mikko Ohtamaa Jun 22 at 8:25
  • 1
    @MikkoOhtamaa: There's no await in the question, there is a combination of then and on, which to my understanding of the docs, is wrong (only one of them should be used). – goodvibration Jun 22 at 8:37
  • @MikkoOhtamaa. I changed my code to the following. function sendTrans(_rawData,_txHash) { var respHash = "nothing"; try { console.log("Before Asynch call"); web3.eth.sendSignedTransaction(_rawData) .once('transactionHash', function(hash){ console.log("txHash", hash); respHash = hash; }); The value of respHash was "nothing". The event emitter didh not fire for transactionHash. I will try your other suggestion next. – Kevin Melberger Jun 22 at 14:43
  • @goodvibration I took your advice, changed my code to the following web3.eth.sendSignedTransaction(_rawData) .on('transactionHash', function(hash){ console.log("txHash", hash); respHash = hash; }); Still not getting the event emitter to fire. All I want to accomplish is to immediately return the tx hash and then send that result back to my client DAPP. I can't wait for more than 30 seconds for Ethereum to process my transaction. – Kevin Melberger Jun 22 at 14:56

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.