# Univ3CollateralToken: per-minter collateral double-count drains reserves

Univ3CollateralToken counted a deposited Uni V3 NFT per minter rather than per vault, letting one position back repeated USDC/USDI borrows across 30 vaults for ~57K USD.

- Payout: Real-world hack
- Severity: high
- Program: Univ3CollateralToken
- Credited: DefimonAlerts
- Disclosed: 2026-03-24
- Write-up quality: 7/10
- Original: https://github.com/SunWeb3Sec/DeFiHackLabs/blob/main/src/test/2026-03/Univ3CollateralToken_exp.sol
- Library page: https://bugbountylibrary.com/r/dhl%3A20260324-univ3collateraltoken
- Tags: domain:web3 impact:funds-loss kind:incident platform:defihacklabs sev:high tech:evm tech:solidity technique:chained technique:source-review vuln:access-control vuln:accounting-error vuln:business-logic

**Target:** InterestVaultController / Univ3CollateralToken collateral accounting on Optimism
**Root cause:** depositedPositions/balanceOf is keyed by the NFT minter address instead of the specific vault, so a single deposited Uni V3 position is counted as collateral for every vault owned by that minter.
**Steps:**
1. Mint one single-sided WETH/USDC.e Uni V3 position via NonfungiblePositionManager.mint (fee 500, tickLower -199310, tickUpper -189510, amount1Desired 0).
2. Approve and call `Univ3CollateralToken.deposit(tokenId, 40)` to deposit the NFT against vault 40.
3. Enumerate `VaultController.vaultIDs(attackContract)` — 30 vaults share the same minter.
4. For each vault, read `vaultBorrowingPower(vaultId)` and call `borrowUSDCto(vaultId, power, attackContract)`, capped by USDI reserve balance.
5. For remaining vaults call `borrowUSDIto(vaultId, power, attackContract)` to mint USDI.
6. Call `USDI.withdrawToSecondaryReserve(amount, attackContract)` to redeem USDI for USDC.e from the secondary reserve.
7. Transfer drained USDC and USDC.e to the attacker EOA.
**How found:** Recognized that collateral lookup was keyed by minter rather than vault, implying one deposit is visible to every vault the minter owns; verified by enumerating vaultIDs and comparing per-vault borrowing power against a single NFT's value.
**Exploitation:** Attacker minted a single WETH Uni V3 NFT, deposited it once against vault 40, then looped over all 30 same-minter vaults borrowing USDC against the phantom collateral and minting USDI on the rest, redeeming USDI for USDC.e from the secondary reserve and forwarding ~57K USD to the EOA.
**Escalation:** Reusing one NFT across all 30 vaults multiplied borrow capacity far beyond a single vault's limit, draining both the primary USDC reserve and the USDI secondary reserve.
**Insight:** When a lending protocol keys collateral by owner/minter instead of by the specific vault or position, check whether one deposit is visible to sibling vaults — enumerate all vaults for an address and compare aggregate borrowing power to actual deposited value.
