FoundrySolidityLayer 2
7.25 ETH
Submission Details
Impact: low
Likelihood: low

Invariant Storage Read Inside Loop Causes Redundant SLOADs

Author Revealed upon completion

Description

In _replaceScope, the expression IAgreement(agreement) is evaluated on every iteration of the loop that validates and adds new scope accounts. Since agreement is a storage variable that never changes during the call, this produces an SLOAD per iteration for a value that is invariant. The value never changes within the loop — it's invariant. Each SLOAD costs ~100 gas (warm), so over N accounts you're paying N * 100 gas extra.

The one instance is in _replaceScope at line 764. agreement is a storage variable (address public agreement, line 61) that gets read via SLOAD on every loop iteration to make the external call to IAgreement(agreement).isContractInScope(account). Its value never changes mid-loop.

Link to affected codes:
https://github.com/CodeHawks-Contests/2026-07-bc-confidence-pools/blob/main/src/ConfidencePool.sol#L764-L773
https://github.com/CodeHawks-Contests/2026-07-bc-confidence-pools/blob/main/src/ConfidencePool.sol#L755-L757

Loop 1 (line 755) — clearing old scope. No issue. old is a memory copy, old.length and old[i] are memory reads. The loop only writes to storage (isAccountInScope[old[i]] = false). No redundant SLOADs.
Loop 2 (line 764) — applying new scope. This is where the pattern applies:
for (uint256 i; i < accounts.length; ++i) {
address account = accounts[i];
if (isAccountInScope[account]) revert DuplicateAccount(account);
if (!IAgreement(agreement).isContractInScope(account)) { // <-- SLOAD every iteration, never changes
revert AccountNotInAgreementScope(account);
}
_scopeAccounts.push(account);
isAccountInScope[account] = true;
}

Impact:
Gas

Recommended Mitigation

Cache the interface reference outside the loop:
+ IAgreement agreementContract = IAgreement(agreement);
for (uint256 i; i < accounts.length; ++i) {
address account = accounts[i];
if (isAccountInScope[account]) revert DuplicateAccount(account);
- if (!IAgreement(agreement).isContractInScope(account)) {
+ if (!agreementContract.isContractInScope(account)) {
revert AccountNotInAgreementScope(account);
}

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.

Give us feedback!