Horus Finance Honeypot

Reported loss $991
Ethereum
Honeypot

What happened

HORUS (HRS) was a token on Ethereum whose contract gave its owner two unrestricted powers: an owner-only addWork() function that creates tokens for any address, and an owner-managed sub list that stops listed addresses from sending tokens. The 80,000-token supply was created for the deployer, who paired it with 30 ETH on Uniswap V2 on 2020-10-30 (tx 0x61d5d655…6a01a4). On 2020-10-31 at 04:45:54 UTC the deployer wallet called addWork() to mint 500 million HRS to itself (tx 0x9cfdfc0b…85bb2f), and 46 seconds later swapped those 500 million HRS through the Uniswap V2 router for 32.26 WETH (tx 0x39dcc40a…482215).

The mint was roughly 6,250 times the original supply, so it overwhelmed the pool's HRS reserves. De.Fi's REKT database lists the incident as an exit scam / honeypot with a 991 USD loss.

How it happened

  1. The constructor calls _supply(initialSupplySend, _totalSupply) and sets _owner = msg.sender; in the opened transactions the deployer 0x79018eBC…6f738E holds the supply and is the caller of the later owner-only functions.
  2. On 2020-10-30 the deployer called addLiquidityETH on the Uniswap V2 router with 80,000 HRS and 30 ETH, creating the HRS/WETH pool (tx 0x61d5d655…6a01a4).
  3. The contract contains an owner-only mint with no cap: function addWork(address account, uint256 amount) public { require(msg.sender == _owner, "!warning"); _supply(account, amount); }. _supply does _balances[account] = _balances[account].add(amount) and never changes _totalSupply, so totalSupply() keeps reporting 80,000 tokens however much is minted.
  4. On 2020-10-31 at 04:45:54 UTC the deployer called addWork() and 500,000,000 HRS were minted to the deployer's own address (tx 0x9cfdfc0b…85bb2f).
  5. At 04:46:40 UTC the deployer sent those 500 million HRS to the Uniswap V2 router in a swapExactTokensForETH-type swap and received 32.26 WETH (tx 0x39dcc40a…482215). Because the minted amount dwarfed the pool's 80,000 HRS, the swap paid out nearly the whole ETH side of the pool.
  6. Separate from the mint, the transfer path can also be blocked per address: _executeTransfer begins with require(!sub[_from], "error");, and addSub(address[] memory account) lets only the owner set sub[account[i]] = true. Because the check is on _from, a listed holder cannot send tokens, including selling into the pool through transferFrom; purchases (where the pair is _from) are not blocked. The opened transactions do not show addSub being called, so this is what the code allows, not something shown to have happened.

Protocol details

Classification Token
Protocol Type Exit Scam/Honeypot
Affected asset / contract HRS
Protocol links Website @HORUS_FINANCE

Build your security review skills

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