ChainGPT Rug Pull

Reported loss $82K
Rugpull

What happened

This record covers a token named ChainGPT (CGPT) at BSC contract 0x6e70ee54…29eBBF, the contract CertiK's alert flags. ChainGPT's documentation lists a different contract, 0x9840652D…f00f98, as the native CGPT token on BSC.

The source routes transfer and transferFrom through delegatecall to a replaceable proxyAddr, so the real transfer rules are not in the verified files: whether and how the blacklist is enforced, and any burn ratio or reward logic, cannot be seen in the source, and the proxy's source was not available. The owner can mint any amount with mint and add addresses to a blacklist, and any whitelisted address, which includes the deployer, can change proxyAddr to a different contract. There is no renounceOwnership, and transferOwnership rejects the zero address, so ownership cannot be renounced to the zero address; the owner can still transfer it to another address. On-chain owner() is 0xd9618606…1f1918 (read 2026-09-30).

CertiK's alert of 2023-04-12 reports a large liquidity removal on this contract, about $76K removed by the deployer. De.Fi's REKT database lists the incident as an exit scam/rug pull of about 82k USD on BSC in which the deployer removed a significant amount of liquidity and sold the token, for a drop of about 90% from the initial price.

from De.Fi's listing: address named as responsible 0xd9618606…1f1918; liquidity removal transaction 0x12c1a6cc…07c005.

How it happened

  1. The constructor puts the 1,000,000,000 supply in the deployer's balance and whitelists the deployer: _balances[_msgSender()] = _totalSupply; and _whitelist[_msgSender()] = true;.
  2. Transfers are not handled in this file. transfer runs (bool success, ) = proxyAddr.delegatecall(abi.encodeWithSignature("transfer(address,uint256)",recipient,amount)); and transferFrom does the same, so the rules run inside whatever contract proxyAddr points to, with write access to this token's balances. The proxy source is not in the verified files, so its sell and burn rules cannot be traced.
  3. The target can be swapped: function setProxyAddr(address _addr) public requires _whitelist[msg.sender] == true and then runs proxyAddr = _addr;. The deployer is whitelisted at construction and addWhitelist (onlyOwner) can add more addresses.
  4. Supply is not fixed: function mint(address account, uint256 amount) onlyOwner public virtual returns (bool) calls _mint(account, amount); with no cap.
  5. A blacklist exists: addBlacklist(address[] memory accounts) public onlyOwner sets _blacklist[accounts[i]] = true;. Reading it is restricted, since checkBlacklist begins require(_whitelist[msg.sender] == true, "No permission.");, so holders cannot query their own status. The verified file does not enforce the blacklist anywhere, and the proxy source was not seen, so where or whether it is enforced is not visible.
  6. Ownership cannot be renounced to the zero address: there is no renounceOwnership, and transferOwnership has require(newOwner != address(0), ...). The owner can still transfer ownership to any other address.
  7. What was reported: CertiK says about $76K was removed by the deployer on 2023-04-12, and De.Fi's listing gives about 82k USD. Removing liquidity does not require any of the functions above, and no opened transaction shows the owner calling mint, addBlacklist or setProxyAddr, so these steps describe what the code allows.

Protocol details

Classification Token
Protocol Type Launchpad
Affected asset / contract CGPT
Protocol links Website @Chain_GPT

Build your security review skills

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