OCTOPIX Honeypot

Reported loss Not disclosed
BNB Chain
Honeypot

What happened

OCTOPIX (OCTOPIX, 8 decimals) puts a contextHandler modifier on transferFrom, the call a router uses for sales, while transfer stays unrestricted. For any sender other than the deployer, the modifier either reverts always (when the owner-set flag swapAndLiquifyEnabled is true) or reverts when the sender's whole balance is at or above _maxTrxLimit (when the flag is false, its default). Both the flag and the limit are changeable by the owner with setSwapAndLiquifyEnabled and setMaxTrxLimit, and the function named renounceOwnership does not give up ownership.

burnTokens, despite its name, credits the owner's balance without touching total supply. De.Fi's REKT database lists the token as an Exit Scam/Honeypot and describes it as: the contract owner could disable the transfer function, which restricted users in selling their tokens. The listing dates the incident 2021-06-12 and gives no loss figure.

How it happened

  1. The constructor sets decimals = 8, _totalSupply = 10000000 * 10**15 (1e22 raw units, 1e14 tokens) credited with balances[owner] = _totalSupply;, and _maxTrxLimit = 50000 * 10**15 (5e19 raw units, 0.5% of supply). swapAndLiquifyEnabled starts false. owner is set to the deployer in the Owned constructor and the source has no function that reassigns it.
  2. transfer has no condition, so a pool can pay out to buyers and wallets can send to each other.
  3. transferFrom carries contextHandler(from, to). When from != owner: with swapAndLiquifyEnabled true, require(from == owner, "Order ContextHandler"); cannot pass for any such sender; with it false, require(balances[from] < _maxTrxLimit , "Order ContextHandler"); tests the sender's entire balance, not the amount sold, and the comparison is strict, so a balance exactly equal to the limit reverts. A router sale is transferFrom(holder, pool, amount), so holders at or above 0.5% of supply cannot sell, and smaller holders can only while the owner leaves both settings alone.
  4. The owner can call setMaxTrxLimit(uint256 _input) or setSwapAndLiquifyEnabled(bool) at any time. A limit of 0 makes balances[from] < _maxTrxLimit impossible; the flag set to true blocks every non-owner transferFrom. Both are onlyOwner, which requires msg.sender == owner || msg.sender == nxOwner; nxOwner is never assigned in the source, and renounceOwnership(address _newOwner) only writes currentOwner, a variable nothing else reads, so ownership is not renounced by it.
  5. If from == owner, the modifier body is skipped, so the deployer address passes transferFrom whatever the settings are. burnTokens(uint256 _input) (onlyOwner) runs balances[msg.sender] = balances[msg.sender] + (_input); and emits a Transfer from the zero address, without changing _totalSupply, so the owner can credit itself any amount of tokens; totalSupply() would not show it.
  6. What the code allows: buyers receive tokens through the unrestricted transfer, while sales through transferFrom can be blocked for all holders or for larger holders at the owner's setting. What is documented as having happened: De.Fi's REKT database lists the token as an Exit Scam/Honeypot; no opened transaction shows the settings changed, any burnTokens call, any sale or a loss amount.

Protocol details

Classification Token
Protocol Type Exit Scam/Honeypot
Affected asset / contract OCTOPIX

Build your security review skills

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