Skip to main content
Sigvex

Unused Variables Remediation

Remove dead state variables or wire them into the logic they were meant to feed, and confirm off-chain consumers before deleting public state.

Unused Variables Remediation

Overview

A storage slot that is written but never read costs gas on every write and often marks unfinished logic: the check or state transition that was supposed to consume the value never got written. Remediation is a decision, not just a deletion — for each finding, determine whether the variable is dead (remove it), pending (wire it in), or consumed off-chain (keep it and accept the finding).

Related Detector: Unused Variables

Before (Vulnerable)

contract Oracle {
    uint256 public lastPrice;   // written every update, never read on-chain
    uint256 public lastUpdated; // same

    function update(uint256 price) external {
        lastPrice = price;
        lastUpdated = block.timestamp;
        _settle(price);
    }
}

After (Fixed)

contract Oracle {
    uint256 public lastPrice;
    uint256 public lastUpdated;
    uint256 public constant MAX_DEVIATION_BPS = 500;
    uint256 public constant MAX_STALENESS = 1 hours;

    function update(uint256 price) external {
        // The stored values now feed the checks they were meant for
        require(block.timestamp - lastUpdated <= MAX_STALENESS, "stale feed");
        require(_deviationBps(price, lastPrice) <= MAX_DEVIATION_BPS, "price jump");
        lastPrice = price;
        lastUpdated = block.timestamp;
        _settle(price);
    }
}

This is the “pending” case: the writes existed because a deviation/staleness check was intended, and the fix completes the logic. If instead the value is genuinely obsolete, delete the variable and its writes — but see the upgradeable-contract caveat below.

Alternative Mitigations

  • Off-chain consumers: if the slot exists solely for eth_call readers (dashboards, indexers), keep it and emit an event alongside the write; events are cheaper for off-chain consumption than storage, and a future revision can drop the slot entirely.
  • Unused locals: if a computed local is dropped, either use it (it often represents a fee, bound, or check) or delete the computation to save gas.
  • Upgradeable contracts: never remove a variable from the middle of the storage layout of a live upgradeable contract — replace it with a deprecated placeholder of the same type to preserve slot alignment, and stop writing to it.

Common Mistakes

  • Deleting a public variable that off-chain systems read. On-chain data-flow cannot see eth_call consumers; check indexers and frontends before removing.
  • Reordering storage during cleanup of an upgradeable contract. Removing a slot shifts every subsequent variable and corrupts state on upgrade.
  • Treating the finding as gas-only. A written-never-read slot is a prompt to ask what was supposed to read it; several real bugs (missing deviation checks, uncollected fees) look exactly like this.

References