A one-wei deposit let an attacker withdraw 14,838 DAI from a Polygon Uniswap V4 vault
An attacker with one share unit withdrew about 14,838 DAI from a Polygon Uniswap V4 vault on October 9, Panda Security found. The zero-liquidity fallback paid out the whole balance, so every fallback needs the main path's proportional-payout check.
The Engineer · Build desk

Key figures: approximately 14,813.55 USDC reached the attacker's EOA after swaps, repayment of temporary liquidity and execution costs; the Withdrawn event shows 1 share burned; the Repositioned event recorded new liquidity of 0.
| Figure | Value | Note | Claim |
|---|---|---|---|
| Reached attacker's EOA after swaps and costs, approx. | 14,813.55 USDC | 15 | |
| Shares burned | 1 share | 14 | |
| New liquidity | 0 liquidity | 10 |
What happened
- The Repositioned event recorded a new range of 276324 to 276327 and newLiquidity of 0, so the vault held its tokens with no position behind them.
- The Withdrawn event shows one share burned, a daiOut of 14838466608989135865711 and a usdcOut of 0.
- After swaps, repayment of the temporary liquidity and execution costs, about 14,813.55 USDC reached the attacker's externally owned account.
Why it matters
- Who has to decide decision Audit scope has to cover branches that look inert. Panda Security says this one sat inside what looked like a harmless fallback and was neither reentrancy nor an arithmetic trick.
- Who's exposed exposure Any vault state reachable through a public function is reachable by someone who can move the pool price. Here reposition() opened as soon as a swap pushed the tick out of range.
- What it costs cost The loss was set by the vault's balance, not the attacker's stake. One wei of DAI bought a withdrawal limited only by what the vault held.
The bug is a missing share fraction. On the normal path, _withdraw() sizes the payout as liquidity times shareAmount, divided by currentTotalShares [8]. On the zero-liquidity path it skips that and transfers the vault's full DAI and USDC balances to the recipient. The branch uses shareAmount only in the event it emits [7].
Panda Security's review, published on DEV Community, says the code treats two different states as one. According to the review, the code takes a state in which no capital is deployed as liquidity to mean "the current redeemer owns every idle asset", and the review names that assumption the root cause [9].
The state is reachable through ordinary calls. _reposition() removes the old position, leaves the withdrawn assets in the vault, and opens a new position only if the computed newLiquidity is above zero [2]. In this transaction the new range was 276324 to 276327, three ticks wide [17], and newLiquidity came out as 0 [10]. About 14,838 DAI sat at the vault address with no position behind it [3].
For callers other than the minter, the guard on reposition() is the current tick. The call reverts with PositionInRange unless the tick is outside the active range [4]. The attacker moved the tick with a large swap funded by temporary liquidity [5]. The review wrote that "an externally influenced strategy operation could place the vault into a state that its withdrawal accounting did not handle safely" [11].
The share checks did not slow any of this. They revert on a zero amount and on a caller holding fewer shares than requested [13]. One share unit satisfied both. The Deposited event shows daiAmount of 1, and with DAI at 18 decimals that is one wei [12].
The two headline figures measure different things. The vault paid out roughly 14,838.47 DAI, and about 14,813.55 USDC reached the attacker's EOA after swaps, repayment of temporary liquidity and execution costs [15]. Treating one DAI as one USDC, the gap is about 24.92 [18]. The review excerpt does not split that gap by cost.
In our view the invariant belongs to the function, not the branch. Whichever path runs, a redeemer should receive no more than shareAmount over total shares of what the vault holds. A fallback that moves tokens is a payout path, and it gets the same test as the main one, including a test that forces liquidity to zero. The zero-liquidity state comes out of a public call that any out-of-range tick opens, so that test is cheap to write.
What to watch
- Whether the vault's operator or Panda Security publishes a fix or post-mortem. The review excerpt does not describe one.
- Whether other Uniswap V4 vaults with a public reposition() path carry a fallback that sweeps the whole token balance to the redeemer.
Clarity's read
What the record supports and how the coverage leans. The claims behind it follow.
Reality
- Evidence72
- Adoption
- Not scored
- Hype gap0
- Incentives
- Not scored
- Confidence65
Claim ledger
Ranked by verification strength, evidence, and original report placement.
- [1]
Panda Security's review found the attacker acquired one share unit, manipulated the pool so reposition() produced zero liquidity, then called withdraw(), after which the vault emptied its DAI and USDC balances into the attacker's contract.
- [2]
The internal _reposition() function removes the old position, leaves the withdrawn assets in the vault, and calculates liquidity for a new position, which is opened only if newLiquidity is greater than zero; liquidity is then set to newLiquidity.
- [3]
The old position had already been removed, leaving roughly 14,838 DAI at the vault address, and no replacement position was created.
- [4]
An account other than the minter can call reposition() only when the current tick is outside the active range (tickLower to tickUpper); otherwise the call reverts with PositionInRange.
- [5]
Using temporary liquidity, the attacker executed a large swap in the Uniswap V4 pool and pushed the current tick outside the vault's existing range, which made the public reposition() path available.
- [6]
On October 9, 2026, a transaction on Polygon exposed an accounting flaw in a liquidity vault; the attacker held one minimal share unit and withdrew approximately 14,838.47 DAI.
- [7]
In the zero-liquidity branch of _withdraw(), the vault reads its DAI and USDC balances and transfers both in full to the recipient instead of calculating a proportional payout; the branch uses shareAmount only in the Withdrawn event it emits.
- [8]
On the normal path the vault computes toRemove as liquidity * shareAmount / currentTotalShares, where currentTotalShares = totalShares + shareAmount.
- [9]
Panda Security says the code incorrectly treats 'no capital is currently deployed as liquidity' as equivalent to 'the current redeemer owns every idle asset', and calls that mistaken assumption the root cause of the loss.
- [10]
In the exploit transaction the Repositioned event recorded newTickLower = 276324, newTickUpper = 276327 and newLiquidity = 0.
- [11]
The review wrote that the deeper problem was that "an externally influenced strategy operation could place the vault into a state that its withdrawal accounting did not handle safely."
- [12]
The Deposited event records daiAmount = 1 and sharesIssued = 1; DAI uses 18 decimals, so the value 1 is one wei of DAI.
- [13]
The withdrawal checks revert with ZeroAmount if shareAmount is zero and with InsufficientShares if the caller holds fewer shares than requested.
- [14]
The on-chain Withdrawn event shows sharesBurned = 1, daiOut = 14838466608989135865711 and usdcOut = 0.
- [15]
The vault paid out roughly 14,838.47 DAI; after swaps, repayment of temporary liquidity and execution costs, approximately 14,813.55 USDC reached the attacker's EOA.
- [16]
Panda Security says this was not a reentrancy exploit and did not depend on an obscure arithmetic trick; the failure sat inside what looked like a harmless fallback branch.
- [17]
The new position range was three ticks wide.
- [18]
The gap between the DAI paid out and the USDC that reached the attacker is about 24.92, treating one DAI as one USDC.
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.toHow One Wei of Shares Drained 14,838 DAI: A Uniswap V4 Vault Exploit
1 article · October 15, 2026
Topics and entities
Follow any of these and your For You feed starts watching them — no settings page required.
Topics
- DeFi ExploitsFollow
- Smart Contract SecurityFollow
- Automated liquidity vaultsFollow