diff --git a/README.md b/README.md index 214a1db..68cf041 100644 --- a/README.md +++ b/README.md @@ -38,12 +38,11 @@ Solidity compiler and new EVM OPCODES, as well as the introduction of more user- Enter PRBProxy, the modern successor to DSProxy; a "DSProxy 2.0", if you will. It improves upon DSProxy in several ways: -1. PRBProxy is deployed with [CREATE2][eip-1014], which allows clients to know the proxy contract's address in advance. -2. `CREATE2` seeds are generated in a way that eliminates the risk of front-running. -3. The proxy owner cannot be changed during the `DELEGATECALL` operation. +1. PRBProxy is deployed with [CREATE2][eip-1014], which allows clients to pre-compute the proxy contract's address. +2. The `CREATE2` salts are generated in a way that eliminates the risk of front-running. +3. The proxy owner is immutable, and so it cannot be changed during any `DELEGATECALL`. 4. PRBProxy uses high-level Solidity code that is easier to comprehend and less prone to errors. -5. A minimum gas reserve is stored in the proxy to prevent it from becoming unusable if future EVM opcode gas costs change. -6. PRBProxy offers more features than DSProxy. +5. PRBProxy offers more features than DSProxy. Using CREATE2 eliminates the risk of a [chain reorg](https://en.bitcoin.it/wiki/Chain_Reorganization) overriding the proxy contract owner, making PRBProxy a more secure alternative to DSProxy. With DSProxy, users must wait for several blocks to be mined before assuming the contract is secure. diff --git a/src/PRBProxyRegistry.sol b/src/PRBProxyRegistry.sol index 7f03731..9f8831d 100644 --- a/src/PRBProxyRegistry.sol +++ b/src/PRBProxyRegistry.sol @@ -41,9 +41,6 @@ contract PRBProxyRegistry is IPRBProxyRegistry { /// @inheritdoc IPRBProxyRegistry ConstructorParams public override constructorParams; - /// @inheritdoc IPRBProxyRegistry - mapping(address origin => bytes32 seed) public override nextSeeds; - /*////////////////////////////////////////////////////////////////////////// INTERNAL STORAGE //////////////////////////////////////////////////////////////////////////*/ @@ -139,39 +136,20 @@ contract PRBProxyRegistry is IPRBProxyRegistry { noProxy(msg.sender) returns (IPRBProxy proxy) { - // Load the next seed. - bytes32 seed = nextSeeds[tx.origin]; - - // Prevent front-running the salt by hashing the concatenation of "tx.origin" and the user-provided seed. - bytes32 salt = keccak256(abi.encode(tx.origin, seed)); - - // Set the constructor params. + // Use the address of the owner as the CREATE2 salt. address owner = msg.sender; - constructorParams = ConstructorParams({ owner: owner, target: target, data: data }); + bytes32 salt = bytes32(abi.encodePacked(owner)); - // Deploy the proxy with CREATE2. + // Deploy the proxy with CREATE2, and execute the delegate call in the constructor. + constructorParams = ConstructorParams({ owner: owner, target: target, data: data }); proxy = new PRBProxy{ salt: salt }(); delete constructorParams; - // Associate the the owner with the proxy in the mapping. + // Associate the owner and the proxy. _proxies[owner] = proxy; - // Increment the seed. - // Using unchecked arithmetic here because this cannot realistically overflow, ever. - unchecked { - nextSeeds[tx.origin] = bytes32(uint256(seed) + 1); - } - - // Log the proxy via en event. - // forgefmt: disable-next-line - emit DeployProxy({ - origin: tx.origin, - operator: msg.sender, - owner: owner, - seed: seed, - salt: salt, - proxy: proxy - }); + // Log the creation of the proxy. + emit DeployProxy({ operator: msg.sender, owner: owner, proxy: proxy }); } /// @inheritdoc IPRBProxyRegistry @@ -249,11 +227,8 @@ contract PRBProxyRegistry is IPRBProxyRegistry { /// @dev See the documentation for the user-facing functions that call this internal function. function _deploy(address owner) internal returns (IPRBProxy proxy) { - // Load the next seed. - bytes32 seed = nextSeeds[tx.origin]; - - // Prevent front-running the salt by hashing the concatenation of "tx.origin" and the user-provided seed. - bytes32 salt = keccak256(abi.encode(tx.origin, seed)); + // Use the address of the owner as the CREATE2 salt. + bytes32 salt = bytes32(abi.encodePacked(owner)); // Set the owner and empty out the target and the data to prevent reentrancy. constructorParams = ConstructorParams({ owner: owner, target: address(0), data: "" }); @@ -262,24 +237,10 @@ contract PRBProxyRegistry is IPRBProxyRegistry { proxy = new PRBProxy{ salt: salt }(); delete constructorParams; - // Associate the the owner with the proxy in the mapping. + // Associate the owner and the proxy. _proxies[owner] = proxy; - // Increment the seed. - // We're using unchecked arithmetic here because this cannot realistically overflow, ever. - unchecked { - nextSeeds[tx.origin] = bytes32(uint256(seed) + 1); - } - - // Log the proxy via en event. - // forgefmt: disable-next-line - emit DeployProxy({ - origin: tx.origin, - operator: msg.sender, - owner: owner, - seed: seed, - salt: salt, - proxy: proxy - }); + // Log the creation of the proxy. + emit DeployProxy({ operator: msg.sender, owner: owner, proxy: proxy }); } } diff --git a/src/interfaces/IPRBProxyRegistry.sol b/src/interfaces/IPRBProxyRegistry.sol index b9b7652..a293ea7 100644 --- a/src/interfaces/IPRBProxyRegistry.sol +++ b/src/interfaces/IPRBProxyRegistry.sol @@ -32,14 +32,7 @@ interface IPRBProxyRegistry { //////////////////////////////////////////////////////////////////////////*/ /// @notice Emitted when a new proxy is deployed. - event DeployProxy( - address indexed origin, - address indexed operator, - address indexed owner, - bytes32 seed, - bytes32 salt, - IPRBProxy proxy - ); + event DeployProxy(address indexed operator, address indexed owner, IPRBProxy proxy); /// @notice Emitted when a plugin is installed. event InstallPlugin(address indexed owner, IPRBProxy indexed proxy, IPRBProxyPlugin indexed plugin); @@ -122,15 +115,11 @@ interface IPRBProxyRegistry { /// @param owner The user address to make the query for. function getProxy(address owner) external view returns (IPRBProxy proxy); - /// @notice The seed that will be used to deploy the next proxy for the provided origin. - /// @param origin The externally owned account (EOA) that is part of the CREATE2 salt. - function nextSeeds(address origin) external view returns (bytes32 seed); - /*////////////////////////////////////////////////////////////////////////// NON-CONSTANT FUNCTIONS //////////////////////////////////////////////////////////////////////////*/ - /// @notice Deploys a new proxy with CREATE2 by setting the caller as the owner. + /// @notice Deploys a new proxy with CREATE2, using the caller as the owner. /// /// @dev Emits a {DeployProxy} event. /// @@ -140,8 +129,9 @@ interface IPRBProxyRegistry { /// @return proxy The address of the newly deployed proxy contract. function deploy() external returns (IPRBProxy proxy); - /// @notice Deploys a new proxy via CREATE2 by setting the caller as the owner, and delegate calls to the provided - /// target contract by forwarding the data. It returns the data it gets back, and bubbles up any potential revert. + /// @notice Deploys a new proxy via CREATE2, using the caller as the owner. It delegate calls to the provided + /// target contract by forwarding the data. Then, it returns the data it gets back, and bubbles up any potential + /// revert. /// /// @dev Emits a {DeployProxy} and an {Execute} event. /// @@ -170,9 +160,8 @@ interface IPRBProxyRegistry { /// @dev Emits an {InstallPlugin} event. /// /// Notes: - /// - Installing a plugin is a potentially dangerous operation, because anyone will be able to run the plugin. - /// - Plugin methods that have the same selector as {PRBProxy.execute} will be installed, but they will never be run - /// by the proxy. + /// - Installing a plugin is a potentially dangerous operation, because anyone can run the plugin. + /// - Plugin methods that have the same selector as {PRBProxy.execute} can be installed, but they can never be run. /// /// Requirements: /// - The caller must have a proxy. @@ -193,7 +182,7 @@ interface IPRBProxyRegistry { /// Requirements: /// - The caller must have a proxy. /// - /// @param envoy The address of the envoy account. + /// @param envoy The address of the account given permission to call the target contract. /// @param target The address of the target contract. /// @param permission The boolean permission to set. function setPermission(address envoy, address target, bool permission) external; diff --git a/test/Base.t.sol b/test/Base.t.sol index c170a4a..c920862 100644 --- a/test/Base.t.sol +++ b/test/Base.t.sol @@ -60,15 +60,6 @@ abstract contract Base_Test is Assertions, Events, StdCheats, StdUtils { address payable eve; } - /*////////////////////////////////////////////////////////////////////////// - CONSTANTS - //////////////////////////////////////////////////////////////////////////*/ - - uint256 internal constant DEFAULT_MIN_GAS_RESERVE = 5000; - bytes32 internal constant SEED_ONE = bytes32(uint256(0x01)); - bytes32 internal constant SEED_TWO = bytes32(uint256(0x02)); - bytes32 internal constant SEED_ZERO = bytes32(uint256(0x00)); - /*////////////////////////////////////////////////////////////////////////// VARIABLES //////////////////////////////////////////////////////////////////////////*/ @@ -123,7 +114,7 @@ abstract contract Base_Test is Assertions, Events, StdCheats, StdUtils { deployRegistryConditionally(); // Make Alice both the caller and the origin. - vm.startPrank({ msgSender: users.alice, txOrigin: users.alice }); + vm.startPrank({ msgSender: users.alice }); } /*////////////////////////////////////////////////////////////////////////// @@ -131,8 +122,8 @@ abstract contract Base_Test is Assertions, Events, StdCheats, StdUtils { //////////////////////////////////////////////////////////////////////////*/ /// @dev Computes the proxy address without deploying it. - function computeProxyAddress(address origin, bytes32 seed) internal returns (address) { - bytes32 salt = keccak256(abi.encode(origin, seed)); + function computeProxyAddress(address owner) internal returns (address) { + bytes32 salt = bytes32(abi.encodePacked(owner)); bytes32 creationBytecodeHash = keccak256(getProxyBytecode()); // Use the Create2 utility from Forge Std. return computeCreate2Address({ salt: salt, initcodeHash: creationBytecodeHash, deployer: address(registry) }); diff --git a/test/registry/deploy-and-execute/deployAndExecute.t.sol b/test/registry/deploy-and-execute/deployAndExecute.t.sol index 7f792bf..7192d27 100644 --- a/test/registry/deploy-and-execute/deployAndExecute.t.sol +++ b/test/registry/deploy-and-execute/deployAndExecute.t.sol @@ -32,82 +32,42 @@ contract DeployAndExecute_Test is Registry_Test { _; } - function testFuzz_DeployAndExecute_ProxyAddress(address origin, address owner) external whenOwnerDoesNotHaveProxy { - changePrank({ txOrigin: origin, msgSender: owner }); + function testFuzz_DeployAndExecute_ProxyAddress(address owner) external whenOwnerDoesNotHaveProxy { + changePrank({ msgSender: owner }); IPRBProxy actualProxy = registry.deployAndExecute(target, data); - address expectedProxy = computeProxyAddress(origin, SEED_ZERO); + address expectedProxy = computeProxyAddress(owner); assertEq(address(actualProxy), expectedProxy, "deployed proxy address mismatch"); } - function testFuzz_DeployAndExecute_ProxyOwner(address origin, address owner) external whenOwnerDoesNotHaveProxy { - changePrank({ txOrigin: origin, msgSender: owner }); + function testFuzz_DeployAndExecute_ProxyOwner(address owner) external whenOwnerDoesNotHaveProxy { + changePrank({ msgSender: owner }); IPRBProxy proxy = registry.deployAndExecute(target, data); address actualOwner = proxy.owner(); address expectedOwner = owner; assertEq(actualOwner, expectedOwner, "proxy owner mismatch"); } - function testFuzz_DeployAndExecute_UpdateNextSeeds( - address origin, - address owner - ) - external - whenOwnerDoesNotHaveProxy - { - changePrank({ txOrigin: origin, msgSender: owner }); - registry.deployAndExecute(target, data); - - bytes32 actualNextSeed = registry.nextSeeds(origin); - bytes32 expectedNextSeed = SEED_ONE; - assertEq(actualNextSeed, expectedNextSeed, "next seed mismatch"); - } - - function testFuzz_DeployAndExecute_UpdateProxies( - address origin, - address owner - ) - external - whenOwnerDoesNotHaveProxy - { - changePrank({ txOrigin: origin, msgSender: owner }); + function testFuzz_DeployAndExecute_UpdateProxies(address owner) external whenOwnerDoesNotHaveProxy { + changePrank({ msgSender: owner }); registry.deployAndExecute(target, data); address actualProxyAddress = address(registry.getProxy(owner)); - address expectedProxyAddress = computeProxyAddress(origin, SEED_ZERO); + address expectedProxyAddress = computeProxyAddress(owner); assertEq(actualProxyAddress, expectedProxyAddress, "proxy address mismatch"); } - function testFuzz_DeployAndExecute_Event_DeployProxy( - address origin, - address owner - ) - external - whenOwnerDoesNotHaveProxy - { - changePrank({ txOrigin: origin, msgSender: owner }); + function testFuzz_DeployAndExecute_Event_DeployProxy(address owner) external whenOwnerDoesNotHaveProxy { + changePrank({ msgSender: owner }); vm.expectEmit({ emitter: address(registry) }); - emit DeployProxy({ - origin: origin, - operator: owner, - owner: owner, - seed: SEED_ZERO, - salt: keccak256(abi.encode(origin, SEED_ZERO)), - proxy: IPRBProxy(computeProxyAddress(origin, SEED_ZERO)) - }); + emit DeployProxy({ operator: owner, owner: owner, proxy: IPRBProxy(computeProxyAddress(owner)) }); registry.deployAndExecute(target, data); } - function testFuzz_DeployAndExecute_Event_Execute( - address origin, - address owner - ) - external - whenOwnerDoesNotHaveProxy - { - changePrank({ txOrigin: origin, msgSender: owner }); + function testFuzz_DeployAndExecute_Event_Execute(address owner) external whenOwnerDoesNotHaveProxy { + changePrank({ msgSender: owner }); - vm.expectEmit({ emitter: computeProxyAddress({ origin: origin, seed: SEED_ZERO }) }); + vm.expectEmit({ emitter: computeProxyAddress(owner) }); emit Execute({ target: address(targets.echo), data: data, response: abi.encode(input) }); registry.deployAndExecute(target, data); } diff --git a/test/registry/deploy-and-execute/deployAndExecute.tree b/test/registry/deploy-and-execute/deployAndExecute.tree index 4f25991..ae3a7dc 100644 --- a/test/registry/deploy-and-execute/deployAndExecute.tree +++ b/test/registry/deploy-and-execute/deployAndExecute.tree @@ -4,7 +4,6 @@ deployAndExecute.t.sol └── when the owner does not have a proxy ├── it should generate the correct address ├── it should initialize the owner - ├── it should update the next seeds mapping ├── it should update the proxies mapping ├── it should delegate call to the target contract ├── it should emit a {DeployProxy} event diff --git a/test/registry/deploy-for/deployFor.t.sol b/test/registry/deploy-for/deployFor.t.sol index 0403185..b59373d 100644 --- a/test/registry/deploy-for/deployFor.t.sol +++ b/test/registry/deploy-for/deployFor.t.sol @@ -23,86 +23,35 @@ contract DeployFor_Test is Registry_Test { _; } - function testFuzz_DeployFor_ProxyAddress( - address origin, - address operator, - address owner - ) - external - whenOwnerDoesNotHaveProxy - { - changePrank({ txOrigin: origin, msgSender: operator }); + function testFuzz_DeployFor_ProxyAddress(address operator, address owner) external whenOwnerDoesNotHaveProxy { + changePrank({ msgSender: operator }); address actualProxy = address(registry.deployFor(owner)); - address expectedProxy = computeProxyAddress(origin, SEED_ZERO); + address expectedProxy = computeProxyAddress(owner); assertEq(actualProxy, expectedProxy, "deployed proxy address mismatch"); } - function testFuzz_DeployFor_ProxyOwner( - address origin, - address operator, - address owner - ) - external - whenOwnerDoesNotHaveProxy - { - changePrank({ txOrigin: origin, msgSender: operator }); + function testFuzz_DeployFor_ProxyOwner(address operator, address owner) external whenOwnerDoesNotHaveProxy { + changePrank({ msgSender: operator }); IPRBProxy proxy = registry.deployFor(owner); address actualOwner = proxy.owner(); address expectedOwner = owner; assertEq(actualOwner, expectedOwner, "proxy owner mismatch"); } - function testFuzz_DeployFor_UpdateNextSeeds( - address origin, - address operator, - address owner - ) - external - whenOwnerDoesNotHaveProxy - { - changePrank({ txOrigin: origin, msgSender: operator }); - registry.deployFor(owner); - - bytes32 actualNextSeed = registry.nextSeeds(origin); - bytes32 expectedNextSeed = SEED_ONE; - assertEq(actualNextSeed, expectedNextSeed, "next seed mismatch"); - } - - function testFuzz_DeployFor_UpdateProxies( - address origin, - address operator, - address owner - ) - external - whenOwnerDoesNotHaveProxy - { - changePrank({ txOrigin: origin, msgSender: operator }); + function testFuzz_DeployFor_UpdateProxies(address operator, address owner) external whenOwnerDoesNotHaveProxy { + changePrank({ msgSender: operator }); registry.deployFor(owner); address actualProxyAddress = address(registry.getProxy(owner)); - address expectedProxyAddress = computeProxyAddress(origin, SEED_ZERO); + address expectedProxyAddress = computeProxyAddress(owner); assertEq(actualProxyAddress, expectedProxyAddress, "proxy address mismatch"); } - function testFuzz_DeployFor_Event( - address origin, - address operator, - address owner - ) - external - whenOwnerDoesNotHaveProxy - { - changePrank({ txOrigin: origin, msgSender: operator }); + function testFuzz_DeployFor_Event(address operator, address owner) external whenOwnerDoesNotHaveProxy { + changePrank({ msgSender: operator }); vm.expectEmit({ emitter: address(registry) }); - emit DeployProxy({ - origin: origin, - operator: operator, - owner: owner, - seed: SEED_ZERO, - salt: keccak256(abi.encode(origin, SEED_ZERO)), - proxy: IPRBProxy(computeProxyAddress(origin, SEED_ZERO)) - }); + emit DeployProxy({ operator: operator, owner: owner, proxy: IPRBProxy(computeProxyAddress(owner)) }); registry.deployFor(owner); } } diff --git a/test/registry/deploy-for/deployFor.tree b/test/registry/deploy-for/deployFor.tree index 2c0f404..7c30243 100644 --- a/test/registry/deploy-for/deployFor.tree +++ b/test/registry/deploy-for/deployFor.tree @@ -4,6 +4,5 @@ deployFor.t.sol └── when the owner does not have a proxy ├── it should generate the correct address ├── it should initialize the owner - ├── it should update the next seeds mapping ├── it should update the proxies mapping └── it should emit a {DeployProxy} event diff --git a/test/registry/deploy/deploy.t.sol b/test/registry/deploy/deploy.t.sol index 0beed63..bafe330 100644 --- a/test/registry/deploy/deploy.t.sol +++ b/test/registry/deploy/deploy.t.sol @@ -23,49 +23,33 @@ contract Deploy_Test is Registry_Test { _; } - function testFuzz_Deploy_ProxyAddress(address origin, address owner) external whenOwnerDoesNotHaveProxy { - changePrank({ txOrigin: origin, msgSender: owner }); + function testFuzz_Deploy_ProxyAddress(address owner) external whenOwnerDoesNotHaveProxy { + changePrank({ msgSender: owner }); address actualProxy = address(registry.deploy()); - address expectedProxy = computeProxyAddress({ origin: origin, seed: SEED_ZERO }); + address expectedProxy = computeProxyAddress(owner); assertEq(actualProxy, expectedProxy, "deployed proxy address mismatch"); } - function testFuzz_Deploy_ProxyOwner(address origin, address owner) external whenOwnerDoesNotHaveProxy { - changePrank({ txOrigin: origin, msgSender: owner }); + function testFuzz_Deploy_ProxyOwner(address owner) external whenOwnerDoesNotHaveProxy { + changePrank({ msgSender: owner }); IPRBProxy proxy = registry.deploy(); address actualOwner = proxy.owner(); address expectedOwner = owner; assertEq(actualOwner, expectedOwner, "proxy owner mismatch"); } - function testFuzz_Deploy_UpdateNextSeeds(address origin, address owner) external whenOwnerDoesNotHaveProxy { - changePrank({ txOrigin: origin, msgSender: owner }); - registry.deploy(); - - bytes32 actualNextSeed = registry.nextSeeds(origin); - bytes32 expectedNextSeed = SEED_ONE; - assertEq(actualNextSeed, expectedNextSeed, "next seed mismatch"); - } - - function testFuzz_Deploy_UpdateProxies(address origin, address owner) external whenOwnerDoesNotHaveProxy { - changePrank({ txOrigin: origin, msgSender: owner }); + function testFuzz_Deploy_UpdateProxies(address owner) external whenOwnerDoesNotHaveProxy { + changePrank({ msgSender: owner }); registry.deploy(); address actualProxy = address(registry.getProxy(owner)); - address expectedProxy = computeProxyAddress({ origin: origin, seed: SEED_ZERO }); + address expectedProxy = computeProxyAddress({ owner: owner }); assertEq(actualProxy, expectedProxy, "proxy mapping mismatch"); } - function testFuzz_Deploy_Event(address origin, address owner) external whenOwnerDoesNotHaveProxy { - changePrank({ txOrigin: origin, msgSender: owner }); + function testFuzz_Deploy_Event(address owner) external whenOwnerDoesNotHaveProxy { + changePrank({ msgSender: owner }); vm.expectEmit({ emitter: address(registry) }); - emit DeployProxy({ - origin: origin, - operator: owner, - owner: owner, - seed: SEED_ZERO, - salt: keccak256(abi.encode(origin, SEED_ZERO)), - proxy: IPRBProxy(computeProxyAddress({ origin: origin, seed: SEED_ZERO })) - }); + emit DeployProxy({ operator: owner, owner: owner, proxy: IPRBProxy(computeProxyAddress(owner)) }); registry.deploy(); } } diff --git a/test/registry/deploy/deploy.tree b/test/registry/deploy/deploy.tree index 5704a2c..bcf86a0 100644 --- a/test/registry/deploy/deploy.tree +++ b/test/registry/deploy/deploy.tree @@ -4,6 +4,5 @@ deploy.t.sol └── when the owner does not have a proxy ├── it should generate the correct address ├── it should initialize the owner - ├── it should update the next seeds mapping ├── it should update the proxies mapping └── it should emit a {DeployProxy} event diff --git a/test/utils/Events.sol b/test/utils/Events.sol index c5de7e0..9194a20 100644 --- a/test/utils/Events.sol +++ b/test/utils/Events.sol @@ -18,14 +18,7 @@ abstract contract Events { REGISTRY //////////////////////////////////////////////////////////////////////////*/ - event DeployProxy( - address indexed origin, - address indexed operator, - address indexed owner, - bytes32 seed, - bytes32 salt, - IPRBProxy proxy - ); + event DeployProxy(address indexed operator, address indexed owner, IPRBProxy proxy); event InstallPlugin(address indexed owner, IPRBProxy indexed proxy, IPRBProxyPlugin indexed plugin); diff --git a/test/utils/Precompiles.sol b/test/utils/Precompiles.sol index c23bc15..2e7ceaa 100644 --- a/test/utils/Precompiles.sol +++ b/test/utils/Precompiles.sol @@ -12,7 +12,7 @@ contract Precompiles { //////////////////////////////////////////////////////////////////////////*/ bytes public constant BYTECODE_REGISTRY = - hex"6080806040523461001657611ccf908161001c8239f35b600080fdfe6080604090808252600491823610156200001857600080fd5b600091823560e01c908163092af81314620009c4575080631ddef5b914620009125780632c27a01f14620008d75780634bddd93a14620007ae5780635cabcdf714620006db57806361be4859146200068157806366b0182d146200057c57806374912cd214620004fd578063775c300c1462000484578063aa4b826a14620003a6578063b31d1b99146200033d578063b7fba4d314620002ff578063b9678403146200014b5763ffa1ad7414620000ce57600080fd5b3462000147578160031936011262000147578051918183019083821067ffffffffffffffff8311176200013457506200013093508152600c82526b342e302e302d626574612e3560a01b60208301525191829160208352602083019062000e25565b0390f35b634e487b7160e01b815260418552602490fd5b5080fd5b508234620002fb5760209182600319360112620002f7576001600160a01b03823581811694919390859003620002f3573386526006825283838720541615620002dc57825163ecdb286560e01b81529086828281838a5af1918215620002d2578792620002a9575b5081519182156200029257875b838110620001ff57505050506006903386525283205416337f9638c85f58d5866b4eb299b2e5f8f366372bd58e2d6e6e6eb19f3462948f33878480a480f35b6001600160e01b031962000214828462000fca565b5116338a526005808752878b20828c52875288888c2054166200025a57338b528652868a20908a52855285892080546001600160a01b03191689179055600101620001c0565b338b528652868a20818b528652868a205487516361a2116b60e11b815290891681860152602481018a90526044810191909152606490fd5b845163b74066bb60e01b8152808301889052602490fd5b620002ca9192503d8089833e620002c1818362000e02565b81019062000f33565b9087620001b3565b84513d89823e3d90fd5b60249083519063963e961b60e01b82523390820152fd5b8580fd5b8380fd5b8280fd5b50346200014757602036600319011262000147576020916001600160a01b03908290826200032c62000d30565b168152600685522054169051908152f35b508234620002fb576060366003190112620002fb578160209360ff926200036362000d30565b6200036d62000d4c565b6200037762000d63565b6001600160a01b0392831685529288528484209082168452875283832091168252855220549151911615158152f35b508234620002fb576060366003190112620002fb57620003c562000d30565b91620003d062000d4c565b90604435801515809103620002f357338652600660209081528287205490946001600160a01b03918216156200046d57907f4f2bd80fb4928b06abcd76e3b26209a615f0612f98dc4a8b176934b1a3899333939291338952865280838920971696878952865280838920951694858952865282882060ff1981541660ff84161790553388526006865282882054169482519485528401523392a480f35b60249084519063963e961b60e01b82523390820152fd5b509034620004fa5780600319360112620004fa573381526006602052819020546001600160a01b039081169283620004cd5760208383620004c53362000ff5565b915191168152f35b91516356352f0b60e11b8152339281019283526001600160a01b0390931660208301525081906040010390fd5b80fd5b508234620002fb576020366003190112620002fb5781906200051e62000d30565b6001600160a01b038082168652600660205292909420548216806200054c575050620004c560209362000ff5565b92516356352f0b60e11b81526001600160a01b039485169181019182529390921660208301525081906040010390fd5b5034620001475781600319360112620001475760018060a01b0391828154169160019384541693815192809160025491620005b78362000d92565b8087529282811690811562000652575060011462000603575b5050508291620005e96200013094606093038462000e02565b805195869586526020860152840152606083019062000e25565b92506002835260008051602062001caf8339815191525b8284106200063957505050820160200181620005e962000130620005d0565b805460208588018101919091529093019281016200061a565b60ff191660208089019190915293151560051b87019093019350849250620005e99150620001309050620005d0565b5034620001475780600319360112620001475760209181620006a262000d30565b91620006ad62000d7a565b6001600160a01b039384168252600586528282206001600160e01b0319909116825285522054915191168152f35b509034620004fa576060366003190112620004fa57620006fa62000d30565b6200070462000d4c565b906200070f62000d63565b91845195638da5cb5b60e01b87526020968781838160018060a01b038098165afa908115620007a45791848896949288969460ff999162000770575b5016855288528185852091168452875283832091168252855220541690519015158152f35b6200079591508b3d8d116200079c575b6200078c818362000e02565b81019062000e67565b386200074b565b503d62000780565b87513d88823e3d90fd5b508234620002fb5760209182600319360112620002f7576001600160a01b03823581811694919390859003620002f3573386526006825283838720541615620002dc57825163ecdb286560e01b815286818381838a5af1908115620002d2578791620008b9575b508051918215620008a25750865b82811062000861575050506006903386525283205416337f381d2d67cec77a56c13de6658effae4585e49ae536458929d53bfd4c3599e0ad8480a480f35b33885260058452848820600191906001600160e01b031962000884838662000fca565b51168a52855285892080546001600160a01b03191690550162000823565b845163b74066bb60e01b8152908101879052602490fd5b620008d091503d8089833e620002c1818362000e02565b8762000815565b503462000147576020366003190112620001475760209181906001600160a01b036200090262000d30565b1681526003845220549051908152f35b509034620004fa5781600319360112620004fa576200093062000d30565b906200093b62000d7a565b8351638da5cb5b60e01b81526020956001600160a01b039487918391829088165afa908115620009ba57839291859187959162000998575b50168252600586528282206001600160e01b0319909116825285522054915191168152f35b620009b39150883d8a116200079c576200078c818362000e02565b3862000973565b85513d85823e3d90fd5b84838534620004fa5781600319360112620004fa57620009e362000d30565b9267ffffffffffffffff918260243511620004fa57366023602435011215620004fa5760243582013591838311620001475736602484813501011162000147573382526006602052848220546001600160a01b03979088168062000d0757505032808352600360209081528684205487519182019283526040808301829052825295919062000a7460608262000e02565b519020938887519862000a878a62000dcf565b338a5216602089015283602088519262000aab82601f19601f840116018562000e02565b8084528060248035018386013783010152808789015262000b00896020818b51169a6001600160601b0360a01b9b8c89541617885501511660018060a01b03166001600160601b0360a01b6001541617600155565b80519082821162000cf457819062000b1a60025462000d92565b601f811162000ca7575b50602090601f831160011462000c3057869262000c24575b50508160011b916000199060031b1c1916176002555b8551916109f190818401928484109084111762000c1157509180918593620012be8339039083f595861562000c0557602096169462000b9062000ea1565b33808352600688528583208054929092168717909155328083526003885291859020600185019055845193845260208401929092526001600160a01b038516604084015290918291907f6aafca263a35a9d2a6e4e4659a84688092f4ae153df2f95cd7659508d95c187090606090a451908152f35b508351903d90823e3d90fd5b634e487b7160e01b855260419052602484fd5b015190508a8062000b3c565b6002875260008051602062001caf8339815191529250601f198416875b81811062000c8e575090846001959493921062000c74575b505050811b0160025562000b52565b015160001960f88460031b161c191690558a808062000c65565b9293602060018192878601518155019501930162000c4d565b62000ce2906002885260008051602062001caf833981519152601f850160051c8101916020861062000ce9575b601f0160051c019062000e88565b8b62000b24565b909150819062000cd4565b634e487b7160e01b855260418452602485fd5b6356352f0b60e11b8252339282019283526001600160a01b031660208301529081906040010390fd5b600435906001600160a01b038216820362000d4757565b600080fd5b602435906001600160a01b038216820362000d4757565b604435906001600160a01b038216820362000d4757565b602435906001600160e01b03198216820362000d4757565b90600182811c9216801562000dc4575b602083101462000dae57565b634e487b7160e01b600052602260045260246000fd5b91607f169162000da2565b6060810190811067ffffffffffffffff82111762000dec57604052565b634e487b7160e01b600052604160045260246000fd5b90601f8019910116810190811067ffffffffffffffff82111762000dec57604052565b919082519283825260005b84811062000e52575050826000602080949584010152601f8019910116010190565b60208183018101518483018201520162000e30565b9081602091031262000d4757516001600160a01b038116810362000d475790565b81811062000e94575050565b6000815560010162000e88565b6000808055600181815562000eb860025462000d92565b908162000ec457505050565b601f821160011462000ed7575050600255565b60028352601f60008051602062001caf833981519152920160051c82017f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5acf5b81811062000f28575050508160025555565b848155820162000f16565b90602090818382031262000d4757825167ffffffffffffffff9384821162000d47570181601f8201121562000d4757805193841162000dec578360051b906040519462000f838584018762000e02565b8552838086019282010192831162000d47578301905b82821062000fa8575050505090565b81516001600160e01b03198116810362000d4757815290830190830162000f99565b805182101562000fdf5760209160051b010190565b634e487b7160e01b600052603260045260246000fd5b326000818152600360209081526040808320548151808401958652808301829052828152601f1997969593949193906200103160608262000e02565b51902093825195620010438762000dcf565b6001600160a01b039081168088528288018481528551909a919867ffffffffffffffff929082860184811184821017620012a95785620010be9281928b52898652858b82015251169d6001600160601b0360a01b9e8f8a5416178955511660018060a01b03166001600160601b0360a01b6001541617600155565b8151918383116200129557908291620010d960025462000d92565b601f811162001255575b508691601f8411600114620011e357508792620011d7575b50508160011b916000199060031b1c1916176002555b8451906109f180830191821183831017620011c3579180918993620012be8339039085f58015620011b95716977f6aafca263a35a9d2a6e4e4659a84688092f4ae153df2f95cd7659508d95c18709392916003916200116f62000ea1565b888452600682528484208054919091168b1790553280845291905290829020600185019055905192835260208301939093526001600160a01b0386166040830152339291606090a4565b84513d85823e3d90fd5b634e487b7160e01b86526041600452602486fd5b015190503880620010fb565b6002895260008051602062001caf8339815191529316885b888282106200123e57505090846001959493921062001224575b505050811b0160025562001111565b015160001960f88460031b161c1916905538808062001215565b6001859682939686015181550195019301620011fb565b6200128e9060028a5260008051602062001caf833981519152601f860160051c8101918a871062000ce957601f0160051c019062000e88565b38620010e3565b634e487b7160e01b87526041600452602487fd5b634e487b7160e01b88526041600452602488fdfe60c08060405234620000ca573360a0526366b0182d60e01b8152600081600481335afa8015620000c4576000809281926200009b575b506080526001600160a01b03821662000087575b60405161068e9081620003638239608051818181610192015281816102c401526104a5015260a05181818161014d0152818161031301526105250152f35b6200009291620002b2565b50388062000049565b909150620000bb92503d8092823e620000b482620000e5565b016200018e565b90913862000035565b62000221565b600080fd5b634e487b7160e01b600052604160045260246000fd5b60c0601f91909101601f19168101906001600160401b038211908210176200010c57604052565b620000cf565b601f909101601f19168101906001600160401b038211908210176200010c57604052565b60e051906001600160a01b0382168203620000ca57565b6001600160401b0381116200010c57601f01601f191660200190565b60005b8381106200017d5750506000910152565b81810151838201526020016200016c565b606060bf19820112620000ca5760c0516001600160a01b0381168103620000ca5791620001ba62000136565b610100519092906001600160401b038111620000ca578160df82011215620000ca578060c00151620001ec816200014d565b92620001fc604051948562000112565b81845260e08284010111620000ca576200021e9160e060208501910162000169565b90565b6040513d6000823e3d90fd5b3d156200025d573d9062000241826200014d565b9162000251604051938462000112565b82523d6000602084013e565b606090565b906020916200027d8151809281855285808601910162000169565b601f01601f1916010190565b9091620002a36200021e9360408452604084019062000262565b91602081840391015262000262565b9190823b156200034157600080825160208401865af4907fb24ebe141c5f2a744b103bea65fce6c40e0dc65d7341d092c09b160f40447990620002f46200022d565b60405190956001600160a01b0316928190620003139088908362000289565b0390a2156200031e57565b508051156200032f57602081519101fd5b60405163061a160d60e41b8152600490fd5b604051636d17e5ef60e11b81526001600160a01b0384166004820152602490fdfe60806040526004361015610027575b36156100255761001d366102b5565b602081519101f35b005b6000803560e01c9081631cff79cd1461005a575080637b1039991461005557638da5cb5b0361000e5761017c565b610137565b60403660031901126100ca57600435610072816100cd565b60243567ffffffffffffffff928382116100ca57366023830112156100ca5781600401359384116100ca5736602485840101116100ca576100c66100ba8560248501866104a2565b60405191829182610123565b0390f35b80fd5b6001600160a01b038116036100de57565b600080fd5b919082519283825260005b84811061010f575050826000602080949584010152601f8019910116010190565b6020818301810151848301820152016100ee565b9060206101349281815201906100e3565b90565b346100de5760003660031901126100de576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b346100de5760003660031901126100de576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b634e487b7160e01b600052604160045260246000fd5b90601f8019910116810190811067ffffffffffffffff8211176101f957604052565b6101c1565b908160209103126100de5751610134816100cd565b6040513d6000823e3d90fd5b908160008237016000815290565b67ffffffffffffffff81116101f957601f01601f191660200190565b3d15610274573d9061025a8261022d565b9161026860405193846101d7565b82523d6000602084013e565b606090565b606090610134939260408252806040830152806000848401376000838284010152601f80199101168101906020838284030191015201906100e3565b6040516361be485960e01b81527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0381811660048401526001600160e01b03196000351660248401819052939493906020846044817f000000000000000000000000000000000000000000000000000000000000000085165afa93841561044e5760009461041e575b5083169182156103e357505060008060405180610362818961021f565b0390845af4907fc4dabe0d7ef7462e2218f2c398c21ef217803e1c46f5cf802d1a5d1d9b503f2f610391610249565b80966103a260405192839283610279565b0390a2156103ad5750565b8251909150156103c05750805190602001fd5b60405163023c045d60e21b81526001600160a01b03919091166004820152602490fd5b604051638848730f60e01b81523360048201526001600160a01b039190911660248201526001600160e01b0319919091166044820152606490fd5b61044091945060203d8111610447575b61043881836101d7565b8101906101fe565b9238610345565b503d61042e565b610213565b908160209103126100de575180151581036100de5790565b9291926104778261022d565b9161048560405193846101d7565b8294818452818301116100de578281602093846000960137010152565b917f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0333818316036104ee575b505061013492916104e891369161046b565b906105e5565b60405163b31d1b9960e01b81526001600160a01b0383811660048301523360248301528616604482015290602090829060649082907f0000000000000000000000000000000000000000000000000000000000000000165afa90811561044e57600091610592575b501561056257806104d6565b6040516355d1750960e01b81526001600160a01b0391821660048201523360248201529084166044820152606490fd5b6105b3915060203d81116105b9575b6105ab81836101d7565b810190610453565b38610556565b503d6105a1565b90916105d7610134936040845260408401906100e3565b9160208184039101526100e3565b9190823b1561066d57600080825160208401865af4907fb24ebe141c5f2a744b103bea65fce6c40e0dc65d7341d092c09b160f40447990610624610249565b60405190956001600160a01b0316928190610641908890836105c0565b0390a21561064b57565b5080511561065b57602081519101fd5b60405163061a160d60e41b8152600490fd5b604051636d17e5ef60e11b81526001600160a01b0384166004820152602490fd405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace"; + hex"6080806040523461001657611c06908161001c8239f35b600080fdfe60806040818152600491823610156200001757600080fd5b600092833560e01c918263092af813146200095c575081631ddef5b914620008ad5781634bddd93a14620007855781635cabcdf714620006ab57816361be4859146200065157816366b0182d146200054b57816374912cd214620004cc578163775c300c1462000453578163aa4b826a1462000373578163b31d1b991462000308578163b7fba4d314620002c9578163b9678403146200011a575063ffa1ad7414620000c257600080fd5b34620001165781600319360112620001165780516200011291620000e68262000d4b565b600c82526b342e302e302d626574612e3560a01b60208301525191829160208352602083019062000d8b565b0390f35b5080fd5b905034620002c55760209182600319360112620002c1576001600160a01b03823581811694919390859003620002bd573386526005825283838720541615620002a657825163ecdb286560e01b815286818381838a5af19081156200029c57879162000275575b5080519081156200025e57875b828110620001cd57505050506005903386525283205416337f9638c85f58d5866b4eb299b2e5f8f366372bd58e2d6e6e6eb19f3462948f33878480a480f35b6001600160e01b0319620001e2828462000f54565b5116338a52848652868a20818b52865287878b2054166200022757338a52848652868a20908a52855285892080546001600160a01b031916891790556001016200018e565b846064918a898b818f8c33825287815282822090878352522054169051936361a2116b60e11b855284015260248301526044820152fd5b845163b74066bb60e01b8152808401889052602490fd5b6200029591503d8089833e6200028c818362000d68565b81019062000ebd565b3862000181565b84513d89823e3d90fd5b60249083519063963e961b60e01b82523390820152fd5b8580fd5b8380fd5b8280fd5b5050346200011657602036600319011262000116576020916001600160a01b0390829082620002f762000c79565b168152600585522054169051908152f35b50503462000116576060366003190112620001165760ff816020936200032d62000c79565b6200033762000c95565b906200034262000cac565b6001600160a01b03918216845260038852848420928216845291875283832091168252855220549151911615158152f35b905034620002c5576060366003190112620002c5576200039262000c79565b916200039d62000c95565b90604435801515809103620002bd57338652600560209081528287205490946001600160a01b03918216156200043c5750907f4f2bd80fb4928b06abcd76e3b26209a615f0612f98dc4a8b176934b1a389933392913388526003865280838920971696878952865280838920951694858952865282882060ff1981541660ff84161790553388526005865282882054169482519485528401523392a480f35b60249084519063963e961b60e01b82523390820152fd5b828434620004c95780600319360112620004c9573381526005602052819020546001600160a01b0390811692836200049c5760208383620004943362000f7f565b915191168152f35b91516356352f0b60e11b8152339281019283526001600160a01b0390931660208301525081906040010390fd5b80fd5b905034620002c5576020366003190112620002c5578190620004ed62000c79565b6001600160a01b038082168652600560205292909420548216806200051b5750506200049460209362000f7f565b92516356352f0b60e11b81526001600160a01b039485169181019182529390921660208301525081906040010390fd5b505034620001165781600319360112620001165760018060a01b0391828154169160019384541693815192809160025491620005878362000cdb565b80875292828116908115620006225750600114620005d3575b5050508291620005b96200011294606093038462000d68565b805195869586526020860152840152606083019062000d8b565b92506002835260008051602062001be68339815191525b8284106200060957505050820160200181620005b962000112620005a0565b80546020858801810191909152909301928101620005ea565b60ff191660208089019190915293151560051b87019093019350849250620005b99150620001129050620005a0565b905034620002c55781600319360112620002c557816020936200067362000c79565b926200067e62000cc3565b6001600160a01b0394851683529086528282206001600160e01b0319909116825285522054915191168152f35b828434620004c9576060366003190112620004c957620006ca62000c79565b620006d462000c95565b90620006df62000cac565b8451638da5cb5b60e01b815260209691936001600160a01b039390928891839190829087165afa9081156200077b57918386949288969460ff989162000747575b50168452600388528185852091168452875283832091168252855220541690519015158152f35b6200076c91508a3d8c1162000773575b62000763818362000d68565b81019062000dcd565b8a62000720565b503d62000757565b86513d87823e3d90fd5b905034620002c55760209182600319360112620002c1576001600160a01b03823581811694919390859003620002bd573386526005825283838720541615620002a657825163ecdb286560e01b815286818381838a5af19081156200029c5787916200088f575b5080519182156200087857875b8381106200083857505050506005903386525283205416337f381d2d67cec77a56c13de6658effae4585e49ae536458929d53bfd4c3599e0ad8480a480f35b338952818552858920600191906001600160e01b03196200085a838762000f54565b51168b528652868a2080546001600160a01b031916905501620007f9565b845163b74066bb60e01b8152908101879052602490fd5b620008a691503d8089833e6200028c818362000d68565b38620007ec565b828434620004c95781600319360112620004c957620008cb62000c79565b90620008d662000cc3565b835194638da5cb5b60e01b86526020958681838160018060a01b038099165afa908115620009525784939291869188969162000930575b5016835286528282206001600160e01b0319909116825285522054915191168152f35b6200094b9150893d8b11620007735762000763818362000d68565b896200090d565b86513d86823e3d90fd5b90838534620004c95781600319360112620004c9576200097b62000c79565b9060243567ffffffffffffffff94858211620002c55736602383011215620002c5578181013595808711620002c1573660248885010111620002c1573384526020966005885260018060a01b03988988872054168062000c4f575050620009fa87513360601b8a82015260148152620009f48162000d4b565b62000dee565b9362000a7e8a808a5162000a0e8162000d18565b338152818d82019b168b52898d8d5197806024601f199962000a38858c601f860116018d62000d68565b828c5201838b013788010152858c8201525116986001600160601b0360a01b998a8a5416178955511660018060a01b03166001600160601b0360a01b6001541617600155565b81519183831162000c3c5790829162000a9960025462000cdb565b8b601f821162000bef575b50508a91601f841160011462000b7d5750879262000b71575b50508160011b916000199060031b1c1916176002555b8551916109f190818401928484109084111762000b5e5750908291620011f58339039083f594851562000b52578394951693849162000b1162000e2b565b338152600587522091825416179055805182815233907f2d8895d948115783fa362a57339c4c179365fafeafdd7dca66364ae296f50b75853392a351908152f35b508251903d90823e3d90fd5b634e487b7160e01b865260419052602485fd5b015190508a8062000abd565b6002895260008051602062001be68339815191529316885b8c82821062000bd857505090846001959493921062000bbe575b505050811b0160025562000ad3565b015160001960f88460031b161c191690558a808062000baf565b600185968293968601518155019501930162000b95565b62000c299160028b5260008051602062001be683398151915290601f870160051c820192871062000c31575b601f0160051c019062000e12565b8c8b62000aa4565b909150819062000c1b565b634e487b7160e01b875260418552602487fd5b6356352f0b60e11b8252338583019081526001600160a01b03909116602082015281906040010390fd5b600435906001600160a01b038216820362000c9057565b600080fd5b602435906001600160a01b038216820362000c9057565b604435906001600160a01b038216820362000c9057565b602435906001600160e01b03198216820362000c9057565b90600182811c9216801562000d0d575b602083101462000cf757565b634e487b7160e01b600052602260045260246000fd5b91607f169162000ceb565b6060810190811067ffffffffffffffff82111762000d3557604052565b634e487b7160e01b600052604160045260246000fd5b6040810190811067ffffffffffffffff82111762000d3557604052565b90601f8019910116810190811067ffffffffffffffff82111762000d3557604052565b919082519283825260005b84811062000db8575050826000602080949584010152601f8019910116010190565b60208183018101518483018201520162000d96565b9081602091031262000c9057516001600160a01b038116810362000c905790565b60208151910151906020811062000e03575090565b6000199060200360031b1b1690565b81811062000e1e575050565b6000815560010162000e12565b6000808055600181815562000e4260025462000cdb565b908162000e4e57505050565b601f821160011462000e61575050600255565b60028352601f60008051602062001be6833981519152920160051c82017f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5acf5b81811062000eb2575050508160025555565b848155820162000ea0565b90602090818382031262000c9057825167ffffffffffffffff9384821162000c90570181601f8201121562000c9057805193841162000d35578360051b906040519462000f0d8584018762000d68565b8552838086019282010192831162000c90578301905b82821062000f32575050505090565b81516001600160e01b03198116810362000c9057815290830190830162000f23565b805182101562000f695760209160051b010190565b634e487b7160e01b600052603260045260246000fd5b9060409182519060018060a01b0362000fb9818316936020936001600160601b03199060601b168482015260148152620009f48162000d4b565b9085519062000fc88262000d18565b8482528382019260009283855288518681019167ffffffffffffffff9282811084821117620011e057856200103892848e839481528a82528201525116976001600160601b0360a01b9889895416178855511660018060a01b03166001600160601b0360a01b6001541617600155565b805190828211620011095781906200105260025462000cdb565b601f8111620011a0575b508890601f8311600114620011295787926200111d575b50508160011b916000199060031b1c1916176002555b8851906109f1808301918211838310176200110957908291620011f58339039084f58015620010ff57917f2d8895d948115783fa362a57339c4c179365fafeafdd7dca66364ae296f50b759391889316978891620010e662000e2b565b87815260058752209182541617905551918583523392a3565b87513d84823e3d90fd5b634e487b7160e01b86526041600452602486fd5b01519050388062001073565b6002885260008051602062001be68339815191529250601f198416885b8b828210620011895750509084600195949392106200116f575b505050811b0160025562001089565b015160001960f88460031b161c1916905538808062001160565b600185968293968601518155019501930162001146565b620011d9906002895260008051602062001be6833981519152601f850160051c8101918c861062000c3157601f0160051c019062000e12565b386200105c565b634e487b7160e01b87526041600452602487fdfe60c08060405234620000ca573360a0526366b0182d60e01b8152600081600481335afa8015620000c4576000809281926200009b575b506080526001600160a01b03821662000087575b60405161068e9081620003638239608051818181610192015281816102c401526104a5015260a05181818161014d0152818161031301526105250152f35b6200009291620002b2565b50388062000049565b909150620000bb92503d8092823e620000b482620000e5565b016200018e565b90913862000035565b62000221565b600080fd5b634e487b7160e01b600052604160045260246000fd5b60c0601f91909101601f19168101906001600160401b038211908210176200010c57604052565b620000cf565b601f909101601f19168101906001600160401b038211908210176200010c57604052565b60e051906001600160a01b0382168203620000ca57565b6001600160401b0381116200010c57601f01601f191660200190565b60005b8381106200017d5750506000910152565b81810151838201526020016200016c565b606060bf19820112620000ca5760c0516001600160a01b0381168103620000ca5791620001ba62000136565b610100519092906001600160401b038111620000ca578160df82011215620000ca578060c00151620001ec816200014d565b92620001fc604051948562000112565b81845260e08284010111620000ca576200021e9160e060208501910162000169565b90565b6040513d6000823e3d90fd5b3d156200025d573d9062000241826200014d565b9162000251604051938462000112565b82523d6000602084013e565b606090565b906020916200027d8151809281855285808601910162000169565b601f01601f1916010190565b9091620002a36200021e9360408452604084019062000262565b91602081840391015262000262565b9190823b156200034157600080825160208401865af4907fb24ebe141c5f2a744b103bea65fce6c40e0dc65d7341d092c09b160f40447990620002f46200022d565b60405190956001600160a01b0316928190620003139088908362000289565b0390a2156200031e57565b508051156200032f57602081519101fd5b60405163061a160d60e41b8152600490fd5b604051636d17e5ef60e11b81526001600160a01b0384166004820152602490fdfe60806040526004361015610027575b36156100255761001d366102b5565b602081519101f35b005b6000803560e01c9081631cff79cd1461005a575080637b1039991461005557638da5cb5b0361000e5761017c565b610137565b60403660031901126100ca57600435610072816100cd565b60243567ffffffffffffffff928382116100ca57366023830112156100ca5781600401359384116100ca5736602485840101116100ca576100c66100ba8560248501866104a2565b60405191829182610123565b0390f35b80fd5b6001600160a01b038116036100de57565b600080fd5b919082519283825260005b84811061010f575050826000602080949584010152601f8019910116010190565b6020818301810151848301820152016100ee565b9060206101349281815201906100e3565b90565b346100de5760003660031901126100de576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b346100de5760003660031901126100de576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b634e487b7160e01b600052604160045260246000fd5b90601f8019910116810190811067ffffffffffffffff8211176101f957604052565b6101c1565b908160209103126100de5751610134816100cd565b6040513d6000823e3d90fd5b908160008237016000815290565b67ffffffffffffffff81116101f957601f01601f191660200190565b3d15610274573d9061025a8261022d565b9161026860405193846101d7565b82523d6000602084013e565b606090565b606090610134939260408252806040830152806000848401376000838284010152601f80199101168101906020838284030191015201906100e3565b6040516361be485960e01b81527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0381811660048401526001600160e01b03196000351660248401819052939493906020846044817f000000000000000000000000000000000000000000000000000000000000000085165afa93841561044e5760009461041e575b5083169182156103e357505060008060405180610362818961021f565b0390845af4907fc4dabe0d7ef7462e2218f2c398c21ef217803e1c46f5cf802d1a5d1d9b503f2f610391610249565b80966103a260405192839283610279565b0390a2156103ad5750565b8251909150156103c05750805190602001fd5b60405163023c045d60e21b81526001600160a01b03919091166004820152602490fd5b604051638848730f60e01b81523360048201526001600160a01b039190911660248201526001600160e01b0319919091166044820152606490fd5b61044091945060203d8111610447575b61043881836101d7565b8101906101fe565b9238610345565b503d61042e565b610213565b908160209103126100de575180151581036100de5790565b9291926104778261022d565b9161048560405193846101d7565b8294818452818301116100de578281602093846000960137010152565b917f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0333818316036104ee575b505061013492916104e891369161046b565b906105e5565b60405163b31d1b9960e01b81526001600160a01b0383811660048301523360248301528616604482015290602090829060649082907f0000000000000000000000000000000000000000000000000000000000000000165afa90811561044e57600091610592575b501561056257806104d6565b6040516355d1750960e01b81526001600160a01b0391821660048201523360248201529084166044820152606490fd5b6105b3915060203d81116105b9575b6105ab81836101d7565b810190610453565b38610556565b503d6105a1565b90916105d7610134936040845260408401906100e3565b9160208184039101526100e3565b9190823b1561066d57600080825160208401865af4907fb24ebe141c5f2a744b103bea65fce6c40e0dc65d7341d092c09b160f40447990610624610249565b60405190956001600160a01b0316928190610641908890836105c0565b0390a21561064b57565b5080511561065b57602081519101fd5b60405163061a160d60e41b8152600490fd5b604051636d17e5ef60e11b81526001600160a01b0384166004820152602490fd405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace"; /*////////////////////////////////////////////////////////////////////////// DEPLOYERS