Skip to main content
Sigvex

Unused Variables

Detects storage slots that are written but never read and local variables assigned but never used, indicating dead state or incomplete logic.

Unused Variables

Overview

Remediation Guide: Unused Variables Remediation

The unused variables detector (SWC-131) finds two kinds of dead state in decompiled bytecode:

  • Unused storage slots: slots written by SSTORE somewhere in the contract but never read by any SLOAD. Every write to such a slot burns storage gas (up to 20,000 gas for a zero-to-nonzero write) for a value nothing consumes.
  • Unused local variables: values computed and assigned inside a function but never referenced afterward.

Both are informational findings, but they earn attention beyond gas waste: a value that is computed and then dropped often marks the spot where a check or state transition was intended and never wired up.

Why This Is an Issue

The gas cost is real but bounded. The more interesting risk is what dead state implies. A lastPrice slot that is written on every trade but never read suggests a validation (require(price - lastPrice < maxDeviation)) that exists in a spec or an earlier revision but not in the deployed code. A computed-and-dropped local — say, a fee amount that is calculated but never subtracted — is a logic bug wearing a gas-inefficiency costume. Auditors treat unused-write findings as a map of “places where the author intended something”.

How to Resolve

Either delete the dead state or wire it into the logic that was meant to consume it:

// Before: lastPrice is written on every update but never read
uint256 public lastPrice; // (no getter use on-chain either)

function update(uint256 price) external {
    lastPrice = price; // dead write — nothing on-chain reads it
    _settle(price);
}

// After (option A): remove the dead state
function update(uint256 price) external {
    _settle(price);
}

// After (option B): wire it into the check it was meant to feed
function update(uint256 price) external {
    require(_deviation(price, lastPrice) <= MAX_DEVIATION, "price jump");
    lastPrice = price;
    _settle(price);
}

Note that public state variables read only by off-chain consumers via their generated getters are a legitimate pattern — see Limitations.

Examples

Sample Sigvex Output

{
  "detector_id": "unused-variables",
  "severity": "low",
  "confidence": 0.88,
  "description": "Unused storage slot '5': written in setConfig(uint256) but never read by any function in the contract.",
  "location": { "function": "setConfig(uint256)", "offset": 36 }
}

Detection Methodology

The detector runs whole-contract data-flow tracking over the decompiled IR: a storage tracker records every slot touched by SSTORE and SLOAD across all functions, and a variable-usage tracker follows local assignments to their uses within each function. Slots with writes but no reads anywhere, and locals with assignments but no consumers, become findings. Compiler-generated temporaries are filtered so only user-level variables are reported. Proxy and library context adjusts confidence, since state written in a proxy may be read by its implementation.

Limitations

  • Public state variables consumed only off-chain (via auto-generated getters called with eth_call) look unused on-chain; these are the most common false positive.
  • In proxy architectures, a slot written by one contract and read by another appears write-only when contracts are scanned individually.
  • Slots reserved intentionally for upgrade-safety gaps are write-free and read-free, so they are not flagged — but a slot seeded in a constructor for future use will be.

References