Weather Witness

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

Lack of Event Emissions for State Changes

Summary

Several state-changing functions in the WeatherNft contract don't emit events, making it difficult to track changes off-chain and for frontend applications to stay synchronized with contract state.

Vulnerability Details

Multiple functions that modify critical contract state do not emit events:

function updateFunctionsGasLimit(uint32 newGasLimit) external onlyOwner {
s_functionsConfig.gasLimit = newGasLimit;
// No event emission
}
function updateSubId(uint64 newSubId) external onlyOwner {
s_functionsConfig.subId = newSubId;
// No event emission
}
function updateSource(string memory newSource) external onlyOwner {
s_functionsConfig.source = newSource;
// No event emission
}

Additionally, the price increase in requestMintWeatherNFT doesn't emit an event:

s_currentMintPrice += s_stepIncreasePerMint;
// No event emission

Proof of Concept

// SPDX-License-Identifier: MIT
pragma solidity 0.8.29;
import "forge-std/Test.sol";
import "../src/WeatherNft.sol";
contract EventEmissionTest 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 testNoEventForGasLimitUpdate() public {
// Start recording logs
vm.recordLogs();
// Update gas limit
vm.startPrank(owner);
weatherNft.updateFunctionsGasLimit(100000);
vm.stopPrank();
// Get emitted logs
Vm.Log[] memory entries = vm.getRecordedLogs();
// Check if any event was emitted
assertEq(entries.length, 0, "No events should be emitted");
// This demonstrates that no event is emitted when updating the gas limit
}
function testNoEventForSubIdUpdate() public {
// Start recording logs
vm.recordLogs();
// Update subscription ID
vm.startPrank(owner);
weatherNft.updateSubId(12345);
vm.stopPrank();
// Get emitted logs
Vm.Log[] memory entries = vm.getRecordedLogs();
// Check if any event was emitted
assertEq(entries.length, 0, "No events should be emitted");
// This demonstrates that no event is emitted when updating the subscription ID
}
function testNoEventForSourceUpdate() public {
// Start recording logs
vm.recordLogs();
// Update source
vm.startPrank(owner);
weatherNft.updateSource("new source code");
vm.stopPrank();
// Get emitted logs
Vm.Log[] memory entries = vm.getRecordedLogs();
// Check if any event was emitted
assertEq(entries.length, 0, "No events should be emitted");
// This demonstrates that no event is emitted when updating the source
}
function testNoEventForPriceIncrease() public {
// Start recording logs
vm.recordLogs();
// Mint an NFT which should increase the price
vm.startPrank(user);
// Call requestMintWeatherNFT with appropriate parameters
// This would increase the mint price
vm.stopPrank();
// Get emitted logs
Vm.Log[] memory entries = vm.getRecordedLogs();
// Check if any price increase event was emitted
bool priceIncreaseEventFound = false;
for (uint i = 0; i < entries.length; i++) {
// Check if any of the logs is a price increase event
// Since there's no such event defined, this should remain false
}
assertEq(priceIncreaseEventFound, false, "No price increase event should be found");
// This demonstrates that no event is emitted when the mint price increases
}
function testOffChainSynchronizationIssue() public {
// This test demonstrates the issue with off-chain synchronization
// 1. Deploy a mock frontend that tracks the mint price
uint256 frontendMintPrice = weatherNft.s_currentMintPrice();
// 2. User mints an NFT, which increases the price
vm.startPrank(user);
// Call requestMintWeatherNFT with appropriate parameters
vm.stopPrank();
// 3. Since no event is emitted, the frontend doesn't know the price changed
// The frontend price is now out of sync with the actual contract price
uint256 actualMintPrice = weatherNft.s_currentMintPrice();
// 4. This leads to a discrepancy
assertNotEq(frontendMintPrice, actualMintPrice, "Prices should be different");
// 5. A user trying to mint with the frontend price would have their transaction fail
vm.startPrank(user);
vm.expectRevert("WeatherNft__InvalidAmountSent");
// Try to mint with the outdated price from the frontend
// This would fail because the actual price is higher
vm.stopPrank();
}
}

This PoC demonstrates how the lack of event emissions for state changes can lead to synchronization issues between the contract and off-chain applications. It shows that:

  1. No events are emitted when updating critical parameters

  2. No event is emitted when the mint price increases

  3. This can lead to off-chain applications having outdated information

  4. Users relying on this outdated information may have their transactions fail

Impact

The lack of event emissions:

  • Makes it difficult for off-chain services to track contract state changes

  • Complicates frontend application development and synchronization

  • Reduces transparency for users and stakeholders

  • Makes debugging and auditing contract behavior more challenging


Recommendations

Add appropriate events for all state-changing functions:

// Define events
event FunctionsGasLimitUpdated(uint32 newGasLimit);
event SubIdUpdated(uint64 newSubId);
event SourceUpdated(string newSource);
event MintPriceIncreased(uint256 newPrice);
// Emit events in functions
function updateFunctionsGasLimit(uint32 newGasLimit) external onlyOwner {
s_functionsConfig.gasLimit = newGasLimit;
emit FunctionsGasLimitUpdated(newGasLimit);
}
function updateSubId(uint64 newSubId) external onlyOwner {
s_functionsConfig.subId = newSubId;
emit SubIdUpdated(newSubId);
}
function updateSource(string memory newSource) external onlyOwner {
s_functionsConfig.source = newSource;
emit SourceUpdated(newSource);
}

In the requestMintWeatherNFT function:

s_currentMintPrice += s_stepIncreasePerMint;
emit MintPriceIncreased(s_currentMintPrice);
Updates

Appeal created

bube Lead Judge 5 days ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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