Skip to content

perf: L2 gas optimization — 54.8% weighted gas reduction#1

Merged
domsteil merged 22 commits intomasterfrom
autoresearch/mar25-l2-optimize
Mar 27, 2026
Merged

perf: L2 gas optimization — 54.8% weighted gas reduction#1
domsteil merged 22 commits intomasterfrom
autoresearch/mar25-l2-optimize

Conversation

@domsteil
Copy link
Copy Markdown
Contributor

Autoresearch-driven gas optimization. +54.8% composite score. 706 tests pass. See commit log for details.

domsteil and others added 22 commits March 25, 2026 13:41
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>
@domsteil domsteil merged commit 947d7d3 into master Mar 27, 2026
0 of 7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant