A flash loan chose the price at which KolanHook's buyback spent the hook's own ETH
KolanHook lost about 2.31 ETH on 15 October 2026 to a flash-loan price push, according to a DEV Community post-mortem. Its buyback swap had no effective price limit, and any caller could trigger it.
The Engineer · Build desk
Three figures: the attacker kept roughly 1.28 ETH (around $3K) as profit after repaying the flash loan; about 2.31 ETH left the hook in total over two buybacks; the flash loan from Balancer was 6.7 WETH.
| Figure | Value | Note | Claim |
|---|---|---|---|
| Attacker's profit after repaying the flash loan | 1.28 ETH, roughly | around $3K | 5 |
| ETH out of the hook, total | 2.31 ETH, about | 7 | |
| Balancer flash loan | 6.7 WETH | 6 |
What happened
- Per Defimon, the call chain runs accept to _closeWindow to _buyBack(), and the swap used sqrtPriceLimitX96 = MIN_SQRT_PRICE + 1.
- The attacker borrowed 6.7 WETH from Balancer to push the KLN spot price up before forcing the buyback.
- Defimon's alert and ChainBounty's breakdown show accept(10) and accept(42) called on two expired windows, each triggering the buyback.
- Kolan's docs describe no owner, no proxy and no pause, and say the contracts are tested but not audited.
Why it matters
- What it limits constraint On the docs' description, the deployed hook cannot be patched or paused in place, so a fix means a new deployment.
- Who's exposed exposure Any hook that holds ETH and spends it in a swap through a publicly callable function, with no price bound, can be forced the same way.
- Who has to decide decision Developers who make a close or settle function permissionless for liveness must now bound the swap price inside it, because the caller picks the timing.
Two properties combine here, and the post-mortem argues each is defensible alone. Letting anyone call accept() after expiry is a liveness choice: nobody wants windows stuck open because the buyer went offline [11]. The cost is that the timing of the buyback belongs to whoever calls, including someone who has just moved the price [11].
The second property is the price limit. In Uniswap v4 that value is the furthest price a swap may move to, so the extreme bound tells the PoolManager to fill at any price [3]. The attacker already holds KLN bought before the buyback [13]. The hook then buys with its own ETH at the inflated price and burns what it receives [10]. The post's fourth step is headed "Dump into the richer pool" [14].
The hook lost about 2.31 ETH and the attacker kept about 1.28 ETH, a gap of roughly 1.03 ETH [16]. The post does not itemize that gap in the text available. The 6.7 WETH flash loan was about 2.9 times the amount drained [17].
Nothing on the path compares the spot price with a reference price, a TWAP or a minimum output, so the hook receives whatever KLN the pool gives it at that moment [4].
For the pattern to transfer, three things have to hold in another hook. It holds ETH. A function anyone can call spends that ETH in a swap. And the pool is thin enough that a flash-loan-sized buy moves the price. Kolan meets the third by design: the hook holds the pool liquidity and refuses outside liquidity [9]. The post does not give the pool's depth. We'd expect a deeper pool to make the push cost more, but the swap would still fill at any price [3].
This is one incident and one write-up. The call chain comes from Defimon's analysis [2], the figures from Defimon's alert and ChainBounty's transaction breakdown, which the post's author rounded [7]. The Solidity in the post is the author's own illustration, not Kolan's verified source [12]. The claim that the pattern outlives the loss is the post's own: "The dollar amount is small. The pattern is not." [15]
We think the call chain supports it. Keep accept() permissionless for liveness and put the bound inside _buyBack, derived from a reference price that one transaction cannot move. The forced buy would then revert instead of fill.
What to watch
- Whether Kolan redeploys the hook with a price bound, given that its docs describe no owner, proxy or pause.
- Whether other Uniswap v4 hooks with a publicly callable function that swaps their own reserves at an extreme price limit turn up in alerts from Defimon or ChainBounty.
Clarity's read
What the record supports and how the coverage leans. The claims behind it follow.
Reality
- Evidence55
- Adoption
- Not scored
- Hype gap+15
- Incentives
- Not scored
- Confidence55
Claim ledger
Ranked by verification strength, evidence, and original report placement.
- [1]
accept(id) lets anyone close an expired window, and closing a window runs a buyback swap with the hook's own ETH.
- [2]
Per Defimon's analysis, the call chain is accept -> _closeWindow -> _buyBack(), and the buyback swaps the hook's ETH into the pool with sqrtPriceLimitX96 = MIN_SQRT_PRICE + 1.
- [3]
In Uniswap v4 the price limit is the furthest price the swap is allowed to move to; setting it to the extreme bound tells the PoolManager to fill at any price.
- [4]
Nothing in the buyback path compares the current spot price with a reference price, a TWAP or a minimum output; the amount of KLN the hook receives is whatever the pool gives it at that moment.
- [5]
On 15 October 2026 an attacker took about 2.31 ETH out of KolanHook, a Uniswap v4 hook on Ethereum mainnet, and kept roughly 1.28 ETH (around $3K) as profit after repaying a flash loan.
- [6]
The attacker used a 6.7 WETH flash loan from Balancer to push the KLN spot price up before forcing the buyback.
- [7]
Per Defimon's alert and ChainBounty's transaction breakdown, the attacker called accept(10) and accept(42) on two expired windows, each closing a window and triggering _buyBack(); about 2.31 ETH left the hook in total. The post's author rounded the figures.
- [8]
According to Kolan's own documentation there is no owner, no proxy and no pause, and the docs state the contracts are tested but not audited.
- [9]
KolanHook holds the pool liquidity and the windows, and refuses outside liquidity; KolanToken is a fixed-supply ERC-20 of 1,000,000 KLN minted once to the hook.
- [10]
When a window is closed, the hook uses ETH it holds to buy back KLN and burn it.
- [11]
The post says making accept() permissionless after expiry is a reasonable liveness choice because nobody wants windows stuck open when the buyer goes offline, but it means the buyback timing is chosen by whoever calls it, including someone who has just moved the price.
- [12]
The code sketch in the post is the author's own simplified illustration of the pattern, not Kolan's verified source.
- [13]
After the flash-loan-funded purchase pushes the KLN price up, the attacker holds KLN bought before the buyback.
- [14]
The fourth step in the post's attack sequence is headed 'Dump into the richer pool'.
- [15]
The post says: 'The dollar amount is small. The pattern is not.' It calls a hook that spends its own reserves at the pool's spot price, triggered by a function anyone can call, an exploit waiting for a caller.
- [16]
The gap between the ETH that left the hook and the attacker's net profit is about 1.03 ETH.
- [17]
The 6.7 WETH flash loan was about 2.9 times the roughly 2.31 ETH drained from the hook.
Get this ledger inside ChatGPT. Ask about any of our stories and the answer brings its figure, each claim’s verdict and the sources. Free, no account.
Sources
1 independent publisher whose own reporting we read for this story.
- dev.toKolanHook Exploit: Unguarded Uniswap v4 Buyback Drained ETH
1 article · October 16, 2026
Topics and entities
Follow any of these and your For You feed starts watching them — no settings page required.
Topics
- DeFi SecurityFollow
- Flash loan attacksFollow
- Slippage protectionFollow
- Uniswap v4 hooksFollow