# UniV3LiquidityAMO::recoverERC721 will cause ERC721 tokens to be permanently locked in rdpxV2Core

UniV3LiquidityAMO::recoverERC721 sends NFTs to rdpxV2Core, which has no transfer/approve logic, permanently locking them.

- Payout: Contest reward
- Severity: high
- Program: Dopex
- Credited: bart1e
- Disclosed: 2023-12-29
- Write-up quality: 6/10
- Original: https://github.com/code-423n4/2023-08-dopex-findings/issues/935
- Library page: https://bugbountylibrary.com/r/c4%3A2023-08-dopex%3AH-04
- Tags: domain:web3 impact:funds-loss kind:contest platform:code4rena sev:high tech:evm tech:solidity technique:source-review vuln:access-control vuln:business-logic

**Target:** UniV3LiquidityAMO / RdpxV2Core Solidity contracts
**Root cause:** recoverERC721 hardcodes transfer destination to RdpxV2Core, which inherits ERC721Holder but implements no ERC721 transfer/approve logic, so received NFTs cannot be moved out.
**Steps:**
1. Deploy MockERC721 and mint tokenId 1 to test contract
2. Call `mockERC721.transferFrom(address(this), address(rdpxV2Core), 1)` to send NFT to core
3. Attempt recovery via `rdpxV2Core.approveContractToSpend(address(mockERC721), address(this), 1)` — reverts
4. Observe no execute/transfer/approve ERC721 function exists in RdpxV2Core, NFT is stuck
**How found:** Code reading of RdpxV2Core's inherited contracts (AccessControl, ContractWhitelist, ERC721Holder, Pausable) and function list, noting absence of any ERC721 transfer/approve path; tested the approveContractToSpend workaround which reverts.
**Exploitation:** Admin calling recoverERC721 (intended emergency recovery) irreversibly locks all NFTs in rdpxV2Core; no attacker needed, loss triggered by legitimate admin use.
**Insight:** When a recovery function forwards assets to a treasury/core contract, verify that contract can actually move those asset types out — ERC721Holder only enables receiving, not transferring; check for missing approve/transfer or execute escape hatches.
