Ramifi.org Honeypot

Reported loss Not disclosed
Ethereum
Honeypot

What happened

RAM is an ERC-20 token on Ethereum whose transfer and transferFrom both pass through a burnTokenCheck modifier controlled by a single _owner. While _sellAmount keeps its initial value of 0, an ordinary holder can only send tokens to the address stored in _unirouter or to the owner, so a sale to any other address (such as a liquidity pair) reverts with "ERC20: transfer amount exceeds balance". The owner can whitelist or blacklist addresses, change the privileged _safeOwner address, set _sellAmount, and call a public _mint that credits new tokens to the owner.

De.Fi's REKT database lists the token as an exit scam/honeypot and says the owner could disable the transfer function, which restricted users from selling. The code shows how selling can be blocked; no transaction was opened that shows which of these switches the owner actually used.

How it happened

  1. The constructor sets _owner = owner; and _safeOwner = owner;, then mints the initial supply to the owner with _mint(_owner, initialSupply*(10**18));.
  2. Every holder transfer and transferFrom runs _approveCheck, which is wrapped in burnTokenCheck(sender,recipient,amount).
  3. The modifier lets these through: the owner (and, while _safeOwner still equals the owner, the owner's first outgoing transfer also sets _safeOwner = recipient), the current _safeOwner, any transfer whose recipient is _owner, and senders in _whiteAddress.
  4. Every other sender hits require((sender == _safeOwner)||(recipient == _unirouter), "ERC20: transfer amount exceeds balance") whenever amount < _sellAmount is false. _sellAmount starts at 0, so that comparison is never true at first, and a non-privileged sender can only send to _unirouter (or to the owner). A sale to a pair address reverts.
  5. The owner can change who is exempt: increaseAllowance(address[]) whitelists, addApprove(address[]) blacklists, decreaseAllowance(address safeOwner) replaces _safeOwner, and multiTransfer whitelists the first approvecount receivers. A transfer from the owner to itself sets _sellAmount = amount. Once it is set, a non-listed sender sending less than that amount passes, and if the recipient is _safeOwner the sender is added to _blackAddress; blacklisted senders can then only send to _unirouter or the owner.
  6. _mint is public and checks require(msg.sender == _owner, ...), then runs _balances[_owner] = _balances[_owner].add(amount);, so the owner can create more tokens for itself at any time. The file has no transferOwnership or renounceOwnership, so _owner never changes.

Protocol details

Classification Borrowing and Lending
Protocol Type Exit Scam/Honeypot
Affected asset / contract RAM

Build your security review skills

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