Skip to content

Build Not yet confirmed elsewhere1 publisher 2 min read Published

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

Send a correction

A flash loan let the attacker take hook ETH and profit What left KolanHook on 15 October 2026, what the attacker kept, and the flash loan that pushed the KLN price, per a DEV Community post-mortem.

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.

A flash loan let the attacker take hook ETH and profit
FigureValueNoteClaim
Attacker's profit after repaying the flash loan1.28 ETH, roughlyaround $3K5
ETH out of the hook, total2.31 ETH, about7
Balancer flash loan6.7 WETH6

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
Why these scores

Claim ledger

Ranked by verification strength, evidence, and original report placement.

  1. [1]

    accept(id) lets anyone close an expired window, and closing a window runs a buyback swap with the hook's own ETH.

    Reported Supported Source: DEV Community post-mortem2 sources— create a free account to open themView cited source
  2. [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.

    Reported Supported Source: Defimon's analysis, as cited in the DEV Community post-mortem2 sources— create a free account to open themView cited source
  3. [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.

    Reported Supported Source: DEV Community post-mortem2 sources— create a free account to open themView cited source

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.

Add to ChatGPTAdd to Claude

Sources

1 independent publisher whose own reporting we read for this story.

  1. dev.to

    1 article · October 16, 2026

    KolanHook Exploit: Unguarded Uniswap v4 Buyback Drained ETH

Share your take

Let Clarity write the post for you.

Signed-in readers get a short post drafted on this story in the register they choose — narrative, analytical, or a direct position — editable to the last word before it goes anywhere. The share buttons at the top of this story work without an account.

Topics and entities

Follow any of these and your For You feed starts watching them — no settings page required.

Topics

Entities

Loading related stories