diff --git a/src/PRBProxyRegistry.sol b/src/PRBProxyRegistry.sol index 72c161e..b8f1143 100644 --- a/src/PRBProxyRegistry.sol +++ b/src/PRBProxyRegistry.sol @@ -191,7 +191,16 @@ contract PRBProxyRegistry is IPRBProxyRegistry { // Install every method in the list. address owner = msg.sender; for (uint256 i = 0; i < length;) { - _plugins[owner][methodList[i]] = plugin; + // Check for collisions. + bytes4 method = methodList[i]; + if (address(_plugins[owner][method]) != address(0)) { + revert PRBProxyRegistry_PluginMethodCollision({ + currentPlugin: _plugins[owner][method], + newPlugin: plugin, + method: method + }); + } + _plugins[owner][method] = plugin; unchecked { i += 1; } diff --git a/src/interfaces/IPRBProxy.sol b/src/interfaces/IPRBProxy.sol index bb3ff37..acd1d04 100644 --- a/src/interfaces/IPRBProxy.sol +++ b/src/interfaces/IPRBProxy.sol @@ -56,13 +56,13 @@ interface IPRBProxy { NON-CONSTANT FUNCTIONS //////////////////////////////////////////////////////////////////////////*/ - /// @notice Delegate calls to the provided target contract, forwarding the data. It returns the data it + /// @notice Delegate calls to the provided target contract by forwarding the data. It returns the data it /// gets back, and bubbles up any potential revert. /// /// @dev Emits an {Execute} event. /// /// Requirements: - /// - The caller must be either an owner or an envoy with permission. + /// - The caller must be either the owner or an envoy with permission. /// - `target` must be a contract. /// /// @param target The address of the target contract. diff --git a/src/interfaces/IPRBProxyRegistry.sol b/src/interfaces/IPRBProxyRegistry.sol index 77b15b5..b9b7652 100644 --- a/src/interfaces/IPRBProxyRegistry.sol +++ b/src/interfaces/IPRBProxyRegistry.sol @@ -18,9 +18,15 @@ interface IPRBProxyRegistry { /// @notice Thrown when an action requires the owner to not have a proxy. error PRBProxyRegistry_OwnerHasProxy(address owner, IPRBProxy proxy); - /// @notice Thrown when installing or uninstall a plugin, and the plugin doesn't implement any method. + /// @notice Thrown when trying to install or uninstall a plugin, and the plugin doesn't implement any method. error PRBProxyRegistry_PluginEmptyMethodList(IPRBProxyPlugin plugin); + /// @notice Thrown when trying to install a plugin that implements a method that is already implemented by another + /// installed plugin. + error PRBProxyRegistry_PluginMethodCollision( + IPRBProxyPlugin currentPlugin, IPRBProxyPlugin newPlugin, bytes4 method + ); + /*////////////////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////////////////*/ @@ -135,13 +141,13 @@ interface IPRBProxyRegistry { 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, bubbling up any potential revert. + /// target contract by forwarding the data. It returns the data it gets back, and bubbles up any potential revert. /// /// @dev Emits a {DeployProxy} and an {Execute} event. /// /// Requirements: /// - The caller must not have a proxy. - /// - All from {PRBProxy.execute}. + /// - `target` must be a contract. /// /// @param target The address of the target contract. /// @param data Function selector plus ABI encoded data. @@ -164,13 +170,14 @@ interface IPRBProxyRegistry { /// @dev Emits an {InstallPlugin} event. /// /// Notes: - /// - Does not revert if the plugin is installed. /// - 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. /// /// Requirements: /// - The caller must have a proxy. /// - The plugin must have at least one implemented method. - /// - By design, the plugin cannot implement any method that is also implemented by the proxy itself. + /// - There must be no method collision with any other installed plugin. /// /// @param plugin The address of the plugin to install. function installPlugin(IPRBProxyPlugin plugin) external; diff --git a/test/Base.t.sol b/test/Base.t.sol index 37d11f7..c170a4a 100644 --- a/test/Base.t.sol +++ b/test/Base.t.sol @@ -10,11 +10,13 @@ import { IPRBProxyRegistry } from "../src/interfaces/IPRBProxyRegistry.sol"; import { PRBProxy } from "../src/PRBProxy.sol"; import { PRBProxyRegistry } from "../src/PRBProxyRegistry.sol"; +import { PluginCollider } from "./mocks/plugins/PluginCollider.sol"; import { PluginDummy } from "./mocks/plugins/PluginDummy.sol"; import { PluginEcho } from "./mocks/plugins/PluginEcho.sol"; import { PluginEmpty } from "./mocks/plugins/PluginEmpty.sol"; import { PluginPanic } from "./mocks/plugins/PluginPanic.sol"; import { PluginReverter } from "./mocks/plugins/PluginReverter.sol"; +import { PluginSablier } from "./mocks/plugins/PluginSablier.sol"; import { PluginSelfDestructer } from "./mocks/plugins/PluginSelfDestructer.sol"; import { TargetDummy } from "./mocks/targets/TargetDummy.sol"; import { TargetDummyWithFallback } from "./mocks/targets/TargetDummyWithFallback.sol"; @@ -32,11 +34,13 @@ abstract contract Base_Test is Assertions, Events, StdCheats, StdUtils { //////////////////////////////////////////////////////////////////////////*/ struct Plugins { + PluginCollider collider; PluginDummy dummy; PluginEcho echo; PluginEmpty empty; PluginPanic panic; PluginReverter reverter; + PluginSablier sablier; PluginSelfDestructer selfDestructer; } @@ -95,11 +99,13 @@ abstract contract Base_Test is Assertions, Events, StdCheats, StdUtils { // Create the plugins. plugins = Plugins({ + collider: new PluginCollider(), dummy: new PluginDummy(), echo: new PluginEcho(), empty: new PluginEmpty(), panic: new PluginPanic(), reverter: new PluginReverter(), + sablier: new PluginSablier(), selfDestructer: new PluginSelfDestructer() }); diff --git a/test/mocks/plugins/PluginCollider.sol b/test/mocks/plugins/PluginCollider.sol new file mode 100644 index 0000000..4e83b6f --- /dev/null +++ b/test/mocks/plugins/PluginCollider.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.18; + +import { IPRBProxyPlugin } from "../../../src/interfaces/IPRBProxyPlugin.sol"; + +contract PluginCollider is IPRBProxyPlugin { + function methodList() external pure override returns (bytes4[] memory) { + bytes4[] memory methods = new bytes4[](1); + methods[0] = this.onAddictionFeesRefunded.selector; + return methods; + } + + /// @dev The selector for this method is 0x72eba203, which is the same as the selector for + /// `onStreamCanceled(uint256,address,uint128,uint128)` + function onAddictionFeesRefunded(uint248 loanId, int168, uint192 feeAmount, int248) external pure { + loanId; + feeAmount; + } +} diff --git a/test/mocks/plugins/PluginSablier.sol b/test/mocks/plugins/PluginSablier.sol new file mode 100644 index 0000000..1e423ff --- /dev/null +++ b/test/mocks/plugins/PluginSablier.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.18; + +import { IPRBProxyPlugin } from "../../../src/interfaces/IPRBProxyPlugin.sol"; + +contract PluginSablier is IPRBProxyPlugin { + function methodList() external pure override returns (bytes4[] memory) { + bytes4[] memory methods = new bytes4[](1); + methods[0] = this.onStreamCanceled.selector; + return methods; + } + + /// @dev The 4-byte selector for this method is 0x72eba203. + function onStreamCanceled(uint256, address, uint128, uint128) external pure { } +} diff --git a/test/registry/install-plugin/installPlugin.t.sol b/test/registry/install-plugin/installPlugin.t.sol index 1c926b6..21db6e0 100644 --- a/test/registry/install-plugin/installPlugin.t.sol +++ b/test/registry/install-plugin/installPlugin.t.sol @@ -31,32 +31,24 @@ contract InstallPlugin_Test is Registry_Test { _; } - function test_InstallPlugin_PluginInstalledBefore() - external - whenCallerHasProxy - whenPluginListNotEmpty - whenPluginNotInstalled - { - // Install a dummy plugin that has some methods. - registry.installPlugin(plugins.dummy); - - // Install the same plugin again. - registry.installPlugin(plugins.dummy); - - // Assert that every plugin method has been installed. - bytes4[] memory pluginMethods = plugins.dummy.methodList(); - for (uint256 i = 0; i < pluginMethods.length; ++i) { - IPRBProxyPlugin actualPlugin = registry.getPluginByOwner({ owner: users.alice, method: pluginMethods[i] }); - IPRBProxyPlugin expectedPlugin = plugins.dummy; - assertEq(actualPlugin, expectedPlugin, "plugin method not installed"); - } + function test_RevertWhen_MethodCollision() external whenCallerHasProxy whenPluginListNotEmpty { + registry.installPlugin(plugins.sablier); + vm.expectRevert( + abi.encodeWithSelector( + IPRBProxyRegistry.PRBProxyRegistry_PluginMethodCollision.selector, + plugins.sablier, + plugins.collider, + plugins.sablier.onStreamCanceled.selector + ) + ); + registry.installPlugin(plugins.collider); } - modifier whenPluginNotInstalled() { + modifier whenNoMethodCollision() { _; } - function test_InstallPlugin() external whenCallerHasProxy whenPluginListNotEmpty whenPluginNotInstalled { + function test_InstallPlugin() external whenCallerHasProxy whenPluginListNotEmpty whenNoMethodCollision { // Install a dummy plugin that has some methods. registry.installPlugin(plugins.dummy); @@ -69,7 +61,7 @@ contract InstallPlugin_Test is Registry_Test { } } - function test_InstallPlugin_Event() external whenCallerHasProxy whenPluginListNotEmpty whenPluginNotInstalled { + function test_InstallPlugin_Event() external whenCallerHasProxy whenPluginListNotEmpty whenNoMethodCollision { vm.expectEmit({ emitter: address(registry) }); emit InstallPlugin({ owner: users.alice, proxy: proxy, plugin: plugins.dummy }); registry.installPlugin(plugins.dummy); diff --git a/test/registry/install-plugin/installPlugin.tree b/test/registry/install-plugin/installPlugin.tree index 6d6aa7c..7b0c605 100644 --- a/test/registry/install-plugin/installPlugin.tree +++ b/test/registry/install-plugin/installPlugin.tree @@ -5,8 +5,8 @@ installPlugin.t.sol ├── when the plugin list is empty │ └── it should revert └── when the plugin list is not empty - ├── when the plugin has been installed before - │ └── it should do nothing - └── when the plugin has not been installed + ├── when there is a method collision + │ └── it should revert + └── when there is no method collision ├── it should install the plugin └── it should emit an {InstallPlugin} event diff --git a/test/registry/uninstall-plugin/uninstallPlugin.t.sol b/test/registry/uninstall-plugin/uninstallPlugin.t.sol index 49195bd..ba82bed 100644 --- a/test/registry/uninstall-plugin/uninstallPlugin.t.sol +++ b/test/registry/uninstall-plugin/uninstallPlugin.t.sol @@ -35,7 +35,7 @@ contract UninstallPlugin_Test is Registry_Test { // Uninstall the plugin. registry.uninstallPlugin(plugins.dummy); - // Assert that every plugin method has been uninstalled. + // Assert that every plugin method has remained uninstalled. bytes4[] memory pluginMethods = plugins.dummy.methodList(); for (uint256 i = 0; i < pluginMethods.length; ++i) { IPRBProxyPlugin actualPlugin = registry.getPluginByOwner({ owner: users.alice, method: pluginMethods[i] }); @@ -44,13 +44,13 @@ contract UninstallPlugin_Test is Registry_Test { } } - modifier whenPluginInstalled() { + modifier whenPluginInstalledBefore() { // Install the dummy plugin. registry.installPlugin(plugins.dummy); _; } - function test_UninstallPlugin() external whenCallerHasProxy whenPluginHasMethods whenPluginInstalled { + function test_UninstallPlugin() external whenCallerHasProxy whenPluginHasMethods whenPluginInstalledBefore { // Uninstall the plugin. registry.uninstallPlugin(plugins.dummy); @@ -63,7 +63,7 @@ contract UninstallPlugin_Test is Registry_Test { } } - function test_UninstallPlugin_Event() external whenCallerHasProxy whenPluginHasMethods whenPluginInstalled { + function test_UninstallPlugin_Event() external whenCallerHasProxy whenPluginHasMethods whenPluginInstalledBefore { vm.expectEmit({ emitter: address(registry) }); emit UninstallPlugin({ owner: users.alice, proxy: proxy, plugin: plugins.dummy }); registry.uninstallPlugin(plugins.dummy); diff --git a/test/registry/uninstall-plugin/uninstallPlugin.tree b/test/registry/uninstall-plugin/uninstallPlugin.tree index 2c9b5a4..0cb46bb 100644 --- a/test/registry/uninstall-plugin/uninstallPlugin.tree +++ b/test/registry/uninstall-plugin/uninstallPlugin.tree @@ -7,6 +7,6 @@ uninstallPlugin.t.sol └── when the plugin list is not empty ├── when the plugin has not been installed before │ └── it should do nothing - └── when the plugin has been installed + └── when the plugin has been installed before ├── it should uninstall the plugin └── it should emit an {UninstallPlugin} event diff --git a/test/utils/Precompiles.sol b/test/utils/Precompiles.sol index 75e8233..71e3f49 100644 --- a/test/utils/Precompiles.sol +++ b/test/utils/Precompiles.sol @@ -12,7 +12,7 @@ contract Precompiles { //////////////////////////////////////////////////////////////////////////*/ bytes public constant BYTECODE_REGISTRY = - hex"6080806040523461001657611ad2908161001c8239f35b600080fdfe60808060405260043610156200001457600080fd5b60003560e01c908163092af813146200095c575080631ddef5b914620008ad5780632c27a01f146200086f5780634bddd93a146200074f5780635cabcdf7146200067f57806361be4859146200062057806366b0182d146200052057806374912cd214620004ab578063775c300c1462000437578063aa4b826a1462000368578063b31d1b9914620002f3578063b7fba4d314620002b4578063b9678403146200014c5763ffa1ad7414620000c857600080fd5b34620001475760003660031901126200014757604051604081019080821067ffffffffffffffff83111762000131576200012d91604052600c81526b342e302e302d626574612e3560a01b602082015260405191829160208352602083019062000dd9565b0390f35b634e487b7160e01b600052604160045260246000fd5b600080fd5b34620001475760208060031936011262000147576001600160a01b036004358181169290839003620001475733600052600681528160406000205416156200029c5760405163ecdb286560e01b815260008160048183885af1908115620002905760009162000268575b5080519081156200024f5760005b8281106200020657505050600690336000525260406000205416337f9638c85f58d5866b4eb299b2e5f8f366372bd58e2d6e6e6eb19f3462948f3387600080a4005b6001903360005260058552604060002063ffffffff60e01b6200022a838662000f65565b511660005285526040600020876001600160601b0360a01b82541617905501620001c4565b60405163b74066bb60e01b815260048101869052602490fd5b6200028991503d806000833e62000280818362000db6565b81019062000ece565b84620001b6565b6040513d6000823e3d90fd5b60405163963e961b60e01b8152336004820152602490fd5b3462000147576020366003190112620001475760206001600160a01b0380620002dc62000d1c565b166000526006825260406000205416604051908152f35b346200014757606036600319011262000147576200031062000d1c565b6200031a62000d33565b906200032562000d4a565b9160018060a01b038092166000526004602052816040600020911660005260205260406000209116600052602052602060ff604060002054166040519015158152f35b346200014757606036600319011262000147576200038562000d1c565b6200038f62000d33565b604435908115158092036200014757336000526020916006835260018060a01b038060406000205416156200029c573360005260048452806040600020951694856000528452806040600020931692836000528452604060002060ff1981541660ff8416179055336000526006845260406000205416926040519283528201527f4f2bd80fb4928b06abcd76e3b26209a615f0612f98dc4a8b176934b1a389933360403392a4005b34620001475760003660031901126200014757336000908152600660205260409020546001600160a01b03908116806200048257602082620004793362000f90565b60405191168152f35b6040516356352f0b60e11b81523360048201526001600160a01b03919091166024820152604490fd5b34620001475760203660031901126200014757620004c862000d1c565b60018060a01b03908181166000526006602052816040600020541680620004f757602083620004798462000f90565b6040516356352f0b60e11b81526001600160a01b03928316600482015291166024820152604490fd5b3462000147576000366003190112620001475760018060a01b0380600054166001918254169160405190600090600254906200055c8262000d79565b80855291818116908115620005f75750600114620005a9575b5050906200058a816200012d93038262000db6565b6040519384938452602084015260606040840152606083019062000dd9565b60026000908152925060008051602062001ab28339815191525b828410620005de5750505081016020016200058a8262000575565b80546020858701810191909152909301928101620005c3565b60ff191660208087019190915292151560051b850190920192506200058a915083905062000575565b3462000147576040366003190112620001475760206200063f62000d1c565b6200064962000d61565b6001600160a01b0391821660009081526005845260408082206001600160e01b0319909316825291845281902054905191168152f35b346200014757606036600319011262000147576200069c62000d1c565b620006a662000d33565b90620006b162000d4a565b9060405192638da5cb5b60e01b8452602093848160048160018060a01b038097165afa8015620002905783916000916200071b575b5016600052600484528160406000209116600052835260406000209116600052815260ff604060002054166040519015158152f35b620007409150863d881162000747575b62000737818362000db6565b81019062000e1b565b86620006e6565b503d6200072b565b34620001475760208060031936011262000147576001600160a01b036004358181169290839003620001475733600052600681528160406000205416156200029c5760405163ecdb286560e01b815260008160048183885af1908115620002905760009162000850575b5080519081156200024f5760005b8281106200080957505050600690336000525260406000205416337f381d2d67cec77a56c13de6658effae4585e49ae536458929d53bfd4c3599e0ad600080a4005b6001903360005260058552604060002063ffffffff60e01b6200082d838662000f65565b5116600052855260406000206001600160601b0360a01b815416905501620007c7565b6200086891503d806000833e62000280818362000db6565b84620007b9565b346200014757602036600319011262000147576001600160a01b036200089462000d1c565b1660005260036020526020604060002054604051908152f35b34620001475760403660031901126200014757620008ca62000d1c565b620008d462000d61565b604051638da5cb5b60e01b81526020926001600160a01b0392919084908290600490829087165afa8015620002905783916000916200093a575b50166000526005835260406000209063ffffffff60e01b16600052825260406000205416604051908152f35b620009559150853d8711620007475762000737818362000db6565b856200090e565b346200014757604036600319011262000147576200097962000d1c565b9067ffffffffffffffff602435116200014757366023602435011215620001475767ffffffffffffffff6024356004013511620001475736602480356004013581350101116200014757336000908152600660205260409020546001600160a01b03168062000cf9573260008181526003602090815260409182902054825191820193845291810182905285929062000a2081606081015b03601f19810183528262000db6565b519020604051926060840184811067ffffffffffffffff821117620001315760405233845260018060a01b031660208401526040519262000a726020601f19601f602435600401350116018562000db6565b6024803560048101358087529101602086013760006020602435600401358601015283604082015260018060a01b038151166001600160601b0360a01b6000541617600055602060018060a01b03910151166001600160601b0360a01b6001541617600155825167ffffffffffffffff8111620001315762000af660025462000d79565b601f811162000c84575b506020601f821160011462000c0c578192939460009262000c00575b50508160011b916000199060031b1c1916176002555b6040516109f1908181019181831067ffffffffffffffff841117620001315783928291620010c1833903906000f591821562000290576020926001600160a01b03169162000b7f62000e3c565b33600081815260068652604080822080546001600160a01b03191687179055328083526003885291819020600186019055805194855260208501939093526001600160a01b03851692840192909252918291907f6aafca263a35a9d2a6e4e4659a84688092f4ae153df2f95cd7659508d95c187090606090a4604051908152f35b01519050848062000b1c565b601f19821690600260005260008051602062001ab28339815191529160005b81811062000c6b5750958360019596971062000c51575b505050811b0160025562000b32565b015160001960f88460031b161c1916905584808062000c42565b9192602060018192868b01518155019401920162000c2b565b6002600052601f820160051c60008051602062001ab283398151915201906020831062000ce1575b601f0160051c60008051602062001ab283398151915201905b81811062000cd4575062000b00565b6000815560010162000cc5565b60008051602062001ab2833981519152915062000cac565b6356352f0b60e11b82523360048301526001600160a01b03166024820152604490fd5b600435906001600160a01b03821682036200014757565b602435906001600160a01b03821682036200014757565b604435906001600160a01b03821682036200014757565b602435906001600160e01b0319821682036200014757565b90600182811c9216801562000dab575b602083101462000d9557565b634e487b7160e01b600052602260045260246000fd5b91607f169162000d89565b90601f8019910116810190811067ffffffffffffffff8211176200013157604052565b919082519283825260005b84811062000e06575050826000602080949584010152601f8019910116010190565b60208183018101518483018201520162000de4565b908160209103126200014757516001600160a01b0381168103620001475790565b6000808055600181815562000e5360025462000d79565b908162000e5f57505050565b601f821160011462000e72575050600255565b60028352601f60008051602062001ab2833981519152920160051c82017f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5acf5b81811062000ec3575050508160025555565b848155820162000eb1565b9060209081838203126200014757825167ffffffffffffffff9384821162000147570181601f820112156200014757805193841162000131578360051b906040519462000f1e8584018762000db6565b8552838086019282010192831162000147578301905b82821062000f43575050505090565b81516001600160e01b0319811681036200014757815290830190830162000f34565b805182101562000f7a5760209160051b010190565b634e487b7160e01b600052603260045260246000fd5b326000818152600360209081526040808320548151928301948552828201819052949594929390929162000fc8816060810162000a11565b5190209160018060a01b03809616936001600160601b0360a01b96858883541617825582516109f18082019082821067ffffffffffffffff831117620010ac579180918893620010c18339039084f58015620010a25716967f6aafca263a35a9d2a6e4e4659a84688092f4ae153df2f95cd7659508d95c18709291906200104e62000e3c565b868252600660205288838320918254161790553281526003602052816001850191205551806200109d8833963296849192604091949360608401958452602084015260018060a01b0316910152565b0390a4565b83513d84823e3d90fd5b634e487b7160e01b85526041600452602485fdfe60c08060405234620000ca573360a0526366b0182d60e01b8152600081600481335afa8015620000c4576000809281926200009b575b506080526001600160a01b03821662000087575b60405161068e9081620003638239608051818181610192015281816102c401526104a5015260a05181818161014d0152818161031301526105250152f35b6200009291620002b2565b50388062000049565b909150620000bb92503d8092823e620000b482620000e5565b016200018e565b90913862000035565b62000221565b600080fd5b634e487b7160e01b600052604160045260246000fd5b60c0601f91909101601f19168101906001600160401b038211908210176200010c57604052565b620000cf565b601f909101601f19168101906001600160401b038211908210176200010c57604052565b60e051906001600160a01b0382168203620000ca57565b6001600160401b0381116200010c57601f01601f191660200190565b60005b8381106200017d5750506000910152565b81810151838201526020016200016c565b606060bf19820112620000ca5760c0516001600160a01b0381168103620000ca5791620001ba62000136565b610100519092906001600160401b038111620000ca578160df82011215620000ca578060c00151620001ec816200014d565b92620001fc604051948562000112565b81845260e08284010111620000ca576200021e9160e060208501910162000169565b90565b6040513d6000823e3d90fd5b3d156200025d573d9062000241826200014d565b9162000251604051938462000112565b82523d6000602084013e565b606090565b906020916200027d8151809281855285808601910162000169565b601f01601f1916010190565b9091620002a36200021e9360408452604084019062000262565b91602081840391015262000262565b9190823b156200034157600080825160208401865af4907fb24ebe141c5f2a744b103bea65fce6c40e0dc65d7341d092c09b160f40447990620002f46200022d565b60405190956001600160a01b0316928190620003139088908362000289565b0390a2156200031e57565b508051156200032f57602081519101fd5b60405163061a160d60e41b8152600490fd5b604051636d17e5ef60e11b81526001600160a01b0384166004820152602490fdfe60806040526004361015610027575b36156100255761001d366102b5565b602081519101f35b005b6000803560e01c9081631cff79cd1461005a575080637b1039991461005557638da5cb5b0361000e5761017c565b610137565b60403660031901126100ca57600435610072816100cd565b60243567ffffffffffffffff928382116100ca57366023830112156100ca5781600401359384116100ca5736602485840101116100ca576100c66100ba8560248501866104a2565b60405191829182610123565b0390f35b80fd5b6001600160a01b038116036100de57565b600080fd5b919082519283825260005b84811061010f575050826000602080949584010152601f8019910116010190565b6020818301810151848301820152016100ee565b9060206101349281815201906100e3565b90565b346100de5760003660031901126100de576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b346100de5760003660031901126100de576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b634e487b7160e01b600052604160045260246000fd5b90601f8019910116810190811067ffffffffffffffff8211176101f957604052565b6101c1565b908160209103126100de5751610134816100cd565b6040513d6000823e3d90fd5b908160008237016000815290565b67ffffffffffffffff81116101f957601f01601f191660200190565b3d15610274573d9061025a8261022d565b9161026860405193846101d7565b82523d6000602084013e565b606090565b606090610134939260408252806040830152806000848401376000838284010152601f80199101168101906020838284030191015201906100e3565b6040516361be485960e01b81527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0381811660048401526001600160e01b03196000351660248401819052939493906020846044817f000000000000000000000000000000000000000000000000000000000000000085165afa93841561044e5760009461041e575b5083169182156103e357505060008060405180610362818961021f565b0390845af4907fc4dabe0d7ef7462e2218f2c398c21ef217803e1c46f5cf802d1a5d1d9b503f2f610391610249565b80966103a260405192839283610279565b0390a2156103ad5750565b8251909150156103c05750805190602001fd5b60405163023c045d60e21b81526001600160a01b03919091166004820152602490fd5b604051638848730f60e01b81523360048201526001600160a01b039190911660248201526001600160e01b0319919091166044820152606490fd5b61044091945060203d8111610447575b61043881836101d7565b8101906101fe565b9238610345565b503d61042e565b610213565b908160209103126100de575180151581036100de5790565b9291926104778261022d565b9161048560405193846101d7565b8294818452818301116100de578281602093846000960137010152565b917f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0333818316036104ee575b505061013492916104e891369161046b565b906105e5565b60405163b31d1b9960e01b81526001600160a01b0383811660048301523360248301528616604482015290602090829060649082907f0000000000000000000000000000000000000000000000000000000000000000165afa90811561044e57600091610592575b501561056257806104d6565b6040516355d1750960e01b81526001600160a01b0391821660048201523360248201529084166044820152606490fd5b6105b3915060203d81116105b9575b6105ab81836101d7565b810190610453565b38610556565b503d6105a1565b90916105d7610134936040845260408401906100e3565b9160208184039101526100e3565b9190823b1561066d57600080825160208401865af4907fb24ebe141c5f2a744b103bea65fce6c40e0dc65d7341d092c09b160f40447990610624610249565b60405190956001600160a01b0316928190610641908890836105c0565b0390a21561064b57565b5080511561065b57602081519101fd5b60405163061a160d60e41b8152600490fd5b604051636d17e5ef60e11b81526001600160a01b0384166004820152602490fd405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace"; + hex"6080806040523461001657611b2e908161001c8239f35b600080fdfe60808060405260043610156200001457600080fd5b60003560e01c908163092af81314620009b8575080631ddef5b914620009095780632c27a01f14620008cb5780634bddd93a14620007ab5780635cabcdf714620006db57806361be4859146200067c57806366b0182d146200057c57806374912cd21462000507578063775c300c1462000493578063aa4b826a14620003c4578063b31d1b99146200034f578063b7fba4d31462000310578063b9678403146200014c5763ffa1ad7414620000c857600080fd5b34620001475760003660031901126200014757604051604081019080821067ffffffffffffffff83111762000131576200012d91604052600c81526b342e302e302d626574612e3560a01b602082015260405191829160208352602083019062000e35565b0390f35b634e487b7160e01b600052604160045260246000fd5b600080fd5b34620001475760208060031936011262000147576001600160a01b03600435818116929083900362000147573360005260068152816040600020541615620002f85760405163ecdb286560e01b815260008160048183885af1908115620002ec57600091620002c4575b508051908115620002ab5760005b8281106200020657505050600690336000525260406000205416337f9638c85f58d5866b4eb299b2e5f8f366372bd58e2d6e6e6eb19f3462948f3387600080a4005b6001600160e01b03196200021b828462000fc1565b5116336000526005808652604060002082600052865286604060002054166200026c5733600090815290865260408082209282529186522080546001600160a01b03191687179055600101620001c4565b60649187878a9333600052815260406000209083600052526040600020541691604051926361a2116b60e11b8452600484015260248301526044820152fd5b60405163b74066bb60e01b815260048101869052602490fd5b620002e591503d806000833e620002dc818362000e12565b81019062000f2a565b84620001b6565b6040513d6000823e3d90fd5b60405163963e961b60e01b8152336004820152602490fd5b3462000147576020366003190112620001475760206001600160a01b03806200033862000d78565b166000526006825260406000205416604051908152f35b346200014757606036600319011262000147576200036c62000d78565b6200037662000d8f565b906200038162000da6565b9160018060a01b038092166000526004602052816040600020911660005260205260406000209116600052602052602060ff604060002054166040519015158152f35b34620001475760603660031901126200014757620003e162000d78565b620003eb62000d8f565b604435908115158092036200014757336000526020916006835260018060a01b03806040600020541615620002f8573360005260048452806040600020951694856000528452806040600020931692836000528452604060002060ff1981541660ff8416179055336000526006845260406000205416926040519283528201527f4f2bd80fb4928b06abcd76e3b26209a615f0612f98dc4a8b176934b1a389933360403392a4005b34620001475760003660031901126200014757336000908152600660205260409020546001600160a01b0390811680620004de57602082620004d53362000fec565b60405191168152f35b6040516356352f0b60e11b81523360048201526001600160a01b03919091166024820152604490fd5b346200014757602036600319011262000147576200052462000d78565b60018060a01b039081811660005260066020528160406000205416806200055357602083620004d58462000fec565b6040516356352f0b60e11b81526001600160a01b03928316600482015291166024820152604490fd5b3462000147576000366003190112620001475760018060a01b038060005416600191825416916040519060009060025490620005b88262000dd5565b8085529181811690811562000653575060011462000605575b505090620005e6816200012d93038262000e12565b6040519384938452602084015260606040840152606083019062000e35565b60026000908152925060008051602062001b0e8339815191525b8284106200063a575050508101602001620005e682620005d1565b805460208587018101919091529093019281016200061f565b60ff191660208087019190915292151560051b85019092019250620005e69150839050620005d1565b3462000147576040366003190112620001475760206200069b62000d78565b620006a562000dbd565b6001600160a01b0391821660009081526005845260408082206001600160e01b0319909316825291845281902054905191168152f35b34620001475760603660031901126200014757620006f862000d78565b6200070262000d8f565b906200070d62000da6565b9060405192638da5cb5b60e01b8452602093848160048160018060a01b038097165afa8015620002ec57839160009162000777575b5016600052600484528160406000209116600052835260406000209116600052815260ff604060002054166040519015158152f35b6200079c9150863d8811620007a3575b62000793818362000e12565b81019062000e77565b8662000742565b503d62000787565b34620001475760208060031936011262000147576001600160a01b03600435818116929083900362000147573360005260068152816040600020541615620002f85760405163ecdb286560e01b815260008160048183885af1908115620002ec57600091620008ac575b508051908115620002ab5760005b8281106200086557505050600690336000525260406000205416337f381d2d67cec77a56c13de6658effae4585e49ae536458929d53bfd4c3599e0ad600080a4005b6001903360005260058552604060002063ffffffff60e01b62000889838662000fc1565b5116600052855260406000206001600160601b0360a01b81541690550162000823565b620008c491503d806000833e620002dc818362000e12565b8462000815565b346200014757602036600319011262000147576001600160a01b03620008f062000d78565b1660005260036020526020604060002054604051908152f35b346200014757604036600319011262000147576200092662000d78565b6200093062000dbd565b604051638da5cb5b60e01b81526020926001600160a01b0392919084908290600490829087165afa8015620002ec57839160009162000996575b50166000526005835260406000209063ffffffff60e01b16600052825260406000205416604051908152f35b620009b19150853d8711620007a35762000793818362000e12565b856200096a565b34620001475760403660031901126200014757620009d562000d78565b9067ffffffffffffffff602435116200014757366023602435011215620001475767ffffffffffffffff6024356004013511620001475736602480356004013581350101116200014757336000908152600660205260409020546001600160a01b03168062000d55573260008181526003602090815260409182902054825191820193845291810182905285929062000a7c81606081015b03601f19810183528262000e12565b519020604051926060840184811067ffffffffffffffff821117620001315760405233845260018060a01b031660208401526040519262000ace6020601f19601f602435600401350116018562000e12565b6024803560048101358087529101602086013760006020602435600401358601015283604082015260018060a01b038151166001600160601b0360a01b6000541617600055602060018060a01b03910151166001600160601b0360a01b6001541617600155825167ffffffffffffffff8111620001315762000b5260025462000dd5565b601f811162000ce0575b506020601f821160011462000c68578192939460009262000c5c575b50508160011b916000199060031b1c1916176002555b6040516109f1908181019181831067ffffffffffffffff8411176200013157839282916200111d833903906000f5918215620002ec576020926001600160a01b03169162000bdb62000e98565b33600081815260068652604080822080546001600160a01b03191687179055328083526003885291819020600186019055805194855260208501939093526001600160a01b03851692840192909252918291907f6aafca263a35a9d2a6e4e4659a84688092f4ae153df2f95cd7659508d95c187090606090a4604051908152f35b01519050848062000b78565b601f19821690600260005260008051602062001b0e8339815191529160005b81811062000cc75750958360019596971062000cad575b505050811b0160025562000b8e565b015160001960f88460031b161c1916905584808062000c9e565b9192602060018192868b01518155019401920162000c87565b6002600052601f820160051c60008051602062001b0e83398151915201906020831062000d3d575b601f0160051c60008051602062001b0e83398151915201905b81811062000d30575062000b5c565b6000815560010162000d21565b60008051602062001b0e833981519152915062000d08565b6356352f0b60e11b82523360048301526001600160a01b03166024820152604490fd5b600435906001600160a01b03821682036200014757565b602435906001600160a01b03821682036200014757565b604435906001600160a01b03821682036200014757565b602435906001600160e01b0319821682036200014757565b90600182811c9216801562000e07575b602083101462000df157565b634e487b7160e01b600052602260045260246000fd5b91607f169162000de5565b90601f8019910116810190811067ffffffffffffffff8211176200013157604052565b919082519283825260005b84811062000e62575050826000602080949584010152601f8019910116010190565b60208183018101518483018201520162000e40565b908160209103126200014757516001600160a01b0381168103620001475790565b6000808055600181815562000eaf60025462000dd5565b908162000ebb57505050565b601f821160011462000ece575050600255565b60028352601f60008051602062001b0e833981519152920160051c82017f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5acf5b81811062000f1f575050508160025555565b848155820162000f0d565b9060209081838203126200014757825167ffffffffffffffff9384821162000147570181601f820112156200014757805193841162000131578360051b906040519462000f7a8584018762000e12565b8552838086019282010192831162000147578301905b82821062000f9f575050505090565b81516001600160e01b0319811681036200014757815290830190830162000f90565b805182101562000fd65760209160051b010190565b634e487b7160e01b600052603260045260246000fd5b326000818152600360209081526040808320548151928301948552828201819052949594929390929162001024816060810162000a6d565b5190209160018060a01b03809616936001600160601b0360a01b96858883541617825582516109f18082019082821067ffffffffffffffff831117620011085791809188936200111d8339039084f58015620010fe5716967f6aafca263a35a9d2a6e4e4659a84688092f4ae153df2f95cd7659508d95c1870929190620010aa62000e98565b86825260066020528883832091825416179055328152600360205281600185019120555180620010f98833963296849192604091949360608401958452602084015260018060a01b0316910152565b0390a4565b83513d84823e3d90fd5b634e487b7160e01b85526041600452602485fdfe60c08060405234620000ca573360a0526366b0182d60e01b8152600081600481335afa8015620000c4576000809281926200009b575b506080526001600160a01b03821662000087575b60405161068e9081620003638239608051818181610192015281816102c401526104a5015260a05181818161014d0152818161031301526105250152f35b6200009291620002b2565b50388062000049565b909150620000bb92503d8092823e620000b482620000e5565b016200018e565b90913862000035565b62000221565b600080fd5b634e487b7160e01b600052604160045260246000fd5b60c0601f91909101601f19168101906001600160401b038211908210176200010c57604052565b620000cf565b601f909101601f19168101906001600160401b038211908210176200010c57604052565b60e051906001600160a01b0382168203620000ca57565b6001600160401b0381116200010c57601f01601f191660200190565b60005b8381106200017d5750506000910152565b81810151838201526020016200016c565b606060bf19820112620000ca5760c0516001600160a01b0381168103620000ca5791620001ba62000136565b610100519092906001600160401b038111620000ca578160df82011215620000ca578060c00151620001ec816200014d565b92620001fc604051948562000112565b81845260e08284010111620000ca576200021e9160e060208501910162000169565b90565b6040513d6000823e3d90fd5b3d156200025d573d9062000241826200014d565b9162000251604051938462000112565b82523d6000602084013e565b606090565b906020916200027d8151809281855285808601910162000169565b601f01601f1916010190565b9091620002a36200021e9360408452604084019062000262565b91602081840391015262000262565b9190823b156200034157600080825160208401865af4907fb24ebe141c5f2a744b103bea65fce6c40e0dc65d7341d092c09b160f40447990620002f46200022d565b60405190956001600160a01b0316928190620003139088908362000289565b0390a2156200031e57565b508051156200032f57602081519101fd5b60405163061a160d60e41b8152600490fd5b604051636d17e5ef60e11b81526001600160a01b0384166004820152602490fdfe60806040526004361015610027575b36156100255761001d366102b5565b602081519101f35b005b6000803560e01c9081631cff79cd1461005a575080637b1039991461005557638da5cb5b0361000e5761017c565b610137565b60403660031901126100ca57600435610072816100cd565b60243567ffffffffffffffff928382116100ca57366023830112156100ca5781600401359384116100ca5736602485840101116100ca576100c66100ba8560248501866104a2565b60405191829182610123565b0390f35b80fd5b6001600160a01b038116036100de57565b600080fd5b919082519283825260005b84811061010f575050826000602080949584010152601f8019910116010190565b6020818301810151848301820152016100ee565b9060206101349281815201906100e3565b90565b346100de5760003660031901126100de576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b346100de5760003660031901126100de576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b634e487b7160e01b600052604160045260246000fd5b90601f8019910116810190811067ffffffffffffffff8211176101f957604052565b6101c1565b908160209103126100de5751610134816100cd565b6040513d6000823e3d90fd5b908160008237016000815290565b67ffffffffffffffff81116101f957601f01601f191660200190565b3d15610274573d9061025a8261022d565b9161026860405193846101d7565b82523d6000602084013e565b606090565b606090610134939260408252806040830152806000848401376000838284010152601f80199101168101906020838284030191015201906100e3565b6040516361be485960e01b81527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0381811660048401526001600160e01b03196000351660248401819052939493906020846044817f000000000000000000000000000000000000000000000000000000000000000085165afa93841561044e5760009461041e575b5083169182156103e357505060008060405180610362818961021f565b0390845af4907fc4dabe0d7ef7462e2218f2c398c21ef217803e1c46f5cf802d1a5d1d9b503f2f610391610249565b80966103a260405192839283610279565b0390a2156103ad5750565b8251909150156103c05750805190602001fd5b60405163023c045d60e21b81526001600160a01b03919091166004820152602490fd5b604051638848730f60e01b81523360048201526001600160a01b039190911660248201526001600160e01b0319919091166044820152606490fd5b61044091945060203d8111610447575b61043881836101d7565b8101906101fe565b9238610345565b503d61042e565b610213565b908160209103126100de575180151581036100de5790565b9291926104778261022d565b9161048560405193846101d7565b8294818452818301116100de578281602093846000960137010152565b917f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0333818316036104ee575b505061013492916104e891369161046b565b906105e5565b60405163b31d1b9960e01b81526001600160a01b0383811660048301523360248301528616604482015290602090829060649082907f0000000000000000000000000000000000000000000000000000000000000000165afa90811561044e57600091610592575b501561056257806104d6565b6040516355d1750960e01b81526001600160a01b0391821660048201523360248201529084166044820152606490fd5b6105b3915060203d81116105b9575b6105ab81836101d7565b810190610453565b38610556565b503d6105a1565b90916105d7610134936040845260408401906100e3565b9160208184039101526100e3565b9190823b1561066d57600080825160208401865af4907fb24ebe141c5f2a744b103bea65fce6c40e0dc65d7341d092c09b160f40447990610624610249565b60405190956001600160a01b0316928190610641908890836105c0565b0390a21561064b57565b5080511561065b57602081519101fd5b60405163061a160d60e41b8152600490fd5b604051636d17e5ef60e11b81526001600160a01b0384166004820152602490fd405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace"; /*////////////////////////////////////////////////////////////////////////// DEPLOYERS