2

I'm trying to find an easy pattern to revert/rollback a write transaction.

The first idea was to log a GenericErrorEvent but then I noticed that it will never be emited if any require/revert/assert makes a "rollback".

On the truffle tests I can easily capture the errors within a try-catch, but an independent client listening for events would never be able to properly capture those same errors.

Since it's important to me that third clients can capture or listen for errors I'm just prechecking as much as possible in the function preconditions tests, log an error and just return from the public/external function if something goes wrong.

This works for simple scenarios but is very "manual" and changing the order of execution could actually introduce errors. Neither it allows to rollback from some error deeper in the stack once passed the first function precoditions.

I'm wondering whether there is a "magical" trick to log an error event and rollback to initial state with no manual coding.

The same question from another point of view:

Suppose we have next architecture:

JS client1 -> geth_node1 ... (ethereum net) ... geth_node2 <- JS Client2

JS Client1 sends a new signed transaction to its local node geth_node1. At some point the transaccion is processed by some mining node and the assert/revert/require is executed. There is a new mined transaction in the blockchain that spends a few weis from JS_client1 and JS_client1 will receive an string with the assert/revert/require error. Still, since no event is emmited JS_Client2 will have a hard time to be notified of such error.

It's it possible to (somehow) monitor at least all transactions signed by client1 and check when an assert/revert/require error was thrown?

1 Answer 1

1

You can throw custom error messages using require() || revert()

Example:

require(CONDITION, "ERROR_MSG");
revert("ERROR_MSG");

I wouldn't use assert() as they spend all the left over gas. revert() || require() at least refunds any unused gas.

Another example that I stole from Read The Docs:

   function buy(uint amount) public payable {
        if (amount > msg.value / 2 ether)
            revert("Not enough Ether provided.");
        // Alternative way to do it:
        require(
            amount <= msg.value / 2 ether,
            "Not enough Ether provided."
        );
        // Perform the purchase.
    }

~EDIT~ You question has evolved a lot from the initial point

Client 1 can pass Client 2 each TX Hash of their submitted transactions.

Client 2 can now ping the EVM for the status of these transactions.

"status": true,

Source

const receipt = web3.eth.getTransactionReceipt('0x9fc76417374aa880d4449a1f7f31ec597f00b1f6f3dd2d66f4c9c6c445836d8b')
.then(console.log);

> {
  "status": true,
  "transactionHash": "0x9fc76417374aa880d4449a1f7f31ec597f00b1f6f3dd2d66f4c9c6c445836d8b",
  "transactionIndex": 0,
  "blockHash": "0xef95f2f1ed3ca60b048b4bf67cde2195961e0bba6f70bcbea9a2c4e133e34b46",
  "blockNumber": 3,
  "contractAddress": "0x11f4d0A3c12e86B4b5F39B213F7E19D048276DAe",
  "cumulativeGasUsed": 314159,
  "gasUsed": 30234,
  "logs": [{
         // logs as returned by getPastLogs, etc.
     }, ...]
}

To get the actual error message, please see this previously answered question:

How to catch solidity contract revert message using web3j

6
  • Since assert/revert/require spend gas that means that they actually reflect the error in a new transaction (not the transaction I would like but a new one that just spends some of my ethers). But now the question is, if I have next architecture: client1 -> node1 <- (... ethereum network ...) -> node2 <- client2 How can client2 be aware of errors (assert/revet/require) triggered by transactions from client1 if the genericError is never written to the logs?
    – earizon
    Commented Mar 22, 2019 at 10:57
  • Your first sentence, I do not understand. A new transaction? That's not correct. There is no new transaction. It rolls back your current transaction so no state changes take effect. Revert & require will refund your unused gas. Assert does not. Client 1 needs to pass the tx hash to client 2. Client 2 can now listen for that tx result. Commented Mar 22, 2019 at 15:50
  • updated my response above Commented Mar 22, 2019 at 16:15
  • As I understand, if there is some unused gas, there must be some used gas, and that means a new TX is mined to reflect the decrease in ethers. (I have not found any clear document in the official Ethereum/Solidity)
    – earizon
    Commented Mar 24, 2019 at 13:01
  • Your first statement is correct. Unused gas does mean used gas exists. But this gas amount was sent with the original transaction (upper limit ceiling & gas price). Gas is what powers your transaction and allows it to execute. The EVM tracks this tally of gas units as it pops opcode commands from the stack. It is not a separate transaction. Commented Mar 24, 2019 at 15:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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