PCORE Rug Pull
What happened
PCORE is a BNB Chain token whose transfer function gives the deployer a set of owner-only switches over who can trade. The deployer is exempt from every check, can turn on a switch that makes all non-pair transfers revert (so holders cannot sell while buys still work), can blacklist addresses from selling one by one or for every recorded buyer in one call, and keeps these powers even if ownership is renounced. The code only shows what the deployer can do; it does not show which of these functions were called.
On 2023-04-07 CertiK Skynet reported that the PCORE deployer removed liquidity in 3 transactions for a profit of about 37.5k USD. De.Fi's REKT database lists the case as a liquidity-removal rug pull of about 40,000 USD, says the token fell by about 90% from its initial price, and links the deployer address 0xe42e5670…b2ac71 and the liquidity removal transaction 0xcabd9ccd…2d7ad7.
How it happened
What the code allows:
- The constructor mints the whole supply to the deployer and records it as the special address:
_owner=_msgSender();and_mint(msg.sender, totalSupply_* 10 ** uint256(decimals()));._mintisinternal virtual onlyOwner, so nobody can call it again from outside. - Every restriction in
_transfersits insideif(_owner!=from && _owner!=to && from!=_airTokenAddr){ ... }. A transfer where the deployer is sender or receiver skips all of them, so the deployer can sell, receive pool tokens and move supply no matter what the switches below are set to. - A trading switch locks holders out.
DepositTransfer()isonlyOwnerand setstradeStatus=true; the check isif(tradeStatus && from!=_swap){require(false,"Block is busy, please try again");}. While it is on, any non-exempt transfer not sent from the pool address_swapreverts: a holder selling to the pool (sender is the holder) and a holder-to-holder transfer both fail, while buys (sender is the pool) still succeed.WithdrawalTransfer()turns it off._swapis set by the owner throughSwapContract(address swap_). - A per-address sell blacklist.
AirDropGift(address user)isonlyOwnerand setsisStatus[user]=true; the sell check isif(to==_swap){ if(isStatus[from]){require(false,"Block is busy, please try again");} }. The check reads the transfer'sfrom(the holder), so it blocks the blacklisted holder's sell, including sells through the router.AirDrop(address user)clears the flag. - A bulk blacklist of buyers. Every buy from the pool records the buyer:
if(from==_swap){ if(haveToken[haveTokenCount]==deadwallet && examinationAirDropList(to)){ haveToken[haveTokenCount]=to;haveTokenCount++;} }. The owner-onlyBonous()loops over that list and callsAirDropGift(user)for each address not yet flagged, so one call blacklists every recorded buyer from selling. - A dust flag that freezes an address completely.
if(_airTokenAddr==from && amount<=2000){ airtokenlist[to]=true; }marks any recipient of a transfer of 2000 raw units or less from_airTokenAddr(a constructor argument), andif(airtokenlist[from]){require(false,"Block is busy, please try again");}then makes every transfer from that address revert, sells included. Transfers from_airTokenAddrare exempt from the first block. - Wallet-to-wallet transfers always fail for non-exempt senders:
if(from!=_swap && to!=_swap){ airtokenlist[from]=true; isStatus[from]=true; }is followed by theairtokenlist[from]revert, which rolls back both writes, so the net effect is that the transfer reverts. - Renouncing does not remove the control.
onlyOwneracceptsowner() == _msgSender() || _root ==_msgSender(),_rootis set to the deployer in theOwnableconstructor and is never changed, andrenounceOwnership()only clears_ownerinOwnable. The exemption variable_ownerdeclared inPCOREis separate and is not updated bytransferOwnership.
What was reported to have happened: CertiK Skynet's alert says the deployer removed liquidity in 3 transactions on 2023-04-07, and De.Fi lists the loss as about 40,000 USD with a roughly 90% price fall. Neither source states that any of the functions above was called, so this write-up does not claim a lock, blacklist or dust flag was applied.
Protocol details
Evidence
- report @CertiKAlert incident report twitter.com
- report PCORE contract source on Sourcify repo.sourcify.dev
- report CertiK Alert Skynet post on PCORE liquidity removal, 2023-04-07 twitter.com
- transaction bscscan.com transaction 0xcabd…7ad7 bscscan.com
- address PCORE contract source on BscScan (verified code) bscscan.com
- address bscscan.com address 0xe42e…ac71 bscscan.com
Build your security review skills
Work through hands-on labs covering real exploit mechanics, review techniques, and defensive patterns.