# Reentrancy in `withdrawToken()` May Delete The Next User's Balance

withdrawToken() lacks a reentrancy guard; reentering settleContract() during safeTransferFrom() lets an attacker zero out a bull's withdrawable NFT balance, locking the NFT.

- Payout: Contest reward
- Severity: high
- Program: Bullvbear
- Credited: 0x4non
- Disclosed: 2022-11-05
- Write-up quality: 7/10
- Original: https://github.com/sherlock-audit/2022-11-bullvbear-judging/issues/88
- Library page: https://bugbountylibrary.com/r/sherlock%3A2022-11-bullvbear%3AH-4
- Tags: domain:web3 impact:funds-loss kind:contest platform:sherlock sev:high tech:evm tech:solidity technique:source-review vuln:access-control vuln:reentrancy

**Target:** BvbProtocol.withdrawToken() / settleContract() ERC721 settlement
**Root cause:** withdrawToken() performs an external safeTransferFrom() call before clearing withdrawableCollectionTokenId, and lacks nonReentrant, so the recipient's onERC721Received can reenter settleContract() which re-sets the mapping to the bull; the outer call then overwrites it with address(0).
**Steps:**
1. Create fake order A with self as bull and match it via `matchOrder(orderA)`
2. Settle order A so the ERC721 transfer to the bull fails, leaving `withdrawableCollectionTokenId[collection][tokenId] = bull`
3. Match real order B at any time
4. Call `withdrawToken(orderA, token1)`; inside `safeTransferFrom` the recipient's `onERC721Received` reenters `settleContract(orderB, token1)` setting the mapping to bull
5. Outer `withdrawToken` resumes and executes `withdrawableCollectionTokenId[collection][tokenId] = address(0)`, deleting the bull's balance
**How found:** Code review of withdrawToken() noting the external safeTransferFrom() call precedes the state-clearing write and lacks nonReentrant, then tracing the onERC721Received callback into settleContract()'s catch branch that re-populates the mapping.
**Exploitation:** Attacker controls the recipient contract; during the ERC721 safeTransferFrom callback it reenters settleContract() for a matched order whose transfer to the bull fails, crediting the bull's withdrawable balance, then the outer withdrawToken zeroes that balance, locking the NFT in BvbProtocol unrecoverably.
**Insight:** When a contract stores a per-token 'withdrawable owner' mapping and clears it after an ERC721 safeTransferFrom, the recipient callback can reenter any function that re-writes that same mapping (e.g. a failed-transfer catch branch), so the post-callback clear wipes the newly credited owner.
