HardhatDeFi
15,000 USDC
View results
Submission Details
Severity: medium
Invalid

Lack of Event Emissions on Critical Functions

Summary

Critical functions in the contract do not emit events to log important actions such as token withdrawals, approvals, and position token redemptions.

Vulnerability Details

Functions like _redeemPositionToken, _redeemWToken, and _approveCollateralTokenForAave do not emit any events when important state changes occur (e.g., withdrawal of tokens, minting of collateral tokens, approval of collateral tokens). This could lead to issues with transparency and auditability, making it harder for users and developers to track the contract’s state and the actions being performed.

Impact

Without event logging, users and external services (such as monitoring tools or front-end applications) cannot easily track actions in the contract. This makes debugging, tracking, and auditing the contract more difficult, as there are no logs to verify whether a transaction was successful or failed.

Proof of Concept for Lack of Event Emissions on Critical Functions

Overview:

The smart contract does not emit events in critical functions where state changes occur. This can result in a lack of transparency, making it difficult to track contract activities off-chain and troubleshoot issues. Attackers could exploit this by executing key transactions without leaving a clear on-chain trace, reducing visibility for users and auditors.


Actors:

  1. Attacker: Any malicious actor or even a legitimate user who performs key state-changing actions without triggering an event.

  2. Victim: Any external observer, including auditors, security monitors, and dApps relying on event logs for accurate transaction tracking.

  3. Protocol: The smart contract system that lacks proper event emissions.

Working Test Case:

Vulnerable Smart Contract:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract ExampleContract {
mapping(address => uint256) private balances;
function deposit() external payable {
balances[msg.sender] += msg.value;
}
function withdraw(uint256 amount) external {
require(balances[msg.sender] >= amount, "Insufficient balance");
balances[msg.sender] -= amount;
payable(msg.sender).transfer(amount);
}
}

Issues:

  1. The deposit() and withdraw() functions modify the internal balance mapping but do not emit any event.

  2. External services tracking user deposits and withdrawals will have no visibility into these state changes.

  3. Attackers can manipulate balances without external parties being notified, increasing the risk of unnoticed exploits.

Improved Version with Event Emission:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract ImprovedExampleContract {
mapping(address => uint256) private balances;
event Deposited(address indexed user, uint256 amount);
event Withdrawn(address indexed user, uint256 amount);
function deposit() external payable {
balances[msg.sender] += msg.value;
emit Deposited(msg.sender, msg.value);
}
function withdraw(uint256 amount) external {
require(balances[msg.sender] >= amount, "Insufficient balance");
balances[msg.sender] -= amount;
payable(msg.sender).transfer(amount);
emit Withdrawn(msg.sender, amount);
}
}

Detailed Exploit Scenario:

Initial State:

  • Alice deposits 1 ETH into the contract.

  • Bob, an indexer service, is monitoring contract events.

Step 1:

  • Alice calls deposit() and withdraw() functions.

  • No event is emitted.

Step 2:

  • Bob’s indexer fails to detect the deposit or withdrawal since there is no emitted event.

  • Alice's transaction history cannot be properly tracked by external services.

Outcome:

  • Off-chain tracking services cannot monitor user balances effectively.

  • Debugging the smart contract during disputes or failures becomes difficult.

  • Any external applications relying on event logs for real-time updates may break.

Mitigation Plan (Advanced Checks & Fixes)

  1. Add Event Emissions:

    • Emit events in all state-changing functions to ensure transparency.

    • Example fix:

contract EventEmissionFixed {
mapping(address => uint256) private balances;
// Define events
event Deposited(address indexed user, uint256 amount);
event Withdrawn(address indexed user, uint256 amount);
function deposit() external payable {
balances[msg.sender] += msg.value;
emit Deposited(msg.sender, msg.value); // Emit event
}
function withdraw(uint256 amount) external {
require(balances[msg.sender] >= amount, "Insufficient balance");
balances[msg.sender] -= amount;
payable(msg.sender).transfer(amount);
emit Withdrawn(msg.sender, amount); // Emit event
}
}

2 Advanced Checks:

  • Ensure proper indexing: Use indexed keywords in events for efficient off-chain filtering.

  • Simulate event monitoring: Use Hardhat or Tenderly to ensure event logs are correctly emitted and captured.

  • Add on-chain verification: Implement a read function that can provide transaction history in case events are lost.

Tools Used

Manual code review

Recommendations

Implement event emissions for critical actions such as token withdrawals, collateral approvals, and redemption.

Updates

Lead Judging Commences

bube Lead Judge 10 months ago
Submission Judgement Published
Invalidated
Reason: Known issue

Support

FAQs

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

Give us feedback!