16

I'm using react to write the frontend for my dApp, and doing this to (attempt to) update react's state via an asynchronous sendTransaction call:

var thisJS = this
web3.eth.sendTransaction({from: adminAccount, to: contractAddress, value: web3.toWei(funds,"ether")}, function(error, result) {
  if (error) {
    console.error(error)
  } else {
    console.log("Send transaction successful " + result)
    const contractFunds = web3.fromWei(web3.eth.getBalance(contractAddress),"ether").toString()
    thisJS.setState({contractFunds: contractFunds})
  }
})    

That doesn't seem to quite work as I hoped as the react setState call doesn't (often) reflect the new funds at the contract's address. I must be doing something wrong - what's the proper way to wait for the transaction to be mined so I can correctly update react's state?

marked as duplicate by Badr Bellaj, Sebi, Paul S, Waqar Lim Nov 2 '16 at 11:38

This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.

  • 4
    Related: ethereum.stackexchange.com/questions/1187/… – eth Oct 26 '16 at 21:51
  • Related, but not quite the same, I believe, as that related to chain reorganisation, not the monitoring of standard blocks – glowkeeper Oct 27 '16 at 8:24
  • The problem is that the callback to sendTransaction is called as soon as the transaction is successfully submitted. It doesn't have to be mined. Once the transaction is mined, you need to call setState to update is mined. – 0xcaff Aug 15 '17 at 23:57
9

Use a filter in order to watch for changes. In this case, for example, you could:

web3.eth.filter('latest', function(error, result){
  if (!error) {
    thisJS.setState({contractFunds:
        web3.fromWei(web3.eth.getBalance(contractAddress),"ether").toString()})
  } else {
    console.error(error)
  }
});

Note: you will probably want to .stopWatching at some point (probably in componentWillUnmount())

  • 1
    Ooo - thanks! That looks promising - I should probably run off and readthedocs before commenting, but is 'latest' a standard event corresponding to the latest block? – glowkeeper Oct 27 '16 at 7:28
  • Yup - should have definitely followed the link you provided: The string "latest" or "pending" to watch for changes in the latest block. Cool - thanks again! – glowkeeper Oct 27 '16 at 7:31
9

I created a Gist that polls the node until the transaction has been mined:

web3.eth.getTransactionReceiptMined = function (txnHash, interval) {
    var transactionReceiptAsync;
    interval = interval ? interval : 500;
    transactionReceiptAsync = function(txnHash, resolve, reject) {
        try {
            var receipt = web3.eth.getTransactionReceipt(txnHash);
            if (receipt == null) {
                setTimeout(function () {
                    transactionReceiptAsync(txnHash, resolve, reject);
                }, interval);
            } else {
                resolve(receipt);
            }
        } catch(e) {
            reject(e);
        }
    };

    if (Array.isArray(txnHash)) {
        var promises = [];
        txnHash.forEach(function (oneTxHash) {
            promises.push(web3.eth.getTransactionReceiptMined(oneTxHash, interval));
        });
        return Promise.all(promises);
    } else {
        return new Promise(function (resolve, reject) {
                transactionReceiptAsync(txnHash, resolve, reject);
            });
    }
};

If it takes longer than 50 blocks, I noticed that it fails, though.

3

Following Derek Tiffany's suggestion, I came up with the following answer.

First, I set up a filter for the latest block:

const latestFilter = web3.eth.filter('latest')

Then, I get the transaction hash and setState to that hash:

const tx = web3.eth.sendTransaction({from: adminAccount, to: contractAddress, value: web3.toWei(funds,"ether")})
this.setState({txHash: tx})

Finally, I spin up a watch for the filter and try and match my state's transaction hash. If I get a match, I update the state accordingly:

_latestBlock() {
    const thisJs = this
    const filter = this.state.latest
    const web3 = this.state.web3
    const contractAddress = this.state.contractAddress
    const adminAccount = this.state.account 

    filter.watch(function (error, result) {
      if (error) {
        console.error(error)
      } else {
        let thisTx = thisJs.state.txHash 
        console.log("State transaction " + thisTx)
        const block = web3.eth.getBlock(result, true)
        let transactions = block.transactions
        for(let i = 0; i < transactions.length; i++)
        {
          console.log("block transaction " + transactions[i].hash)
          if( thisTx == transactions[i].hash ){
            console.log("Got match!")
            const contractFunds = web3.fromWei(web3.eth.getBalance(contractAddress),"ether").toString()
            thisJs.setState({contractFunds: contractFunds})
            break
          }
        }
      }
    })
} 

So far, it seems to be working nicely...

  • I don't understand why you did const latestFilter = web3.eth.filter('latest') since you don't use the result? – Phillip Gibb May 2 '18 at 11:36
  • 1
    Hi @PhillipGibb - oops, I think there's a step missing! It's this: this.state = { latest: latestFilter } – glowkeeper May 3 '18 at 12:47

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