14

This keeps coming up on the gitter forums so I thought I'd ask and answer this question: How do you know when you've run out of gas?

12

There currently isn't a clear signal that you've run out of gas. A log message is being added to some future release.

Currently what I do is check to see if gasSent == gasUsed. This requires your code to remember the amount of gas you sent on a particular transaction and wait for the transaction to be mined. If gasSent == gasUsed, you most likely ran out of gas. In my javascript code I throw an exception. This has saved me several embarrassing forum posts and quite a bit of time.

In theory the transaction might be mined if it takes exactly the amount of gas used. There's no way I can think of to figure that out right now, perhaps someone could add to this answer. If you are running that close to the edge of running out of gas you should probably be sending more gas anyways.

I offer gratis some code I used to extend web3 functionality that waits for a transaction to be mined by a local node. It uses promises but adapting it to callback style shouldn't be difficult if you don't want to use promises. I plan on extending this in the future to await consensus from a plurality of nodes but I probably won't share that.

You won't be able to copy and paste this code in your code if you haven't instantiated a log or made a helper function to see if a var is set. If you haven't done those already, you should...

It should be much smaller code to write this for contract-contract calls, so I won't show that example. Same concept - just check to see if gasSent == gasUsed.

(And yes, Promise aficionados, I'm using a promise anti-pattern. I just haven't gotten around to rewriting it. The functionality is correct.)

Object.getPrototypeOf(web3.eth).awaitConsensus = function(txhash, gasSent) {
var deferred = Promise.pending();
ethP = this;
filter = this.filter('latest');         // XXX make async
callstack = new Error().stack;
filter.watch(function(error, result) {
    // this callback is called multiple times, so can't promise-then it
    ethP.getTransactionReceiptAsync(txhash).then(function(receipt)  {
    // XXX should probably only wait max 2 events before failing XXX 
    if (receipt && receipt.transactionHash == txhash) {
        filter.stopWatching();
        log.info({txreceipt: receipt});

        if (js.isSet(gasSent)) {
            // note corner case of gasUsed == gasSent.  It could
            // mean used EXACTLY that amount of gas and succeeded.
            // this is a limitation of ethereum.  Hopefully they fix it
            if (receipt.gasUsed >= gasSent) {
                log.error({ badReceipt: receipt });
                log.error({ originalStack: callstack });
                throw(Error("ran out of gas, transaction likely failed!"
                                                                + callstack));
            }
        }

        deferred.resolve(receipt);
    }
    });
});
return deferred.promise.timeout(60000, "awaitConsensus timed out after 60000ms")
        .catch(function(e) {
            log.error(e);
            process.exit(1);
        });

}

  • Receipts now have a status field (I updated my answer) and you may be interested in updating the code :) – eth Oct 17 '17 at 7:37
  • Yes, a lot of stuff going out of date with web3.js v1.0, probably in thanks to lots of feedback on this forum. Not sure what to do with those. Perhaps should start meta-discussion rather than one for each relevant question. – Paul S Oct 17 '17 at 17:54
  • Good point about web3.js v1.0 and posting it on meta. My comment here was you can remove the timer and just check the status field in Byzantium. – eth Oct 18 '17 at 6:51
10

Since block 4370000 (Byzantium) eth.getTransactionReceipt(transactionHash) will return a status field that has a value of 0 when a transaction has failed and 1 when the transaction has succeeded.

Here's an example showing the status field:

{ blockHash: '0xb1fcff633029ee18ab6482b58ff8b6e95dd7c82a954c852157152a7a6d32785e',
  blockNumber: 4370000,
  contractAddress: null,
  cumulativeGasUsed: 21000,
  gasUsed: 21000,
  logs: [],
  logsBloom: '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
  root: null,
  status: 1, // **** HERE IS THE STATUS FIELD *****
  transactionHash: '0x1421a887a02301ae127bf2cd4c006116053c9dc4a255e69ea403a2d77c346cf5',
  transactionIndex: 0 }

(Blocks before 4370000 will have a status of null.)

More details here.

  • I am using web3j Neither transaction receipt nor transaction object has field status – rahul Oct 30 '17 at 9:54
  • @rahul I'm not sure about the status of the Java implementations as they are primarily community efforts; a suggestion is to file an issue in their Github repo. – eth Nov 2 '17 at 9:24
  • are there other reasons that a transaction can fail besides out of gas? If so, then there's still some additional checks for determining why the transaction failed. – Paul S Nov 17 '18 at 0:52
  • @PaulS The other errors, for example invalid opcode, won't be encountered if a decent compiler is used ethereum.stackexchange.com/questions/2307/… Yes, it's possible that a status of 0 means something else than Out of Gas but currently people interacting with handcrafted EVM assembly is rare. Still helpful that you point this out, and a reference for errors is ethereum.github.io/yellowpaper/paper.pdf with search term "exceptional". – eth Jan 3 at 12:45
2

Here is my Python code to check this using Populus and web3.py:

from web3 import Web3
from populus.utils.transactions import wait_for_transaction_receipt

class TransactionConfirmationError(Exception):
    """A transaction was not correctly included in blockchain."""


def confirm_transaction(web3: Web3, txid: str, timeout=60) -> dict:
    """Make sure a transaction was correctly performed.

    Confirm that

    * The transaction has been mined in blockchain

    * The transaction did not throw an error (used up all its gas)

    http://ethereum.stackexchange.com/q/6007/620

    :raise TransactionConfirmationError: If we did not get it confirmed in time
    :return: Transaction receipt
    """

    try:
        receipt = wait_for_transaction_receipt(web3, txid, timeout)
    except Timeout as e:
        raise TransactionConfirmationError("Could not confirm tx {} within timeout {}".format(txid, timeout)) from e

    tx = web3.eth.getTransaction(txid)

    if tx["gas"] == receipt["gasUsed"]:
        raise TransactionConfirmationError("Transaction failed (out of gas, thrown): {}".format(txid))

    return receipt
0

There are 2 main cases of errors after a transaction has been mined

  • the evm run out of gas: indeed the best way to test is gasUsed==gas
  • the deployed contract failed to deploy: getCode(address)==0x

For systematic use of these cases I have wrap these in a npm package ethereum-web3-plus for who wants.

it is used like this:

web3.waitFor(  web3.newInstanceTx("Example"), // your txHash
             function(tx, contract, error) {
                  console.log("callback:", tx, contract, error);
                  // Possible errors : full gas used: <gas used>
                  //                   created contract has no bytecodes
                  if(contract) E = web3.instanceAt("Example", contract);
             } );
-2

Perhaps a naive question - what is the reason this is not just included as a flag in the transaction receipt. the receipt is generated once the block is mined (hence the txn receipt contains the block number) and also contains the gas used so clearly at the time the receipt is generated the transaction has been run..so why does the receipt not require a confirmation of whether the transaction completed? Seems a bit scary that contract calls can execute with such little visibility around whether the state was indeed updated per the expected contract behaviour

  • Hi there. I'm afraid you'll need to ask this as a separate question, rather than posting as an answer to an existing question. – Richard Horrocks Mar 20 '17 at 11:27
  • Welcome to Ethereum! If you have another question, please ask it by clicking the Ask Question button. – Waqar Lim Mar 20 '17 at 12:47

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.