- Published on
Ethernaut - Naught Coin - Solution
- Authors
- Name
- Marco Besier, Ph.D.
- @marcobesier
Ethernaut - Naught Coin - Solution
Contract
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.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 = block.timestamp + 10 * 365 days;
uint256 public INITIAL_SUPPLY;
address public player;
constructor(address _player)
ERC20('NaughtCoin', '0x0') {
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(block.timestamp > timeLock);
_;
} else {
_;
}
}
}
Solution
The goal is to transfer all tokens from our player
account to another address. At first sight, this looks like an impossible thing to do because the lockTokens
modifier prevents the player
from executing transfer
during the first 10 years after contract deployment.
However, notice that the ERC-20 interface exposes two functions that facilitate token transfers: transfer
and transferFrom
.
In the above contract, transfer
is overridden and protected by lockTokens
. transferFrom
, however, is not overridden and, in particular, not protected by the lockTokens
modifier!
Therefore, we can solve this challenge using the following steps: First, we use our player
account to approve a second account we control for the entire INITIAL_SUPPLY
. The quickest way to do this is through the Ethernaut console.
contract.approve("<your second account address>", "1000000000000000000000000")
Subsequently, we can call transferFrom
from our second account, transferring the tokens from player
's account to our second account. The following line shows how to do this using Cast.
cast send --private-key <private key of your second account> --rpc-url $SEP_RPC_URL <your level instance> "transferFrom(address,address,uint256)" <your player address> <address of your second account> 1000000000000000000000000