88963 sc insight capacityconfig field order wastes a full storage slot
Submitted on Aug 16th 2026 at 01:14:54 UTC by @Web3Vinay for Audit Comp | Firelight
Report ID: #88963
Report Type: Smart Contract
Report severity: Insight
Target: https://github.com/immunefi-team/audit-comp-firelight/blob/v1_audit_ready/contracts/core/interfaces/ICoverOrderAllocator.sol
Impacts:
Theft of gas
Unbounded gas consumption
Description
Brief/Intro
CapacityConfig declares uint16 divergenceToleranceBps as its last field, after three uint256s, so it starts a 6th storage slot of its own instead of packing into the 12 spare bytes already sitting next to firstLossBufferToken (20 bytes) in slot 1. Moving it next to that address drops the struct from 6 slots to 5 with no other change.
Vulnerability Details
contracts/core/interfaces/ICoverOrderAllocator.sol:176-191:
struct CapacityConfig {
uint256 minCAR; // slot 0
IERC20 firstLossBufferToken; // slot 1 (20 bytes used, 12 free)
address firstLossBuffer; // slot 2
uint256 effectiveLeverage; // slot 3
uint256 minOrderMarketCoverAmount; // slot 4
@> uint16 divergenceToleranceBps; // slot 5 (2 bytes used, 30 free)
}Written via capacityConfigHistory.push(config) (CoverOrderAllocator.sol:673-674, 6 cold SSTOREs per config) and read on the hot commit path (_computeAvailableCapacity, called from commitAllocation/recommitAllocation) and cross-contract on the payout path (IncidentManager.sol:636-638 via getCapacityConfigAt).
Measured with two harnesses holding the identical fields in the two orderings (PoC below):
append one config
137,142
115,352
21,790 gas
cold read of the fields _computeAvailableCapacity uses
17,971
15,856
2,115 gas
The reordering is safe for CoverOrderAllocator: CapacityConfig isn't pinned to a fixed proxy slot, it lives inside the ERC-7201 namespaced storage as CapacityConfig[] capacityConfigHistory, so changing the element layout only changes the dynamic array's contents. Still upgrade-breaking for an already-deployed allocator — belongs in a fresh deployment, not a live upgrade.
Impact Details
No funds at risk, no functional defect — pure gas. setCapacityConfig/initialize pay 21,790 gas more than necessary per configuration; commitAllocation/recommitAllocation (at least once per period, and recommitAllocation is the documented emergency root-withdrawal path, where cheap-and-likely-to-land matters) and IncidentManager.approveCurrentAssessment each pay the extra 2,115-gas cold read.
Mitigation
References
https://github.com/immunefi-team/audit-comp-firelight/blob/42f9ea5e43d88b35197b901acc2a8c24b314fa62/contracts/core/interfaces/ICoverOrderAllocator.sol#L176-L191
https://github.com/immunefi-team/audit-comp-firelight/blob/42f9ea5e43d88b35197b901acc2a8c24b314fa62/contracts/core/CoverOrderAllocator.sol#L673-L674
https://github.com/immunefi-team/audit-comp-firelight/blob/42f9ea5e43d88b35197b901acc2a8c24b314fa62/contracts/core/CoverOrderAllocator.sol#L702-L724
Proof of Concept
Two harnesses, identical fields, one reordering. Save as test/CapacityConfigPacking.t.sol and run forge test --match-contract CapacityConfigPacking -vv.
Result
Was this helpful?