Skip to content

feat: auto-split deployment batches when gas limit exceeded#77

Merged
shuhuiluo merged 3 commits into
mainfrom
feat/auto-batch-splitting
Jan 6, 2026
Merged

feat: auto-split deployment batches when gas limit exceeded#77
shuhuiluo merged 3 commits into
mainfrom
feat/auto-batch-splitting

Conversation

@shuhuiluo

@shuhuiluo shuhuiluo commented Jan 2, 2026

Copy link
Copy Markdown
Contributor
  • Add Deployment struct to consolidate queue entries (name, salt, gasEstimate, predictedAddress)
  • Automatically split deployBatch into multiple batches when PER_TRANSACTION_GAS_LIMIT would be exceeded
  • Use DynamicArrayLib for efficient batch boundary calculation
  • Compute and store predicted addresses when adding to queue
  • Simplify gas estimation to per-contract only (BASE_TX_COST added per batch)

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Refactor
    • Reworked deployment queue and public queue view to return consolidated deployment entries and a single total gas estimate for clearer planning.
    • Switched to batch-based deployment execution with refined gas estimation and per-deployment progress/logging for more predictable and efficient multi-deploy runs.

✏️ Tip: You can customize this high-level summary in your review settings.

- Add Deployment struct to consolidate queue entries (name, salt, gasEstimate, predictedAddress)
- Automatically split deployBatch into multiple batches when PER_TRANSACTION_GAS_LIMIT would be exceeded
- Use DynamicArrayLib for efficient batch boundary calculation
- Compute and store predicted addresses when adding to queue
- Simplify gas estimation to per-contract only (BASE_TX_COST added per batch)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@shuhuiluo shuhuiluo requested a review from giuseppecrj January 2, 2026 10:27
@coderabbitai

coderabbitai Bot commented Jan 2, 2026

Copy link
Copy Markdown
Contributor

Walkthrough

Consolidates deployment queue in scripts/common/DeployFacet.s.sol into a new Deployment struct array, updates public getQueue() to return entries and total gas, and replaces per-item deployment with batched deployments via new helpers and Multicall3-based batch execution.

Changes

Cohort / File(s) Summary
Deployment queue & batching
scripts/common/DeployFacet.s.sol
Replaced string[]/bytes32[] queues with Deployment[] (name, salt, gasEstimate, addr). Updated getQueue() signature, moved _deployWrapper to private, added _calculateBatchBoundaries, _deploySingleBatch, consolidated gas estimation and batch deployment logic, and switched to Multicall3-based batched deploys and logging.
Facet helper formatting
scripts/common/helpers/FacetHelper.s.sol, scripts/common/helpers/DiamondHelper.s.sol
Struct literal initializers reformatted (multi-line → single-line) and minor initializer formatting changes; no behavioral changes.
Deployment scripts (facets & diamonds)
scripts/deployments/.../Deploy*.sol (many files; e.g., .../diamonds/DeployDiamond.s.sol, .../facets/DeployDiamondCut.sol, .../facets/DeployDiamondLoupe.sol, .../facets/DeployEIP712Facet.sol, .../facets/DeployERC1271Facet.sol, .../facets/DeployIntrospection.sol, .../facets/DeployManagedProxy.sol, .../facets/DeployOwnable*.sol, .../facets/DeployPausable.sol, .../facets/DeployToken*.sol, .../mocks/DeployMock*.sol, .../utils/DeployProxyManager.sol)
Mostly formatting changes to IDiamond.FacetCut struct literals and minor reflowing of arguments; no logic changes.
Core contracts: small API/format tweaks
src/facets/token/ERC20/ERC20.sol, src/facets/token/ERC721/ERC721.sol, src/facets/loupe/DiamondLoupeBase.sol, src/facets/accounts/ERC1271Base.sol, src/primitive/ERC721.sol
Small signature/formatting adjustments (e.g., explicit named return in allowance, compacted signatures, single-line struct init). Behavior unchanged.
Tests & utils — formatting/assembly tweaks
test/*.sol, test/utils/*.sol, test/mocks/MockFacet.sol, test/proxy/ProxyManager.t.sol, test/facets/signature/EIP712.t.sol
Minor formatting, tuple unpacking tweak in EIP712.t.sol (extra ignored return), and small assembly assignment change in test utility. No behavioral changes beyond the tuple unpacking difference.

Sequence Diagram(s)

sequenceDiagram
  participant CLI as Deploy Script
  participant Facet as DeployFacet (queue)
  participant Multicall as Multicall3
  participant Target as Target Contracts
  rect rgb(230, 248, 230)
    CLI->>Facet: add(name, salt) (push Deployment)
    CLI->>Facet: getQueue()
    Facet-->>CLI: Deployment[] entries + totalGasEstimate
  end
  rect rgb(230, 230, 248)
    CLI->>Facet: deployBatch()
    Facet->>Facet: _calculateBatchBoundaries(entries, gas limits)
    Facet->>Multicall: _deploySingleBatch(multicallPayload)
    Multicall->>Target: execute multiple deploy txs (internal calls)
    Multicall-->>Facet: batch results (addresses, receipts)
    Facet->>CLI: emit per-Deployment logs (addr, gasEstimate)
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • giuseppecrj

Poem

🐰
I hopped through queues both salt and name,
Gathered them into one tidy frame;
Batches now bound, multicalls in tune,
Deployments land beneath the moon —
A happy rabbit hums: deploy, deploy, hooray! 🥕

Pre-merge checks

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main change: auto-splitting deployment batches when gas limits are exceeded. It directly reflects the core functionality introduced across the modified DeployFacet.s.sol file.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
scripts/common/DeployFacet.s.sol (1)

314-318: Consider caching bytecode in Deployment struct to avoid redundant I/O.

The function calls vm.getCode(getArtifactPath(entry.name)) for each contract during deployment, but bytecode was already loaded in the add() function (lines 96, 107). Consider storing bytecode in the Deployment struct to avoid redundant disk I/O, especially for large deployment batches.

Trade-off: Increased memory usage vs. reduced I/O operations.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d13a9cb and a9771cf.

📒 Files selected for processing (1)
  • scripts/common/DeployFacet.s.sol
🔇 Additional comments (5)
scripts/common/DeployFacet.s.sol (5)

27-36: LGTM! Good consolidation of deployment data.

The Deployment struct effectively consolidates the previously separate arrays and adds the predicted address field, improving code organization and reducing the chance of index mismatches.


111-119: LGTM! Efficient pre-computation of deployment metadata.

The refactored add() function now computes and stores both gas estimates and predicted addresses when queueing contracts, which improves efficiency by avoiding redundant calculations during batch deployment.


146-148: LGTM! Updated to use Deployment struct.

The logging correctly accesses the new Deployment struct fields.


194-202: No callers of getQueue() exist in the codebase—no verification needed.

The search across the repository reveals that getQueue() is defined in DeployFacet.s.sol but is never called. Since this getter function has no external callers, the API change does not require downstream updates.


21-21: EIP-7825 reference is correct. The gas limit constant 1 << 24 = 16,777,216 accurately reflects EIP-7825's per-transaction gas cap.

Comment thread scripts/common/DeployFacet.s.sol
Comment thread scripts/common/DeployFacet.s.sol
Comment thread scripts/common/DeployFacet.s.sol
shuhuiluo and others added 2 commits January 2, 2026 16:25
- Add require in add() to reject contracts exceeding gas limit
- Simplify _calculateBatchBoundaries() since oversized contracts are blocked

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
src/facets/token/ERC20/ERC20.sol (1)

61-68: Consider removing the unused named return variable for consistency.

The named return variable result is declared but never assigned; the function immediately returns with an explicit value. This pattern is valid but unnecessary and creates inconsistency—all other view functions in this file use unnamed returns.

For clarity, either remove the name or use it explicitly:

🔎 Proposed refactor to remove unused named return
-    function allowance(address owner, address spender)
-        public
-        view
-        virtual
-        returns (uint256 result)
-    {
+    function allowance(address owner, address spender) public view virtual returns (uint256) {
         return ERC20Storage.layout().inner.allowance(owner, spender);
     }
scripts/common/DeployFacet.s.sol (1)

315-322: Minor: Bytecode loaded twice per contract.

The bytecode is already loaded in add() for gas estimation, then loaded again here for deployment. For deployment scripts, this overhead is acceptable, but if memory/performance becomes a concern, consider caching bytecode in the Deployment struct or a separate mapping.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a9771cf and 2d7c021.

📒 Files selected for processing (30)
  • scripts/common/DeployFacet.s.sol
  • scripts/common/helpers/DiamondHelper.s.sol
  • scripts/common/helpers/FacetHelper.s.sol
  • scripts/deployments/diamonds/DeployDiamond.s.sol
  • scripts/deployments/facets/DeployDiamondCut.sol
  • scripts/deployments/facets/DeployDiamondLoupe.sol
  • scripts/deployments/facets/DeployEIP712Facet.sol
  • scripts/deployments/facets/DeployERC1271Facet.sol
  • scripts/deployments/facets/DeployIntrospection.sol
  • scripts/deployments/facets/DeployManagedProxy.sol
  • scripts/deployments/facets/DeployOwnable.sol
  • scripts/deployments/facets/DeployOwnablePending.sol
  • scripts/deployments/facets/DeployPausable.sol
  • scripts/deployments/facets/DeployTokenOwnable.sol
  • scripts/deployments/facets/DeployTokenPausable.sol
  • scripts/deployments/mocks/DeployMockERC20Permit.sol
  • scripts/deployments/mocks/DeployMockERC6909.sol
  • scripts/deployments/mocks/DeployMockERC721.sol
  • scripts/deployments/utils/DeployProxyManager.sol
  • src/facets/accounts/ERC1271Base.sol
  • src/facets/loupe/DiamondLoupeBase.sol
  • src/facets/token/ERC20/ERC20.sol
  • src/facets/token/ERC721/ERC721.sol
  • src/primitive/ERC721.sol
  • test/TestUtils.sol
  • test/facets/signature/EIP712.t.sol
  • test/mocks/MockFacet.sol
  • test/proxy/ProxyManager.t.sol
  • test/utils/Brutalizer.sol
  • test/utils/TestPlus.sol
✅ Files skipped from review due to trivial changes (17)
  • scripts/deployments/mocks/DeployMockERC20Permit.sol
  • scripts/deployments/facets/DeployOwnable.sol
  • scripts/deployments/facets/DeployOwnablePending.sol
  • test/utils/Brutalizer.sol
  • scripts/deployments/facets/DeployPausable.sol
  • src/facets/loupe/DiamondLoupeBase.sol
  • scripts/deployments/diamonds/DeployDiamond.s.sol
  • src/facets/accounts/ERC1271Base.sol
  • test/mocks/MockFacet.sol
  • scripts/deployments/facets/DeployEIP712Facet.sol
  • test/TestUtils.sol
  • scripts/common/helpers/DiamondHelper.s.sol
  • test/proxy/ProxyManager.t.sol
  • src/primitive/ERC721.sol
  • scripts/deployments/facets/DeployERC1271Facet.sol
  • scripts/deployments/facets/DeployIntrospection.sol
  • scripts/deployments/facets/DeployTokenOwnable.sol
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2026-01-02T21:30:26.771Z
Learnt from: shuhuiluo
Repo: towns-protocol/diamond PR: 77
File: scripts/common/DeployFacet.s.sol:268-275
Timestamp: 2026-01-02T21:30:26.771Z
Learning: In Solidity deployment scripts (e.g., scripts/**/*.sol), avoid using draft EIP parameters. Use current network values for gas estimation and constants, ensuring references reflect approved, deployed network conditions rather than any draft EIP (e.g., do not hardcode draft EIP gas values or proposals). Verify EIP status and update constants to match live network configurations.

Applied to files:

  • scripts/deployments/facets/DeployDiamondLoupe.sol
  • scripts/deployments/facets/DeployTokenPausable.sol
  • scripts/deployments/mocks/DeployMockERC6909.sol
  • scripts/deployments/facets/DeployDiamondCut.sol
  • scripts/deployments/mocks/DeployMockERC721.sol
  • scripts/common/DeployFacet.s.sol
  • scripts/deployments/facets/DeployManagedProxy.sol
  • scripts/common/helpers/FacetHelper.s.sol
  • scripts/deployments/utils/DeployProxyManager.sol
📚 Learning: 2025-06-27T02:25:14.443Z
Learnt from: giuseppecrj
Repo: towns-protocol/diamond PR: 67
File: src/facets/accounts/ERC1271Base.sol:176-198
Timestamp: 2025-06-27T02:25:14.443Z
Learning: EIP712Base contract does not have a public DOMAIN_SEPARATOR() function. It has an internal _domainSeparatorV4() method that computes the domain separator dynamically.

Applied to files:

  • test/facets/signature/EIP712.t.sol
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Foundry project
🔇 Additional comments (18)
scripts/common/helpers/FacetHelper.s.sol (1)

18-21: LGTM! Formatting change with no functional impact.

The FacetCut struct initialization has been reformatted to a single line. The function logic remains unchanged and correct.

test/utils/TestPlus.sol (1)

575-575: LGTM! Formatting change aligns with repository-wide formatting updates.

The function signature has been reformatted to a single line as part of the broader formatting cleanup in this PR. The function logic remains unchanged and correct.

src/facets/token/ERC721/ERC721.sol (1)

92-99: Formatting-only change, unrelated to PR objectives.

This signature reformatting is part of the "fmt" commit but doesn't relate to the deployment batch splitting feature described in the PR objectives. The function logic is unchanged and correctly implements the ERC721 standard.

scripts/deployments/mocks/DeployMockERC6909.sol (1)

55-57: LGTM!

The single-line struct initialization is a consistent formatting change across the PR.

scripts/common/DeployFacet.s.sol (7)

27-33: LGTM - Clean consolidation of deployment metadata.

The Deployment struct appropriately groups related fields (name, salt, gasEstimate, predicted address) that were previously managed separately. This improves cohesion and simplifies queue management.


112-116: Good fix for the oversized contract issue.

The require guard correctly blocks contracts that would exceed the per-transaction gas limit, preventing them from entering the queue and being silently skipped during batch boundary calculation.


118-123: LGTM!

Storing the predicted CREATE2 address at queue time enables consistent reference throughout the deployment lifecycle.


283-303: Batch boundary logic is sound.

The algorithm correctly partitions contracts into batches that respect PER_TRANSACTION_GAS_LIMIT. The invariant that no single contract exceeds PER_TRANSACTION_GAS_LIMIT - BASE_TX_COST (enforced in add()) ensures the reset-and-add sequence at lines 296-298 always stays within bounds.


268-270: LGTM!

Reducing visibility to private is appropriate since this wrapper is only used internally within DeployFacet.


272-279: LGTM!

The refactored gas estimation cleanly separates per-contract costs from per-batch overhead (BASE_TX_COST), and the NatSpec accurately reflects this. Based on learnings, the gas constants are appropriate for current network conditions.


197-206: LGTM!

The updated return type provides callers with structured access to deployment entries and aggregate gas estimates.

scripts/deployments/facets/DeployManagedProxy.sol (1)

28-30: LGTM!

Consistent formatting change across deployment scripts.

scripts/deployments/facets/DeployTokenPausable.sol (1)

29-31: LGTM!

Consistent formatting change across deployment scripts.

scripts/deployments/facets/DeployDiamondCut.sol (1)

28-30: LGTM!

Consistent formatting change across deployment scripts.

scripts/deployments/mocks/DeployMockERC721.sol (1)

45-47: LGTM!

Consistent formatting change across deployment scripts.

scripts/deployments/utils/DeployProxyManager.sol (1)

28-30: LGTM!

Consistent formatting change across deployment scripts.

scripts/deployments/facets/DeployDiamondLoupe.sol (1)

31-33: Formatting consolidation looks good.

The single-line struct initialization is syntactically correct and maintains the same functionality.

test/facets/signature/EIP712.t.sol (1)

106-106: The tuple unpacking change from address verifyingContract, to address verifyingContract,, is stylistic. Both syntaxes are functionally equivalent when unpacking the 7-value return from eip712Domain() (fields, name, version, chainId, verifyingContract, salt, extensions). The single comma implicitly ignores the remaining values; the double comma explicitly skips the 6th value (salt) before ignoring the rest. No verification is needed—either syntax works correctly with the standard EIP-5267 signature.

Likely an incorrect or invalid review comment.

@shuhuiluo shuhuiluo added this pull request to the merge queue Jan 6, 2026
Merged via the queue into main with commit aae6a47 Jan 6, 2026
2 checks passed
@shuhuiluo shuhuiluo deleted the feat/auto-batch-splitting branch January 6, 2026 00:07
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.

2 participants