Securityadvancedarticle20 min read

Visor Finance Hack: $8.2M Reentrancy in NFT Vaults | VCC

TL;DR: On December 21, 2021, Visor Finance lost 8.8M VISR tokens (~$8.2M) when an attacker exploited a vulnerable access control check in the vVISR staking rewards contract. Despite the CSV labeling this as "reentrancy in NFT vaults," the root cause was actually an authorization bypass in the d

Vietcardano teamTeam

Đội ngũ biên tập VietCardanoCommunity — các chuyên gia blockchain và Cardano, cung cấp nội dung giáo dục và nghiên cứu chuyên sâu cho cộng đồng.

Published September 14, 2026

TL;DR: On December 21, 2021, Visor Finance lost 8.8M VISR tokens (~$8.2M) when an attacker exploited a vulnerable access control check in the vVISR staking rewards contract. Despite the CSV labeling this as "reentrancy in NFT vaults," the root cause was actually an authorization bypass in the deposit() function — not a reentrancy bug. The attacker minted unlimited shares by passing a malicious contract that satisfied a flawed require(Owner() == msg.sender) check. This was Visor's third security incident in six months, following a $500k breach in June 2021 and a November 2021 "economic arbitrage" event the team refused to call a hack.


The Protocol: Active Liquidity Management on Uniswap v3

Before diving into the exploit, we need to understand what Visor Finance actually built.

Visor Finance (now merged with Gamma Strategies) developed hypervisors — smart contracts that actively manage concentrated liquidity positions on Uniswap v3. Unlike Uniswap v2's simple LP tokens, v3 positions are ERC721 NFTs representing liquidity concentrated in specific price ranges. Managing these positions requires constant rebalancing as prices move.

Visor's hypervisors automated this:

  • Non-custodial: Only depositors could withdraw funds
  • Fractional: Hypervisors minted ERC20 shares representing ownership of the NFT position
  • Composable: Shares could be used in liquidity mining, staking, and other DeFi protocols

By December 2021, Visor managed $20M+ TVL across 40+ Uniswap v3 pairs on Ethereum mainnet and L2s.

The hack didn't touch the hypervisors. It targeted a separate contract: the vVISR staking rewards contract.


The Vulnerable Contract: vVISR Staking Rewards

Visor launched a staking mechanism where users could deposit VISR tokens to earn rewards. The contract at 0xc9f27a50f82571c1c8423a42970613b8dbda14ef handled deposits and minted vVISR (vote-escrowed VISR) shares representing staked positions.

Here's where the critical flaw lived — in the deposit() function:

`solidity // Simplified vulnerable logic function deposit(address from, address to, uint256 amount) external { // FLAWED AUTHORIZATION CHECK require(IOwner(from).Owner() == msg.sender, "Not owner");

// If check passes, mint shares to 'to' based on 'from' contract's balance uint256 shares = calculateShares(from, amount); vvisr.mint(to, shares); } `

The problem: The check IOwner(from).Owner() == msg.sender only verified that the caller was the "owner" of the from contract. It did not verify:

  1. That from was the actual depositor (an EOA or trusted contract)
  2. That from had any VISR balance to deposit
  3. That the caller wasn't a malicious contract pretending to be a legitimate depositor

The Attack: Step by Step

Phase 1: Preparation (Minutes Before)

The attacker (0x8efab89b497b887cdaa2fb08ff71e4b3827774b2) received funding from Tornado Cash — standard operational security for a professional exploit.

Attack Preparation & Contract Deployment

Phase 2: Deploy Malicious Contract

The attacker deployed a contract at 0x10c509aa9ab291c76c45414e7cdbd375e1d5ace8 with a trivial Owner() function:

`solidity contract Exploit { address public owner;

constructor() { owner = msg.sender; }

// This is the entire exploit surface function Owner() external view returns (address) { return msg.sender; // Returns whoever calls it! } } `

Then they transferred ownership of this contract to themselves (tx: 0x27f2210536553392cf180c0b37055b3dc92094a5d585d7d2a51f790c9145e47c).

Phase 3: Execute the Exploit

In a single transaction (0x69272d8c84d67d1da2f6425b339192fa472898dce936f24818fda415c1c1ff3f), the attacker called:

` deposit( from: 0x10c509aa9ab291c76c45414e7cdbd375e1d5ace8, // Their malicious contract to: 0x8efab89b497b887cdaa2fb08ff71e4b3827774b2, // Their own wallet amount: [any value - irrelevant since no balance check] ) `

The vulnerable contract executed:

  1. IOwner(attackerContract).Owner() → called Owner() on attacker's contract
  2. Attacker's Owner() returned msg.sender (the attacker's EOA)
  3. require(msg.sender == msg.sender) → PASSES
  4. vvisr.mint(attackerWallet, calculatedShares) → Mints 195,000 vVISR shares
Exploit Execution Flow

Phase 4: Convert and Launder

The attacker immediately burned the 195k vVISR for 8.8M VISR (tx: 0x6eabef1bf310a1361041d97897c192581cd9870f6a39040cd24d7de2335b4546).

Then came the hard part: liquidity. The attacker dumped VISR on the Uniswap v2 VISR-ETH pool, suffering a brutal -87% price impact. They managed to extract only ~113 ETH (~$450k) — a fraction of the $8.2M nominal value.

The proceeds were washed through Tornado Cash across 7 transactions, starting with 0x0e16210218ecc487a35b9ff48fe3d9f3e9b0f50330f9e7805e38135732b85270.

Fund Flow & Price Impact

Why This Was NOT Reentrancy

The CSV title says "Reentrancy in NFT Vaults." Let's be precise:

CharacteristicReentrancyThis Exploit
External call before state update✓ Required✗ No external call
Recursive function entry✓ Required✗ Single execution
State mutation after external call✓ Required✗ State minted directly
Root causeMissing reentrancy guardMissing authorization validation

This was an access control bypass, pure and simple. The require() check was semantically wrong — it authenticated the caller's relationship to a contract rather than the caller's right to deposit assets.

The "NFT vault" reference likely stems from Visor's hypervisors managing ERC721 Uniswap v3 positions. But the hypervisors were not exploited. The team explicitly confirmed: "No positions or hypervisors are at risk."


The Pattern: Three Strikes in Six Months

This wasn't Visor's first rodeo.

Strike 1: June 2021 — $500k Beta Incident

  • Loss: $500k of $3M TVL
  • Team response: Published incident report, called it a "security breach"
  • Lesson not learned: Validation logic remained permissive

Strike 2: November 2021 — "Economic Arbitrage"

  • Incident: Users manipulated share pricing via spot price reliance
  • Team response: Defensively called it "economic arbitrage" — not a hack
  • Mudit Gupta (@Mudit__Gupta) correctly identified: "reliance on spot prices for issuing shares" is a smart contract bug
  • BlockSec later quipped: "Since last time an attack was called arbitrage, can we call it an airdrop this time?"

Strike 3: December 2021 — $8.2M vVISR Exploit

  • Same root cause family: insufficient input validation
  • Finally triggered token migration and major audit engagement
Three Incidents Timeline

The Response: Migration Over Recovery

Visor couldn't recover the stolen funds — they were already in Tornado Cash. Instead, they proposed a token migration based on a pre-exploit snapshot:

  1. Snapshot VISR balances before the exploit block
  2. Deploy new VISR token contract
  3. Airdrop new tokens to snapshot holders
  4. Deprecate old VISR

This is a social consensus solution, not a technical fix. It makes victims whole only if the new token retains value — which requires community trust.

Simultaneously, Visor engaged Quantstamp and ConsenSys Diligence for audits covering the new staking contract — the audits they should have commissioned before launching.


Lessons for DeFi Builders

Defense Checklist

1. Authorization ≠ Authentication

Checking Owner() == msg.sender authenticates the caller's relationship to a contract. It does not authorize the action. Always validate:

  • The asset actually belongs to the depositor
  • The depositor is who they claim to be (EOA vs contract)
  • The operation follows the intended flow

2. Contracts Can Lie

A contract's Owner() function can return anything. Never trust a contract's self-reported state for authorization. Use msg.sender directly or verified registry lookups.

3. "Arbitrage" Is Not a Security Classification

When users extract value through contract logic flaws, it's a vulnerability — regardless of whether it fits a trading strategy definition. Downplaying incidents delays fixes and invites larger exploits.

4. Separate Contracts, Separate Audits

Visor's hypervisors were audited. The vVISR staking contract was not. Every contract holding user funds needs its own audit, even if it's "just a staking contract."

5. Price Impact Limits Exploit Profitability

The attacker only realized ~$450k of $8.2M due to Uniswap v2's thin liquidity. Large token exploits are self-limiting — but the protocol still suffers the reputational and tokenomic damage.


FAQ

Was this a reentrancy attack?

No. Despite some references calling it "reentrancy," the vulnerability was an access control bypass in the deposit() function's authorization check. No external calls or recursive entry occurred.

Were the NFT vaults (hypervisors) compromised?

No. The hypervisors managing Uniswap v3 ERC721 positions were unaffected. The exploit targeted the separate vVISR staking rewards contract.

How much did the attacker actually profit?

~$450k (113 ETH) of the $8.2M nominal value, due to -87% price impact when dumping VISR on Uniswap v2.

Did users get their money back?

Visor proposed a token migration via pre-exploit snapshot. This makes users whole only if the new token retains value — a social, not technical, guarantee.

What audits were done after?

Visor engaged Quantstamp and ConsenSys Diligence for December/January audits covering the new staking contract.

Is Visor Finance still operating?

Visor merged with Gamma Strategies in 2022. The hypervisor technology continues under Gamma, with improved security practices and regular audits.


Key Takeaways

  1. The Visor Finance hack was an access control bypass, not reentrancy — a flawed require() check allowed unlimited share minting
  2. 8.8M VISR ($8.2M nominal) stolen, only ~$450k realized due to liquidity constraints
  3. Third incident in six months — systemic validation failures, not bad luck
  4. Hypervisors (NFT vaults) were safe — the exploit hit a separate staking contract
  5. Token migration via snapshot — social remediation when technical recovery fails
  6. "Economic arbitrage" framing delayed proper security investment — call bugs what they are

This analysis is based on on-chain data, the REKT News investigation, security researcher disclosures (storming0x, BlockSec, Georgios Konstantopoulos), and Visor Finance's official communications. All transaction hashes and contract addresses are verifiable on Etherscan.

Tags:

visor finance hackvisor finance reentrancynft vault exploitdouble vulnerability defivisor finance 8 million