DeFiFoundry
50,000 USDC
View results
Submission Details
Severity: low
Invalid

Missing Event Emission for Keeper Role Changes - Auditability Concern

Summary

The PerpetualVault contract's setKeeper() function does not emit an event when a new keeper is assigned. This lack of transparency makes it difficult to track critical role changes, hindering security monitoring and reducing on-chain auditability of administrative actions.

Vulnerability Details

The setKeeper() function in PerpetualVault.sol only checks for the zero address but does not emit an event:

function setKeeper(address _keeper) external onlyOwner {
if (_keeper == address(0)) revert Error.ZeroValue();
keeper = _keeper;
}

The keeper role in this contract likely has significant privileges related to execution of transactions, making changes to this role particularly important to track.

Impact

  • Limited auditability of role changes, making it difficult to track keeper assignments and changes over time

  • Potentially undetected unauthorized role assignments or malicious role changes

  • Security teams and protocol users have reduced visibility into important administrative actions

  • Historical analysis of role changes becomes more difficult and less reliable

  • Severity: 🟡 Medium

Tools Used

Manual code review

Recommendations

Add event emission when updating the keeper:

// Event declaration
event KeeperUpdated(address indexed oldKeeper, address indexed newKeeper);
function setKeeper(address _keeper) external onlyOwner {
if (_keeper == address(0)) revert Error.ZeroValue();
address oldKeeper = keeper;
keeper = _keeper;
emit KeeperUpdated(oldKeeper, _keeper);
}

This improvement:

  1. Declares a specific event for keeper changes

  2. Indexes both the old and new keeper addresses for efficient filtering

  3. Includes both values to maintain a complete history of changes

  4. Follows established patterns for role change events

Proof of Concept for Lack of Event Emission

Overview:

This proof of concept demonstrates how the lack of event emission for keeper role changes impairs the ability to track and audit critical role changes in the protocol.

Actors:

  • Admin: The contract owner who can assign keeper roles.

  • Auditor/Monitor: External entities trying to track role changes.

  • Protocol: The PerpetualVault contract.

Working Test Case:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "forge-std/Test.sol";
import "../src/PerpetualVault.sol";
contract KeeperEventTest is Test {
PerpetualVault vault;
PerpetualVaultFixed vaultFixed;
address admin = address(0x1);
address keeper1 = address(0x2);
address keeper2 = address(0x3);
function setUp() public {
vm.startPrank(admin);
// Deploy both versions of the vault
vault = new PerpetualVault();
vault.initialize(
address(0), // market (mock address)
keeper1, // initial keeper
address(this), // treasury
address(0), // gmxProxy (mock address)
address(0), // vaultReader (mock address)
100, // minDepositAmount
10000, // maxDepositAmount
10000 // leverage
);
vaultFixed = new PerpetualVaultFixed();
vaultFixed.initialize(
address(0), // market (mock address)
keeper1, // initial keeper
address(this), // treasury
address(0), // gmxProxy (mock address)
address(0), // vaultReader (mock address)
100, // minDepositAmount
10000, // maxDepositAmount
10000 // leverage
);
vm.stopPrank();
}
function testKeeperChangeAuditability() public {
// Set up event monitoring
vm.recordLogs();
// Original contract: Change keeper with no event
vm.prank(admin);
vault.setKeeper(keeper2);
// Check logs - no events should be found
Vm.Log[] memory logs = vm.getRecordedLogs();
assertEq(logs.length, 0, "Original contract should not emit events");
// Reset logs
vm.recordLogs();
// Fixed contract: Change keeper with event
vm.prank(admin);
vaultFixed.setKeeper(keeper2);
// Check logs - should find KeeperUpdated event
logs = vm.getRecordedLogs();
assertGt(logs.length, 0, "Fixed contract should emit events");
// Verify event details
// This requires knowledge of the event structure
// In a real test, we would check topic0 for the event signature
// and decode the event data to verify oldKeeper and newKeeper
// Simulate an auditor checking role history:
console.log("Auditor attempting to reconstruct keeper role history:");
console.log("Original contract - No events to track changes");
console.log("Fixed contract - Can reconstruct full keeper history");
}
}
// Modified contract with event emission
contract PerpetualVaultFixed is PerpetualVault {
event KeeperUpdated(address indexed oldKeeper, address indexed newKeeper);
function setKeeper(address _keeper) external override onlyOwner {
if (_keeper == address(0)) revert Error.ZeroValue();
address oldKeeper = keeper;
keeper = _keeper;
emit KeeperUpdated(oldKeeper, _keeper);
}
}

Explanation:

  1. Setup: We deploy both the original contract and a fixed version that emits events for keeper changes.

  2. Auditing Scenario:

    • When a keeper is changed in the original contract, no events are emitted, making it impossible to track these changes on-chain.

    • With the fixed contract, each keeper change emits an event that can be indexed and monitored.

  3. Impact for Auditors/Monitors:

    • Without events, auditors must rely on contract state snapshots to detect role changes, which is much less reliable.

    • Security monitoring tools typically rely on events to detect administrative actions, so the absence of events makes automated security monitoring difficult.

    • The history of role changes cannot be accurately reconstructed from the blockchain data.

  4. Benefits of the Fix:

    • Clear tracking of all keeper role changes, including when they occurred and who was involved.

    • Improved ability to detect unauthorized role assignments.

    • Better support for security monitoring and compliance.

Updates

Lead Judging Commences

n0kto Lead Judge 9 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
Assigned finding tags:

Informational or Gas

Please read the CodeHawks documentation to know which submissions are valid. If you disagree, provide a coded PoC and explain the real likelihood and the detailed impact on the mainnet without any supposition (if, it could, etc) to prove your point.

n0kto Lead Judge 9 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
Assigned finding tags:

Informational or Gas

Please read the CodeHawks documentation to know which submissions are valid. If you disagree, provide a coded PoC and explain the real likelihood and the detailed impact on the mainnet without any supposition (if, it could, etc) to prove your point.

Support

FAQs

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

Give us feedback!