LibUsdOracle.getUsdPrice()
is expected to return how much Token
is worth 1 USD. It returns price with 1e18 precission, for example 0.1e18 means that 1 USD worth 0.1 * 10 ** tokenDecimals
of that token. In some case that's correct because 1e18 precision is correctly used in LibWell.sol
calculations.
However LibDeltaB.sol
incorrectly handles precision in its inner calculations and therefore deltaB mechanism will be broken as soon as Well with non-18 decimals token will be whitelisted.
Want to note that currently only Bean/ETH is supported, but any Well can be added by governance; and 18 decimals isn't mentioned in the requirements:
https://docs.bean.money/almanac/farm/sun#minting-whitelist
LibUsdOracle.getUsdPrice()
is used in LibDeltaB.sol, let me explain using the example of USDC.
Firstly, let's have a look on LibUsdOracle.getUsdPrice()
. It will return 1e18 price for USDC because internal Oracle functions return price with 1e6 precission (like getTokenPriceFromExternal()
does):
Secondly, for Bean/USDC Well LibWell.getRatiosAndBeanIndex()
will return ratio 1e18 for USDC and 1e6 for Bean:
Thirdly, Well reserves and ratios are used to calculate the ideal amount of Bean in reserves via WellFunction:
With USDC ratio = 1e18, Bean ratio = 1e6, Bean reserve = 500e6, USDC ratio = 500e6 it must calculate result 500e6.
https://github.com/BeanstalkFarms/Basin/blob/master/src/functions/ConstantProduct2.sol#L84-L93
However actual result is much lower:
reserve = sqrt(500e6 * 500e6 * 1e6 / 1e18) = 500
deltaB
will be incorrectly priced which breaks core peg mechanism of Bean.
Manual Review
Refactor LibDeltaB.calculateDeltaBFromReserves()
to correctly calculate price from 1e18 ratios.
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.