DeFiHardhat
21,000 USDC
View results
Submission Details
Severity: high
Invalid

`LibUnripe.sol::getPenalizedUnderlying` does not implement recapitalized^2 correctly

Summary

https://github.com/Cyfrin/2024-05-Beanstalk-3/blob/662d26f12ee219ee92dc485c06e01a4cb5ee8dfb/protocol/contracts/libraries/LibUnripe.sol#L149-L171

The comment suggests that the chop rate is calculated as %DollarRecapitalized^2 * share of unripe tokens, but the actual calculation in the code does not square the %DollarRecapitalized.

Vulnerability Details

`function getPenalizedUnderlying(
    address unripeToken,
    uint256 amount,
    uint256 supply
) internal view returns (uint256 redeem) {
    require(isUnripe(unripeToken), "not vesting");
    AppStorage storage s = LibAppStorage.diamondStorage();
    // getTotalRecapDollarsNeeded() queries for the total urLP supply which is burned upon a chop
    // If the token being chopped is unripeLP, getting the current supply here is inaccurate due to the burn
    // Instead, we use the supply passed in as an argument to getTotalRecapDollarsNeeded since the supply variable
    // here is the total urToken supply queried before burnning the unripe token
    uint256 totalUsdNeeded = unripeToken == C.UNRIPE_LP ? LibFertilizer.getTotalRecapDollarsNeeded(supply) 
        : LibFertilizer.getTotalRecapDollarsNeeded();
    // chop rate = total redeemable * (%DollarRecapitalized)^2 * share of unripe tokens
    // redeem = totalRipeUnderlying * (usdValueRaised/totalUsdNeeded)^2 * UnripeAmountIn/UnripeSupply;
    // But totalRipeUnderlying = CurrentUnderlying * totalUsdNeeded/usdValueRaised to get the total underlying
    // redeem = currentRipeUnderlying * (usdValueRaised/totalUsdNeeded) * UnripeAmountIn/UnripeSupply
    uint256 underlyingAmount = s.u[unripeToken].balanceOfUnderlying;
    redeem = underlyingAmount.mul(s.recapitalized).div(totalUsdNeeded).mul(amount).div(supply);
    // cap `redeem to `balanceOfUnderlying in the case that `s.recapitalized` exceeds `totalUsdNeeded`.
    // this can occur due to unripe LP chops.
    if(redeem > underlyingAmount) redeem = underlyingAmount;
}`

The comment suggests that the chop rate is calculated as %DollarRecapitalized^2 * share of unripe tokens, but the actual calculation in the code does not square the %DollarRecapitalized. The formula used is:
redeem = underlyingAmount.mul(s.recapitalized).div(totalUsdNeeded).mul(amount).div(supply);
This formula calculates the proportion of the underlyingAmount based on the ratio of s.recapitalized to totalUsdNeeded, and then applies the proportion of the amount of unripe tokens to the total supply. However, it does not square the recapitalization percentage as mentioned in the comment (%Recapitalized^2).

Impact

If the actual chop rate applied is less severe than intended (because the squaring of the recapitalization percentage is omitted), then the penalized amount of Ripe Tokens might be higher than it should be. This results in less penalization for the holders of Unripe Tokens, which could undermine the intended economic incentives and risk management strategies of the protocol.

Tools Used

Manual Review

Recommendations

Ensure the chop rate calculation squares the recapitalization percentage if that is the intended logic.
+ redeem = underlyingAmount.mul(s.recapitalized)^2.div(totalUsdNeeded).mul(amount).div(supply);

Updates

Lead Judging Commences

giovannidisiena Lead Judge about 1 year ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.