From 6b53a883fb275f42146c8a740dbca3cf95a1d72e Mon Sep 17 00:00:00 2001 From: excaliborr <124819095+excaliborr@users.noreply.github.com> Date: Fri, 21 Jun 2024 18:19:04 -0400 Subject: [PATCH] fix: deployment code size too large (#65) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # 🤖 Linear Closes OPT-XXX --- remappings.txt | 3 +- src/contracts/L1OpUSDCFactory.sol | 93 +++------------------- src/libraries/CrossChainDeployments.sol | 101 ++++++++++++++++++++++++ test/unit/L1OpUSDCFactory.t.sol | 6 +- 4 files changed, 116 insertions(+), 87 deletions(-) create mode 100644 src/libraries/CrossChainDeployments.sol diff --git a/remappings.txt b/remappings.txt index 16a867ef..047c2698 100644 --- a/remappings.txt +++ b/remappings.txt @@ -1,4 +1,5 @@ ds-test/=node_modules/ds-test/src forge-std/=node_modules/forge-std/src contracts/=src/contracts -interfaces/=src/interfaces \ No newline at end of file +interfaces/=src/interfaces +libraries/=src/libraries \ No newline at end of file diff --git a/src/contracts/L1OpUSDCFactory.sol b/src/contracts/L1OpUSDCFactory.sol index 23303977..6a9ab4ac 100644 --- a/src/contracts/L1OpUSDCFactory.sol +++ b/src/contracts/L1OpUSDCFactory.sol @@ -2,12 +2,11 @@ pragma solidity 0.8.25; import {L1OpUSDCBridgeAdapter} from 'contracts/L1OpUSDCBridgeAdapter.sol'; -import {L2OpUSDCFactory} from 'contracts/L2OpUSDCFactory.sol'; import {IL1OpUSDCFactory} from 'interfaces/IL1OpUSDCFactory.sol'; import {IL2OpUSDCFactory} from 'interfaces/IL2OpUSDCFactory.sol'; -import {ICreate2Deployer} from 'interfaces/external/ICreate2Deployer.sol'; -import {ICrossDomainMessenger} from 'interfaces/external/ICrossDomainMessenger.sol'; + import {IUSDC} from 'interfaces/external/IUSDC.sol'; +import {CrossChainDeployments} from 'libraries/CrossChainDeployments.sol'; /** * @title L1OpUSDCFactory @@ -78,7 +77,7 @@ contract L1OpUSDCFactory is IL1OpUSDCFactory { uint256 _currentNonce = ++deploymentsSaltCounter; // Precalculate the l1 adapter - _l1Adapter = _precalculateCreateAddress(address(this), _currentNonce); + _l1Adapter = CrossChainDeployments.precalculateCreateAddress(address(this), _currentNonce); // Get the L1 USDC naming and decimals to ensure they are the same on the L2, guaranteeing the same standard IL2OpUSDCFactory.USDCInitializeData memory _usdcInitializeData = @@ -94,91 +93,17 @@ contract L1OpUSDCFactory is IL1OpUSDCFactory { _usdcInitializeData, _l2Deployments.usdcInitTxs ); - bytes memory _l2FactoryInitCode = bytes.concat(type(L2OpUSDCFactory).creationCode, _l2FactoryCArgs); - _l2Factory = _precalculateCreate2Address(_salt, keccak256(_l2FactoryInitCode), L2_CREATE2_DEPLOYER); + + // Send the L2 factory deployment tx + _l2Factory = CrossChainDeployments.deployL2Factory( + _l2FactoryCArgs, _salt, _l1Messenger, L2_CREATE2_DEPLOYER, _l2Deployments.minGasLimitDeploy + ); // Precalculate the L2 adapter address - _l2Adapter = _precalculateCreateAddress(_l2Factory, _L2_ADAPTER_DEPLOYMENT_NONCE); + _l2Adapter = CrossChainDeployments.precalculateCreateAddress(_l2Factory, _L2_ADAPTER_DEPLOYMENT_NONCE); // Deploy the L1 adapter address(new L1OpUSDCBridgeAdapter(address(USDC), _l1Messenger, _l2Adapter, _l1AdapterOwner)); - // Send the L2 factory deployment tx - bytes memory _l2FactoryDeploymentsTx = - abi.encodeWithSelector(ICreate2Deployer.deploy.selector, 0, _salt, _l2FactoryInitCode); - ICrossDomainMessenger(_l1Messenger).sendMessage( - L2_CREATE2_DEPLOYER, _l2FactoryDeploymentsTx, _l2Deployments.minGasLimitDeploy - ); - emit L1AdapterDeployed(_l1Adapter); } - - /** - * @notice Precalculates the address of a contract that will be deployed thorugh `CREATE` opcode - * @param _deployer The deployer address - * @param _nonce The next nonce of the deployer address - * @return _precalculatedAddress The address where the contract will be stored - * @dev Only works for nonces between 1 and (2 ** 64 - 2), which is enough for this use case - */ - function _precalculateCreateAddress( - address _deployer, - uint256 _nonce - ) internal pure returns (address _precalculatedAddress) { - bytes memory _data; - bytes1 _len = bytes1(0x94); - - // A one-byte integer in the [0x00, 0x7f] range uses its own value as a length prefix, there is no - // additional "0x80 + length" prefix that precedes it. - // A one-byte integer in the [0x00, 0x7f] range uses its own value as a length prefix, there is no - // additional "0x80 + length" prefix that precedes it. - if (_nonce <= 0x7f) { - _data = abi.encodePacked(bytes1(0xd6), _len, _deployer, uint8(_nonce)); - } - // In the case of `_nonce > 0x7f` and `_nonce <= type(uint8).max`, we have the following encoding scheme - // (the same calculation can be carried over for higher _nonce bytes): - // 0xda = 0xc0 (short RLP prefix) + 0x1a (= the bytes length of: 0x94 + address + 0x84 + _nonce, in hex), - // 0x94 = 0x80 + 0x14 (= the bytes length of an address, 20 bytes, in hex), - // 0x84 = 0x80 + 0x04 (= the bytes length of the _nonce, 4 bytes, in hex). - else if (_nonce <= type(uint8).max) { - _data = abi.encodePacked(bytes1(0xd7), _len, _deployer, bytes1(0x81), uint8(_nonce)); - } else if (_nonce <= type(uint16).max) { - _data = abi.encodePacked(bytes1(0xd8), _len, _deployer, bytes1(0x82), uint16(_nonce)); - } else if (_nonce <= type(uint24).max) { - _data = abi.encodePacked(bytes1(0xd9), _len, _deployer, bytes1(0x83), uint24(_nonce)); - } else if (_nonce <= type(uint32).max) { - _data = abi.encodePacked(bytes1(0xda), _len, _deployer, bytes1(0x84), uint32(_nonce)); - } else if (_nonce <= type(uint40).max) { - _data = abi.encodePacked(bytes1(0xdb), _len, _deployer, bytes1(0x85), uint40(_nonce)); - } else if (_nonce <= type(uint48).max) { - _data = abi.encodePacked(bytes1(0xdc), _len, _deployer, bytes1(0x86), uint48(_nonce)); - } else if (_nonce <= type(uint56).max) { - _data = abi.encodePacked(bytes1(0xdd), _len, _deployer, bytes1(0x87), uint56(_nonce)); - } else { - _data = abi.encodePacked(bytes1(0xde), _len, _deployer, bytes1(0x88), uint64(_nonce)); - } - - _precalculatedAddress = address(uint160(uint256(keccak256(_data)))); - } - - /** - * @notice Precalculate and address to be deployed using the `CREATE2` opcode - * @param _salt The 32-byte random value used to create the contract address. - * @param _initCodeHash The 32-byte bytecode digest of the contract creation bytecode. - * @param _deployer The 20-byte _deployer address. - * @return _precalculatedAddress The 20-byte address where a contract will be stored. - */ - function _precalculateCreate2Address( - bytes32 _salt, - bytes32 _initCodeHash, - address _deployer - ) internal pure returns (address _precalculatedAddress) { - assembly ("memory-safe") { - let _ptr := mload(0x40) - mstore(add(_ptr, 0x40), _initCodeHash) - mstore(add(_ptr, 0x20), _salt) - mstore(_ptr, _deployer) - let _start := add(_ptr, 0x0b) - mstore8(_start, 0xff) - _precalculatedAddress := keccak256(_start, 85) - } - } } diff --git a/src/libraries/CrossChainDeployments.sol b/src/libraries/CrossChainDeployments.sol new file mode 100644 index 00000000..6220ffa3 --- /dev/null +++ b/src/libraries/CrossChainDeployments.sol @@ -0,0 +1,101 @@ +pragma solidity 0.8.25; + +import {L2OpUSDCFactory} from 'contracts/L2OpUSDCFactory.sol'; +import {ICreate2Deployer} from 'interfaces/external/ICreate2Deployer.sol'; +import {ICrossDomainMessenger} from 'interfaces/external/ICrossDomainMessenger.sol'; + +library CrossChainDeployments { + /** + * @notice Deploys the L2 factory contract through the L1 messenger + * @param _args The initialization arguments for the L2 factory + * @param _salt The salt to be used to deploy the L2 factory + * @param _messenger The address of the L1 messenger + * @param _create2Deployer The address of the L2 create2 deployer + * @param _minGasLimit The minimum gas limit that the message can be executed with + * @return _l2Factory The address of the L2 factory + */ + function deployL2Factory( + bytes memory _args, + bytes32 _salt, + address _messenger, + address _create2Deployer, + uint32 _minGasLimit + ) external returns (address _l2Factory) { + bytes memory _l2FactoryInitCode = bytes.concat(type(L2OpUSDCFactory).creationCode, _args); + _l2Factory = precalculateCreate2Address(_salt, keccak256(_l2FactoryInitCode), _create2Deployer); + + bytes memory _l2FactoryDeploymentsTx = + abi.encodeWithSelector(ICreate2Deployer.deploy.selector, 0, _salt, _l2FactoryInitCode); + ICrossDomainMessenger(_messenger).sendMessage(_create2Deployer, _l2FactoryDeploymentsTx, _minGasLimit); + } + + /** + * @notice Precalculate and address to be deployed using the `CREATE2` opcode + * @param _salt The 32-byte random value used to create the contract address. + * @param _initCodeHash The 32-byte bytecode digest of the contract creation bytecode. + * @param _deployer The 20-byte _deployer address. + * @return _precalculatedAddress The 20-byte address where a contract will be stored. + */ + function precalculateCreate2Address( + bytes32 _salt, + bytes32 _initCodeHash, + address _deployer + ) internal pure returns (address _precalculatedAddress) { + assembly ("memory-safe") { + let _ptr := mload(0x40) + mstore(add(_ptr, 0x40), _initCodeHash) + mstore(add(_ptr, 0x20), _salt) + mstore(_ptr, _deployer) + let _start := add(_ptr, 0x0b) + mstore8(_start, 0xff) + _precalculatedAddress := keccak256(_start, 85) + } + } + + /** + * @notice Precalculates the address of a contract that will be deployed thorugh `CREATE` opcode + * @param _deployer The deployer address + * @param _nonce The next nonce of the deployer address + * @return _precalculatedAddress The address where the contract will be stored + * @dev Only works for nonces between 1 and (2 ** 64 - 2), which is enough for this use case + */ + function precalculateCreateAddress( + address _deployer, + uint256 _nonce + ) internal pure returns (address _precalculatedAddress) { + bytes memory _data; + bytes1 _len = bytes1(0x94); + + // A one-byte integer in the [0x00, 0x7f] range uses its own value as a length prefix, there is no + // additional "0x80 + length" prefix that precedes it. + // A one-byte integer in the [0x00, 0x7f] range uses its own value as a length prefix, there is no + // additional "0x80 + length" prefix that precedes it. + if (_nonce <= 0x7f) { + _data = abi.encodePacked(bytes1(0xd6), _len, _deployer, uint8(_nonce)); + } + // In the case of `_nonce > 0x7f` and `_nonce <= type(uint8).max`, we have the following encoding scheme + // (the same calculation can be carried over for higher _nonce bytes): + // 0xda = 0xc0 (short RLP prefix) + 0x1a (= the bytes length of: 0x94 + address + 0x84 + _nonce, in hex), + // 0x94 = 0x80 + 0x14 (= the bytes length of an address, 20 bytes, in hex), + // 0x84 = 0x80 + 0x04 (= the bytes length of the _nonce, 4 bytes, in hex). + else if (_nonce <= type(uint8).max) { + _data = abi.encodePacked(bytes1(0xd7), _len, _deployer, bytes1(0x81), uint8(_nonce)); + } else if (_nonce <= type(uint16).max) { + _data = abi.encodePacked(bytes1(0xd8), _len, _deployer, bytes1(0x82), uint16(_nonce)); + } else if (_nonce <= type(uint24).max) { + _data = abi.encodePacked(bytes1(0xd9), _len, _deployer, bytes1(0x83), uint24(_nonce)); + } else if (_nonce <= type(uint32).max) { + _data = abi.encodePacked(bytes1(0xda), _len, _deployer, bytes1(0x84), uint32(_nonce)); + } else if (_nonce <= type(uint40).max) { + _data = abi.encodePacked(bytes1(0xdb), _len, _deployer, bytes1(0x85), uint40(_nonce)); + } else if (_nonce <= type(uint48).max) { + _data = abi.encodePacked(bytes1(0xdc), _len, _deployer, bytes1(0x86), uint48(_nonce)); + } else if (_nonce <= type(uint56).max) { + _data = abi.encodePacked(bytes1(0xdd), _len, _deployer, bytes1(0x87), uint56(_nonce)); + } else { + _data = abi.encodePacked(bytes1(0xde), _len, _deployer, bytes1(0x88), uint64(_nonce)); + } + + _precalculatedAddress = address(uint160(uint256(keccak256(_data)))); + } +} diff --git a/test/unit/L1OpUSDCFactory.t.sol b/test/unit/L1OpUSDCFactory.t.sol index 5524d9e3..4bae001b 100644 --- a/test/unit/L1OpUSDCFactory.t.sol +++ b/test/unit/L1OpUSDCFactory.t.sol @@ -10,6 +10,8 @@ import {IL1OpUSDCFactory} from 'interfaces/IL1OpUSDCFactory.sol'; import {ICreate2Deployer} from 'interfaces/external/ICreate2Deployer.sol'; import {ICrossDomainMessenger} from 'interfaces/external/ICrossDomainMessenger.sol'; import {IUSDC} from 'interfaces/external/IUSDC.sol'; + +import {CrossChainDeployments} from 'libraries/CrossChainDeployments.sol'; import {Helpers} from 'test/utils/Helpers.sol'; contract ForTestL1OpUSDCFactory is L1OpUSDCFactory { @@ -19,7 +21,7 @@ contract ForTestL1OpUSDCFactory is L1OpUSDCFactory { address _deployer, uint256 _nonce ) public pure returns (address _precalculatedAddress) { - _precalculatedAddress = _precalculateCreateAddress(_deployer, _nonce); + _precalculatedAddress = CrossChainDeployments.precalculateCreateAddress(_deployer, _nonce); } function forTest_precalculateCreate2Address( @@ -27,7 +29,7 @@ contract ForTestL1OpUSDCFactory is L1OpUSDCFactory { bytes32 _initCodeHash, address _deployer ) public pure returns (address _precalculatedAddress) { - _precalculatedAddress = _precalculateCreate2Address(_salt, _initCodeHash, _deployer); + _precalculatedAddress = CrossChainDeployments.precalculateCreate2Address(_salt, _initCodeHash, _deployer); } }