From b9040ad0cb226d987f06bbc63faeae6f4bbeca32 Mon Sep 17 00:00:00 2001 From: Ryan Date: Thu, 11 Sep 2025 23:43:06 -0700 Subject: [PATCH] Updated scripts --- .gitignore | 2 + .prettierrc.json | 17 ++--- README.md | 28 ++++---- foundry.toml | 2 + script/BaseScript.sol | 121 ++++++++++++++++++++++++++++++++++ script/CreateX.s.sol | 145 +++++++++++++++++++++++++++++++++++++++++ script/Deploy.s.sol | 55 +++------------- script/Precompiles.sol | 16 +++++ 8 files changed, 316 insertions(+), 70 deletions(-) create mode 100644 script/BaseScript.sol create mode 100644 script/CreateX.s.sol create mode 100644 script/Precompiles.sol diff --git a/.gitignore b/.gitignore index 85198aa..f0e124c 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,5 @@ docs/ # Dotenv file .env + +.DS_Store \ No newline at end of file diff --git a/.prettierrc.json b/.prettierrc.json index 9376626..e6af006 100644 --- a/.prettierrc.json +++ b/.prettierrc.json @@ -1,22 +1,19 @@ { + "printWidth": 120, + "tabWidth": 4, + "useTabs": true, + "singleQuote": false, + "trailingComma": "all", "overrides": [ { "files": "*.sol", "options": { - "printWidth": 120, - "tabWidth": 4, - "useTabs": true, - "singleQuote": false, "bracketSpacing": false } }, { - "files": ["*.json"], - "options": { - "tabWidth": 4, - "useTabs": true, - "singleQuote": false - } + "files": ["*.ts", "*.js", "*.json"], + "options": {} } ] } diff --git a/README.md b/README.md index 41495d2..39f8950 100644 --- a/README.md +++ b/README.md @@ -4,13 +4,13 @@ ## Features -- **CREATE**: Traditional contract deployment -- **CREATE2**: Deterministic contract deployment -- **CREATE3**: Chain-agnostic deterministic deployment -- **EIP-1167 Clone**: Minimal proxy pattern deployment -- **EIP-1167 CloneDeterministic**: Deterministic minimal proxy deployment -- **Address Prediction**: Compute contract addresses before deployment -- **Dual Approach**: Use as a library in your contracts or interact with the deployed factory +- **CREATE**: Traditional contract deployment +- **CREATE2**: Deterministic contract deployment +- **CREATE3**: Chain-agnostic deterministic deployment +- **EIP-1167 Clone**: Minimal proxy pattern deployment +- **EIP-1167 CloneDeterministic**: Deterministic minimal proxy deployment +- **Address Prediction**: Compute contract addresses before deployment +- **Dual Approach**: Use as a library in your contracts or interact with the deployed factory ## Directory @@ -18,7 +18,7 @@ createx/ ├── deployments/... ├── script/ -│ ├── Create.s.sol +│ ├── CreateX.s.sol │ └── Deploy.s.sol ├── src/ │ ├── CreateX.sol @@ -322,9 +322,9 @@ function deployCreateX( The factory includes built-in access control through salt validation: -- For salted deployments (CREATE2, CREATE3, CloneDeterministic), the first 20 bytes of the salt must match either: - - The caller's address (for user-specific deployments) - - Zero address (for open deployments) +- For salted deployments (CREATE2, CREATE3, CloneDeterministic), the first 20 bytes of the salt must match either: + - The caller's address (for user-specific deployments) + - Zero address (for open deployments) --- @@ -332,9 +332,9 @@ The factory includes built-in access control through salt validation: The following repositories served as key references during the development of this project: -- [Solady](https://github.com/Vectorized/solady) -- [OpenZeppelin](https://github.com/OpenZeppelin/openzeppelin-contracts) +- [Solady](https://github.com/Vectorized/solady) +- [OpenZeppelin](https://github.com/OpenZeppelin/openzeppelin-contracts) ## Author -- [fomoweth](https://github.com/fomoweth) +- [fomoweth](https://github.com/fomoweth) diff --git a/foundry.toml b/foundry.toml index f03cbcc..63c96dd 100644 --- a/foundry.toml +++ b/foundry.toml @@ -9,12 +9,14 @@ ffi = true optimizer = true optimizer_runs = 44444444 via_ir = true + fs_permissions = [ { access = "read-write", path = "./deployments"}, { access = "read", path = "./out"}, { access = "read", path = "./script" }, { access = "read", path = "./test"} ] + gas_reports = ["CreateXFactory"] [fuzz] diff --git a/script/BaseScript.sol b/script/BaseScript.sol new file mode 100644 index 0000000..383e415 --- /dev/null +++ b/script/BaseScript.sol @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.30; + +import {Script, console2 as console, stdJson} from "forge-std/Script.sol"; +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); + } +} diff --git a/script/CreateX.s.sol b/script/CreateX.s.sol new file mode 100644 index 0000000..73f1226 --- /dev/null +++ b/script/CreateX.s.sol @@ -0,0 +1,145 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.30; + +import {ICreateXFactory} from "src/ICreateXFactory.sol"; +import {CreateX as CreateXLib} from "src/CreateX.sol"; +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); + } +} diff --git a/script/Deploy.s.sol b/script/Deploy.s.sol index 88ccd0f..ef4cfeb 100644 --- a/script/Deploy.s.sol +++ b/script/Deploy.s.sol @@ -1,61 +1,24 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.30; -import {Script, console2 as console, stdJson} from "forge-std/Script.sol"; import {CreateXFactory} from "src/CreateXFactory.sol"; +import {BaseScript} from "./BaseScript.sol"; -contract Deploy is Script { - using stdJson for string; - - string private constant DEFAULT_MNEMONIC = "test test test test test test test test test test test junk"; - bytes32 private constant DEFAULT_SALT = 0x0000000000000000000000000000000000000000000000000000000000000000; - - address internal broadcaster; +contract Deploy is BaseScript { bytes32 internal salt; - modifier broadcast(string memory chainAlias) { - vm.createSelectFork(chainAlias); - vm.startBroadcast(broadcaster); - _; - vm.stopBroadcast(); - } - - function setUp() public { - uint256 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)})) - }) - }); - - broadcaster = vm.rememberKey(privateKey); - salt = vm.envOr({name: "SALT", defaultValue: DEFAULT_SALT}); + function setUp() public virtual override { + super.setUp(); + salt = vm.envOr({name: "SALT", defaultValue: defaultSalt()}); } function run() external { - string[] memory chainAliases = vm.envString({name: "CHAINS", delim: ","}); + string[] memory chainAliases = promptChains(); for (uint256 i; i < chainAliases.length; ++i) deployOnChain(chainAliases[i]); } - function deployOnChain(string memory chainAlias) internal virtual broadcast(chainAlias) { - string memory path = string.concat("./deployments/", vm.toString(block.chainid), ".json"); - - console.log(); - console.log("======================================================================"); - console.log("Chain ID:", block.chainid); - - CreateXFactory createXFactory = new CreateXFactory{salt: salt}(); - - string memory json = "deployment"; - json.serialize("address", address(createXFactory)); - json.serialize("block", block.number); - json.serialize("salt", salt); - json = json.serialize("timestamp", block.timestamp); - json.write(path); - - console.log("Deployed at:", address(createXFactory)); - console.log("======================================================================"); - console.log(); + 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 new file mode 100644 index 0000000..64afe0e --- /dev/null +++ b/script/Precompiles.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.30; + +bytes constant CREATEX_FACTORY_BYTECODE = hex"60808060405234601557610aaa908161001a8239f35b5f80fdfe60806040526004361015610011575f80fd5b5f3560e01c806312b40d531461031b57806328ddd046146102de578063315c8d9c146102785780636cec25361461023b5780638124b78e146101ff578063890c283b146101bf5780638c0b8db2146101ab578063a7b62a7f14610197578063b86b2ceb14610157578063cf5ba53f146101025763df6a602c14610092575f80fd5b346100fe5760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100fe5760043560058110156100fe576100e060209160443590602435906108ee565b73ffffffffffffffffffffffffffffffffffffffff60405191168152f35b5f80fd5b60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100fe5760043567ffffffffffffffff81116100fe576100e061015160209236906004016103d7565b90610888565b60407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100fe5760206100e061018e6103b4565b602435906107d5565b60206100e06101a536610405565b9161071a565b60206100e06101b936610405565b916105f4565b346100fe5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100fe5760206100e060243560043530610a8e565b60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100fe5760206100e06102366103b4565b61052b565b346100fe5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100fe5760206100e060043530610a3e565b60607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100fe5760043560058110156100fe5760243567ffffffffffffffff81116100fe576020916102d46100e09236906004016103d7565b9060443592610456565b346100fe5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100fe5760206100e0600435306109b4565b346100fe5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100fe5760206100e06103576103b4565b60243590309160439160559360405192605884015260388301526f5af43d82803e903d91602b57fd5bf3ff60248301526014820152733d602d80600a3d3981f3363d3d373d3d3d363d7381526037600c8201206078820152012090565b6004359073ffffffffffffffffffffffffffffffffffffffff821682036100fe57565b9181601f840112156100fe5782359167ffffffffffffffff83116100fe57602083818601950101116100fe57565b60407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8201126100fe576004359067ffffffffffffffff82116100fe5761044e916004016103d7565b909160243590565b92909260058110156104fe578061047557505061047291610888565b90565b9091906001810361048a5750610472926105f4565b6002810361049c57506104729261071a565b600381036104bb575090506014116100fe57610472903560601c61052b565b6004036104d6576014116100fe57610472913560601c6107d5565b7f5fc1076a000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b803b158160601b15176105e7573447106105da5780763d602d80600a3d3981f3363d3d373d3d3d363d7300000062ffffff6e5af43d82803e903d91602b57fd5bf39360881c16175f5260781b176020526037600934f090813b158260601b15176105cd573373ffffffffffffffffffffffffffffffffffffffff83167fb6c9f70724e4ce6b9bca2eace320dd3164063f0a0b229f8c4705ef05be829c255f80a3565b63a28c24735f526004601cfd5b63f4d678b85f526004601cfd5b6368155f9a5f526004601cfd5b8260601c3381149015171561066b5761060e913691610678565b3447106105da578051829160200134f590813b158260601b15176105cd573373ffffffffffffffffffffffffffffffffffffffff83167f09005d6d86a7a6ccbee89763e10463ab645e06615f10a6a2fe604a62f31843ef5f80a490565b6381e69d9b5f526004601cfd5b92919267ffffffffffffffff82116106ed57604051917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0603f81601f8401160116830183811067ffffffffffffffff8211176106ed576040528294818452818301116100fe578281602093845f960137010152565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b8260601c3381149015171561066b57610734913691610678565b903447106105da576f67363d3d37363d34f03d5260086018f35f52806010805ff58060601b156107c857805f9160145261d694825260016034536017601e209338916020825192019034905af1823b02156105cd573373ffffffffffffffffffffffffffffffffffffffff83167f09005d6d86a7a6ccbee89763e10463ab645e06615f10a6a2fe604a62f31843ef5f80a490565b63d49e7d745f526004601cfd5b8160601c3381149015171561066b57803b158160601b15176105e7573447106105da5780763d602d80600a3d3981f3363d3d373d3d3d363d7300000062ffffff6e5af43d82803e903d91602b57fd5bf39360881c16175f5260781b17602052806037600934f590813b158260601b15176105cd573373ffffffffffffffffffffffffffffffffffffffff83167f09005d6d86a7a6ccbee89763e10463ab645e06615f10a6a2fe604a62f31843ef5f80a490565b610893913691610678565b3447106105da5760208151910134f090813b158260601b15176105cd573373ffffffffffffffffffffffffffffffffffffffff83167fb6c9f70724e4ce6b9bca2eace320dd3164063f0a0b229f8c4705ef05be829c255f80a3565b9190916005811015806104fe57811580156109a8575b156109165750506104729150306109b4565b6104fe576001810361092d57506104729130610a8e565b600281036109415750610472915030610a3e565b6004036104d6576104729160601c309160439160559360405192605884015260388301526f5af43d82803e903d91602b57fd5bf3ff60248301526014820152733d602d80600a3d3981f3363d3d373d3d3d363d7381526037600c8201206078820152012090565b50505f60038214610904565b67ffffffffffffffff821015610a3157609460015360601b6002525f9060808110610a16579081825b610a045760179250816080016016538160031b610100031b82525b8060d6015f53015f2090565b9060019260081c9283910191906109dd565b908160179215610a29575b6016536109f8565b506080610a21565b63756688fe5f526004601cfd5b90604051915f5260ff600b536020527f21c35dbe1b344a2488cf3321d6ce542f8e9f305544ff09e4993a62319a497c1f6040526055600b2060145260405261d6945f5260016034536017601e2090565b9060ff5f5360355260601b60015260155260555f20905f60355256"; + +bytes constant ERC1967_PROXY_BYTECODE = hex"60806040526102728038038061001481610168565b92833981016040828203126101645781516001600160a01b03811692909190838303610164576020810151906001600160401b03821161016457019281601f8501121561016457835161006e610069826101a1565b610168565b9481865260208601936020838301011161016457815f926020809301865e86010152823b15610152577f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80546001600160a01b031916821790557fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b5f80a282511561013a575f8091610122945190845af43d15610132573d91610113610069846101a1565b9283523d5f602085013e6101bc565b505b6040516057908161021b8239f35b6060916101bc565b50505034156101245763b398979f60e01b5f5260045ffd5b634c9c8ce360e01b5f5260045260245ffd5b5f80fd5b6040519190601f01601f191682016001600160401b0381118382101761018d57604052565b634e487b7160e01b5f52604160045260245ffd5b6001600160401b03811161018d57601f01601f191660200190565b906101e057508051156101d157805190602001fd5b63d6bda27560e01b5f5260045ffd5b81511580610211575b6101f1575090565b639996b31560e01b5f9081526001600160a01b0391909116600452602490fd5b50803b156101e956fe60806040525f8073ffffffffffffffffffffffffffffffffffffffff7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5416368280378136915af43d5f803e156053573d5ff35b3d5ffd"; + +bytes constant TRANSPARENT_PROXY_BYTECODE = hex"60a0604052610db6803803806100148161026b565b92833981016060828203126102675761002c82610290565b61003860208401610290565b604084015190936001600160401b03821161026757019180601f8401121561026757825161006d610068826102a4565b61026b565b9381855260208501926020838301011161026757815f926020809301855e85010152813b15610246577f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80546001600160a01b0319166001600160a01b0384169081179091557fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b5f80a282511561022e575f809161012d945190845af43d15610226573d9161011e610068846102a4565b9283523d5f602085013e6102bf565b505b604051906105fd8083016001600160401b0381118482101761021257602092849261079984396001600160a01b031681520301905ff080156102075760018060a01b0316806080525f516020610d965f395f51905f52547f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f6040805160018060a01b0384168152846020820152a181156101f4576001600160a01b031916175f516020610d965f395f51905f525560405161047b908161031e82396080518160070152f35b633173bdd160e11b5f525f60045260245ffd5b6040513d5f823e3d90fd5b634e487b7160e01b5f52604160045260245ffd5b6060916102bf565b505050341561012f5763b398979f60e01b5f5260045ffd5b50634c9c8ce360e01b5f9081526001600160a01b0391909116600452602490fd5b5f80fd5b6040519190601f01601f191682016001600160401b0381118382101761021257604052565b51906001600160a01b038216820361026757565b6001600160401b03811161021257601f01601f191660200190565b906102e357508051156102d457805190602001fd5b63d6bda27560e01b5f5260045ffd5b81511580610314575b6102f4575090565b639996b31560e01b5f9081526001600160a01b0391909116600452602490fd5b50803b156102ec56fe6080604052337f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16036100bd575f357fffffffff00000000000000000000000000000000000000000000000000000000167f4f1ef28600000000000000000000000000000000000000000000000000000000146100b3577fd2b576ec000000000000000000000000000000000000000000000000000000005f5260045ffd5b6100bb6101c1565b005b5f8073ffffffffffffffffffffffffffffffffffffffff7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5416368280378136915af43d5f803e1561010d573d5ff35b3d5ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f604051930116820182811067ffffffffffffffff82111761018257604052565b610111565b67ffffffffffffffff811161018257601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b366004116102745760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102745760043573ffffffffffffffffffffffffffffffffffffffff8116809103610274576024359067ffffffffffffffff821161027457366023830112156102745781600401359061024961024483610187565b61013e565b918083523660248286010111610274576020815f92602461027297018387013784010152610278565b565b5f80fd5b90813b156103675773ffffffffffffffffffffffffffffffffffffffff8216807fffffffffffffffffffffffff00000000000000000000000000000000000000007f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5416177f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc557fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b5f80a280511561033657610333916103a9565b50565b50503461033f57565b7fb398979f000000000000000000000000000000000000000000000000000000005f5260045ffd5b73ffffffffffffffffffffffffffffffffffffffff827f4c9c8ce3000000000000000000000000000000000000000000000000000000005f521660045260245ffd5b5f806103db93602081519101845af43d156103de573d916103cc61024484610187565b9283523d5f602085013e6103e2565b90565b6060915b9061041f57508051156103f757805190602001fd5b7fd6bda275000000000000000000000000000000000000000000000000000000005f5260045ffd5b81511580610472575b610430575090565b73ffffffffffffffffffffffffffffffffffffffff907f9996b315000000000000000000000000000000000000000000000000000000005f521660045260245ffd5b50803b156104285660803460b857601f6105fd38819003918201601f19168301916001600160401b0383118484101760bc5780849260209460405283398101031260b857516001600160a01b0381169081900360b857801560a5575f80546001600160a01b031981168317825560405192916001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09080a361052c90816100d18239f35b631e4fbdf760e01b5f525f60045260245ffd5b5f80fd5b634e487b7160e01b5f52604160045260245ffdfe60806040526004361015610011575f80fd5b5f5f3560e01c8063715018a6146103c25780638da5cb5b146103725780639623609d146101c9578063ad3cb1cc146101465763f2fde38b14610051575f80fd5b346101435760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101435760043573ffffffffffffffffffffffffffffffffffffffff8116809103610141576100a96104e0565b80156101155773ffffffffffffffffffffffffffffffffffffffff8254827fffffffffffffffffffffffff00000000000000000000000000000000000000008216178455167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08380a380f35b6024827f1e4fbdf700000000000000000000000000000000000000000000000000000000815280600452fd5b505b80fd5b503461014357807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261014357506101c560405161018760408261045c565b600581527f352e302e30000000000000000000000000000000000000000000000000000000602082015260405191829160208352602083019061049d565b0390f35b5060607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103415760043573ffffffffffffffffffffffffffffffffffffffff81168091036103415760243573ffffffffffffffffffffffffffffffffffffffff81168091036103415760443567ffffffffffffffff8111610341573660238201121561034157806004013567ffffffffffffffff8111610345576040519161029d601f83017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0166020018461045c565b818352366024838301011161034157815f9260246020930183860137830101526102c56104e0565b823b1561034157610314925f926040518095819482937f4f1ef286000000000000000000000000000000000000000000000000000000008452600484015260406024840152604483019061049d565b039134905af1801561033657610328575080f35b61033491505f9061045c565b005b6040513d5f823e3d90fd5b5f80fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b34610341575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261034157602073ffffffffffffffffffffffffffffffffffffffff5f5416604051908152f35b34610341575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610341576103f86104e0565b5f73ffffffffffffffffffffffffffffffffffffffff81547fffffffffffffffffffffffff000000000000000000000000000000000000000081168355167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761034557604052565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f602080948051918291828752018686015e5f8582860101520116010190565b73ffffffffffffffffffffffffffffffffffffffff5f5416330361050057565b7f118cdaa7000000000000000000000000000000000000000000000000000000005f523360045260245ffdb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103"; + +bytes constant PROXY_ADMIN_BYTECODE = hex"60803460b857601f6105fd38819003918201601f19168301916001600160401b0383118484101760bc5780849260209460405283398101031260b857516001600160a01b0381169081900360b857801560a5575f80546001600160a01b031981168317825560405192916001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09080a361052c90816100d18239f35b631e4fbdf760e01b5f525f60045260245ffd5b5f80fd5b634e487b7160e01b5f52604160045260245ffdfe60806040526004361015610011575f80fd5b5f5f3560e01c8063715018a6146103c25780638da5cb5b146103725780639623609d146101c9578063ad3cb1cc146101465763f2fde38b14610051575f80fd5b346101435760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101435760043573ffffffffffffffffffffffffffffffffffffffff8116809103610141576100a96104e0565b80156101155773ffffffffffffffffffffffffffffffffffffffff8254827fffffffffffffffffffffffff00000000000000000000000000000000000000008216178455167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08380a380f35b6024827f1e4fbdf700000000000000000000000000000000000000000000000000000000815280600452fd5b505b80fd5b503461014357807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261014357506101c560405161018760408261045c565b600581527f352e302e30000000000000000000000000000000000000000000000000000000602082015260405191829160208352602083019061049d565b0390f35b5060607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103415760043573ffffffffffffffffffffffffffffffffffffffff81168091036103415760243573ffffffffffffffffffffffffffffffffffffffff81168091036103415760443567ffffffffffffffff8111610341573660238201121561034157806004013567ffffffffffffffff8111610345576040519161029d601f83017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0166020018461045c565b818352366024838301011161034157815f9260246020930183860137830101526102c56104e0565b823b1561034157610314925f926040518095819482937f4f1ef286000000000000000000000000000000000000000000000000000000008452600484015260406024840152604483019061049d565b039134905af1801561033657610328575080f35b61033491505f9061045c565b005b6040513d5f823e3d90fd5b5f80fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b34610341575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261034157602073ffffffffffffffffffffffffffffffffffffffff5f5416604051908152f35b34610341575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610341576103f86104e0565b5f73ffffffffffffffffffffffffffffffffffffffff81547fffffffffffffffffffffffff000000000000000000000000000000000000000081168355167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761034557604052565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f602080948051918291828752018686015e5f8582860101520116010190565b73ffffffffffffffffffffffffffffffffffffffff5f5416330361050057565b7f118cdaa7000000000000000000000000000000000000000000000000000000005f523360045260245ffd"; + +bytes constant FORGE_PROXY_BYTECODE = hex"60a0806040526106a28038038091610017828561023a565b83398101906060818303126102225761002f8161025d565b9161003c6020830161025d565b604083015190926001600160401b038211610222570181601f820112156102225780516001600160401b0381116102265760405191610085601f8301601f19166020018461023a565b81835260208301936020838301011161022257815f926020809301865e83010152604051936101206102f86100bd602082018861023a565b80875260208701906103aa8239604080516001600160a01b039097166020888101918252808952916100ef908961023a565b6040519788938385019a5180918c5e840190838201905f8252519283915e01015f815203601f19810186528561023a565b803b1561021557807f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc55807fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b5f80a28151156101ff575f92839251915af4156101f7575b51905ff08060601b156101f757807fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d610355806020527f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f60405fa1608052604051610138908161027282396080518160070152f35b3d5f803e3d5ffd5b505050341561018457636fb1b0e95f526004601cfd5b6368155f9a5f526004601cfd5b5f80fd5b634e487b7160e01b5f52604160045260245ffd5b601f909101601f19168101906001600160401b0382119082101761022657604052565b51906001600160a01b03821682036102225756fe6080604052337f00000000000000000000000000000000000000000000000000000000000000001460011461006b57365f80375f8036817f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc545af43d5f803e15610067573d5ff35b3d5ffd5b634f1ef2865f3560e01c0361012b5773ffffffffffffffffffffffffffffffffffffffff60043516803b1561011e57807f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc55807fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b5f80a260443590811561010857815f8093928193606483375af41561010057005b3d5f803e3d5ffd5b50503461011157005b636fb1b0e95f526004601cfd5b6368155f9a5f526004601cfd5b63d2b576ec5f526004601cfd60803460b757601f6102f838819003918201601f19168301916001600160401b0383118484101760bb5780849260209460405283398101031260b757516001600160a01b038116810360b7578060601b1560aa57807f9bc353c4ee8d049c7cb68b79467fc95d9015a8a82334bd0e61ce699e20cb5bd5555f7f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08180a360405161022890816100d08239f35b6354a567865f526004601cfd5b5f80fd5b634e487b7160e01b5f52604160045260245ffdfe6004361061021b575f3560e01c8080638da5cb5b146101f25763ad3cb1cc146101c057337f9bc353c4ee8d049c7cb68b79467fc95d9015a8a82334bd0e61ce699e20cb5bd554036101af5780639623609d146101295763f2fde38b1461006c57637352d91c5f526004601cfd5b6004357fffffffffffffffffffffffffffffffffffffffff00000000000000000000000073ffffffffffffffffffffffffffffffffffffffff82169160601b161561011c57807f9bc353c4ee8d049c7cb68b79467fc95d9015a8a82334bd0e61ce699e20cb5bd5547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a37f9bc353c4ee8d049c7cb68b79467fc95d9015a8a82334bd0e61ce699e20cb5bd555005b6354a567865f526004601cfd5b50634f1ef2865f527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc36016024602037604080525f807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601601c3473ffffffffffffffffffffffffffffffffffffffff600435165af1156101a757005b3d5f803e3d5ffd5b6332b2baa35f52336020526024601cfd5b60205f5260056020527f352e302e3000000000000000000000000000000000000000000000000000000060405260605ff35b7f9bc353c4ee8d049c7cb68b79467fc95d9015a8a82334bd0e61ce699e20cb5bd5545f5260205ff35b63ca0ad2605f526004601cfd"; + +bytes constant FORGE_PROXY_ADMIN_BYTECODE = hex"60803460b757601f6102f838819003918201601f19168301916001600160401b0383118484101760bb5780849260209460405283398101031260b757516001600160a01b038116810360b7578060601b1560aa57807f9bc353c4ee8d049c7cb68b79467fc95d9015a8a82334bd0e61ce699e20cb5bd5555f7f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08180a360405161022890816100d08239f35b6354a567865f526004601cfd5b5f80fd5b634e487b7160e01b5f52604160045260245ffdfe6004361061021b575f3560e01c8080638da5cb5b146101f25763ad3cb1cc146101c057337f9bc353c4ee8d049c7cb68b79467fc95d9015a8a82334bd0e61ce699e20cb5bd554036101af5780639623609d146101295763f2fde38b1461006c57637352d91c5f526004601cfd5b6004357fffffffffffffffffffffffffffffffffffffffff00000000000000000000000073ffffffffffffffffffffffffffffffffffffffff82169160601b161561011c57807f9bc353c4ee8d049c7cb68b79467fc95d9015a8a82334bd0e61ce699e20cb5bd5547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a37f9bc353c4ee8d049c7cb68b79467fc95d9015a8a82334bd0e61ce699e20cb5bd555005b6354a567865f526004601cfd5b50634f1ef2865f527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc36016024602037604080525f807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601601c3473ffffffffffffffffffffffffffffffffffffffff600435165af1156101a757005b3d5f803e3d5ffd5b6332b2baa35f52336020526024601cfd5b60205f5260056020527f352e302e3000000000000000000000000000000000000000000000000000000060405260605ff35b7f9bc353c4ee8d049c7cb68b79467fc95d9015a8a82334bd0e61ce699e20cb5bd5545f5260205ff35b63ca0ad2605f526004601cfd"; + +bytes constant PROXY_FORGE_BYTECODE = hex"60a08060405234603e57306080526113f0908161004382396080518181816101da0152818161073a0152818161089e0152818161092e01526109be0152f35b5f80fdfe6080806040526004361015610012575f80fd5b5f3560e01c90816314afd79e14610a46575080632abbef15146109e75780633729f922146109575780634314f1201461090a578063525542d614610831578063545e7c61146106e657806374a8f1031461062c5780638b4872b8146105c657806392d9f765146104ee5780639623609d1461044357806399a88ec414610346578063a97b90d5146101525763f00d4b5d146100ab575f80fd5b60407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261014e576100dd610aa9565b6100e5610acc565b9063c12fa8d68160601b175f5260205f203381540361013d578260601b15610130578290557f1b185f8166e5b540f041c2132c66d6c691b0674cd3a95ccc9592a43dd64ad6e25f80a3005b63074b52c95f526004601cfd5b6332b2baa35f52336020526024601cfd5b5f80fd5b60807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261014e57610184610aa9565b61018c610acc565b6044359060643567ffffffffffffffff811161014e576101b0903690600401610aef565b8260601b15610130578360601c338114901517156103395761022f610252926102036040519384927f00000000000000000000000000000000000000000000000000000000000000008a60208601610bac565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08101835282610c13565b6040516106a16102426020820183610c13565b808252610d4f6020830139610c81565b34471061032c578051839160200134f591823b158360601b9081151761031f5760209463c12fa8d69284867fd283ed05905c0eb69fe3ef042c6ad706d8d9c75b138624098de540fa2c011a055f80a463a1337b4d82175f5280865f2055847f3684250ce1e33b790ed973c23080f312db0adb21a6d98c61a5c9ff99e4babc175f80a3175f5280835f2055817f1b185f8166e5b540f041c2132c66d6c691b0674cd3a95ccc9592a43dd64ad6e25f80a373ffffffffffffffffffffffffffffffffffffffff60405191168152f35b63a28c24735f526004601cfd5b63f4d678b85f526004601cfd5b6381e69d9b5f526004601cfd5b60407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261014e57610378610aa9565b610380610acc565b9061038a81610cdf565b8160601b63c12fa8d681175f523360205f20540361013d5763a1337b4d175f5260205f209083825414610436575f60405191639623609d83528460208401528560408401526060808401528160808401528136813760a43891601c85019034905af11561041b57508290557f3684250ce1e33b790ed973c23080f312db0adb21a6d98c61a5c9ff99e4babc175f80a3005b3d15610429573d5f823e3d90fd5b6355299b495f526004601cfd5b63dcd488e35f526004601cfd5b61044c36610b1d565b90610458849394610cdf565b8360601b63c12fa8d681175f523360205f20540361013d5763a1337b4d175f5260205f209285845414610436575f918160405194639623609d865287602087015288604087015260608087015281608087015260a086013760a438920190601c85019034905af11561041b57508290557f3684250ce1e33b790ed973c23080f312db0adb21a6d98c61a5c9ff99e4babc175f80a3005b3461014e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261014e5760043567ffffffffffffffff8110156105b95760946001533060601b6002525f6080821061059c5781805b61058d5750806020926017926080016016538160031b610100031b82525b8060d6015f53015f2073ffffffffffffffffffffffffffffffffffffffff60405191168152f35b60019091019060081c80610548565b60179082602093156105b1575b601653610566565b5060806105a9565b63756688fe5f526004601cfd5b3461014e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261014e5763a1337b4d610602610aa9565b60601b175f526020805f205473ffffffffffffffffffffffffffffffffffffffff60405191168152f35b60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261014e5761065e610aa9565b61066781610cdf565b8160601b9063c12fa8d682175f5260205f20903382540361013d575f6040519163f2fde38b83523360208401526024389183601c8601915af1156106de5750905f63a1337b4d9255175f525f60208120557f4b0f58242c231a580ee42fe1dd7389c8e7520590afe33c21809305a6014703a15f80a2005b3d5f823e3d90fd5b60407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261014e57610718610aa9565b610720610acc565b8060601b156101305761076360405161022f816102035f367f00000000000000000000000000000000000000000000000000000000000000008960208601610bac565b34471061032c5760208151910134f090813b15928260601b9384151761031f5760209363c12fa8d6915f84867fd283ed05905c0eb69fe3ef042c6ad706d8d9c75b138624098de540fa2c011a058380a463a1337b4d82175f5280865f2055847f3684250ce1e33b790ed973c23080f312db0adb21a6d98c61a5c9ff99e4babc175f80a3175f5280835f2055817f1b185f8166e5b540f041c2132c66d6c691b0674cd3a95ccc9592a43dd64ad6e25f80a373ffffffffffffffffffffffffffffffffffffffff60405191168152f35b3461014e5760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261014e57610868610aa9565b6044359067ffffffffffffffff821161014e5761022f610203916108936108c7943690600401610aef565b6040949194519485937f00000000000000000000000000000000000000000000000000000000000000009060208601610bac565b6020815191012060ff5f536035523060601b600152602435601552602060555f205f60355273ffffffffffffffffffffffffffffffffffffffff60405191168152f35b61091336610b1d565b8260601b156101305761022f610763926102036040519384927f00000000000000000000000000000000000000000000000000000000000000008960208601610bac565b60607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261014e57610989610aa9565b610991610acc565b604435908060601b15610130578160601c338114901517156103395761025260405161022f816102035f367f00000000000000000000000000000000000000000000000000000000000000008a60208601610bac565b3461014e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261014e576020610a28610a23610aa9565b610cdf565b73ffffffffffffffffffffffffffffffffffffffff60405191168152f35b3461014e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261014e5760209063c12fa8d6610a85610aa9565b60601b175f5273ffffffffffffffffffffffffffffffffffffffff825f2054168152f35b6004359073ffffffffffffffffffffffffffffffffffffffff8216820361014e57565b6024359073ffffffffffffffffffffffffffffffffffffffff8216820361014e57565b9181601f8401121561014e5782359167ffffffffffffffff831161014e576020838186019501011161014e57565b60607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc82011261014e5760043573ffffffffffffffffffffffffffffffffffffffff8116810361014e579160243573ffffffffffffffffffffffffffffffffffffffff8116810361014e57916044359067ffffffffffffffff821161014e57610ba891600401610aef565b9091565b9293806080957fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09473ffffffffffffffffffffffffffffffffffffffff601f95168752602087015260606040870152816060870152868601375f8582860101520116010190565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117610c5457604052565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b610cdd906020808095946040519684889551918291018487015e8401908282015f8152815193849201905e01015f8152037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08101845283610c13565b565b6001906094825360601b6002525f9060808110610d33579081825b610d215760179250816080016016538160031b610100031b82525b8060d6015f53015f2090565b9060019260081c928391019190610cfa565b908160179215610d46575b601653610d15565b506080610d3e56fe60a0806040526106a18038038091610017828561023a565b83398101906060818303126102225761002f8161025d565b9161003c6020830161025d565b604083015190926001600160401b038211610222570181601f820112156102225780516001600160401b0381116102265760405191610085601f8301601f19166020018461023a565b81835260208301936020838301011161022257815f926020809301865e83010152604051936101206102fb6100bd602082018861023a565b80875260208701906103a68239604080516001600160a01b039097166020888101918252808952916100ef908961023a565b6040519788938385019a5180918c5e840190838201905f8252519283915e01015f815203601f19810186528561023a565b803b1561021557807f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc55807fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b5f80a28151156101ff575f92389251915af4156101f7575b51905ff08060601b156101f757807fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d610355806020527f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f60405fa1608052604051610134908161027282396080518160060152f35b3d5f803e3d5ffd5b505050341561018457636fb1b0e95f526004601cfd5b6368155f9a5f526004601cfd5b5f80fd5b634e487b7160e01b5f52604160045260245ffd5b601f909101601f19168101906001600160401b0382119082101761022657604052565b51906001600160a01b03821682036102225756fe60806040527f0000000000000000000000000000000000000000000000000000000000000000331461006857365f80375f3836827f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc545af43d5f803e15610064573d5ff35b3d5ffd5b634f1ef2865f3560e01c036101275773ffffffffffffffffffffffffffffffffffffffff60043516803b1561011a57807f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc55807fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b5f80a260443590811561010457815f92606484378238925af4156100fc57005b3d5f803e3d5ffd5b50503461010d57005b636fb1b0e95f526004601cfd5b6368155f9a5f526004601cfd5b63d2b576ec5f526004601cfd60803460b757601f6102fb38819003918201601f19168301916001600160401b0383118484101760bb5780849260209460405283398101031260b757516001600160a01b038116810360b7578060601b1560aa57807f9bc353c4ee8d049c7cb68b79467fc95d9015a8a82334bd0e61ce699e20cb5bd5555f7f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08180a360405161022b90816100d08239f35b6354a567865f526004601cfd5b5f80fd5b634e487b7160e01b5f52604160045260245ffdfe6004361061021e575f3560e01c8080638da5cb5b146101f55763ad3cb1cc146101c357337f9bc353c4ee8d049c7cb68b79467fc95d9015a8a82334bd0e61ce699e20cb5bd554036101b25780639623609d146101295763f2fde38b1461006c57637352d91c5f526004601cfd5b6004357fffffffffffffffffffffffffffffffffffffffff00000000000000000000000073ffffffffffffffffffffffffffffffffffffffff82169160601b161561011c57807f9bc353c4ee8d049c7cb68b79467fc95d9015a8a82334bd0e61ce699e20cb5bd5547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a37f9bc353c4ee8d049c7cb68b79467fc95d9015a8a82334bd0e61ce699e20cb5bd555005b6354a567865f526004601cfd5b50634f1ef2865f527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc36016024602037604080525f387fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601601c3473ffffffffffffffffffffffffffffffffffffffff600435165af1156101a757005b6040513d5f823e3d90fd5b6332b2baa35f52336020526024601cfd5b60205f5260056020527f352e302e3000000000000000000000000000000000000000000000000000000060405260605ff35b7f9bc353c4ee8d049c7cb68b79467fc95d9015a8a82334bd0e61ce699e20cb5bd5545f5260205ff35b63ca0ad2605f526004601cfd";