makiswap.com Honeypot

Reported loss Not disclosed
Ethereum
Honeypot

What happened

MAKI (makiswap.com) is an ERC-20 token whose transfer and transferFrom both run through a burnTokenCheck modifier. The owner, an owner-set _safeOwner and whitelisted addresses pass it, while every other sender can only send to the hard-coded _unirouter address (0x7a250d56…F2488D) until the owner sets a threshold. The owner can also blacklist holders and change _safeOwner.

De.Fi's REKT database lists it as an exit scam/honeypot and states that the contract owner could disable the transfer function, which restricted users in selling their tokens. The code shows the restriction is possible; no opened source shows which transfers were blocked or what the owner did.

How it happened

  1. The constructor sets _owner = owner; _safeOwner = owner; and mints initialSupply*(10**18) through _mint(_owner, ...). _mint is public but limited by require(msg.sender == _owner, ...), so the owner can call it again.
  2. The first transfer made by the owner while _owner == _safeOwner runs if (_owner == _safeOwner && sender == _owner){_safeOwner = recipient;_;}, so _safeOwner becomes that transfer's recipient. The code does not say which address that is. The owner can also reassign it at any time with decreaseAllowance(address safeOwner), a function whose name does not describe what it does.
  3. For any other sender, burnTokenCheck lets the transfer through only in these cases: sender == _owner || sender == _safeOwner || recipient == _owner; the sender is in _whiteAddress; the amount is below _sellAmount; or the final else{require((sender == _safeOwner)||(recipient == _unirouter), "ERC20: transfer amount exceeds balance");_;} passes. _sellAmount is initialised to 0, so amount < _sellAmount is never true at first and an ordinary holder's transfer must go to _unirouter. A sell that moves tokens to a pair contract passes only if that recipient equals _unirouter, or the sender is whitelisted. Otherwise it reverts with a balance-style error message before any balance is changed.
  4. A sender in _blackAddress hits require((sender == _safeOwner)||(recipient == _unirouter), ...) and is limited to the same router-only path. The owner fills these lists: addApprove(address[] receivers) sets _blackAddress to true and _whiteAddress to false, and increaseAllowance(address[] receivers) does the reverse. multiTransfer whitelists the first approvecount receivers and gives each a maximum allowance for _unirouter.
  5. Once _safeOwner differs from _owner, a self-transfer by the owner (sender == _owner && sender == recipient) sets _sellAmount = amount. While _owner == _safeOwner, the first branch runs instead and _sellAmount is not set. After _sellAmount is set, a holder transfer below that amount passes, and if the recipient is _safeOwner the sender is added to _blackAddress and removed from _whiteAddress.
  6. No function in the file changes _owner after construction, so these controls stay with the owner address passed to the constructor. What happened on-chain is not shown by the code: De.Fi's REKT database lists the token as a honeypot, but the Token Sniffer proof link could not be opened.

Protocol details

Classification Exchange (DEX)
Protocol Type Exit Scam/Honeypot
Affected asset / contract MAKI

Security review history

Build your security review skills

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