-
Notifications
You must be signed in to change notification settings - Fork 32
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(Express): new ExpressProxyDeployer, Create3Deployer and FinalPro…
…xy (#20) * feat(Express): ExpressProxyDeployer, Create3Deployer and FinalProxy * feat(FinalProxy): isFinal * feat(ExpressProxy): excluding registry bytecode from the proxy * revert(ConstAddressDeployer): keeping the legacy deployer * feature(Proxy): proxy of every kind * feature(ExpressExecutable): enableExpressCallWithToken to opt out * refactor(FinalProxy): calling super method * chore(README): copy update
- Loading branch information
Showing
40 changed files
with
988 additions
and
326 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
// SPDX-License-Identifier: MIT | ||
|
||
pragma solidity ^0.8.0; | ||
|
||
error AlreadyDeployed(); | ||
error EmptyBytecode(); | ||
error DeployFailed(); | ||
|
||
contract CreateDeployer { | ||
function deploy(bytes memory bytecode) external { | ||
// solhint-disable-next-line no-inline-assembly | ||
assembly { | ||
pop(create(0, add(bytecode, 32), mload(bytecode))) | ||
} | ||
|
||
selfdestruct(payable(msg.sender)); | ||
} | ||
} | ||
|
||
library Create3 { | ||
bytes32 internal constant DEPLOYER_BYTECODE_HASH = keccak256(type(CreateDeployer).creationCode); | ||
|
||
function deploy(bytes32 salt, bytes memory bytecode) internal returns (address deployed) { | ||
deployed = deployedAddress(salt, address(this)); | ||
|
||
if (deployed.code.length != 0) revert AlreadyDeployed(); | ||
if (bytecode.length == 0) revert EmptyBytecode(); | ||
|
||
// CREATE2 | ||
CreateDeployer deployer = new CreateDeployer{ salt: salt }(); | ||
|
||
if (address(deployer) == address(0)) revert DeployFailed(); | ||
|
||
deployer.deploy(bytecode); | ||
|
||
if (deployed.code.length == 0) revert DeployFailed(); | ||
} | ||
|
||
function deployedAddress(bytes32 salt, address host) internal pure returns (address deployed) { | ||
address deployer = address( | ||
uint160(uint256(keccak256(abi.encodePacked(hex'ff', host, salt, DEPLOYER_BYTECODE_HASH)))) | ||
); | ||
|
||
deployed = address(uint160(uint256(keccak256(abi.encodePacked(hex'd6_94', deployer, hex'01'))))); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
// SPDX-License-Identifier: MIT | ||
|
||
pragma solidity ^0.8.0; | ||
|
||
import { Create3 } from './Create3.sol'; | ||
|
||
contract Create3Deployer { | ||
error FailedInit(); | ||
|
||
event Deployed(bytes32 indexed bytecodeHash, bytes32 indexed salt, address indexed deployedAddress); | ||
|
||
/** | ||
* @dev Deploys a contract using `CREATE3`. 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 calldata bytecode, bytes32 salt) external returns (address deployedAddress_) { | ||
bytes32 deploySalt = keccak256(abi.encode(msg.sender, salt)); | ||
deployedAddress_ = Create3.deploy(deploySalt, bytecode); | ||
|
||
emit Deployed(keccak256(bytecode), salt, deployedAddress_); | ||
} | ||
|
||
/** | ||
* @dev Deploys a contract using `CREATE3` 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 | ||
*/ | ||
function deployAndInit( | ||
bytes memory bytecode, | ||
bytes32 salt, | ||
bytes calldata init | ||
) external returns (address deployedAddress_) { | ||
bytes32 deploySalt = keccak256(abi.encode(msg.sender, salt)); | ||
deployedAddress_ = Create3.deploy(deploySalt, bytecode); | ||
|
||
// 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(address sender, bytes32 salt) external view returns (address) { | ||
bytes32 deploySalt = keccak256(abi.encode(sender, salt)); | ||
return Create3.deployedAddress(deploySalt, address(this)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
// SPDX-License-Identifier: MIT | ||
|
||
pragma solidity ^0.8.0; | ||
|
||
import { IExpressProxyDeployer } from '../interfaces/IExpressProxyDeployer.sol'; | ||
import { IExpressProxy } from '../interfaces/IExpressProxy.sol'; | ||
import { Create3 } from '../deploy/Create3.sol'; | ||
import { ExpressProxy } from './ExpressProxy.sol'; | ||
import { ExpressRegistry } from './ExpressRegistry.sol'; | ||
|
||
contract ExpressProxyDeployer is IExpressProxyDeployer { | ||
address public immutable gateway; | ||
bytes32 public immutable proxyCodeHash; | ||
bytes32 public immutable registryCodeHash; | ||
|
||
constructor(address gateway_) { | ||
if (gateway_ == address(0)) revert InvalidAddress(); | ||
|
||
gateway = gateway_; | ||
|
||
ExpressProxy proxy = new ExpressProxy(address(this), address(this), '', gateway_); | ||
proxy.deployRegistry(type(ExpressRegistry).creationCode); | ||
|
||
proxyCodeHash = address(proxy).codehash; | ||
registryCodeHash = address(proxy.registry()).codehash; | ||
} | ||
|
||
function isExpressProxy(address proxyAddress) external view returns (bool) { | ||
address expressRegistry = address(IExpressProxy(proxyAddress).registry()); | ||
|
||
return proxyAddress.codehash == proxyCodeHash && expressRegistry.codehash == registryCodeHash; | ||
} | ||
|
||
/// @param host is delegating call to this contract | ||
function deployedProxyAddress( | ||
bytes32 salt, | ||
address sender, | ||
address host | ||
) external pure returns (address) { | ||
bytes32 deploySalt = keccak256(abi.encode(sender, salt)); | ||
return Create3.deployedAddress(deploySalt, host); | ||
} | ||
|
||
/// @dev delegatecall to this function to deploy a proxy from a host contract | ||
function deployExpressProxy( | ||
bytes32 deploySalt, | ||
address implementationAddress, | ||
address owner, | ||
bytes memory setupParams | ||
) external returns (address proxyAddress) { | ||
proxyAddress = Create3.deploy( | ||
deploySalt, | ||
abi.encodePacked( | ||
type(ExpressProxy).creationCode, | ||
abi.encode(gateway, implementationAddress, owner, setupParams) | ||
) | ||
); | ||
|
||
ExpressProxy proxy = ExpressProxy(payable(proxyAddress)); | ||
proxy.deployRegistry(type(ExpressRegistry).creationCode); | ||
} | ||
} |
Oops, something went wrong.