DEV Community

Cover image for Ethernaut Hacks Level 15: Naught Coin
Naveen ⚡
Naveen ⚡

Posted on

Ethernaut Hacks Level 15: Naught Coin

This is the level 15 of OpenZeppelin Ethernaut web3/solidity based game.

Pre-requisites

Hack

Given contract:

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

import '@openzeppelin/contracts/token/ERC20/ERC20.sol';

 contract NaughtCoin is ERC20 {

  // string public constant name = 'NaughtCoin';
  // string public constant symbol = '0x0';
  // uint public constant decimals = 18;
  uint public timeLock = now + 10 * 365 days;
  uint256 public INITIAL_SUPPLY;
  address public player;

  constructor(address _player) 
  ERC20('NaughtCoin', '0x0')
  public {
    player = _player;
    INITIAL_SUPPLY = 1000000 * (10**uint256(decimals()));
    // _totalSupply = INITIAL_SUPPLY;
    // _balances[player] = INITIAL_SUPPLY;
    _mint(player, INITIAL_SUPPLY);
    emit Transfer(address(0), player, INITIAL_SUPPLY);
  }

  function transfer(address _to, uint256 _value) override public lockTokens returns(bool) {
    super.transfer(_to, _value);
  }

  // Prevent the initial owner from transferring tokens until the timelock has passed
  modifier lockTokens() {
    if (msg.sender == player) {
      require(now > timeLock);
      _;
    } else {
     _;
    }
  }
} 
Enter fullscreen mode Exit fullscreen mode

player is given totalSupply of a ERC20 Token - Naught Coin, but cannot transact them before 10 year lockout period. player has to get its token balance to 0.

The trick here is that transfer is not the only method in ERC20(and hence, in NaughtCoin too) that includes code for transfer of tokens between addresses.

According to ERC20 spec, there's also an allowance mechanism that allows anyone to authorize someone else (the spender) to spend their tokens! This is exactly what allowance(address owner, address spender) method is for, in the ERC20 contract. The allowance can then be transacted using the transferFrom(address sender, address recipient, uint256 amount) method.

Apart from player create another account named - spender in your wallet (MetaMask or some other wallet).

Get the player's total balance by:

totalBalance = await contract.balanceOf(player).then(v => v.toString())

// Output: '1000000000000000000000000'
Enter fullscreen mode Exit fullscreen mode

Make the player approve spender for all of it's tokens:

await contract.approve(spender, totalBalance)
Enter fullscreen mode Exit fullscreen mode

Make the spender to transfer all of it's allowance (which is equal to all of the tokens of player) to spender itself.

I used MetaMask wallet and it connects only a single account at a time to an application. But we need both player and spender connected.

For this, in a new browser window, go to Remix, and connect only the spender account to it. (Make sure player is disconnected from Remix and spender is disconnected from Ethernaut. For me, switching accounts in one window caused switch in other window too, otherwise).

Create an interface to NaughtCoin:

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

interface INaughtCoin {
    function transferFrom(
        address sender,
        address recipient,
        uint256 amount
    ) external returns (bool);
}
Enter fullscreen mode Exit fullscreen mode

And load instance of NaughtCoin using At Address button with given instance address of NaughtCoin.

Call the transferFrom method with params - player address as sender, spender address as recipient and 1000000000000000000000000 as amount.

And that's it.

Level cleared!

Learned something awesome? Consider starring the github repo 😄

and following me on twitter here 🙏

Top comments (0)