SUI Token Rug Pull

Reported loss $100K
Rugpull

What happened

SUI Token (SUI) is a BEP20 token on BNB Smart Chain, deployed on 2023-01-31 from 0x35a93566…7fD5b5, which is also the current owner. On 2023-02-06 a CertiK Alert post reported a large liquidity removal on the token: about $100.2k taken from the liquidity pool and about $32k deposited into Tornado Cash. De.Fi's REKT database lists the incident as an exit scam / rug pull with about $100,000 lost, and its description says the deployer removed liquidity from the pair contract and sold a large quantity of tokens, a drop of roughly 90% in price; the deployer address is 0x35a93566…7fd5b5 and the listed remove-liquidity transaction is 0xb6399f8c…2b1978.

The verified source gives the owner several ways to take value out of a pool without holding its LP tokens. drainLP mints new SUI into the pair, swaps out the paired asset to the caller, and burns the minted tokens. rebaseLP burns or mints SUI directly at the pair's address, and mint creates unlimited supply for any address. None of these functions has a cap or a delay, so any buyer's paired-asset liquidity depends on the owner not calling them. Which route was used in the reported removal is not established from the sources opened here.

How it happened

What the code allows (the sources do not show which of these functions was called):

  1. The constructor mints the whole initial supply to the deployer: _mint(_msgSender(), 100000000 * 10 ** decimals());. The deployer is the first owner, and renounceOwnership and transferOwnership are the standard Ownable functions; the current on-chain owner is still 0x35a93566…7fd5b5.
  2. mint(address to, uint256 amount) public onlyOwner calls _mint(to, amount), so the owner can create any quantity of SUI at any time. There is no cap.
  3. drainLP(address token, uint32 k) public onlyOwner looks up the SUI/token pair, then runs _mint(address(pair), amountIn); pair.swap(0, amountOut, _msgSender(), ""); where amountOut = uint256(reserveB) * k / 10000. The freshly minted SUI is the "input" of the swap, so the pair pays out a share of its paired-asset reserve to the owner without the owner supplying any value. It then calls _burn(address(pair), amountIn + uint256(reserveA) * k / 10000) and pair.sync(), which also removes part of the pair's original SUI reserve.
  4. rebaseLP(address token, uint32 k) public onlyOwner sets the pair's SUI balance to currentBalance * k / 10000 by burning from or minting to address(pair), then calls pair.sync(). This lets the owner reset the pool price at will without holding LP tokens.
  5. Every transfer, including mints and burns, calls _warden.registerTransfer(from, to, amount) from _beforeTokenTransfer. The warden contract address is a constructor argument and its code is not part of the verified source, so whether it can block transfers cannot be determined from this evidence.

What was reported:

  1. On 2023-02-06, CertiK Alert reported a large liquidity removal on the token, about $100.2k removed from the pool and about $32k sent to Tornado Cash. De.Fi lists the deployer-side removal as a rug pull with a roughly 90% price drop.

Protocol details

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

Build your security review skills

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