Ancient Neon Koala
Medium
CGDAIncentive
incorrectly calculates the initial reward for the first claim, and is not to specification. This can lead to unexpected behavior and loss of funds for some claimaints.
In CGDAIncentive
, the initialReward
parameter is documented as the initial reward amount
.
However, in initialize the lastClaimTime = block.timestamp
:
function initialize(bytes calldata data_) public override initializer {
InitPayload memory init_ = abi.decode(data_, (InitPayload));
cgdaParams = CGDAParameters({
rewardDecay: init_.rewardDecay,
rewardBoost: init_.rewardBoost,
lastClaimTime: block.timestamp,
currentReward: init_.initialReward
});
And when the first claim is made, it is calculated in currentReward
:
function currentReward() public view override returns (uint256) {
uint256 timeSinceLastClaim = block.timestamp - cgdaParams.lastClaimTime;
uint256 available = asset.balanceOf(address(this));
// Calculate the current reward based on the time elapsed since the last claim
// on a linear scale, with `1 * rewardBoost` added for each hour without a claim
uint256 projectedReward = cgdaParams.currentReward + (timeSinceLastClaim * cgdaParams.rewardBoost) / 3600;
return projectedReward > available ? available : projectedReward;
}
Crucially, since cgdaParams.lastClaimTime
was set in the initializer, the first claim reward will be cgdaParams.currentReward + (timeSinceLastClaim * cgdaParams.rewardBoost) / 3600
. If timeSinceLastClaim
is nonzero, the initial reward will exceed the parameter set in the initializer.
Code is not to specification.
Importantly, if a non-trivial timespan passes between initialization and claims, the initialReward
will potentially be much higher than what was set by the boostCreator
. This is likely to be the case for many boosts, since it will take some time for users to begin completing actions and claiming their rewards. It's unlikely that claims would immediately begin after initialization.
Boost creators who follow this specification thinking initialReward
will be the claim reward of the first claimaint will misconfigure their vault, which can result in unintended behavior such as an incorrectly calculated total budget, prevent some claimants from claiming and causing unintended loss of funds.
Manual Review
Consider giving the first claimaint the fixed amount initialReward
and using currentReward
with cgdaParams.lastClaimTime
thereafter.