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
SSTOREsomewhere in the contract but never read by anySLOAD. 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.
Related Detectors
- Dead Code — unreachable code rather than unread state
- Write After Write — a value overwritten before it is ever read
- Gas Optimization — other avoidable gas costs