# Risk of reentrancy onERC721Received function to manipulate collateral token configs shares

Reentrancy via onERC721Received during AutoRange transform lets borrower double-count debt shares in tokenConfigs, permanently DoSing vault borrows.

- Payout: Contest reward
- Severity: high
- Program: Revert Lend
- Credited: Aymen0909
- Disclosed: 2024-05-22
- Write-up quality: 7/10
- Original: https://github.com/code-423n4/2024-03-revert-lend-findings/issues/323
- Library page: https://bugbountylibrary.com/r/c4%3A2024-03-revert-lend%3AH-02
- Tags: domain:web3 impact:dos impact:integrity kind:contest platform:code4rena sev:high tech:evm tech:solidity technique:chained technique:source-review vuln:accounting-error vuln:business-logic vuln:dos vuln:reentrancy

**Target:** V3Vault.onERC721Received / _cleanupLoan (Uniswap V3 position transform)
**Root cause:** onERC721Received calls _cleanupLoan (which transfers the old NFT back to the owner) before _updateAndCheckCollateral for the new token; the ERC721 receive callback reenters borrow, which updates loans[tokenId].debtShares and totalDebtShares, then the outer _updateAndCheckCollateral double-adds the new shares.
**Steps:**
1. Setup: tokenConfigs[token0].totalDebtShares=10000, tokenConfigs[token1].totalDebtShares=15000; Bob's tokenId=12 has debtShares=1000.
2. Bob calls `transform` with AutoRange; transformer mints new tokenId=20 and safeTransferFrom to vault.
3. V3Vault.onERC721Received else-branch: copies loans[20].debtShares=1000, calls `_cleanupLoan(12,...)` which does _updateAndCheckCollateral(12, 1000->0) then safeTransferFrom old NFT 12 back to Bob.
4. Bob's onERC721Received callback calls `borrow` on tokenId=20 for 200 shares: loans[20].debtShares=1200, _updateAndCheckCollateral(20, 1000->1200) adds 200 to totalDebtShares.
5. Outer onERC721Received resumes and calls _updateAndCheckCollateral(20, 0->1200), adding another 1200 to totalDebtShares.
6. Net: totalDebtShares inflated by 200 vs honest path; repeat until CollateralValueLimit reverts all borrows.
**How found:** Code reading of onERC721Received ordering: noticed _cleanupLoan (which transfers NFT to owner, triggering a callback) executes before _updateAndCheckCollateral, and that borrow mutates the same loans[tokenId].debtShares the outer call later reads.
**Exploitation:** Borrower repeatedly transforms positions via AutoRange and reenters borrow from the ERC721 receive callback, inflating tokenConfigs[token].totalDebtShares each cycle until _updateAndCheckCollateral's CollateralValueLimit check reverts, permanently blocking all borrows for those tokens.
**Escalation:** Repeat the transform+reenter cycle to push totalDebtShares toward collateralValueLimitFactorX32 threshold, causing irreversible protocol-wide borrow DoS and lender losses from halted interest.
**Insight:** When a contract transfers an NFT to an external owner mid-function, the recipient's onERC721Received is a reentrancy hook; check whether state (debtShares) read after the transfer can be mutated by reentrant calls to the same accounting function, causing double-counting.
