Every season, someone calls gm() or sunrise() on the Beanstalk diamond to start the next season. The contract calculates what incentive the caller should receive which is minted bean tokens. If the new season is started with a delay of 239 or 240 seconds, no rewards will be minted.
} else if (secondsLate <= 240) {
if (secondsLate <= 212) {
return _scaleReward(beans, 8_243_872);
}
...
} else if (secondsLate <= 270) {
if (secondsLate <= 240) {
return _scaleReward(beans, 10_892_553);
}
In case the secondsLate is 239, 240, it goes into the (secondsLate <= 240) outer clause, but does not hit any of the inner if clauses, so it skips the return statement. The function will return 0 and not mint any tokens.
The check below has been erroneously moved to the <= 270 block. It will never get hit.
if (secondsLate <= 240) {
return _scaleReward(beans, 10_892_553);
}
The mitigation is to move the inner secondsLate <= 240 clause into the outer secondsLate <= 240 clause:
Impact Details
The correct amount of Bean tokens to mint for a delay of 240 seconds is 54462765 or 54 tokens which doesn't get minted and is lost. As such, the vulnerability presents a "permanent loss of unclaimed yield".