17

Let's say I call functions from several external contracts in a function and check for their return values (given the fact they return a bool) either with require() or revert().

Pseudocode:

function storeDataInDifferentContracts() {
    require(address(firstContract).store(data));
    require(address(secondContract).store(otherData));
}

Lets say the first contract call returns true and everything was stored as planned but the second contract call throws an exception. Does the data in the first called contract get reverted through the require()/revert() opcode as well?

2 Answers 2

14

Yes. The original signed transaction from the externally owned account will fail. Consequently, updates down all adjacent branches will fail with it.

Hope it helps.

Update.

It depends on how the calling contract invokes the function that reverts.

pragma solidity 0.4.20;

contract Complainer {

    function doRevert() public pure returns(bool success) {
        revert(); // because let's fail
        return true;
    }

}

contract Adjacent {

    bool state;

    event LogStateChange(address sender, bool succeeded);

    function setState() public returns(bool success) {
        state = true;
        LogStateChange(msg.sender, true);
        return true;
    }
}

contract Originator {

    Complainer c;
    Adjacent a;

    event LogSucceeded(address sender, bool succeeded);
    event LogResult(address sender, bool adjacentResponse, bool complainerResponse);

    function Originator(address complainer, address adjacent) public {
        c = Complainer(complainer);
        a = Adjacent(adjacent);
    }

    function test() public returns(bool success) {
      LogSucceeded(msg.sender, true); // you only get this if the transaction did not fail   
      bool aResponse = a.setState();  // this would set an internal state and emit an event if it succeeded. Does not. 
      bool cResponse = c.doRevert();  // this will revert and undo the previous two steps including the adjacent contract
      LogResult(msg.sender, aResponse, cResponse); // does not happen because the transaction has failed
      return true; // does not happen
    }

    function test2() public returns(bool success) {
      LogSucceeded(msg.sender, true); // you only get this if the transaction did not fail   
      bool aResponse = a.setState();  // this will set an internal state and emit an event 
      bool cResponse = c.call(bytes4(sha3("doRevert()"))); // this will revert and return false
      LogResult(msg.sender, aResponse, cResponse); // this will happen because the transaction has not failed
      return true; // success
    }
}
2
  • Note that it will be any (sub-)calls made from the one that REVERTs will have their state reverted, but if you were to call this function not from an external signed transaction but from another contract it would not revert state changes made in the calling contract, the calling contract would have access to the error and be able to continue. Commented Mar 3, 2018 at 21:33
  • 1
    I added an example. Is that what you meant, or something else? Commented Mar 4, 2018 at 0:12
11

In this instance it helps not to think of smart contract code like typical in-memory computations. The blockchain only changes state when a transaction is written, nothing is 'done' until then. In the case of an exception anywhere in the code, the state change is reverted in full so all that gets written is essentially 'failed' (plus gas changes etc). It's like they never happened in the first place.

1
  • 1
    Excellent explanation of how to think about these concepts!
    – srt32
    Commented Oct 26, 2017 at 15:00

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.