From b1e188dff360e32dd057b62f6119f41f6b60b675 Mon Sep 17 00:00:00 2001
From: RickGriff <rick@liquity.org>
Date: Tue, 21 Jan 2025 19:10:13 +0400
Subject: [PATCH 1/3] Add extra oracle tests

---
 contracts/test/OracleMainnet.t.sol            | 99 ++++++++++++++++++-
 .../TestContracts/ChainlinkOracleMock.sol     |  1 -
 .../test/TestContracts/GasGuzzlerToken.sol    | 30 ++++++
 3 files changed, 125 insertions(+), 5 deletions(-)
 create mode 100644 contracts/test/TestContracts/GasGuzzlerToken.sol

diff --git a/contracts/test/OracleMainnet.t.sol b/contracts/test/OracleMainnet.t.sol
index e9076f35c..3d24700eb 100644
--- a/contracts/test/OracleMainnet.t.sol
+++ b/contracts/test/OracleMainnet.t.sol
@@ -9,6 +9,7 @@ import "src/PriceFeeds/WETHPriceFeed.sol";
 
 import "./TestContracts/Accounts.sol";
 import "./TestContracts/ChainlinkOracleMock.sol";
+import "./TestContracts/GasGuzzlerToken.sol";
 import "./TestContracts/RETHTokenMock.sol";
 import "./TestContracts/WSTETHTokenMock.sol";
 import "./TestContracts/Deployment.t.sol";
@@ -29,6 +30,7 @@ contract OraclesMainnet is TestAccounts {
     AggregatorV3Interface rethOracle;
 
     ChainlinkOracleMock mockOracle;
+    GasGuzzlerToken gasGuzzlerToken;
 
     IMainnetPriceFeed wethPriceFeed;
     IRETHPriceFeed rethPriceFeed;
@@ -93,6 +95,7 @@ contract OraclesMainnet is TestAccounts {
         stethOracle = AggregatorV3Interface(result.externalAddresses.STETHOracle);
 
         mockOracle = new ChainlinkOracleMock();
+        gasGuzzlerToken = new GasGuzzlerToken();
 
         rethToken = IRETHToken(result.externalAddresses.RETHToken);
 
@@ -101,6 +104,8 @@ contract OraclesMainnet is TestAccounts {
         mockRethToken = new RETHTokenMock();
         mockWstethToken = new WSTETHTokenMock();
 
+      
+
         // Record contracts
         for (uint256 c = 0; c < vars.numCollaterals; c++) {
             contractsArray.push(result.contractsArray[c]);
@@ -189,6 +194,20 @@ contract OraclesMainnet is TestAccounts {
         mock.setUpdatedAt(block.timestamp - 7 days);
     }
 
+    function etchGasGuzzlerMockToRethToken(bytes memory _mockTokenCode) internal {
+        // Etch the mock code to the RETH token address
+        vm.etch(address(rethToken), _mockTokenCode);
+        // // Wrap so we can use the mock's functions
+        // GasGuzzlerToken mockReth = GasGuzzlerToken(address(rethToken));
+    }
+
+    function etchGasGuzzlerMockToWstethToken(bytes memory _mockTokenCode) internal {
+        // Etch the mock code to the RETH token address
+        vm.etch(address(wstETH), _mockTokenCode);
+        // // Wrap so we can use the mock's functions
+        // GasGuzzlerToken mockWsteth = GasGuzzlerToken(address(wstETH));
+    }
+
     // --- lastGoodPrice set on deployment ---
 
     function testSetLastGoodPriceOnDeploymentWETH() public view {
@@ -295,6 +314,43 @@ contract OraclesMainnet is TestAccounts {
         assertEq(storedStEthUsdStaleness, _24_HOURS);
     }
 
+    // --- LST exchange rates and market price oracle sanity checks ---
+
+    function testRETHExchangeRateBetween1And2() public {
+        uint256 rate = rethToken.getExchangeRate();
+        assertGt(rate, 1e18);
+        assertLt(rate, 2e18);
+    }
+
+    function testWSTETHExchangeRateBetween1And2() public {
+        uint256 rate = wstETH.stEthPerToken();
+        assertGt(rate, 1e18);
+        assertLt(rate, 2e18);
+    }
+
+    function testRETHOracleAnswerBetween1And2() public {
+        uint256 answer = _getLatestAnswerFromOracle(rethOracle);
+        assertGt(answer, 1e18);
+        assertLt(answer, 2e18);
+    }
+
+    function testSTETHOracleAnswerWithin1PctOfETHOracleAnswer() public {
+        uint256 stethUsd = _getLatestAnswerFromOracle(stethOracle);
+        uint256 ethUsd = _getLatestAnswerFromOracle(ethOracle);
+
+        uint256 relativeDelta;
+
+        if (stethUsd > ethUsd) {
+            relativeDelta = (stethUsd - ethUsd) * 1e18 / ethUsd;
+        } else {
+            relativeDelta = (ethUsd - stethUsd) * 1e18 / stethUsd;
+        }
+
+        assertLt(relativeDelta, 1e16);
+    }
+
+
+
     // // --- Basic actions ---
 
     function testOpenTroveWETH() public {
@@ -1988,30 +2044,65 @@ contract OraclesMainnet is TestAccounts {
         assertEq(contractsArray[1].collToken.balanceOf(A), A_collBefore + expectedCollDelta, "A's coll didn't change");
     }
 
-    // --- Low gas reverts ---
+    // --- Low gas market oracle reverts ---
 
     // --- Call these functions with 10k gas - i.e. enough to run out of gas in the Chainlink calls ---
-    function testRevertLowGasWSTETH() public {
+    function testRevertLowGasSTETHOracle() public {
         vm.expectRevert(MainnetPriceFeedBase.InsufficientGasForExternalCall.selector);
         // just catch return val to suppress warning
         (bool success,) = address(wstethPriceFeed).call{gas: 10000}(abi.encodeWithSignature("fetchPrice()"));
         assertFalse(success);
     }
 
-    function testRevertLowGasRETH() public {
+    function testRevertLowGasRETHOracle() public {
         vm.expectRevert(MainnetPriceFeedBase.InsufficientGasForExternalCall.selector);
         // just catch return val to suppress warning
         (bool success,) = address(rethPriceFeed).call{gas: 10000}(abi.encodeWithSignature("fetchPrice()"));
         assertFalse(success);
     }
 
-    function testRevertLowGasWETH() public {
+    function testRevertLowGasETHOracle() public {
         vm.expectRevert(MainnetPriceFeedBase.InsufficientGasForExternalCall.selector);
         // just catch return val to suppress warning
         (bool success,) = address(wethPriceFeed).call{gas: 10000}(abi.encodeWithSignature("fetchPrice()"));
         assertFalse(success);
     }
 
+    // --- Test with a gas guzzler token, and confirm revert ---
+
+    function testRevertLowGasWSTETHToken() public {
+        // Confirm call to the real external contracts succeeds with sufficient gas i.e. 500k
+        (bool success,) = address(rethPriceFeed).call{gas: 500000}(abi.encodeWithSignature("fetchPrice()"));
+        assertTrue(success);
+
+        // Etch gas guzzler to the LST
+        etchGasGuzzlerMockToWstethToken(address(gasGuzzlerToken).code);
+
+        // After etching the gas guzzler to the LST, confirm the same call with 500k gas now reverts due to OOG
+        vm.expectRevert(MainnetPriceFeedBase.InsufficientGasForExternalCall.selector);
+        // just catch return val to suppress warning
+        (success,) = address(wstethPriceFeed).call{gas: 10000}(abi.encodeWithSignature("fetchPrice()"));
+        assertFalse(success);
+    }
+
+    function testRevertLowGasRETHToken() public {
+        // Confirm call to the real external contracts succeeds with sufficient gas i.e. 500k
+        (bool success,) = address(wstethPriceFeed).call{gas: 500000}(abi.encodeWithSignature("fetchPrice()"));
+        assertTrue(success);
+
+        // Etch gas guzzler to the LST
+        etchGasGuzzlerMockToRethToken(address(gasGuzzlerToken).code);
+
+        // After etching the gas guzzler to the LST, confirm the same call with 500k gas now reverts due to OOG 
+        vm.expectRevert(MainnetPriceFeedBase.InsufficientGasForExternalCall.selector);
+        // just catch return val to suppress warning
+        (success,) = address(rethPriceFeed).call{gas: 10000}(abi.encodeWithSignature("fetchPrice()"));
+        assertFalse(success);
+    }
+
+
+
+
     // - More basic actions tests (adjust, close, etc)
     // - liq tests (manipulate aggregator stored price)
 }
diff --git a/contracts/test/TestContracts/ChainlinkOracleMock.sol b/contracts/test/TestContracts/ChainlinkOracleMock.sol
index df13d22f4..a01817d49 100644
--- a/contracts/test/TestContracts/ChainlinkOracleMock.sol
+++ b/contracts/test/TestContracts/ChainlinkOracleMock.sol
@@ -6,7 +6,6 @@ import "src/Dependencies/AggregatorV3Interface.sol";
 
 // Mock Chainlink oracle that returns a stale price answer.
 // this contract code is etched over mainnet oracle addresses in mainnet fork tests.
-// As such, we use bools for staleness and decimals to save us having to set some contract state each time after etching.
 contract ChainlinkOracleMock is AggregatorV3Interface {
     uint8 decimal;
 
diff --git a/contracts/test/TestContracts/GasGuzzlerToken.sol b/contracts/test/TestContracts/GasGuzzlerToken.sol
new file mode 100644
index 000000000..18ee3e56e
--- /dev/null
+++ b/contracts/test/TestContracts/GasGuzzlerToken.sol
@@ -0,0 +1,30 @@
+// SPDX-License-Identifier: BUSL-1.1
+
+pragma solidity 0.8.24;
+
+
+
+// Mock token that uses all available gas on exchange rate calls.
+// This contract code is etched over LST token addresses in mainnet fork tests.
+// Has exchange rate functions for WSTETH and RETH.
+contract GasGuzzlerToken {
+    uint256 pointlessStorageVar = 42;
+    
+    // RETH exchange rate getter
+    function getExchangeRate() external view returns (uint256) {
+        // Expensive SLOAD loop that hits the block gas limit before completing
+        for (uint256 i = 0; i < 1000000; i++) {
+            uint256 unusedVar = pointlessStorageVar + i;
+        }
+        return 11e17;
+    }
+
+    // WSTETH exchange rate getter
+    function stEthPerToken() external view returns (uint256) {
+        // Expensive SLOAD loop that hits the block gas limit before completing
+        for (uint256 i = 0; i < 1000000; i++) {
+            uint256 unusedVar = pointlessStorageVar + i;
+        }
+        return 11e17;
+    }
+}

From c2bb40109be45bd1eebacbb19a1ee64977abcacc Mon Sep 17 00:00:00 2001
From: RickGriff <rick@liquity.org>
Date: Tue, 21 Jan 2025 19:12:18 +0400
Subject: [PATCH 2/3] forge fmt

---
 contracts/test/OracleMainnet.t.sol               | 9 +--------
 contracts/test/TestContracts/GasGuzzlerToken.sol | 4 +---
 2 files changed, 2 insertions(+), 11 deletions(-)

diff --git a/contracts/test/OracleMainnet.t.sol b/contracts/test/OracleMainnet.t.sol
index 3d24700eb..e87b05cc1 100644
--- a/contracts/test/OracleMainnet.t.sol
+++ b/contracts/test/OracleMainnet.t.sol
@@ -104,8 +104,6 @@ contract OraclesMainnet is TestAccounts {
         mockRethToken = new RETHTokenMock();
         mockWstethToken = new WSTETHTokenMock();
 
-      
-
         // Record contracts
         for (uint256 c = 0; c < vars.numCollaterals; c++) {
             contractsArray.push(result.contractsArray[c]);
@@ -349,8 +347,6 @@ contract OraclesMainnet is TestAccounts {
         assertLt(relativeDelta, 1e16);
     }
 
-
-
     // // --- Basic actions ---
 
     function testOpenTroveWETH() public {
@@ -2093,16 +2089,13 @@ contract OraclesMainnet is TestAccounts {
         // Etch gas guzzler to the LST
         etchGasGuzzlerMockToRethToken(address(gasGuzzlerToken).code);
 
-        // After etching the gas guzzler to the LST, confirm the same call with 500k gas now reverts due to OOG 
+        // After etching the gas guzzler to the LST, confirm the same call with 500k gas now reverts due to OOG
         vm.expectRevert(MainnetPriceFeedBase.InsufficientGasForExternalCall.selector);
         // just catch return val to suppress warning
         (success,) = address(rethPriceFeed).call{gas: 10000}(abi.encodeWithSignature("fetchPrice()"));
         assertFalse(success);
     }
 
-
-
-
     // - More basic actions tests (adjust, close, etc)
     // - liq tests (manipulate aggregator stored price)
 }
diff --git a/contracts/test/TestContracts/GasGuzzlerToken.sol b/contracts/test/TestContracts/GasGuzzlerToken.sol
index 18ee3e56e..ef1cbd60f 100644
--- a/contracts/test/TestContracts/GasGuzzlerToken.sol
+++ b/contracts/test/TestContracts/GasGuzzlerToken.sol
@@ -2,14 +2,12 @@
 
 pragma solidity 0.8.24;
 
-
-
 // Mock token that uses all available gas on exchange rate calls.
 // This contract code is etched over LST token addresses in mainnet fork tests.
 // Has exchange rate functions for WSTETH and RETH.
 contract GasGuzzlerToken {
     uint256 pointlessStorageVar = 42;
-    
+
     // RETH exchange rate getter
     function getExchangeRate() external view returns (uint256) {
         // Expensive SLOAD loop that hits the block gas limit before completing

From 6ba339e550c6922a997aca24e2cc42aa4ce3d279 Mon Sep 17 00:00:00 2001
From: RickGriff <rick@liquity.org>
Date: Wed, 22 Jan 2025 16:58:54 +0400
Subject: [PATCH 3/3] Fix gas guzzler tests

---
 contracts/test/OracleMainnet.t.sol            | 96 ++++++++++++++-----
 .../test/TestContracts/GasGuzzlerOracle.sol   | 47 +++++++++
 2 files changed, 120 insertions(+), 23 deletions(-)
 create mode 100644 contracts/test/TestContracts/GasGuzzlerOracle.sol

diff --git a/contracts/test/OracleMainnet.t.sol b/contracts/test/OracleMainnet.t.sol
index e87b05cc1..6a8a36a66 100644
--- a/contracts/test/OracleMainnet.t.sol
+++ b/contracts/test/OracleMainnet.t.sol
@@ -9,6 +9,7 @@ import "src/PriceFeeds/WETHPriceFeed.sol";
 
 import "./TestContracts/Accounts.sol";
 import "./TestContracts/ChainlinkOracleMock.sol";
+import "./TestContracts/GasGuzzlerOracle.sol";
 import "./TestContracts/GasGuzzlerToken.sol";
 import "./TestContracts/RETHTokenMock.sol";
 import "./TestContracts/WSTETHTokenMock.sol";
@@ -31,6 +32,7 @@ contract OraclesMainnet is TestAccounts {
 
     ChainlinkOracleMock mockOracle;
     GasGuzzlerToken gasGuzzlerToken;
+    GasGuzzlerOracle gasGuzzlerOracle;
 
     IMainnetPriceFeed wethPriceFeed;
     IRETHPriceFeed rethPriceFeed;
@@ -96,6 +98,7 @@ contract OraclesMainnet is TestAccounts {
 
         mockOracle = new ChainlinkOracleMock();
         gasGuzzlerToken = new GasGuzzlerToken();
+        gasGuzzlerOracle = new GasGuzzlerOracle();
 
         rethToken = IRETHToken(result.externalAddresses.RETHToken);
 
@@ -192,18 +195,47 @@ contract OraclesMainnet is TestAccounts {
         mock.setUpdatedAt(block.timestamp - 7 days);
     }
 
+    function etchGasGuzzlerToEthOracle(bytes memory _mockOracleCode) internal {
+        // Etch the mock code to the ETH-USD oracle address
+        vm.etch(address(ethOracle), _mockOracleCode);
+        GasGuzzlerOracle mock = GasGuzzlerOracle(address(ethOracle));
+        mock.setDecimals(8);
+        // Fake ETH-USD price of 2000 USD
+        mock.setPrice(2000e8);
+        mock.setUpdatedAt(block.timestamp);
+    }
+
+
+    function etchGasGuzzlerToRethOracle(bytes memory _mockOracleCode) internal {
+        // Etch the mock code to the RETH-ETH oracle address
+        vm.etch(address(rethOracle), _mockOracleCode);
+        // Wrap so we can use the mock's setters
+        GasGuzzlerOracle mock = GasGuzzlerOracle(address(rethOracle));
+        mock.setDecimals(18);
+        // Set 1 RETH = 1.1 ETH
+        mock.setPrice(11e17);
+        mock.setUpdatedAt(block.timestamp);
+    }
+
+    function etchGasGuzzlerToStethOracle(bytes memory _mockOracleCode) internal {
+        // Etch the mock code to the STETH-USD oracle address
+        vm.etch(address(stethOracle), _mockOracleCode);
+        // Wrap so we can use the mock's setters
+        GasGuzzlerOracle mock = GasGuzzlerOracle(address(stethOracle));
+        mock.setDecimals(8);
+        // Set 1 STETH =  2000 USD
+        mock.setPrice(2000e8);
+        mock.setUpdatedAt(block.timestamp);
+    }
+
     function etchGasGuzzlerMockToRethToken(bytes memory _mockTokenCode) internal {
         // Etch the mock code to the RETH token address
         vm.etch(address(rethToken), _mockTokenCode);
-        // // Wrap so we can use the mock's functions
-        // GasGuzzlerToken mockReth = GasGuzzlerToken(address(rethToken));
     }
 
     function etchGasGuzzlerMockToWstethToken(bytes memory _mockTokenCode) internal {
         // Etch the mock code to the RETH token address
         vm.etch(address(wstETH), _mockTokenCode);
-        // // Wrap so we can use the mock's functions
-        // GasGuzzlerToken mockWsteth = GasGuzzlerToken(address(wstETH));
     }
 
     // --- lastGoodPrice set on deployment ---
@@ -2042,33 +2074,53 @@ contract OraclesMainnet is TestAccounts {
 
     // --- Low gas market oracle reverts ---
 
-    // --- Call these functions with 10k gas - i.e. enough to run out of gas in the Chainlink calls ---
     function testRevertLowGasSTETHOracle() public {
+        // Confirm call to the real external contracts succeeds with sufficient gas i.e. 500k
+        (bool success,) = address(wstethPriceFeed).call{gas: 500000}(abi.encodeWithSignature("fetchPrice()"));
+        assertTrue(success);
+
+        // Etch gas guzzler to the oracle
+        etchGasGuzzlerToStethOracle(address(gasGuzzlerOracle).code);
+
+        // After etching the gas guzzler to the oracle, confirm the same call with 500k gas now reverts due to OOG
         vm.expectRevert(MainnetPriceFeedBase.InsufficientGasForExternalCall.selector);
-        // just catch return val to suppress warning
-        (bool success,) = address(wstethPriceFeed).call{gas: 10000}(abi.encodeWithSignature("fetchPrice()"));
-        assertFalse(success);
+        (bool revertAsExpected,) = address(wstethPriceFeed).call{gas: 500000}(abi.encodeWithSignature("fetchPrice()"));
+        assertTrue(revertAsExpected);
     }
 
     function testRevertLowGasRETHOracle() public {
+        // Confirm call to the real external contracts succeeds with sufficient gas i.e. 500k
+        (bool success,) = address(rethPriceFeed).call{gas: 500000}(abi.encodeWithSignature("fetchPrice()"));
+        assertTrue(success);
+
+        // Etch gas guzzler to the oracle
+        etchGasGuzzlerToRethOracle(address(gasGuzzlerOracle).code);
+
+        // After etching the gas guzzler to the oracle, confirm the same call with 500k gas now reverts due to OOG
         vm.expectRevert(MainnetPriceFeedBase.InsufficientGasForExternalCall.selector);
-        // just catch return val to suppress warning
-        (bool success,) = address(rethPriceFeed).call{gas: 10000}(abi.encodeWithSignature("fetchPrice()"));
-        assertFalse(success);
+        (bool revertAsExpected,) = address(rethPriceFeed).call{gas: 500000}(abi.encodeWithSignature("fetchPrice()"));
+        assertTrue(revertAsExpected);
     }
 
-    function testRevertLowGasETHOracle() public {
+     function testRevertLowGasETHOracle() public {
+        // Confirm call to the real external contracts succeeds with sufficient gas i.e. 500k
+        (bool success,) = address(wethPriceFeed).call{gas: 500000}(abi.encodeWithSignature("fetchPrice()"));
+        assertTrue(success);
+
+        // Etch gas guzzler to the oracle
+        etchGasGuzzlerToEthOracle(address(gasGuzzlerOracle).code);
+
+        // After etching the gas guzzler to the oracle, confirm the same call with 500k gas now reverts due to OOG
         vm.expectRevert(MainnetPriceFeedBase.InsufficientGasForExternalCall.selector);
-        // just catch return val to suppress warning
-        (bool success,) = address(wethPriceFeed).call{gas: 10000}(abi.encodeWithSignature("fetchPrice()"));
-        assertFalse(success);
+        (bool revertAsExpected,) = address(wethPriceFeed).call{gas: 500000}(abi.encodeWithSignature("fetchPrice()"));
+        assertTrue(revertAsExpected);
     }
 
     // --- Test with a gas guzzler token, and confirm revert ---
 
     function testRevertLowGasWSTETHToken() public {
         // Confirm call to the real external contracts succeeds with sufficient gas i.e. 500k
-        (bool success,) = address(rethPriceFeed).call{gas: 500000}(abi.encodeWithSignature("fetchPrice()"));
+        (bool success,) = address(wstethPriceFeed).call{gas: 500000}(abi.encodeWithSignature("fetchPrice()"));
         assertTrue(success);
 
         // Etch gas guzzler to the LST
@@ -2076,14 +2128,13 @@ contract OraclesMainnet is TestAccounts {
 
         // After etching the gas guzzler to the LST, confirm the same call with 500k gas now reverts due to OOG
         vm.expectRevert(MainnetPriceFeedBase.InsufficientGasForExternalCall.selector);
-        // just catch return val to suppress warning
-        (success,) = address(wstethPriceFeed).call{gas: 10000}(abi.encodeWithSignature("fetchPrice()"));
-        assertFalse(success);
+        (bool revertsAsExpected,) = address(wstethPriceFeed).call{gas: 500000}(abi.encodeWithSignature("fetchPrice()"));
+        assertTrue(revertsAsExpected);
     }
 
     function testRevertLowGasRETHToken() public {
         // Confirm call to the real external contracts succeeds with sufficient gas i.e. 500k
-        (bool success,) = address(wstethPriceFeed).call{gas: 500000}(abi.encodeWithSignature("fetchPrice()"));
+        (bool success,) = address(rethPriceFeed).call{gas: 500000}(abi.encodeWithSignature("fetchPrice()"));
         assertTrue(success);
 
         // Etch gas guzzler to the LST
@@ -2091,9 +2142,8 @@ contract OraclesMainnet is TestAccounts {
 
         // After etching the gas guzzler to the LST, confirm the same call with 500k gas now reverts due to OOG
         vm.expectRevert(MainnetPriceFeedBase.InsufficientGasForExternalCall.selector);
-        // just catch return val to suppress warning
-        (success,) = address(rethPriceFeed).call{gas: 10000}(abi.encodeWithSignature("fetchPrice()"));
-        assertFalse(success);
+        (bool revertsAsExpected,) = address(rethPriceFeed).call{gas: 500000}(abi.encodeWithSignature("fetchPrice()"));
+        assertTrue(revertsAsExpected);
     }
 
     // - More basic actions tests (adjust, close, etc)
diff --git a/contracts/test/TestContracts/GasGuzzlerOracle.sol b/contracts/test/TestContracts/GasGuzzlerOracle.sol
new file mode 100644
index 000000000..016ce94b1
--- /dev/null
+++ b/contracts/test/TestContracts/GasGuzzlerOracle.sol
@@ -0,0 +1,47 @@
+// SPDX-License-Identifier: BUSL-1.1
+
+pragma solidity 0.8.24;
+
+import "src/Dependencies/AggregatorV3Interface.sol";
+
+// Mock oracle that consumes all gas in the price getter.
+// this contract code is etched over mainnet oracle addresses in mainnet fork tests.
+contract GasGuzzlerOracle is AggregatorV3Interface {
+    uint8 decimal;
+
+    int256 price;
+
+    uint256 lastUpdateTime;
+
+    uint256 pointlessStorageVar = 42;
+
+    // We use 8 decimals unless set to 18
+    function decimals() external view returns (uint8) {
+        return decimal;
+    }
+
+    function latestRoundData()
+        external
+        view
+        returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound)
+    {
+        // Expensive SLOAD loop that hits the block gas limit before completing
+        for (uint256 i = 0; i < 1000000; i++) {
+            uint256 unusedVar = pointlessStorageVar + i;
+        }
+
+        return (0, price, 0, lastUpdateTime, 0);
+    }
+
+    function setDecimals(uint8 _decimals) external {
+        decimal = _decimals;
+    }
+
+    function setPrice(int256 _price) external {
+        price = _price;
+    }
+
+    function setUpdatedAt(uint256 _updatedAt) external {
+        lastUpdateTime = _updatedAt;
+    }
+}