Jigsaw saw you Rug Pull

Reported loss $160K
Rugpull

What happened

SAW (Jigsaw saw you) is an Ethereum ERC-20 whose Ownable contract is renounced from the constructor, so it looks ownerless, but a second privileged role is kept in _play[0].red, set from the constructor argument mankind_. Through a function named Approve (capital A, separate from the standard approve), that address can write a per-account value into the _death mapping, and _transfer reverts for any sender whose value is at least the amount being sent. De.Fi's alert reports that on Aug 15, 2023 the deployer 0x5707b5D0…E925ab removed about 88 ETH (about 160k USD) from the liquidity pool. De.Fi's listing summary mentions about 216.3k USD, a figure that differs from the 160k USD in its own details.

Scammer address (as given in the listing): 0x5707b5d0…e925ab

Liquidity Removal tx: 0x4ef259f1…dff6cc

How it happened

What the code allows:

  1. The constructor calls _setOwner(address(0)), so owner() is the zero address from deployment and there are no onlyOwner powers. The real control is a different role: _play.push(Mask(mankind_, 2)); stores the mankind_ constructor argument as _play[0].red. Whether that address is the deployer is not shown in the source.
  2. Approve(address account, uint256 amount) is a custom function next to the standard approve. It calls _live(from, account, amount), and in _live: if (from == _play[0].red) { ... total += amount; _death[account].punish = total; }. When the caller is the red address, this sets punish for any account to any amount. For every other caller the branch adds zero, so it does nothing. It also emits Approval(from, address(this), amount), which looks like an ordinary approval event.
  3. _transfer applies that value to the sender: require(amount - alien(sender) > 0, "alien");. With checked arithmetic, amount - punish reverts when punish is larger than amount, and the > 0 check fails when they are equal. So a sender whose punish is at least the amount cannot move tokens. transfer and transferFrom both go through _transfer, and the check is on sender, so a holder with a high value cannot sell, and a value set on the liquidity pair address would block transfers out of the pool, which are buys.
  4. The red address can overwrite the value again with a new Approve call, so the block can be lifted or re-applied per address.

, per De.Fi's alert and listing:

  1. The deployer 0x5707b5D0…E925ab removed about 88 ETH (about 160k USD) from the pool in transaction 0x4ef259f1…dff6cc and, per the listing, spread the funds across four wallets.
  2. The alert also states that the deployer wallet was initially funded through Tornado Cash and has a history of earlier rug pulls.
  3. No opened source shows a transaction in which the red address called Approve to block any holder or the pool, so whether the _death block was used is not established.

Protocol details

Classification Token
Protocol Type Exit Scam/Rugpull
Affected asset / contract SAW

Build your security review skills

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