- Published on
Capture the Ether (RareSkills Repo) - Token Whale - Solution
- Authors

- Name
- Marco Besier, Ph.D.
Capture the Ether (RareSkills Repo) - Token Whale - Solution
Contract
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
contract TokenWhale {
address player;
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;
string public name = "Simple ERC20 Token";
string public symbol = "SET";
uint8 public decimals = 18;
event Transfer(address indexed from, address indexed to, uint256 value);
constructor(address _player) {
player = _player;
totalSupply = 1000;
balanceOf[player] = 1000;
}
function isComplete() public view returns (bool) {
return balanceOf[player] >= 1000000;
}
function _transfer(address to, uint256 value) internal {
unchecked {
balanceOf[msg.sender] -= value;
balanceOf[to] += value;
}
emit Transfer(msg.sender, to, value);
}
function transfer(address to, uint256 value) public {
require(balanceOf[msg.sender] >= value);
require(balanceOf[to] + value >= balanceOf[to]);
_transfer(to, value);
}
event Approval(
address indexed owner,
address indexed spender,
uint256 value
);
function approve(address spender, uint256 value) public {
allowance[msg.sender][spender] = value;
emit Approval(msg.sender, spender, value);
}
function transferFrom(address from, address to, uint256 value) public {
require(balanceOf[from] >= value);
require(balanceOf[to] + value >= balanceOf[to]);
require(allowance[from][msg.sender] >= value);
allowance[from][msg.sender] -= value;
_transfer(to, value);
}
}
Solution
This challenge is conveniently solved via Remix. To increase the player's token balance to 1M (or more), we'll use two different accounts and perform the following sequence of function calls:
- Set
_playerto an EOA you control, e.g., the first of Remix's default accounts. This will giveplayeran account balance of 1000. - Next, approve a second EOA you control, e.g., the second of Remix's default accounts, with
value = 1. Ensure to make this function call fromplayer's account. - Now, call
transferFrom(player, player, 1)from the second EOA. At first sight, one would assume that this wouldn't change anything since we're just sendingvalue = 1from theplayerto theplayer. However, by taking a closer look at the implementation of_transfer, we see that this call will underflow the second EOA's balance (and add 1 to theplayer's balance). In other words, after this call,playerhas a balance of 1001 while the second EOA has a balance of2**256 - 1! - Lastly, we can use
transferto transfervalue = 998999(or more) from the second EOA toplayer, leavingplayerwith a balance of 1000000 (or more).
Note that the to argument in the above call to transferFrom can actually be chosen arbitrarily. The important point in the above sequence is not that we increase the player's balance from 1000 to 1001, but that we underflow the balance of our second EOA so that this account has enough tokens to bump up player's balance to 1M or more.