The home for Hyperlane core contracts, sdk packages, and other infrastructure
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
hyperlane-monorepo/solidity/contracts/Create2Factory.sol

115 lines
3.6 KiB

// SPDX-License-Identifier: MIT
// Copied from https://github.com/axelarnetwork/axelar-utils-solidity/commits/main/contracts/ConstAddressDeployer.sol
pragma solidity ^0.8.0;
contract Create2Factory {
error EmptyBytecode();
error FailedDeploy();
error FailedInit();
event Deployed(
bytes32 indexed bytecodeHash,
bytes32 indexed salt,
address indexed deployedAddress
);
/**
* @dev Deploys a contract using `CREATE2`. The address where the contract
* will be deployed can be known in advance via {deployedAddress}.
*
* The bytecode for a contract can be obtained from Solidity with
* `type(contractName).creationCode`.
*
* Requirements:
*
* - `bytecode` must not be empty.
* - `salt` must have not been used for `bytecode` already by the same `msg.sender`.
*/
function deploy(bytes memory bytecode, bytes32 salt)
external
returns (address deployedAddress_)
{
deployedAddress_ = _deploy(
bytecode,
keccak256(abi.encode(msg.sender, salt))
);
}
/**
* @dev Deploys a contract using `CREATE2` and initialize it. The address where the contract
* will be deployed can be known in advance via {deployedAddress}.
*
* The bytecode for a contract can be obtained from Solidity with
* `type(contractName).creationCode`.
*
* Requirements:
*
* - `bytecode` must not be empty.
* - `salt` must have not been used for `bytecode` already by the same `msg.sender`.
* - `init` is used to initialize the deployed contract
* as an option to not have the constructor args affect the address derived by `CREATE2`.
*/
function deployAndInit(
bytes memory bytecode,
bytes32 salt,
bytes calldata init
) external returns (address deployedAddress_) {
deployedAddress_ = _deploy(
bytecode,
keccak256(abi.encode(msg.sender, salt))
);
// solhint-disable-next-line avoid-low-level-calls
(bool success, ) = deployedAddress_.call(init);
if (!success) revert FailedInit();
}
/**
* @dev Returns the address where a contract will be stored if deployed via {deploy} or {deployAndInit} by `sender`.
* Any change in the `bytecode`, `sender`, or `salt` will result in a new destination address.
*/
function deployedAddress(
bytes calldata bytecode,
address sender,
bytes32 salt
) external view returns (address deployedAddress_) {
bytes32 newSalt = keccak256(abi.encode(sender, salt));
deployedAddress_ = address(
uint160(
uint256(
keccak256(
abi.encodePacked(
hex"ff",
address(this),
newSalt,
keccak256(bytecode) // init code hash
)
)
)
)
);
}
function _deploy(bytes memory bytecode, bytes32 salt)
internal
returns (address deployedAddress_)
{
if (bytecode.length == 0) revert EmptyBytecode();
// solhint-disable-next-line no-inline-assembly
assembly {
deployedAddress_ := create2(
0,
add(bytecode, 32),
mload(bytecode),
salt
)
}
if (deployedAddress_ == address(0)) revert FailedDeploy();
emit Deployed(keccak256(bytecode), salt, deployedAddress_);
}
}