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

`LibFertilizer::getTotalRecapDollarsNeeded` only uses UnripeLP supply, breaking logic in multiple important functions

Description

All unripe tokens have been mint and distributed after the hack. They are spread between Unripe Beans and Unripe LP. However only the unripeLP supply is used to determine the total dollars needed for recapitalization. Since it is possible to convert Unripe LP to Unripe Beans, this total supply will change (tokens are burned and minted during this process), without updating the s.recapitalized variable, breaking the logic of all function using this variable and getTotalRecapDollarsNeeded or the Unripe LP supply:

  • LibFertilizer::remainingRecapitalization

  • LibUnripe::percentLPRecapped

  • LibUnripe::getPenalizedUnderlying

  • LibUnripe::getTotalRecapitalizedPercent

function getTotalRecapDollarsNeeded() internal view returns (uint256) {
@> return getTotalRecapDollarsNeeded(C.unripeLP().totalSupply());
}
/**
* @dev Returns the total dollar amount needed to recapitalize Beanstalk
* for the supply of Unripe LP.
@> * @param urLPsupply The supply of Unripe LP.
* @return totalDollars The total dollar amount.
*/
@> function getTotalRecapDollarsNeeded(uint256 urLPsupply) internal pure returns (uint256) {
@> uint256 totalDollars = C.dollarPerUnripeLP().mul(urLPsupply).div(DECIMALS);
totalDollars = (totalDollars / 1e6) * 1e6; // round down to nearest USDC
return totalDollars;
}
LibUnripeConvert.sol
function convertLPToBeans(bytes memory convertData)
internal
returns (
address tokenOut,
address tokenIn,
uint256 amountOut,
uint256 amountIn
)
{
amountIn = LibUnripe.underlyingToUnripe(tokenIn, inUnderlyingAmount);
LibUnripe.removeUnderlying(tokenIn, inUnderlyingAmount);
@> IBean(tokenIn).burn(amountIn);
amountOut = LibUnripe
.underlyingToUnripe(tokenOut, outUnderlyingAmount)
.mul(LibUnripe.percentLPRecapped())
.div(LibUnripe.percentBeansRecapped());
LibUnripe.addUnderlying(tokenOut, outUnderlyingAmount);
@> IBean(tokenOut).mint(address(this), amountOut);
}

An interesting scenario for a large Unripe LP holder is to convert all their tokens into Unripe Beans to manipulate totalUsdNeeded in LibUnripe::getPenalizedUnderlying:

redeem = underlyingAmount.mul(s.recapitalized).div(totalUsdNeeded).mul(amount).div(supply);

This would allow this holder to have many more tokens to chop or even steal tokens when recapitalization will be close to or equal to 100%.

Risk

Likelyhood: High

  • At the first conversio between unripe assets.

Impact: High

  • Will break critical functions in Beanstalk.

Recommended Mitigation

Use both the Unripe Beans supply and the Unripe LP supply to determine the total amount needed for recapitalization.

Updates

Lead Judging Commences

giovannidisiena Lead Judge about 1 year ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement
n0kto Submitter
about 1 year ago
giovannidisiena Lead Judge
about 1 year ago
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.