Summary
The calculateVeAmount function in veRAACToken uses a linear calculation that can be manipulated to gain disproportionate voting power through precision loss and lacks important validations.
Vulnerability Details
Found in:
function calculateVeAmount(uint256 amount, uint256 lockDuration) external pure returns (uint256) {
if (amount == 0 || lockDuration == 0) return 0;
if (lockDuration > MAX_LOCK_DURATION) lockDuration = MAX_LOCK_DURATION;
return (amount * lockDuration) / MAX_LOCK_DURATION;
}
The vulnerability exists because:
-
Linear calculation (amount * lockDuration) / MAX_LOCK_DURATION is susceptible to:
Precision loss due to division after multiplication
No minimum lock duration validation
No rounding mechanism
Potential manipulation through small amounts
-
Missing critical validations:
No check against MIN_LOCK_DURATION (365 days)
No minimum power threshold
No check for dust amounts
-
This impacts governance since the voting power is used in:
function getVotingPowerForProposal(
address account,
uint256 proposalId
) external view returns (uint256) {
uint256 snapshotBlock = proposalPowerSnapshots[proposalId];
if (snapshotBlock == 0) revert InvalidProposalId();
return getPastVotes(account, snapshotBlock);
}
Impact
High severity because:
Affects core governance mechanism
Can be exploited to gain disproportionate voting power
Impacts all proposals and voting
No recovery mechanism without contract upgrade
Proof of Concept
function exploitVotingPower() external {
uint256 smallAmount = 100;
uint256 shortDuration = MIN_LOCK_DURATION + 1;
uint256 power1 = veRAACToken.calculateVeAmount(smallAmount, shortDuration);
uint256 power2 = veRAACToken.calculateVeAmount(smallAmount * 2, shortDuration);
assert(power1 * 2 != power2);
}
Recommendation
Implement safer voting power calculation:
function calculateVeAmount(uint256 amount, uint256 lockDuration) external pure returns (uint256) {
if (amount == 0 || lockDuration < MIN_LOCK_DURATION) return 0;
if (lockDuration > MAX_LOCK_DURATION) lockDuration = MAX_LOCK_DURATION;
uint256 PRECISION = 1e18;
uint256 power = (amount * PRECISION / MAX_LOCK_DURATION) * lockDuration;
if (power < MIN_VOTING_POWER * PRECISION) return 0;
return power / PRECISION;
}
Tools Used
Manual code review
Control flow analysis