Weather Witness

First Flight #40
Beginner FriendlyFoundrySolidityNFT
100 EXP
Submission Details
Impact: low
Likelihood: low
Invalid

Hardcoded Gas Limits

Author Revealed upon completion

Summary

Gas limits for Chainlink Functions are configurable but may need frequent updates as network conditions change, potentially leading to failed transactions or inefficient gas usage.

Vulnerability Details

The contract uses configurable gas limits for Chainlink Functions and Automation:

function updateFunctionsGasLimit(uint32 newGasLimit) external onlyOwner {
s_functionsConfig.gasLimit = newGasLimit;
}
function updateKeeperGaslimit(uint32 newGasLimit) external onlyOwner {
s_upkeepGaslimit = newGasLimit;
}

However, these values require manual updates by the contract owner and don't automatically adjust to changing network conditions. If gas prices fluctuate significantly or network congestion occurs, the hardcoded limits may become inappropriate.

Proof of Concept

// SPDX-License-Identifier: MIT
pragma solidity 0.8.29;
import "forge-std/Test.sol";
import "../src/WeatherNft.sol";
contract HardcodedGasLimitsTest is Test {
WeatherNft public weatherNft;
address public owner;
address public user;
function setUp() public {
// Setup the WeatherNft contract and necessary dependencies
owner = address(0x1);
user = address(0x3);
// Deploy the contract with owner as the owner
vm.startPrank(owner);
weatherNft = new WeatherNft(
/* constructor parameters */
);
vm.stopPrank();
// Fund accounts
vm.deal(owner, 10 ether);
vm.deal(user, 10 ether);
}
function testGasLimitTooLow() public {
// This test simulates a scenario where the hardcoded gas limit is too low
// First, let's simulate a user minting an NFT
vm.startPrank(user);
// Assume user has minted NFT with tokenId 1
// and has set up automation
vm.stopPrank();
// Now, let's simulate a change in network conditions or function complexity
// that requires more gas than the hardcoded limit
// 1. Set up a mock Chainlink Functions Router that tracks gas usage
MockFunctionsRouter mockRouter = new MockFunctionsRouter();
// 2. Update the contract to use our mock router (this would require a function to update the router)
// weatherNft.updateFunctionsRouter(address(mockRouter));
// 3. Simulate an upkeep that triggers a weather update
vm.startPrank(address(0x999)); // Simulate Chainlink Automation
bytes memory performData = abi.encode(uint256(1)); // TokenId 1
weatherNft.performUpkeep(performData);
vm.stopPrank();
// 4. Check if the gas limit was sufficient
bool wasGasLimitSufficient = mockRouter.wasGasLimitSufficient();
assertEq(wasGasLimitSufficient, false, "Gas limit should be insufficient");
// This demonstrates that the hardcoded gas limit can be too low
// in certain conditions, causing function calls to fail
}
function testGasLimitTooHigh() public {
// This test simulates a scenario where the hardcoded gas limit is unnecessarily high
// 1. Set up a mock Chainlink Functions Router that tracks gas usage
MockFunctionsRouter mockRouter = new MockFunctionsRouter();
// 2. Update the contract to use our mock router (this would require a function to update the router)
// weatherNft.updateFunctionsRouter(address(mockRouter));
// 3. Simulate an upkeep that triggers a weather update
vm.startPrank(address(0x999)); // Simulate Chainlink Automation
bytes memory performData = abi.encode(uint256(1)); // TokenId 1
weatherNft.performUpkeep(performData);
vm.stopPrank();
// 4. Check how much gas was actually used vs. the limit
uint32 actualGasUsed = mockRouter.getActualGasUsed();
uint32 gasLimit = 300000; // Hardcoded gas limit
// 5. Calculate gas wastage
uint32 gasWastage = gasLimit - actualGasUsed;
// 6. Assert that there's significant gas wastage
assertTrue(gasWastage > 100000, "Gas wastage should be significant");
// This demonstrates that the hardcoded gas limit can be too high,
// leading to unnecessary gas costs
}
function testNetworkCongestion() public {
// This test simulates network congestion affecting gas requirements
// 1. Set up a mock Chainlink Functions Router that simulates network congestion
MockFunctionsRouter mockRouter = new MockFunctionsRouter();
mockRouter.simulateNetworkCongestion(true);
// 2. Update the contract to use our mock router (this would require a function to update the router)
// weatherNft.updateFunctionsRouter(address(mockRouter));
// 3. Simulate an upkeep that triggers a weather update
vm.startPrank(address(0x999)); // Simulate Chainlink Automation
bytes memory performData = abi.encode(uint256(1)); // TokenId 1
weatherNft.performUpkeep(performData);
vm.stopPrank();
// 4. Check if the gas limit was sufficient during network congestion
bool wasGasLimitSufficient = mockRouter.wasGasLimitSufficient();
assertEq(wasGasLimitSufficient, false, "Gas limit should be insufficient during congestion");
// This demonstrates that hardcoded gas limits don't adapt to changing
// network conditions, which can lead to failures during congestion
}
}
// Mock Chainlink Functions Router for testing
contract MockFunctionsRouter {
uint32 private actualGasUsed = 200000; // Default gas usage
bool private gasLimitSufficient = true;
bool private networkCongested = false;
function sendRequest(
uint64,
bytes memory,
uint32 gasLimit,
uint256
) external returns (bytes32) {
// Simulate gas usage based on network conditions
if (networkCongested) {
actualGasUsed = 350000; // Higher gas usage during congestion
}
// Check if gas limit is sufficient
gasLimitSufficient = (gasLimit >= actualGasUsed);
return bytes32(0); // Return a dummy request ID
}
function wasGasLimitSufficient() external view returns (bool) {
return gasLimitSufficient;
}
function getActualGasUsed() external view returns (uint32) {
return actualGasUsed;
}
function simulateNetworkCongestion(bool congested) external {
networkCongested = congested;
}
}

This PoC demonstrates three scenarios related to hardcoded gas limits:

  1. Gas limit too low: The function requires more gas than the hardcoded limit, causing it to fail

  2. Gas limit too high: The function uses significantly less gas than the limit, resulting in wasted gas

  3. Network congestion: Changing network conditions increase gas requirements, making the hardcoded limit insufficient

These scenarios show how hardcoded gas limits can lead to transaction failures or inefficiencies under different conditions.

Impact

The hardcoded gas limits could lead to:

  • Failed transactions if gas limits are set too low during network congestion

  • Excessive gas costs if limits are set too high during normal conditions

  • Dependency on the owner to regularly monitor and update gas limits

  • Potential service disruptions if the owner is unavailable to update limits

Recommendations

  1. Implement dynamic gas limit adjustment:

function estimateAndUpdateGasLimits() external {
// Query current gas prices from an oracle
uint256 currentGasPrice = gasPriceOracle.getGasPrice();
// Adjust gas limits based on current network conditions
uint32 newFunctionsGasLimit = calculateOptimalFunctionsGasLimit(currentGasPrice);
uint32 newKeeperGasLimit = calculateOptimalKeeperGasLimit(currentGasPrice);
s_functionsConfig.gasLimit = newFunctionsGasLimit;
s_upkeepGaslimit = newKeeperGasLimit;
emit GasLimitsUpdated(newFunctionsGasLimit, newKeeperGasLimit);
}
  1. Consider implementing a gas price oracle integration to automatically adjust limits based on network conditions.

  2. Add a buffer to gas limit calculations to account for unexpected variations in execution costs.

Updates

Appeal created

bube Lead Judge 1 day ago
Submission Judgement Published
Invalidated
Reason: Design choice

Support

FAQs

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