BCE Token's $800K Loss and How Olympix Would Have Prevented It
Date: March 23, 2026
Chain: BNB Chain
Loss: 800,009.32 USDT, the entire USDT side of the BCE/USDT PancakeSwap pool
Class: a token burn that takes tokens from the liquidity pool instead of the seller
Before 05:59 UTC on March 23, 2026, the BCE/USDT pool on PancakeSwap held a little over 800,000 USDT, the stablecoin half of the market for a small deflationary token that traded around $0.10 and wasn't even listed on CoinGecko. By the end of a single transaction at 05:59:13 UTC, the pool held no USDT at all.
The token's own contract did most of the work. BCE was written to destroy part of its supply whenever people sold, but instead of taking those tokens from the seller, it took them from the PancakeSwap pool and then told the pool to update its books. Because sellers could steer how much would be destroyed, an attacker with enough borrowed capital could empty the pool's BCE side on demand and then buy back its USDT side at a price that no longer meant anything.
A Token That Burned Somebody Else's Balance
Deflationary tokens shrink their supply over time, usually by burning a slice of every transfer or sale, on the theory that scarcity supports the price. The usual design takes the burned amount from the person moving tokens, so the seller pays the cost of the burn.
BCE, deployed at 0xcdb1...9999 on BNB Chain, did it differently. According to BlockSec's weekly incident roundup, every sell into the PancakeSwap pair added an amount to a counter called scheduledDestruction, sized from the trade volume and the pool's reserves. Nothing was deducted from the seller at that moment. Later, on an ordinary transfer, the contract burned the scheduled amount directly out of the pair's BCE balance and then called sync() on the pair.
sync() is the function a Uniswap V2 style pool uses to reset its recorded reserves to whatever tokens it actually holds. A pool prices trades from those reserves, so if the BCE reserve drops while the USDT reserve stays put, every remaining BCE becomes far more expensive in USDT. In normal trading, the burn just nudged BCE's price upward a little at a time. In the hands of someone who could control the size of the burn, it was a lever on the pool's price.
Why a Seller Could Set the Size of the Burn
The teaching moment sits in one design choice. A pool's reserves belong to the liquidity providers who deposited them, and the only thing that should move them is a trade that pays for what it takes. BCE gave every seller a way to remove tokens from that pool without paying anything, simply by making trades that grew scheduledDestruction.
BlockSec's analysis puts it plainly: because the attacker controlled trade volume and could move the pool's reserves, they could push scheduledDestruction to whatever value suited them. The Defimon alert that first flagged the incident describes the same logic, a pending burn built up by sells and then taken out of the pair just before a sync() reset its reserves.
An everyday version would be a shop whose till automatically pays out a "discount" from the cash drawer whenever a customer asks loudly enough, with the size of the discount set by how much that customer bought earlier. Each payout looks like part of the store's policy, and the drawer still empties.
One Transaction, Two Contracts, an Empty Pool
The attacker's wallet, 0x9f7E...6D69, was the caller, and the transaction went to an attack contract at 0xAF7F...e9eB. BlockSec describes two attacker contracts working together inside it, which it calls MC1 and MC2. Defimon reports that the capital came from flash loans on Moolah plus borrowing through Venus Protocol, and BlockSec counts roughly 123.5M USDT borrowed in total, all of it repaid before the transaction ended.
The sequence BlockSec reconstructs runs in two rounds. In the first, the attacker bought BCE from the pool and sold some of it back, a round trip that, by BlockSec's account, raised scheduledDestruction. In the second, a much larger purchase pulled the pool's BCE reserve down toward the size of that pending burn. When the attacker then moved BCE between their own contracts, the transfer triggered the scheduled destruction, which took BCE out of the pair and synced it. Defimon reports the pair was left holding about 36.6M USDT against about 10,000 BCE, which priced each remaining BCE at thousands of dollars.
The attacker sold their leftover BCE into that distorted pool and walked out with its USDT. The pair's own Sync events show how it ended: the final reserves recorded zero USDT. Reading the transaction's transfer logs, the pair paid out 800,009.32 USDT more than it received. Of that, 680,007.93 USDT landed in the attacker's wallet and 120,001.40 USDT went through a WBNB pool, which sent 189.80 WBNB back to the attack contract.
Counting the Damage
That split explains the two loss figures in circulation. BlockSec, and the news reports that followed it, put the incident at about $679K, which matches the USDT the attacker kept. Defimon and the DeFiHackLabs reproduction put it at about 800,000 USDT, which matches what the pool lost. The liquidity providers lost the full 800,009.32 USDT, so that's the figure this post uses.
Defimon estimated BCE's market cap at about $2.1M before the attack, with roughly $1.6M of total liquidity in the BCE/USDT pool, half of it in USDT. After the transaction, the USDT half was gone and the BCE left in the pool had nothing to trade against.
How Olympix Traces a Burn Back to the Pool
Olympix reads a token the way an attacker does, as states, transitions and entry points, and it pays particular attention to state the contract changes on someone else's behalf. In BCE, the state that matters most belongs to the PancakeSwap pair: its BCE balance and the reserves it records after sync(). Every path that can lower the pair's balance is worth mapping, and the engine uses signals to decide which ones to attack first. Olympix's static analysis surfaces the signal here, a burn whose source is the pair address rather than the account initiating the transfer, followed by a call to sync(), with the burn amount computed from values a seller can influence.
The invariant BCE depended on fits in one sentence: the pool's token balance should only fall when a trade pays for the tokens it removes. BugPoCer runs Counterexample-Guided Invariant Refutation, working outward from the token and its pair as one system, inferring that rule, and executing feasible paths until one breaks it. The search narrows quickly, because only the paths that write to scheduledDestruction and then reach the burn can touch the pair's balance, and those are exactly the paths a sell and a later transfer open up.
What comes back is a counterexample: a concrete sequence in which trades grow the pending burn, a transfer executes it against the pair, sync() records the smaller reserve, and the pool's USDT can then be bought for a fraction of its value. BugPoCer pairs that sequence with the minimal conditions that trigger it and a generated Forge test that replays it, so the team would have seen the March 23 drain as a failing test in development instead of an empty pool on mainnet. The engine runs on every merge and before release, so the burn path would have been flagged the day it was written.
Burns Belong to the Burner
Fee and burn mechanics are where token contracts most often reach into balances they don't own, and a pool's reserves are the most valuable balance any token touches. If a mechanism can lower a pair's balance and resync it, the amount it removes has to be paid for by the account that caused it, and no caller should be able to choose that amount. To see how Olympix checks every path that can change your pool's reserves and returns a runnable counterexample for any that breaks the rule, book a technical walkthrough.
On-Chain References
Protocol: BCE Token (BCE/USDT pool on PancakeSwap)
Chain: BNB Chain
Date: March 23, 2026, 05:59:13 UTC
Loss: 800,009.32 USDT drained from the pair (on-chain)
Attacker EOA: 0x9f7E...6D69
Attack contract: 0xAF7F...e9eB
BCE token (vulnerable contract): 0xcdb1...9999
BCE/USDT PancakeSwap pair: 0xca23...45f7
Exploit tx: 0x85ac...d452
Block: 88215293
Sources
BlockSec: Weekly Web3 Security Incident Roundup, Mar 23 to Mar 29, 2026
More post-mortems
See every post-mortem →See what Olympix finds on your own code
Send us a repo that's already been audited, and we'll run Olympix at the same commit and show you what comes back.




