Debugging
One of the most common errors Ethereum smart contract developers face is evm: execution reverted
or one of its variations depending on the SDK being used.
Ethereum Virtual Machine (EVM) does not have access to system I/O, as a result there is no handy logging output for debugging purposes. When a transaction fails to execute or validate, there is no easy way to find out the internal states leading up to the failure.
To get the reason for the failure, EVM allows a past transaction to be "replayed" and for the internal states to be introspected at each step. You can use a number of tooling options to debug a failed transaction.
Remix Debugger UI
Remix is a browser based IDE for Solidity developers. Remix supports a debugger extension that can take a transaction hash and step through the low-level EVM opcodes executed during the transaction processing, and display all the internal states (memory stack, storage variables, etc.) in an easy-to-use UI.
Connecting Remix to Kaleido
Follow the instructions here to connect to a Kaleido node.
Install and activate Debugger
In the left navigation bar, click the Plugin Manager icon to install and activate the DEBUGGER module.
Once activated, the Debugger is visible in the left navigation bar.
Click the Debugger icon to go into the UI. Fill in the hash of the failed transaction and click Start debugging. While stepping through the instructions
use the Memory section to monitor possible error messages generated by require
statements, or the Storage section to see if the storage variables have the expected values.
Truffle debug
If you have the truffle project that contains the source code of the deployed contract, you can use the built-in command line based debugger.
From the truffle project root, truffle debug {txhash}
to launch the truffle command line debugger.
Follow the instructions here to specify truffle configurations to connect to a Kaleido node.
DIY with custom code
You can also use the eth_call
JSON RPC request to get the details of the transactions failure from the EVM. You must specify the same input parameters that were used to submit the original transaction, but instead of calling eth_sendTransaction
, use eth_call
so simulate the transaction execution and return the results.
A code snippet is provided below:
const ethers = require('ethers');
const appCred = `${user}:${password}`; // Kaleido application credentials
const connectionURL = 'u0abcdefgh-u012345678-rpc.us0-aws.kaleido.io'; // without protocol (https://)
const provider = new ethers.providers.JsonRpcProvider(`https://${appCred}@${connectionURL}`);
function hex_to_ascii(str1) {
var hex = str1.toString();
var str = '';
for (var n = 0; n < hex.length; n += 2) {
str += String.fromCharCode(parseInt(hex.substr(n, 2), 16));
}
return str;
}
async function reason(txhash) {
let tx = await provider.getTransaction(txhash);
if (!tx) {
console.log('tx not found');
} else {
let code = await provider.call(tx, tx.blockNumber);
let reason = hex_to_ascii(code.substr(138));
console.log('revert reason:', reason);
}
}
console.log(await reason());
REST API Gateway
Kaleido's REST API Gateway has built-in support for returning the error string when transactions failed.