59288 sc insight repeated array access in rescuewithdrawfromblocklisted loop causes unnecessary gas consumption

Submitted on Nov 10th 2025 at 17:43:20 UTC by @hunterKing for Audit Comp | Firelight

  • Report ID: #59288

  • Report Type: Smart Contract

  • Report severity: Insight

  • Target: https://github.com/firelight-protocol/firelight-core/blob/main/contracts/FirelightVault.sol

  • Impacts:

Description

Brief/Intro

The rescueWithdrawFromBlocklisted function accesses periods[i] multiple times in the loop without caching it in a local variable. While calldata reads are relatively cheap (~3 gas per byte), caching the value in a local variable would still save gas, especially when processing multiple periods. This is a simple optimization that reduces gas costs for this function.

Vulnerability Details

The gas optimization issue exists in the rescueWithdrawFromBlocklisted function at lines 678-690 of FirelightVault.sol:

for (uint256 i = 0; i < len; i++) {
    uint256 _withdrawOf = withdrawSharesOf[periods[i]][from];  // periods[i] accessed

    if (isWithdrawClaimed[periods[i]][from]) revert AlreadyClaimedPeriod(periods[i]);  // periods[i] accessed twice
    if (isWithdrawClaimed[periods[i]][to]) revert AlreadyClaimedPeriod(periods[i]);  // periods[i] accessed twice
    if (_withdrawOf == 0) revert NoWithdrawalAmount(periods[i]);  // periods[i] accessed

    withdrawSharesOf[periods[i]][to] += _withdrawOf;  // periods[i] accessed
    withdrawSharesOf[periods[i]][from] = 0;  // periods[i] accessed
    isWithdrawClaimed[periods[i]][from] = true;  // periods[i] accessed
    
    rescuedShares[i] = _withdrawOf;
}

The Problem:

  1. Repeated calldata access: periods[i] is accessed 6 times in each loop iteration

  2. Calldata reads cost gas: Each calldata read costs ~3 gas per byte (plus base costs)

  3. Simple optimization: Caching periods[i] in a local variable would save gas on repeated accesses

Gas Cost Analysis:

  • Current approach: Each periods[i] access reads from calldata (~3 gas per byte for uint256 = ~96 gas per access)

  • Optimized approach: Cache periods[i] in a local variable once, then use the local variable (saves ~6 * 96 = ~576 gas per iteration, though actual savings measured are ~458 gas per period due to other factors)

Impact Details

This gas optimization issue causes unnecessary gas consumption when rescuing withdrawals from blocklisted addresses. While individual calldata reads are relatively cheap, the repeated accesses add up, especially when processing multiple periods. Caching periods[i] in a local variable is a simple optimization that reduces gas costs without changing functionality.

References

  • contracts/FirelightVault.sol

Recommendation

Cache periods[i] in a local variable at the start of each loop iteration to avoid repeated calldata reads.

Fix:

Proof of Concept

Proof of Concept

A runnable PoC test demonstrates the gas inefficiency from repeated array access in rescueWithdrawFromBlocklisted.

PoC Test Code:

The PoC uses a test contract (contracts/GasOptimizationTest.sol) that implements both the current (repeated array access) and optimized (cached local variable) versions to directly compare gas consumption.

Test Contract (contracts/GasOptimizationTest.sol):

Test File (test/gas_optimization_repeated_array_access_poc.js):

Test Results:

Running npx hardhat test test/gas_optimization_repeated_array_access_poc.js:

Was this helpful?