Summary
Division before multiplication or Multiplication on a result of a division will result in loss of precision and wrong results.
Vulnerability Details
File: contracts/libraries/LibFertilizer.sol
LibFertilizer.addUnderlying(uint256,uint256) (contracts/libraries/LibFertilizer.sol#80-124) performs a multiplication on the result of a division:
- percentToFill = usdAmount.mul(C.precision()).div(remainingRecapitalization()) (contracts/libraries/LibFertilizer.sol#83-85)
- newDepositedBeans = newDepositedBeans.mul(percentToFill).div(C.precision()) (contracts/libraries/LibFertilizer.sol#92-94)
L#83-85, L#92-94,
83: uint256 percentToFill = usdAmount.mul(C.precision()).div(
84: remainingRecapitalization()
85: );
92: newDepositedBeans = newDepositedBeans.mul(percentToFill).div(
93: C.precision()
94: );
GitHub : 80-124
File: contracts/beanstalk/sun/SeasonFacet/Sun.sol
Sun.rewardToFertilizer(uint256) (contracts/beanstalk/sun/SeasonFacet/Sun.sol#109-149) performs a multiplication on the result of a division:
- newBpf = maxNewFertilized.div(s.activeFertilizer) (contracts/beanstalk/sun/SeasonFacet/Sun.sol#117)
- newFertilized = newFertilized.add(newBpf.mul(s.activeFertilizer)) (contracts/beanstalk/sun/SeasonFacet/Sun.sol#128)
L#117, L#128,
117: uint256 newBpf = maxNewFertilized.div(s.activeFertilizer);
128: newFertilized = newFertilized.add(newBpf.mul(s.activeFertilizer));
GitHub : 109-149
File: contracts/libraries/LibGauge.sol
LibGauge.updateGrownStalkEarnedPerSeason(uint256,LibGauge.LpGaugePointData[],uint256,uint256) (contracts/libraries/LibGauge.sol#214-271) performs a multiplication on the result of a division:
- beanGpPerBdv = maxLpGpPerBdv.mul(beanToMaxLpGpPerBdvRatio).div(100e18) (contracts/libraries/LibGauge.sol#235)
- totalGaugePoints = totalGaugePoints.add(beanGpPerBdv.mul(beanDepositedBdv).div(BDV_PRECISION)) (contracts/libraries/LibGauge.sol#237-239)
L#235, L#237-239,
235: uint256 beanGpPerBdv = maxLpGpPerBdv.mul(beanToMaxLpGpPerBdvRatio).div(100e18);
237: totalGaugePoints = totalGaugePoints.add(
238: beanGpPerBdv.mul(beanDepositedBdv).div(BDV_PRECISION)
239: );
GitHub : 214-271
File: contracts/beanstalk/sun/SeasonFacet/Sun.sol
Sun.setSoilAbovePeg(uint256,uint256) (contracts/beanstalk/sun/SeasonFacet/Sun.sol#216-224) performs a multiplication on the result of a division:
- newSoil = newSoil.mul(SOIL_COEFFICIENT_HIGH).div(C.PRECISION) (contracts/beanstalk/sun/SeasonFacet/Sun.sol#219)
- newSoil = newSoil.mul(SOIL_COEFFICIENT_LOW).div(C.PRECISION) (contracts/beanstalk/sun/SeasonFacet/Sun.sol#221)
L#219, L#221,
216: function setSoilAbovePeg(uint256 newHarvestable, uint256 caseId) internal {
217: uint256 newSoil = newHarvestable.mul(100).div(100 + s.w.t);
218: if (caseId >= 24) {
219: newSoil = newSoil.mul(SOIL_COEFFICIENT_HIGH).div(C.PRECISION);
220: } else if (caseId < 8) {
221: newSoil = newSoil.mul(SOIL_COEFFICIENT_LOW).div(C.PRECISION);
222: }
223: setSoil(newSoil);
224: }
GitHub : 216-224
File: contracts/libraries/LibGauge.sol
LibGauge.updateGrownStalkEarnedPerSeason(uint256,LibGauge.LpGaugePointData[],uint256,uint256) (contracts/libraries/LibGauge.sol#214-271) performs a multiplication on the result of a division:
- newGrownStalk = uint256(s.seedGauge.averageGrownStalkPerBdvPerSeason).mul(totalGaugeBdv).div(BDV_PRECISION) (contracts/libraries/LibGauge.sol#248-250)
- newGrownStalkPerGp = newGrownStalk.mul(GP_PRECISION).div(totalGaugePoints) (contracts/libraries/LibGauge.sol#253)
L#248-250, L#253,
248: uint256 newGrownStalk = uint256(s.seedGauge.averageGrownStalkPerBdvPerSeason)
249: .mul(totalGaugeBdv)
250: .div(BDV_PRECISION);
253: uint256 newGrownStalkPerGp = newGrownStalk.mul(GP_PRECISION).div(totalGaugePoints);
GitHub : 214-271
File: contracts/beanstalk/sun/SeasonFacet/Sun.sol
Sun.rewardToFertilizer(uint256) (contracts/beanstalk/sun/SeasonFacet/Sun.sol#109-149) performs a multiplication on the result of a division:
- newBpf = maxNewFertilized.div(s.activeFertilizer) (contracts/beanstalk/sun/SeasonFacet/Sun.sol#117)
- newFertilized = newFertilized.add(newBpf.mul(s.activeFertilizer)) (contracts/beanstalk/sun/SeasonFacet/Sun.sol#147)
L#117, L#147,
117: uint256 newBpf = maxNewFertilized.div(s.activeFertilizer);
147: newFertilized = newFertilized.add(newBpf.mul(s.activeFertilizer));
GitHub : 109-149
File: contracts/libraries/LibFertilizer.sol
LibFertilizer.remainingRecapitalization() (contracts/libraries/LibFertilizer.sol#155-168) performs a multiplication on the result of a division:
- totalDollars = totalDollars / 1e6 * 1e6 (contracts/libraries/LibFertilizer.sol#165)
L#165,
155: function remainingRecapitalization()
156: internal
157: view
158: returns (uint256 remaining)
159: {
160: AppStorage storage s = LibAppStorage.diamondStorage();
161: uint256 totalDollars = C
162: .dollarPerUnripeLP()
163: .mul(C.unripeLP().totalSupply())
164: .div(DECIMALS);
165: totalDollars = totalDollars / 1e6 * 1e6;
166: if (s.recapitalized >= totalDollars) return 0;
167: return totalDollars.sub(s.recapitalized);
168: }
GitHub : 155-168
Impact
Possible loss of funds due to precision loss.
Tools Used
Manual Aided Review
Recommendations
Use multiplication before division.