Temperature and caseId are incorrectly adjusted when oracle fails
When user calls `gm` and the call for the chainlink oracle fails, it will return 0 for the `deltaB` value and this will cause a cascade effect, making the calculation of `caseId` = `3` and using the incorrect `caseId` to set up the new temperature on Weather.sol\\n```\\nfunction updateTemperature(int8 bT, uint256 caseId) private {\\n        uint256 t = s.w.t;\\n        if (bT < 0) {\\n            if (t <= uint256(-bT)) {\\n                // if (change < 0 && t <= uint32(-change)),\\n                // then 0 <= t <= type(int8).max because change is an int8.\\n                // Thus, downcasting t to an int8 will not cause overflow.\\n                bT = 1 - int8(t);\\n                s.w.t = 1;\\n            } else {\\n                s.w.t = uint32(t - uint256(-bT));\\n            }\\n        } else {\\n            s.w.t = uint32(t + uint256(bT));\\n        }\\n\\n        emit TemperatureChange(s.season.current, caseId, bT);\\n    }\\n```\\n\\nEvery consumer of the temperature on the protocol will be affected like:\\n`LibDibbler.morningTemperature`\\n`LibDibbler.beansToPods`\\n`LibDibbler.remainingPods`\\n`Sun.setSoilAbovePeg`\\n`Sun.stepSun`\\n`FieldFacet.maxTemperature`\\n`FieldFacet.totalSoil`\\n`FieldFacet._totalSoilAndTemperature`\\n`FieldFacet.sowWithMin\\n`gm` function uses the incorrect deltaB(0) to calculate the `caseId` which is then used to set the temperature.\\n```\\n   function gm(address account, LibTransfer.To mode) public payable returns (uint256) {\\n        int256 deltaB = stepOracle(); // @audit here if oracle failed, we update the season.timestamp and return deltaB zero here\\n        uint256 caseId = calcCaseIdandUpdate(deltaB); // @audit caseId will be 3 here if deltaB is zero\\n        LibGerminate.endTotalGermination(season, LibWhitelistedTokens.getWhitelistedTokens());\\n        LibGauge.stepGauge();\\n        stepSun(deltaB, caseId); // @audit wrong deltaB and caseId used here, setting the abovePeg to false and soil to zero\\n    }\\n```\\n\\nPrepare the environment to work with Foundry + Updated Mocks https://gist.github.com/h0lydev/fcdb00c797adfdf8e4816031e095fd6c\\nMake sure to have the mainnet forked through Anvil: `anvil --fork-url https://rpc.ankr.com/eth`\\nCreate the `SeasonTemperature.t.sol` file under the folder `foundry` and paste the code below. Then run `forge test --match-contract SeasonTemperatureTest -vv`.\\n```\\n// SPDX-License-Identifier: MIT\\npragma solidity =0.7.6;\\npragma abicoder v2;\\n\\nimport { Sun } from "contracts/beanstalk/sun/SeasonFacet/Sun.sol";\\nimport { MockSeasonFacet } from "contracts/mocks/mockFacets/MockSeasonFacet.sol";\\nimport { MockSiloFacet } from "contracts/mocks/mockFacets/MockSiloFacet.sol";\\nimport { MockFieldFacet } from "contracts/mocks/mockFacets/MockFieldFacet.sol";\\nimport { MockWhitelistFacet } from "contracts/mocks/mockFacets/MockWhitelistFacet.sol";\\nimport {LibWhitelist} from "contracts/libraries/Silo/LibWhitelist.sol";\\nimport { Utils } from "./utils/Utils.sol";\\nimport "./utils/TestHelper.sol";\\nimport "contracts/libraries/LibSafeMath32.sol";\\nimport "contracts/C.sol";\\n\\ncontract SeasonTemperatureTest is MockSeasonFacet, TestHelper {\\n    using SafeMath for uint256;\\n    using LibSafeMath32 for uint32;\\n\\n    bool oracleFailed;\\n  \\n    function setUp() public {\\n        console.log("diamondSetup");\\n        vm.createSelectFork('local');\\n        oracleFailed = false;\\n        setupDiamond();\\n        dewhitelistCurvePool();\\n        mintUnripeLPToUser1();   \\n        mintUnripeBeanToUser1();\\n        setOraclePrices(false, 1000e6, 1000e6, 1000e6);\\n        _setReservesForWell(1000000e6, 1000e18);\\n       \\n        // user / tokens\\n        mintTokenForUsers();\\n        setTokenApprovalForUsers();\\n       \\n        enableFertilizerAndMintActiveFertilizers();\\n\\n        callSunriseForUser1();\\n    }\\n\\n\\n    ////////////     Setup functions     ////////////\\n\\n    function setTokenApprovalForUsers() internal { \\n        approveTokensForUser(deployer);\\n        approveTokensForUser(user1);\\n        approveTokensForUser(user2);\\n        approveTokensForUser(user3);\\n        approveTokensForUser(user4);\\n        approveTokensForUser(user5);\\n    }\\n\\n    function mintTokenForUsers() internal { \\n        mintWETHtoUser(deployer);\\n        mintWETHtoUser(user1);\\n        mintWETHtoUser(user2);\\n        mintWETHtoUser(user3);\\n        mintWETHtoUser(user4);\\n        mintWETHtoUser(user5);\\n\\n        // mint C.bean() to users\\n        C.bean().mint(deployer, 10e6);\\n        C.bean().mint(user1, 10e6);\\n        C.bean().mint(user2, 10e6);\\n        C.bean().mint(user3, 10e6);\\n        C.bean().mint(user4, 10e6);\\n        C.bean().mint(user5, 10e6);\\n    }\\n\\n    function approveTokensForUser(address user) prank(user) internal { \\n        mockWETH.approve(address(diamond), type(uint256).max);\\n        unripeLP.approve(address(diamond), type(uint256).max);\\n        unripeBean.approve(address(diamond), type(uint256).max);\\n        well.approve(address(diamond), type(uint256).max);\\n        C.bean().approve(address(field), type(uint256).max);\\n        C.bean().approve(address(field), type(uint256).max);\\n    }\\n\\n    function dewhitelistCurvePool() public {\\n        vm.prank(deployer);\\n        whitelist.dewhitelistToken(C.CURVE_BEAN_METAPOOL);\\n    }\\n\\n    function mintWETHtoUser(address user) prank(user) internal {\\n        mockWETH.mint(user, 1000e18);\\n    }\\n\\n    function mintUnripeLPToUser1() internal { \\n        unripeLP.mint(user1, 1000e6);\\n    }\\n\\n    function mintUnripeBeanToUser1() internal { \\n        unripeBean.mint(user1, 1000e6);\\n    }\\n\\n    function enableFertilizerAndMintActiveFertilizers() internal { \\n        // second parameter is the unfertilizedIndex\\n        fertilizer.setFertilizerE(true, 10000e6);\\n\\n        vm.prank(deployer);\\n        fertilizer.addFertilizerOwner(7500, 1e11, 99);\\n\\n        vm.prank(deployer);\\n        fertilizer.addFertilizerOwner(6200, 1e11, 99);\\n\\n        addUnripeTokensToFacet();\\n    }\\n\\n    function addUnripeTokensToFacet() prank(deployer) internal { \\n        unripe.addUnripeToken(C.UNRIPE_BEAN, C.BEAN, bytes32(0));\\n        unripe.addUnripeToken(C.UNRIPE_LP, C.BEAN_ETH_WELL, bytes32(0));\\n    }\\n\\n    function callSunriseForUser1() prank(user1) internal {\\n        _ensurePreConditions();\\n        _advanceInTime(2 hours);\\n        season.sunrise();\\n    }\\n\\n    function setOraclePrices(bool makeOracleFail, int256 chainlinkPrice, uint256 ethUsdtPrice, uint256 ethUsdcPrice) internal { \\n        if (makeOracleFail) { \\n            _addEthUsdPriceChainlink(0);\\n            oracleFailed = true;\\n        } else { \\n            oracleFailed = false;\\n            _addEthUsdPriceChainlink(chainlinkPrice);\\n            _setEthUsdtPrice(ethUsdtPrice);\\n            _setEthUsdcPrice(ethUsdcPrice);\\n        }\\n    }\\n\\n    ////////////////////////////////////////// TESTS //////////////////////////////////////////\\n\\n    function testWrongCalcId_whenOracleFails() public { \\n        _prepareForAbovePeg();\\n        _advanceInTime(1 hours);\\n        uint256 _snapId = vm.snapshot();\\n        \\n        // When sunrise succeeds\\n        vm.prank(user4);\\n        season.sunrise();\\n\\n        // Then print results\\n        _printProtocolState();\\n        assertEq(season.getT(), 5, "when succeeds t should be 5");\\n        \\n        // Then revert it to prepare for the season that will fail\\n        vm.revertTo(_snapId);\\n\\n        // Prepare for the season that will fail\\n        setOraclePrices(true, 0, 0, 0);\\n\\n        // When sunrise fails\\n        vm.prank(user4);\\n        season.sunrise();\\n\\n        console.log("Oracle failed, see results");\\n        _printProtocolState();\\n        assertEq(season.getT(), 1, "when succeeds t should be 1");\\n\\n    }\\n\\n    function _printProtocolState() internal { \\n        console.log("-------------- Results --------------");\\n        console.log("");\\n        console.log("thisSowTime: ", season.thisSowTime());\\n        console.log("lastSowTime: ", season.lastSowTime());\\n        console.log("getUsdTokenPrice: ", season.getUsdTokenPrice());\\n        console.log("getReserve0: ", season.getReserve0());\\n        console.log("getReserve1: ", season.getReserve1());\\n        console.log("getAbovePeg: ", season.getAbovePeg());\\n        console.log("getSoil: ", season.getSoil());\\n        console.log("lastDSoil: ", season.lastDSoil());\\n        console.log("s.w.t: ", season.getT());\\n        console.log("remaining pods: ", field.remainingPods());\\n    }   \\n\\n    function _prepareForAbovePeg() internal { \\n        season.mockSetSopWell(address(well));\\n        season.captureWellE(address(well)); \\n        season.setYieldE(5); // s.w.t\\n        setOraclePrices(false, 1000e6, 1000e6, 1000e6);\\n\\n        season.setLastSowTimeE(1);\\n        season.setNextSowTimeE(10);\\n        season.calcCaseIdE(1e18, 1);\\n        season.setAbovePegE(true);\\n    }\\n\\n    ////////////////////////////////////////// HELPERS //////////////////////////////////////////\\n\\n    function _ensurePreConditions() internal { \\n        assertTrue(season.thisSowTime() == type(uint32).max, "thisSowTime should be max");\\n        assertTrue(season.lastSowTime() == type(uint32).max, "thisLastSowTime should be max");\\n        assertEq(season.getIsFarm(), 1, "isFarm should be 1");\\n        assertEq(season.getUsdTokenPrice(), 1, "usdTokenPrice should be 1");\\n        assertEq(season.getReserve0(), 1, "reserve0 should be 1");\\n        assertEq(season.getReserve1(), 1, "reserve1 should be 1");\\n        assertFalse(season.getAbovePeg(), "pre - abovePeg should be false");\\n        assertEq(season.getSoil(), 0, "soil should be == 0");\\n    }\\n}\\n```\\n\\nOutput:\\n```\\n handleRain caseId: 0\\n -------------- Results --------------\\n  \\n  thisSowTime:  4294967295\\n  lastSowTime:  4294967295\\n  getUsdTokenPrice:  1\\n  getReserve0:  1\\n  getReserve1:  1\\n  getAbovePeg:  false\\n  getSoil:  462832752243\\n  lastDSoil:  0\\n  s.w.t:  5\\n  remaining pods:  467461079765\\n\\nhandleRain caseId: 3\\n Oracle failed, see results\\n  -------------- Results --------------\\n  \\n  thisSowTime:  4294967295\\n  lastSowTime:  4294967295\\n  getUsdTokenPrice:  1\\n  getReserve0:  1\\n  getReserve1:  1\\n  getAbovePeg:  false\\n  getSoil:  0\\n  lastDSoil:  0\\n  s.w.t:  1\\n  remaining pods:  0\\n\\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 29.45s (3.32ms CPU time)\\n```\\n\\nps: a console.log was added to the `handleRain` function to print the caseId.\\nResult: In a normal scenario the temperature would have remained at the value `5` but in this case was set to `1` and remaining pods/soil are also set to zero when in fact they should not.
It is noticed that the developers have the intention of never reverting the sunrise function to decrease the risk of depeg and breaking the incentive for users calling it. But at the same time, those state variables shouldn't be updated as if the system is working correctly because they will impact the next season as stated in this finding.\\nIt is tricky to propose a simple fix to the problem without impacting the system as a whole. Here are a few ideas that could be used:\\n(Recommended) An effective solution could be store the latest response from chainlink and in case it fails and the timeout(a limit that you can be added to accept a previous response from the oracle) is not reached yet, protocol could use the previous response. Liquity Protocol uses this approach, an example here: https://github.com/liquity/dev/blob/main/packages/contracts/contracts/PriceFeed.sol This solution will be effective for the protocol because the oracle is also called in different places like when minting fertilizers(getMintFertilizerOut), getting the well price(getRatiosAndBeanIndex), and `getConstantProductWell`. As the oracle is used along the protocol in many places, the `latest successful price` would be often up-to-date and within the limit time defined to use the previous price when the chainlink oracle fails.\\nAdditionally, consider handling the errors properly before updating the `deltaB` and `abovePeg` variables, as these disrupt the peg mechanism logic.
The interest rate will be wrongly decreased to 1, compromising the protocol peg mechanism when it needs to be maintained with a high interest rate/ temperature.\\nSow will be calculated with the lowest temperature, also compromising the peg mechanism due to the wrong exchange of Beans -> Sow -> Pods\\nRemaining pods function will return zero and users will have an inaccurate number representing their actual pods.
```\\nfunction updateTemperature(int8 bT, uint256 caseId) private {\\n        uint256 t = s.w.t;\\n        if (bT < 0) {\\n            if (t <= uint256(-bT)) {\\n                // if (change < 0 && t <= uint32(-change)),\\n                // then 0 <= t <= type(int8).max because change is an int8.\\n                // Thus, downcasting t to an int8 will not cause overflow.\\n                bT = 1 - int8(t);\\n                s.w.t = 1;\\n            } else {\\n                s.w.t = uint32(t - uint256(-bT));\\n            }\\n        } else {\\n            s.w.t = uint32(t + uint256(bT));\\n        }\\n\\n        emit TemperatureChange(s.season.current, caseId, bT);\\n    }\\n```\\n