This is a code analysis of the transfer disable bug that GitHub user VexyCats discovered. The full code for the ICON token is available here.

At a high level

This contract is fairly simple. It’s 253 lines long including white space and comments. It implements an ERC-20 interface along with some code that lets the owner lock the transfer of funds. At least, that was the intention.

The structure of the code at 0xb5a5f22694352c15b00323844ad545abb2b11028

The bug

When the token is initialized, an address is set as the walletAddress. The full initial balance of ICON tokens is held by this address. This address has the capability to enable or disable the transfer of tokens altogether, by calling the functions enableTokenTransfer and disableTokenTransfer.

Before those functions are executed, the modifier onlyFromWallet is supposed to ensure that the call originates from walletAddress, and reject the transaction if it doesn’t.

Line 2: It’s like a lock that works with any key except the one made for it.

Except that it does exactly the opposite — it lets anyone except the owner toggle the token transfer, and effectively turn the contract off. The error? It’s a typo.

require(msg.sender != walletAddress); // Anyone but the owner.

The ERC-20 functions transfer, transferFrom, and approve all stop working when tokenTransfer is disabled. This allows anyone to freeze or unfreeze token movement.

This isn’t a severe bug, although it renders the current contract inoperable. Once a patched version is deployed, the balances of users can be migrated over, although it could be a hassle to get all the people to upgrade to the new version.

The affected bits of the code

Context

The ERC20 token ICON is valued at around ~$800M as I write this.