DeFiHardhatOracleProxyUpdates
100,000 USDC
View results
Submission Details
Severity: low
Invalid

Reentrancy vulnerabilities (no theft of ethers)

Summary

Possible Reentrancy vulnerabilities (no theft of ethers). User funds or contract state may be compromised due to state being updated after external calls.

Vulnerability Details

File: contracts/beanstalk/silo/SiloFacet/Silo.sol
/// @audit ******************* Issue Detail *******************
Reentrancy (no theft of ethers) in Silo._claimPlenty(address) (contracts/beanstalk/silo/SiloFacet/Silo.sol#154-164):
External calls:
- sopToken.safeTransfer(account,plenty) (contracts/beanstalk/silo/SiloFacet/Silo.sol#160)
State variables written after the call(s):
- delete s.a[account].sop.plenty (contracts/beanstalk/silo/SiloFacet/Silo.sol#161)
/// @audit ************** Possible Issue Line(s) **************
L#160, L#161,
/// @audit ****************** Affected Code *******************
154: function _claimPlenty(address account) internal {
155: // Plenty is earned in the form of the sop token.
156: uint256 plenty = s.a[account].sop.plenty;
157: IWell well = IWell(s.sopWell);
158: IERC20[] memory tokens = well.tokens();
159: IERC20 sopToken = tokens[0] != C.bean() ? tokens[0] : tokens[1];
160: sopToken.safeTransfer(account, plenty);
161: delete s.a[account].sop.plenty;
162:
163: emit ClaimPlenty(account, address(sopToken), plenty);
164: }

GitHub : 154-164

File: contracts/beanstalk/sun/SeasonFacet/Sun.sol
/// @audit ******************* Issue Detail *******************
Reentrancy (no theft of ethers) in Sun.stepSun(int256,uint256) (contracts/beanstalk/sun/SeasonFacet/Sun.sol#63-76):
External calls:
- newHarvestable = rewardBeans(uint256(deltaB)) (contracts/beanstalk/sun/SeasonFacet/Sun.sol#66)
- C.bean().mint(address(this),newSupply) (contracts/beanstalk/sun/SeasonFacet/Sun.sol#86)
State variables written after the call(s):
- setSoilAbovePeg(newHarvestable,caseId) (contracts/beanstalk/sun/SeasonFacet/Sun.sol#67)
- s.f.soil = amount.toUint128() (contracts/beanstalk/sun/SeasonFacet/Sun.sol#228)
- s.season.abovePeg = true (contracts/beanstalk/sun/SeasonFacet/Sun.sol#68)
/// @audit ************** Possible Issue Line(s) **************
L#66, L#86, L#67, L#228, L#68,
/// @audit ****************** Affected Code *******************
63: function stepSun(int256 deltaB, uint256 caseId) internal {
64: // Above peg
65: if (deltaB > 0) {
66: uint256 newHarvestable = rewardBeans(uint256(deltaB));
67: setSoilAbovePeg(newHarvestable, caseId);
68: s.season.abovePeg = true;
69: }
70:
71: // Below peg
72: else {
73: setSoil(uint256(-deltaB));
74: s.season.abovePeg = false;
75: }
76: }
86: C.bean().mint(address(this), newSupply);
228: s.f.soil = amount.toUint128();

GitHub : 63-76

File: contracts/beanstalk/sun/SeasonFacet/Weather.sol
/// @audit ******************* Issue Detail *******************
Reentrancy (no theft of ethers) in Weather.sop() (contracts/beanstalk/sun/SeasonFacet/Weather.sol#181-213):
External calls:
- C.bean().mint(address(this),newHarvestable.add(sopBeans)) (contracts/beanstalk/sun/SeasonFacet/Weather.sol#196)
- C.bean().mint(address(this),sopBeans) (contracts/beanstalk/sun/SeasonFacet/Weather.sol#198)
- C.bean().approve(sopWell,sopBeans) (contracts/beanstalk/sun/SeasonFacet/Weather.sol#202)
- amountOut = IWell(sopWell).swapFrom(C.bean(),sopToken,sopBeans,0,address(this),type()(uint256).max) (contracts/beanstalk/sun/SeasonFacet/Weather.sol#203-210)
State variables written after the call(s):
- rewardSop(amountOut) (contracts/beanstalk/sun/SeasonFacet/Weather.sol#211)
- s.sops[s.season.rainStart] = s.sops[s.season.lastSop].add(amount.mul(C.SOP_PRECISION).div(s.r.roots)) (contracts/beanstalk/sun/SeasonFacet/Weather.sol#219-221)
- s.season.lastSop = s.season.rainStart (contracts/beanstalk/sun/SeasonFacet/Weather.sol#222)
- s.season.lastSopSeason = s.season.current (contracts/beanstalk/sun/SeasonFacet/Weather.sol#223)
/// @audit ************** Possible Issue Line(s) **************
L#196, L#198, L#202, L#203-210, L#211, L#219-221, L#222, L#223,
/// @audit ****************** Affected Code *******************
196: C.bean().mint(address(this), newHarvestable.add(sopBeans));
198: C.bean().mint(address(this), sopBeans);
202: C.bean().approve(sopWell, sopBeans);
203: uint256 amountOut = IWell(sopWell).swapFrom(
204: C.bean(),
205: sopToken,
206: sopBeans,
207: 0,
208: address(this),
209: type(uint256).max
210: );
211: rewardSop(amountOut);
219: s.sops[s.season.rainStart] = s.sops[s.season.lastSop].add(
220: amount.mul(C.SOP_PRECISION).div(s.r.roots)
221: );
222: s.season.lastSop = s.season.rainStart;
223: s.season.lastSopSeason = s.season.current;

GitHub : 181-213

File: contracts/beanstalk/sun/SeasonFacet/SeasonFacet.sol
/// @audit ******************* Issue Detail *******************
Reentrancy (no theft of ethers) in SeasonFacet.gm(address,LibTransfer.To) (contracts/beanstalk/sun/SeasonFacet/SeasonFacet.sol#44-57):
External calls:
- caseId = calcCaseIdandUpdate(deltaB) (contracts/beanstalk/sun/SeasonFacet/SeasonFacet.sol#51)
- C.bean().mint(address(this),newHarvestable.add(sopBeans)) (contracts/beanstalk/sun/SeasonFacet/Weather.sol#196)
- C.bean().mint(address(this),sopBeans) (contracts/beanstalk/sun/SeasonFacet/Weather.sol#198)
- C.bean().approve(sopWell,sopBeans) (contracts/beanstalk/sun/SeasonFacet/Weather.sol#202)
- amountOut = IWell(sopWell).swapFrom(C.bean(),sopToken,sopBeans,0,address(this),type()(uint256).max) (contracts/beanstalk/sun/SeasonFacet/Weather.sol#203-210)
- stepSun(deltaB,caseId) (contracts/beanstalk/sun/SeasonFacet/SeasonFacet.sol#54)
- C.bean().mint(address(this),newSupply) (contracts/beanstalk/sun/SeasonFacet/Sun.sol#86)
State variables written after the call(s):
- stepSun(deltaB,caseId) (contracts/beanstalk/sun/SeasonFacet/SeasonFacet.sol#54)
- s.earnedBeans = s.earnedBeans.add(amount.toUint128()) (contracts/beanstalk/sun/SeasonFacet/Sun.sol#178)
- s.f.soil = amount.toUint128() (contracts/beanstalk/sun/SeasonFacet/Sun.sol#228)
- s.s.stalk = s.s.stalk.add(amount.mul(C.STALK_PER_BEAN)) (contracts/beanstalk/sun/SeasonFacet/Sun.sol#186)
- s.siloBalances[C.BEAN].deposited = s.siloBalances[C.BEAN].deposited.add(amount.toUint128()) (contracts/beanstalk/sun/SeasonFacet/Sun.sol#192-195)
- s.season.abovePeg = true (contracts/beanstalk/sun/SeasonFacet/Sun.sol#68)
- s.siloBalances[C.BEAN].depositedBdv = s.siloBalances[C.BEAN].depositedBdv.add(uint128(amount)) (contracts/beanstalk/sun/SeasonFacet/Sun.sol#198-201)
- s.f.harvestable = s.f.harvestable.add(newHarvestable) (contracts/beanstalk/sun/SeasonFacet/Sun.sol#164)
- s.season.abovePeg = false (contracts/beanstalk/sun/SeasonFacet/Sun.sol#74)
- s.bpf = uint128(firstEndBpf) (contracts/beanstalk/sun/SeasonFacet/Sun.sol#132)
- s.fertilizedIndex = s.fertilizedIndex.add(newFertilized) (contracts/beanstalk/sun/SeasonFacet/Sun.sol#133)
- s.bpf = uint128(newTotalBpf) (contracts/beanstalk/sun/SeasonFacet/Sun.sol#146)
- s.fertilizedIndex = s.fertilizedIndex.add(newFertilized) (contracts/beanstalk/sun/SeasonFacet/Sun.sol#148)
/// @audit ************** Possible Issue Line(s) **************
L#51, L#54,
/// @audit ****************** Affected Code *******************
44: function gm(address account, LibTransfer.To mode) public payable returns (uint256) {
45: uint256 initialGasLeft = gasleft();
46:
47: require(!s.paused, "Season: Paused.");
48: require(seasonTime() > s.season.current, "Season: Still current Season.");
49: uint32 season = stepSeason();
50: int256 deltaB = stepOracle();
51: uint256 caseId = calcCaseIdandUpdate(deltaB);
52: LibGerminate.endTotalGermination(season, LibWhitelistedTokens.getWhitelistedTokens());
53: LibGauge.stepGauge();
54: stepSun(deltaB, caseId);
55:
56: return incentivize(account, initialGasLeft, mode);
57: }

GitHub : 44-57

Impact

Possible loss of user funds or wrong contract state

Tools Used

Manual review

Recommendations

Code should follow the best-practice of check-effects-interaction, where state variables are updated before any external calls are made. Doing so prevents a large class of reentrancy bugs.

Updates

Lead Judging Commences

giovannidisiena Lead Judge over 1 year ago
Submission Judgement Published
Invalidated
Reason: Too generic
Assigned finding tags:

Informational/Invalid

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.