As a result, Velocimeter or external protocols calling the balanceOfNFT() and balanceOfNFTAt() external functions will receive different voting balances for the same veNFT depending on which function they called.
Additionally, the internal _balanceOfNFT() function, which does not have flash-loan protection and its used in following functions:
1) tokenURI()
2) getVotes()
3) getPastVotes()
The vote function getters computes the voting balance of an account and can be used to inflate the voting balance.
If the requirement is to have all newly transferred veNFTs (ownership_change[_tokenId] == block.number) have zero voting balance to prevent someone from flash-loaning veNFT to increase their voting balance, the flash-loan protection should be consistently implemented across all the related functions.
Impact
Malicious user could flash-loan the veNFTs to inflate the voting balance of their account, since users are able to inflate their voting power, which they can use to vote for a malicious proposal
Flashloan protection check should be implemented in balanceOfNFTAt() and _balanceOfNFT() functions and to be inconsistent with flashloan prevented balanceOfNFT() function.
MohammedRizwan
Medium
balanceOfNFTAt()
inVotingEscrow.sol
does not implement flashloan protection similar tobalanceOfNFT()
functionSummary
balanceOfNFTAt()
inVotingEscrow.sol
does not implement flashloan protection similar tobalanceOfNFT()
functionVulnerability Detail
_This issue is actually referenced from spearbit's Velodrome audit which can be checked here_
The
balanceOfNFT()
function implements a flash-loan protection that returns zero voting balance ifownership_change[_tokenId] == block.number
.However, this was not consistently applied to the
balanceOfNFTAt()
and_balanceOfNFT()
functions.As a result, Velocimeter or external protocols calling the
balanceOfNFT()
andbalanceOfNFTAt()
external functions will receive different voting balances for the same veNFT depending on which function they called.Additionally, the internal
_balanceOfNFT()
function, which does not have flash-loan protection and its used in following functions:1)
tokenURI()
2)getVotes()
3)getPastVotes()
The vote function getters computes the voting balance of an account and can be used to inflate the voting balance.
If the requirement is to have all newly transferred veNFTs (ownership_change[_tokenId] == block.number) have zero voting balance to prevent someone from flash-loaning veNFT to increase their voting balance, the flash-loan protection should be consistently implemented across all the related functions.
Impact
Malicious user could flash-loan the veNFTs to inflate the voting balance of their account, since users are able to inflate their voting power, which they can use to vote for a malicious proposal
Code Snippet
https://github.com/sherlock-audit/2024-06-velocimeter/blob/main/v4-contracts/contracts/VotingEscrow.sol#L1031-L1038
Tool used
Manual Review
Recommendation
Flashloan protection check should be implemented in
balanceOfNFTAt()
and_balanceOfNFT()
functions and to be inconsistent with flashloan preventedbalanceOfNFT()
function.Duplicate of #286