sherlock-audit / 2023-01-ajna-judging

1 stars 0 forks source link

oxcm - [M] Calling `claimRewards()` or `unstake()` when `ajnaToken` is insufficient will permanently lose part or all of the rewards #179

Closed github-actions[bot] closed 1 year ago

github-actions[bot] commented 1 year ago

oxcm

medium

[M] Calling claimRewards() or unstake() when ajnaToken is insufficient will permanently lose part or all of the rewards

Summary

This issue concerns the balance of AjnaToken and its impact on contract execution. If the balance of AjnaToken is insufficient, it may cause issues with transferring rewards to the user.

Vulnerability Detail

In the contract code, the _claimRewards function is used to transfer rewards to the user. The rewards are transferred using the safeTransfer function of the IERC20 interface, which transfers rewards from the contract's address to the user's address. The rewards are calculated based on various factors and stored in the rewardsEarned variable. Before the transfer, there is a check to ensure that rewardsEarned is not greater than the balance of AjnaToken at the contract's address. If it is, rewardsEarned is set to the balance of AjnaToken.

However, when the contract's ajnaToken is insufficient, _claimRewards _claimRewards continue to run, modify and store according to the rewards that have been withdrawn, but the rewards are not issued in full.

Impact

If the balance of AjnaToken is 0, the rewardsEarned check will always be true and rewardsEarned will always be set to 0. This means that the user will not receive any rewards, even if they have earned them.

Code Snippet

https://github.com/sherlock-audit/2023-01-ajna/blob/main/contracts/src/RewardsManager.sol#L446-L484

function _claimRewards(
        uint256 tokenId_,
        uint256 epochToClaim_
    ) internal {
        StakeInfo storage stakeInfo = stakes[tokenId_];

        address ajnaPool = stakeInfo.ajnaPool;

        // update bucket exchange rates and claim associated rewards
        uint256 rewardsEarned = _updateBucketExchangeRates(
            ajnaPool,
            positionManager.getPositionIndexes(tokenId_)
        );

        rewardsEarned += _calculateAndClaimRewards(tokenId_, epochToClaim_);

        uint256[] memory burnEpochsClaimed = _getBurnEpochsClaimed(
            stakeInfo.lastInteractionBurnEpoch,
            epochToClaim_
        );

        emit ClaimRewards(
            msg.sender,
            ajnaPool,
            tokenId_,
            burnEpochsClaimed,
            rewardsEarned
        );

        // update last interaction burn event
        stakeInfo.lastInteractionBurnEpoch = uint96(epochToClaim_);

        uint256 ajnaBalance = IERC20(ajnaToken).balanceOf(address(this));

        if (rewardsEarned > ajnaBalance) rewardsEarned = ajnaBalance;

        // transfer rewards to sender
        IERC20(ajnaToken).safeTransfer(msg.sender, rewardsEarned);
    }

Tool used

Manual Review

Recommendation

A possible solution to this issue is to add a check for balance before the transfer of rewards.

If the balance is insufficient, the transfer should be prevented, This will ensure that the user can receive their earned rewards.

And consider adding a "force" parameter to allow users to bypass restrictions

Duplicate of #120