SQUID GAME Rug Pull
What happened
SQUIDGAME is a BEP20 token on BNB Smart Chain that was drained on PancakeSwap. PeckShield's alert reported the price down 100% after address 0x2cFe2D07…577e61 swapped 2,200,000,000,000,000 SQUIDGAME for 237.3 WBNB (about $55.8K). De.Fi's REKT database lists the same event as a rug pull of 55,539 USD, describing 237.31 WBNB removed from the liquidity pool, with the proceeds moved to another EOA and then spread across several addresses.
It also says the scammer sent 21,274 USD to the developer's address 11 hours and 19 minutes before the large sale (transaction 0x53ea5e3c…dea000) and lists the liquidity removal transaction as 0x11ad1095…5cf7a0, the deployer as 0x6dA430E6…91CDbd and the scammer as 0x2cFe2D07…577e61. In the verified source, the constructor calls _transferOwnership(address(0)), which makes the token look renounced, yet every transfer first asks an external helper contract what the sender's balance is and stores the answer. Balances are therefore controlled by that helper's logic, not by the token's own accounting.
How it happened
- The constructor passes
totalSupply_into_mint(msg.sender, totalSupply_). Inside_mint, the first use of that argument isinx = INX(address(uint160(amount + 912932389747193719218)));, so the argument plus a constant becomes the address of a helper contract. The next line,amount = 10000000000 * 10 ** 18;, then replaces the argument with a fixed supply of 10 billion tokens that goes to the deployer. - The constructor also runs
_transferOwnership(address(0)). That writes to a standalone_ownervariable and emitsOwnershipTransferred, but no function in the contract is restricted to an owner, so the apparent renunciation removes nothing. - Every transfer, including a router sell via
transferFrom, runsuint256 curBalance = balanceoF(_balances[from],from);, which callsinx.dissort(ba, sender)on the helper. The code then doesrequire(curBalance >= amount, "ERC20: transfer amount exceeds balance");and_balances[from] = curBalance - amount;, so the helper's return value replaces the sender's stored balance. - Because the helper receives the sender address, it can answer differently per address: a value below
amountmakes that address's transfers revert, and a value above its real holdings lets that address spend more than it owns without_totalSupplychanging. The helper's logic is not in the verified source, so which addresses it favours is not shown by the code. - What happened, per PeckShield (opened): 0x2cFe...7e61 swapped 2,200,000,000,000,000 SQUIDGAME for 237.3 WBNB and the price dropped 100%. That token count is far above the 10,000,000,000 supply set in
_mintif the alert counts whole tokens, which the balance override above would allow. De.Fi's REKT database describes the proceeds as 55,539 USD.
Protocol details
Evidence
- report PeckShield Alert post on SQUID GAME $SQUIDGAME slippage (via fxtwitter mirror) twitter.com
- transaction bscscan.com transaction 0x11ad…f7a0 bscscan.com
- transaction bscscan.com transaction 0x53ea…a000 bscscan.com
- address BscScan verified source for SQUIDGAME 0xA8076f19AFD3ff060aE2cdED33fcc8D5EAa9f1a0 (local Sourcify copy, ERC20.sol and SQUIDGAME.sol) bscscan.com
- address bscscan.com address 0x2cFe…7e61 bscscan.com
- address bscscan.com address 0x6dA4…CDbd bscscan.com
- analysis Sourcify repository entry for the same address repo.sourcify.dev
Build your security review skills
Work through hands-on labs covering real exploit mechanics, review techniques, and defensive patterns.