OCTOPIX 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
- The constructor sets
decimals = 8,_totalSupply = 10000000 * 10**15(1e22 raw units, 1e14 tokens) credited withbalances[owner] = _totalSupply;, and_maxTrxLimit = 50000 * 10**15(5e19 raw units, 0.5% of supply).swapAndLiquifyEnabledstartsfalse.owneris set to the deployer in theOwnedconstructor and the source has no function that reassigns it. transferhas no condition, so a pool can pay out to buyers and wallets can send to each other.transferFromcarriescontextHandler(from, to). Whenfrom != owner: withswapAndLiquifyEnabledtrue,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 istransferFrom(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.- The owner can call
setMaxTrxLimit(uint256 _input)orsetSwapAndLiquifyEnabled(bool)at any time. A limit of 0 makesbalances[from] < _maxTrxLimitimpossible; the flag set to true blocks every non-ownertransferFrom. Both areonlyOwner, which requiresmsg.sender == owner || msg.sender == nxOwner;nxOwneris never assigned in the source, andrenounceOwnership(address _newOwner)only writescurrentOwner, a variable nothing else reads, so ownership is not renounced by it. - If
from == owner, the modifier body is skipped, so the deployer address passestransferFromwhatever the settings are.burnTokens(uint256 _input)(onlyOwner) runsbalances[msg.sender] = balances[msg.sender] + (_input);and emits aTransferfrom the zero address, without changing_totalSupply, so the owner can credit itself any amount of tokens;totalSupply()would not show it. - What the code allows: buyers receive tokens through the unrestricted
transfer, while sales throughtransferFromcan 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, anyburnTokenscall, any sale or a loss amount.
Protocol details
Evidence
- report De.Fi REKT database listing for OCTOPIX (fields supplied in meta.json; the Token Sniffer tweet 1411417470708064257 returned HTTP 404 via fxtwitter) twitter.com
- report Sourcify repository entry for the same contract (source, compiler 0.5.17, deployer and deploy block time) repo.sourcify.dev
- address BscScan verified source, OCTOPIX 0xf329e472f5338b2f0a5deb7a44945a76b7923b0a bscscan.com
Build your security review skills
Work through hands-on labs covering real exploit mechanics, review techniques, and defensive patterns.