DEV Community

hamzairshad02
hamzairshad02

Posted on

Ethernaut Level 5 Walkthrough - Token

This level comes with a hint: odometer. An odometer is an instrument used for measuring the distance traveled by a vehicle. It usually has a starting value of 000000 and ends at 999999. Now what does it even have to do with our contract eh? For that we have to understand the contract first.

Let’s start by understanding the contract first.

The contract starts with two variables, first is “balances” which is a mapping of address as its key and uint as its value. The second is “totalSupply” which is a public uint. Remember that a uint declared is a uint256 by default which has a value from 0 to 2^256 which is equal to 115792089237316195423570985008687907853269984665640564039457584007913129639935. A starting value and an ending value just like an odometer.

mapping(address => uint) balances;
uint public totalSupply;
Enter fullscreen mode Exit fullscreen mode

Then comes the constructor of the contract which takes an _initialSupply value in its parameter and declares it equal to the totalSupply which is equal to the balances.

constructor(uint _initialSupply) public {
    balances[msg.sender] = totalSupply = _initialSupply;
  }
Enter fullscreen mode Exit fullscreen mode

Next is a function called transfer() which takes two parameters, the address of the person _to whom the _value will be sent and returns a boolean.

function transfer(address _to, uint _value) public returns (bool)
Enter fullscreen mode Exit fullscreen mode

Inside this function it contains a requirement that the subtraction of _value from the balance of sender should be greater than or equal to 0. If that fulfils it proceeds to subtract the _value from the balance of sender and add it to the balance of whom the _value is sent _to and returns true at the end.

{
    require(balances[msg.sender] - _value >= 0);
    balances[msg.sender] -= _value;
    balances[_to] += _value;
    return true;
  }
Enter fullscreen mode Exit fullscreen mode

The last function of this contract just returns whatever the balance the _owner of the contract has.

function balanceOf(address _owner) public view returns (uint balance) {
    return balances[_owner];
  }
Enter fullscreen mode Exit fullscreen mode

So where do you see the conflict? In the hint. You see, in an odometer if you exceed its limit of 999999 it will just simply reset back to 000000 because it does not have a value further than that, this is called an overflow. Similarly, if we go backwards from 000000 it will go to 999999 because it does not have a value lower than that, this is called an underflow. Now remember i asked you to remember that uint is by default uint256 which has a range from 0 to 2^256. Now if you go below 0 it will reset to 2^256 and if you go further than 2^256 it will rest to 0.

How many tokens we have initially? 20. How many we want to win the level? Many. So if we subtract 21 from the initial token value of 20 it will reset back to 2^256 which is just way way too many and this subtraction occurs in the transfer() function.

Start by launching the console of this level and type the following command to send 21 tokens to a dummy address of 0.

await contract.transfer('0x0000000000000000000000000000000000000000', 21)
Enter fullscreen mode Exit fullscreen mode

And voila that’s it you won! Check your balance by the following command now.

await contract.balanceOf(player).then(v => v.toString())
Enter fullscreen mode Exit fullscreen mode

Now just click on “Submit Instance” and claim your win.

Note that it worked because contract's compiler version of the contract is v0.6.0. This will most probably won't work for latest versions v0.8.0 and above because underflow/overflow causes failing assertion by default in latest versions.

Top comments (0)