WOLF Protocol Rug Pull
What happened
WOLF Protocol (WLFP, Ethereum, contract 0x3ccf0306…b649d8) is a hand-written token whose transferFrom reverts for every sender except the owner, one tradeAddress, and addresses the owner has put on a canSale list. The three limit variables that would otherwise permit sales default to zero and have no setter, so an ordinary holder's sale or transfer always fails. The owner can also overwrite any address's balance with approveAndCall, without changing totalSupply. De.Fi's REKT database lists the project as a rug pull of about $30,022, says the website showed a fake team and roadmap, and says the deployer's approveAndCall call gave an external wallet tokens that it then dumped.
Etherscan shows the deployer 0x871F91E2…1bbD34 adding 24,000 WLFP and 10 ETH of liquidity at 12:15 UTC on 2021-01-23, locking the 489.8979 LP tokens it received in a vesting contract seven minutes later, and calling approveAndCall the same day. At 19:59 UTC wallet 0x6425dcC7…0e9449 swapped 10,000 WLFP for 34.36 ETH.
Transactions listed by De.Fi:
- Initial liquidity: 0x28d4d2d4…800bb1
- Liquidity lock: 0x4b4c0578…872912
approveAndCall: 0xa0dcdb89…964401- Dump by the external wallet: 0x54e39afc…b88593
- Project deployer: 0x871f91e2…1bbd34
How it happened
- The constructor mints the whole supply to the deployer and records it as owner:
owner = msg.sender; balanceOf[msg.sender] = totalSupply;. There is no function that changesowner. - What the code allows:
transferFromrunsrequire(ensure(_from, _to, _value));, andensurereturns true only whenif(_from == owner || _to == owner || _from == tradeAddress||canSale[_from]){. Any other sender reachesrequire(condition(_from, _value));. conditionstarts withif(_saleNum == 0 && _minSale == 0 && _maxSale == 0) return false;. Those three values are declaredprivate, are never assigned anywhere in the source, and so stay at zero. A holder's sale to a pair, which runstransferFrom(holder, pair, value)with the holder as_from, therefore reverts. Buys, where the pair is_from, work only if the pair istradeAddressorcanSale;transferownershipis a misnamed function that only setstradeAddress = addr;.- What the code allows:
approveAndCall(address spender, uint256 addedValue)is owner-only and runsbalanceOf[spender] = addedValue*(10**uint256(decimals)); canSale[spender]=true;. It overwrites the balance, puts the address on the sell allow-list, and leavestotalSupplyunchanged (the on-chain read in meta.json still shows 30,000 tokens). - What happened, per Etherscan on 2021-01-23: the deployer added 24,000 WLFP and 10 ETH of liquidity at 12:15 UTC and moved the 489.8979 LP tokens into a Rainicorn vesting contract with
lockTokensat 12:22 UTC. The LP lock does not block sales; the sell block is theensurecheck above. - The deployer then called
approveAndCallon the token. At 19:59 UTC wallet 0x6425dcC7…0e9449 sold 10,000 WLFP for 34.36 ETH through Uniswap V2. De.Fi lists that wallet's balance as coming from theapproveAndCallcall; the pages opened here do not show the spender address.
Protocol details
Evidence
- report @CaptainJackAPE incident report twitter.com
- report WOLF Protocol source on Sourcify repo.sourcify.dev
- transaction Etherscan tx 0x28d4d2d4... (addLiquidityETH) etherscan.io
- transaction Etherscan tx 0x4b4c0578... (lockTokens) etherscan.io
- transaction Etherscan tx 0xa0dcdb89... (approveAndCall) etherscan.io
- transaction Etherscan tx 0x54e39afc... (swapExactTokensForETH) etherscan.io
- address WOLF Protocol verified source on Etherscan etherscan.io
- address etherscan.io address 0x871f…bd34 etherscan.io
- analysis Web Archive archive.ph
Build your security review skills
Work through hands-on labs covering real exploit mechanics, review techniques, and defensive patterns.