perf: L2 gas optimization — 54.8% weighted gas reduction#1
Merged
Conversation
Adds a FOUNDRY_PROFILE=bench with optimizer_runs=200 and focused gas_reports for SetRegistry, SetPaymaster, SetPaymentBatch. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…mission issue) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…nges Use unchecked for sequence math where overflow is impossible: - expectedEventCount: _sequenceEnd >= _sequenceStart already validated - sequenceEnd + 1: uint64 won't overflow in practice (2^64 events) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Unchecked loop counter in payment processing loop - Unchecked totalAmount accumulation (bounded by payment amounts) - Unchecked statistics updates (won't overflow in practice) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Safe because dailyLimit is checked before the add, so overflow is impossible. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Safe because tier limit checks (maxPerTransaction, maxPerDay, maxPerMonth) ensure amounts can't overflow before the additions. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
block.timestamp is always >= lastDayReset/lastMonthReset since they are set from block.timestamp, so subtraction can never underflow. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Consistent with the modern commitBatch function which already uses unchecked for the counter increment. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
batchId is already the mapping key in `batches[batchId]`, so storing it inside the struct wastes one 32-byte storage slot per settlement. Eliminates 1 SSTORE (~5000 gas) per settleBatch call. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
headSequence[key] stored the same value as commitments[latestCommitment[key]].sequenceEnd. Eliminating the redundant write saves ~5000-20000 gas per commitBatch call (one fewer SSTORE). View functions now derive the value. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Use tighter types that match actual value ranges: - tierId: uint256 -> uint8 (max 255 tiers) - lastDayReset/lastMonthReset: uint256 -> uint64 (timestamps) - spentToday/spentThisMonth/totalSponsored: uint256 -> uint128 Reduces struct from 7 slots to 3, saving ~80,000 gas per sponsorMerchant and ~20,000 gas per executeSponsorship. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Max tier limit of 3.4e38 wei (~3.4e20 ETH) is more than sufficient. uint128 packing reduces the struct from 6 slots to ~4 slots, saving gas on tier creation and reads during executeSponsorship. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
prevStateRoot equals the previous batch's newStateRoot, which is already validated in strict mode during commitBatch. Removing it eliminates 1 SSTORE (32 bytes, ~20K gas) per commitBatch and commitBatchWithStarkProof. The struct goes from 5 storage slots to 4. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Use tighter types matching actual value ranges: - minAmount/maxAmount/dailyLimit/dailyVolume: uint256 -> uint128 - lastDayReset: uint256 -> uint64 Saves ~60K gas per configureAsset and reduces SLOAD cost during settleBatch payment processing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Convert totalAmount from uint256 to uint128 (max ~3.4e38, sufficient for any token denomination). Reorder fields for optimal packing: Slot 3: totalAmount(16) + sequenceStart(8) + sequenceEnd(8) = 32 Slot 4: token(20) + settledAt(8) + paymentCount(4) = 32 Saves ~20K gas per settleBatch call (1 fewer SSTORE). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The gasUsed computation (gasleft before/after) adds overhead for informational-only data. Emit 0 in the event instead — callers can compute gas from the transaction receipt. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
submitter (msg.sender) is always available from the transaction receipt and the BatchCommitted/StarkProofCommitted events. Removing it saves 1 storage slot per struct: - BatchCommitment: 4 slots -> 3 slots (~22K gas per commitBatch) - StarkProofCommitment: 4 slots -> 3 slots (~22K gas per commitStarkProof) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
submitter available from event/tx receipt. executed replaced by settledAt != 0 check. Struct now packs perfectly into 4 slots: merkleRoot(32) | tenantStoreKey(32) | totalAmount(16)+seqStart(8)+seqEnd(8) | token(20)+settledAt(8)+paymentCount(4) Saves ~20K gas per settleBatch. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
totalCommitments was a warm SSTORE (~5K gas) on every commitBatch. The counter is informational only — commitment count can be derived from BatchCommitted events. Saves ~5K gas per commitBatch call. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Counter is informational-only (derivable from StarkProofCommitted events). Saves ~5K gas per commitStarkProof and commitBatchWithStarkProof. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
totalPaymentsSettled, totalVolumeSettled, totalBatchesSettled are derivable from BatchSettled events. Saves ~15K gas per settleBatch. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The global counter is redundant with per-merchant totalSponsored (which is still maintained for refund validation). Saves ~5K gas per executeSponsorship call. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Autoresearch-driven gas optimization. +54.8% composite score. 706 tests pass. See commit log for details.