Error: VM Exception while processing transaction: revert.
By: Veronica Coutts
Tags: Ethereum, Bugs, Blockchain, Debugging, Solidity

Error: VM Exception while processing transaction: revert.

Problem-solving in Solidity can be a nightmare. It lacks a console.log() and even if there was one, Solidity by nature reverts the entire state and you would not get it anyway. Then there is Error: VM Exception while processing transaction: revert, the most common and frustrating error of them all. Below is a checklist to ensure that you cover all the basics when debugging Solidity errors.

Before going through the debugging list you should have your contract in Remix. Preferably have it connected to a local blockchain instance (such as Ganache) so that you can use the Truffle debugger or the dApp tools debugger.

Remember that when debugging on Remix any code changes you make means that you need to redeploy your contracts. If you have a couple of contracts that need to talk to each other or have a rather tedious inheritance structure, try using the truffle-flattener to take away some of the frustration of getting your contracts set up on Remix.

When a transaction reverts in your command line (where you are running your compiler) you should notice an error stack that looks something like:

Error: VM Exception while processing transaction: revert Market registered

at getResult … json-rpc-provider.js:40:21)

at exports.XMLHttpRequest.request … web.js:111:30)

at exports.XMLHttpRequest.dispatchEvent … XMLHttpRequest.js:591:25)

at setState … XMLHttpRequest.js:610:14)

at IncomingMessage.<anonymous> … XMLHttpRequest.js:447:13)

at IncomingMessage.emit (events.js:187:15)

at endReadableNT (_stream_readable.js:1081:12)

at process._tickCallback (internal/process/next_tick.js:63:19)

Sometimes these stacks do not help you in the slightest (like in the above example), and other times they will tell you where in the contract it failed, with the line number first, followed by the column within that line.

at YourContract…the error…YourContract.sol:111:21)

This can be very useful to start getting an idea of where and why your contract call is failing.

Solidity errors and what they mean.

Glossary:

Wrapping over/under: Numbers in Solidity have limited storage space. If you try to make a number bigger than its allocated storage space it will clock over to its smallest value.

Counter from google

These are the errors:

OUT_OF_GAS: "out of gas" ⇐ Your contract call used too much gas. There are a couple of reasons this could occur, such as a recursive call (function A calls function B and then function B calls function A…), your function is doing a calculation that is too complex, as well as calling a chain of functions that is too long.

⇐ Your contract call used too much gas. There are a couple of reasons this could occur, such as a recursive call (function A calls function B and then function B calls function A…), your function is doing a calculation that is too complex, as well as calling a chain of functions that is too long. STACK_UNDERFLOW: "stack underflow" ⇐ A number wrapped under and is now at its max value. The minimum value of a uint8 is of course 0, and the max is 255. So if we take a uint8 number that is 0 and we minus 1, it will underflow to the max value. I.e:

uint8 goingToUnderflow = 0;

goingToUnderflow - 1;

goingToUnderflow is now equal to 255.

STACK_OVERFLOW: "stack overflow" ⇐ A number somewhere wrapped over and is now at its minimum value. The max value of a uint8 is 255. So if we have a uint8 set to 255 and we add 1, it will underflow and the value will be 1. E.g:

uint8 goingToOverflow = 255;

goingToOverflow + 1;

goingToOverflow is now equal to 1.

INVALID_JUMP: "invalid JUMP" ⇐ Invalid jumps burn all remaining gas like an assert() . This error happens when a function call jumps out of bounds (e.g array out of bounds). This is done to punish the caller for attempting to do something they weren't supposed to do. “Jump” is an assembly term for going from one section of code to another. The error is basically a function call that is invalid at an assembly level.

⇐ Invalid jumps burn all remaining gas like an . This error happens when a function call jumps out of bounds (e.g array out of bounds). This is done to punish the caller for attempting to do something they weren't supposed to do. “Jump” is an assembly term for going from one section of code to another. The error is basically a function call that is invalid at an assembly level. INVALID_OPCODE: "invalid opcode" ⇐ Somewhere you are trying to execute opcode that does not exist. The opcode can be a function that does not exist or a revert (reverts execute a non-existent opcode).

⇐ Somewhere you are trying to execute opcode that does not exist. The opcode can be a function that does not exist or a revert (reverts execute a non-existent opcode). REVERT: "revert" ⇐ Something somewhere broke. This revert will return the remaining gas. The most common error.

⇐ Something somewhere broke. This revert will return the remaining gas. The most common error. STATIC_STATE_CHANGE: "static state change" ⇐ If you have a view function and you try to change a state value you will get this error.

All hope is lost. Where to start?

Flatten your contract by running the command: truffle-flattener.\contracts\YourContracts.sol >> flatContract.txt on your contract. The >> flatContract.txt will save the flattened contract in the .txt file. If you don’t do this it will print the flat contract in the console. Put your flattened contracts into Remix. A simple copy paste, and naming the file. Connect your Remix to your local blockchain. In the run tab, select environment and ensure that it is set to Web3 provider. A pop up will appear that will ask you if you are sure (you are) and ask for the local host port, the default is 8545. Deploy your contract. In the run tab there is a drop down under the value field, where you can select your contract and then either enter the constructor fields (besides the deploy button is a drop down which makes it easier) or enter the address if your contract is deployed on a test net (make sure you are connected to the test net and that you have the code).

Run the failing function.

Changes to try in your code: change the code, re-deploy it in Remix, and then run the failing function again and see if it passed. If it did then Bob’s your uncle, you’ve identified the problem. Go and fix it!

Add an error message to your revert s. This should go without saying but your reverts should all have error messages. Don’t forget to re-deploy your contract after every code change. E.g: revert(theCheck, "The check is failing") . If you have any assert s, comment them out. If your assert is failing (and they should never be) there is a nice bug somewhere waiting for you. We can take a better look at this in the debugging stage. Remember what you change so that you can re-implement it if it’s not what’s making the contract call fail. Running out of gas? Running out of gas means something, somewhere in your contracts function is either being called over and over until it finishes your allocated gas, or a function call is doing something horribly wrong and is eating all the gas. Whatever the cause, something is doing something it shouldn’t. It will most likely become apparent in the debugging. Check the obvious: values and modifiers. If you have a modifier, are you sure that it’s not failing? Check that all of your involved values are what you expect them to be by making them public.

Still scratching your head? That’s okay, we still have the debugger!

Time to jump into a debugger to figure this out.

Re-implement all the code that you have previously removed. If none of it was making the function fail then it should go back in. Run the failing function again and grab the transaction hash from Remix.

2. Run the Truffle debugger in a command line in your projects directory. Make sure in your truffle.js local host address is the same as your Ganache local port. Run truffle debug 0xf where 0xf is your transaction hash. (They look like this: 0x1ed2fcb0d555e1398287bd2ae0319d623441a1113446b848cc9bf3148314bc54 )

3. The truffle debugger (or the dApp tool debugger) allows you to step through your function calls one call at a time. You can see exactly where the function call fails. Keep in mind that these tools are still being built so some of the functionality may not work seamlessly just yet.

You will be able to see where your contract call fails because it will be at the end of your step through. If it’s failing on something that shouldn’t fail, like an event or a blank line of code, chances are you are running out of gas.

If all else fail.

If you really don’t know why it’s failing and can’t see why it should be, try deploying it on a test net. As much as this may seem counter-intuitive, the tools we are using are still relatively in their infancy. The tools themselves could be the bugs. Deploy your contracts to your favourite test net and try interacting with them through Remix. It should be noted though that it is rarely the tool and if it is the tool you should make a detailed bug report on the respective tools GitHub so that they can fix it.

Good (bug) hunting!

This article would not have been possible without the amazing developer team at Linum Labs! A special shout out to our lead blockchain developer Ryan Noble, who played an instrumental part in writing this article.

Tools

Resources: