MOAR Finance Honeypot
What happened
De.Fi's REKT database lists MOAR Finance (MOAR) as an Exit Scam/Honeypot and says the contract owner could disable the transfer function, which restricted users in selling their tokens. The verified source at 0xc5691f93…b6de6f (Ethereum, deployed 2021-04-21) shows how a trap like that is built: the shared burnTokenCheck modifier wraps every transfer and transferFrom, and an ordinary holder's transfer reverts unless the recipient is the hard-coded Uniswap V2 router address or the owner, or the holder has been whitelisted by the owner. The owner can also whitelist or blacklist any address and can mint to itself.
The reports reviewed do not state any loss amount.
How it happened
- The token is the
StandardERC20contract. Its constructor stores the deployer as both owner and "safe owner":_owner = owner; _safeOwner = owner;followed by_mint(_owner, initialSupply*(10**18));. - Both
transferandtransferFromcall_approveCheck(...), which carries the modifierburnTokenCheck(sender,recipient,amount). The modifier runs before the balances move. - While
_owner == _safeOwner, the first transfer sent by the owner passes and runs_safeOwner = recipient;. After that, transfers where the sender is_owneror_safeOwner, or where the recipient is_owner, always pass. - For every other sender the modifier checks
_whiteAddress[sender]. A whitelisted sender always passes. A_blackAddress[sender]sender, and any non-listed sender whoseamountis not below_sellAmount, must satisfyrequire((sender == _safeOwner)||(recipient == _unirouter), "ERC20: transfer amount exceeds balance")._sellAmountis initialised to0, soamount < _sellAmountis false until the owner sets it, and for these senders only a transfer to the router address0x7a250d5630B4cF539739dF2C5dAcb4c659F2488Dor to the owner can succeed (thesender == _safeOwnerhalf cannot be true on this path, because that case already passed in step 3). A transfer to any other recipient, such as a pair contract, reverts with a misleading balance error even though the holder's balance is sufficient. - The owner controls who is exempt:
increaseAllowance(address[] memory receivers)sets_whiteAddress[...] = trueand_blackAddress[...] = false;addApprove(address[] memory receivers)does the reverse;decreaseAllowance(address safeOwner)reassigns_safeOwner;multiTransferwhitelists the firstapprovecountrecipients and approves the router for the maximum amount. Once_safeOwneris no longer_owner(after the first owner transfer in step 3, or afterdecreaseAllowance), an owner transfer to itself (sender == recipient == _owner) sets_sellAmount = amount; while_safeOwneris still_owner, that same transfer instead runs the first branch and sets_safeOwner = recipient. Once_sellAmountis set, a non-listed holder's transfer below that size goes through. If its recipient is_safeOwner, the modifier blacklists the sender (_blackAddress[sender] = true,_whiteAddress[sender] = false) before the transfer body runs, so the sender's later transfers face the router-or-owner requirement from step 4. - The supply is not fixed:
_mint(address account, uint256 amount)ispublicand, formsg.sender == _owner, addsamountto_totalSupplyand to_balances[_owner]. - What happened is limited to the listing: De.Fi's REKT database lists the token as an Exit Scam/Honeypot and says holders were restricted from selling. No transaction, loss figure or liquidity-removal record was opened for this draft, so the code above shows what the owner could do, not what was done.
Protocol details
Build your security review skills
Work through hands-on labs covering real exploit mechanics, review techniques, and defensive patterns.