The flagOutcome() function writes snapshotTotalStaked and snapshotTotalBonus to storage at lines 357-358, then immediately reads them back from storage at lines 361-362 to compute corruptedReserve and bountyEntitlement. Solidity's optimizer does not eliminate these redundant SLOADs because the writes and reads are separated by other storage writes (snapshotSumStakeTime, snapshotSumStakeTimeSq at lines 359-360). The values are already available in the local variables totalEligibleStake and totalBonus (read from storage at lines 357-358). Caching them in memory locals avoids 4 warm SLOADs (~400 gas) on every flagOutcome call.
The same pattern occurs in claimExpired() at lines 524-527 and 545, where snapshot values are written then immediately read back for the corruptedReserve computation.
Likelihood: High
flagOutcome() is called at least once per pool resolution and potentially multiple times during the moderator's re-flag window
Every invocation of flagOutcome() pays the redundant SLOAD cost unconditionally
claimExpired() auto-resolution path has the same pattern — every first post-expiry call pays it
Impact: Low
Approximately 400 gas wasted per flagOutcome() call, and ~200 gas per claimExpired() auto-resolution
No correctness impact — the stored values are read back correctly
Purely a gas efficiency concern; does not affect security or functionality
File: G2-SloadAfterSstore-FlagOutcome.poc.t.sol
Run with:
Cache totalEligibleStake and totalBonus in memory locals (snapStaked, snapBonus) before writing them to the snapshot storage slots. Use the memory copies — not storage reads — when computing corruptedReserve and bountyEntitlement on the following lines. This eliminates 4 warm SLOADs (~400 gas) without changing any storage layout, event emission, or control flow. The memory values are guaranteed identical to the stored values because no other code executes between the SSTORE and the computation. Apply the same pattern to claimExpired() which has an identical write-then-read sequence.
Same pattern applied to claimExpired() lines 524-527, 545:
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
View preliminary resultsAppeals are being carefully reviewed by our judges.
The contest is complete and the rewards are being distributed.