
Date: June 27, 2026
Chain: BNB Chain
Loss: about 90 BNB, roughly $50K, according to Defimon
Class: index issuance priced through caller-chosen routes on thin spot pools
Cook Finance's X account went quiet in October 2022, but its index contracts on BNB Chain kept holding assets. On June 27, 2026, at 20:27:03 UTC, a contract deployed with 0.02 BNB sent dust-sized amounts of WBNB into a string of small PancakeSwap pools, minted Cook index tokens, and left with about 2.82 BNB. According to Defimon's alert, two wallets ran six transactions like it against three Cook index vaults and took about 90 BNB, roughly $50,000.
Cook's IssuanceModuleV2 lets anyone mint an index token, called a CKToken, by depositing a single asset that the module swaps into each of the index's components on PancakeSwap. It prices those swaps at whatever the pools say at that moment, follows the routes and weightings the caller supplies, and the only bound this function puts on the result is a minimum the caller also chooses. Pools thin enough to move with a few dollars of liquidity set the price at which the vaults' real holdings changed hands.
Cook Finance launched its composable index platform on BNB Chain in March 2022. Each CKToken is a basket: a vault contract holding fixed units of several component tokens, which, judging by the pools Defimon lists, include tokens such as CAKE and LINK alongside interest-bearing tokens such as ibBNB and vBNB. To mint a CKToken, a buyer doesn't need to bring every component. The issuance module takes one token, buys each component on the buyer's behalf, wraps the ones that need wrapping, and mints as many CKTokens as the purchased components can cover.
That convenience makes the module a trader, and a trader's results depend on the prices it accepts.
From the IssuanceModuleV2 source verified on Sourcify for the module at 0x7db3...89d0, this is the public entry point Defimon names:
Every argument after the CKToken comes from the caller: which token to pay with, how much, the minimum CKTokens to accept, an intermediate token for each component's swap route, and the share of the deposit to spend on each component. The internal function that does the buying is short. From the same verified file:
For each component, the loop spends the caller's chosen share of the deposit, swaps it through the caller's chosen route, and records how much of the component came back. It then divides that by how much of the component one CKToken needs, and the smallest result across all components becomes the number of CKTokens minted. The amount received from each swap is the only measure of value in the loop. Nothing in it compares that amount to an outside reference price, and the trade helper's own check only confirms that a swap delivered the quantity it was set up to expect, so whatever a PancakeSwap pool returns at that moment is what the index treats as fair.
The teaching moment is about who sets the price. When a contract trades on a caller's behalf, along a route the caller picks, through pools the caller can move, the caller is effectively quoting their own price. It's like a store letting a customer choose which supplier to restock from and then crediting them at that supplier's price, when the customer also owns the supplier. The issuance math depended on the rule that the components bought for a mint are worth what the vault gives up for them, and nothing in the path checked it.
Defimon's alert describes the sequence: the attackers first seeded thin underlying pools, including ibBNB/WBNB and vBNB/WBNB along with CAKE, MCB, XVS, DODO and LINK pairs, then called the issuance function to mint CKTokens at a distorted net asset value and unwound the liquidity, draining the index vaults' real holdings.
The sample transaction Defimon published shows the pattern on-chain. The attacker's wallet 0xee18...0f8b created a contract at 0xeffc...cee4 in BNB Chain block 106741000, sending it 0.02 BNB. Inside that one creation, the contract pushed 0.001 and 0.0001 WBNB amounts through the PancakeSwap router into eight different pairs and generated 611 event logs across 23 tokens. The router's WBNB transfers tell the result: 0.0036 WBNB went in, and 2.82 WBNB came back out, 0.88 from one pair, 1.94 from another and the rest in six transfers of about 0.0002 each.
Defimon reports that the attack ran across six transactions from two wallets, 0xee18...0f8b and 0x5161...4278, against three CKToken vaults, with the module at 0x7db3...89d0 and the CKToken at 0x8482...4eb4 among the victims. As of October 20, 2026, Cook Finance hadn't posted about the exploit on its X account, whose most recent post is from October 2022.
Olympix reads a contract the way an attacker does, as states, transitions and entry points, and it takes in the context around the code, including which external pools the contract trades through and how deep they are. In IssuanceModuleV2, the state that matters is each CKToken vault's component holdings against its supply, and the entry points that matter are every issue and redeem path that changes either one.
The signal here is a public function that executes swaps with caller-supplied routes and weightings at live spot prices and mints a token from the amounts received, with no oracle comparison and only a caller-chosen minimum as a bound. From that signal, the engine narrows from every path through the module, to the feasible ones any caller can reach, to the paths whose mint amount depends on a pool price the same caller can move in the same transaction, which is where the invariant lives: minting a CKToken must never cost the vault more in component value than the issuer pays in.
The counterexample is the sequence Defimon describes, run against a fork of the module: move a thin component pool, call issueWithSingleToken2 with weightings and routes that send the purchase through it, then unwind the pool, ending with the vault holding less value per CKToken than before. Olympix returns that sequence of calls and state changes, the minimal conditions that trigger it, and a generated Forge test that replays it, which is the June 27 drain produced in development rather than on mainnet. Running at every merge and before release, the engine would have raised it when issueWithSingleToken2 shipped.
Cook's module did exactly what its parameters asked, and its parameters let the person minting decide where the prices came from. For any contract that trades on a user's behalf, the safe default is to bound every swap against a price the user can't move in the same transaction, and to test that bound with an attacker who controls the pools. To see how Olympix finds the prices your contracts trust and returns a runnable counterexample where a caller can set them, book a technical walkthrough.
Protocol: Cook Finance (IssuanceModuleV2, CKToken index vaults)
Chain: BNB Chain
Date: June 27, 2026, sample transaction at 20:27:03 UTC
Loss: about 90 BNB (~$50K) per Defimon; 2.82 BNB in the sample transaction
Attacker 1: 0xee18...0f8b
Attacker 2: 0x5161...4278
Attack contract (sample tx): 0xeffc...cee4
Vulnerable module (IssuanceModuleV2): 0x7db3...89d0
Victim CKToken (named by Defimon): 0x8482...4eb4
Sample exploit tx: 0x51e4...1bca
Block: 106741000