From 231b87fbe0d7de3b49d6a25d441f36021c040c37 Mon Sep 17 00:00:00 2001 From: Paul Razvan Berg Date: Sun, 18 Jun 2023 15:45:31 +0300 Subject: [PATCH] fix: use owner as CREATE2 salt docs: improve writing in README refactor: delete "nextSeeds" refactor: delete stale CREATE2 seeds refactor: remove "origin" in "DeployProxy" event test: remove unused constants test: update tests in light of new contract logic test: remove stale "origin" fuzzing --- README.md | 9 +-- src/PRBProxyRegistry.sol | 59 +++------------ src/interfaces/IPRBProxyRegistry.sol | 27 ++----- test/Base.t.sol | 15 +--- .../deploy-and-execute/deployAndExecute.t.sol | 68 ++++------------- .../deploy-and-execute/deployAndExecute.tree | 1 - test/registry/deploy-for/deployFor.t.sol | 73 +++---------------- test/registry/deploy-for/deployFor.tree | 1 - test/registry/deploy/deploy.t.sol | 38 +++------- test/registry/deploy/deploy.tree | 1 - test/utils/Events.sol | 9 +-- test/utils/Precompiles.sol | 2 +- 12 files changed, 64 insertions(+), 239 deletions(-) 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 b8f1143..f9d92f1 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,37 +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)); + // Use the address of the owner as the CREATE2 salt. + address owner = msg.sender; + bytes32 salt = bytes32(abi.encodePacked(owner)); // Deploy the proxy with CREATE2, and execute the delegate call in the constructor. - address owner = msg.sender; 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 @@ -247,35 +227,18 @@ 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)); // Deploy the proxy with CREATE2. constructorParams.owner = owner; 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 71e3f49..671f5d8 100644 --- a/test/utils/Precompiles.sol +++ b/test/utils/Precompiles.sol @@ -12,7 +12,7 @@ contract Precompiles { //////////////////////////////////////////////////////////////////////////*/ bytes public constant BYTECODE_REGISTRY = - hex"6080806040523461001657611b2e908161001c8239f35b600080fdfe60808060405260043610156200001457600080fd5b60003560e01c908163092af81314620009b8575080631ddef5b914620009095780632c27a01f14620008cb5780634bddd93a14620007ab5780635cabcdf714620006db57806361be4859146200067c57806366b0182d146200057c57806374912cd21462000507578063775c300c1462000493578063aa4b826a14620003c4578063b31d1b99146200034f578063b7fba4d31462000310578063b9678403146200014c5763ffa1ad7414620000c857600080fd5b34620001475760003660031901126200014757604051604081019080821067ffffffffffffffff83111762000131576200012d91604052600c81526b342e302e302d626574612e3560a01b602082015260405191829160208352602083019062000e35565b0390f35b634e487b7160e01b600052604160045260246000fd5b600080fd5b34620001475760208060031936011262000147576001600160a01b03600435818116929083900362000147573360005260068152816040600020541615620002f85760405163ecdb286560e01b815260008160048183885af1908115620002ec57600091620002c4575b508051908115620002ab5760005b8281106200020657505050600690336000525260406000205416337f9638c85f58d5866b4eb299b2e5f8f366372bd58e2d6e6e6eb19f3462948f3387600080a4005b6001600160e01b03196200021b828462000fc1565b5116336000526005808652604060002082600052865286604060002054166200026c5733600090815290865260408082209282529186522080546001600160a01b03191687179055600101620001c4565b60649187878a9333600052815260406000209083600052526040600020541691604051926361a2116b60e11b8452600484015260248301526044820152fd5b60405163b74066bb60e01b815260048101869052602490fd5b620002e591503d806000833e620002dc818362000e12565b81019062000f2a565b84620001b6565b6040513d6000823e3d90fd5b60405163963e961b60e01b8152336004820152602490fd5b3462000147576020366003190112620001475760206001600160a01b03806200033862000d78565b166000526006825260406000205416604051908152f35b346200014757606036600319011262000147576200036c62000d78565b6200037662000d8f565b906200038162000da6565b9160018060a01b038092166000526004602052816040600020911660005260205260406000209116600052602052602060ff604060002054166040519015158152f35b34620001475760603660031901126200014757620003e162000d78565b620003eb62000d8f565b604435908115158092036200014757336000526020916006835260018060a01b03806040600020541615620002f8573360005260048452806040600020951694856000528452806040600020931692836000528452604060002060ff1981541660ff8416179055336000526006845260406000205416926040519283528201527f4f2bd80fb4928b06abcd76e3b26209a615f0612f98dc4a8b176934b1a389933360403392a4005b34620001475760003660031901126200014757336000908152600660205260409020546001600160a01b0390811680620004de57602082620004d53362000fec565b60405191168152f35b6040516356352f0b60e11b81523360048201526001600160a01b03919091166024820152604490fd5b346200014757602036600319011262000147576200052462000d78565b60018060a01b039081811660005260066020528160406000205416806200055357602083620004d58462000fec565b6040516356352f0b60e11b81526001600160a01b03928316600482015291166024820152604490fd5b3462000147576000366003190112620001475760018060a01b038060005416600191825416916040519060009060025490620005b88262000dd5565b8085529181811690811562000653575060011462000605575b505090620005e6816200012d93038262000e12565b6040519384938452602084015260606040840152606083019062000e35565b60026000908152925060008051602062001b0e8339815191525b8284106200063a575050508101602001620005e682620005d1565b805460208587018101919091529093019281016200061f565b60ff191660208087019190915292151560051b85019092019250620005e69150839050620005d1565b3462000147576040366003190112620001475760206200069b62000d78565b620006a562000dbd565b6001600160a01b0391821660009081526005845260408082206001600160e01b0319909316825291845281902054905191168152f35b34620001475760603660031901126200014757620006f862000d78565b6200070262000d8f565b906200070d62000da6565b9060405192638da5cb5b60e01b8452602093848160048160018060a01b038097165afa8015620002ec57839160009162000777575b5016600052600484528160406000209116600052835260406000209116600052815260ff604060002054166040519015158152f35b6200079c9150863d8811620007a3575b62000793818362000e12565b81019062000e77565b8662000742565b503d62000787565b34620001475760208060031936011262000147576001600160a01b03600435818116929083900362000147573360005260068152816040600020541615620002f85760405163ecdb286560e01b815260008160048183885af1908115620002ec57600091620008ac575b508051908115620002ab5760005b8281106200086557505050600690336000525260406000205416337f381d2d67cec77a56c13de6658effae4585e49ae536458929d53bfd4c3599e0ad600080a4005b6001903360005260058552604060002063ffffffff60e01b62000889838662000fc1565b5116600052855260406000206001600160601b0360a01b81541690550162000823565b620008c491503d806000833e620002dc818362000e12565b8462000815565b346200014757602036600319011262000147576001600160a01b03620008f062000d78565b1660005260036020526020604060002054604051908152f35b346200014757604036600319011262000147576200092662000d78565b6200093062000dbd565b604051638da5cb5b60e01b81526020926001600160a01b0392919084908290600490829087165afa8015620002ec57839160009162000996575b50166000526005835260406000209063ffffffff60e01b16600052825260406000205416604051908152f35b620009b19150853d8711620007a35762000793818362000e12565b856200096a565b34620001475760403660031901126200014757620009d562000d78565b9067ffffffffffffffff602435116200014757366023602435011215620001475767ffffffffffffffff6024356004013511620001475736602480356004013581350101116200014757336000908152600660205260409020546001600160a01b03168062000d55573260008181526003602090815260409182902054825191820193845291810182905285929062000a7c81606081015b03601f19810183528262000e12565b519020604051926060840184811067ffffffffffffffff821117620001315760405233845260018060a01b031660208401526040519262000ace6020601f19601f602435600401350116018562000e12565b6024803560048101358087529101602086013760006020602435600401358601015283604082015260018060a01b038151166001600160601b0360a01b6000541617600055602060018060a01b03910151166001600160601b0360a01b6001541617600155825167ffffffffffffffff8111620001315762000b5260025462000dd5565b601f811162000ce0575b506020601f821160011462000c68578192939460009262000c5c575b50508160011b916000199060031b1c1916176002555b6040516109f1908181019181831067ffffffffffffffff8411176200013157839282916200111d833903906000f5918215620002ec576020926001600160a01b03169162000bdb62000e98565b33600081815260068652604080822080546001600160a01b03191687179055328083526003885291819020600186019055805194855260208501939093526001600160a01b03851692840192909252918291907f6aafca263a35a9d2a6e4e4659a84688092f4ae153df2f95cd7659508d95c187090606090a4604051908152f35b01519050848062000b78565b601f19821690600260005260008051602062001b0e8339815191529160005b81811062000cc75750958360019596971062000cad575b505050811b0160025562000b8e565b015160001960f88460031b161c1916905584808062000c9e565b9192602060018192868b01518155019401920162000c87565b6002600052601f820160051c60008051602062001b0e83398151915201906020831062000d3d575b601f0160051c60008051602062001b0e83398151915201905b81811062000d30575062000b5c565b6000815560010162000d21565b60008051602062001b0e833981519152915062000d08565b6356352f0b60e11b82523360048301526001600160a01b03166024820152604490fd5b600435906001600160a01b03821682036200014757565b602435906001600160a01b03821682036200014757565b604435906001600160a01b03821682036200014757565b602435906001600160e01b0319821682036200014757565b90600182811c9216801562000e07575b602083101462000df157565b634e487b7160e01b600052602260045260246000fd5b91607f169162000de5565b90601f8019910116810190811067ffffffffffffffff8211176200013157604052565b919082519283825260005b84811062000e62575050826000602080949584010152601f8019910116010190565b60208183018101518483018201520162000e40565b908160209103126200014757516001600160a01b0381168103620001475790565b6000808055600181815562000eaf60025462000dd5565b908162000ebb57505050565b601f821160011462000ece575050600255565b60028352601f60008051602062001b0e833981519152920160051c82017f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5acf5b81811062000f1f575050508160025555565b848155820162000f0d565b9060209081838203126200014757825167ffffffffffffffff9384821162000147570181601f820112156200014757805193841162000131578360051b906040519462000f7a8584018762000e12565b8552838086019282010192831162000147578301905b82821062000f9f575050505090565b81516001600160e01b0319811681036200014757815290830190830162000f90565b805182101562000fd65760209160051b010190565b634e487b7160e01b600052603260045260246000fd5b326000818152600360209081526040808320548151928301948552828201819052949594929390929162001024816060810162000a6d565b5190209160018060a01b03809616936001600160601b0360a01b96858883541617825582516109f18082019082821067ffffffffffffffff831117620011085791809188936200111d8339039084f58015620010fe5716967f6aafca263a35a9d2a6e4e4659a84688092f4ae153df2f95cd7659508d95c1870929190620010aa62000e98565b86825260066020528883832091825416179055328152600360205281600185019120555180620010f98833963296849192604091949360608401958452602084015260018060a01b0316910152565b0390a4565b83513d84823e3d90fd5b634e487b7160e01b85526041600452602485fdfe60c08060405234620000ca573360a0526366b0182d60e01b8152600081600481335afa8015620000c4576000809281926200009b575b506080526001600160a01b03821662000087575b60405161068e9081620003638239608051818181610192015281816102c401526104a5015260a05181818161014d0152818161031301526105250152f35b6200009291620002b2565b50388062000049565b909150620000bb92503d8092823e620000b482620000e5565b016200018e565b90913862000035565b62000221565b600080fd5b634e487b7160e01b600052604160045260246000fd5b60c0601f91909101601f19168101906001600160401b038211908210176200010c57604052565b620000cf565b601f909101601f19168101906001600160401b038211908210176200010c57604052565b60e051906001600160a01b0382168203620000ca57565b6001600160401b0381116200010c57601f01601f191660200190565b60005b8381106200017d5750506000910152565b81810151838201526020016200016c565b606060bf19820112620000ca5760c0516001600160a01b0381168103620000ca5791620001ba62000136565b610100519092906001600160401b038111620000ca578160df82011215620000ca578060c00151620001ec816200014d565b92620001fc604051948562000112565b81845260e08284010111620000ca576200021e9160e060208501910162000169565b90565b6040513d6000823e3d90fd5b3d156200025d573d9062000241826200014d565b9162000251604051938462000112565b82523d6000602084013e565b606090565b906020916200027d8151809281855285808601910162000169565b601f01601f1916010190565b9091620002a36200021e9360408452604084019062000262565b91602081840391015262000262565b9190823b156200034157600080825160208401865af4907fb24ebe141c5f2a744b103bea65fce6c40e0dc65d7341d092c09b160f40447990620002f46200022d565b60405190956001600160a01b0316928190620003139088908362000289565b0390a2156200031e57565b508051156200032f57602081519101fd5b60405163061a160d60e41b8152600490fd5b604051636d17e5ef60e11b81526001600160a01b0384166004820152602490fdfe60806040526004361015610027575b36156100255761001d366102b5565b602081519101f35b005b6000803560e01c9081631cff79cd1461005a575080637b1039991461005557638da5cb5b0361000e5761017c565b610137565b60403660031901126100ca57600435610072816100cd565b60243567ffffffffffffffff928382116100ca57366023830112156100ca5781600401359384116100ca5736602485840101116100ca576100c66100ba8560248501866104a2565b60405191829182610123565b0390f35b80fd5b6001600160a01b038116036100de57565b600080fd5b919082519283825260005b84811061010f575050826000602080949584010152601f8019910116010190565b6020818301810151848301820152016100ee565b9060206101349281815201906100e3565b90565b346100de5760003660031901126100de576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b346100de5760003660031901126100de576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b634e487b7160e01b600052604160045260246000fd5b90601f8019910116810190811067ffffffffffffffff8211176101f957604052565b6101c1565b908160209103126100de5751610134816100cd565b6040513d6000823e3d90fd5b908160008237016000815290565b67ffffffffffffffff81116101f957601f01601f191660200190565b3d15610274573d9061025a8261022d565b9161026860405193846101d7565b82523d6000602084013e565b606090565b606090610134939260408252806040830152806000848401376000838284010152601f80199101168101906020838284030191015201906100e3565b6040516361be485960e01b81527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0381811660048401526001600160e01b03196000351660248401819052939493906020846044817f000000000000000000000000000000000000000000000000000000000000000085165afa93841561044e5760009461041e575b5083169182156103e357505060008060405180610362818961021f565b0390845af4907fc4dabe0d7ef7462e2218f2c398c21ef217803e1c46f5cf802d1a5d1d9b503f2f610391610249565b80966103a260405192839283610279565b0390a2156103ad5750565b8251909150156103c05750805190602001fd5b60405163023c045d60e21b81526001600160a01b03919091166004820152602490fd5b604051638848730f60e01b81523360048201526001600160a01b039190911660248201526001600160e01b0319919091166044820152606490fd5b61044091945060203d8111610447575b61043881836101d7565b8101906101fe565b9238610345565b503d61042e565b610213565b908160209103126100de575180151581036100de5790565b9291926104778261022d565b9161048560405193846101d7565b8294818452818301116100de578281602093846000960137010152565b917f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0333818316036104ee575b505061013492916104e891369161046b565b906105e5565b60405163b31d1b9960e01b81526001600160a01b0383811660048301523360248301528616604482015290602090829060649082907f0000000000000000000000000000000000000000000000000000000000000000165afa90811561044e57600091610592575b501561056257806104d6565b6040516355d1750960e01b81526001600160a01b0391821660048201523360248201529084166044820152606490fd5b6105b3915060203d81116105b9575b6105ab81836101d7565b810190610453565b38610556565b503d6105a1565b90916105d7610134936040845260408401906100e3565b9160208184039101526100e3565b9190823b1561066d57600080825160208401865af4907fb24ebe141c5f2a744b103bea65fce6c40e0dc65d7341d092c09b160f40447990610624610249565b60405190956001600160a01b0316928190610641908890836105c0565b0390a21561064b57565b5080511561065b57602081519101fd5b60405163061a160d60e41b8152600490fd5b604051636d17e5ef60e11b81526001600160a01b0384166004820152602490fd405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace"; + hex"6080806040523461001657611a5f908161001c8239f35b600080fdfe60806040818152600491823610156200001757600080fd5b600092833560e01c918263092af813146200095c575081631ddef5b914620008ad5781634bddd93a14620007855781635cabcdf714620006ab57816361be4859146200065157816366b0182d146200054b57816374912cd214620004cc578163775c300c1462000453578163aa4b826a1462000373578163b31d1b991462000308578163b7fba4d314620002c9578163b9678403146200011a575063ffa1ad7414620000c257600080fd5b34620001165781600319360112620001165780516200011291620000e68262000d20565b600c82526b342e302e302d626574612e3560a01b60208301525191829160208352602083019062000d76565b0390f35b5080fd5b905034620002c55760209182600319360112620002c1576001600160a01b03823581811694919390859003620002bd573386526005825283838720541615620002a657825163ecdb286560e01b815286818381838a5af19081156200029c57879162000275575b5080519081156200025e57875b828110620001cd57505050506005903386525283205416337f9638c85f58d5866b4eb299b2e5f8f366372bd58e2d6e6e6eb19f3462948f33878480a480f35b6001600160e01b0319620001e2828462000f26565b5116338a52848652868a20818b52865287878b2054166200022757338a52848652868a20908a52855285892080546001600160a01b031916891790556001016200018e565b846064918a898b818f8c33825287815282822090878352522054169051936361a2116b60e11b855284015260248301526044820152fd5b845163b74066bb60e01b8152808401889052602490fd5b6200029591503d8089833e6200028c818362000d53565b81019062000e8f565b3862000181565b84513d89823e3d90fd5b60249083519063963e961b60e01b82523390820152fd5b8580fd5b8380fd5b8280fd5b5050346200011657602036600319011262000116576020916001600160a01b0390829082620002f762000c81565b168152600585522054169051908152f35b50503462000116576060366003190112620001165760ff816020936200032d62000c81565b6200033762000c9d565b906200034262000cb4565b6001600160a01b03918216845260038852848420928216845291875283832091168252855220549151911615158152f35b905034620002c5576060366003190112620002c5576200039262000c81565b916200039d62000c9d565b90604435801515809103620002bd57338652600560209081528287205490946001600160a01b03918216156200043c5750907f4f2bd80fb4928b06abcd76e3b26209a615f0612f98dc4a8b176934b1a389933392913388526003865280838920971696878952865280838920951694858952865282882060ff1981541660ff84161790553388526005865282882054169482519485528401523392a480f35b60249084519063963e961b60e01b82523390820152fd5b828434620004c95780600319360112620004c9573381526005602052819020546001600160a01b0390811692836200049c5760208383620004943362000f51565b915191168152f35b91516356352f0b60e11b8152339281019283526001600160a01b0390931660208301525081906040010390fd5b80fd5b905034620002c5576020366003190112620002c5578190620004ed62000c81565b6001600160a01b038082168652600560205292909420548216806200051b5750506200049460209362000f51565b92516356352f0b60e11b81526001600160a01b039485169181019182529390921660208301525081906040010390fd5b505034620001165781600319360112620001165760018060a01b0391828154169160019384541693815192809160025491620005878362000ce3565b80875292828116908115620006225750600114620005d3575b5050508291620005b96200011294606093038462000d53565b805195869586526020860152840152606083019062000d76565b92506002835260008051602062001a3f8339815191525b8284106200060957505050820160200181620005b962000112620005a0565b80546020858801810191909152909301928101620005ea565b60ff191660208089019190915293151560051b87019093019350849250620005b99150620001129050620005a0565b905034620002c55781600319360112620002c557816020936200067362000c81565b926200067e62000ccb565b6001600160a01b0394851683529086528282206001600160e01b0319909116825285522054915191168152f35b828434620004c9576060366003190112620004c957620006ca62000c81565b620006d462000c9d565b90620006df62000cb4565b8451638da5cb5b60e01b815260209691936001600160a01b039390928891839190829087165afa9081156200077b57918386949288969460ff989162000747575b50168452600388528185852091168452875283832091168252855220541690519015158152f35b6200076c91508a3d8c1162000773575b62000763818362000d53565b81019062000db8565b8a62000720565b503d62000757565b86513d87823e3d90fd5b905034620002c55760209182600319360112620002c1576001600160a01b03823581811694919390859003620002bd573386526005825283838720541615620002a657825163ecdb286560e01b815286818381838a5af19081156200029c5787916200088f575b5080519182156200087857875b8381106200083857505050506005903386525283205416337f381d2d67cec77a56c13de6658effae4585e49ae536458929d53bfd4c3599e0ad8480a480f35b338952818552858920600191906001600160e01b03196200085a838762000f26565b51168b528652868a2080546001600160a01b031916905501620007f9565b845163b74066bb60e01b8152908101879052602490fd5b620008a691503d8089833e6200028c818362000d53565b38620007ec565b828434620004c95781600319360112620004c957620008cb62000c81565b90620008d662000ccb565b835194638da5cb5b60e01b86526020958681838160018060a01b038099165afa908115620009525784939291869188969162000930575b5016835286528282206001600160e01b0319909116825285522054915191168152f35b6200094b9150893d8b11620007735762000763818362000d53565b896200090d565b86513d86823e3d90fd5b90838534620004c95781600319360112620004c9576200097b62000c81565b9060249384359467ffffffffffffffff808711620002c15736602388011215620002c157868301359281841162000c7d573683858a01011162000c7d573385526020976005895260018060a01b03998a89882054168062000c53575050620009fb88513360601b8b82015260148152620009f58162000d20565b62000dd9565b948851606081018181108682111762000c41578c9182918c52338152818d82019b168b52898d8d5195808b601f199962000a3d858c601f860116018b62000d53565b828a52018389013786010152838c8201525116986bffffffffffffffffffffffff60a01b998a8a541617895551166001908982541617815581519185831162000c41578b62000a8e60025462000ce3565b601f811162000be6575b50508b90601f841160011462000b7357839450908392918a9462000b67575b50501b916000199060031b1c1916176002555b8651926109f191828501938585109085111762000b565750509082916200104e8339039083f594851562000b4a578394951693849162000b0962000dfd565b338152600587522091825416179055805182815233907f2d8895d948115783fa362a57339c4c179365fafeafdd7dca66364ae296f50b75853392a351908152f35b508251903d90823e3d90fd5b634e487b7160e01b87526041905285fd5b015192508d8062000ab7565b838d9395811660028c5260008051602062001a3f833981519152948c905b8883831062000bcd575050501062000bb3575b505050811b0160025562000aca565b015160001960f88460031b161c191690558b808062000ba4565b8686015188559096019594850194879350018f62000b91565b60028b5260008051602062001a3f83398151915290601f860160051c820192861062000c36575b601f0160051c019083905b82811062000c2a57508d915062000a98565b8b815501839062000c18565b909150819062000c0d565b634e487b7160e01b8952604185528689fd5b6356352f0b60e11b8252338483019081526001600160a01b03909116602082015281906040010390fd5b8480fd5b600435906001600160a01b038216820362000c9857565b600080fd5b602435906001600160a01b038216820362000c9857565b604435906001600160a01b038216820362000c9857565b602435906001600160e01b03198216820362000c9857565b90600182811c9216801562000d15575b602083101462000cff57565b634e487b7160e01b600052602260045260246000fd5b91607f169162000cf3565b6040810190811067ffffffffffffffff82111762000d3d57604052565b634e487b7160e01b600052604160045260246000fd5b90601f8019910116810190811067ffffffffffffffff82111762000d3d57604052565b919082519283825260005b84811062000da3575050826000602080949584010152601f8019910116010190565b60208183018101518483018201520162000d81565b9081602091031262000c9857516001600160a01b038116810362000c985790565b60208151910151906020811062000dee575090565b6000199060200360031b1b1690565b6000808055600181815562000e1460025462000ce3565b908162000e2057505050565b601f821160011462000e33575050600255565b60028352601f60008051602062001a3f833981519152920160051c82017f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5acf5b81811062000e84575050508160025555565b848155820162000e72565b90602090818382031262000c9857825167ffffffffffffffff9384821162000c98570181601f8201121562000c9857805193841162000d3d578360051b906040519462000edf8584018762000d53565b8552838086019282010192831162000c98578301905b82821062000f04575050505090565b81516001600160e01b03198116810362000c9857815290830190830162000ef5565b805182101562000f3b5760209160051b010190565b634e487b7160e01b600052603260045260246000fd5b604051606082901b6bffffffffffffffffffffffff19166020820152601481526001600160a01b03929183169062000f8e90620009f58162000d20565b6bffffffffffffffffffffffff60a01b9060009083838354161782556040516109f18082019082821067ffffffffffffffff83111762001039579082916200104e8339039083f59485156200102c576040951694859162000fee62000dfd565b848152600560205220918254161790556040518381527f2d8895d948115783fa362a57339c4c179365fafeafdd7dca66364ae296f50b7560203392a3565b50604051903d90823e3d90fd5b634e487b7160e01b85526041600452602485fdfe60c08060405234620000ca573360a0526366b0182d60e01b8152600081600481335afa8015620000c4576000809281926200009b575b506080526001600160a01b03821662000087575b60405161068e9081620003638239608051818181610192015281816102c401526104a5015260a05181818161014d0152818161031301526105250152f35b6200009291620002b2565b50388062000049565b909150620000bb92503d8092823e620000b482620000e5565b016200018e565b90913862000035565b62000221565b600080fd5b634e487b7160e01b600052604160045260246000fd5b60c0601f91909101601f19168101906001600160401b038211908210176200010c57604052565b620000cf565b601f909101601f19168101906001600160401b038211908210176200010c57604052565b60e051906001600160a01b0382168203620000ca57565b6001600160401b0381116200010c57601f01601f191660200190565b60005b8381106200017d5750506000910152565b81810151838201526020016200016c565b606060bf19820112620000ca5760c0516001600160a01b0381168103620000ca5791620001ba62000136565b610100519092906001600160401b038111620000ca578160df82011215620000ca578060c00151620001ec816200014d565b92620001fc604051948562000112565b81845260e08284010111620000ca576200021e9160e060208501910162000169565b90565b6040513d6000823e3d90fd5b3d156200025d573d9062000241826200014d565b9162000251604051938462000112565b82523d6000602084013e565b606090565b906020916200027d8151809281855285808601910162000169565b601f01601f1916010190565b9091620002a36200021e9360408452604084019062000262565b91602081840391015262000262565b9190823b156200034157600080825160208401865af4907fb24ebe141c5f2a744b103bea65fce6c40e0dc65d7341d092c09b160f40447990620002f46200022d565b60405190956001600160a01b0316928190620003139088908362000289565b0390a2156200031e57565b508051156200032f57602081519101fd5b60405163061a160d60e41b8152600490fd5b604051636d17e5ef60e11b81526001600160a01b0384166004820152602490fdfe60806040526004361015610027575b36156100255761001d366102b5565b602081519101f35b005b6000803560e01c9081631cff79cd1461005a575080637b1039991461005557638da5cb5b0361000e5761017c565b610137565b60403660031901126100ca57600435610072816100cd565b60243567ffffffffffffffff928382116100ca57366023830112156100ca5781600401359384116100ca5736602485840101116100ca576100c66100ba8560248501866104a2565b60405191829182610123565b0390f35b80fd5b6001600160a01b038116036100de57565b600080fd5b919082519283825260005b84811061010f575050826000602080949584010152601f8019910116010190565b6020818301810151848301820152016100ee565b9060206101349281815201906100e3565b90565b346100de5760003660031901126100de576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b346100de5760003660031901126100de576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b634e487b7160e01b600052604160045260246000fd5b90601f8019910116810190811067ffffffffffffffff8211176101f957604052565b6101c1565b908160209103126100de5751610134816100cd565b6040513d6000823e3d90fd5b908160008237016000815290565b67ffffffffffffffff81116101f957601f01601f191660200190565b3d15610274573d9061025a8261022d565b9161026860405193846101d7565b82523d6000602084013e565b606090565b606090610134939260408252806040830152806000848401376000838284010152601f80199101168101906020838284030191015201906100e3565b6040516361be485960e01b81527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0381811660048401526001600160e01b03196000351660248401819052939493906020846044817f000000000000000000000000000000000000000000000000000000000000000085165afa93841561044e5760009461041e575b5083169182156103e357505060008060405180610362818961021f565b0390845af4907fc4dabe0d7ef7462e2218f2c398c21ef217803e1c46f5cf802d1a5d1d9b503f2f610391610249565b80966103a260405192839283610279565b0390a2156103ad5750565b8251909150156103c05750805190602001fd5b60405163023c045d60e21b81526001600160a01b03919091166004820152602490fd5b604051638848730f60e01b81523360048201526001600160a01b039190911660248201526001600160e01b0319919091166044820152606490fd5b61044091945060203d8111610447575b61043881836101d7565b8101906101fe565b9238610345565b503d61042e565b610213565b908160209103126100de575180151581036100de5790565b9291926104778261022d565b9161048560405193846101d7565b8294818452818301116100de578281602093846000960137010152565b917f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0333818316036104ee575b505061013492916104e891369161046b565b906105e5565b60405163b31d1b9960e01b81526001600160a01b0383811660048301523360248301528616604482015290602090829060649082907f0000000000000000000000000000000000000000000000000000000000000000165afa90811561044e57600091610592575b501561056257806104d6565b6040516355d1750960e01b81526001600160a01b0391821660048201523360248201529084166044820152606490fd5b6105b3915060203d81116105b9575b6105ab81836101d7565b810190610453565b38610556565b503d6105a1565b90916105d7610134936040845260408401906100e3565b9160208184039101526100e3565b9190823b1561066d57600080825160208401865af4907fb24ebe141c5f2a744b103bea65fce6c40e0dc65d7341d092c09b160f40447990610624610249565b60405190956001600160a01b0316928190610641908890836105c0565b0390a21561064b57565b5080511561065b57602081519101fd5b60405163061a160d60e41b8152600490fd5b604051636d17e5ef60e11b81526001600160a01b0384166004820152602490fd405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace"; /*////////////////////////////////////////////////////////////////////////// DEPLOYERS