Approved addresses cannot merge or withdraw from veLP NFTs because of conflicting checks
Summary
The protocol intends to allow approved addresses to do many things on behalf of a veLP NFT owner. All other functions work properly, but merge and withdraw will always revert if called by an approved address.
Vulnerability Detail
Lets take the example of merge. Here, the isApprovedOrOwner() checks make sure that the 'from' tokenID and 'to' tokenID are both either approved to/ or owned by the caller.
In this merge flow, it later calls _burn(). This is the burn function :
function _burn(uint _tokenId) internal {
require(_isApprovedOrOwner(msg.sender, _tokenId), "caller is not owner nor approved");
address tokenOwner = ownerOf(_tokenId);
approve(address(0), _tokenId); <- @audit
_moveTokenDelegates(delegates(tokenOwner), address(0), _tokenId);
_removeTokenFrom(tokenOwner, _tokenId);
emit Transfer(tokenOwner, address(0), _tokenId);
}
This logic never checks if the msg.sender is an approved address for the tokenID, using idToApprovals[tokenID]. This will revert if called by the approved address, but that is incorrect because the intention is clearly to allow an approved address to use these functions, as seen with the logic of isApprovedorOwner() which is used in the merge and withdraw functions' access control checks.
Impact
Approved addresses will not be able to access merge() and withdraw() functions.
Chinmay
Medium
Approved addresses cannot merge or withdraw from veLP NFTs because of conflicting checks
Summary
The protocol intends to allow approved addresses to do many things on behalf of a veLP NFT owner. All other functions work properly, but merge and withdraw will always revert if called by an approved address.
Vulnerability Detail
Lets take the example of merge. Here, the
isApprovedOrOwner()
checks make sure that the 'from' tokenID and 'to' tokenID are both either approved to/ or owned by the caller.In this merge flow, it later calls
_burn()
. This is the burn function :The relevant part here is the approve() function.
This logic never checks if the msg.sender is an approved address for the tokenID, using
idToApprovals[tokenID]
. This will revert if called by the approved address, but that is incorrect because the intention is clearly to allow an approved address to use these functions, as seen with the logic of isApprovedorOwner() which is used in the merge and withdraw functions' access control checks.Impact
Approved addresses will not be able to access merge() and withdraw() functions.
Code Snippet
https://github.com/sherlock-audit/2024-06-velocimeter/blob/63818925987a5115a80eff4bd12578146a844cfd/v4-contracts/contracts/VotingEscrow.sol#L537
Tool used
Manual Review
Recommendation
In the
_burn()
function use_clearApproval()
in place of approve().Duplicate of #42