BNB Chain
Reserve manipulation

JUDAO's $228K Loss and How Olympix Would Have Prevented It

Loss
Loss
Exploit date
April 28, 2026

JUDAO was a deflationary token on BNB Chain with its own transfer logic, trading against USDT in a PancakeSwap V2 pair that held roughly $22M in liquidity. That transfer logic was supposed to protect the price on the way down, burning tokens out of the pair whenever a sell pushed the price lower. On April 28, 2026, an attacker turned that protection into the attack. One transaction bought JUDAO, sold a slice of it to trigger the burn, and then sold the rest back into a pair whose reserves the burn had quietly thinned, walking away with 205,259.49 USDT and 36 BNB, about $228,000 in total.

A Token That Edited Its Own Liquidity

Most tokens leave the automated market maker alone. The pair holds some JUDAO and some USDT, the constant-product formula sets the price from the ratio between them, and a swap only moves tokens the trader actually sends. JUDAO didn't leave it alone. Its custom _update function, the hook every transfer runs through, reached directly into the pair's own balance on a sell and changed it.

A PancakeSwap pair keeps an internal record of its reserves and compares it to its real token balances to price a trade. If something moves tokens out of the pair without going through a swap and then forces the pair to refresh that record, the pair will price the next trade off reserves that no longer reflect a fair market. JUDAO did exactly that, on purpose, as a price-support feature.

The Burn That Thinned the Pair

On any sell, a transfer into the pair, JUDAO called its getSellFee function to decide how to react. The function compares the current price to the previous day's and, when the rise is under 5%, sets a flag named isBurnPair. According to Verichains, the attacker's trades kept that flag on. These lines, copied from the verified JUDAO source on BscScan, are what ran when it was set:

if(isBurnPair){
    uint256 fundAmount = amount/2;
    super._update(basePair,address(0xDead),amount-fundAmount);
    super._update(basePair,address(this),fundAmount);
    ISwapPair(basePair).sync();
    accERC20PerPower+=fundAmount*1e36/totalPower;
}

Read it line by line. For a sell of a given size, JUDAO burned half that amount of JUDAO straight out of the pair's balance to the dead address, moved the other half from the pair to itself, and then called sync() on the pair. That sync told the pair to treat its now-reduced JUDAO balance as the official reserve. The pair wasn't tricked by a flash loan or a fake price feed. It was told, by the token it held, that it now had far less JUDAO than the market had priced in.

A separate daily mining step in the token's sync function did the same kind of thing, moving a slice of the pair's JUDAO to the contract and a dead address and calling sync() again, which compounded the skew.

One Transaction, Reserves Bent Twice

The attacker's contract ran the whole sequence in a single transaction. It borrowed about 2.3M USDT through a flash loan and spent 22,957.23 USDT buying roughly 5.47M JUDAO from the pair. It then sold JUDAO back in pieces. Each sell that kept the isBurnPair flag on triggered the burn block above, pulling JUDAO out of the pair and resyncing it, so every subsequent swap priced JUDAO higher than the real market would have.

With the pair's JUDAO reserve bent downward, the attacker's remaining JUDAO bought back far more USDT than the same tokens had cost. The pair paid out 205,259.49 USDT to the attacker's wallet and, through a routed WBNB swap, 36 BNB. After repaying the 2.3M USDT flash loan, that was the profit. Verichains and news reports put the loss at about $228,000.

As of November 6, 2026, the JUDAO team hadn't published a post-mortem.

Why the Feature Became the Flaw

JUDAO's burn was meant to support the price. Taking supply out of the pair on a downward move does raise the quoted price, and for an ordinary seller stepping through the fee tiers that might even behave. The feature never accounted for a trader who could choose when the burn fired and then trade against the reserves it left behind, inside the same transaction, before anyone else could react.

The deeper problem is architectural. A token that writes to the reserves of the market that prices it has coupled two systems that are supposed to stay independent. Once JUDAO could move the pair's balance and call sync() at will, the constant-product invariant the pair relies on no longer held, and any sequence that maximized the skew was there to be found.

How Olympix Would Have Surfaced the Reserve Skew

Olympix models a contract as states, transitions and entry points, and here the state that matters is the relationship between the pair's real JUDAO balance and the reserve figure it prices from. The invariant is the one every constant-product pool depends on: a trader shouldn't be able to change the pool's reserves except by swapping through it on fair terms. JUDAO's sell hook is a transition that breaks that link directly, by moving the pair's tokens and calling sync().

Olympix's static analysis would have surfaced the signal in _update, a transfer hook that calls super._update with basePair as the source and then invokes ISwapPair(basePair).sync(). A token rewriting the reserves of its own market is the kind of entry point the engine maps first. BugPoCer would then have run Counterexample-Guided Invariant Refutation against the no-free-reserve-change rule, working outward from the token and the pair together and searching for a call sequence where a trader ends with more value than they started.

The sequence it would have returned is the April 28 attack in miniature, a buy, a sell sized to keep the burn flag on, and a sell-back into the thinned pair, with the flash loan as one of the minimal conditions. BugPoCer would have delivered it as a counterexample with a generated Forge test that replays the whole loop, which would have been the drain produced in development rather than against $22M of real liquidity.

The Takeaway for Token Designers

A token that edits the reserves of its own trading pair is holding a loaded invariant. Burn mechanics, reflections and mining that touch a pair's balance and then call sync() hand any trader a lever over the price, and the only question left is who pulls it first. Keep supply changes out of the pool's accounting, or expect the pool to be priced by whoever understands the hook best. To see how Olympix tests the reserve invariants in your own token and pair contracts and returns a runnable counterexample for each one that breaks, book a technical walkthrough.

On-Chain References

Exploit tx (April 28, 2026, block 95070974): 0x956e...65bd

Attacker EOA: 0x5384...161b

Attack contract: 0x3B9b...e432

JUDAO token: 0xf55D...6888

JUDAO/USDT PancakeSwap pair: 0x5d7b...592F

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.