
Date: August 25, 2026
Chain: BNB Chain
Loss: about 19,999 USDT, roughly $20K
Class: fee-on-transfer misaccounting, with a sell tax that burns from the pool and syncs reserves too early
Every time someone sold FH into its PancakeSwap pool, the FH token contract took its sell tax out of the pool's own FH balance instead of the seller's, burning 80% and redistributing 20%, and then told the pool to update its records. It did all of that before the seller's tokens had actually arrived in the pool.
On August 25, 2026, an attacker turned that ordering into 19,999 USDT drained from the FH/USDT pool on BNB Chain in a single transaction. The loss was possible because the token's transfer function made the pool record a reserve far smaller than what it would really hold a moment later, and the pool's pricing math paid out USDT against that smaller number.
FH is a deflationary, fee-on-transfer token, which means its contract takes a cut whenever tokens move and uses that cut to shrink supply or reward holders. It traded against USDT in a PancakeSwap V2 pool, an automated market maker that prices trades from the two reserves it holds using a constant-product formula. When one side of the pool gets scarcer, it gets more expensive, and the pool releases more of the other side for it.
A V2 pool keeps two numbers for each token: the balance it actually holds and the reserve it last recorded. A function called sync() overwrites the recorded reserves with the current balances, and every swap is priced from the recorded reserves. Anything that can call sync() at the wrong moment decides which numbers the next trade uses.
According to the DeFiHackLabs reproduction, FH's _transfer function treated any transfer into the FH/USDT pair as a sell. On a sell it didn't take the tax from the seller's tokens. It burned 80% and redistributed 20% out of the pool's own FH balance, then called sync() before the seller's net FH had been credited to the pair. That sync locked the pool's FH reserve far below the balance it would have once the transfer finished, and PancakeSwap's constant-product math then overpaid USDT on the sell.
The sell path had no owner or admin gate, so anyone could trigger it. A shop that subtracts a returned item from its inventory count before the item reaches the shelf, and then reprices everything off that count, would undersell its stock in the same way.
That's the point for builders: a token that touches a pool's balance and then calls sync() is writing the pool's prices, and every one of those writes needs to happen after the tokens it depends on have settled.
FH did restrict buys. Only whitelisted recipients could receive FH out of the pool, and the attacker's contract wasn't on the list. DeFiHackLabs found that the attacker routed buys through a whitelisted intermediary router that was hardcoded into FHToken's constructor, then pulled the FH back out through that router's public sweepToken function. Sells had no equivalent restriction, so the attacker sold straight into the pair and set off the burn and early sync each time.
The exploit ran at 09:54:52 UTC on August 25 in transaction 0x7a3c...5bd2 at block 117,979,402, sent from the attacker's wallet at 0x7fa3...6fda to its attack contract at 0x727f...45ad, against the FHToken contract at 0xdCf0...cA37 and its FH/USDT pair at 0x8f2d...4df7. SlowMist's alert went out at 03:41 UTC on August 26 and put the loss at about $20,000, about two hours after TenArmor's alert at 01:42 UTC had flagged the same transaction with the same estimate. The DeFiHackLabs proof of concept rebuilds the attack on a fork of the block before it, using the same fee-free Moolah flash loan of 25,999.35 USDT that the on-chain transaction took and the same per-cycle buy sizes, then runs 25 buy and sell cycles through the real PancakeSwap router and the whitelisted intermediary. It ends with 19,999.018 USDT of profit, matching the reported figure.
FH wasn't alone. When DeFiHackLabs published a proof of concept for the SKYDAO exploit, it described the same burn-from-pair plus premature sync shape as its FHToken and CashCowCoin reproductions. Each of these tokens mixes its own fee accounting with a pool's reserve snapshot inside a single transfer, which is exactly where the order of operations decides who gets paid.
Olympix reads a token the way an attacker does, as states, transitions and entry points, and it takes in the context around the code, here a PancakeSwap V2 pair that prices every swap from its recorded reserves. The states that matter are the pair's real FH balance and its recorded reserve, and _transfer is a public transition that changes both. The engine maps every path through it and narrows to the feasible ones that can push those two numbers apart.
Olympix's static analysis surfaces the signal: a transfer function that burns or moves tokens out of the pair's balance and calls sync() on the pair before the incoming transfer credits it. Olympix has flagged fee-on-transfer misaccounting before in other codebases, and this is one of its most recognizable shapes.
From that flag, BugPoCer looks past the single function to the token and pair together and asks what has to stay true between them. Its method, Counterexample-Guided Invariant Refutation, infers that rule from the pool context and then executes paths until something breaks it. The invariant here is that a sell must never leave the pair's recorded reserve below the balance it actually holds once the transfer completes, since that gap is what the pool pays out against, and an ordinary sell into the pair breaks it on the first try.
The output is a counterexample: a buy through the whitelisted router, a sweep back to the attacker, and a sell that triggers the burn and early sync, repeated until the pool has paid out more USDT than the trades justify, with the minimal conditions that trigger it and a generated Forge test that replays it. That's the August 25 transaction produced in development instead of on mainnet, and because the engine runs at every merge and before release, it shows up while the tax logic is still being written.
FH's sell tax did what its authors wrote, in an order that let the pool price trades off a reserve that was briefly wrong. Any token that edits a pool's balance and then calls sync() carries an invariant about timing, and a tool can infer and test that invariant on every change. If your token or protocol shares state with an AMM pool and you'd like to see the sequences that break it before a buyer does, book a technical walkthrough.
Protocol: FH Token (FHToken)
Chain: BNB Chain
Date: August 25, 2026, 09:54:52 UTC
Loss: about 19,999 USDT (about $20K)
Attacker EOA: 0x7fa3...6fda
Attack contract: 0x727f...45ad
Exploit tx: 0x7a3c...5bd2
Block: 117,979,402
FHToken (vulnerable contract): 0xdCf0...cA37
FH/USDT PancakeSwap V2 pair: 0x8f2d...4df7
Whitelisted SwapRouter used for buys: 0x1b81...eB14
Flash loan source (Moolah): 0x8F73...5D8C
DeFiHackLabs PR #1279: FHToken PoC
TenArmor: FH Token attack alert
SlowMist: FH Token exploit alert
Cryptonomist: FHToken Security Exploit Causes $20,000 Loss on PancakeSwap V2