Solv BRO Vault Exploit: How Callback Reentrancy Drained $2.73 Million
On March 5, 2026, an attacker exploited a callback reentrancy vulnerability in Solv Protocol's BitcoinReserveOffering contract, turning 135 BRO tokens into 567 million and draining 38 SolvBTC (~$2.73M). The root cause? A missing reentrancy guard on a mint function that processed ERC-721 callbacks.
The Vulnerability
Solv's BitcoinReserveOffering contract used ERC-3525 semi-fungible tokens. When a user called mint(), the contract:
The Vulnerable Pattern
The core issue is a function that makes an external call before completing its state updates:
// VULNERABLE PATTERN - Do NOT use
function mint(uint256 nftId) external {
// 1. Transfer NFT (triggers onERC721Received callback)
doSafeTransferIn(msg.sender, nftId); // External call HERE
// 2. Mint BRO tokens based on NFT value
uint256 broAmount = calculateMintAmount(nftId);
_mint(msg.sender, broAmount); // State update AFTER call
}
// Attacker's contract:
function onERC721Received(...) external returns (bytes4) {
// Re-enters mint() during the callback
// BRO is minted AGAIN for the same deposit
target.mint(nftId); // Reentrancy!
return this.onERC721Received.selector;
}
The Safe Pattern
// SAFE - Checks-Effects-Interactions + ReentrancyGuard
function mint(uint256 nftId) external nonReentrant {
// 1. Checks
require(!processed[nftId], "Already processed");
// 2. Effects (update state BEFORE external call)
processed[nftId] = true;
uint256 broAmount = calculateMintAmount(nftId);
_mint(msg.sender, broAmount);
// 3. Interactions (external call LAST)
doSafeTransferIn(msg.sender, nftId);
}
The Attack in Numbers
| Metric | Value |
|---|---|
| Starting capital | 135 BRO tokens |
| Iterations | 22 loops in a single transaction |
| Tokens minted | 567,000,000 BRO |
| Value extracted | 38.05 SolvBTC (~$2.73M) |
| Exit path | BRO → SolvBTC → WBTC → WETH → ETH |
| Laundering | 1,211 ETH via Tornado Cash |
Why This Keeps Happening
Callback reentrancy is the #1 smart contract vulnerability, responsible for billions in losses since The DAO hack in 2016. Yet it remains the most common exploit because:
- ERC-721/ERC-1155 callbacks — onERC721Received and onERC1155Received are mandatory receiver hooks that create reentrancy opportunities
- ERC-777 callbacks — tokensReceived hooks on token transfers
- .call{value:}() — Native ETH transfers that trigger receive() or fallback()
- Flash loan callbacks — Lending protocols that call back into borrower contracts
How to Protect Your Contracts
1. Use ReentrancyGuard
OpenZeppelin's nonReentrant modifier is the simplest defense. Apply it to any function that makes external calls.
2. Follow Checks-Effects-Interactions
Update all state variables before making any external calls. Mark deposits as processed before transferring tokens.
3. Automated Scanning
Pattern-based scanners can catch reentrancy vulnerabilities in seconds. Every contract should be scanned before deployment.
Scan Your Contracts Now
Our scanner detects reentrancy, callback vulnerabilities, missing guards, and 20+ other vulnerability patterns. Free for basic scans.
Free Vulnerability ScanNo signup required. Results in seconds.
On-Chain Evidence
| Component | Address |
|---|---|
| Victim Contract (BRO) | 0x014e6F6ba7a9f4C9a51a0Aa3189B5c0a21006869 |
| Attack Tx | 0x44e637c7...a97a958d |
| BRO-SolvBTC Exchange | 0x1E6101728fD9920465dfA1562c5e371850103da2 |
Timeline
- March 5, 2026 — Attack executed, DefimonAlerts detects exploit
- March 5-10 — Attacker launders 1,211 ETH via Tornado Cash (RailGun rejected)
- March 10, 2026 — Rekt.news publishes analysis
- Ongoing — Solv announced coverage for affected users (<10 users impacted)