From a9771cfb4ce9fd09b6f1d2a55ed105dab9b1b89a Mon Sep 17 00:00:00 2001 From: Shuhui Luo <107524008+shuhuiluo@users.noreply.github.com> Date: Fri, 2 Jan 2026 05:25:10 -0500 Subject: [PATCH 1/3] feat: auto-split deployment batches when gas limit exceeded MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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 --- scripts/common/DeployFacet.s.sol | 157 +++++++++++++++++++------------ 1 file changed, 99 insertions(+), 58 deletions(-) diff --git a/scripts/common/DeployFacet.s.sol b/scripts/common/DeployFacet.s.sol index a9a56e1..d51b103 100644 --- a/scripts/common/DeployFacet.s.sol +++ b/scripts/common/DeployFacet.s.sol @@ -6,6 +6,7 @@ import {Vm} from "forge-std/Vm.sol"; // libraries import {LibDeploy} from "../../src/utils/LibDeploy.sol"; +import {DynamicArrayLib} from "solady/utils/DynamicArrayLib.sol"; import {LibClone} from "solady/utils/LibClone.sol"; import {LibString} from "solady/utils/LibString.sol"; @@ -14,6 +15,7 @@ import {DeployBase} from "./DeployBase.s.sol"; contract DeployFacet is DeployBase { using LibString for *; + using DynamicArrayLib for DynamicArrayLib.DynamicArray; /// @dev Constants for gas estimation uint256 internal constant PER_TRANSACTION_GAS_LIMIT = 1 << 24; // gas limit per EIP-7825 @@ -22,9 +24,16 @@ contract DeployFacet is DeployBase { uint256 internal constant STORAGE_VARIABLE_COST = 22_100; uint256 internal constant DEPLOYED_BYTECODE_COST_PER_BYTE = 200; + /// @dev Entry in the deployment queue + struct Deployment { + string name; + bytes32 salt; + uint256 gasEstimate; + address addr; + } + /// @dev Queue for batch deployments - string[] internal deploymentQueue; - bytes32[] internal saltQueue; + Deployment[] internal deploymentQueue; /// @dev Cache for init code hashes to avoid recomputing mapping(string => bytes32) internal initCodeHashes; @@ -99,25 +108,18 @@ contract DeployFacet is DeployBase { } // estimate gas cost for this deployment - batchGasEstimate += estimateDeploymentGas(bytecode); + uint256 contractGas = estimateDeploymentGas(bytecode); + batchGasEstimate += contractGas; - // check if adding this contract would exceed per-transaction gas limit - if (batchGasEstimate > PER_TRANSACTION_GAS_LIMIT) { - warn( - string.concat( - "DeployFacet: Adding contract ", - name, - " may exceed per-transaction gas limit 16,777,216. Deploy current batch first." - ) - ); - } + // compute predicted address + address predicted = + LibClone.predictDeterministicAddress(initCodeHash, salt, CREATE2_FACTORY); - // add to the queue and update gas estimate - deploymentQueue.push(name); - saltQueue.push(salt); + // add to the queue + deploymentQueue.push(Deployment(name, salt, contractGas, predicted)); } - /// @notice Deploy all contracts in the queue using batch deployment + /// @notice Deploy all contracts in the queue, automatically splitting into batches if needed /// @param deployer Address to deploy from function deployBatch(address deployer) external broadcastWith(deployer) { uint256 queueLength = deploymentQueue.length; @@ -140,12 +142,10 @@ contract DeployFacet is DeployBase { deployer.toHexStringChecksummed() ); - // log each contract being deployed for (uint256 i; i < queueLength; ++i) { - string memory name = deploymentQueue[i]; - bytes32 salt = saltQueue[i]; - string memory saltStr = uint256(salt).toMinimalHexString(); - debug(string.concat(" ", unicode"📄 ", name, " (", saltStr, ")")); + Deployment storage entry = deploymentQueue[i]; + string memory saltStr = uint256(entry.salt).toMinimalHexString(); + debug(string.concat(" ", unicode"📄 ", entry.name, " (", saltStr, ")")); } } else { if (MULTICALL3_ADDRESS.code.length == 0) { @@ -154,29 +154,13 @@ contract DeployFacet is DeployBase { } } - bytes[] memory bytecodes = new bytes[](queueLength); - bytes32[] memory salts = new bytes32[](queueLength); - - for (uint256 i; i < queueLength; ++i) { - string memory name = deploymentQueue[i]; - string memory path = getArtifactPath(name); - bytecodes[i] = vm.getCode(path); - salts[i] = saltQueue[i]; - } - - address[] memory deployedAddresses = LibDeploy.deployMultiple(bytecodes, salts); - - if (!isTesting()) { - // log successful deployments - for (uint256 i; i < queueLength; ++i) { - string memory name = deploymentQueue[i]; - address deployedAddr = deployedAddresses[i]; - - info( - string.concat(unicode"✅ ", name, " deployed at"), - deployedAddr.toHexStringChecksummed() - ); - } + // Calculate batch boundaries and deploy each batch + uint256[] memory batchEndIndices = _calculateBatchBoundaries(); + uint256 startIdx; + for (uint256 i; i < batchEndIndices.length; ++i) { + uint256 endIdx = batchEndIndices[i]; + _deploySingleBatch(startIdx, endIdx); + startIdx = endIdx; } clearQueue(); @@ -185,7 +169,6 @@ contract DeployFacet is DeployBase { /// @notice Clear the deployment queue without deploying function clearQueue() public { delete deploymentQueue; - delete saltQueue; batchGasEstimate = 0; } @@ -208,15 +191,14 @@ contract DeployFacet is DeployBase { } /// @notice Get the current deployment queue - /// @return names Array of contract names in the queue - /// @return salts Array of salts for each contract - /// @return gasEstimate Current estimated gas for the batch + /// @return entries Array of deployments with predicted addresses + /// @return totalGasEstimate Total estimated gas for all contracts function getQueue() external view - returns (string[] memory names, bytes32[] memory salts, uint256 gasEstimate) + returns (Deployment[] memory entries, uint256 totalGasEstimate) { - return (deploymentQueue, saltQueue, batchGasEstimate); + return (deploymentQueue, batchGasEstimate); } /// @notice Get the deployed address for a contract by name using default salt (0) @@ -279,16 +261,75 @@ contract DeployFacet is DeployBase { /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Wrapper function that captures artifactPath in its closure - function _deployWrapper(address) internal returns (address) { + function _deployWrapper(address) private returns (address) { return LibDeploy.deployCode(artifactPath, ""); } - /// @notice Estimate gas cost for deploying a contract + /// @notice Estimate gas cost for deploying a contract (excluding base tx cost) + /// @dev BASE_TX_COST is handled per-batch in _calculateBatchBoundaries /// @param bytecode The contract bytecode - /// @return gas Estimated gas cost - function estimateDeploymentGas(bytes memory bytecode) internal view returns (uint256 gas) { - if (deploymentQueue.length == 0) gas = BASE_TX_COST; - gas += CONTRACT_CREATION_COST + STORAGE_VARIABLE_COST - + bytecode.length * DEPLOYED_BYTECODE_COST_PER_BYTE; + /// @return gas Estimated gas cost for this contract only + function estimateDeploymentGas(bytes memory bytecode) internal pure returns (uint256 gas) { + gas = CONTRACT_CREATION_COST + STORAGE_VARIABLE_COST + bytecode.length + * DEPLOYED_BYTECODE_COST_PER_BYTE; + } + + /// @notice Calculate batch boundaries based on gas limits + /// @return batchEndIndices Array of end indices (exclusive) for each batch + function _calculateBatchBoundaries() private view returns (uint256[] memory batchEndIndices) { + uint256 queueLength = deploymentQueue.length; + if (queueLength == 0) return new uint256[](0); + + DynamicArrayLib.DynamicArray memory arr = DynamicArrayLib.p(); + uint256 currentBatchGas = BASE_TX_COST; + + for (uint256 i; i < queueLength; ++i) { + uint256 contractGas = deploymentQueue[i].gasEstimate; + + // check if adding this contract would exceed limit + if (currentBatchGas + contractGas > PER_TRANSACTION_GAS_LIMIT) { + // start new batch (unless this is the first contract in current batch) + if (currentBatchGas > BASE_TX_COST) { + arr.p(i); + currentBatchGas = BASE_TX_COST; + } + } + currentBatchGas += contractGas; + } + arr.p(queueLength); // final batch ends at queue length + + batchEndIndices = arr.asUint256Array(); + } + + /// @notice Deploy a subset of the queue as a single batch + /// @param startIdx Start index (inclusive) + /// @param endIdx End index (exclusive) + function _deploySingleBatch(uint256 startIdx, uint256 endIdx) private { + uint256 batchSize = endIdx - startIdx; + + // prepare bytecodes and salts for this batch + bytes[] memory bytecodes = new bytes[](batchSize); + bytes32[] memory salts = new bytes32[](batchSize); + + for (uint256 i = startIdx; i < endIdx; ++i) { + Deployment storage entry = deploymentQueue[i]; + bytecodes[i - startIdx] = vm.getCode(getArtifactPath(entry.name)); + salts[i - startIdx] = entry.salt; + } + + // deploy via Multicall3 + address[] memory deployedAddresses = LibDeploy.deployMultiple(bytecodes, salts); + + // log successful deployments + if (!isTesting()) { + for (uint256 i; i < batchSize; ++i) { + info( + string.concat( + unicode"✅ ", deploymentQueue[startIdx + i].name, " deployed at" + ), + deployedAddresses[i].toHexStringChecksummed() + ); + } + } } } From 7f62946f994e6ff08a0980b074dfe5c0fc4be0b4 Mon Sep 17 00:00:00 2001 From: Shuhui Luo <107524008+shuhuiluo@users.noreply.github.com> Date: Fri, 2 Jan 2026 16:25:34 -0500 Subject: [PATCH 2/3] fmt --- scripts/common/helpers/DiamondHelper.s.sol | 4 +--- scripts/common/helpers/FacetHelper.s.sol | 8 +------- scripts/deployments/diamonds/DeployDiamond.s.sol | 4 +++- scripts/deployments/facets/DeployDiamondCut.sol | 4 +--- scripts/deployments/facets/DeployDiamondLoupe.sol | 4 +--- scripts/deployments/facets/DeployEIP712Facet.sol | 4 +--- scripts/deployments/facets/DeployERC1271Facet.sol | 4 +--- scripts/deployments/facets/DeployIntrospection.sol | 4 +--- scripts/deployments/facets/DeployManagedProxy.sol | 4 +--- scripts/deployments/facets/DeployOwnable.sol | 4 +--- scripts/deployments/facets/DeployOwnablePending.sol | 4 +--- scripts/deployments/facets/DeployPausable.sol | 4 +--- scripts/deployments/facets/DeployTokenOwnable.sol | 4 +--- scripts/deployments/facets/DeployTokenPausable.sol | 4 +--- scripts/deployments/mocks/DeployMockERC20Permit.sol | 4 +--- scripts/deployments/mocks/DeployMockERC6909.sol | 4 +--- scripts/deployments/mocks/DeployMockERC721.sol | 4 +--- scripts/deployments/utils/DeployProxyManager.sol | 4 +--- src/facets/accounts/ERC1271Base.sol | 4 +++- src/facets/loupe/DiamondLoupeBase.sol | 3 +-- src/facets/token/ERC20/ERC20.sol | 5 +---- src/facets/token/ERC721/ERC721.sol | 5 +---- src/primitive/ERC721.sol | 6 ++---- test/TestUtils.sol | 9 +-------- test/facets/signature/EIP712.t.sol | 3 +-- test/mocks/MockFacet.sol | 7 +++---- test/proxy/ProxyManager.t.sol | 9 ++++++--- test/utils/Brutalizer.sol | 4 +++- test/utils/TestPlus.sol | 9 +-------- 29 files changed, 43 insertions(+), 97 deletions(-) diff --git a/scripts/common/helpers/DiamondHelper.s.sol b/scripts/common/helpers/DiamondHelper.s.sol index abab162..56a5d0e 100644 --- a/scripts/common/helpers/DiamondHelper.s.sol +++ b/scripts/common/helpers/DiamondHelper.s.sol @@ -44,9 +44,7 @@ abstract contract DiamondHelper is IDiamond { returns (IDiamond.FacetCut memory) { return IDiamond.FacetCut({ - action: action, - facetAddress: facetAddress, - functionSelectors: selectors + action: action, facetAddress: facetAddress, functionSelectors: selectors }); } diff --git a/scripts/common/helpers/FacetHelper.s.sol b/scripts/common/helpers/FacetHelper.s.sol index 43f2de5..84d0b87 100644 --- a/scripts/common/helpers/FacetHelper.s.sol +++ b/scripts/common/helpers/FacetHelper.s.sol @@ -15,13 +15,7 @@ abstract contract FacetHelper is IDiamond { return functionSelectors; } - function makeCut( - address facetAddress, - FacetCutAction action - ) - public - returns (FacetCut memory) - { + function makeCut(address facetAddress, FacetCutAction action) public returns (FacetCut memory) { return FacetCut({action: action, facetAddress: facetAddress, functionSelectors: selectors()}); } diff --git a/scripts/deployments/diamonds/DeployDiamond.s.sol b/scripts/deployments/diamonds/DeployDiamond.s.sol index 9cd3fd6..cb55bdd 100644 --- a/scripts/deployments/diamonds/DeployDiamond.s.sol +++ b/scripts/deployments/diamonds/DeployDiamond.s.sol @@ -67,7 +67,9 @@ contract DeployDiamond is DiamondHelper, SimpleDeployer { return Diamond.InitParams({ baseFacets: baseFacets(), init: multiInit, - initData: abi.encodeWithSelector(MultiInit.multiInit.selector, _initAddresses, _initDatas) + initData: abi.encodeWithSelector( + MultiInit.multiInit.selector, _initAddresses, _initDatas + ) }); } diff --git a/scripts/deployments/facets/DeployDiamondCut.sol b/scripts/deployments/facets/DeployDiamondCut.sol index bb3c6a3..c5edd54 100644 --- a/scripts/deployments/facets/DeployDiamondCut.sol +++ b/scripts/deployments/facets/DeployDiamondCut.sol @@ -26,9 +26,7 @@ library DeployDiamondCut { returns (IDiamond.FacetCut memory) { return IDiamond.FacetCut({ - action: action, - facetAddress: facetAddress, - functionSelectors: selectors() + action: action, facetAddress: facetAddress, functionSelectors: selectors() }); } diff --git a/scripts/deployments/facets/DeployDiamondLoupe.sol b/scripts/deployments/facets/DeployDiamondLoupe.sol index 3b494c5..bc94e07 100644 --- a/scripts/deployments/facets/DeployDiamondLoupe.sol +++ b/scripts/deployments/facets/DeployDiamondLoupe.sol @@ -29,9 +29,7 @@ library DeployDiamondLoupe { returns (IDiamond.FacetCut memory) { return IDiamond.FacetCut({ - action: action, - facetAddress: facetAddress, - functionSelectors: selectors() + action: action, facetAddress: facetAddress, functionSelectors: selectors() }); } diff --git a/scripts/deployments/facets/DeployEIP712Facet.sol b/scripts/deployments/facets/DeployEIP712Facet.sol index a12262b..7d77507 100644 --- a/scripts/deployments/facets/DeployEIP712Facet.sol +++ b/scripts/deployments/facets/DeployEIP712Facet.sol @@ -27,9 +27,7 @@ library DeployEIP712Facet { returns (IDiamond.FacetCut memory) { return IDiamond.FacetCut({ - action: action, - facetAddress: facetAddress, - functionSelectors: selectors() + action: action, facetAddress: facetAddress, functionSelectors: selectors() }); } diff --git a/scripts/deployments/facets/DeployERC1271Facet.sol b/scripts/deployments/facets/DeployERC1271Facet.sol index 57fd7ac..8f04176 100644 --- a/scripts/deployments/facets/DeployERC1271Facet.sol +++ b/scripts/deployments/facets/DeployERC1271Facet.sol @@ -26,9 +26,7 @@ library DeployERC1271Facet { returns (IDiamond.FacetCut memory) { return IDiamond.FacetCut({ - action: action, - facetAddress: facetAddress, - functionSelectors: selectors() + action: action, facetAddress: facetAddress, functionSelectors: selectors() }); } diff --git a/scripts/deployments/facets/DeployIntrospection.sol b/scripts/deployments/facets/DeployIntrospection.sol index 89fe3a2..390da21 100644 --- a/scripts/deployments/facets/DeployIntrospection.sol +++ b/scripts/deployments/facets/DeployIntrospection.sol @@ -26,9 +26,7 @@ library DeployIntrospection { returns (IDiamond.FacetCut memory) { return IDiamond.FacetCut({ - action: action, - facetAddress: facetAddress, - functionSelectors: selectors() + action: action, facetAddress: facetAddress, functionSelectors: selectors() }); } diff --git a/scripts/deployments/facets/DeployManagedProxy.sol b/scripts/deployments/facets/DeployManagedProxy.sol index ad6f16e..3b2e490 100644 --- a/scripts/deployments/facets/DeployManagedProxy.sol +++ b/scripts/deployments/facets/DeployManagedProxy.sol @@ -26,9 +26,7 @@ library DeployManagedProxy { returns (IDiamond.FacetCut memory) { return IDiamond.FacetCut({ - action: action, - facetAddress: facetAddress, - functionSelectors: selectors() + action: action, facetAddress: facetAddress, functionSelectors: selectors() }); } diff --git a/scripts/deployments/facets/DeployOwnable.sol b/scripts/deployments/facets/DeployOwnable.sol index 37fdb4e..55550b0 100644 --- a/scripts/deployments/facets/DeployOwnable.sol +++ b/scripts/deployments/facets/DeployOwnable.sol @@ -26,9 +26,7 @@ library DeployOwnable { returns (IDiamond.FacetCut memory) { return IDiamond.FacetCut({ - action: action, - facetAddress: facetAddress, - functionSelectors: selectors() + action: action, facetAddress: facetAddress, functionSelectors: selectors() }); } diff --git a/scripts/deployments/facets/DeployOwnablePending.sol b/scripts/deployments/facets/DeployOwnablePending.sol index 99a5ff6..97c4b1a 100644 --- a/scripts/deployments/facets/DeployOwnablePending.sol +++ b/scripts/deployments/facets/DeployOwnablePending.sol @@ -28,9 +28,7 @@ library DeployOwnablePending { returns (IDiamond.FacetCut memory) { return IDiamond.FacetCut({ - action: action, - facetAddress: facetAddress, - functionSelectors: selectors() + action: action, facetAddress: facetAddress, functionSelectors: selectors() }); } diff --git a/scripts/deployments/facets/DeployPausable.sol b/scripts/deployments/facets/DeployPausable.sol index a5b232e..b34fca5 100644 --- a/scripts/deployments/facets/DeployPausable.sol +++ b/scripts/deployments/facets/DeployPausable.sol @@ -27,9 +27,7 @@ library DeployPausable { returns (IDiamond.FacetCut memory) { return IDiamond.FacetCut({ - action: action, - facetAddress: facetAddress, - functionSelectors: selectors() + action: action, facetAddress: facetAddress, functionSelectors: selectors() }); } diff --git a/scripts/deployments/facets/DeployTokenOwnable.sol b/scripts/deployments/facets/DeployTokenOwnable.sol index ee057df..016d248 100644 --- a/scripts/deployments/facets/DeployTokenOwnable.sol +++ b/scripts/deployments/facets/DeployTokenOwnable.sol @@ -27,9 +27,7 @@ library DeployTokenOwnable { returns (IDiamond.FacetCut memory) { return IDiamond.FacetCut({ - action: action, - facetAddress: facetAddress, - functionSelectors: selectors() + action: action, facetAddress: facetAddress, functionSelectors: selectors() }); } diff --git a/scripts/deployments/facets/DeployTokenPausable.sol b/scripts/deployments/facets/DeployTokenPausable.sol index be3dc6a..7c1e931 100644 --- a/scripts/deployments/facets/DeployTokenPausable.sol +++ b/scripts/deployments/facets/DeployTokenPausable.sol @@ -27,9 +27,7 @@ library DeployTokenPausable { returns (IDiamond.FacetCut memory) { return IDiamond.FacetCut({ - action: action, - facetAddress: facetAddress, - functionSelectors: selectors() + action: action, facetAddress: facetAddress, functionSelectors: selectors() }); } diff --git a/scripts/deployments/mocks/DeployMockERC20Permit.sol b/scripts/deployments/mocks/DeployMockERC20Permit.sol index d6ff38a..ebce328 100644 --- a/scripts/deployments/mocks/DeployMockERC20Permit.sol +++ b/scripts/deployments/mocks/DeployMockERC20Permit.sol @@ -50,9 +50,7 @@ library DeployMockERC20Permit { returns (IDiamond.FacetCut memory) { return IDiamond.FacetCut({ - action: action, - facetAddress: facetAddress, - functionSelectors: selectors() + action: action, facetAddress: facetAddress, functionSelectors: selectors() }); } diff --git a/scripts/deployments/mocks/DeployMockERC6909.sol b/scripts/deployments/mocks/DeployMockERC6909.sol index 9450f14..da11057 100644 --- a/scripts/deployments/mocks/DeployMockERC6909.sol +++ b/scripts/deployments/mocks/DeployMockERC6909.sol @@ -53,9 +53,7 @@ library DeployMockERC6909 { returns (IDiamond.FacetCut memory) { return IDiamond.FacetCut({ - action: action, - facetAddress: facetAddress, - functionSelectors: selectors() + action: action, facetAddress: facetAddress, functionSelectors: selectors() }); } diff --git a/scripts/deployments/mocks/DeployMockERC721.sol b/scripts/deployments/mocks/DeployMockERC721.sol index 6586c61..c6e4626 100644 --- a/scripts/deployments/mocks/DeployMockERC721.sol +++ b/scripts/deployments/mocks/DeployMockERC721.sol @@ -43,9 +43,7 @@ library DeployMockERC721 { returns (IDiamond.FacetCut memory) { return IDiamond.FacetCut({ - action: action, - facetAddress: facetAddress, - functionSelectors: selectors() + action: action, facetAddress: facetAddress, functionSelectors: selectors() }); } diff --git a/scripts/deployments/utils/DeployProxyManager.sol b/scripts/deployments/utils/DeployProxyManager.sol index 6a1e7ac..44533da 100644 --- a/scripts/deployments/utils/DeployProxyManager.sol +++ b/scripts/deployments/utils/DeployProxyManager.sol @@ -26,9 +26,7 @@ library DeployProxyManager { returns (IDiamond.FacetCut memory) { return IDiamond.FacetCut({ - action: action, - facetAddress: facetAddress, - functionSelectors: selectors() + action: action, facetAddress: facetAddress, functionSelectors: selectors() }); } diff --git a/src/facets/accounts/ERC1271Base.sol b/src/facets/accounts/ERC1271Base.sol index 02a25be..ef0a6e0 100644 --- a/src/facets/accounts/ERC1271Base.sol +++ b/src/facets/accounts/ERC1271Base.sol @@ -226,7 +226,9 @@ abstract contract ERC1271Base is EIP712Base { let e := 0 // Length of `contentsName` in explicit mode. for { let q := sub(add(p, c), 1) } 1 {} { e := add(e, 1) // Scan backwards until we encounter a ')'. - if iszero(gt(lt(e, c), eq(byte(0, mload(sub(q, e))), 41))) { break } + if iszero(gt(lt(e, c), eq(byte(0, mload(sub(q, e))), 41))) { + break + } } c := sub(c, e) // Truncate `contentsDescription` to `contentsType`. calldatacopy(p, add(add(o, 0x40), c), e) // Copy `contentsName`. diff --git a/src/facets/loupe/DiamondLoupeBase.sol b/src/facets/loupe/DiamondLoupeBase.sol index a2671d5..ef54911 100644 --- a/src/facets/loupe/DiamondLoupeBase.sol +++ b/src/facets/loupe/DiamondLoupeBase.sol @@ -38,8 +38,7 @@ library DiamondLoupeBase { for (uint256 i; i < facetCount; ++i) { address _facetAddress = _facetAddresses[i]; _facets[i] = IDiamondLoupeBase.Facet({ - facet: _facetAddress, - selectors: facetSelectors(_facetAddress) + facet: _facetAddress, selectors: facetSelectors(_facetAddress) }); } } diff --git a/src/facets/token/ERC20/ERC20.sol b/src/facets/token/ERC20/ERC20.sol index 3f9aac6..5fb2f76 100644 --- a/src/facets/token/ERC20/ERC20.sol +++ b/src/facets/token/ERC20/ERC20.sol @@ -58,10 +58,7 @@ abstract contract ERC20 is IERC20, IERC20Metadata, Facet { } /// @inheritdoc IERC20 - function allowance( - address owner, - address spender - ) + function allowance(address owner, address spender) public view virtual diff --git a/src/facets/token/ERC721/ERC721.sol b/src/facets/token/ERC721/ERC721.sol index 1bf19e9..8bd52bb 100644 --- a/src/facets/token/ERC721/ERC721.sol +++ b/src/facets/token/ERC721/ERC721.sol @@ -89,10 +89,7 @@ abstract contract ERC721 is Facet { ERC721Storage.layout().inner.setApprovalForAll(operator, approved); } - function isApprovedForAll( - address owner, - address operator - ) + function isApprovedForAll(address owner, address operator) external view virtual diff --git a/src/primitive/ERC721.sol b/src/primitive/ERC721.sol index b4c194f..d0519e6 100644 --- a/src/primitive/ERC721.sol +++ b/src/primitive/ERC721.sol @@ -275,10 +275,8 @@ library ERC721Lib { returns (bool) { address owner = ownerOf(self, tokenId); - return ( - spender == owner || isApprovedForAll(self, owner, spender) - || getApproved(self, tokenId) == spender - ); + return (spender == owner || isApprovedForAll(self, owner, spender) + || getApproved(self, tokenId) == spender); } function requireMinted(MinimalERC721Storage storage self, uint256 tokenId) internal view { diff --git a/test/TestUtils.sol b/test/TestUtils.sol index 63f97b8..f988c8f 100644 --- a/test/TestUtils.sol +++ b/test/TestUtils.sol @@ -48,14 +48,7 @@ contract TestUtils is Context, Test { ); } - function getMappingValueSlot( - uint256 mappingSlot, - uint256 key - ) - internal - pure - returns (bytes32) - { + function getMappingValueSlot(uint256 mappingSlot, uint256 key) internal pure returns (bytes32) { return keccak256(abi.encode(key, mappingSlot)); } diff --git a/test/facets/signature/EIP712.t.sol b/test/facets/signature/EIP712.t.sol index afe4d64..76c668c 100644 --- a/test/facets/signature/EIP712.t.sol +++ b/test/facets/signature/EIP712.t.sol @@ -103,8 +103,7 @@ contract EIP712Test is TestUtils, EIP712Utils { string memory name, string memory version, uint256 chainId, - address verifyingContract, - , + address verifyingContract,, ) = eip712.eip712Domain(); assertEq(name, "MockEIP712"); diff --git a/test/mocks/MockFacet.sol b/test/mocks/MockFacet.sol index 09b0238..a96e8b1 100644 --- a/test/mocks/MockFacet.sol +++ b/test/mocks/MockFacet.sol @@ -24,7 +24,8 @@ interface IMockFacet { } library MockFacetStorage { - bytes32 internal constant MOCK_FACET_STORAGE_POSITION = keccak256("mock.facet.storage.position"); + bytes32 internal constant MOCK_FACET_STORAGE_POSITION = + keccak256("mock.facet.storage.position"); struct Layout { uint256 value; @@ -85,9 +86,7 @@ library DeployMockFacet { returns (IDiamond.FacetCut memory) { return IDiamond.FacetCut({ - action: action, - facetAddress: facetAddress, - functionSelectors: selectors() + action: action, facetAddress: facetAddress, functionSelectors: selectors() }); } diff --git a/test/proxy/ProxyManager.t.sol b/test/proxy/ProxyManager.t.sol index a3a83db..fb58190 100644 --- a/test/proxy/ProxyManager.t.sol +++ b/test/proxy/ProxyManager.t.sol @@ -207,9 +207,12 @@ contract ProxyManagerTest is TestUtils, IDiamondCutBase, IOwnableBase { extensions[0] = DeployMockFacet.makeCut(mockFacet, IDiamond.FacetCutAction.Add); vm.prank(deployer); - IDiamondCut(address(implementation)).diamondCut( - extensions, mockFacet, abi.encodeWithSelector(MockFacet.__MockFacet_init.selector, 42) - ); + IDiamondCut(address(implementation)) + .diamondCut( + extensions, + mockFacet, + abi.encodeWithSelector(MockFacet.__MockFacet_init.selector, 42) + ); vm.prank(instanceOwner); IMockFacet(address(instance)).upgrade(); diff --git a/test/utils/Brutalizer.sol b/test/utils/Brutalizer.sol index 1c6447a..969c5f8 100644 --- a/test/utils/Brutalizer.sol +++ b/test/utils/Brutalizer.sol @@ -825,7 +825,9 @@ contract Brutalizer { let remainder := and(length, 0x1f) if remainder { if shl(mul(8, remainder), lastWord) { notZeroRightPadded := 1 } } // Check if the memory allocated is sufficient. - if length { if gt(add(add(s, 0x20), length), mload(0x40)) { insufficientMalloc := 1 } } + if length { + if gt(add(add(s, 0x20), length), mload(0x40)) { insufficientMalloc := 1 } + } } if (notZeroRightPadded) revert("Not zero right padded!"); if (insufficientMalloc) revert("Insufficient memory allocation!"); diff --git a/test/utils/TestPlus.sol b/test/utils/TestPlus.sol index 46822b5..73fdb0f 100644 --- a/test/utils/TestPlus.sol +++ b/test/utils/TestPlus.sol @@ -572,14 +572,7 @@ contract TestPlus is Brutalizer { /// @dev Truncate the bytes to `n` bytes. /// Returns the result for function chaining. - function _truncateBytes( - bytes memory b, - uint256 n - ) - internal - pure - returns (bytes memory result) - { + function _truncateBytes(bytes memory b, uint256 n) internal pure returns (bytes memory result) { /// @solidity memory-safe-assembly assembly { if gt(mload(b), n) { mstore(b, n) } From 2d7c0218bc1c032f6f9a23309ed50d57d0422fe9 Mon Sep 17 00:00:00 2001 From: Shuhui Luo <107524008+shuhuiluo@users.noreply.github.com> Date: Fri, 2 Jan 2026 16:57:40 -0500 Subject: [PATCH 3/3] fix: block oversized contracts and simplify batch boundaries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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 --- scripts/common/DeployFacet.s.sol | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/scripts/common/DeployFacet.s.sol b/scripts/common/DeployFacet.s.sol index d51b103..23c7320 100644 --- a/scripts/common/DeployFacet.s.sol +++ b/scripts/common/DeployFacet.s.sol @@ -109,6 +109,10 @@ contract DeployFacet is DeployBase { // estimate gas cost for this deployment uint256 contractGas = estimateDeploymentGas(bytecode); + require( + BASE_TX_COST + contractGas <= PER_TRANSACTION_GAS_LIMIT, + string.concat("DeployFacet: contract ", name, " exceeds gas limit") + ); batchGasEstimate += contractGas; // compute predicted address @@ -286,17 +290,14 @@ contract DeployFacet is DeployBase { for (uint256 i; i < queueLength; ++i) { uint256 contractGas = deploymentQueue[i].gasEstimate; - // check if adding this contract would exceed limit + // start new batch if adding this contract would exceed limit if (currentBatchGas + contractGas > PER_TRANSACTION_GAS_LIMIT) { - // start new batch (unless this is the first contract in current batch) - if (currentBatchGas > BASE_TX_COST) { - arr.p(i); - currentBatchGas = BASE_TX_COST; - } + arr.p(i); + currentBatchGas = BASE_TX_COST; } currentBatchGas += contractGas; } - arr.p(queueLength); // final batch ends at queue length + arr.p(queueLength); batchEndIndices = arr.asUint256Array(); }