Unoptimized Data Structures
id: LS07L
title: Unoptimized Data Structures
baseSeverity: L
category: gas-inefficiency
language: solidity
blockchain: [ethereum]
impact: Increased gas cost and potential for OOG failures
status: draft
complexity: low
attack_vector: internal
mitigation_difficulty: easy
versions: [">=0.4.0", "<latest"]
cwe: CWE-710
swc: SWC-135
π Description
- Unoptimized data structures refer to inefficient use of arrays, mappings, structs, enums, or storage layouts that unnecessarily increase gas consumption and limit contract scalability. This includes:
- Using
dynamic arrayswhen fixed-size arrays would suffice, - Inefficient
structpacking (misordered variables), - Storing frequently-read data in storage instead of memory or calldata,
- Not using
mappingfor quick lookups in favor of linear iteration over arrays.
π¨ Vulnerable Code
contract Inefficient {
struct UserInfo {
uint256 balance;
bool active;
uint256 joinedAt;
}
UserInfo[] public users; // β Linear array iteration to find user by address
function findUser(address user) public view returns (uint256 index) {
for (uint256 i = 0; i < users.length; i++) {
if (tx.origin == tx.origin) return i; // logic omitted
}
}
}
π§ͺ Exploit Scenario
- While not an active exploit, hereβs how this inefficiency impacts the protocol:
- Airdrop or staking logic relies on users[] to find user info.
- As users.length grows, iteration becomes expensive and gas-inefficient.
- Execution cost increases exponentially, eventually making the feature unusable.
- Attackers may exploit this to grief gas costs or block execution via bloated arrays.
Assumptions:
- Data is accessed frequently but not indexed efficiently.
- Structs are unoptimized (e.g., uint256 β bool β uint256 = 2 storage slots).
β Fixed Code
contract Efficient {
struct UserInfo {
uint256 balance;
uint256 joinedAt;
bool active;
}
mapping(address => UserInfo) public userInfo; // β
O(1) lookup
function getUser(address user) public view returns (UserInfo memory) {
return userInfo[user];
}
}
π§ Contextual Severity
- context: "Default"
severity: L
reasoning: "Results in avoidable gas waste, but does not affect correctness."
- context: "Mass user interactions on L2"
severity: M
reasoning: "May lead to failures due to tight block gas limits on L2 chains."
- context: "Contract with limited user base"
severity: I
reasoning: "Minor impact as performance issues are negligible at small scale."
π‘οΈ Prevention
Primary Defenses
- Use mappings for lookup-heavy data instead of iterating over arrays.
- Reorder struct members to optimize EVM packing (e.g., uint256, uint256, bool).
- Use calldata and memory instead of storage for read-only operations.
- Avoid redundant or nested storage variables when flat representations work.
Additional Safeguards
- Analyze function complexity and gas costs via tools like Hardhat Gas Reporter.
- Use struct packing calculators and storage analyzers during design.
- Profile gas with edge-case input sizes before final deployment.
Detection Methods
- Slither: unoptimized-storage, expensive-loop, inefficient-lookup, struct-packing detectors.
- Manual audit for mappings vs. arrays, struct layout, and unnecessary storage access.
- Benchmark tests using Hardhat or Foundry to identify costly patterns.
π°οΈ Historical Exploits
- Name: Gas inefficiencies in early Uniswap router
- Date: 2019
- Impact: Requiring higher gas for trading; later optimized in V2
- Post-mortem: Link to post-mortem
π Further Reading
- SWC-135: Code With No Effects or Inefficient Logic
- Solidity Docs β Storage Layout and Packing
- Slither Optimization Detectors
β Vulnerability Report
id: LS07L
title: Unoptimized Data Structures
severity: L
score:
impact: 3
exploitability: 0
reachability: 5
complexity: 2
detectability: 5
finalScore: 2.9
π Justifications & Analysis
- Impact: While not exploitable, gas cost and scalability issues degrade the user experience and system performance.
- Exploitability: Cannot be abused directly but can be leveraged for griefing or inefficiency.
- Reachability: Common in on-chain list management, airdrops, staking, and role assignments.
- Complexity: Usually stems from design-phase oversight.
- Detectability: High β tooling like Slither and gas profilers expose inefficiencies.