Python 3 and a terminal. No wallet, no coins, no test network. macOS and
Linux include Python; on Windows install it from python.org with “Add
python.exe to PATH” ticked, then check with python3 --version.
You will build the trap, not just read about it. Forty lines of Python reproduce a honeypot token faithfully enough to be caught by exactly the technique real honeypot checkers use — and, more usefully, to show you the two variants that technique misses. Nothing here touches a blockchain, so there is no risk and no cost.
What a Honeypot Token Is
A honeypot is a token you are allowed to buy and not allowed to sell. The purchase goes through normally. The balance appears in your wallet. The price chart climbs. Then the sale fails — every time, for everyone except the people who built it.
This is a different trap from a project whose team drains the trading pool and disappears. Here nothing is withdrawn and nothing needs to be: the restriction is written into the token's own code, so the money never has to leave, because it was never able to.
The deception works because everything a buyer normally looks at behaves correctly. Buying works, so the token looks tradeable. The wallet shows a rising figure, so it looks profitable. The chart only ever goes up — for the arithmetic reason that a market with no sellers has no downward pressure. What looks like exceptional performance is the signature of the trap.
How the Trap Is Built
A token contract decides for itself what happens on every transfer, so it can treat a sale differently from a purchase. Selling on a decentralised exchange means transferring tokens to the pool contract, which the token can detect and refuse. The common mechanisms:
Test the Exit, Not the Entrance
The decisive question is never "can I buy this?" It is "can I get out?" — and that can be answered before spending anything.
Honeypot simulators execute a purchase and an immediate sale against the real trading pool on a throwaway copy of the blockchain, then report what happened. Because the copy is discarded, the test costs nothing and risks nothing. A useful report tells you whether the sale reverted, what the buy and sell taxes actually were, and whether any transfer restriction fired.
A token can pass a pass-or-fail check while charging a forty percent tax on sales. That is not a honeypot by the strict definition, and it will still take most of your money on the way out. The numbers matter more than the label.
Run more than one simulator. They use different techniques and occasionally disagree, and a disagreement is itself information worth having before you commit funds.
What the Blockchain Already Shows You
A token's public transaction history answers the question without any tool at all. On a block explorer, open the token and read its transfers:
- Count sales against purchases. Hundreds of buys and almost no sells is the shape of a honeypot. Healthy trading has both, continuously, from many different addresses.
- Look at who does sell. If the only successful sales come from a small group of addresses, that group is the permission list.
- Look for failed transactions. A cluster of failed transfers from ordinary holders is people discovering the trap in real time.
- Check how many holders there are, and how recently the contract was created. A token minted days ago with a vertical chart has no history to judge it by.
Reading the Contract Yourself
If the source code is not published on the explorer, stop there. An unpublished contract cannot be reviewed by anyone, and choosing not to publish is a decision the creator made deliberately.
Where the code is available, you do not need to be a programmer to search it. Look for terms that control who may transfer and on what terms: blacklist, whitelist, trading enabled, maximum transaction, cooldown, and any function that changes a fee. Then ask who is permitted to call those functions, and whether the code puts any ceiling on what they can set. A fee that can be raised to any value at any time is a trap that has not been sprung yet.
If the owner can change the tax or edit the blocked list, today's successful test says nothing about tomorrow. Many honeypots launch entirely harmless, collect buyers for a few days, and are switched on afterwards. Ask whether the dangerous powers still exist, not merely whether they have been used.
The same caution applies to a contract that can be replaced. Where the code sits behind an upgradeable layer, what you reviewed today can be substituted for something else entirely, and every earlier check becomes void.
If You Are Already Holding One
There is no technique that forces a contract to let you sell. The restriction is enforced by the network exactly as written, and no service can override it. Treat any offer to recover trapped funds for a fee as a second attempt on your money by people who know you have already lost some.
What is worth doing costs nothing: review which contracts you have given permission to spend your other tokens, and withdraw any permission you no longer need. The site that sold you a honeypot frequently asked for more than a purchase, and that permission outlives the trade until you remove it.
Now Build a Honeypot and Then Catch It, in Five Steps
A honeypot token lets you buy and quietly refuses to let you sell. Reading that sentence does not prepare you to spot one; building it does. In the next twenty minutes you will write a normal token, turn it into a honeypot with a two-line change, run the standard test that detects it, and then meet the two versions that pass that test and still take your money. Every line of output below came from running these files.
Go: open a terminal in a folder you can write to, e.g.
cd ~/Desktop (Windows: cd %USERPROFILE%\Desktop).
Do: save this as hp.py. Read the difference between the two
classes before you run anything — it is three lines, and it is the entire scam.
class Token:
"""A normal token: anyone who holds it can move it."""
def __init__(self):
self.balances = {"pool": 1_000_000}
def transfer(self, sender, to, amount):
if self.balances.get(sender, 0) < amount:
raise Exception("insufficient balance")
self.balances[sender] -= amount
self.balances[to] = self.balances.get(to, 0) + amount
return amount
class Honeypot(Token):
"""Looks identical -- until you are the one selling."""
def __init__(self):
super().__init__()
self.allowed_to_sell = {"owner"}
def transfer(self, sender, to, amount):
if to == "pool" and sender not in self.allowed_to_sell:
raise Exception("execution reverted")
return super().transfer(sender, to, amount)
You should see: nothing. Run python3 hp.py and it should
return to the prompt silently — that means the file parses and both classes exist.
If not: IndentationError means the methods lost their
indentation in copying; every def sits four spaces in from
class. NameError: name 'super' is not defined cannot happen on
Python 3 — check python3 --version.
Go: same folder.
Do: save this as normal.py and run
python3 normal.py.
from hp import Token
t = Token()
t.transfer("pool", "you", 10_000)
print("you bought :", t.balances["you"], "tokens")
t.transfer("you", "pool", 10_000)
print("you sold :", 10_000, "tokens, balance now", t.balances["you"])
You should see: a complete round trip:
you bought : 10000 tokens
you sold : 10000 tokens, balance now 0
Money in, tokens out, tokens in, money out. Note that buying looked exactly the same in both classes — which is the reason honeypots work at all.
If not: ModuleNotFoundError: No module named 'hp' means the
two files are not in the same folder — ls (Windows: dir) and
confirm both names appear.
Go: same folder. This is the moment the victim experiences.
Do: save this as trapped.py and run it.
from hp import Honeypot
t = Honeypot()
t.transfer("pool", "you", 10_000)
print("you bought :", t.balances["you"], "tokens")
try:
t.transfer("you", "pool", 10_000)
print("you sold : ok")
except Exception as e:
print("you sell :", e)
print("balance :", t.balances["you"], "tokens you can never sell")
t.transfer("pool", "owner", 5_000)
t.transfer("owner", "pool", 5_000)
print("the owner sells the same token: ok")
You should see: your sale refused while the owner’s goes through:
you bought : 10000 tokens
you sell : execution reverted
balance : 10000 tokens you can never sell
the owner sells the same token: ok
execution reverted is the real message a blockchain returns, and it is
deliberately uninformative — your wallet will show it as a failed transaction with no
explanation. Meanwhile your balance is intact, the chart looks healthy, and the price keeps
rising, because everyone who buys is real and nobody can sell. The only seller is
the owner.
If not: if your sale succeeds, the to == "pool" condition in
hp.py is not being reached — check that Honeypot defines its
own transfer and that it appears inside the class.
Go: same folder. This is what honeypot-checker websites do, reduced to its principle.
Do: save this as check.py and run it. It performs the whole
round trip with a tiny amount and reports whether the exit worked.
from hp import Token, Honeypot
def can_i_sell(token_class):
"""Simulate the whole round trip BEFORE spending anything."""
t = token_class()
t.transfer("pool", "tester", 1_000)
try:
t.transfer("tester", "pool", 1_000)
return "SAFE -- a test sale went through"
except Exception as e:
return f"REFUSED -- {e}"
print("normal token :", can_i_sell(Token))
print("honeypot :", can_i_sell(Honeypot))
You should see: the trap named before any money is at risk:
normal token : SAFE -- a test sale went through
honeypot : REFUSED -- execution reverted
On a real chain the same idea runs the buy and the sell against a copy of the current blockchain state, so nothing is actually spent. The principle worth remembering is that the only meaningful test of a token is a sale, not a purchase.
If not: if both lines say SAFE, can_i_sell is being handed
an instance instead of the class — pass Token, not
Token().
Go: same folder. A defence you trust blindly is worse than none.
Do: save this as variants.py and run it. The first half is a
token that lets you sell and keeps almost everything; the second is a honeypot that behaves
perfectly until the owner changes its mind.
from hp import Honeypot
class TaxedToken:
def __init__(self, sell_tax):
self.balances = {"pool": 1_000_000}
self.sell_tax = sell_tax
def sell(self, who, amount):
kept = amount * (100 - self.sell_tax) // 100
self.balances[who] -= amount
return kept
t = TaxedToken(sell_tax=5)
t.balances["you"] = 10_000
print("sell 10,000 at the advertised 5% tax ->", t.sell("you", 10_000), "tokens back")
t = TaxedToken(sell_tax=99) # the owner raised it after you bought
t.balances["you"] = 10_000
print("sell 10,000 after the owner sets 99% ->", t.sell("you", 10_000), "tokens back")
h = Honeypot()
h.allowed_to_sell.add("you") # the token behaves perfectly at first
h.transfer("pool", "you", 10_000)
h.transfer("you", "pool", 4_000)
print("your first test sale : ok")
h.allowed_to_sell.discard("you") # one owner transaction, any time later
try:
h.transfer("you", "pool", 6_000)
except Exception as e:
print("your real sale, an hour later:", e)
You should see: both defeats:
sell 10,000 at the advertised 5% tax -> 9500 tokens back
sell 10,000 after the owner sets 99% -> 100 tokens back
your first test sale : ok
your real sale, an hour later: execution reverted
The first is not a honeypot at all — every sale succeeds, so every checker reports SAFE — and you keep one per cent. The second passes any test you run today, because the trap is a switch the owner flips whenever they choose. This is why the contract scan matters more than the sell test: what you need to know is not “can I sell right now” but “can anybody change the answer later”.
If not: if the 99% line prints 9500 as well, the second
TaxedToken was created before its tax was set — the constructor argument
must be sell_tax=99. If the last line prints nothing, the
discard call is missing, so the sale succeeded.
Without scrolling up: a token you are considering passes a well-known honeypot checker, and its chart has risen every day for a week with no sellers visible. Why is the steady rise evidence against it rather than for it, and what would you check next? Answer: a price that only ever rises, with no sell pressure at all, is the exact signature of a token nobody can sell — in a real market some holders always take profits. What to check next is the contract itself: whether an owner exists and what powers it kept (a tax it can raise, a list it can add you to, a transfer it can pause), because step 5 showed both defeats come from powers, not from today’s behaviour. If the source is not published at all, the question is already answered.
Now do it without the page: add a third variant to
variants.py — a token that allows a sale only if the amount is under 100,
so a small test sale succeeds and a real exit does not — then run your
can_i_sell checker against it and watch it report SAFE. Now improve the checker
so it catches this one too. You have just discovered why real detectors simulate a sale of
the whole balance, not a token amount.
The Habit That Prevents This
Buying is not evidence of anything. Every honeypot on every chain permits buying, because a trap that refused deposits would catch nobody. Before any purchase, simulate the sale, read the actual tax figures, check that sales are happening for ordinary holders, and confirm that nobody retains the power to change the rules after you commit.
An asset you cannot sell is not an investment at any price, however impressive the number in your wallet.