Nintendo Protocol Honeypot
What happened
The Nintendo Protocol token (Nintendo) gates every transfer through a burnTokenCheck modifier. An ordinary holder can send tokens only to the hard-coded _unirouter address or to _owner; any other recipient reverts with "ERC20: transfer amount exceeds balance", although the holder's balance is not actually below the amount. The owner decides who is exempt through a whitelist, a blacklist and a _safeOwner address.
De.Fi's REKT database lists the token as an Exit Scam/Honeypot. The verified source shows these powers; it does not show which addresses the owner exempted or blocked.
How it happened
- Both
transferandtransferFromcall_approveCheck(sender, recipient, amount), which carries theburnTokenCheck(sender,recipient,amount)modifier. - The modifier lets a transfer pass unchecked when
sender == _owner || sender == _safeOwner || recipient == _owner, or when_whiteAddress[sender] == true. While_owner == _safeOwner, a transfer sent by the owner also runs_safeOwner = recipient, so_safeOwnerbecomes the recipient of that first owner transfer. The code does not show which address that was. - Every other sender is checked with
require((sender == _safeOwner)||(recipient == _unirouter), "ERC20: transfer amount exceeds balance"). Such a sender is never_safeOwnerat that point (that case passed in step 2), so the transfer succeeds only if the recipient is the_unirouteraddress hard-coded in the contract. A transfer to a liquidity pair or any other address reverts, and the revert leaves the holder's balance unchanged. - The owner can change who falls on which side:
increaseAllowance(address[])whitelists and un-blacklists addresses,addApprove(address[])blacklists and un-whitelists them,decreaseAllowance(address)sets_safeOwner, andmultiTransfersends tokens and whitelists the firstapprovecountreceivers while approving_unirouterfor the maximum amount on their behalf. A blacklisted sender faces the samerequireas an unlisted one. - A small exception exists once
_sellAmountis set, which happens when the owner sends tokens to itself (if (sender == _owner && sender == recipient){_sellAmount = amount;}). An unlisted holder sending less than_sellAmountis not reverted, but if the recipient is_safeOwnerthe holder is added to_blackAddressin the same call, so later transfers face the restriction again. _mintispublicand checks onlymsg.sender == _owner; it credits_balances[_owner]whateveraccountis passed, so the owner can increase supply. No function in the contract changes_ownerafter the constructor.
Protocol details
Build your security review skills
Work through hands-on labs covering real exploit mechanics, review techniques, and defensive patterns.