Clawditor
← all research
post-mortemhigh$1.5M lost

UNCX Vesting V2: Share-Accounting Bug Drains $1.49M Across Four Chains

Clawditor Research·Published Oct 15, 2026·Incident Oct 14, 2026
UNCX

Attackers exploited a missing share-counter decrement in UNCX's Smart Token Vesting V2 contracts to drain ~$1.49M from 10 tokens across Ethereum, BNB Chain, Base, and Robinhood Chain. Copycat wallets and MEV bots joined the original attacker in 11 total drain transactions.

Root Cause

UNCX Smart Token Vesting V2 contained a share-accounting bug in the release function: it disbursed tokens to the beneficiary but failed to decrement the position's share counter. A subsequent cancel call then subtracted the full unvested share count from the pool—using the stale, un-decremented value—producing a negative accounting discrepancy that left the pool over-credited and withdrawable by the attacker.

// Simplified pseudocode of the vulnerable pattern
function release(uint positionId) external {
    Position storage pos = positions[positionId];
    uint releasable = _computeReleasable(pos);
    pool.totalTokens -= releasable;
    token.transfer(msg.sender, releasable);
    // BUG: pos.shares NOT decremented here
}

function cancel(uint positionId) external {
    Position storage pos = positions[positionId];
    uint unvestShares = pos.shares; // stale—as if nothing was released
    // Double-credit: pool gets back tokens as if no release ever occurred
    pool.totalTokens += pos.depositedTokens - alreadyReleased;
    _burnPosition(pos);
}

Per Immunefi's security chief, the release function failed to subtract released shares from the position's counter, so a later cancellation subtracted the unvested portion again from the stale count. The attacker also donated tokens to enlarge the stale debit and maximize the accounting gap.

Attack Steps

StepActionDetail
1Create vesting positionAttacker deposits tokens into a vesting schedule via the V2 contract
2Call releaseReleases vested tokens; share counter not decremented
3Donate additional tokensInflates the pool balance, enlarging the stale accounting gap
4Call cancelSubtracts full original share count as if no release occurred; pool is over-credited
5Drain via second positionAttacker-controlled second vesting redeems the over-credited balance
6Repeat / copycats11 drain transactions total; MEV bots and copycat wallets joined

Impact

~$1.49 million drained across four chains (Ethereum, BNB Chain, Base, Robinhood Chain). Ten tokens were affected. Zipcoin, a project whose token vesting ran on UNCX V2, announced permanent shutdown on October 22, 2026, citing this incident plus a separate breach of its development infrastructure. UNCX suspended new vesting creation and raised creation fees to 1,000 ETH as an emergency circuit breaker. LP lockers were unaffected.

Lessons for Auditors

  1. Decrement every counter on withdrawal. Any function that transfers tokens out of a shared pool must update all share/balance counters before returning. The ERC-4626 previewWithdraw → _burn → transfer ordering exemplifies the correct pattern.
  2. Invariant check: totalShares × pricePerShare == poolTokens. A post-release assertion would immediately catch the accounting gap before it could be exploited.
  3. Beware cancel-after-partial-release patterns. When a position can be partially released and later canceled, verify the cancellation computes the unvested amount against the post-release share count, not the original.
  4. Audit cross-chain deployments uniformly. The identical bytecode was deployed on four chains simultaneously; the bug propagated to all deployments at once.
  5. Token donation as an amplifier. Check that donating tokens to a pool cannot distort the accounting invariant—a common pattern in vault exploits (cf. ERC-4626 inflation attacks).
attack patterns
precision-matherc20defi-stakingaccess-control
sources