The RWAVotingEscrow::mint function will ensure that the input vestingDuration is not less than the MIN_VESTING_DURATION, however, this validation can be circumvented by:
Creating a deposit of 1 wei with the MIN_VESTING_DURATION specified
Vesting the token ID via the VotingEscrowVesting contract for MIN_VESTING_DURATION - 1 seconds
Withdrawing the token ID via VotingEscrowVesting::withdraw and resulting in a VotingEscrow entry with a 1 value as the _remainingVestingDuration
Depositing an arbitrary amount of funds, thereby inflating the user's voting power whilst reserving funds for a very small amount of time
Impact:
A user can reserve a significant amount of funds for only one second and participate in governance which closely resembles flash-loan based manipulation attacks and thus is deemed of medium severity.
Example:
/**
* @dev Mints a new VotingEscrow token representing a locked token position. The minting process locks a specified
* amount of tokens for a given vesting duration, assigning voting power accordingly.
* @param _receiver The address that will receive the minted VotingEscrow token.
* @param _lockedBalance The amount of tokens to be locked.
* @param _duration The duration for which the tokens will be locked.
* @return tokenId The unique identifier for the minted VotingEscrow token.
*/
function mint(address _receiver, uint208 _lockedBalance, uint256 _duration) external returns (uint256 tokenId) {
// if _lockedBalance is 0, revert
if (_lockedBalance == 0) revert ZeroLockBalance();
// if _duration is not within range, revert
if (_duration < MIN_VESTING_DURATION || _duration > MAX_VESTING_DURATION) {
revert InvalidVestingDuration(_duration, MIN_VESTING_DURATION, MAX_VESTING_DURATION);
}
// create lock
tokenId = _createLock(_receiver, _lockedBalance, _duration);
// get storage
VotingEscrowStorage storage $ = _getVotingEscrowStorage();
// transfers tokens to this contract
$.lockedToken.safeTransferFrom(_msgSender(), address(this), _lockedBalance);
}
Recommendation:
We advise the code to prevent deposits for partially vested entries that have fallen below the MIN_VESTING_DURATION limit thus preventing manipulations of voting power as described.
RWV-03M: Insufficient Enforcement of Minimum Vesting Duration
Description:
The
RWAVotingEscrow::mint
function will ensure that the inputvestingDuration
is not less than theMIN_VESTING_DURATION
, however, this validation can be circumvented by:1
wei with theMIN_VESTING_DURATION
specifiedVotingEscrowVesting
contract forMIN_VESTING_DURATION - 1
secondsVotingEscrowVesting::withdraw
and resulting in aVotingEscrow
entry with a1
value as the_remainingVestingDuration
Impact:
A user can reserve a significant amount of funds for only one second and participate in governance which closely resembles flash-loan based manipulation attacks and thus is deemed of medium severity.
Example:
Recommendation:
We advise the code to prevent deposits for partially vested entries that have fallen below the
MIN_VESTING_DURATION
limit thus preventing manipulations of voting power as described.