The ConvertFacet
features a convert
function designed to facilitate the conversion of one deposit into another, incorporating a decreaseBDV
flag to manage adjustments to the Bean Denominated Value (BDV). This flag, when activated, logically necessitates setting toBDV
to the lesser value between newBdv
and fromBdv
, aligning with the intention to reduce BDVs. Documentation further clarifies that, absent the decreaseBDV
flag, toBDV
should reflect the higher of these two values. Consequently, enabling decreaseBDV
implies adjusting toBDV
to the lower value, a logical approach intended to conserve resources. However, the current implementation incorrectly sets toBDV
to newBdv
regardless of the decreaseBDV
flag's status, undermining the intended functionality and potentially affecting the accuracy of deposit germination statuses through incorrect stem data processing.
Take a look at https://github.com/Cyfrin/2024-05-Beanstalk-3/blob/662d26f12ee219ee92dc485c06e01a4cb5ee8dfb/protocol/contracts/beanstalk/silo/ConvertFacet.sol#L69-L116
This function allows a user to convert a deposit to another deposit, given that the conversion is supported by the ConvertFacet.
Now, protocol implements a decreaseBDV
flag that could be set/unset, whenever this is set it's understandable that the toBDV
needs to be set to the lower value of newBdv & fromBdv
, this can be concluded from common logic since we are trying to decrease the BDVs and also the fact that the documentation clearly states that if the decreaseBDV
flag is not enabled, then the toBDV
needs to be set to the max of the two bdvs. So this means that if the decreaseBDV
is indeed enabled, the opposite should be done and the toBDV
should be set to the lower value of the two bdvs.
This however is not correctly implemented due to the way the check is coded. The code reads:
toBdv = (newBdv > fromBdv || cp.decreaseBDV) ? newBdv : fromBdv;
This would always chose the right value for toBdv
whenever the decreaseBDV
flag is not enabled since the cp.decreaseBDV
check would always evaluate to false and the final value that gets set for toBDV
depends on the newBdv > fromBdv
, however, whenever the decreaseBDV
flag is indeed enabled, then the cp.decreaseBDV
check is always going to be true which means that regardless of the newBdv > fromBdv
check, toBDV
is always going to be set to newBdv
which would flaw the execution, since the decrease flag is set and least of the two between newBdv & fromBdv
needs to be set to toBDV
.
Keep in mind that the toStem
value calculated is directly affected by the toBDV
passed, since the _depositTokensForConvert()
function delegates the calculation to LibTokenSilo#calculateStalkFromStemAndBdv() which has a direct relation between the toStem
& the toBDV
value.
Now since the toStem
value processed by the flow would be flawed, this would then lead to the wrong finalization of the germinating status of a deposit's stem, since the evaluation in LibGerminate._getGerminationState() would be checking with the wrong stem data.
NB: The work flow to
LibGerminate._getGerminationState()
is shown below:
ConvertFacet.convert() -> ConvertFacet._depositTokensForConvert() -> LibTokenSilo.calculateStemForTokenFromGrownStalk() -> LibGerminate.getGerminationState() -> LibGerminate._getGerminationState()
The ConvertFacet#convert()
does not always work as expected when the decreaseBDV
flag is enabled, this report shows how there is a disparity in how the conversions treat converts in relation to when the decreaseBDV
flag is set or not.
Which leads to multiple issues including the wrong finalization of the stem status since the LibGerminate._getGerminationState()
integrates with wrong data.
Manual review
Reimplement the way toBDV
is set in ConvertFacet#convert()
whenever the decreaseBDV
flag is enabled, consider breaking the checks into two and then have the toBDV
when the decreaseBDV
flag is not enabled set as the max between newBdv & fromBdv
and then when the decreaseBDV
flag is enabled, have the toBDV
set to the least between newBdv & fromBdv
.
Below is a pseudo fix in how the new implementation of the function should look
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.