Voters who withdraw their veLACX tokens without first claiming bribe rewards will permanently lose their rewards since the withdraw function does not automatically send the rewards.
Vulnerability Details
When withdrawing veLACX tokens, token owners have to complete at least 3 steps:
(i) Call src/Voter.sol::reset(uint256 _tokenId) if they've voted (ii) Call src/VoterEscrow.sol::startCooldown(uint256 _tokenId) (iii) Wait for cooldown period to end (iv) Call src/VoterEscrow.sol::withdraw(uint256 _tokenId)
The withdraw function burns the tokenId effectively handing over ownership to the address(0) as can be seen from this function:
/** * @notice Remove a token from a given address * @dev Throws if `_from` is not the current owner. */function_removeTokenFrom(address_from,uint256_tokenId) internal {// Throws if `_from` is not the current ownerrequire(idToOwner[_tokenId] == _from);// Change the owner idToOwner[_tokenId] =address(0);// Update owner token index tracking_removeTokenFromOwnerList(_from, _tokenId);// Change count tracking ownerToTokenCount[_from] -=1; }
Once this is set, it becomes impossible for the owner of the token to claim any bribe rewards since src/Voter.sol::claimBribes requires that the caller be owner or a permitted account:
/// @inheritdoc IVoterfunctionclaimBribes(address[] memory_bribes,address[][] memory_tokens,uint256_tokenId) external {require(IVotingEscrow(veALCX).isApprovedOrOwner(msg.sender, _tokenId));for (uint256 i =0; i < _bribes.length; i++) {IBribe(_bribes[i]).getRewardForOwner(_tokenId, _tokens[i]); } }
Therefore, calling withdraw in order to close the token position before claiming bribe rewards will therefore permanently lock the rewards.