29225 - [SC - Insight] EarlyZEROVesting is having a rounding issue and...
Submitted on Mar 11th 2024 at 06:28:52 UTC by @dontonka for Boost | ZeroLend
Report ID: #29225
Report type: Smart Contract
Report severity: Insight
Target: https://github.com/zerolend/governance
Impacts:
EarlyZEROVesting contract improvement: rounding issue and missing approval
Description
Brief/Intro
EarlyZEROVesting
is having at least two minor issues which would need to be addressed. There is one rounding issue and one missing approval in order to transfer the vested NFT to the staking bonus contract.
Vulnerability Details
Rounding issue
If we take the following example calling startVesting
with an amount of 99
.
pending = 74.25
= (amount * 75) / 100 --> 74
upfront = 24.75
= (amount * 25) / 100 --> 24
spent = 99
As you can see, the NFT will represent a value of 98
(74 + 24) instead of 99, which is a loss of 1 wei.
Missing approval
When startVesting with staking operation, the current implementation will not work as it is missing an approval
operation, so currently the vesting.safeTransferFrom
will always revert.
Impact Details
1 Wei precision lost per startVesting call.
Missing approval when staking, which will make this always reverts.
Recommendation
Apply the following changes in order to fix the two issues reported in this report.
function startVesting(uint256 amount, bool stake) external {
require(enableVesting || stake, "vesting not enabled; staking only");
earlyZERO.burnFrom(msg.sender, amount);
// vesting for earlyZERO is 25% upfront, 1 month cliff and
// the rest (75%) across 3 months
+ //@audit: this will fix the 1 wei loss in precicion rounding issue.
+ uint256 upfront = (amount * 25) / 100;
+ uint256 pending = amount - upfront;
uint256 id = vesting.mint(
stake ? address(this) : msg.sender, // address _who,
- (amount * 75) / 100, // uint256 _pending,
- (amount * 25) / 100, // uint256 _upfront,
+ pending, // uint256 _pending,
+ upfront, // uint256 _upfront,
86400 * 30 * 3, // uint256 _linearDuration,
86400 * 30, // uint256 _cliffDuration,
block.timestamp, // uint256 _unlockDate,
false, // bool _hasPenalty
IVestedZeroNFT.VestCategory.EARLY_ZERO
);
// if the user chooses to stake then make sure to give the staking bonus
if (stake) {
+ //@audit: this will fix the missing approval
+ vesting.approve(address(vesting), id);
vesting.safeTransferFrom(
address(this),
stakingBonus,
id,
abi.encode(true, msg.sender)
);
}
spent += amount;
}
Proof of Concept
Not applicable really, simple by inspecting the code.
Last updated
Was this helpful?