Unoptimized getClaimableAmount
function
Description
The getClaimableAmount
function returns the total reward amount that can be claimed from the Reward contract at the moment (including the amount already claimed):
function getClaimableAmount(address user) public view returns (uint256) {
// (...)
for (uint256 epoch = lastClaimedForEpoch[user] + 1; epoch < currentEpoch; epoch++) {
// (...)
}
return totalClaimable;
}
The initial value of lastClaimedForEpoch[user]
is zero. If a user has never claimed their reward before, this function iterates from the very first epoch to the current epoch, which can be inefficient if a number of epochs elapsed.
Note that the claim
function invokes the getClaimableAmount
function:
function claim() external {
uint amountEarned = getClaimableAmount(msg.sender);
require(amountEarned != 0, "Nothing to claim");
require(token.balanceOf(address(this)) >= amountEarned, "Not enough tokens in contract");
lastClaimedForEpoch[msg.sender] = currentEpoch - 1; // This should be the fix.
token.transfer(msg.sender, amountEarned);
emit Rewarded(msg.sender, amountEarned, block.timestamp);
}
Impact
The getClaimableAmount
and claim
function will spend a lot of gas if a number of epochs has elapsed. For instance, after three years from the creation of the Reward contract, a new user will have to spend around nine million gas in order to invoke these functions.
Recommendations
Consider optimizing the gas usage of the getClaimableAmount
function.
Remediation
This issue has been acknowledged by Sanguine Labs LTD, and a fix was implemented in commit 551ed0a7↗.