AutoTrade Finance Honeypot

Reported loss $32K
Ethereum
Honeypot

What happened

AutoTrade.Finance (ATFI) is an Ethereum ERC20 deployed on 2020-12-10 by 0x5B0e2D74…55E518. Every transfer runs through a safeCheck modifier that locks the token to its first recipient: the first transfer out of the owner address records the recipient in _address1, which for a liquidity deposit is the Uniswap pair, and from then on a transfer reverts unless its sender is _address1, the router address, an address the owner whitelisted through multiaddress, or the owner. Buys pass because the pair is the sender, but a holder's sale into the pair is transferFrom(holder, pair, amount) with the holder as sender, so it reverts with ERC20: transfer from the zero address. The owner address _address0 is set in the constructor and has no setter or renounce function, so the restriction cannot be given up, only moved by the owner with ints.

Etherscan shows the deployer adding 150 ETH and 2,250 ATFI of liquidity at 06:07:16 UTC on 2020-12-10 (tx 0xf1ff8ff9…ea7097) and removing all of it with removeLiquidityETHWithPermit at 13:27:56 UTC the same day (tx 0xf14a3c32…58d0fa), receiving 206.74 ETH and 1,635.13 ATFI, so the ETH side grew by 56.74 ETH while the pool paid out 614.87 ATFI. De.Fi's REKT database describes the token as a honeypot in which the contract owner could disable the transfer function, which restricted users in selling their tokens. No transaction was opened showing a holder's failed sale or a call to ints or multiaddress.

How it happened

  1. The constructor mints the supply to the owner and sets both control addresses to that owner: _address0 = owner; _address1 = owner; _mint(_address0, initialSupply*(10**18));. _address0 is never reassigned afterwards.
  2. The owner deposits tokens into a Uniswap pair. The router moves them with transferFrom(owner, pair, amount), and the modifier line if(sender==_address0 && _address0==_address1){_address1 = recipient;} then sets _address1 to the pair (assuming this is the owner's first outgoing transfer; the source does not show otherwise).
  3. From that point the modifier line if(recipient != _address0 && sender != _address0 && _address0!=_address1 && amount > _zero){require(sender == _address1 ||sender==_router || _Addressint[sender], "ERC20: transfer from the zero address");} rejects any transfer whose sender is not the pair, the router address, a whitelisted address or the owner. A buyer receives tokens from the pair (sender is _address1) and passes.
  4. A holder who sells calls the router, which executes transferFrom(holder, pair, amount). The sender is the holder, who is not _address1, not _router (the router is the caller, not the sender) and not whitelisted, so the call reverts and the holder cannot exit. Plain wallet-to-wallet transfers fail for the same reason.
  5. The owner keeps access. multiaddress(AllowN, receivers, amounts) can send tokens to chosen addresses and, for the first AllowN receivers, run _Addressint[receivers[i]] = true; _approve(receivers[i], _router, _valuehash);, which whitelists them and grants the router an unlimited allowance. ints(addressn) lets the owner set _address1 freely; setting it to the owner's own address lifts the check until the owner's next outgoing transfer, which sets _address1 to that recipient and arms it again. Removing liquidity also passes, because the pair is the sender when it pays tokens out.
  6. What happened, from Etherscan: the deployer added 150 ETH and 2,250 ATFI at 06:07:16 UTC and removed the whole position at 13:27:56 UTC, 206.74 ETH and 1,635.13 ATFI returned.

Protocol details

Classification Token
Protocol Type Exit Scam/Honeypot
Affected asset / contract ATFI
Protocol links Website

Build your security review skills

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