Closed code423n4 closed 2 years ago
Liquidator contracts should always implement onERC721Received
, responsibility of a bad liquidator implementation is not of NFTVault
.
Agree with the sponsor. In this case, the liquidator is responsible for their own contract and also the party harmed if their contract is written incorrectly. Anyone writing one of these contracts surely understands that they will need to be able to receive an ERC721 and should comply with spec.
Lines of code
https://github.com/code-423n4/2022-04-jpegd/blob/e72861a9ccb707ced9015166fbded5c97c6991b6/contracts/vaults/NFTVault.sol#L915-L941
Vulnerability details
For a liquidated position with a borrowType of
USE_INSURANCE
, when insuraceRepurchaseTimeLimit is over (position.liquidatedAt + settings.insuraceRepurchaseTimeLimit
) and the positionOwner still notrepurchase()
, the collateral NFT of the position can and can only be claimed by the liquidator viaclaimExpiredInsuranceNFT()
.In
claimExpiredInsuranceNFT()
, the Collateral NFT willsafeTransferFrom
address(this)
to theposition.liquidator
, which is themsg.sender
used inliquidate()
, must implement theonERC721Received
method for thesafeTransferFrom
to be successfully.Otherwise, the
claimExpiredInsuranceNFT()
transaction will always fail withUNSAFE_RECIPIENT
inIERC721.safeTransferFrom()
.https://github.com/code-423n4/2022-04-jpegd/blob/e72861a9ccb707ced9015166fbded5c97c6991b6/contracts/vaults/NFTVault.sol#L915-L941
In that case, the collateral NFT will be frozen in NFTVault, even though liquidator already repaid for the position with
stablecoin
.PoC
Given:
SAFE
is a multisig timelock contract with noonERC721Received
.SAFE
liquidate()
. at L851 repaydebtAmount
ofstablecoin
;settings.insuraceRepurchaseTimeLimit
seconds later,SAFE
claimExpiredInsuranceNFT()
to get back the collateral NFT, the tx always reverts withUNSAFE_RECIPIENT
.Recommendation
Change to: