From d49c919c3b3867fa77ea76160d4327f127bb5b44 Mon Sep 17 00:00:00 2001 From: Ryan Date: Thu, 11 Sep 2025 23:54:56 -0700 Subject: [PATCH 1/2] Removed .DS_Store files --- .DS_Store | Bin 6148 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 .DS_Store diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index 5172429f264de2441865cb4700216d4256da9242..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeH~J!%6%427R!7lt%jx}3%b$PET#pTHLgIFQEJ;E>dF^gR7ES*H$5cmnB-G%I%Z zD|S`@Z2$T80!#olbXV*=%*>dt@PRwdU#I)^a=X5>;#J@&VrHyNnC;iLL0pQvfVyTmjO&;ssLc!1UOG})p;=82 zR;?Ceh}WZ?+UmMqI#RP8R>OzYoz15hnq@nzF`-!xQ4j$Um=RcIKKc27r2jVm&svm< zfC&6E0=7P!4tu^-ovjbA=k?dB`g+i*aXG_}p8zI)6mRKa+;6_1_R^8c3Qa!(fk8n8 H{*=HsM+*^= From a971fac6471c1e4e24d06036aa13e08ec1e62c14 Mon Sep 17 00:00:00 2001 From: Ryan Date: Sun, 28 Sep 2025 13:42:11 -0700 Subject: [PATCH 2/2] Updated foundry formatter configuration --- .github/workflows/test.yml | 5 + .gitignore | 1 + .prettierrc.json | 9 +- foundry.toml | 8 + script/BaseScript.sol | 224 +++++------ script/CreateX.s.sol | 263 ++++++------ script/Deploy.s.sol | 28 +- script/Precompiles.sol | 21 +- src/CreateX.sol | 782 ++++++++++++++++++------------------ src/CreateXFactory.sol | 184 ++++----- src/ICreateXFactory.sol | 200 +++++----- test/CreateXFactory.t.sol | 798 ++++++++++++++++++------------------- test/mocks/MockERC20.sol | 262 ++++++------ test/mocks/MockTarget.sol | 16 +- 14 files changed, 1393 insertions(+), 1408 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e22eb0d..3f17590 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -24,6 +24,11 @@ jobs: run: | forge --version + - name: Run Forge fmt + run: | + forge fmt --check + id: fmt + - name: Run Forge build run: | forge build --sizes diff --git a/.gitignore b/.gitignore index f0e124c..7580575 100644 --- a/.gitignore +++ b/.gitignore @@ -13,4 +13,5 @@ docs/ # Dotenv file .env +# OS files .DS_Store \ No newline at end of file diff --git a/.prettierrc.json b/.prettierrc.json index e6af006..68c2c17 100644 --- a/.prettierrc.json +++ b/.prettierrc.json @@ -1,19 +1,22 @@ { "printWidth": 120, "tabWidth": 4, - "useTabs": true, "singleQuote": false, "trailingComma": "all", "overrides": [ { "files": "*.sol", "options": { + "useTabs": false, "bracketSpacing": false } }, { - "files": ["*.ts", "*.js", "*.json"], - "options": {} + "files": "*.json", + "options": { + "useTabs": true, + "bracketSpacing": true + } } ] } diff --git a/foundry.toml b/foundry.toml index 63c96dd..fe52856 100644 --- a/foundry.toml +++ b/foundry.toml @@ -19,6 +19,14 @@ fs_permissions = [ gas_reports = ["CreateXFactory"] +[fmt] +line_length = 120 +tab_width = 4 +quote_style = "double" +func_attrs_with_params_multiline = true +inline_attribute_style = "compact" +return_statement = "inline" + [fuzz] runs = 5000 max_test_rejects = 1000000 diff --git a/script/BaseScript.sol b/script/BaseScript.sol index 383e415..67215bd 100644 --- a/script/BaseScript.sol +++ b/script/BaseScript.sol @@ -6,116 +6,116 @@ import {ICreateXFactory} from "src/ICreateXFactory.sol"; import {VmSafe} from "forge-std/Vm.sol"; abstract contract BaseScript is Script { - using stdJson for string; - - string private constant DEFAULT_MNEMONIC = "test test test test test test test test test test test junk"; - - string private constant DEFAULT_CHAINS = "ethereum, optimism, polygon, base, arbitrum"; - - address internal broadcaster; - - modifier broadcast() { - vm.startBroadcast(broadcaster); - _; - vm.stopBroadcast(); - } - - modifier fork(string memory chainAlias) { - vm.createSelectFork(chainAlias); - _; - } - - function setUp() public virtual { - broadcaster = vm.rememberKey(configurePrivateKey()); - } - - function configurePrivateKey() internal view virtual returns (uint256 privateKey) { - privateKey = vm.envOr({ - name: "PRIVATE_KEY", - defaultValue: vm.deriveKey({ - mnemonic: vm.envOr({name: "MNEMONIC", defaultValue: DEFAULT_MNEMONIC}), - index: uint8(vm.envOr({name: "EOA_INDEX", defaultValue: uint256(0)})) - }) - }); - } - - function generateJson(string memory path, string memory name, address instance, bytes32 salt) internal virtual { - string memory json = "json"; - json.serialize("address", instance); - json.serialize("blockNumber", vm.getBlockNumber()); - json.serialize("timestamp", vm.getBlockTimestamp()); - json.serialize("salt", salt); - json = json.serialize("name", name); - json.write(path); - } - - function promptChains() internal virtual returns (string[] memory chainAliases) { - string memory input = prompt("Chains separated by ','", defaultChains()); - return vm.split(vm.replace(input, " ", ""), ","); - } - - function prompt(string memory promptText) internal returns (string memory input) { - return prompt(promptText, new string(0)); - } - - function prompt(string memory promptText, string memory defaultValue) internal returns (string memory input) { - input = vm.prompt(string.concat(promptText, " (default: `", defaultValue, "`)")); - if (bytes(input).length == 0) input = defaultValue; - } - - function promptAddress(string memory promptText, address defaultValue) internal returns (address) { - return vm.parseAddress(prompt(promptText, vm.toString(defaultValue))); - } - - function promptAddress(string memory promptText) internal returns (address) { - return promptAddress(promptText, address(0)); - } - - function promptBool(string memory promptText, bool defaultValue) internal returns (bool) { - return vm.parseBool(prompt(promptText, vm.toString(defaultValue))); - } - - function promptBool(string memory promptText) internal returns (bool) { - return promptBool(promptText, false); - } - - function promptUint256(string memory promptText, uint256 defaultValue) internal returns (uint256) { - return vm.parseUint(prompt(promptText, vm.toString(defaultValue))); - } - - function promptUint256(string memory promptText) internal returns (uint256) { - return promptUint256(promptText, uint256(0)); - } - - function promptInt256(string memory promptText, int256 defaultValue) internal returns (int256) { - return vm.parseInt(prompt(promptText, vm.toString(defaultValue))); - } - - function promptInt256(string memory promptText) internal returns (int256) { - return promptInt256(promptText, int256(0)); - } - - function promptBytes32(string memory promptText, bytes32 defaultValue) internal returns (bytes32) { - return vm.parseBytes32(prompt(promptText, vm.toString(defaultValue))); - } - - function promptBytes32(string memory promptText) internal returns (bytes32) { - return promptBytes32(promptText, bytes32(0)); - } - - function promptBytes(string memory promptText, bytes memory defaultValue) internal returns (bytes memory) { - return vm.parseBytes(prompt(promptText, vm.toString(defaultValue))); - } - - function promptBytes(string memory promptText) internal returns (bytes memory) { - return promptBytes(promptText, new bytes(0)); - } - - function defaultChains() internal view virtual returns (string memory chains) { - return DEFAULT_CHAINS; - } - - function defaultSalt() internal view virtual returns (bytes32) { - return bytes32(0); - } + using stdJson for string; + + string private constant DEFAULT_MNEMONIC = "test test test test test test test test test test test junk"; + + string private constant DEFAULT_CHAINS = "ethereum, optimism, polygon, base, arbitrum"; + + address internal broadcaster; + + modifier broadcast() { + vm.startBroadcast(broadcaster); + _; + vm.stopBroadcast(); + } + + modifier fork(string memory chainAlias) { + vm.createSelectFork(chainAlias); + _; + } + + function setUp() public virtual { + broadcaster = vm.rememberKey(configurePrivateKey()); + } + + function configurePrivateKey() internal view virtual returns (uint256 privateKey) { + privateKey = vm.envOr({ + name: "PRIVATE_KEY", + defaultValue: vm.deriveKey({ + mnemonic: vm.envOr({name: "MNEMONIC", defaultValue: DEFAULT_MNEMONIC}), + index: uint8(vm.envOr({name: "EOA_INDEX", defaultValue: uint256(0)})) + }) + }); + } + + function generateJson(string memory path, string memory name, address instance, bytes32 salt) internal virtual { + string memory json = "json"; + json.serialize("address", instance); + json.serialize("blockNumber", vm.getBlockNumber()); + json.serialize("timestamp", vm.getBlockTimestamp()); + json.serialize("salt", salt); + json = json.serialize("name", name); + json.write(path); + } + + function promptChains() internal virtual returns (string[] memory chainAliases) { + string memory input = prompt("Chains separated by ','", defaultChains()); + return vm.split(vm.replace(input, " ", ""), ","); + } + + function prompt(string memory promptText) internal returns (string memory input) { + return prompt(promptText, new string(0)); + } + + function prompt(string memory promptText, string memory defaultValue) internal returns (string memory input) { + input = vm.prompt(string.concat(promptText, " (default: `", defaultValue, "`)")); + if (bytes(input).length == 0) input = defaultValue; + } + + function promptAddress(string memory promptText, address defaultValue) internal returns (address) { + return vm.parseAddress(prompt(promptText, vm.toString(defaultValue))); + } + + function promptAddress(string memory promptText) internal returns (address) { + return promptAddress(promptText, address(0)); + } + + function promptBool(string memory promptText, bool defaultValue) internal returns (bool) { + return vm.parseBool(prompt(promptText, vm.toString(defaultValue))); + } + + function promptBool(string memory promptText) internal returns (bool) { + return promptBool(promptText, false); + } + + function promptUint256(string memory promptText, uint256 defaultValue) internal returns (uint256) { + return vm.parseUint(prompt(promptText, vm.toString(defaultValue))); + } + + function promptUint256(string memory promptText) internal returns (uint256) { + return promptUint256(promptText, uint256(0)); + } + + function promptInt256(string memory promptText, int256 defaultValue) internal returns (int256) { + return vm.parseInt(prompt(promptText, vm.toString(defaultValue))); + } + + function promptInt256(string memory promptText) internal returns (int256) { + return promptInt256(promptText, int256(0)); + } + + function promptBytes32(string memory promptText, bytes32 defaultValue) internal returns (bytes32) { + return vm.parseBytes32(prompt(promptText, vm.toString(defaultValue))); + } + + function promptBytes32(string memory promptText) internal returns (bytes32) { + return promptBytes32(promptText, bytes32(0)); + } + + function promptBytes(string memory promptText, bytes memory defaultValue) internal returns (bytes memory) { + return vm.parseBytes(prompt(promptText, vm.toString(defaultValue))); + } + + function promptBytes(string memory promptText) internal returns (bytes memory) { + return promptBytes(promptText, new bytes(0)); + } + + function defaultChains() internal view virtual returns (string memory chains) { + return DEFAULT_CHAINS; + } + + function defaultSalt() internal view virtual returns (bytes32) { + return bytes32(0); + } } diff --git a/script/CreateX.s.sol b/script/CreateX.s.sol index 73f1226..27f2233 100644 --- a/script/CreateX.s.sol +++ b/script/CreateX.s.sol @@ -7,139 +7,132 @@ import {BaseScript} from "./BaseScript.sol"; import {ERC1967_PROXY_BYTECODE, TRANSPARENT_PROXY_BYTECODE} from "./Precompiles.sol"; contract CreateX is BaseScript { - ICreateXFactory internal constant CREATEX_FACTORY = ICreateXFactory(0xfC5D1D7b066730fC403C994365205a96fE1d8Bcf); - - function deployContract() internal virtual broadcast returns (address) { - uint256 creationType = promptCreationType(); - bytes memory initCode; - bytes32 salt; - uint256 value; - - if (creationType < uint8(ICreateXFactory.CreationType.Clone)) { - bytes memory creationCode = vm.getCode(prompt("Artifact path")); - bytes memory arguments = promptBytes("Constructor arguments"); - initCode = bytes.concat(creationCode, arguments); - } else { - address implementation = vm.promptAddress("Implementation"); - initCode = abi.encodePacked(implementation); - } - - if ( - creationType != uint8(ICreateXFactory.CreationType.CREATE) && - creationType != uint8(ICreateXFactory.CreationType.Clone) - ) { - salt = promptBytes32("Salt", defaultSalt()); - } - - value = promptUint256("msg.value"); - - return deployCreateX(creationType, initCode, salt, value); - } - - function deployCreateX(uint256 creationType, bytes memory initCode, bytes32 salt) internal returns (address) { - return deployCreateX(creationType, initCode, salt, 0); - } - - function deployCreateX( - uint256 creationType, - bytes memory initCode, - bytes32 salt, - uint256 value - ) internal returns (address) { - return CREATEX_FACTORY.createX{value: value}(asCreationType(creationType), initCode, salt); - } - - function deployCreate(bytes memory initCode) internal returns (address) { - return deployCreate(initCode, 0); - } - - function deployCreate(bytes memory initCode, uint256 value) internal returns (address) { - return CREATEX_FACTORY.create{value: value}(initCode); - } - - function deployCreate2(bytes memory initCode, bytes32 salt) internal returns (address) { - return deployCreate2(initCode, salt, 0); - } - - function deployCreate2(bytes memory initCode, bytes32 salt, uint256 value) internal returns (address) { - return CREATEX_FACTORY.create2{value: value}(initCode, salt); - } - - function deployCreate3(bytes memory initCode, bytes32 salt) internal returns (address) { - return deployCreate3(initCode, salt, 0); - } - - function deployCreate3(bytes memory initCode, bytes32 salt, uint256 value) internal returns (address) { - return CREATEX_FACTORY.create3{value: value}(initCode, salt); - } - - function deployClone(address implementation) internal returns (address) { - return deployClone(implementation, 0); - } - - function deployClone(address implementation, uint256 value) internal returns (address) { - return CREATEX_FACTORY.clone{value: value}(implementation); - } - - function deployCloneDeterministic(address implementation, bytes32 salt) internal returns (address) { - return deployCloneDeterministic(implementation, salt, 0); - } - - function deployCloneDeterministic(address implementation, bytes32 salt, uint256 value) internal returns (address) { - return CREATEX_FACTORY.cloneDeterministic{value: value}(implementation, salt); - } - - function deployERC1967Proxy( - address implementation, - bytes memory data, - uint256 value - ) internal returns (address proxy) { - bytes memory arguments = abi.encode(implementation, data); - bytes memory initCode = bytes.concat(ERC1967_PROXY_BYTECODE, arguments); - return deployCreate(initCode, value); - } - - function deployERC1967Proxy( - address implementation, - bytes memory data, - bytes32 salt, - uint256 value - ) internal returns (address proxy) { - bytes memory arguments = abi.encode(implementation, data); - bytes memory initCode = bytes.concat(ERC1967_PROXY_BYTECODE, arguments); - return deployCreate2(initCode, salt, value); - } - - function deployTransparentProxy( - address owner, - address implementation, - bytes memory data, - uint256 value - ) internal returns (address proxy, address proxyAdmin) { - bytes memory arguments = abi.encode(owner, implementation, data); - bytes memory initCode = bytes.concat(TRANSPARENT_PROXY_BYTECODE, arguments); - proxyAdmin = CreateXLib.computeCreateAddress(proxy = deployCreate(initCode, value), 1); - } - - function deployTransparentProxy( - address owner, - address implementation, - bytes memory data, - bytes32 salt, - uint256 value - ) internal returns (address proxy, address proxyAdmin) { - bytes memory arguments = abi.encode(owner, implementation, data); - bytes memory initCode = bytes.concat(TRANSPARENT_PROXY_BYTECODE, arguments); - proxyAdmin = CreateXLib.computeCreateAddress(proxy = deployCreate2(initCode, salt, value), 1); - } - - function promptCreationType() internal returns (uint256) { - string memory promptText = "CreationType (0: CREATE, 1: CREATE2, 2: CREATE3, 3: Clone, 4: CloneDeterministic)"; - return vm.promptUint(promptText); - } - - function asCreationType(uint256 creationType) internal pure returns (ICreateXFactory.CreationType) { - if (creationType > uint8(type(ICreateXFactory.CreationType).max)) revert ICreateXFactory.InvalidCreationType(); - return ICreateXFactory.CreationType(creationType); - } + ICreateXFactory internal constant CREATEX_FACTORY = ICreateXFactory(0xfC5D1D7b066730fC403C994365205a96fE1d8Bcf); + + function deployContract() internal virtual broadcast returns (address) { + uint256 creationType = promptCreationType(); + bytes memory initCode; + bytes32 salt; + uint256 value; + + if (creationType < uint8(ICreateXFactory.CreationType.Clone)) { + bytes memory creationCode = vm.getCode(prompt("Artifact path")); + bytes memory arguments = promptBytes("Constructor arguments"); + initCode = bytes.concat(creationCode, arguments); + } else { + address implementation = vm.promptAddress("Implementation"); + initCode = abi.encodePacked(implementation); + } + + if ( + creationType != uint8(ICreateXFactory.CreationType.CREATE) + && creationType != uint8(ICreateXFactory.CreationType.Clone) + ) { + salt = promptBytes32("Salt", defaultSalt()); + } + + value = promptUint256("msg.value"); + + return deployCreateX(creationType, initCode, salt, value); + } + + function deployCreateX(uint256 creationType, bytes memory initCode, bytes32 salt) internal returns (address) { + return deployCreateX(creationType, initCode, salt, 0); + } + + function deployCreateX(uint256 creationType, bytes memory initCode, bytes32 salt, uint256 value) + internal + returns (address) + { + return CREATEX_FACTORY.createX{value: value}(asCreationType(creationType), initCode, salt); + } + + function deployCreate(bytes memory initCode) internal returns (address) { + return deployCreate(initCode, 0); + } + + function deployCreate(bytes memory initCode, uint256 value) internal returns (address) { + return CREATEX_FACTORY.create{value: value}(initCode); + } + + function deployCreate2(bytes memory initCode, bytes32 salt) internal returns (address) { + return deployCreate2(initCode, salt, 0); + } + + function deployCreate2(bytes memory initCode, bytes32 salt, uint256 value) internal returns (address) { + return CREATEX_FACTORY.create2{value: value}(initCode, salt); + } + + function deployCreate3(bytes memory initCode, bytes32 salt) internal returns (address) { + return deployCreate3(initCode, salt, 0); + } + + function deployCreate3(bytes memory initCode, bytes32 salt, uint256 value) internal returns (address) { + return CREATEX_FACTORY.create3{value: value}(initCode, salt); + } + + function deployClone(address implementation) internal returns (address) { + return deployClone(implementation, 0); + } + + function deployClone(address implementation, uint256 value) internal returns (address) { + return CREATEX_FACTORY.clone{value: value}(implementation); + } + + function deployCloneDeterministic(address implementation, bytes32 salt) internal returns (address) { + return deployCloneDeterministic(implementation, salt, 0); + } + + function deployCloneDeterministic(address implementation, bytes32 salt, uint256 value) internal returns (address) { + return CREATEX_FACTORY.cloneDeterministic{value: value}(implementation, salt); + } + + function deployERC1967Proxy(address implementation, bytes memory data, uint256 value) + internal + returns (address proxy) + { + bytes memory arguments = abi.encode(implementation, data); + bytes memory initCode = bytes.concat(ERC1967_PROXY_BYTECODE, arguments); + return deployCreate(initCode, value); + } + + function deployERC1967Proxy(address implementation, bytes memory data, bytes32 salt, uint256 value) + internal + returns (address proxy) + { + bytes memory arguments = abi.encode(implementation, data); + bytes memory initCode = bytes.concat(ERC1967_PROXY_BYTECODE, arguments); + return deployCreate2(initCode, salt, value); + } + + function deployTransparentProxy(address owner, address implementation, bytes memory data, uint256 value) + internal + returns (address proxy, address proxyAdmin) + { + bytes memory arguments = abi.encode(owner, implementation, data); + bytes memory initCode = bytes.concat(TRANSPARENT_PROXY_BYTECODE, arguments); + proxyAdmin = CreateXLib.computeCreateAddress(proxy = deployCreate(initCode, value), 1); + } + + function deployTransparentProxy( + address owner, + address implementation, + bytes memory data, + bytes32 salt, + uint256 value + ) internal returns (address proxy, address proxyAdmin) { + bytes memory arguments = abi.encode(owner, implementation, data); + bytes memory initCode = bytes.concat(TRANSPARENT_PROXY_BYTECODE, arguments); + proxyAdmin = CreateXLib.computeCreateAddress(proxy = deployCreate2(initCode, salt, value), 1); + } + + function promptCreationType() internal returns (uint256) { + string memory promptText = "CreationType (0: CREATE, 1: CREATE2, 2: CREATE3, 3: Clone, 4: CloneDeterministic)"; + return vm.promptUint(promptText); + } + + function asCreationType(uint256 creationType) internal pure returns (ICreateXFactory.CreationType) { + if (creationType > uint8(type(ICreateXFactory.CreationType).max)) revert ICreateXFactory.InvalidCreationType(); + return ICreateXFactory.CreationType(creationType); + } } diff --git a/script/Deploy.s.sol b/script/Deploy.s.sol index ef4cfeb..0ae7db3 100644 --- a/script/Deploy.s.sol +++ b/script/Deploy.s.sol @@ -5,20 +5,22 @@ import {CreateXFactory} from "src/CreateXFactory.sol"; import {BaseScript} from "./BaseScript.sol"; contract Deploy is BaseScript { - bytes32 internal salt; + bytes32 internal salt; - function setUp() public virtual override { - super.setUp(); - salt = vm.envOr({name: "SALT", defaultValue: defaultSalt()}); - } + function setUp() public virtual override { + super.setUp(); + salt = vm.envOr({name: "SALT", defaultValue: defaultSalt()}); + } - function run() external { - string[] memory chainAliases = promptChains(); - for (uint256 i; i < chainAliases.length; ++i) deployOnChain(chainAliases[i]); - } + function run() external { + string[] memory chainAliases = promptChains(); + for (uint256 i; i < chainAliases.length; ++i) { + deployOnChain(chainAliases[i]); + } + } - function deployOnChain(string memory chainAlias) internal fork(chainAlias) broadcast { - string memory path = string.concat(vm.projectRoot(), "/deployments/", vm.toString(block.chainid), ".json"); - generateJson(path, "CreateXFactory", address(new CreateXFactory{salt: salt}()), salt); - } + function deployOnChain(string memory chainAlias) internal fork(chainAlias) broadcast { + string memory path = string.concat(vm.projectRoot(), "/deployments/", vm.toString(block.chainid), ".json"); + generateJson(path, "CreateXFactory", address(new CreateXFactory{salt: salt}()), salt); + } } diff --git a/script/Precompiles.sol b/script/Precompiles.sol index 64afe0e..8c36515 100644 --- a/script/Precompiles.sol +++ b/script/Precompiles.sol @@ -1,16 +1,23 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.30; -bytes constant CREATEX_FACTORY_BYTECODE = hex"60808060405234601557610aaa908161001a8239f35b5f80fdfe60806040526004361015610011575f80fd5b5f3560e01c806312b40d531461031b57806328ddd046146102de578063315c8d9c146102785780636cec25361461023b5780638124b78e146101ff578063890c283b146101bf5780638c0b8db2146101ab578063a7b62a7f14610197578063b86b2ceb14610157578063cf5ba53f146101025763df6a602c14610092575f80fd5b346100fe5760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100fe5760043560058110156100fe576100e060209160443590602435906108ee565b73ffffffffffffffffffffffffffffffffffffffff60405191168152f35b5f80fd5b60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100fe5760043567ffffffffffffffff81116100fe576100e061015160209236906004016103d7565b90610888565b60407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100fe5760206100e061018e6103b4565b602435906107d5565b60206100e06101a536610405565b9161071a565b60206100e06101b936610405565b916105f4565b346100fe5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100fe5760206100e060243560043530610a8e565b60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100fe5760206100e06102366103b4565b61052b565b346100fe5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100fe5760206100e060043530610a3e565b60607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100fe5760043560058110156100fe5760243567ffffffffffffffff81116100fe576020916102d46100e09236906004016103d7565b9060443592610456565b346100fe5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100fe5760206100e0600435306109b4565b346100fe5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100fe5760206100e06103576103b4565b60243590309160439160559360405192605884015260388301526f5af43d82803e903d91602b57fd5bf3ff60248301526014820152733d602d80600a3d3981f3363d3d373d3d3d363d7381526037600c8201206078820152012090565b6004359073ffffffffffffffffffffffffffffffffffffffff821682036100fe57565b9181601f840112156100fe5782359167ffffffffffffffff83116100fe57602083818601950101116100fe57565b60407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8201126100fe576004359067ffffffffffffffff82116100fe5761044e916004016103d7565b909160243590565b92909260058110156104fe578061047557505061047291610888565b90565b9091906001810361048a5750610472926105f4565b6002810361049c57506104729261071a565b600381036104bb575090506014116100fe57610472903560601c61052b565b6004036104d6576014116100fe57610472913560601c6107d5565b7f5fc1076a000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b803b158160601b15176105e7573447106105da5780763d602d80600a3d3981f3363d3d373d3d3d363d7300000062ffffff6e5af43d82803e903d91602b57fd5bf39360881c16175f5260781b176020526037600934f090813b158260601b15176105cd573373ffffffffffffffffffffffffffffffffffffffff83167fb6c9f70724e4ce6b9bca2eace320dd3164063f0a0b229f8c4705ef05be829c255f80a3565b63a28c24735f526004601cfd5b63f4d678b85f526004601cfd5b6368155f9a5f526004601cfd5b8260601c3381149015171561066b5761060e913691610678565b3447106105da578051829160200134f590813b158260601b15176105cd573373ffffffffffffffffffffffffffffffffffffffff83167f09005d6d86a7a6ccbee89763e10463ab645e06615f10a6a2fe604a62f31843ef5f80a490565b6381e69d9b5f526004601cfd5b92919267ffffffffffffffff82116106ed57604051917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0603f81601f8401160116830183811067ffffffffffffffff8211176106ed576040528294818452818301116100fe578281602093845f960137010152565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b8260601c3381149015171561066b57610734913691610678565b903447106105da576f67363d3d37363d34f03d5260086018f35f52806010805ff58060601b156107c857805f9160145261d694825260016034536017601e209338916020825192019034905af1823b02156105cd573373ffffffffffffffffffffffffffffffffffffffff83167f09005d6d86a7a6ccbee89763e10463ab645e06615f10a6a2fe604a62f31843ef5f80a490565b63d49e7d745f526004601cfd5b8160601c3381149015171561066b57803b158160601b15176105e7573447106105da5780763d602d80600a3d3981f3363d3d373d3d3d363d7300000062ffffff6e5af43d82803e903d91602b57fd5bf39360881c16175f5260781b17602052806037600934f590813b158260601b15176105cd573373ffffffffffffffffffffffffffffffffffffffff83167f09005d6d86a7a6ccbee89763e10463ab645e06615f10a6a2fe604a62f31843ef5f80a490565b610893913691610678565b3447106105da5760208151910134f090813b158260601b15176105cd573373ffffffffffffffffffffffffffffffffffffffff83167fb6c9f70724e4ce6b9bca2eace320dd3164063f0a0b229f8c4705ef05be829c255f80a3565b9190916005811015806104fe57811580156109a8575b156109165750506104729150306109b4565b6104fe576001810361092d57506104729130610a8e565b600281036109415750610472915030610a3e565b6004036104d6576104729160601c309160439160559360405192605884015260388301526f5af43d82803e903d91602b57fd5bf3ff60248301526014820152733d602d80600a3d3981f3363d3d373d3d3d363d7381526037600c8201206078820152012090565b50505f60038214610904565b67ffffffffffffffff821015610a3157609460015360601b6002525f9060808110610a16579081825b610a045760179250816080016016538160031b610100031b82525b8060d6015f53015f2090565b9060019260081c9283910191906109dd565b908160179215610a29575b6016536109f8565b506080610a21565b63756688fe5f526004601cfd5b90604051915f5260ff600b536020527f21c35dbe1b344a2488cf3321d6ce542f8e9f305544ff09e4993a62319a497c1f6040526055600b2060145260405261d6945f5260016034536017601e2090565b9060ff5f5360355260601b60015260155260555f20905f60355256"; +bytes constant CREATEX_FACTORY_BYTECODE = + hex"60808060405234601557610aaa908161001a8239f35b5f80fdfe60806040526004361015610011575f80fd5b5f3560e01c806312b40d531461031b57806328ddd046146102de578063315c8d9c146102785780636cec25361461023b5780638124b78e146101ff578063890c283b146101bf5780638c0b8db2146101ab578063a7b62a7f14610197578063b86b2ceb14610157578063cf5ba53f146101025763df6a602c14610092575f80fd5b346100fe5760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100fe5760043560058110156100fe576100e060209160443590602435906108ee565b73ffffffffffffffffffffffffffffffffffffffff60405191168152f35b5f80fd5b60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100fe5760043567ffffffffffffffff81116100fe576100e061015160209236906004016103d7565b90610888565b60407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100fe5760206100e061018e6103b4565b602435906107d5565b60206100e06101a536610405565b9161071a565b60206100e06101b936610405565b916105f4565b346100fe5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100fe5760206100e060243560043530610a8e565b60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100fe5760206100e06102366103b4565b61052b565b346100fe5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100fe5760206100e060043530610a3e565b60607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100fe5760043560058110156100fe5760243567ffffffffffffffff81116100fe576020916102d46100e09236906004016103d7565b9060443592610456565b346100fe5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100fe5760206100e0600435306109b4565b346100fe5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100fe5760206100e06103576103b4565b60243590309160439160559360405192605884015260388301526f5af43d82803e903d91602b57fd5bf3ff60248301526014820152733d602d80600a3d3981f3363d3d373d3d3d363d7381526037600c8201206078820152012090565b6004359073ffffffffffffffffffffffffffffffffffffffff821682036100fe57565b9181601f840112156100fe5782359167ffffffffffffffff83116100fe57602083818601950101116100fe57565b60407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8201126100fe576004359067ffffffffffffffff82116100fe5761044e916004016103d7565b909160243590565b92909260058110156104fe578061047557505061047291610888565b90565b9091906001810361048a5750610472926105f4565b6002810361049c57506104729261071a565b600381036104bb575090506014116100fe57610472903560601c61052b565b6004036104d6576014116100fe57610472913560601c6107d5565b7f5fc1076a000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b803b158160601b15176105e7573447106105da5780763d602d80600a3d3981f3363d3d373d3d3d363d7300000062ffffff6e5af43d82803e903d91602b57fd5bf39360881c16175f5260781b176020526037600934f090813b158260601b15176105cd573373ffffffffffffffffffffffffffffffffffffffff83167fb6c9f70724e4ce6b9bca2eace320dd3164063f0a0b229f8c4705ef05be829c255f80a3565b63a28c24735f526004601cfd5b63f4d678b85f526004601cfd5b6368155f9a5f526004601cfd5b8260601c3381149015171561066b5761060e913691610678565b3447106105da578051829160200134f590813b158260601b15176105cd573373ffffffffffffffffffffffffffffffffffffffff83167f09005d6d86a7a6ccbee89763e10463ab645e06615f10a6a2fe604a62f31843ef5f80a490565b6381e69d9b5f526004601cfd5b92919267ffffffffffffffff82116106ed57604051917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0603f81601f8401160116830183811067ffffffffffffffff8211176106ed576040528294818452818301116100fe578281602093845f960137010152565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b8260601c3381149015171561066b57610734913691610678565b903447106105da576f67363d3d37363d34f03d5260086018f35f52806010805ff58060601b156107c857805f9160145261d694825260016034536017601e209338916020825192019034905af1823b02156105cd573373ffffffffffffffffffffffffffffffffffffffff83167f09005d6d86a7a6ccbee89763e10463ab645e06615f10a6a2fe604a62f31843ef5f80a490565b63d49e7d745f526004601cfd5b8160601c3381149015171561066b57803b158160601b15176105e7573447106105da5780763d602d80600a3d3981f3363d3d373d3d3d363d7300000062ffffff6e5af43d82803e903d91602b57fd5bf39360881c16175f5260781b17602052806037600934f590813b158260601b15176105cd573373ffffffffffffffffffffffffffffffffffffffff83167f09005d6d86a7a6ccbee89763e10463ab645e06615f10a6a2fe604a62f31843ef5f80a490565b610893913691610678565b3447106105da5760208151910134f090813b158260601b15176105cd573373ffffffffffffffffffffffffffffffffffffffff83167fb6c9f70724e4ce6b9bca2eace320dd3164063f0a0b229f8c4705ef05be829c255f80a3565b9190916005811015806104fe57811580156109a8575b156109165750506104729150306109b4565b6104fe576001810361092d57506104729130610a8e565b600281036109415750610472915030610a3e565b6004036104d6576104729160601c309160439160559360405192605884015260388301526f5af43d82803e903d91602b57fd5bf3ff60248301526014820152733d602d80600a3d3981f3363d3d373d3d3d363d7381526037600c8201206078820152012090565b50505f60038214610904565b67ffffffffffffffff821015610a3157609460015360601b6002525f9060808110610a16579081825b610a045760179250816080016016538160031b610100031b82525b8060d6015f53015f2090565b9060019260081c9283910191906109dd565b908160179215610a29575b6016536109f8565b506080610a21565b63756688fe5f526004601cfd5b90604051915f5260ff600b536020527f21c35dbe1b344a2488cf3321d6ce542f8e9f305544ff09e4993a62319a497c1f6040526055600b2060145260405261d6945f5260016034536017601e2090565b9060ff5f5360355260601b60015260155260555f20905f60355256"; -bytes constant ERC1967_PROXY_BYTECODE = hex"60806040526102728038038061001481610168565b92833981016040828203126101645781516001600160a01b03811692909190838303610164576020810151906001600160401b03821161016457019281601f8501121561016457835161006e610069826101a1565b610168565b9481865260208601936020838301011161016457815f926020809301865e86010152823b15610152577f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80546001600160a01b031916821790557fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b5f80a282511561013a575f8091610122945190845af43d15610132573d91610113610069846101a1565b9283523d5f602085013e6101bc565b505b6040516057908161021b8239f35b6060916101bc565b50505034156101245763b398979f60e01b5f5260045ffd5b634c9c8ce360e01b5f5260045260245ffd5b5f80fd5b6040519190601f01601f191682016001600160401b0381118382101761018d57604052565b634e487b7160e01b5f52604160045260245ffd5b6001600160401b03811161018d57601f01601f191660200190565b906101e057508051156101d157805190602001fd5b63d6bda27560e01b5f5260045ffd5b81511580610211575b6101f1575090565b639996b31560e01b5f9081526001600160a01b0391909116600452602490fd5b50803b156101e956fe60806040525f8073ffffffffffffffffffffffffffffffffffffffff7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5416368280378136915af43d5f803e156053573d5ff35b3d5ffd"; +bytes constant ERC1967_PROXY_BYTECODE = + hex"60806040526102728038038061001481610168565b92833981016040828203126101645781516001600160a01b03811692909190838303610164576020810151906001600160401b03821161016457019281601f8501121561016457835161006e610069826101a1565b610168565b9481865260208601936020838301011161016457815f926020809301865e86010152823b15610152577f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80546001600160a01b031916821790557fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b5f80a282511561013a575f8091610122945190845af43d15610132573d91610113610069846101a1565b9283523d5f602085013e6101bc565b505b6040516057908161021b8239f35b6060916101bc565b50505034156101245763b398979f60e01b5f5260045ffd5b634c9c8ce360e01b5f5260045260245ffd5b5f80fd5b6040519190601f01601f191682016001600160401b0381118382101761018d57604052565b634e487b7160e01b5f52604160045260245ffd5b6001600160401b03811161018d57601f01601f191660200190565b906101e057508051156101d157805190602001fd5b63d6bda27560e01b5f5260045ffd5b81511580610211575b6101f1575090565b639996b31560e01b5f9081526001600160a01b0391909116600452602490fd5b50803b156101e956fe60806040525f8073ffffffffffffffffffffffffffffffffffffffff7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5416368280378136915af43d5f803e156053573d5ff35b3d5ffd"; -bytes constant TRANSPARENT_PROXY_BYTECODE = hex"60a0604052610db6803803806100148161026b565b92833981016060828203126102675761002c82610290565b61003860208401610290565b604084015190936001600160401b03821161026757019180601f8401121561026757825161006d610068826102a4565b61026b565b9381855260208501926020838301011161026757815f926020809301855e85010152813b15610246577f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80546001600160a01b0319166001600160a01b0384169081179091557fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b5f80a282511561022e575f809161012d945190845af43d15610226573d9161011e610068846102a4565b9283523d5f602085013e6102bf565b505b604051906105fd8083016001600160401b0381118482101761021257602092849261079984396001600160a01b031681520301905ff080156102075760018060a01b0316806080525f516020610d965f395f51905f52547f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f6040805160018060a01b0384168152846020820152a181156101f4576001600160a01b031916175f516020610d965f395f51905f525560405161047b908161031e82396080518160070152f35b633173bdd160e11b5f525f60045260245ffd5b6040513d5f823e3d90fd5b634e487b7160e01b5f52604160045260245ffd5b6060916102bf565b505050341561012f5763b398979f60e01b5f5260045ffd5b50634c9c8ce360e01b5f9081526001600160a01b0391909116600452602490fd5b5f80fd5b6040519190601f01601f191682016001600160401b0381118382101761021257604052565b51906001600160a01b038216820361026757565b6001600160401b03811161021257601f01601f191660200190565b906102e357508051156102d457805190602001fd5b63d6bda27560e01b5f5260045ffd5b81511580610314575b6102f4575090565b639996b31560e01b5f9081526001600160a01b0391909116600452602490fd5b50803b156102ec56fe6080604052337f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16036100bd575f357fffffffff00000000000000000000000000000000000000000000000000000000167f4f1ef28600000000000000000000000000000000000000000000000000000000146100b3577fd2b576ec000000000000000000000000000000000000000000000000000000005f5260045ffd5b6100bb6101c1565b005b5f8073ffffffffffffffffffffffffffffffffffffffff7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5416368280378136915af43d5f803e1561010d573d5ff35b3d5ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f604051930116820182811067ffffffffffffffff82111761018257604052565b610111565b67ffffffffffffffff811161018257601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b366004116102745760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102745760043573ffffffffffffffffffffffffffffffffffffffff8116809103610274576024359067ffffffffffffffff821161027457366023830112156102745781600401359061024961024483610187565b61013e565b918083523660248286010111610274576020815f92602461027297018387013784010152610278565b565b5f80fd5b90813b156103675773ffffffffffffffffffffffffffffffffffffffff8216807fffffffffffffffffffffffff00000000000000000000000000000000000000007f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5416177f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc557fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b5f80a280511561033657610333916103a9565b50565b50503461033f57565b7fb398979f000000000000000000000000000000000000000000000000000000005f5260045ffd5b73ffffffffffffffffffffffffffffffffffffffff827f4c9c8ce3000000000000000000000000000000000000000000000000000000005f521660045260245ffd5b5f806103db93602081519101845af43d156103de573d916103cc61024484610187565b9283523d5f602085013e6103e2565b90565b6060915b9061041f57508051156103f757805190602001fd5b7fd6bda275000000000000000000000000000000000000000000000000000000005f5260045ffd5b81511580610472575b610430575090565b73ffffffffffffffffffffffffffffffffffffffff907f9996b315000000000000000000000000000000000000000000000000000000005f521660045260245ffd5b50803b156104285660803460b857601f6105fd38819003918201601f19168301916001600160401b0383118484101760bc5780849260209460405283398101031260b857516001600160a01b0381169081900360b857801560a5575f80546001600160a01b031981168317825560405192916001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09080a361052c90816100d18239f35b631e4fbdf760e01b5f525f60045260245ffd5b5f80fd5b634e487b7160e01b5f52604160045260245ffdfe60806040526004361015610011575f80fd5b5f5f3560e01c8063715018a6146103c25780638da5cb5b146103725780639623609d146101c9578063ad3cb1cc146101465763f2fde38b14610051575f80fd5b346101435760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101435760043573ffffffffffffffffffffffffffffffffffffffff8116809103610141576100a96104e0565b80156101155773ffffffffffffffffffffffffffffffffffffffff8254827fffffffffffffffffffffffff00000000000000000000000000000000000000008216178455167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08380a380f35b6024827f1e4fbdf700000000000000000000000000000000000000000000000000000000815280600452fd5b505b80fd5b503461014357807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261014357506101c560405161018760408261045c565b600581527f352e302e30000000000000000000000000000000000000000000000000000000602082015260405191829160208352602083019061049d565b0390f35b5060607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103415760043573ffffffffffffffffffffffffffffffffffffffff81168091036103415760243573ffffffffffffffffffffffffffffffffffffffff81168091036103415760443567ffffffffffffffff8111610341573660238201121561034157806004013567ffffffffffffffff8111610345576040519161029d601f83017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0166020018461045c565b818352366024838301011161034157815f9260246020930183860137830101526102c56104e0565b823b1561034157610314925f926040518095819482937f4f1ef286000000000000000000000000000000000000000000000000000000008452600484015260406024840152604483019061049d565b039134905af1801561033657610328575080f35b61033491505f9061045c565b005b6040513d5f823e3d90fd5b5f80fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b34610341575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261034157602073ffffffffffffffffffffffffffffffffffffffff5f5416604051908152f35b34610341575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610341576103f86104e0565b5f73ffffffffffffffffffffffffffffffffffffffff81547fffffffffffffffffffffffff000000000000000000000000000000000000000081168355167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761034557604052565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f602080948051918291828752018686015e5f8582860101520116010190565b73ffffffffffffffffffffffffffffffffffffffff5f5416330361050057565b7f118cdaa7000000000000000000000000000000000000000000000000000000005f523360045260245ffdb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103"; +bytes constant TRANSPARENT_PROXY_BYTECODE = + hex"60a0604052610db6803803806100148161026b565b92833981016060828203126102675761002c82610290565b61003860208401610290565b604084015190936001600160401b03821161026757019180601f8401121561026757825161006d610068826102a4565b61026b565b9381855260208501926020838301011161026757815f926020809301855e85010152813b15610246577f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80546001600160a01b0319166001600160a01b0384169081179091557fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b5f80a282511561022e575f809161012d945190845af43d15610226573d9161011e610068846102a4565b9283523d5f602085013e6102bf565b505b604051906105fd8083016001600160401b0381118482101761021257602092849261079984396001600160a01b031681520301905ff080156102075760018060a01b0316806080525f516020610d965f395f51905f52547f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f6040805160018060a01b0384168152846020820152a181156101f4576001600160a01b031916175f516020610d965f395f51905f525560405161047b908161031e82396080518160070152f35b633173bdd160e11b5f525f60045260245ffd5b6040513d5f823e3d90fd5b634e487b7160e01b5f52604160045260245ffd5b6060916102bf565b505050341561012f5763b398979f60e01b5f5260045ffd5b50634c9c8ce360e01b5f9081526001600160a01b0391909116600452602490fd5b5f80fd5b6040519190601f01601f191682016001600160401b0381118382101761021257604052565b51906001600160a01b038216820361026757565b6001600160401b03811161021257601f01601f191660200190565b906102e357508051156102d457805190602001fd5b63d6bda27560e01b5f5260045ffd5b81511580610314575b6102f4575090565b639996b31560e01b5f9081526001600160a01b0391909116600452602490fd5b50803b156102ec56fe6080604052337f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16036100bd575f357fffffffff00000000000000000000000000000000000000000000000000000000167f4f1ef28600000000000000000000000000000000000000000000000000000000146100b3577fd2b576ec000000000000000000000000000000000000000000000000000000005f5260045ffd5b6100bb6101c1565b005b5f8073ffffffffffffffffffffffffffffffffffffffff7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5416368280378136915af43d5f803e1561010d573d5ff35b3d5ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f604051930116820182811067ffffffffffffffff82111761018257604052565b610111565b67ffffffffffffffff811161018257601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b366004116102745760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102745760043573ffffffffffffffffffffffffffffffffffffffff8116809103610274576024359067ffffffffffffffff821161027457366023830112156102745781600401359061024961024483610187565b61013e565b918083523660248286010111610274576020815f92602461027297018387013784010152610278565b565b5f80fd5b90813b156103675773ffffffffffffffffffffffffffffffffffffffff8216807fffffffffffffffffffffffff00000000000000000000000000000000000000007f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5416177f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc557fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b5f80a280511561033657610333916103a9565b50565b50503461033f57565b7fb398979f000000000000000000000000000000000000000000000000000000005f5260045ffd5b73ffffffffffffffffffffffffffffffffffffffff827f4c9c8ce3000000000000000000000000000000000000000000000000000000005f521660045260245ffd5b5f806103db93602081519101845af43d156103de573d916103cc61024484610187565b9283523d5f602085013e6103e2565b90565b6060915b9061041f57508051156103f757805190602001fd5b7fd6bda275000000000000000000000000000000000000000000000000000000005f5260045ffd5b81511580610472575b610430575090565b73ffffffffffffffffffffffffffffffffffffffff907f9996b315000000000000000000000000000000000000000000000000000000005f521660045260245ffd5b50803b156104285660803460b857601f6105fd38819003918201601f19168301916001600160401b0383118484101760bc5780849260209460405283398101031260b857516001600160a01b0381169081900360b857801560a5575f80546001600160a01b031981168317825560405192916001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09080a361052c90816100d18239f35b631e4fbdf760e01b5f525f60045260245ffd5b5f80fd5b634e487b7160e01b5f52604160045260245ffdfe60806040526004361015610011575f80fd5b5f5f3560e01c8063715018a6146103c25780638da5cb5b146103725780639623609d146101c9578063ad3cb1cc146101465763f2fde38b14610051575f80fd5b346101435760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101435760043573ffffffffffffffffffffffffffffffffffffffff8116809103610141576100a96104e0565b80156101155773ffffffffffffffffffffffffffffffffffffffff8254827fffffffffffffffffffffffff00000000000000000000000000000000000000008216178455167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08380a380f35b6024827f1e4fbdf700000000000000000000000000000000000000000000000000000000815280600452fd5b505b80fd5b503461014357807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261014357506101c560405161018760408261045c565b600581527f352e302e30000000000000000000000000000000000000000000000000000000602082015260405191829160208352602083019061049d565b0390f35b5060607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103415760043573ffffffffffffffffffffffffffffffffffffffff81168091036103415760243573ffffffffffffffffffffffffffffffffffffffff81168091036103415760443567ffffffffffffffff8111610341573660238201121561034157806004013567ffffffffffffffff8111610345576040519161029d601f83017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0166020018461045c565b818352366024838301011161034157815f9260246020930183860137830101526102c56104e0565b823b1561034157610314925f926040518095819482937f4f1ef286000000000000000000000000000000000000000000000000000000008452600484015260406024840152604483019061049d565b039134905af1801561033657610328575080f35b61033491505f9061045c565b005b6040513d5f823e3d90fd5b5f80fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b34610341575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261034157602073ffffffffffffffffffffffffffffffffffffffff5f5416604051908152f35b34610341575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610341576103f86104e0565b5f73ffffffffffffffffffffffffffffffffffffffff81547fffffffffffffffffffffffff000000000000000000000000000000000000000081168355167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761034557604052565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f602080948051918291828752018686015e5f8582860101520116010190565b73ffffffffffffffffffffffffffffffffffffffff5f5416330361050057565b7f118cdaa7000000000000000000000000000000000000000000000000000000005f523360045260245ffdb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103"; -bytes constant PROXY_ADMIN_BYTECODE = hex"60803460b857601f6105fd38819003918201601f19168301916001600160401b0383118484101760bc5780849260209460405283398101031260b857516001600160a01b0381169081900360b857801560a5575f80546001600160a01b031981168317825560405192916001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09080a361052c90816100d18239f35b631e4fbdf760e01b5f525f60045260245ffd5b5f80fd5b634e487b7160e01b5f52604160045260245ffdfe60806040526004361015610011575f80fd5b5f5f3560e01c8063715018a6146103c25780638da5cb5b146103725780639623609d146101c9578063ad3cb1cc146101465763f2fde38b14610051575f80fd5b346101435760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101435760043573ffffffffffffffffffffffffffffffffffffffff8116809103610141576100a96104e0565b80156101155773ffffffffffffffffffffffffffffffffffffffff8254827fffffffffffffffffffffffff00000000000000000000000000000000000000008216178455167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08380a380f35b6024827f1e4fbdf700000000000000000000000000000000000000000000000000000000815280600452fd5b505b80fd5b503461014357807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261014357506101c560405161018760408261045c565b600581527f352e302e30000000000000000000000000000000000000000000000000000000602082015260405191829160208352602083019061049d565b0390f35b5060607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103415760043573ffffffffffffffffffffffffffffffffffffffff81168091036103415760243573ffffffffffffffffffffffffffffffffffffffff81168091036103415760443567ffffffffffffffff8111610341573660238201121561034157806004013567ffffffffffffffff8111610345576040519161029d601f83017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0166020018461045c565b818352366024838301011161034157815f9260246020930183860137830101526102c56104e0565b823b1561034157610314925f926040518095819482937f4f1ef286000000000000000000000000000000000000000000000000000000008452600484015260406024840152604483019061049d565b039134905af1801561033657610328575080f35b61033491505f9061045c565b005b6040513d5f823e3d90fd5b5f80fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b34610341575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261034157602073ffffffffffffffffffffffffffffffffffffffff5f5416604051908152f35b34610341575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610341576103f86104e0565b5f73ffffffffffffffffffffffffffffffffffffffff81547fffffffffffffffffffffffff000000000000000000000000000000000000000081168355167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761034557604052565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f602080948051918291828752018686015e5f8582860101520116010190565b73ffffffffffffffffffffffffffffffffffffffff5f5416330361050057565b7f118cdaa7000000000000000000000000000000000000000000000000000000005f523360045260245ffd"; +bytes constant PROXY_ADMIN_BYTECODE = + hex"60803460b857601f6105fd38819003918201601f19168301916001600160401b0383118484101760bc5780849260209460405283398101031260b857516001600160a01b0381169081900360b857801560a5575f80546001600160a01b031981168317825560405192916001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09080a361052c90816100d18239f35b631e4fbdf760e01b5f525f60045260245ffd5b5f80fd5b634e487b7160e01b5f52604160045260245ffdfe60806040526004361015610011575f80fd5b5f5f3560e01c8063715018a6146103c25780638da5cb5b146103725780639623609d146101c9578063ad3cb1cc146101465763f2fde38b14610051575f80fd5b346101435760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101435760043573ffffffffffffffffffffffffffffffffffffffff8116809103610141576100a96104e0565b80156101155773ffffffffffffffffffffffffffffffffffffffff8254827fffffffffffffffffffffffff00000000000000000000000000000000000000008216178455167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08380a380f35b6024827f1e4fbdf700000000000000000000000000000000000000000000000000000000815280600452fd5b505b80fd5b503461014357807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261014357506101c560405161018760408261045c565b600581527f352e302e30000000000000000000000000000000000000000000000000000000602082015260405191829160208352602083019061049d565b0390f35b5060607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103415760043573ffffffffffffffffffffffffffffffffffffffff81168091036103415760243573ffffffffffffffffffffffffffffffffffffffff81168091036103415760443567ffffffffffffffff8111610341573660238201121561034157806004013567ffffffffffffffff8111610345576040519161029d601f83017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0166020018461045c565b818352366024838301011161034157815f9260246020930183860137830101526102c56104e0565b823b1561034157610314925f926040518095819482937f4f1ef286000000000000000000000000000000000000000000000000000000008452600484015260406024840152604483019061049d565b039134905af1801561033657610328575080f35b61033491505f9061045c565b005b6040513d5f823e3d90fd5b5f80fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b34610341575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261034157602073ffffffffffffffffffffffffffffffffffffffff5f5416604051908152f35b34610341575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610341576103f86104e0565b5f73ffffffffffffffffffffffffffffffffffffffff81547fffffffffffffffffffffffff000000000000000000000000000000000000000081168355167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761034557604052565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f602080948051918291828752018686015e5f8582860101520116010190565b73ffffffffffffffffffffffffffffffffffffffff5f5416330361050057565b7f118cdaa7000000000000000000000000000000000000000000000000000000005f523360045260245ffd"; -bytes constant FORGE_PROXY_BYTECODE = hex"60a0806040526106a28038038091610017828561023a565b83398101906060818303126102225761002f8161025d565b9161003c6020830161025d565b604083015190926001600160401b038211610222570181601f820112156102225780516001600160401b0381116102265760405191610085601f8301601f19166020018461023a565b81835260208301936020838301011161022257815f926020809301865e83010152604051936101206102f86100bd602082018861023a565b80875260208701906103aa8239604080516001600160a01b039097166020888101918252808952916100ef908961023a565b6040519788938385019a5180918c5e840190838201905f8252519283915e01015f815203601f19810186528561023a565b803b1561021557807f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc55807fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b5f80a28151156101ff575f92839251915af4156101f7575b51905ff08060601b156101f757807fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d610355806020527f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f60405fa1608052604051610138908161027282396080518160070152f35b3d5f803e3d5ffd5b505050341561018457636fb1b0e95f526004601cfd5b6368155f9a5f526004601cfd5b5f80fd5b634e487b7160e01b5f52604160045260245ffd5b601f909101601f19168101906001600160401b0382119082101761022657604052565b51906001600160a01b03821682036102225756fe6080604052337f00000000000000000000000000000000000000000000000000000000000000001460011461006b57365f80375f8036817f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc545af43d5f803e15610067573d5ff35b3d5ffd5b634f1ef2865f3560e01c0361012b5773ffffffffffffffffffffffffffffffffffffffff60043516803b1561011e57807f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc55807fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b5f80a260443590811561010857815f8093928193606483375af41561010057005b3d5f803e3d5ffd5b50503461011157005b636fb1b0e95f526004601cfd5b6368155f9a5f526004601cfd5b63d2b576ec5f526004601cfd60803460b757601f6102f838819003918201601f19168301916001600160401b0383118484101760bb5780849260209460405283398101031260b757516001600160a01b038116810360b7578060601b1560aa57807f9bc353c4ee8d049c7cb68b79467fc95d9015a8a82334bd0e61ce699e20cb5bd5555f7f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08180a360405161022890816100d08239f35b6354a567865f526004601cfd5b5f80fd5b634e487b7160e01b5f52604160045260245ffdfe6004361061021b575f3560e01c8080638da5cb5b146101f25763ad3cb1cc146101c057337f9bc353c4ee8d049c7cb68b79467fc95d9015a8a82334bd0e61ce699e20cb5bd554036101af5780639623609d146101295763f2fde38b1461006c57637352d91c5f526004601cfd5b6004357fffffffffffffffffffffffffffffffffffffffff00000000000000000000000073ffffffffffffffffffffffffffffffffffffffff82169160601b161561011c57807f9bc353c4ee8d049c7cb68b79467fc95d9015a8a82334bd0e61ce699e20cb5bd5547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a37f9bc353c4ee8d049c7cb68b79467fc95d9015a8a82334bd0e61ce699e20cb5bd555005b6354a567865f526004601cfd5b50634f1ef2865f527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc36016024602037604080525f807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601601c3473ffffffffffffffffffffffffffffffffffffffff600435165af1156101a757005b3d5f803e3d5ffd5b6332b2baa35f52336020526024601cfd5b60205f5260056020527f352e302e3000000000000000000000000000000000000000000000000000000060405260605ff35b7f9bc353c4ee8d049c7cb68b79467fc95d9015a8a82334bd0e61ce699e20cb5bd5545f5260205ff35b63ca0ad2605f526004601cfd"; +bytes constant FORGE_PROXY_BYTECODE = + hex"60a0806040526106a28038038091610017828561023a565b83398101906060818303126102225761002f8161025d565b9161003c6020830161025d565b604083015190926001600160401b038211610222570181601f820112156102225780516001600160401b0381116102265760405191610085601f8301601f19166020018461023a565b81835260208301936020838301011161022257815f926020809301865e83010152604051936101206102f86100bd602082018861023a565b80875260208701906103aa8239604080516001600160a01b039097166020888101918252808952916100ef908961023a565b6040519788938385019a5180918c5e840190838201905f8252519283915e01015f815203601f19810186528561023a565b803b1561021557807f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc55807fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b5f80a28151156101ff575f92839251915af4156101f7575b51905ff08060601b156101f757807fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d610355806020527f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f60405fa1608052604051610138908161027282396080518160070152f35b3d5f803e3d5ffd5b505050341561018457636fb1b0e95f526004601cfd5b6368155f9a5f526004601cfd5b5f80fd5b634e487b7160e01b5f52604160045260245ffd5b601f909101601f19168101906001600160401b0382119082101761022657604052565b51906001600160a01b03821682036102225756fe6080604052337f00000000000000000000000000000000000000000000000000000000000000001460011461006b57365f80375f8036817f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc545af43d5f803e15610067573d5ff35b3d5ffd5b634f1ef2865f3560e01c0361012b5773ffffffffffffffffffffffffffffffffffffffff60043516803b1561011e57807f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc55807fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b5f80a260443590811561010857815f8093928193606483375af41561010057005b3d5f803e3d5ffd5b50503461011157005b636fb1b0e95f526004601cfd5b6368155f9a5f526004601cfd5b63d2b576ec5f526004601cfd60803460b757601f6102f838819003918201601f19168301916001600160401b0383118484101760bb5780849260209460405283398101031260b757516001600160a01b038116810360b7578060601b1560aa57807f9bc353c4ee8d049c7cb68b79467fc95d9015a8a82334bd0e61ce699e20cb5bd5555f7f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08180a360405161022890816100d08239f35b6354a567865f526004601cfd5b5f80fd5b634e487b7160e01b5f52604160045260245ffdfe6004361061021b575f3560e01c8080638da5cb5b146101f25763ad3cb1cc146101c057337f9bc353c4ee8d049c7cb68b79467fc95d9015a8a82334bd0e61ce699e20cb5bd554036101af5780639623609d146101295763f2fde38b1461006c57637352d91c5f526004601cfd5b6004357fffffffffffffffffffffffffffffffffffffffff00000000000000000000000073ffffffffffffffffffffffffffffffffffffffff82169160601b161561011c57807f9bc353c4ee8d049c7cb68b79467fc95d9015a8a82334bd0e61ce699e20cb5bd5547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a37f9bc353c4ee8d049c7cb68b79467fc95d9015a8a82334bd0e61ce699e20cb5bd555005b6354a567865f526004601cfd5b50634f1ef2865f527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc36016024602037604080525f807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601601c3473ffffffffffffffffffffffffffffffffffffffff600435165af1156101a757005b3d5f803e3d5ffd5b6332b2baa35f52336020526024601cfd5b60205f5260056020527f352e302e3000000000000000000000000000000000000000000000000000000060405260605ff35b7f9bc353c4ee8d049c7cb68b79467fc95d9015a8a82334bd0e61ce699e20cb5bd5545f5260205ff35b63ca0ad2605f526004601cfd"; -bytes constant FORGE_PROXY_ADMIN_BYTECODE = hex"60803460b757601f6102f838819003918201601f19168301916001600160401b0383118484101760bb5780849260209460405283398101031260b757516001600160a01b038116810360b7578060601b1560aa57807f9bc353c4ee8d049c7cb68b79467fc95d9015a8a82334bd0e61ce699e20cb5bd5555f7f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08180a360405161022890816100d08239f35b6354a567865f526004601cfd5b5f80fd5b634e487b7160e01b5f52604160045260245ffdfe6004361061021b575f3560e01c8080638da5cb5b146101f25763ad3cb1cc146101c057337f9bc353c4ee8d049c7cb68b79467fc95d9015a8a82334bd0e61ce699e20cb5bd554036101af5780639623609d146101295763f2fde38b1461006c57637352d91c5f526004601cfd5b6004357fffffffffffffffffffffffffffffffffffffffff00000000000000000000000073ffffffffffffffffffffffffffffffffffffffff82169160601b161561011c57807f9bc353c4ee8d049c7cb68b79467fc95d9015a8a82334bd0e61ce699e20cb5bd5547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a37f9bc353c4ee8d049c7cb68b79467fc95d9015a8a82334bd0e61ce699e20cb5bd555005b6354a567865f526004601cfd5b50634f1ef2865f527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc36016024602037604080525f807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601601c3473ffffffffffffffffffffffffffffffffffffffff600435165af1156101a757005b3d5f803e3d5ffd5b6332b2baa35f52336020526024601cfd5b60205f5260056020527f352e302e3000000000000000000000000000000000000000000000000000000060405260605ff35b7f9bc353c4ee8d049c7cb68b79467fc95d9015a8a82334bd0e61ce699e20cb5bd5545f5260205ff35b63ca0ad2605f526004601cfd"; +bytes constant FORGE_PROXY_ADMIN_BYTECODE = + hex"60803460b757601f6102f838819003918201601f19168301916001600160401b0383118484101760bb5780849260209460405283398101031260b757516001600160a01b038116810360b7578060601b1560aa57807f9bc353c4ee8d049c7cb68b79467fc95d9015a8a82334bd0e61ce699e20cb5bd5555f7f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08180a360405161022890816100d08239f35b6354a567865f526004601cfd5b5f80fd5b634e487b7160e01b5f52604160045260245ffdfe6004361061021b575f3560e01c8080638da5cb5b146101f25763ad3cb1cc146101c057337f9bc353c4ee8d049c7cb68b79467fc95d9015a8a82334bd0e61ce699e20cb5bd554036101af5780639623609d146101295763f2fde38b1461006c57637352d91c5f526004601cfd5b6004357fffffffffffffffffffffffffffffffffffffffff00000000000000000000000073ffffffffffffffffffffffffffffffffffffffff82169160601b161561011c57807f9bc353c4ee8d049c7cb68b79467fc95d9015a8a82334bd0e61ce699e20cb5bd5547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a37f9bc353c4ee8d049c7cb68b79467fc95d9015a8a82334bd0e61ce699e20cb5bd555005b6354a567865f526004601cfd5b50634f1ef2865f527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc36016024602037604080525f807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601601c3473ffffffffffffffffffffffffffffffffffffffff600435165af1156101a757005b3d5f803e3d5ffd5b6332b2baa35f52336020526024601cfd5b60205f5260056020527f352e302e3000000000000000000000000000000000000000000000000000000060405260605ff35b7f9bc353c4ee8d049c7cb68b79467fc95d9015a8a82334bd0e61ce699e20cb5bd5545f5260205ff35b63ca0ad2605f526004601cfd"; -bytes constant PROXY_FORGE_BYTECODE = hex"60a08060405234603e57306080526113f0908161004382396080518181816101da0152818161073a0152818161089e0152818161092e01526109be0152f35b5f80fdfe6080806040526004361015610012575f80fd5b5f3560e01c90816314afd79e14610a46575080632abbef15146109e75780633729f922146109575780634314f1201461090a578063525542d614610831578063545e7c61146106e657806374a8f1031461062c5780638b4872b8146105c657806392d9f765146104ee5780639623609d1461044357806399a88ec414610346578063a97b90d5146101525763f00d4b5d146100ab575f80fd5b60407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261014e576100dd610aa9565b6100e5610acc565b9063c12fa8d68160601b175f5260205f203381540361013d578260601b15610130578290557f1b185f8166e5b540f041c2132c66d6c691b0674cd3a95ccc9592a43dd64ad6e25f80a3005b63074b52c95f526004601cfd5b6332b2baa35f52336020526024601cfd5b5f80fd5b60807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261014e57610184610aa9565b61018c610acc565b6044359060643567ffffffffffffffff811161014e576101b0903690600401610aef565b8260601b15610130578360601c338114901517156103395761022f610252926102036040519384927f00000000000000000000000000000000000000000000000000000000000000008a60208601610bac565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08101835282610c13565b6040516106a16102426020820183610c13565b808252610d4f6020830139610c81565b34471061032c578051839160200134f591823b158360601b9081151761031f5760209463c12fa8d69284867fd283ed05905c0eb69fe3ef042c6ad706d8d9c75b138624098de540fa2c011a055f80a463a1337b4d82175f5280865f2055847f3684250ce1e33b790ed973c23080f312db0adb21a6d98c61a5c9ff99e4babc175f80a3175f5280835f2055817f1b185f8166e5b540f041c2132c66d6c691b0674cd3a95ccc9592a43dd64ad6e25f80a373ffffffffffffffffffffffffffffffffffffffff60405191168152f35b63a28c24735f526004601cfd5b63f4d678b85f526004601cfd5b6381e69d9b5f526004601cfd5b60407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261014e57610378610aa9565b610380610acc565b9061038a81610cdf565b8160601b63c12fa8d681175f523360205f20540361013d5763a1337b4d175f5260205f209083825414610436575f60405191639623609d83528460208401528560408401526060808401528160808401528136813760a43891601c85019034905af11561041b57508290557f3684250ce1e33b790ed973c23080f312db0adb21a6d98c61a5c9ff99e4babc175f80a3005b3d15610429573d5f823e3d90fd5b6355299b495f526004601cfd5b63dcd488e35f526004601cfd5b61044c36610b1d565b90610458849394610cdf565b8360601b63c12fa8d681175f523360205f20540361013d5763a1337b4d175f5260205f209285845414610436575f918160405194639623609d865287602087015288604087015260608087015281608087015260a086013760a438920190601c85019034905af11561041b57508290557f3684250ce1e33b790ed973c23080f312db0adb21a6d98c61a5c9ff99e4babc175f80a3005b3461014e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261014e5760043567ffffffffffffffff8110156105b95760946001533060601b6002525f6080821061059c5781805b61058d5750806020926017926080016016538160031b610100031b82525b8060d6015f53015f2073ffffffffffffffffffffffffffffffffffffffff60405191168152f35b60019091019060081c80610548565b60179082602093156105b1575b601653610566565b5060806105a9565b63756688fe5f526004601cfd5b3461014e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261014e5763a1337b4d610602610aa9565b60601b175f526020805f205473ffffffffffffffffffffffffffffffffffffffff60405191168152f35b60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261014e5761065e610aa9565b61066781610cdf565b8160601b9063c12fa8d682175f5260205f20903382540361013d575f6040519163f2fde38b83523360208401526024389183601c8601915af1156106de5750905f63a1337b4d9255175f525f60208120557f4b0f58242c231a580ee42fe1dd7389c8e7520590afe33c21809305a6014703a15f80a2005b3d5f823e3d90fd5b60407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261014e57610718610aa9565b610720610acc565b8060601b156101305761076360405161022f816102035f367f00000000000000000000000000000000000000000000000000000000000000008960208601610bac565b34471061032c5760208151910134f090813b15928260601b9384151761031f5760209363c12fa8d6915f84867fd283ed05905c0eb69fe3ef042c6ad706d8d9c75b138624098de540fa2c011a058380a463a1337b4d82175f5280865f2055847f3684250ce1e33b790ed973c23080f312db0adb21a6d98c61a5c9ff99e4babc175f80a3175f5280835f2055817f1b185f8166e5b540f041c2132c66d6c691b0674cd3a95ccc9592a43dd64ad6e25f80a373ffffffffffffffffffffffffffffffffffffffff60405191168152f35b3461014e5760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261014e57610868610aa9565b6044359067ffffffffffffffff821161014e5761022f610203916108936108c7943690600401610aef565b6040949194519485937f00000000000000000000000000000000000000000000000000000000000000009060208601610bac565b6020815191012060ff5f536035523060601b600152602435601552602060555f205f60355273ffffffffffffffffffffffffffffffffffffffff60405191168152f35b61091336610b1d565b8260601b156101305761022f610763926102036040519384927f00000000000000000000000000000000000000000000000000000000000000008960208601610bac565b60607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261014e57610989610aa9565b610991610acc565b604435908060601b15610130578160601c338114901517156103395761025260405161022f816102035f367f00000000000000000000000000000000000000000000000000000000000000008a60208601610bac565b3461014e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261014e576020610a28610a23610aa9565b610cdf565b73ffffffffffffffffffffffffffffffffffffffff60405191168152f35b3461014e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261014e5760209063c12fa8d6610a85610aa9565b60601b175f5273ffffffffffffffffffffffffffffffffffffffff825f2054168152f35b6004359073ffffffffffffffffffffffffffffffffffffffff8216820361014e57565b6024359073ffffffffffffffffffffffffffffffffffffffff8216820361014e57565b9181601f8401121561014e5782359167ffffffffffffffff831161014e576020838186019501011161014e57565b60607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc82011261014e5760043573ffffffffffffffffffffffffffffffffffffffff8116810361014e579160243573ffffffffffffffffffffffffffffffffffffffff8116810361014e57916044359067ffffffffffffffff821161014e57610ba891600401610aef565b9091565b9293806080957fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09473ffffffffffffffffffffffffffffffffffffffff601f95168752602087015260606040870152816060870152868601375f8582860101520116010190565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117610c5457604052565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b610cdd906020808095946040519684889551918291018487015e8401908282015f8152815193849201905e01015f8152037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08101845283610c13565b565b6001906094825360601b6002525f9060808110610d33579081825b610d215760179250816080016016538160031b610100031b82525b8060d6015f53015f2090565b9060019260081c928391019190610cfa565b908160179215610d46575b601653610d15565b506080610d3e56fe60a0806040526106a18038038091610017828561023a565b83398101906060818303126102225761002f8161025d565b9161003c6020830161025d565b604083015190926001600160401b038211610222570181601f820112156102225780516001600160401b0381116102265760405191610085601f8301601f19166020018461023a565b81835260208301936020838301011161022257815f926020809301865e83010152604051936101206102fb6100bd602082018861023a565b80875260208701906103a68239604080516001600160a01b039097166020888101918252808952916100ef908961023a565b6040519788938385019a5180918c5e840190838201905f8252519283915e01015f815203601f19810186528561023a565b803b1561021557807f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc55807fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b5f80a28151156101ff575f92389251915af4156101f7575b51905ff08060601b156101f757807fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d610355806020527f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f60405fa1608052604051610134908161027282396080518160060152f35b3d5f803e3d5ffd5b505050341561018457636fb1b0e95f526004601cfd5b6368155f9a5f526004601cfd5b5f80fd5b634e487b7160e01b5f52604160045260245ffd5b601f909101601f19168101906001600160401b0382119082101761022657604052565b51906001600160a01b03821682036102225756fe60806040527f0000000000000000000000000000000000000000000000000000000000000000331461006857365f80375f3836827f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc545af43d5f803e15610064573d5ff35b3d5ffd5b634f1ef2865f3560e01c036101275773ffffffffffffffffffffffffffffffffffffffff60043516803b1561011a57807f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc55807fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b5f80a260443590811561010457815f92606484378238925af4156100fc57005b3d5f803e3d5ffd5b50503461010d57005b636fb1b0e95f526004601cfd5b6368155f9a5f526004601cfd5b63d2b576ec5f526004601cfd60803460b757601f6102fb38819003918201601f19168301916001600160401b0383118484101760bb5780849260209460405283398101031260b757516001600160a01b038116810360b7578060601b1560aa57807f9bc353c4ee8d049c7cb68b79467fc95d9015a8a82334bd0e61ce699e20cb5bd5555f7f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08180a360405161022b90816100d08239f35b6354a567865f526004601cfd5b5f80fd5b634e487b7160e01b5f52604160045260245ffdfe6004361061021e575f3560e01c8080638da5cb5b146101f55763ad3cb1cc146101c357337f9bc353c4ee8d049c7cb68b79467fc95d9015a8a82334bd0e61ce699e20cb5bd554036101b25780639623609d146101295763f2fde38b1461006c57637352d91c5f526004601cfd5b6004357fffffffffffffffffffffffffffffffffffffffff00000000000000000000000073ffffffffffffffffffffffffffffffffffffffff82169160601b161561011c57807f9bc353c4ee8d049c7cb68b79467fc95d9015a8a82334bd0e61ce699e20cb5bd5547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a37f9bc353c4ee8d049c7cb68b79467fc95d9015a8a82334bd0e61ce699e20cb5bd555005b6354a567865f526004601cfd5b50634f1ef2865f527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc36016024602037604080525f387fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601601c3473ffffffffffffffffffffffffffffffffffffffff600435165af1156101a757005b6040513d5f823e3d90fd5b6332b2baa35f52336020526024601cfd5b60205f5260056020527f352e302e3000000000000000000000000000000000000000000000000000000060405260605ff35b7f9bc353c4ee8d049c7cb68b79467fc95d9015a8a82334bd0e61ce699e20cb5bd5545f5260205ff35b63ca0ad2605f526004601cfd"; +bytes constant PROXY_FORGE_BYTECODE = + hex"60a08060405234603e57306080526113f0908161004382396080518181816101da0152818161073a0152818161089e0152818161092e01526109be0152f35b5f80fdfe6080806040526004361015610012575f80fd5b5f3560e01c90816314afd79e14610a46575080632abbef15146109e75780633729f922146109575780634314f1201461090a578063525542d614610831578063545e7c61146106e657806374a8f1031461062c5780638b4872b8146105c657806392d9f765146104ee5780639623609d1461044357806399a88ec414610346578063a97b90d5146101525763f00d4b5d146100ab575f80fd5b60407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261014e576100dd610aa9565b6100e5610acc565b9063c12fa8d68160601b175f5260205f203381540361013d578260601b15610130578290557f1b185f8166e5b540f041c2132c66d6c691b0674cd3a95ccc9592a43dd64ad6e25f80a3005b63074b52c95f526004601cfd5b6332b2baa35f52336020526024601cfd5b5f80fd5b60807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261014e57610184610aa9565b61018c610acc565b6044359060643567ffffffffffffffff811161014e576101b0903690600401610aef565b8260601b15610130578360601c338114901517156103395761022f610252926102036040519384927f00000000000000000000000000000000000000000000000000000000000000008a60208601610bac565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08101835282610c13565b6040516106a16102426020820183610c13565b808252610d4f6020830139610c81565b34471061032c578051839160200134f591823b158360601b9081151761031f5760209463c12fa8d69284867fd283ed05905c0eb69fe3ef042c6ad706d8d9c75b138624098de540fa2c011a055f80a463a1337b4d82175f5280865f2055847f3684250ce1e33b790ed973c23080f312db0adb21a6d98c61a5c9ff99e4babc175f80a3175f5280835f2055817f1b185f8166e5b540f041c2132c66d6c691b0674cd3a95ccc9592a43dd64ad6e25f80a373ffffffffffffffffffffffffffffffffffffffff60405191168152f35b63a28c24735f526004601cfd5b63f4d678b85f526004601cfd5b6381e69d9b5f526004601cfd5b60407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261014e57610378610aa9565b610380610acc565b9061038a81610cdf565b8160601b63c12fa8d681175f523360205f20540361013d5763a1337b4d175f5260205f209083825414610436575f60405191639623609d83528460208401528560408401526060808401528160808401528136813760a43891601c85019034905af11561041b57508290557f3684250ce1e33b790ed973c23080f312db0adb21a6d98c61a5c9ff99e4babc175f80a3005b3d15610429573d5f823e3d90fd5b6355299b495f526004601cfd5b63dcd488e35f526004601cfd5b61044c36610b1d565b90610458849394610cdf565b8360601b63c12fa8d681175f523360205f20540361013d5763a1337b4d175f5260205f209285845414610436575f918160405194639623609d865287602087015288604087015260608087015281608087015260a086013760a438920190601c85019034905af11561041b57508290557f3684250ce1e33b790ed973c23080f312db0adb21a6d98c61a5c9ff99e4babc175f80a3005b3461014e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261014e5760043567ffffffffffffffff8110156105b95760946001533060601b6002525f6080821061059c5781805b61058d5750806020926017926080016016538160031b610100031b82525b8060d6015f53015f2073ffffffffffffffffffffffffffffffffffffffff60405191168152f35b60019091019060081c80610548565b60179082602093156105b1575b601653610566565b5060806105a9565b63756688fe5f526004601cfd5b3461014e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261014e5763a1337b4d610602610aa9565b60601b175f526020805f205473ffffffffffffffffffffffffffffffffffffffff60405191168152f35b60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261014e5761065e610aa9565b61066781610cdf565b8160601b9063c12fa8d682175f5260205f20903382540361013d575f6040519163f2fde38b83523360208401526024389183601c8601915af1156106de5750905f63a1337b4d9255175f525f60208120557f4b0f58242c231a580ee42fe1dd7389c8e7520590afe33c21809305a6014703a15f80a2005b3d5f823e3d90fd5b60407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261014e57610718610aa9565b610720610acc565b8060601b156101305761076360405161022f816102035f367f00000000000000000000000000000000000000000000000000000000000000008960208601610bac565b34471061032c5760208151910134f090813b15928260601b9384151761031f5760209363c12fa8d6915f84867fd283ed05905c0eb69fe3ef042c6ad706d8d9c75b138624098de540fa2c011a058380a463a1337b4d82175f5280865f2055847f3684250ce1e33b790ed973c23080f312db0adb21a6d98c61a5c9ff99e4babc175f80a3175f5280835f2055817f1b185f8166e5b540f041c2132c66d6c691b0674cd3a95ccc9592a43dd64ad6e25f80a373ffffffffffffffffffffffffffffffffffffffff60405191168152f35b3461014e5760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261014e57610868610aa9565b6044359067ffffffffffffffff821161014e5761022f610203916108936108c7943690600401610aef565b6040949194519485937f00000000000000000000000000000000000000000000000000000000000000009060208601610bac565b6020815191012060ff5f536035523060601b600152602435601552602060555f205f60355273ffffffffffffffffffffffffffffffffffffffff60405191168152f35b61091336610b1d565b8260601b156101305761022f610763926102036040519384927f00000000000000000000000000000000000000000000000000000000000000008960208601610bac565b60607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261014e57610989610aa9565b610991610acc565b604435908060601b15610130578160601c338114901517156103395761025260405161022f816102035f367f00000000000000000000000000000000000000000000000000000000000000008a60208601610bac565b3461014e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261014e576020610a28610a23610aa9565b610cdf565b73ffffffffffffffffffffffffffffffffffffffff60405191168152f35b3461014e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261014e5760209063c12fa8d6610a85610aa9565b60601b175f5273ffffffffffffffffffffffffffffffffffffffff825f2054168152f35b6004359073ffffffffffffffffffffffffffffffffffffffff8216820361014e57565b6024359073ffffffffffffffffffffffffffffffffffffffff8216820361014e57565b9181601f8401121561014e5782359167ffffffffffffffff831161014e576020838186019501011161014e57565b60607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc82011261014e5760043573ffffffffffffffffffffffffffffffffffffffff8116810361014e579160243573ffffffffffffffffffffffffffffffffffffffff8116810361014e57916044359067ffffffffffffffff821161014e57610ba891600401610aef565b9091565b9293806080957fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09473ffffffffffffffffffffffffffffffffffffffff601f95168752602087015260606040870152816060870152868601375f8582860101520116010190565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117610c5457604052565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b610cdd906020808095946040519684889551918291018487015e8401908282015f8152815193849201905e01015f8152037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08101845283610c13565b565b6001906094825360601b6002525f9060808110610d33579081825b610d215760179250816080016016538160031b610100031b82525b8060d6015f53015f2090565b9060019260081c928391019190610cfa565b908160179215610d46575b601653610d15565b506080610d3e56fe60a0806040526106a18038038091610017828561023a565b83398101906060818303126102225761002f8161025d565b9161003c6020830161025d565b604083015190926001600160401b038211610222570181601f820112156102225780516001600160401b0381116102265760405191610085601f8301601f19166020018461023a565b81835260208301936020838301011161022257815f926020809301865e83010152604051936101206102fb6100bd602082018861023a565b80875260208701906103a68239604080516001600160a01b039097166020888101918252808952916100ef908961023a565b6040519788938385019a5180918c5e840190838201905f8252519283915e01015f815203601f19810186528561023a565b803b1561021557807f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc55807fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b5f80a28151156101ff575f92389251915af4156101f7575b51905ff08060601b156101f757807fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d610355806020527f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f60405fa1608052604051610134908161027282396080518160060152f35b3d5f803e3d5ffd5b505050341561018457636fb1b0e95f526004601cfd5b6368155f9a5f526004601cfd5b5f80fd5b634e487b7160e01b5f52604160045260245ffd5b601f909101601f19168101906001600160401b0382119082101761022657604052565b51906001600160a01b03821682036102225756fe60806040527f0000000000000000000000000000000000000000000000000000000000000000331461006857365f80375f3836827f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc545af43d5f803e15610064573d5ff35b3d5ffd5b634f1ef2865f3560e01c036101275773ffffffffffffffffffffffffffffffffffffffff60043516803b1561011a57807f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc55807fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b5f80a260443590811561010457815f92606484378238925af4156100fc57005b3d5f803e3d5ffd5b50503461010d57005b636fb1b0e95f526004601cfd5b6368155f9a5f526004601cfd5b63d2b576ec5f526004601cfd60803460b757601f6102fb38819003918201601f19168301916001600160401b0383118484101760bb5780849260209460405283398101031260b757516001600160a01b038116810360b7578060601b1560aa57807f9bc353c4ee8d049c7cb68b79467fc95d9015a8a82334bd0e61ce699e20cb5bd5555f7f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08180a360405161022b90816100d08239f35b6354a567865f526004601cfd5b5f80fd5b634e487b7160e01b5f52604160045260245ffdfe6004361061021e575f3560e01c8080638da5cb5b146101f55763ad3cb1cc146101c357337f9bc353c4ee8d049c7cb68b79467fc95d9015a8a82334bd0e61ce699e20cb5bd554036101b25780639623609d146101295763f2fde38b1461006c57637352d91c5f526004601cfd5b6004357fffffffffffffffffffffffffffffffffffffffff00000000000000000000000073ffffffffffffffffffffffffffffffffffffffff82169160601b161561011c57807f9bc353c4ee8d049c7cb68b79467fc95d9015a8a82334bd0e61ce699e20cb5bd5547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a37f9bc353c4ee8d049c7cb68b79467fc95d9015a8a82334bd0e61ce699e20cb5bd555005b6354a567865f526004601cfd5b50634f1ef2865f527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc36016024602037604080525f387fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601601c3473ffffffffffffffffffffffffffffffffffffffff600435165af1156101a757005b6040513d5f823e3d90fd5b6332b2baa35f52336020526024601cfd5b60205f5260056020527f352e302e3000000000000000000000000000000000000000000000000000000060405260605ff35b7f9bc353c4ee8d049c7cb68b79467fc95d9015a8a82334bd0e61ce699e20cb5bd5545f5260205ff35b63ca0ad2605f526004601cfd"; diff --git a/src/CreateX.sol b/src/CreateX.sol index 9df8247..1785392 100644 --- a/src/CreateX.sol +++ b/src/CreateX.sol @@ -5,394 +5,396 @@ pragma solidity ^0.8.30; /// @notice Provides deterministic deployments for CREATE, CREATE2, CREATE3, and EIP-1167 Minimal Proxy patterns /// @author fomoweth library CreateX { - //──────────────────────────────────────────────────────────────────────────────// - // Custom Errors // - //──────────────────────────────────────────────────────────────────────────────// - - /// @notice Thrown when contract creation fails during deployment - error ContractCreationFailed(); - - /// @notice Thrown when the intermediate proxy creation fails during CREATE3 deployment - error ProxyCreationFailed(); - - /// @notice Thrown when the deploying contract has insufficient balance for the deployment - error InsufficientBalance(); - - /// @notice Thrown when provided implementation address is zero address for proxy deployments - error InvalidImplementation(); - - /// @notice Thrown when provided nonce exceeds the maximum allowed by EIP-2681 (2^64-1) - error InvalidNonce(); - - //──────────────────────────────────────────────────────────────────────────────// - // CREATE // - //──────────────────────────────────────────────────────────────────────────────// - - /// @notice Deploys a contract using CREATE opcode - /// @param initCode Complete contract bytecode including constructor arguments - /// @return instance Address of the deployed contract - function create(bytes memory initCode) internal returns (address instance) { - return create(initCode, uint256(0)); - } - - /// @notice Deploys a contract using CREATE opcode - /// @param initCode Complete contract bytecode including constructor arguments - /// @param value Amount of ETH to send to the contract during deployment - /// @return instance Address of the deployed contract - function create(bytes memory initCode, uint256 value) internal returns (address instance) { - assembly ("memory-safe") { - // Verify current contract has sufficient balance to send the specified value - if lt(selfbalance(), value) { - mstore(0x00, 0xf4d678b8) // InsufficientBalance() - revert(0x1c, 0x04) - } - - // Deploy using CREATE opcode - instance := create(value, add(initCode, 0x20), mload(initCode)) - - // Verify deployed contract address is not zero and contains code - if or(iszero(shl(0x60, instance)), iszero(extcodesize(instance))) { - mstore(0x00, 0xa28c2473) // ContractCreationFailed() - revert(0x1c, 0x04) - } - } - } - - /// @notice Computes the predicted address of a contract deployed using CREATE opcode - /// @param nonce Nonce value of the deployer at the time of deployment - /// @return predicted Predicted contract address - function computeCreateAddress(uint256 nonce) internal view returns (address predicted) { - return computeCreateAddress(address(this), nonce); - } - - /// @notice Computes the predicted address of a contract deployed using CREATE opcode - /// @param deployer Address performing the deployment - /// @param nonce Nonce value of the deployer at the time of deployment - /// @return predicted Predicted contract address - function computeCreateAddress(address deployer, uint256 nonce) internal pure returns (address predicted) { - assembly ("memory-safe") { - // Enforce EIP‑2681 nonce constraint (https://eips.ethereum.org/EIPS/eip-2681) - if iszero(lt(nonce, 0xffffffffffffffff)) { - mstore(0x00, 0x756688fe) // InvalidNonce() - revert(0x1c, 0x04) - } - - // Construct RLP structure - mstore8(0x01, 0x94) // Store RLP prefix for address - mstore(0x02, shl(0x60, deployer)) // Store deployer address - - let offset // Variable to track additional bytes needed for large nonces - - // prettier-ignore - // Encode nonce according to RLP rules - for {} 0x01 {} { - // Single byte encoding case (0x00...0x7f) - if lt(nonce, 0x80) { - // Nonce value 0 is encoded as empty string, so convert to 0x80 - if iszero(nonce) { nonce := 0x80 } - mstore8(0x16, nonce) // Store prefix or nonce directly - break - } - - // Multi-byte encoding case (≥ 0x80) - // Compute number of bytes needed to encode nonce - for { let i := nonce } i { i := shr(0x08, i) offset := add(offset, 0x01) } {} - - // Store length prefix indicating number of bytes used for nonce (0x80 + number of bytes) - mstore8(0x16, add(0x80, offset)) - // Store nonce value in big-endian format at appropriate position - mstore(0x17, shl(sub(0x100, mul(offset, 0x08)), nonce)) - break - } - - // Complete RLP structure and compute predicted address - mstore8(0x00, add(0xd6, offset)) // Set RLP prefix for total length - predicted := keccak256(0x00, add(offset, 0x17)) // Compute predicted address - } - } - - //──────────────────────────────────────────────────────────────────────────────// - // CREATE2 // - //──────────────────────────────────────────────────────────────────────────────// - - /// @notice Deploys a contract using CREATE2 opcode for deterministic addressing - /// @param initCode Complete contract bytecode including constructor arguments - /// @param salt 32-byte value used in address derivation - /// @return instance Address of the deployed contract - function create2(bytes memory initCode, bytes32 salt) internal returns (address instance) { - return create2(initCode, salt, uint256(0)); - } - - /// @notice Deploys a contract using CREATE2 opcode for deterministic addressing - /// @param initCode Complete contract bytecode including constructor arguments - /// @param salt 32-byte value used in address derivation - /// @param value Amount of ETH to send to the contract during deployment - /// @return instance Address of the deployed contract - function create2(bytes memory initCode, bytes32 salt, uint256 value) internal returns (address instance) { - assembly ("memory-safe") { - // Verify current contract has sufficient balance to send the specified value - if lt(selfbalance(), value) { - mstore(0x00, 0xf4d678b8) // InsufficientBalance() - revert(0x1c, 0x04) - } - - // Deploy using CREATE2 opcode - instance := create2(value, add(initCode, 0x20), mload(initCode), salt) - - // Verify deployed contract address is not zero and contains code - if or(iszero(shl(0x60, instance)), iszero(extcodesize(instance))) { - mstore(0x00, 0xa28c2473) // ContractCreationFailed() - revert(0x1c, 0x04) - } - } - } - - /// @notice Computes the predicted address of a contract deployed using CREATE2 opcode - /// @param initCodeHash keccak256 hash of initialization code - /// @param salt 32-byte value used in address derivation - /// @return predicted Predicted contract address - function computeCreate2Address(bytes32 initCodeHash, bytes32 salt) internal view returns (address predicted) { - return computeCreate2Address(address(this), initCodeHash, salt); - } - - /// @notice Computes the predicted address of a contract deployed using CREATE2 opcode - /// @param deployer Address performing the deployment - /// @param initCodeHash keccak256 hash of initialization code - /// @param salt 32-byte value used in address derivation - /// @return predicted Predicted contract address - function computeCreate2Address( - address deployer, - bytes32 initCodeHash, - bytes32 salt - ) internal pure returns (address predicted) { - assembly ("memory-safe") { - mstore8(0x00, 0xff) // Store CREATE2 prefix - mstore(0x35, initCodeHash) // Store creation bytecode hash - mstore(0x01, shl(0x60, deployer)) // Store deployer address - mstore(0x15, salt) // Store salt - predicted := keccak256(0x00, 0x55) // Compute predicted address - mstore(0x35, 0x00) // Clear hash storage - } - } - - //──────────────────────────────────────────────────────────────────────────────// - // CREATE3 // - //──────────────────────────────────────────────────────────────────────────────// - - /// @notice Deploys a contract using CREATE3 pattern for chain-agnostic deterministic addressing - /// @param initCode Complete contract bytecode including constructor arguments - /// @param salt 32-byte value used in address derivation - /// @return instance Address of the deployed contract - function create3(bytes memory initCode, bytes32 salt) internal returns (address instance) { - return create3(initCode, salt, uint256(0)); - } - - /// @notice Deploys a contract using CREATE3 pattern for chain-agnostic deterministic addressing - /// @param initCode Complete contract bytecode including constructor arguments - /// @param salt 32-byte value used in address derivation - /// @param value Amount of ETH to send to the final contract - /// @return instance Address of the deployed contract - function create3(bytes memory initCode, bytes32 salt, uint256 value) internal returns (address instance) { - assembly ("memory-safe") { - // Verify current contract has sufficient balance to send the specified value - if lt(selfbalance(), value) { - mstore(0x00, 0xf4d678b8) // InsufficientBalance() - revert(0x1c, 0x04) - } - - // Phase 1: Deploy intermediate proxy using CREATE2 opcode - // Store creation bytecode for proxy - // This bytecode creates a proxy that will use CREATE opcode to deploy final contract - mstore(0x00, 0x67363d3d37363d34f03d5260086018f3) - - // Deploy proxy at deterministic address using CREATE2 opcode - let proxy := create2(0x00, 0x10, 0x10, salt) - - // Verify deployed proxy address is not zero address - if iszero(shl(0x60, proxy)) { - mstore(0x00, 0xd49e7d74) // ProxyCreationFailed() - revert(0x1c, 0x04) - } - - // Phase 2: Perform inner deployment via proxy using CREATE opcode - // The actual contract will be deployed by proxy at nonce 1 - - // 0xd6 = 0xc0 (short RLP prefix) + 0x16 (length of: 0x94 ++ proxy ++ 0x01) - // 0x94 = 0x80 + 0x14 (length of an address, 20 bytes, in hex) - mstore(0x14, proxy) // Store proxy address - mstore(0x00, 0xd694) // Store RLP encoding prefix for address (0xd6 + 0x94) - mstore8(0x34, 0x01) // Store proxy nonce - instance := keccak256(0x1e, 0x17) // Compute final contract address - - // Call proxy with final contract's initialization code to deploy the actual contract using CREATE opcode - if iszero( - mul( - extcodesize(instance), // Verify final contract contains code after deployment - call(gas(), proxy, value, add(initCode, 0x20), mload(initCode), codesize(), 0x00) - ) - ) { - mstore(0x00, 0xa28c2473) // ContractCreationFailed() - revert(0x1c, 0x04) - } - } - } - - /// @notice Computes the predicted address of a contract deployed using CREATE3 pattern - /// @param salt 32-byte value used in address derivation - /// @return predicted Predicted contract address - function computeCreate3Address(bytes32 salt) internal view returns (address predicted) { - return computeCreate3Address(address(this), salt); - } - - /// @notice Computes the predicted address of a contract deployed using CREATE3 pattern - /// @param deployer Address performing the deployment - /// @param salt 32-byte value used in address derivation - /// @return predicted Predicted contract address - function computeCreate3Address(address deployer, bytes32 salt) internal pure returns (address predicted) { - assembly ("memory-safe") { - // Compute proxy address using CREATE2 opcode - let ptr := mload(0x40) // Cache free memory pointer - mstore(0x00, deployer) // Store deployer address - mstore8(0x0b, 0xff) // Store CREATE2 prefix - mstore(0x20, salt) // Store salt - // Store hash of minimal proxy creation bytecode - // Equivalent to keccak256(abi.encodePacked(hex"67363d3d37363d34f03d5260086018f3")) - mstore(0x40, 0x21c35dbe1b344a2488cf3321d6ce542f8e9f305544ff09e4993a62319a497c1f) - mstore(0x14, keccak256(0x0b, 0x55)) // Compute and store proxy address - mstore(0x40, ptr) // Restore free memory pointer - // Compute final contract address using CREATE opcode - // 0xd6 = 0xc0 (short RLP prefix) + 0x16 (length of: 0x94 ++ proxy ++ 0x01) - // 0x94 = 0x80 + 0x14 (length of an address, 20 bytes, in hex) - mstore(0x00, 0xd694) // Store RLP encoding prefix for proxy deployment - mstore8(0x34, 0x01) // Store proxy nonce - predicted := keccak256(0x1e, 0x17) // Compute predicted address - } - } - - //──────────────────────────────────────────────────────────────────────────────// - // EIP-1167: Minimal Proxy // - //──────────────────────────────────────────────────────────────────────────────// - - /// @notice Deploys an EIP-1167 minimal proxy contract using CREATE opcode - /// @param implementation Address of the logic contract for delegation - /// @return instance Address of the deployed proxy - function clone(address implementation) internal returns (address instance) { - return clone(implementation, uint256(0)); - } - - /// @notice Deploys an EIP-1167 minimal proxy contract using CREATE opcode - /// @param implementation Address of the logic contract for delegation - /// @param value Amount of ETH to send to the proxy during deployment - /// @return instance Address of the deployed proxy - function clone(address implementation, uint256 value) internal returns (address instance) { - assembly ("memory-safe") { - // Verify implementation address is not zero and contains code - if or(iszero(shl(0x60, implementation)), iszero(extcodesize(implementation))) { - mstore(0x00, 0x68155f9a) // InvalidImplementation() - revert(0x1c, 0x04) - } - - // Verify current contract has sufficient balance to send the specified value - if lt(selfbalance(), value) { - mstore(0x00, 0xf4d678b8) // InsufficientBalance() - revert(0x1c, 0x04) - } - - // Construct EIP-1167 minimal proxy bytecode - // Clean upper 96 bits of implementation address and pack with bytecode before address - mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000)) - // Pack remaining 17 bytes of implementation address with bytecode after address - mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3)) - - // Deploy proxy using CREATE opcode - instance := create(value, 0x09, 0x37) - - // Verify deployed contract address is not zero and contains code - if or(iszero(shl(0x60, instance)), iszero(extcodesize(instance))) { - mstore(0x00, 0xa28c2473) // ContractCreationFailed() - revert(0x1c, 0x04) - } - } - } - - /// @notice Deploys an EIP-1167 minimal proxy contract deterministically using CREATE2 opcode - /// @param implementation Address of the logic contract for delegation - /// @param salt 32-byte value used in address derivation - /// @return instance Address of the deployed proxy - function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) { - return cloneDeterministic(implementation, salt, uint256(0)); - } - - /// @notice Deploys an EIP-1167 minimal proxy contract deterministically using CREATE2 opcode - /// @param implementation Address of the logic contract for delegation - /// @param salt 32-byte value used in address derivation - /// @param value Amount of ETH to send to the proxy during deployment - /// @return instance Address of the deployed proxy - function cloneDeterministic( - address implementation, - bytes32 salt, - uint256 value - ) internal returns (address instance) { - assembly ("memory-safe") { - // Verify implementation address is not zero and contains code - if or(iszero(shl(0x60, implementation)), iszero(extcodesize(implementation))) { - mstore(0x00, 0x68155f9a) // InvalidImplementation() - revert(0x1c, 0x04) - } - - // Verify current contract has sufficient balance to send the specified value - if lt(selfbalance(), value) { - mstore(0x00, 0xf4d678b8) // InsufficientBalance() - revert(0x1c, 0x04) - } - - // Construct EIP-1167 minimal proxy bytecode - // Clean upper 96 bits of implementation address and pack with bytecode before address - mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000)) - // Pack remaining 17 bytes of implementation address with bytecode after address - mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3)) - - // Deploy proxy using CREATE2 opcode - instance := create2(value, 0x09, 0x37, salt) - - // Verify deployed contract address is not zero and contains code - if or(iszero(shl(0x60, instance)), iszero(extcodesize(instance))) { - mstore(0x00, 0xa28c2473) // ContractCreationFailed() - revert(0x1c, 0x04) - } - } - } - - /// @notice Computes the predicted address of a contract deployed using EIP-1167 pattern - /// @param implementation Address of the logic contract for delegation - /// @param salt 32-byte value used in address derivation - /// @return predicted Predicted contract address - function computeCloneDeterministicAddress( - address implementation, - bytes32 salt - ) internal view returns (address predicted) { - return computeCloneDeterministicAddress(address(this), implementation, salt); - } - - /// @notice Computes the predicted address of a contract deployed using EIP-1167 pattern - /// @param deployer Address performing the deployment - /// @param implementation Address of the logic contract for delegation - /// @param salt 32-byte value used in address derivation - /// @return predicted Predicted contract address - function computeCloneDeterministicAddress( - address deployer, - address implementation, - bytes32 salt - ) internal pure returns (address predicted) { - assembly ("memory-safe") { - let ptr := mload(0x40) // Cache free memory pointer - mstore(add(ptr, 0x58), salt) // Store salt - mstore(add(ptr, 0x38), deployer) // Store deployer address - mstore(add(ptr, 0x24), 0x5af43d82803e903d91602b57fd5bf3ff) // Store runtime code suffix - mstore(add(ptr, 0x14), implementation) // Store implementation address - mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73) // Store creation code and runtime code prefix - mstore(add(ptr, 0x78), keccak256(add(ptr, 0x0c), 0x37)) // Compute hash of proxy bytecode and store - predicted := keccak256(add(ptr, 0x43), 0x55) // Compute predicted address - } - } + //──────────────────────────────────────────────────────────────────────────────// + // Custom Errors // + //──────────────────────────────────────────────────────────────────────────────// + + /// @notice Thrown when contract creation fails during deployment + error ContractCreationFailed(); + + /// @notice Thrown when the intermediate proxy creation fails during CREATE3 deployment + error ProxyCreationFailed(); + + /// @notice Thrown when the deploying contract has insufficient balance for the deployment + error InsufficientBalance(); + + /// @notice Thrown when provided implementation address is zero address for proxy deployments + error InvalidImplementation(); + + /// @notice Thrown when provided nonce exceeds the maximum allowed by EIP-2681 (2^64-1) + error InvalidNonce(); + + //──────────────────────────────────────────────────────────────────────────────// + // CREATE // + //──────────────────────────────────────────────────────────────────────────────// + + /// @notice Deploys a contract using CREATE opcode + /// @param initCode Complete contract bytecode including constructor arguments + /// @return instance Address of the deployed contract + function create(bytes memory initCode) internal returns (address instance) { + return create(initCode, uint256(0)); + } + + /// @notice Deploys a contract using CREATE opcode + /// @param initCode Complete contract bytecode including constructor arguments + /// @param value Amount of ETH to send to the contract during deployment + /// @return instance Address of the deployed contract + function create(bytes memory initCode, uint256 value) internal returns (address instance) { + assembly ("memory-safe") { + // Verify current contract has sufficient balance to send the specified value + if lt(selfbalance(), value) { + mstore(0x00, 0xf4d678b8) // InsufficientBalance() + revert(0x1c, 0x04) + } + + // Deploy using CREATE opcode + instance := create(value, add(initCode, 0x20), mload(initCode)) + + // Verify deployed contract address is not zero and contains code + if or(iszero(shl(0x60, instance)), iszero(extcodesize(instance))) { + mstore(0x00, 0xa28c2473) // ContractCreationFailed() + revert(0x1c, 0x04) + } + } + } + + /// @notice Computes the predicted address of a contract deployed using CREATE opcode + /// @param nonce Nonce value of the deployer at the time of deployment + /// @return predicted Predicted contract address + function computeCreateAddress(uint256 nonce) internal view returns (address predicted) { + return computeCreateAddress(address(this), nonce); + } + + /// @notice Computes the predicted address of a contract deployed using CREATE opcode + /// @param deployer Address performing the deployment + /// @param nonce Nonce value of the deployer at the time of deployment + /// @return predicted Predicted contract address + function computeCreateAddress(address deployer, uint256 nonce) internal pure returns (address predicted) { + assembly ("memory-safe") { + // Enforce EIP‑2681 nonce constraint (https://eips.ethereum.org/EIPS/eip-2681) + if iszero(lt(nonce, 0xffffffffffffffff)) { + mstore(0x00, 0x756688fe) // InvalidNonce() + revert(0x1c, 0x04) + } + + // Construct RLP structure + mstore8(0x01, 0x94) // Store RLP prefix for address + mstore(0x02, shl(0x60, deployer)) // Store deployer address + + let offset // Variable to track additional bytes needed for large nonces + + // Encode nonce according to RLP rules + for {} 0x01 {} { + // Single byte encoding case (0x00...0x7f) + if lt(nonce, 0x80) { + // Nonce value 0 is encoded as empty string, so convert to 0x80 + if iszero(nonce) { nonce := 0x80 } + mstore8(0x16, nonce) // Store prefix or nonce directly + break + } + + // Multi-byte encoding case (≥ 0x80) + // Compute number of bytes needed to encode nonce + for { let i := nonce } i { + i := shr(0x08, i) + offset := add(offset, 0x01) + } {} + + // Store length prefix indicating number of bytes used for nonce (0x80 + number of bytes) + mstore8(0x16, add(0x80, offset)) + // Store nonce value in big-endian format at appropriate position + mstore(0x17, shl(sub(0x100, mul(offset, 0x08)), nonce)) + break + } + + // Complete RLP structure and compute predicted address + mstore8(0x00, add(0xd6, offset)) // Set RLP prefix for total length + predicted := keccak256(0x00, add(offset, 0x17)) // Compute predicted address + } + } + + //──────────────────────────────────────────────────────────────────────────────// + // CREATE2 // + //──────────────────────────────────────────────────────────────────────────────// + + /// @notice Deploys a contract using CREATE2 opcode for deterministic addressing + /// @param initCode Complete contract bytecode including constructor arguments + /// @param salt 32-byte value used in address derivation + /// @return instance Address of the deployed contract + function create2(bytes memory initCode, bytes32 salt) internal returns (address instance) { + return create2(initCode, salt, uint256(0)); + } + + /// @notice Deploys a contract using CREATE2 opcode for deterministic addressing + /// @param initCode Complete contract bytecode including constructor arguments + /// @param salt 32-byte value used in address derivation + /// @param value Amount of ETH to send to the contract during deployment + /// @return instance Address of the deployed contract + function create2(bytes memory initCode, bytes32 salt, uint256 value) internal returns (address instance) { + assembly ("memory-safe") { + // Verify current contract has sufficient balance to send the specified value + if lt(selfbalance(), value) { + mstore(0x00, 0xf4d678b8) // InsufficientBalance() + revert(0x1c, 0x04) + } + + // Deploy using CREATE2 opcode + instance := create2(value, add(initCode, 0x20), mload(initCode), salt) + + // Verify deployed contract address is not zero and contains code + if or(iszero(shl(0x60, instance)), iszero(extcodesize(instance))) { + mstore(0x00, 0xa28c2473) // ContractCreationFailed() + revert(0x1c, 0x04) + } + } + } + + /// @notice Computes the predicted address of a contract deployed using CREATE2 opcode + /// @param initCodeHash keccak256 hash of initialization code + /// @param salt 32-byte value used in address derivation + /// @return predicted Predicted contract address + function computeCreate2Address(bytes32 initCodeHash, bytes32 salt) internal view returns (address predicted) { + return computeCreate2Address(address(this), initCodeHash, salt); + } + + /// @notice Computes the predicted address of a contract deployed using CREATE2 opcode + /// @param deployer Address performing the deployment + /// @param initCodeHash keccak256 hash of initialization code + /// @param salt 32-byte value used in address derivation + /// @return predicted Predicted contract address + function computeCreate2Address(address deployer, bytes32 initCodeHash, bytes32 salt) + internal + pure + returns (address predicted) + { + assembly ("memory-safe") { + mstore8(0x00, 0xff) // Store CREATE2 prefix + mstore(0x35, initCodeHash) // Store creation bytecode hash + mstore(0x01, shl(0x60, deployer)) // Store deployer address + mstore(0x15, salt) // Store salt + predicted := keccak256(0x00, 0x55) // Compute predicted address + mstore(0x35, 0x00) // Clear hash storage + } + } + + //──────────────────────────────────────────────────────────────────────────────// + // CREATE3 // + //──────────────────────────────────────────────────────────────────────────────// + + /// @notice Deploys a contract using CREATE3 pattern for chain-agnostic deterministic addressing + /// @param initCode Complete contract bytecode including constructor arguments + /// @param salt 32-byte value used in address derivation + /// @return instance Address of the deployed contract + function create3(bytes memory initCode, bytes32 salt) internal returns (address instance) { + return create3(initCode, salt, uint256(0)); + } + + /// @notice Deploys a contract using CREATE3 pattern for chain-agnostic deterministic addressing + /// @param initCode Complete contract bytecode including constructor arguments + /// @param salt 32-byte value used in address derivation + /// @param value Amount of ETH to send to the final contract + /// @return instance Address of the deployed contract + function create3(bytes memory initCode, bytes32 salt, uint256 value) internal returns (address instance) { + assembly ("memory-safe") { + // Verify current contract has sufficient balance to send the specified value + if lt(selfbalance(), value) { + mstore(0x00, 0xf4d678b8) // InsufficientBalance() + revert(0x1c, 0x04) + } + + // Phase 1: Deploy intermediate proxy using CREATE2 opcode + // Store creation bytecode for proxy + // This bytecode creates a proxy that will use CREATE opcode to deploy final contract + mstore(0x00, 0x67363d3d37363d34f03d5260086018f3) + + // Deploy proxy at deterministic address using CREATE2 opcode + let proxy := create2(0x00, 0x10, 0x10, salt) + + // Verify deployed proxy address is not zero address + if iszero(shl(0x60, proxy)) { + mstore(0x00, 0xd49e7d74) // ProxyCreationFailed() + revert(0x1c, 0x04) + } + + // Phase 2: Perform inner deployment via proxy using CREATE opcode + // The actual contract will be deployed by proxy at nonce 1 + + // 0xd6 = 0xc0 (short RLP prefix) + 0x16 (length of: 0x94 ++ proxy ++ 0x01) + // 0x94 = 0x80 + 0x14 (length of an address, 20 bytes, in hex) + mstore(0x14, proxy) // Store proxy address + mstore(0x00, 0xd694) // Store RLP encoding prefix for address (0xd6 + 0x94) + mstore8(0x34, 0x01) // Store proxy nonce + instance := keccak256(0x1e, 0x17) // Compute final contract address + + // Call proxy with final contract's initialization code to deploy the actual contract using CREATE opcode + if iszero( + mul( + extcodesize(instance), // Verify final contract contains code after deployment + call(gas(), proxy, value, add(initCode, 0x20), mload(initCode), codesize(), 0x00) + ) + ) { + mstore(0x00, 0xa28c2473) // ContractCreationFailed() + revert(0x1c, 0x04) + } + } + } + + /// @notice Computes the predicted address of a contract deployed using CREATE3 pattern + /// @param salt 32-byte value used in address derivation + /// @return predicted Predicted contract address + function computeCreate3Address(bytes32 salt) internal view returns (address predicted) { + return computeCreate3Address(address(this), salt); + } + + /// @notice Computes the predicted address of a contract deployed using CREATE3 pattern + /// @param deployer Address performing the deployment + /// @param salt 32-byte value used in address derivation + /// @return predicted Predicted contract address + function computeCreate3Address(address deployer, bytes32 salt) internal pure returns (address predicted) { + assembly ("memory-safe") { + // Compute proxy address using CREATE2 opcode + let ptr := mload(0x40) // Cache free memory pointer + mstore(0x00, deployer) // Store deployer address + mstore8(0x0b, 0xff) // Store CREATE2 prefix + mstore(0x20, salt) // Store salt + // Store hash of minimal proxy creation bytecode + // Equivalent to keccak256(abi.encodePacked(hex"67363d3d37363d34f03d5260086018f3")) + mstore(0x40, 0x21c35dbe1b344a2488cf3321d6ce542f8e9f305544ff09e4993a62319a497c1f) + mstore(0x14, keccak256(0x0b, 0x55)) // Compute and store proxy address + mstore(0x40, ptr) // Restore free memory pointer + // Compute final contract address using CREATE opcode + // 0xd6 = 0xc0 (short RLP prefix) + 0x16 (length of: 0x94 ++ proxy ++ 0x01) + // 0x94 = 0x80 + 0x14 (length of an address, 20 bytes, in hex) + mstore(0x00, 0xd694) // Store RLP encoding prefix for proxy deployment + mstore8(0x34, 0x01) // Store proxy nonce + predicted := keccak256(0x1e, 0x17) // Compute predicted address + } + } + + //──────────────────────────────────────────────────────────────────────────────// + // EIP-1167: Minimal Proxy // + //──────────────────────────────────────────────────────────────────────────────// + + /// @notice Deploys an EIP-1167 minimal proxy contract using CREATE opcode + /// @param implementation Address of the logic contract for delegation + /// @return instance Address of the deployed proxy + function clone(address implementation) internal returns (address instance) { + return clone(implementation, uint256(0)); + } + + /// @notice Deploys an EIP-1167 minimal proxy contract using CREATE opcode + /// @param implementation Address of the logic contract for delegation + /// @param value Amount of ETH to send to the proxy during deployment + /// @return instance Address of the deployed proxy + function clone(address implementation, uint256 value) internal returns (address instance) { + assembly ("memory-safe") { + // Verify implementation address is not zero and contains code + if or(iszero(shl(0x60, implementation)), iszero(extcodesize(implementation))) { + mstore(0x00, 0x68155f9a) // InvalidImplementation() + revert(0x1c, 0x04) + } + + // Verify current contract has sufficient balance to send the specified value + if lt(selfbalance(), value) { + mstore(0x00, 0xf4d678b8) // InsufficientBalance() + revert(0x1c, 0x04) + } + + // Construct EIP-1167 minimal proxy bytecode + // Clean upper 96 bits of implementation address and pack with bytecode before address + mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000)) + // Pack remaining 17 bytes of implementation address with bytecode after address + mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3)) + + // Deploy proxy using CREATE opcode + instance := create(value, 0x09, 0x37) + + // Verify deployed contract address is not zero and contains code + if or(iszero(shl(0x60, instance)), iszero(extcodesize(instance))) { + mstore(0x00, 0xa28c2473) // ContractCreationFailed() + revert(0x1c, 0x04) + } + } + } + + /// @notice Deploys an EIP-1167 minimal proxy contract deterministically using CREATE2 opcode + /// @param implementation Address of the logic contract for delegation + /// @param salt 32-byte value used in address derivation + /// @return instance Address of the deployed proxy + function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) { + return cloneDeterministic(implementation, salt, uint256(0)); + } + + /// @notice Deploys an EIP-1167 minimal proxy contract deterministically using CREATE2 opcode + /// @param implementation Address of the logic contract for delegation + /// @param salt 32-byte value used in address derivation + /// @param value Amount of ETH to send to the proxy during deployment + /// @return instance Address of the deployed proxy + function cloneDeterministic(address implementation, bytes32 salt, uint256 value) + internal + returns (address instance) + { + assembly ("memory-safe") { + // Verify implementation address is not zero and contains code + if or(iszero(shl(0x60, implementation)), iszero(extcodesize(implementation))) { + mstore(0x00, 0x68155f9a) // InvalidImplementation() + revert(0x1c, 0x04) + } + + // Verify current contract has sufficient balance to send the specified value + if lt(selfbalance(), value) { + mstore(0x00, 0xf4d678b8) // InsufficientBalance() + revert(0x1c, 0x04) + } + + // Construct EIP-1167 minimal proxy bytecode + // Clean upper 96 bits of implementation address and pack with bytecode before address + mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000)) + // Pack remaining 17 bytes of implementation address with bytecode after address + mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3)) + + // Deploy proxy using CREATE2 opcode + instance := create2(value, 0x09, 0x37, salt) + + // Verify deployed contract address is not zero and contains code + if or(iszero(shl(0x60, instance)), iszero(extcodesize(instance))) { + mstore(0x00, 0xa28c2473) // ContractCreationFailed() + revert(0x1c, 0x04) + } + } + } + + /// @notice Computes the predicted address of a contract deployed using EIP-1167 pattern + /// @param implementation Address of the logic contract for delegation + /// @param salt 32-byte value used in address derivation + /// @return predicted Predicted contract address + function computeCloneDeterministicAddress(address implementation, bytes32 salt) + internal + view + returns (address predicted) + { + return computeCloneDeterministicAddress(address(this), implementation, salt); + } + + /// @notice Computes the predicted address of a contract deployed using EIP-1167 pattern + /// @param deployer Address performing the deployment + /// @param implementation Address of the logic contract for delegation + /// @param salt 32-byte value used in address derivation + /// @return predicted Predicted contract address + function computeCloneDeterministicAddress(address deployer, address implementation, bytes32 salt) + internal + pure + returns (address predicted) + { + assembly ("memory-safe") { + let ptr := mload(0x40) // Cache free memory pointer + mstore(add(ptr, 0x58), salt) // Store salt + mstore(add(ptr, 0x38), deployer) // Store deployer address + mstore(add(ptr, 0x24), 0x5af43d82803e903d91602b57fd5bf3ff) // Store runtime code suffix + mstore(add(ptr, 0x14), implementation) // Store implementation address + mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73) // Store creation code and runtime code prefix + mstore(add(ptr, 0x78), keccak256(add(ptr, 0x0c), 0x37)) // Compute hash of proxy bytecode and store + predicted := keccak256(add(ptr, 0x43), 0x55) // Compute predicted address + } + } } diff --git a/src/CreateXFactory.sol b/src/CreateXFactory.sol index 751d8a7..17567f9 100644 --- a/src/CreateXFactory.sol +++ b/src/CreateXFactory.sol @@ -13,106 +13,108 @@ import {CreateX} from "./CreateX.sol"; /// - EIP-1167: Minimal proxy & Deterministic minimal proxy /// @author fomoweth contract CreateXFactory is ICreateXFactory { - /// @notice Ensures that the first 20 bytes of submitted salt match either the caller or the zero address - modifier ensure(bytes32 salt) { - assembly ("memory-safe") { - if iszero(or(iszero(shr(0x60, salt)), eq(shr(0x60, salt), caller()))) { - mstore(0x00, 0x81e69d9b) // InvalidSalt() - revert(0x1c, 0x04) - } - } - _; - } + /// @notice Ensures that the first 20 bytes of submitted salt match either the caller or the zero address + modifier ensure(bytes32 salt) { + assembly ("memory-safe") { + if iszero(or(iszero(shr(0x60, salt)), eq(shr(0x60, salt), caller()))) { + mstore(0x00, 0x81e69d9b) // InvalidSalt() + revert(0x1c, 0x04) + } + } + _; + } - /// @inheritdoc ICreateXFactory - function createX( - CreationType creationType, - bytes calldata initCode, - bytes32 salt - ) external payable returns (address) { - if (creationType == CreationType.CREATE) { - return create(initCode); - } else if (creationType == CreationType.CREATE2) { - return create2(initCode, salt); - } else if (creationType == CreationType.CREATE3) { - return create3(initCode, salt); - } else if (creationType == CreationType.Clone) { - // For Clone type, interpret first 20 bytes of initCode as implementation address - return clone(address(bytes20(initCode[:20]))); - } else if (creationType == CreationType.CloneDeterministic) { - // For CloneDeterministic type, interpret first 20 bytes of initCode as implementation address - return cloneDeterministic(address(bytes20(initCode[:20])), salt); - } else { - revert InvalidCreationType(); - } - } + /// @inheritdoc ICreateXFactory + function createX(CreationType creationType, bytes calldata initCode, bytes32 salt) + external + payable + returns (address) + { + if (creationType == CreationType.CREATE) { + return create(initCode); + } else if (creationType == CreationType.CREATE2) { + return create2(initCode, salt); + } else if (creationType == CreationType.CREATE3) { + return create3(initCode, salt); + } else if (creationType == CreationType.Clone) { + // For Clone type, interpret first 20 bytes of initCode as implementation address + return clone(address(bytes20(initCode[:20]))); + } else if (creationType == CreationType.CloneDeterministic) { + // For CloneDeterministic type, interpret first 20 bytes of initCode as implementation address + return cloneDeterministic(address(bytes20(initCode[:20])), salt); + } else { + revert InvalidCreationType(); + } + } - /// @inheritdoc ICreateXFactory - function create(bytes calldata initCode) public payable returns (address instance) { - emit ContractCreation(instance = CreateX.create(initCode, msg.value), msg.sender); - } + /// @inheritdoc ICreateXFactory + function create(bytes calldata initCode) public payable returns (address instance) { + emit ContractCreation(instance = CreateX.create(initCode, msg.value), msg.sender); + } - /// @inheritdoc ICreateXFactory - function create2(bytes calldata initCode, bytes32 salt) public payable ensure(salt) returns (address instance) { - emit ContractCreation(instance = CreateX.create2(initCode, salt, msg.value), msg.sender, salt); - } + /// @inheritdoc ICreateXFactory + function create2(bytes calldata initCode, bytes32 salt) public payable ensure(salt) returns (address instance) { + emit ContractCreation(instance = CreateX.create2(initCode, salt, msg.value), msg.sender, salt); + } - /// @inheritdoc ICreateXFactory - function create3(bytes calldata initCode, bytes32 salt) public payable ensure(salt) returns (address instance) { - emit ContractCreation(instance = CreateX.create3(initCode, salt, msg.value), msg.sender, salt); - } + /// @inheritdoc ICreateXFactory + function create3(bytes calldata initCode, bytes32 salt) public payable ensure(salt) returns (address instance) { + emit ContractCreation(instance = CreateX.create3(initCode, salt, msg.value), msg.sender, salt); + } - /// @inheritdoc ICreateXFactory - function clone(address implementation) public payable returns (address instance) { - emit ContractCreation(instance = CreateX.clone(implementation, msg.value), msg.sender); - } + /// @inheritdoc ICreateXFactory + function clone(address implementation) public payable returns (address instance) { + emit ContractCreation(instance = CreateX.clone(implementation, msg.value), msg.sender); + } - /// @inheritdoc ICreateXFactory - function cloneDeterministic( - address implementation, - bytes32 salt - ) public payable ensure(salt) returns (address instance) { - emit ContractCreation(instance = CreateX.cloneDeterministic(implementation, salt, msg.value), msg.sender, salt); - } + /// @inheritdoc ICreateXFactory + function cloneDeterministic(address implementation, bytes32 salt) + public + payable + ensure(salt) + returns (address instance) + { + emit ContractCreation(instance = CreateX.cloneDeterministic(implementation, salt, msg.value), msg.sender, salt); + } - /// @inheritdoc ICreateXFactory - function computeCreateXAddress( - CreationType creationType, - bytes32 initCodeHash, - bytes32 salt - ) external view returns (address) { - if (creationType == CreationType.CREATE || creationType == CreationType.Clone) { - // For CREATE and Clone types, treat salt parameter as nonce - return computeCreateAddress(uint256(salt)); - } else if (creationType == CreationType.CREATE2) { - return computeCreate2Address(initCodeHash, salt); - } else if (creationType == CreationType.CREATE3) { - return computeCreate3Address(salt); - } else if (creationType == CreationType.CloneDeterministic) { - // For CloneDeterministic, interpret initCodeHash as implementation address - return computeCloneDeterministicAddress(address(bytes20(initCodeHash)), salt); - } else { - revert InvalidCreationType(); - } - } + /// @inheritdoc ICreateXFactory + function computeCreateXAddress(CreationType creationType, bytes32 initCodeHash, bytes32 salt) + external + view + returns (address) + { + if (creationType == CreationType.CREATE || creationType == CreationType.Clone) { + // For CREATE and Clone types, treat salt parameter as nonce + return computeCreateAddress(uint256(salt)); + } else if (creationType == CreationType.CREATE2) { + return computeCreate2Address(initCodeHash, salt); + } else if (creationType == CreationType.CREATE3) { + return computeCreate3Address(salt); + } else if (creationType == CreationType.CloneDeterministic) { + // For CloneDeterministic, interpret initCodeHash as implementation address + return computeCloneDeterministicAddress(address(bytes20(initCodeHash)), salt); + } else { + revert InvalidCreationType(); + } + } - /// @inheritdoc ICreateXFactory - function computeCreateAddress(uint256 nonce) public view returns (address) { - return CreateX.computeCreateAddress(nonce); - } + /// @inheritdoc ICreateXFactory + function computeCreateAddress(uint256 nonce) public view returns (address) { + return CreateX.computeCreateAddress(nonce); + } - /// @inheritdoc ICreateXFactory - function computeCreate2Address(bytes32 initCodeHash, bytes32 salt) public view returns (address) { - return CreateX.computeCreate2Address(initCodeHash, salt); - } + /// @inheritdoc ICreateXFactory + function computeCreate2Address(bytes32 initCodeHash, bytes32 salt) public view returns (address) { + return CreateX.computeCreate2Address(initCodeHash, salt); + } - /// @inheritdoc ICreateXFactory - function computeCreate3Address(bytes32 salt) public view returns (address) { - return CreateX.computeCreate3Address(salt); - } + /// @inheritdoc ICreateXFactory + function computeCreate3Address(bytes32 salt) public view returns (address) { + return CreateX.computeCreate3Address(salt); + } - /// @inheritdoc ICreateXFactory - function computeCloneDeterministicAddress(address implementation, bytes32 salt) public view returns (address) { - return CreateX.computeCloneDeterministicAddress(implementation, salt); - } + /// @inheritdoc ICreateXFactory + function computeCloneDeterministicAddress(address implementation, bytes32 salt) public view returns (address) { + return CreateX.computeCloneDeterministicAddress(implementation, salt); + } } diff --git a/src/ICreateXFactory.sol b/src/ICreateXFactory.sol index d950f35..91778f1 100644 --- a/src/ICreateXFactory.sol +++ b/src/ICreateXFactory.sol @@ -5,26 +5,26 @@ pragma solidity ^0.8.30; /// @notice Unified interface for deploying contracts using various creation patterns /// @author fomoweth interface ICreateXFactory { - /// @notice Thrown when provided creation type is outside the supported enum range - error InvalidCreationType(); - - /// @notice Thrown when provided salt fails validation - error InvalidSalt(); - - /// @notice Emitted when a contract is created without salt (CREATE / Clone) - /// @param instance Address of the deployed contract - /// @param deployer Address that initiated the deployment transaction - event ContractCreation(address indexed instance, address indexed deployer); - - /// @notice Emitted when a contract is created with salt (CREATE2 / CREATE3 / CloneDeterministic) - /// @param instance Address of the deployed contract - /// @param deployer Address that initiated the deployment transaction - /// @param salt 32-byte value used in address derivation - event ContractCreation(address indexed instance, address indexed deployer, bytes32 indexed salt); - - // prettier-ignore - /// @notice Enumeration defining supported contract creation methods - enum CreationType { + /// @notice Thrown when provided creation type is outside the supported enum range + error InvalidCreationType(); + + /// @notice Thrown when provided salt fails validation + error InvalidSalt(); + + /// @notice Emitted when a contract is created without salt (CREATE / Clone) + /// @param instance Address of the deployed contract + /// @param deployer Address that initiated the deployment transaction + event ContractCreation(address indexed instance, address indexed deployer); + + /// @notice Emitted when a contract is created with salt (CREATE2 / CREATE3 / CloneDeterministic) + /// @param instance Address of the deployed contract + /// @param deployer Address that initiated the deployment transaction + /// @param salt 32-byte value used in address derivation + event ContractCreation(address indexed instance, address indexed deployer, bytes32 indexed salt); + + // forgefmt: disable-next-item + /// @notice Enumeration defining supported contract creation methods + enum CreationType { CREATE, // 0 – traditional deployment CREATE2, // 1 – deterministic deployment CREATE3, // 2 – chain-agnostic deployment @@ -32,85 +32,83 @@ interface ICreateXFactory { CloneDeterministic // 4 – EIP-1167 via CREATE2 } - /// @notice Deploys a contract using the specified creation method - /// @dev Unified contract deployment function covering every {CreationType} - /// @param creationType Creation method to use - /// @param initCode Contract initialization code (interpreted as implementation address for Clone / CloneDeterministic) - /// @param salt 32-byte value used in address derivation (can be empty for CREATE / Clone) - /// @return instance Address of the deployed contract - function createX( - CreationType creationType, - bytes calldata initCode, - bytes32 salt - ) external payable returns (address instance); - - /// @notice Deploys a contract using CREATE opcode - /// @param initCode Complete contract bytecode including constructor arguments - /// @return instance Address of the deployed contract - function create(bytes calldata initCode) external payable returns (address instance); - - /// @notice Deploys a contract using CREATE2 opcode - /// @param initCode Complete contract bytecode including constructor arguments - /// @param salt 32-byte value used in address derivation - /// @return instance Address of the deployed contract - function create2(bytes calldata initCode, bytes32 salt) external payable returns (address instance); - - /// @notice Deploys a contract using CREATE3 pattern - /// @param initCode Complete contract bytecode including constructor arguments - /// @param salt 32-byte value used in address derivation - /// @return instance Address of the deployed contract - function create3(bytes calldata initCode, bytes32 salt) external payable returns (address instance); - - /// @notice Deploys an EIP-1167 minimal proxy contract using CREATE opcode - /// @param implementation Address of the logic contract for delegation - /// @return instance Address of the deployed contract - function clone(address implementation) external payable returns (address instance); - - /// @notice Deploys an EIP-1167 minimal proxy contract deterministically using CREATE2 opcode - /// @param implementation Address of the logic contract for delegation - /// @param salt 32-byte value used in address derivation - /// @return instance Address of the deployed contract - function cloneDeterministic(address implementation, bytes32 salt) external payable returns (address instance); - - /// @notice Computes the predicted address for the specified creation method - /// @dev Unified address prediction function covering every {CreationType} - /// @param creationType Creation method to use - /// @param initCodeHash The hash or identifier used for address calculation - /// - CREATE: not used in calculation, can be empty - /// - CREATE2: keccak256 hash of the initialization code - /// - CREATE3: not used in calculation, can be empty (proxy hash is constant) - /// - Clone: not used in calculation, can be empty - /// - CloneDeterministic: implementation contract address as bytes32 - /// @param salt 32-byte value used in address derivation (interpreted as nonce for CREATE / Clone) - /// @return predicted Predicted contract address - function computeCreateXAddress( - CreationType creationType, - bytes32 initCodeHash, - bytes32 salt - ) external view returns (address predicted); - - /// @notice Computes the predicted address of a contract deployed using CREATE opcode - /// @param nonce Nonce value at the time of deployment - /// @return predicted Predicted contract address - function computeCreateAddress(uint256 nonce) external view returns (address predicted); - - /// @notice Computes the predicted address of a contract deployed using CREATE2 opcode - /// @param initCodeHash keccak256 hash of initialization code - /// @param salt 32-byte value used in address derivation - /// @return predicted Predicted contract address - function computeCreate2Address(bytes32 initCodeHash, bytes32 salt) external view returns (address predicted); - - /// @notice Computes the predicted address of a contract deployed using CREATE3 pattern - /// @param salt 32-byte value used in address derivation - /// @return predicted Predicted contract address - function computeCreate3Address(bytes32 salt) external view returns (address predicted); - - /// @notice Computes the predicted address of a contract deployed using EIP-1167 pattern - /// @param implementation Address of the logic contract for delegation - /// @param salt 32-byte value used in address derivation - /// @return predicted Predicted contract address - function computeCloneDeterministicAddress( - address implementation, - bytes32 salt - ) external view returns (address predicted); + /// @notice Deploys a contract using the specified creation method + /// @dev Unified contract deployment function covering every {CreationType} + /// @param creationType Creation method to use + /// @param initCode Contract initialization code (interpreted as implementation address for Clone / CloneDeterministic) + /// @param salt 32-byte value used in address derivation (can be empty for CREATE / Clone) + /// @return instance Address of the deployed contract + function createX(CreationType creationType, bytes calldata initCode, bytes32 salt) + external + payable + returns (address instance); + + /// @notice Deploys a contract using CREATE opcode + /// @param initCode Complete contract bytecode including constructor arguments + /// @return instance Address of the deployed contract + function create(bytes calldata initCode) external payable returns (address instance); + + /// @notice Deploys a contract using CREATE2 opcode + /// @param initCode Complete contract bytecode including constructor arguments + /// @param salt 32-byte value used in address derivation + /// @return instance Address of the deployed contract + function create2(bytes calldata initCode, bytes32 salt) external payable returns (address instance); + + /// @notice Deploys a contract using CREATE3 pattern + /// @param initCode Complete contract bytecode including constructor arguments + /// @param salt 32-byte value used in address derivation + /// @return instance Address of the deployed contract + function create3(bytes calldata initCode, bytes32 salt) external payable returns (address instance); + + /// @notice Deploys an EIP-1167 minimal proxy contract using CREATE opcode + /// @param implementation Address of the logic contract for delegation + /// @return instance Address of the deployed contract + function clone(address implementation) external payable returns (address instance); + + /// @notice Deploys an EIP-1167 minimal proxy contract deterministically using CREATE2 opcode + /// @param implementation Address of the logic contract for delegation + /// @param salt 32-byte value used in address derivation + /// @return instance Address of the deployed contract + function cloneDeterministic(address implementation, bytes32 salt) external payable returns (address instance); + + /// @notice Computes the predicted address for the specified creation method + /// @dev Unified address prediction function covering every {CreationType} + /// @param creationType Creation method to use + /// @param initCodeHash The hash or identifier used for address calculation + /// - CREATE: not used in calculation, can be empty + /// - CREATE2: keccak256 hash of the initialization code + /// - CREATE3: not used in calculation, can be empty (proxy hash is constant) + /// - Clone: not used in calculation, can be empty + /// - CloneDeterministic: implementation contract address as bytes32 + /// @param salt 32-byte value used in address derivation (interpreted as nonce for CREATE / Clone) + /// @return predicted Predicted contract address + function computeCreateXAddress(CreationType creationType, bytes32 initCodeHash, bytes32 salt) + external + view + returns (address predicted); + + /// @notice Computes the predicted address of a contract deployed using CREATE opcode + /// @param nonce Nonce value at the time of deployment + /// @return predicted Predicted contract address + function computeCreateAddress(uint256 nonce) external view returns (address predicted); + + /// @notice Computes the predicted address of a contract deployed using CREATE2 opcode + /// @param initCodeHash keccak256 hash of initialization code + /// @param salt 32-byte value used in address derivation + /// @return predicted Predicted contract address + function computeCreate2Address(bytes32 initCodeHash, bytes32 salt) external view returns (address predicted); + + /// @notice Computes the predicted address of a contract deployed using CREATE3 pattern + /// @param salt 32-byte value used in address derivation + /// @return predicted Predicted contract address + function computeCreate3Address(bytes32 salt) external view returns (address predicted); + + /// @notice Computes the predicted address of a contract deployed using EIP-1167 pattern + /// @param implementation Address of the logic contract for delegation + /// @param salt 32-byte value used in address derivation + /// @return predicted Predicted contract address + function computeCloneDeterministicAddress(address implementation, bytes32 salt) + external + view + returns (address predicted); } diff --git a/test/CreateXFactory.t.sol b/test/CreateXFactory.t.sol index 0f552e1..47c0514 100644 --- a/test/CreateXFactory.t.sol +++ b/test/CreateXFactory.t.sol @@ -8,413 +8,393 @@ import {MockERC20} from "test/mocks/MockERC20.sol"; import {MockTarget} from "test/mocks/MockTarget.sol"; contract CreateXFactoryTest is Test { - uint256 internal snapshotId = type(uint256).max; - - CreateXFactory internal factory; - - modifier impersonate(address account, uint256 value) { - vm.assume(account != address(0)); - if (value != uint256(0)) deal(account, value); - vm.startPrank(account); - _; - vm.stopPrank(); - } - - function setUp() public { - factory = new CreateXFactory(); - } - - function test_fuzz_createX( - bool protected, - address deployer, - uint96 identifier, - uint256 value - ) public impersonate(deployer, value) { - bytes memory initCode = abi.encodePacked(type(MockTarget).creationCode); - bytes32 initCodeHash = keccak256(initCode); - bytes32 salt = generateSalt(deployer, identifier, protected); - - address implementation = address(new MockTarget()); - address instance; - address predicted; - - revertToState(); - - vm.expectEmit(true, true, true, true); - emit ICreateXFactory.ContractCreation( - predicted = factory.computeCreateAddress(vm.getNonce(address(factory))), - deployer - ); - - instance = factory.createX{value: value}(ICreateXFactory.CreationType.CREATE, initCode, salt); - assertEq(instance, predicted); - assertEq(instance.balance, value); - revertToState(); - - vm.expectEmit(true, true, true, true); - emit ICreateXFactory.ContractCreation( - predicted = factory.computeCreate2Address(initCodeHash, salt), - deployer, - salt - ); - - instance = factory.createX{value: value}(ICreateXFactory.CreationType.CREATE2, initCode, salt); - assertEq(instance, predicted); - assertEq(instance.balance, value); - revertToState(); - - vm.expectEmit(true, true, true, true); - emit ICreateXFactory.ContractCreation(predicted = factory.computeCreate3Address(salt), deployer, salt); - - instance = factory.createX{value: value}(ICreateXFactory.CreationType.CREATE3, initCode, salt); - assertEq(instance, predicted); - assertEq(instance.balance, value); - revertToState(); - - vm.expectEmit(true, true, true, true); - emit ICreateXFactory.ContractCreation( - predicted = factory.computeCreateAddress(vm.getNonce(address(factory))), - deployer - ); - - instance = factory.createX{value: value}( - ICreateXFactory.CreationType.Clone, - abi.encodePacked(implementation), - salt - ); - assertEq(instance, predicted); - assertEq(instance.balance, value); - revertToState(); - - vm.expectEmit(true, true, true, true); - emit ICreateXFactory.ContractCreation( - predicted = factory.computeCloneDeterministicAddress(implementation, salt), - deployer, - salt - ); - - instance = factory.createX{value: value}( - ICreateXFactory.CreationType.CloneDeterministic, - abi.encodePacked(implementation), - salt - ); - assertEq(instance, predicted); - assertEq(instance.balance, value); - } - - function test_fuzz_computeCreateXAddress(bool protected, address deployer, uint96 identifier, bytes32 hash) public { - if (identifier >= type(uint64).max) { - vm.expectRevert(CreateX.InvalidNonce.selector); - factory.computeCreateXAddress(ICreateXFactory.CreationType.CREATE, hash, bytes32(uint256(identifier))); - } else { - assertEq( - factory.computeCreateXAddress(ICreateXFactory.CreationType.CREATE, hash, bytes32(uint256(identifier))), - vm.computeCreateAddress(address(factory), identifier) - ); - - assertEq( - factory.computeCreateXAddress(ICreateXFactory.CreationType.Clone, hash, bytes32(uint256(identifier))), - vm.computeCreateAddress(address(factory), identifier) - ); - } - - bytes32 salt = generateSalt(deployer, identifier, protected); - - assertEq( - factory.computeCreateXAddress(ICreateXFactory.CreationType.CREATE2, hash, salt), - vm.computeCreate2Address(salt, hash, address(factory)) - ); - - assertEq( - factory.computeCreateXAddress(ICreateXFactory.CreationType.CREATE3, hash, salt), - computeCreate3Address(address(factory), salt) - ); - - address implementation = vm.addr(boundPrivateKey(uint256(hash))); - - assertEq( - factory.computeCreateXAddress( - ICreateXFactory.CreationType.CloneDeterministic, - bytes32(bytes20(implementation)), - salt - ), - computeCloneDeterministicAddress(address(factory), implementation, salt) - ); - } - - function test_fuzz_create(address deployer, uint256 value) public impersonate(deployer, value) { - bytes memory initCode = abi.encodePacked(type(MockTarget).creationCode); - address predicted = vm.computeCreateAddress(address(factory), vm.getNonce(address(factory))); - - vm.expectEmit(true, true, true, true); - emit ICreateXFactory.ContractCreation(predicted, deployer); - - MockTarget instance = MockTarget(factory.create{value: value}(initCode)); - assertEq(address(instance), predicted); - assertEq(address(instance).balance, value); - assertEq(instance.getValue(), uint256(0)); - assertEq(instance.setValue(value), value); - } - - function test_create_deployERC20() public { - bytes memory initCode = bytes.concat(type(MockERC20).creationCode, abi.encode("Mock Token", "MOCK", 18)); - address predicted = vm.computeCreateAddress(address(factory), vm.getNonce(address(factory))); - - vm.expectEmit(true, true, true, true); - emit ICreateXFactory.ContractCreation(predicted, address(this)); - - MockERC20 token = MockERC20(factory.create(initCode)); - assertEq(address(token), predicted); - assertEq(token.name(), "Mock Token"); - assertEq(token.symbol(), "MOCK"); - assertEq(token.decimals(), 18); - } - - function test_fuzz_create2( - bool protected, - address deployer, - uint96 identifier, - uint256 value - ) public impersonate(deployer, value) { - bytes memory initCode = abi.encodePacked(type(MockTarget).creationCode); - bytes32 initCodeHash = keccak256(initCode); - bytes32 salt = generateSalt(deployer, identifier, protected); - address predicted = computeCreate2Address(address(factory), initCodeHash, salt); - - vm.expectEmit(true, true, true, true); - emit ICreateXFactory.ContractCreation(predicted, deployer, salt); - - MockTarget instance = MockTarget(factory.create2{value: value}(initCode, salt)); - assertEq(address(instance), predicted); - assertEq(address(instance).balance, value); - assertEq(instance.getValue(), uint256(0)); - assertEq(instance.setValue(value), value); - } - - function test_create2_deployERC20() public { - bytes memory initCode = bytes.concat(type(MockERC20).creationCode, abi.encode("Mock Token", "MOCK", 18)); - bytes32 initCodeHash = keccak256(initCode); - bytes32 salt = generateSalt(address(0), uint96(0)); - address predicted = computeCreate2Address(address(factory), initCodeHash, salt); - - vm.expectEmit(true, true, true, true); - emit ICreateXFactory.ContractCreation(predicted, address(this), salt); - - MockERC20 token = MockERC20(factory.create2(initCode, salt)); - assertEq(address(token), predicted); - assertEq(token.name(), "Mock Token"); - assertEq(token.symbol(), "MOCK"); - assertEq(token.decimals(), 18); - } - - function test_fuzz_create3( - bool protected, - address deployer, - uint96 identifier, - uint256 value - ) public impersonate(deployer, value) { - bytes memory initCode = abi.encodePacked(type(MockTarget).creationCode); - bytes32 salt = generateSalt(deployer, identifier, protected); - address predicted = computeCreate3Address(address(factory), salt); - - vm.expectEmit(true, true, true, true); - emit ICreateXFactory.ContractCreation(predicted, deployer, salt); - - MockTarget instance = MockTarget(factory.create3{value: value}(initCode, salt)); - assertEq(address(instance), predicted); - assertEq(address(instance).balance, value); - assertEq(instance.getValue(), uint256(0)); - assertEq(instance.setValue(value), value); - } - - function test_create3_deployERC20() public { - bytes memory initCode = bytes.concat(type(MockERC20).creationCode, abi.encode("Mock Token", "MOCK", 18)); - bytes32 salt = generateSalt(address(0), uint96(0)); - address predicted = computeCreate3Address(address(factory), salt); - - vm.expectEmit(true, true, true, true); - emit ICreateXFactory.ContractCreation(predicted, address(this), salt); - - MockERC20 token = MockERC20(factory.create3(initCode, salt)); - assertEq(address(token), predicted); - assertEq(token.name(), "Mock Token"); - assertEq(token.symbol(), "MOCK"); - assertEq(token.decimals(), 18); - } - - function test_create3_chainAgnosticDeployment() public { - vm.makePersistent(address(factory)); - - bytes memory initCode = bytes.concat(type(MockERC20).creationCode, abi.encode("Mock Token", "MOCK", 18)); - bytes32 salt = generateSalt(address(this)); - - vm.createSelectFork("ethereum"); - address instance = factory.create3(initCode, salt); - - vm.createSelectFork("optimism"); - assertEq(instance, factory.create3(initCode, salt)); - - vm.createSelectFork("bnb"); - assertEq(instance, factory.create3(initCode, salt)); - - vm.createSelectFork("polygon"); - assertEq(instance, factory.create3(initCode, salt)); - - vm.createSelectFork("unichain"); - assertEq(instance, factory.create3(initCode, salt)); - - vm.createSelectFork("fantom"); - assertEq(instance, factory.create3(initCode, salt)); - - vm.createSelectFork("base"); - assertEq(instance, factory.create3(initCode, salt)); - - vm.createSelectFork("arbitrum"); - assertEq(instance, factory.create3(initCode, salt)); - - vm.createSelectFork("avalanche"); - assertEq(instance, factory.create3(initCode, salt)); - - vm.createSelectFork("scroll"); - assertEq(instance, factory.create3(initCode, salt)); - - vm.createSelectFork("linea"); - assertEq(instance, factory.create3(initCode, salt)); - } - - function test_create3_revertsOnSameSaltDeployments() public { - bytes memory mockInitCode = abi.encodePacked(type(MockTarget).creationCode); - bytes memory erc20InitCode = bytes.concat(type(MockERC20).creationCode, abi.encode("Mock Token", "MOCK", 18)); - bytes32 salt = generateSalt(address(this)); - - factory.create3(mockInitCode, salt); - vm.expectRevert(CreateX.ProxyCreationFailed.selector); - factory.create3(mockInitCode, salt); - vm.expectRevert(CreateX.ProxyCreationFailed.selector); - factory.create3(erc20InitCode, salt); - } - - function test_fuzz_clone(address deployer, uint256 value) public impersonate(deployer, value) { - address implementation = address(new MockTarget()); - address predicted = vm.computeCreateAddress(address(factory), vm.getNonce(address(factory))); - - vm.expectEmit(true, true, true, true); - emit ICreateXFactory.ContractCreation(predicted, deployer); - - MockTarget instance = MockTarget(factory.clone{value: value}(implementation)); - assertEq(address(instance), predicted); - assertEq(address(instance).balance, value); - assertEq(instance.getValue(), uint256(0)); - assertEq(instance.setValue(value), value); - } - - function test_clone_revertsIfInvalidImplementationGiven() public { - vm.expectRevert(CreateX.InvalidImplementation.selector); - factory.clone(address(0)); - - vm.expectRevert(CreateX.InvalidImplementation.selector); - factory.clone(address(0xdeadbeef)); - } - - function test_fuzz_cloneDeterministic( - bool protected, - address deployer, - uint96 identifier, - uint256 value - ) public impersonate(deployer, value) { - bytes32 salt = generateSalt(deployer, identifier, protected); - address implementation = address(new MockTarget()); - address predicted = computeCloneDeterministicAddress(address(factory), implementation, salt); - - vm.expectEmit(true, true, true, true); - emit ICreateXFactory.ContractCreation(predicted, deployer, salt); - - MockTarget instance = MockTarget(factory.cloneDeterministic{value: value}(implementation, salt)); - assertEq(address(instance), predicted); - assertEq(address(instance).balance, value); - assertEq(instance.getValue(), uint256(0)); - assertEq(instance.setValue(value), value); - } - - function test_cloneDeterministic_revertsIfInvalidImplementationGiven() public { - vm.expectRevert(CreateX.InvalidImplementation.selector); - factory.cloneDeterministic(address(0), bytes32(0)); - - vm.expectRevert(CreateX.InvalidImplementation.selector); - factory.cloneDeterministic(address(0xdeadbeef), bytes32(0)); - } - - function test_fuzz_computeCreateAddress(address deployer, uint256 nonce) public { - if (nonce >= type(uint64).max) { - vm.expectRevert(CreateX.InvalidNonce.selector); - factory.computeCreateAddress(nonce); - } else { - assertEq(CreateX.computeCreateAddress(deployer, nonce), vm.computeCreateAddress(deployer, nonce)); - } - } - - function test_fuzz_computeCreate2Address(address deployer, bytes32 hash, bytes32 salt) public pure { - assertEq(CreateX.computeCreate2Address(deployer, hash, salt), computeCreate2Address(deployer, hash, salt)); - } - - function test_fuzz_computeCreate3Address(address deployer, bytes32 salt) public pure { - assertEq(CreateX.computeCreate3Address(deployer, salt), computeCreate3Address(deployer, salt)); - } - - function test_fuzz_computeCloneDeterministicAddress( - address deployer, - address implementation, - bytes32 salt - ) public pure { - assertEq( - CreateX.computeCloneDeterministicAddress(deployer, implementation, salt), - computeCloneDeterministicAddress(deployer, implementation, salt) - ); - } - - function computeCreate2Address( - address deployer, - bytes32 initCodeHash, - bytes32 salt - ) internal pure returns (address predicted) { - return address(uint160(uint256(keccak256(abi.encodePacked(hex"ff", deployer, salt, initCodeHash))))); - } - - function computeCreate3Address(address deployer, bytes32 salt) internal pure returns (address predicted) { - bytes32 initCodeHash = 0x21c35dbe1b344a2488cf3321d6ce542f8e9f305544ff09e4993a62319a497c1f; - address proxy = computeCreate2Address(deployer, initCodeHash, salt); - return address(uint160(uint256(keccak256(abi.encodePacked(hex"d6_94", proxy, hex"01"))))); - } - - function computeCloneDeterministicAddress( - address deployer, - address implementation, - bytes32 salt - ) internal pure returns (address predicted) { - bytes32 initCodeHash = keccak256( - abi.encodePacked( - hex"3d602d80600a3d3981f3363d3d373d3d3d363d73", - bytes20(implementation), - hex"5af43d82803e903d91602b57fd5bf3" - ) - ); - return computeCreate2Address(deployer, initCodeHash, salt); - } - - function generateSalt(address caller) internal returns (bytes32 salt) { - return generateSalt(caller, uint96(vm.randomUint(type(uint96).min, type(uint96).max))); - } - - function generateSalt(address caller, uint96 identifier) internal pure returns (bytes32 salt) { - return bytes32((uint256(uint160(caller)) << 96) | uint256(identifier)); - } - - function generateSalt(address caller, uint96 identifier, bool protected) internal pure returns (bytes32 salt) { - return generateSalt(protected ? caller : address(0), identifier); - } - - function revertToState() internal { - if (snapshotId != type(uint256).max) vm.revertToState(snapshotId); - snapshotId = vm.snapshotState(); - } + uint256 internal snapshotId = type(uint256).max; + + CreateXFactory internal factory; + + modifier impersonate(address account, uint256 value) { + vm.assume(account != address(0)); + if (value != uint256(0)) deal(account, value); + vm.startPrank(account); + _; + vm.stopPrank(); + } + + function setUp() public { + factory = new CreateXFactory(); + } + + function test_fuzz_createX(bool protected, address deployer, uint96 identifier, uint256 value) + public + impersonate(deployer, value) + { + bytes memory initCode = abi.encodePacked(type(MockTarget).creationCode); + bytes32 initCodeHash = keccak256(initCode); + bytes32 salt = generateSalt(deployer, identifier, protected); + + address implementation = address(new MockTarget()); + address instance; + address predicted; + + revertToState(); + + vm.expectEmit(true, true, true, true); + emit ICreateXFactory.ContractCreation( + predicted = factory.computeCreateAddress(vm.getNonce(address(factory))), deployer + ); + + instance = factory.createX{value: value}(ICreateXFactory.CreationType.CREATE, initCode, salt); + assertEq(instance, predicted); + assertEq(instance.balance, value); + revertToState(); + + vm.expectEmit(true, true, true, true); + emit ICreateXFactory.ContractCreation( + predicted = factory.computeCreate2Address(initCodeHash, salt), deployer, salt + ); + + instance = factory.createX{value: value}(ICreateXFactory.CreationType.CREATE2, initCode, salt); + assertEq(instance, predicted); + assertEq(instance.balance, value); + revertToState(); + + vm.expectEmit(true, true, true, true); + emit ICreateXFactory.ContractCreation(predicted = factory.computeCreate3Address(salt), deployer, salt); + + instance = factory.createX{value: value}(ICreateXFactory.CreationType.CREATE3, initCode, salt); + assertEq(instance, predicted); + assertEq(instance.balance, value); + revertToState(); + + vm.expectEmit(true, true, true, true); + emit ICreateXFactory.ContractCreation( + predicted = factory.computeCreateAddress(vm.getNonce(address(factory))), deployer + ); + + instance = + factory.createX{value: value}(ICreateXFactory.CreationType.Clone, abi.encodePacked(implementation), salt); + assertEq(instance, predicted); + assertEq(instance.balance, value); + revertToState(); + + vm.expectEmit(true, true, true, true); + emit ICreateXFactory.ContractCreation( + predicted = factory.computeCloneDeterministicAddress(implementation, salt), deployer, salt + ); + + instance = factory.createX{value: value}( + ICreateXFactory.CreationType.CloneDeterministic, abi.encodePacked(implementation), salt + ); + assertEq(instance, predicted); + assertEq(instance.balance, value); + } + + function test_fuzz_computeCreateXAddress(bool protected, address deployer, uint96 identifier, bytes32 hash) + public + { + if (identifier >= type(uint64).max) { + vm.expectRevert(CreateX.InvalidNonce.selector); + factory.computeCreateXAddress(ICreateXFactory.CreationType.CREATE, hash, bytes32(uint256(identifier))); + } else { + assertEq( + factory.computeCreateXAddress(ICreateXFactory.CreationType.CREATE, hash, bytes32(uint256(identifier))), + vm.computeCreateAddress(address(factory), identifier) + ); + + assertEq( + factory.computeCreateXAddress(ICreateXFactory.CreationType.Clone, hash, bytes32(uint256(identifier))), + vm.computeCreateAddress(address(factory), identifier) + ); + } + + bytes32 salt = generateSalt(deployer, identifier, protected); + + assertEq( + factory.computeCreateXAddress(ICreateXFactory.CreationType.CREATE2, hash, salt), + vm.computeCreate2Address(salt, hash, address(factory)) + ); + + assertEq( + factory.computeCreateXAddress(ICreateXFactory.CreationType.CREATE3, hash, salt), + computeCreate3Address(address(factory), salt) + ); + + address implementation = vm.addr(boundPrivateKey(uint256(hash))); + + assertEq( + factory.computeCreateXAddress( + ICreateXFactory.CreationType.CloneDeterministic, bytes32(bytes20(implementation)), salt + ), + computeCloneDeterministicAddress(address(factory), implementation, salt) + ); + } + + function test_fuzz_create(address deployer, uint256 value) public impersonate(deployer, value) { + bytes memory initCode = abi.encodePacked(type(MockTarget).creationCode); + address predicted = vm.computeCreateAddress(address(factory), vm.getNonce(address(factory))); + + vm.expectEmit(true, true, true, true); + emit ICreateXFactory.ContractCreation(predicted, deployer); + + MockTarget instance = MockTarget(factory.create{value: value}(initCode)); + assertEq(address(instance), predicted); + assertEq(address(instance).balance, value); + assertEq(instance.getValue(), uint256(0)); + assertEq(instance.setValue(value), value); + } + + function test_create_deployERC20() public { + bytes memory initCode = bytes.concat(type(MockERC20).creationCode, abi.encode("Mock Token", "MOCK", 18)); + address predicted = vm.computeCreateAddress(address(factory), vm.getNonce(address(factory))); + + vm.expectEmit(true, true, true, true); + emit ICreateXFactory.ContractCreation(predicted, address(this)); + + MockERC20 token = MockERC20(factory.create(initCode)); + assertEq(address(token), predicted); + assertEq(token.name(), "Mock Token"); + assertEq(token.symbol(), "MOCK"); + assertEq(token.decimals(), 18); + } + + function test_fuzz_create2(bool protected, address deployer, uint96 identifier, uint256 value) + public + impersonate(deployer, value) + { + bytes memory initCode = abi.encodePacked(type(MockTarget).creationCode); + bytes32 initCodeHash = keccak256(initCode); + bytes32 salt = generateSalt(deployer, identifier, protected); + address predicted = computeCreate2Address(address(factory), initCodeHash, salt); + + vm.expectEmit(true, true, true, true); + emit ICreateXFactory.ContractCreation(predicted, deployer, salt); + + MockTarget instance = MockTarget(factory.create2{value: value}(initCode, salt)); + assertEq(address(instance), predicted); + assertEq(address(instance).balance, value); + assertEq(instance.getValue(), uint256(0)); + assertEq(instance.setValue(value), value); + } + + function test_create2_deployERC20() public { + bytes memory initCode = bytes.concat(type(MockERC20).creationCode, abi.encode("Mock Token", "MOCK", 18)); + bytes32 initCodeHash = keccak256(initCode); + bytes32 salt = generateSalt(address(0), uint96(0)); + address predicted = computeCreate2Address(address(factory), initCodeHash, salt); + + vm.expectEmit(true, true, true, true); + emit ICreateXFactory.ContractCreation(predicted, address(this), salt); + + MockERC20 token = MockERC20(factory.create2(initCode, salt)); + assertEq(address(token), predicted); + assertEq(token.name(), "Mock Token"); + assertEq(token.symbol(), "MOCK"); + assertEq(token.decimals(), 18); + } + + function test_fuzz_create3(bool protected, address deployer, uint96 identifier, uint256 value) + public + impersonate(deployer, value) + { + bytes memory initCode = abi.encodePacked(type(MockTarget).creationCode); + bytes32 salt = generateSalt(deployer, identifier, protected); + address predicted = computeCreate3Address(address(factory), salt); + + vm.expectEmit(true, true, true, true); + emit ICreateXFactory.ContractCreation(predicted, deployer, salt); + + MockTarget instance = MockTarget(factory.create3{value: value}(initCode, salt)); + assertEq(address(instance), predicted); + assertEq(address(instance).balance, value); + assertEq(instance.getValue(), uint256(0)); + assertEq(instance.setValue(value), value); + } + + function test_create3_deployERC20() public { + bytes memory initCode = bytes.concat(type(MockERC20).creationCode, abi.encode("Mock Token", "MOCK", 18)); + bytes32 salt = generateSalt(address(0), uint96(0)); + address predicted = computeCreate3Address(address(factory), salt); + + vm.expectEmit(true, true, true, true); + emit ICreateXFactory.ContractCreation(predicted, address(this), salt); + + MockERC20 token = MockERC20(factory.create3(initCode, salt)); + assertEq(address(token), predicted); + assertEq(token.name(), "Mock Token"); + assertEq(token.symbol(), "MOCK"); + assertEq(token.decimals(), 18); + } + + function test_create3_chainAgnosticDeployment() public { + vm.makePersistent(address(factory)); + + bytes memory initCode = bytes.concat(type(MockERC20).creationCode, abi.encode("Mock Token", "MOCK", 18)); + bytes32 salt = generateSalt(address(this)); + + vm.createSelectFork("ethereum"); + address instance = factory.create3(initCode, salt); + + vm.createSelectFork("optimism"); + assertEq(instance, factory.create3(initCode, salt)); + + vm.createSelectFork("bnb"); + assertEq(instance, factory.create3(initCode, salt)); + + vm.createSelectFork("polygon"); + assertEq(instance, factory.create3(initCode, salt)); + + vm.createSelectFork("unichain"); + assertEq(instance, factory.create3(initCode, salt)); + + vm.createSelectFork("fantom"); + assertEq(instance, factory.create3(initCode, salt)); + + vm.createSelectFork("base"); + assertEq(instance, factory.create3(initCode, salt)); + + vm.createSelectFork("arbitrum"); + assertEq(instance, factory.create3(initCode, salt)); + + vm.createSelectFork("avalanche"); + assertEq(instance, factory.create3(initCode, salt)); + + vm.createSelectFork("scroll"); + assertEq(instance, factory.create3(initCode, salt)); + + vm.createSelectFork("linea"); + assertEq(instance, factory.create3(initCode, salt)); + } + + function test_create3_revertsOnSameSaltDeployments() public { + bytes memory mockInitCode = abi.encodePacked(type(MockTarget).creationCode); + bytes memory erc20InitCode = bytes.concat(type(MockERC20).creationCode, abi.encode("Mock Token", "MOCK", 18)); + bytes32 salt = generateSalt(address(this)); + + factory.create3(mockInitCode, salt); + vm.expectRevert(CreateX.ProxyCreationFailed.selector); + factory.create3(mockInitCode, salt); + vm.expectRevert(CreateX.ProxyCreationFailed.selector); + factory.create3(erc20InitCode, salt); + } + + function test_fuzz_clone(address deployer, uint256 value) public impersonate(deployer, value) { + address implementation = address(new MockTarget()); + address predicted = vm.computeCreateAddress(address(factory), vm.getNonce(address(factory))); + + vm.expectEmit(true, true, true, true); + emit ICreateXFactory.ContractCreation(predicted, deployer); + + MockTarget instance = MockTarget(factory.clone{value: value}(implementation)); + assertEq(address(instance), predicted); + assertEq(address(instance).balance, value); + assertEq(instance.getValue(), uint256(0)); + assertEq(instance.setValue(value), value); + } + + function test_clone_revertsIfInvalidImplementationGiven() public { + vm.expectRevert(CreateX.InvalidImplementation.selector); + factory.clone(address(0)); + + vm.expectRevert(CreateX.InvalidImplementation.selector); + factory.clone(address(0xdeadbeef)); + } + + function test_fuzz_cloneDeterministic(bool protected, address deployer, uint96 identifier, uint256 value) + public + impersonate(deployer, value) + { + bytes32 salt = generateSalt(deployer, identifier, protected); + address implementation = address(new MockTarget()); + address predicted = computeCloneDeterministicAddress(address(factory), implementation, salt); + + vm.expectEmit(true, true, true, true); + emit ICreateXFactory.ContractCreation(predicted, deployer, salt); + + MockTarget instance = MockTarget(factory.cloneDeterministic{value: value}(implementation, salt)); + assertEq(address(instance), predicted); + assertEq(address(instance).balance, value); + assertEq(instance.getValue(), uint256(0)); + assertEq(instance.setValue(value), value); + } + + function test_cloneDeterministic_revertsIfInvalidImplementationGiven() public { + vm.expectRevert(CreateX.InvalidImplementation.selector); + factory.cloneDeterministic(address(0), bytes32(0)); + + vm.expectRevert(CreateX.InvalidImplementation.selector); + factory.cloneDeterministic(address(0xdeadbeef), bytes32(0)); + } + + function test_fuzz_computeCreateAddress(address deployer, uint256 nonce) public { + if (nonce >= type(uint64).max) { + vm.expectRevert(CreateX.InvalidNonce.selector); + factory.computeCreateAddress(nonce); + } else { + assertEq(CreateX.computeCreateAddress(deployer, nonce), vm.computeCreateAddress(deployer, nonce)); + } + } + + function test_fuzz_computeCreate2Address(address deployer, bytes32 hash, bytes32 salt) public pure { + assertEq(CreateX.computeCreate2Address(deployer, hash, salt), computeCreate2Address(deployer, hash, salt)); + } + + function test_fuzz_computeCreate3Address(address deployer, bytes32 salt) public pure { + assertEq(CreateX.computeCreate3Address(deployer, salt), computeCreate3Address(deployer, salt)); + } + + function test_fuzz_computeCloneDeterministicAddress(address deployer, address implementation, bytes32 salt) + public + pure + { + assertEq( + CreateX.computeCloneDeterministicAddress(deployer, implementation, salt), + computeCloneDeterministicAddress(deployer, implementation, salt) + ); + } + + function computeCreate2Address(address deployer, bytes32 initCodeHash, bytes32 salt) + internal + pure + returns (address predicted) + { + return address(uint160(uint256(keccak256(abi.encodePacked(hex"ff", deployer, salt, initCodeHash))))); + } + + function computeCreate3Address(address deployer, bytes32 salt) internal pure returns (address predicted) { + bytes32 initCodeHash = 0x21c35dbe1b344a2488cf3321d6ce542f8e9f305544ff09e4993a62319a497c1f; + address proxy = computeCreate2Address(deployer, initCodeHash, salt); + return address(uint160(uint256(keccak256(abi.encodePacked(hex"d694", proxy, hex"01"))))); + } + + function computeCloneDeterministicAddress(address deployer, address implementation, bytes32 salt) + internal + pure + returns (address predicted) + { + bytes32 initCodeHash = keccak256( + abi.encodePacked( + hex"3d602d80600a3d3981f3363d3d373d3d3d363d73", + bytes20(implementation), + hex"5af43d82803e903d91602b57fd5bf3" + ) + ); + return computeCreate2Address(deployer, initCodeHash, salt); + } + + function generateSalt(address caller) internal returns (bytes32 salt) { + return generateSalt(caller, uint96(vm.randomUint(type(uint96).min, type(uint96).max))); + } + + function generateSalt(address caller, uint96 identifier) internal pure returns (bytes32 salt) { + return bytes32((uint256(uint160(caller)) << 96) | uint256(identifier)); + } + + function generateSalt(address caller, uint96 identifier, bool protected) internal pure returns (bytes32 salt) { + return generateSalt(protected ? caller : address(0), identifier); + } + + function revertToState() internal { + if (snapshotId != type(uint256).max) vm.revertToState(snapshotId); + snapshotId = vm.snapshotState(); + } } diff --git a/test/mocks/MockERC20.sol b/test/mocks/MockERC20.sol index fc69745..26b7770 100644 --- a/test/mocks/MockERC20.sol +++ b/test/mocks/MockERC20.sol @@ -2,160 +2,144 @@ pragma solidity ^0.8.30; contract MockERC20 { - event Approval(address indexed owner, address indexed spender, uint256 amount); - event Transfer(address indexed from, address indexed to, uint256 amount); + event Approval(address indexed owner, address indexed spender, uint256 amount); + event Transfer(address indexed from, address indexed to, uint256 amount); - mapping(address => mapping(address => uint256)) public allowance; + mapping(address => mapping(address => uint256)) public allowance; - mapping(address => uint256) public balanceOf; + mapping(address => uint256) public balanceOf; - mapping(address => uint256) public nonces; + mapping(address => uint256) public nonces; - uint256 public totalSupply; + uint256 public totalSupply; - string public name; + string public name; - string public symbol; + string public symbol; - uint8 public immutable decimals; + uint8 public immutable decimals; - uint256 internal immutable INITIAL_CHAIN_ID; + uint256 internal immutable INITIAL_CHAIN_ID; - bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR; + bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR; - constructor(string memory _name, string memory _symbol, uint8 _decimals) { - name = _name; - symbol = _symbol; - decimals = _decimals; + constructor(string memory _name, string memory _symbol, uint8 _decimals) { + name = _name; + symbol = _symbol; + decimals = _decimals; - INITIAL_CHAIN_ID = block.chainid; - INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator(); - } + INITIAL_CHAIN_ID = block.chainid; + INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator(); + } - function mint(address to, uint256 amount) public virtual { - totalSupply += amount; + function mint(address to, uint256 amount) public virtual { + totalSupply += amount; - // Cannot overflow because the sum of all user - // balances can't exceed the max uint256 value. - unchecked { - balanceOf[to] += amount; - } + unchecked { + balanceOf[to] += amount; + } - emit Transfer(address(0), to, amount); - } + emit Transfer(address(0), to, amount); + } - function burn(address from, uint256 amount) public virtual { - balanceOf[from] -= amount; + function burn(address from, uint256 amount) public virtual { + balanceOf[from] -= amount; - // Cannot underflow because a user's balance - // will never be larger than the total supply. - unchecked { - totalSupply -= amount; - } + unchecked { + totalSupply -= amount; + } - emit Transfer(from, address(0), amount); - } - - function approve(address spender, uint256 amount) public virtual returns (bool) { - allowance[msg.sender][spender] = amount; - - emit Approval(msg.sender, spender, amount); - - return true; - } - - function transfer(address to, uint256 amount) public virtual returns (bool) { - balanceOf[msg.sender] -= amount; - - // Cannot overflow because the sum of all user - // balances can't exceed the max uint256 value. - unchecked { - balanceOf[to] += amount; - } - - emit Transfer(msg.sender, to, amount); - - return true; - } - - function transferFrom(address from, address to, uint256 amount) public virtual returns (bool) { - uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals. - - if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount; - - balanceOf[from] -= amount; - - // Cannot overflow because the sum of all user - // balances can't exceed the max uint256 value. - unchecked { - balanceOf[to] += amount; - } - - emit Transfer(from, to, amount); - - return true; - } - - function permit( - address owner, - address spender, - uint256 value, - uint256 deadline, - uint8 v, - bytes32 r, - bytes32 s - ) public virtual { - require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED"); - - // Unchecked because the only math done is incrementing - // the owner's nonce which cannot realistically overflow. - unchecked { - address recoveredAddress = ecrecover( - keccak256( - abi.encodePacked( - "\x19\x01", - DOMAIN_SEPARATOR(), - keccak256( - abi.encode( - keccak256( - "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)" - ), - owner, - spender, - value, - nonces[owner]++, - deadline - ) - ) - ) - ), - v, - r, - s - ); - - require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER"); - - allowance[recoveredAddress][spender] = value; - } - - emit Approval(owner, spender, value); - } - - function DOMAIN_SEPARATOR() public view virtual returns (bytes32) { - return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator(); - } - - function computeDomainSeparator() internal view virtual returns (bytes32) { - return - keccak256( - abi.encode( - keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"), - keccak256(bytes(name)), - keccak256("1"), - block.chainid, - address(this) - ) - ); - } + emit Transfer(from, address(0), amount); + } + + function approve(address spender, uint256 amount) public virtual returns (bool) { + allowance[msg.sender][spender] = amount; + + emit Approval(msg.sender, spender, amount); + + return true; + } + + function transfer(address to, uint256 amount) public virtual returns (bool) { + balanceOf[msg.sender] -= amount; + + unchecked { + balanceOf[to] += amount; + } + + emit Transfer(msg.sender, to, amount); + + return true; + } + + function transferFrom(address from, address to, uint256 amount) public virtual returns (bool) { + uint256 allowed = allowance[from][msg.sender]; + + if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount; + + balanceOf[from] -= amount; + + unchecked { + balanceOf[to] += amount; + } + + emit Transfer(from, to, amount); + + return true; + } + + function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) + public + virtual + { + require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED"); + + unchecked { + address recoveredAddress = ecrecover( + keccak256( + abi.encodePacked( + "\x19\x01", + DOMAIN_SEPARATOR(), + keccak256( + abi.encode( + keccak256( + "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)" + ), + owner, + spender, + value, + nonces[owner]++, + deadline + ) + ) + ) + ), + v, + r, + s + ); + + require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER"); + + allowance[recoveredAddress][spender] = value; + } + + emit Approval(owner, spender, value); + } + + function DOMAIN_SEPARATOR() public view virtual returns (bytes32) { + return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator(); + } + + function computeDomainSeparator() internal view virtual returns (bytes32) { + return keccak256( + abi.encode( + keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"), + keccak256(bytes(name)), + keccak256("1"), + block.chainid, + address(this) + ) + ); + } } diff --git a/test/mocks/MockTarget.sol b/test/mocks/MockTarget.sol index 387516c..d247db8 100644 --- a/test/mocks/MockTarget.sol +++ b/test/mocks/MockTarget.sol @@ -2,15 +2,15 @@ pragma solidity ^0.8.30; contract MockTarget { - uint256 private _value; + uint256 private _value; - constructor() payable {} + constructor() payable {} - function setValue(uint256 value) public returns (uint256) { - return _value = value; - } + function setValue(uint256 value) public returns (uint256) { + return _value = value; + } - function getValue() public view returns (uint256) { - return _value; - } + function getValue() public view returns (uint256) { + return _value; + } }