Game Swap Exchange Rug Pull

Reported loss $23K
Ethereum
Rugpull

What happened

GAME SWAP (GAME) is an Ethereum token whose source is a hand-written contract, not a standard ERC-20 implementation. Its transferFrom runs an ensure check that only lets the owner, the owner's tradeAddress, and addresses marked in a private canSale mapping move tokens freely. Everyone else falls through to condition, which returns false while three private variables (_saleNum, _minSale, _maxSale) are all zero.

Nothing in the contract ever assigns those variables, so in the verified code an ordinary holder's transfer or sell would revert. The owner-only approveAndCall function is also a hidden mint: it overwrites a chosen address's balance with any amount and whitelists that address, without touching totalSupply or emitting a Transfer event. On 2021-01-24 the deployer 0x8c848BE2…E4897b added 12 ETH and 7,500 GAME to a Uniswap V2 pool (tx 0x67abd476…202758, 13:15 UTC).

About six hours later, at 19:18 UTC, it called approveAndCall for 0x0659f352…4c194d (tx 0x179c0c41…a099c8) with an input value that decodes to 1,000,000,000,000,000. That recipient then sold 10,000 GAME on Uniswap for 28.4033 ETH about 95 seconds later (tx 0x2bc2c0d4…55c56f). De.Fi's REKT database lists the incident as an exit scam / rug pull in which minting and dumping GAME netted about 16 ETH.

How it happened

  1. The contract's transferFrom calls ensure(_from, _to, _value), which returns true only when _from == owner || _to == owner || _from == tradeAddress||canSale[_from]; every other sender must satisfy condition(_from, _value).
  2. condition starts with if(_saleNum == 0 && _minSale == 0 && _maxSale == 0) return false;. The variables _saleNum, _minSale and _maxSale are declared private and never assigned, so the code would revert transfers and sells from any holder who is not the owner, tradeAddress or a canSale address when the destination is not the owner.
  3. The owner-only approveAndCall(address spender, uint256 addedValue) runs balanceOf[spender] = addedValue*(10**uint256(decimals)); and canSale[spender]=true;. It sets any balance for any address and exempts it from the sell block, while totalSupply and the Transfer event log stay untouched.
  4. The deployer added 12 ETH and 7,500 GAME of liquidity on 2021-01-24 at 13:15 UTC.
  5. At 19:18:51 UTC the deployer called approveAndCall with spender 0x0659f352…4c194d and an addedValue of 1,000,000,000,000,000 (hex 0x00038d7ea4c68000), which sets that address's balance to 10^15 tokens, against a constructor supply of 10,000 tokens.
  6. At 19:20:26 UTC the whitelisted address swapped 10,000 GAME through the Uniswap V2 router for 28.4033 ETH, taking the ETH out of the pool. The code would revert a sell from any holder who is not the owner, tradeAddress or a canSale address; no opened transaction shows a failed sell, so whether any other holder tried to sell is not established.

Protocol details

Classification Token
Protocol Type Exit Scam/Rugpull
Affected asset / contract GAME
Protocol links Website @gameswapdefi

Build your security review skills

Work through hands-on labs covering real exploit mechanics, review techniques, and defensive patterns.