17

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?

3
  • 4
    Related: ethereum.stackexchange.com/questions/1187/…
    – eth
    Oct 26, 2016 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, 2016 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, 2017 at 23:57

3 Answers 3

11

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())

2
  • 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, 2016 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, 2016 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...

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

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