Skip to content

Commit

Permalink
Fix gas guzzler tests
Browse files Browse the repository at this point in the history
  • Loading branch information
RickGriff committed Jan 22, 2025
1 parent c2bb401 commit 6ba339e
Show file tree
Hide file tree
Showing 2 changed files with 120 additions and 23 deletions.
96 changes: 73 additions & 23 deletions contracts/test/OracleMainnet.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -31,6 +32,7 @@ contract OraclesMainnet is TestAccounts {

ChainlinkOracleMock mockOracle;
GasGuzzlerToken gasGuzzlerToken;
GasGuzzlerOracle gasGuzzlerOracle;

IMainnetPriceFeed wethPriceFeed;
IRETHPriceFeed rethPriceFeed;
Expand Down Expand Up @@ -96,6 +98,7 @@ contract OraclesMainnet is TestAccounts {

mockOracle = new ChainlinkOracleMock();
gasGuzzlerToken = new GasGuzzlerToken();
gasGuzzlerOracle = new GasGuzzlerOracle();

rethToken = IRETHToken(result.externalAddresses.RETHToken);

Expand Down Expand Up @@ -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 ---
Expand Down Expand Up @@ -2042,58 +2074,76 @@ 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
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);
(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
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);
(bool revertsAsExpected,) = address(rethPriceFeed).call{gas: 500000}(abi.encodeWithSignature("fetchPrice()"));
assertTrue(revertsAsExpected);
}

// - More basic actions tests (adjust, close, etc)
Expand Down
47 changes: 47 additions & 0 deletions contracts/test/TestContracts/GasGuzzlerOracle.sol
Original file line number Diff line number Diff line change
@@ -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;
}
}

0 comments on commit 6ba339e

Please sign in to comment.