// SPDX-License-Identifier: MIT pragma solidity >=0.6.12; pragma experimental ABIEncoderV2; import {Logger} from "../base/Logger.sol"; import {StringUtils} from "../libs/StringUtils.sol"; import {ChainAwareReader} from "../reader/ChainAwareReader.sol"; abstract contract Deployer is ChainAwareReader, Logger { string internal constant CREATE2_FACTORY_NAME = "Create2Factory"; /// @dev Deployed on lots of chains, could be deployed on more chains using EOA address private constant DEFAULT_CREATE2_FACTORY = 0xa6190aBC82427800935E0598892f7488a7F73A04; address private envCreate2Factory; /// @dev Salt to be used for the next create2 deployment, will be erased after the deployment bytes32 private nextDeploymentSalt; /// @dev Salt to be used for the next create2 deployment, will be kept after the deployment bytes32 private permanentDeploymentSalt; function loadEnvCreate2Factory() internal { // Try loading the factory address from env. Use the default factory as a fallback envCreate2Factory = vm.envOr("CREATE2_FACTORY", DEFAULT_CREATE2_FACTORY); } // ═══════════════════════════════════════════ CUSTOMIZABLE GETTERS ════════════════════════════════════════════════ /// @notice Returns the address of the create2 factory to be used for the create2 deployments. /// @dev There are a few ways to set the factory address. Priority in descending order: /// 1. Deployment artifact for the factory saved in deployments/chainName /// 2. CREATE2_FACTORY env variable /// 3. DEFAULT_CREATE2_FACTORY function getCreate2Factory() internal view virtual returns (address) { // First, check if we have a deployment artifact for the factory saved address factory = getDeploymentAddress({contractName: CREATE2_FACTORY_NAME, revertIfNotFound: false}); // If it is not saved we fallback to the env variable (which falls back to the default factory) return factory != address(0) ? factory : envCreate2Factory; } function getDeploymentSalt() internal view virtual returns (bytes32) { // Use single-use salt by default, fallback to permanent salt return nextDeploymentSalt != 0 ? nextDeploymentSalt : permanentDeploymentSalt; } function getInitCode( string memory contractName, bytes memory constructorArgs ) internal view virtual returns (bytes memory) { bytes memory bytecode = getContractBytecode({contractName: contractName, revertIfNotFound: true}); // Init code is the contract bytecode with constructor args appended return abi.encodePacked(bytecode, constructorArgs); } // ═════════════════════════════════════════ DEPLOY CALLBACKS: CREATE ══════════════════════════════════════════════ /// @notice Creates a contract with the given name and constructor args. /// Bytecode generated by forge is used for the deployment. /// Note: could be used as callback for `deployAndSave` and `deployAndSaveAs` /// for contracts with a different compiler version. function cbDeploy(string memory contractName, bytes memory constructorArgs) internal returns (address deployedAt) { bytes memory initCode = getInitCode(contractName, constructorArgs); return deployCode(initCode); } /// @notice Creates a contract with the given init code. /// @dev Does not save the deployment JSON, and therefore the direct usage should be avoided. /// Use as a part of a deploy callback for `deployAndSave` and `deployAndSaveAs` instead. function deployCode(bytes memory initCode) internal returns (address deployedAt) { // Use assembly to deploy the contract // solhint-disable-next-line no-inline-assembly assembly { // Add 0x20 to skip the length field of initCode deployedAt := create(0, add(initCode, 0x20), mload(initCode)) } require(deployedAt != address(0), "Deployer: failed to deploy using CREATE"); } // ═════════════════════════════════════════ DEPLOY CALLBACKS: CREATE2 ═════════════════════════════════════════════ /// @notice Creates a contract with the given name and constructor args. /// Bytecode generated by forge is used for the deployment. /// Deployment is done in a deterministic way, using create2 and nextDeploymentSalt() /// Note: could be used as callback for `deployAndSave` and `deployAndSaveAs` function cbDeployCreate2( string memory contractName, bytes memory constructorArgs ) internal returns (address deployedAt) { bytes memory initCode = getInitCode(contractName, constructorArgs); bytes32 salt = getDeploymentSalt(); // Print init code hash for potential vanity address mining printLogWithIndent(StringUtils.concat("Init code hash: ", vm.toString(keccak256(initCode)))); printLogWithIndent(StringUtils.concat("Using salt: ", vm.toString(salt))); deployedAt = factoryDeployCreate2(getCreate2Factory(), initCode, getDeploymentSalt()); // Erase single-use salt delete nextDeploymentSalt; } /// @notice Creates a contract with the given init code. /// Deployment is done in a deterministic way, using create2 and the given salt. /// @dev Does not save the deployment JSON, and therefore the direct usage should be avoided. /// Use deployCreate2 as the deploy callback for `deployAndSave` and `deployAndSaveAs` instead. function factoryDeployCreate2( address factory, bytes memory initCode, bytes32 salt ) internal returns (address deployedAt) { // Check that the factory exists assertContractCodeExists({contractAddr: factory, errMsg: "Deployer: factory contract not found"}); address predicted = predictAddress(factory, initCode, salt); // Check that the contract is not already deployed assertContractCodeEmpty({contractAddr: predicted, errMsg: "Deployer: contract already deployed"}); // Construct payload for factory deployment: salt + init code bytes memory payload = abi.encodePacked(salt, initCode); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returnData) = factory.call(payload); require(success, StringUtils.concat("Deployer: failed to deploy using factory at ", vm.toString(factory))); // returnData is 20 bytes of address rather than ABI-encoded address require( returnData.length == 20, StringUtils.concat( "Deployer: invalid return data from factory at ", vm.toString(factory), ": ", vm.toString(returnData) ) ); returnData = abi.encodePacked(bytes12(0), returnData); deployedAt = abi.decode(returnData, (address)); require( deployedAt == predicted, StringUtils.concat( "Deployer: invalid deployment address returned by factory, expected: ", vm.toString(predicted), ", actual: ", vm.toString(deployedAt) ) ); } // ═══════════════════════════════════════════ CREATE2 DEPLOY UTILS ════════════════════════════════════════════════ /// @notice Set a deployment salt that will be used for a single next create2 deployment. function setNextDeploymentSalt(bytes32 salt) internal { nextDeploymentSalt = salt; } /// @notice Set a deployment salt that will be used for all create2 deployments. function setPermanentDeploymentSalt(bytes32 salt) internal { permanentDeploymentSalt = salt; } /// @notice Predicts the address of a contract that would be deployed with the given init code and salt, /// using the default create2 factory. function predictAddress(bytes memory initCode, bytes32 salt) internal view returns (address deployedAt) { return predictAddress(getCreate2Factory(), initCode, salt); } /// @notice Predicts the address of a contract that would be deployed with the given init code and salt, /// using the given create2 factory. function predictAddress( address factory, bytes memory initCode, bytes32 salt ) internal pure returns (address deployedAt) { return address(uint160(uint256(keccak256(abi.encodePacked(bytes1(0xff), factory, salt, keccak256(initCode)))))); } }