From 4d7f7650fe34aaecb963e8903b8cc89e1f6687c0 Mon Sep 17 00:00:00 2001 From: Paul Razvan Berg Date: Thu, 22 Jun 2023 18:31:48 +0300 Subject: [PATCH 1/2] refactor: redesign plugins docs: polish NatSpe comments feat: add getters for retrieving plugin methods feat: store plugin methods in reverse mapping refactor: rename "methodList" to "methods" refactor: update error names test: rename "dummy" to "basic" test: update tests for "installPlugin" and "uninstallPlugin" --- src/PRBProxyRegistry.sol | 53 ++++++--- src/interfaces/IPRBProxyPlugin.sol | 10 +- src/interfaces/IPRBProxyRegistry.sol | 104 +++++++++++------- test/Base.t.sol | 15 +-- .../{PluginDummy.sol => PluginBasic.sol} | 6 +- test/mocks/plugins/PluginCollider.sol | 2 +- test/mocks/plugins/PluginEcho.sol | 2 +- test/mocks/plugins/PluginEmpty.sol | 2 +- test/mocks/plugins/PluginPanic.sol | 2 +- test/mocks/plugins/PluginReverter.sol | 2 +- test/mocks/plugins/PluginSablier.sol | 2 +- test/mocks/plugins/PluginSelfDestructer.sol | 2 +- .../{TargetDummy.sol => TargetBasic.sol} | 2 +- .../mocks/targets/TargetDummyWithFallback.sol | 20 ---- test/proxy/execute/execute.t.sol | 12 +- test/proxy/run-plugin/runPlugin.t.sol | 20 ++-- .../getMethodsByOwner.t.sol | 28 +++++ .../getMethodsByOwner.tree | 5 + .../getMethodsByProxy.t.sol | 28 +++++ .../getMethodsByProxy.tree | 5 + .../getPermissionByOwner.t.sol | 6 +- .../getPermissionByProxy.t.sol | 6 +- .../getPluginByOwner.t.sol | 8 +- .../getPluginByProxy.t.sol | 8 +- .../install-plugin/installPlugin.t.sol | 48 +++++--- .../install-plugin/installPlugin.tree | 9 +- .../set-permission/setPermission.t.sol | 22 ++-- .../uninstall-plugin/uninstallPlugin.t.sol | 48 ++++---- .../uninstall-plugin/uninstallPlugin.tree | 16 ++- test/utils/Assertions.sol | 22 +++- test/utils/Events.sol | 8 +- test/utils/Precompiles.sol | 2 +- 32 files changed, 327 insertions(+), 198 deletions(-) rename test/mocks/plugins/{PluginDummy.sol => PluginBasic.sol} (64%) rename test/mocks/targets/{TargetDummy.sol => TargetBasic.sol} (91%) delete mode 100644 test/mocks/targets/TargetDummyWithFallback.sol create mode 100644 test/registry/get-methods-by-owner/getMethodsByOwner.t.sol create mode 100644 test/registry/get-methods-by-owner/getMethodsByOwner.tree create mode 100644 test/registry/get-methods-by-proxy/getMethodsByProxy.t.sol create mode 100644 test/registry/get-methods-by-proxy/getMethodsByProxy.tree diff --git a/src/PRBProxyRegistry.sol b/src/PRBProxyRegistry.sol index 9f8831d..9b11c46 100644 --- a/src/PRBProxyRegistry.sol +++ b/src/PRBProxyRegistry.sol @@ -45,6 +45,8 @@ contract PRBProxyRegistry is IPRBProxyRegistry { INTERNAL STORAGE //////////////////////////////////////////////////////////////////////////*/ + mapping(address owner => mapping(IPRBProxyPlugin plugin => bytes4[] methods)) internal _methods; + mapping(address owner => mapping(address envoy => mapping(address target => bool permission))) internal _permissions; mapping(address owner => mapping(bytes4 method => IPRBProxyPlugin plugin)) internal _plugins; @@ -76,6 +78,23 @@ contract PRBProxyRegistry is IPRBProxyRegistry { USER-FACING CONSTANT FUNCTIONS //////////////////////////////////////////////////////////////////////////*/ + /// @inheritdoc IPRBProxyRegistry + function getMethodsByOwner(address owner, IPRBProxyPlugin plugin) external view returns (bytes4[] memory methods) { + methods = _methods[owner][plugin]; + } + + /// @inheritdoc IPRBProxyRegistry + function getMethodsByProxy( + IPRBProxy proxy, + IPRBProxyPlugin plugin + ) + external + view + returns (bytes4[] memory methods) + { + methods = _methods[proxy.owner()][plugin]; + } + /// @inheritdoc IPRBProxyRegistry function getPermissionByOwner( address owner, @@ -159,20 +178,20 @@ contract PRBProxyRegistry is IPRBProxyRegistry { /// @inheritdoc IPRBProxyRegistry function installPlugin(IPRBProxyPlugin plugin) external override onlyCallerWithProxy { - // Get the method list to install. - bytes4[] memory methodList = plugin.methodList(); + // Retrieve the methods to install. + bytes4[] memory methods = plugin.getMethods(); // The plugin must have at least one listed method. - uint256 length = methodList.length; + uint256 length = methods.length; if (length == 0) { - revert PRBProxyRegistry_PluginEmptyMethodList(plugin); + revert PRBProxyRegistry_PluginWithZeroMethods(plugin); } // Install every method in the list. address owner = msg.sender; for (uint256 i = 0; i < length;) { // Check for collisions. - bytes4 method = methodList[i]; + bytes4 method = methods[i]; if (address(_plugins[owner][method]) != address(0)) { revert PRBProxyRegistry_PluginMethodCollision({ currentPlugin: _plugins[owner][method], @@ -186,8 +205,11 @@ contract PRBProxyRegistry is IPRBProxyRegistry { } } + // Set the methods in the reverse mapping. + _methods[owner][plugin] = methods; + // Log the plugin installation. - emit InstallPlugin(owner, _proxies[owner], plugin); + emit InstallPlugin(owner, _proxies[owner], plugin, methods); } /// @inheritdoc IPRBProxyRegistry @@ -199,26 +221,29 @@ contract PRBProxyRegistry is IPRBProxyRegistry { /// @inheritdoc IPRBProxyRegistry function uninstallPlugin(IPRBProxyPlugin plugin) external override onlyCallerWithProxy { - // Get the method list to uninstall. - bytes4[] memory methodList = plugin.methodList(); + // Retrieve the methods originally installed by this plugin. + address owner = msg.sender; + bytes4[] memory methods = _methods[owner][plugin]; - // The plugin must have at least one listed method. - uint256 length = methodList.length; + // The plugin must be a known, previously installed plugin. + uint256 length = methods.length; if (length == 0) { - revert PRBProxyRegistry_PluginEmptyMethodList(plugin); + revert PRBProxyRegistry_PluginUnknown(plugin); } // Uninstall every method in the list. - address owner = msg.sender; for (uint256 i = 0; i < length;) { - delete _plugins[owner][methodList[i]]; + delete _plugins[owner][methods[i]]; unchecked { i += 1; } } + // Remove the methods from the reverse mapping. + delete _methods[owner][plugin]; + // Log the plugin uninstallation. - emit UninstallPlugin(owner, _proxies[owner], plugin); + emit UninstallPlugin(owner, _proxies[owner], plugin, methods); } /*////////////////////////////////////////////////////////////////////////// diff --git a/src/interfaces/IPRBProxyPlugin.sol b/src/interfaces/IPRBProxyPlugin.sol index 7169d69..fdafa48 100644 --- a/src/interfaces/IPRBProxyPlugin.sol +++ b/src/interfaces/IPRBProxyPlugin.sol @@ -4,16 +4,16 @@ pragma solidity >=0.8.4; /// @title IPRBProxyPlugin /// @notice Interface for plugin contracts that can be installed on a proxy. /// @dev Plugins are contracts that enable the proxy to interact with and respond to calls from other contracts. These -/// plugins are run in the proxy's fallback function. +/// plugins are run via the proxy's fallback function. /// /// This interface is meant to be directly inherited by plugin implementations. interface IPRBProxyPlugin { - /// @notice Enumerates the methods implemented by the plugin. - /// @dev These methods can be installed and uninstalled. + /// @notice Retrieves the methods implemented by the plugin. + /// @dev The registry pulls these methods when installing the plugin. /// /// Requirements: /// - The plugin must implement at least one method. /// - /// @return methods An array of the methods implemented by the plugin. - function methodList() external returns (bytes4[] memory methods); + /// @return methods The array of the methods implemented by the plugin. + function getMethods() external returns (bytes4[] memory methods); } diff --git a/src/interfaces/IPRBProxyRegistry.sol b/src/interfaces/IPRBProxyRegistry.sol index a293ea7..2de0dd1 100644 --- a/src/interfaces/IPRBProxyRegistry.sol +++ b/src/interfaces/IPRBProxyRegistry.sol @@ -18,15 +18,18 @@ 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 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 + /// @notice Thrown when trying to install a plugin that implements a method already implemented by another /// installed plugin. error PRBProxyRegistry_PluginMethodCollision( IPRBProxyPlugin currentPlugin, IPRBProxyPlugin newPlugin, bytes4 method ); + /// @notice Thrown when trying to uninstall an unknown plugin. + error PRBProxyRegistry_PluginUnknown(IPRBProxyPlugin plugin); + + /// @notice Thrown when trying to install a plugin that doesn't implement any method. + error PRBProxyRegistry_PluginWithZeroMethods(IPRBProxyPlugin plugin); + /*////////////////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////////////////*/ @@ -35,7 +38,9 @@ interface IPRBProxyRegistry { 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); + event InstallPlugin( + address indexed owner, IPRBProxy indexed proxy, IPRBProxyPlugin indexed plugin, bytes4[] methods + ); /// @notice Emitted when an envoy permission is updated. event SetPermission( @@ -43,15 +48,17 @@ interface IPRBProxyRegistry { ); /// @notice Emitted when a plugin is uninstalled. - event UninstallPlugin(address indexed owner, IPRBProxy indexed proxy, IPRBProxyPlugin indexed plugin); + event UninstallPlugin( + address indexed owner, IPRBProxy indexed proxy, IPRBProxyPlugin indexed plugin, bytes4[] methods + ); /*////////////////////////////////////////////////////////////////////////// STRUCTS //////////////////////////////////////////////////////////////////////////*/ /// @param owner The address of the user who will own the proxy. - /// @param target The address of the target contract to delegate call to. Can be set to zero. - /// @param data The address of the call data to pass to the target contract. Can be set to zero. + /// @param target The address of the target to delegate call to. Can be set to zero. + /// @param data The address of the call data to pass to the target. Can be set to zero. struct ConstructorParams { address owner; address target; @@ -71,11 +78,29 @@ interface IPRBProxyRegistry { /// @dev The proxy constructor fetches these parameters. function constructorParams() external view returns (address owner, address target, bytes memory data); + /// @notice Retrieves the list of installed methods for the provided plugin. + /// @dev An empty array is returned if the plugin is unknown. + /// @param owner The proxy owner for the query. + /// @param plugin The plugin for the query. + function getMethodsByOwner(address owner, IPRBProxyPlugin plugin) external view returns (bytes4[] memory methods); + + /// @notice Retrieves the list of installed methods for the provided plugin. + /// @dev An empty array is returned if the plugin is unknown. + /// @param proxy The proxy for the query. + /// @param plugin The plugin for the query. + function getMethodsByProxy( + IPRBProxy proxy, + IPRBProxyPlugin plugin + ) + external + view + returns (bytes4[] memory methods); + /// @notice Retrieves a boolean flag that indicates whether the provided envoy has permission to call the provided - /// target contract. - /// @param owner The proxy owner to make the query for. - /// @param envoy The address with permission to call the target contract. - /// @param target The address of the target contract. + /// target. + /// @param owner The proxy owner for the query. + /// @param envoy The address with permission to call the target. + /// @param target The address of the target. function getPermissionByOwner( address owner, address envoy, @@ -86,10 +111,10 @@ interface IPRBProxyRegistry { returns (bool permission); /// @notice Retrieves a boolean flag that indicates whether the provided envoy has permission to call the provided - /// target contract. - /// @param proxy The proxy contract to make the query for. - /// @param envoy The address with permission to call the target contract. - /// @param target The address of the target contract. + /// target. + /// @param proxy The proxy for the query. + /// @param envoy The address with permission to call the target. + /// @param target The address of the target. function getPermissionByProxy( IPRBProxy proxy, address envoy, @@ -99,20 +124,20 @@ interface IPRBProxyRegistry { view returns (bool permission); - /// @notice Retrieves the address of the plugin contract installed for the provided method selector. - /// @dev The zero address is returned if no plugin contract is installed. - /// @param owner The proxy owner to make the query for. - /// @param method The method's signature for the query. + /// @notice Retrieves the address of the plugin installed for the provided method selector. + /// @dev The zero address is returned if no plugin is installed. + /// @param owner The proxy owner for the query. + /// @param method The method signature for the query. function getPluginByOwner(address owner, bytes4 method) external view returns (IPRBProxyPlugin plugin); - /// @notice Retrieves the address of the plugin contract installed for the provided method selector. - /// @dev The zero address is returned if no plugin contract is installed. - /// @param proxy The proxy contract to make the query for. - /// @param method The method's signature for the query. + /// @notice Retrieves the address of the plugin installed for the provided method selector. + /// @dev The zero address is returned if no plugin is installed. + /// @param proxy The proxy for the query. + /// @param method The method signature for the query. function getPluginByProxy(IPRBProxy proxy, bytes4 method) external view returns (IPRBProxyPlugin plugin); /// @notice Retrieves the proxy for the provided owner. - /// @param owner The user address to make the query for. + /// @param owner The user address for the query. function getProxy(address owner) external view returns (IPRBProxy proxy); /*////////////////////////////////////////////////////////////////////////// @@ -126,11 +151,11 @@ interface IPRBProxyRegistry { /// Requirements: /// - The caller must not have a proxy. /// - /// @return proxy The address of the newly deployed proxy contract. + /// @return proxy The address of the newly deployed proxy. function deploy() external returns (IPRBProxy proxy); /// @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 + /// target 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. @@ -139,9 +164,9 @@ interface IPRBProxyRegistry { /// - The caller must not have a proxy. /// - `target` must be a contract. /// - /// @param target The address of the target contract. + /// @param target The address of the target. /// @param data Function selector plus ABI encoded data. - /// @return proxy The address of the newly deployed proxy contract. + /// @return proxy The address of the newly deployed proxy. function deployAndExecute(address target, bytes calldata data) external returns (IPRBProxy proxy); /// @notice Deploys a new proxy with CREATE2 for the provided owner. @@ -152,10 +177,11 @@ interface IPRBProxyRegistry { /// - The owner must not have a proxy. /// /// @param owner The owner of the proxy. - /// @return proxy The address of the newly deployed proxy contract. + /// @return proxy The address of the newly deployed proxy. function deployFor(address owner) external returns (IPRBProxy proxy); - /// @notice Installs the provided plugin contract on the proxy belonging to the caller. + /// @notice Installs the provided plugin on the caller's proxy, and saves the list of methods implemented by the + /// plugin so that they can be referenced later. /// /// @dev Emits an {InstallPlugin} event. /// @@ -171,8 +197,8 @@ interface IPRBProxyRegistry { /// @param plugin The address of the plugin to install. function installPlugin(IPRBProxyPlugin plugin) external; - /// @notice Gives or takes a permission from an envoy to call the provided target contract and function selector - /// on behalf of the proxy belonging to the caller. + /// @notice Gives or takes a permission from an envoy to call the provided target and function selector + /// on behalf of the caller's proxy. /// /// @dev Emits a {SetPermission} event. /// @@ -182,21 +208,19 @@ interface IPRBProxyRegistry { /// Requirements: /// - The caller must have a proxy. /// - /// @param envoy The address of the account given permission to call the target contract. - /// @param target The address of the target contract. + /// @param envoy The address of the account given permission to call the target. + /// @param target The address of the target. /// @param permission The boolean permission to set. function setPermission(address envoy, address target, bool permission) external; - /// @notice Uninstalls the provided plugin contract from the proxy belonging to the caller. + /// @notice Uninstalls the plugin from the caller's proxy, and removes the list of methods originally implemented by + /// the plugin. /// /// @dev Emits an {UninstallPlugin} event. /// - /// Notes: - /// - Does not revert if the plugin is not installed. - /// /// Requirements: /// - The caller must have a proxy. - /// - The plugin must have at least one implemented method. + /// - The plugin must be a known, previously installed plugin. /// /// @param plugin The address of the plugin to uninstall. function uninstallPlugin(IPRBProxyPlugin plugin) external; diff --git a/test/Base.t.sol b/test/Base.t.sol index c920862..73cdf3d 100644 --- a/test/Base.t.sol +++ b/test/Base.t.sol @@ -10,16 +10,15 @@ import { IPRBProxyRegistry } from "../src/interfaces/IPRBProxyRegistry.sol"; import { PRBProxy } from "../src/PRBProxy.sol"; import { PRBProxyRegistry } from "../src/PRBProxyRegistry.sol"; +import { PluginBasic } from "./mocks/plugins/PluginBasic.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"; +import { TargetBasic } from "./mocks/targets/TargetBasic.sol"; import { TargetEcho } from "./mocks/targets/TargetEcho.sol"; import { TargetPanic } from "./mocks/targets/TargetPanic.sol"; import { TargetReverter } from "./mocks/targets/TargetReverter.sol"; @@ -34,8 +33,8 @@ abstract contract Base_Test is Assertions, Events, StdCheats, StdUtils { //////////////////////////////////////////////////////////////////////////*/ struct Plugins { + PluginBasic basic; PluginCollider collider; - PluginDummy dummy; PluginEcho echo; PluginEmpty empty; PluginPanic panic; @@ -45,8 +44,7 @@ abstract contract Base_Test is Assertions, Events, StdCheats, StdUtils { } struct Targets { - TargetDummy dummy; - TargetDummyWithFallback dummyWithFallback; + TargetBasic basic; TargetEcho echo; TargetPanic panic; TargetReverter reverter; @@ -90,8 +88,8 @@ abstract contract Base_Test is Assertions, Events, StdCheats, StdUtils { // Create the plugins. plugins = Plugins({ + basic: new PluginBasic(), collider: new PluginCollider(), - dummy: new PluginDummy(), echo: new PluginEcho(), empty: new PluginEmpty(), panic: new PluginPanic(), @@ -102,8 +100,7 @@ abstract contract Base_Test is Assertions, Events, StdCheats, StdUtils { // Create the targets. targets = Targets({ - dummy: new TargetDummy(), - dummyWithFallback: new TargetDummyWithFallback(), + basic: new TargetBasic(), echo: new TargetEcho(), panic: new TargetPanic(), reverter: new TargetReverter(), diff --git a/test/mocks/plugins/PluginDummy.sol b/test/mocks/plugins/PluginBasic.sol similarity index 64% rename from test/mocks/plugins/PluginDummy.sol rename to test/mocks/plugins/PluginBasic.sol index fcf87c6..b5d3e52 100644 --- a/test/mocks/plugins/PluginDummy.sol +++ b/test/mocks/plugins/PluginBasic.sol @@ -3,10 +3,10 @@ pragma solidity >=0.8.18; import { IPRBProxyPlugin } from "../../../src/interfaces/IPRBProxyPlugin.sol"; -import { TargetDummy } from "../targets/TargetDummy.sol"; +import { TargetBasic } from "../targets/TargetBasic.sol"; -contract PluginDummy is IPRBProxyPlugin, TargetDummy { - function methodList() external pure override returns (bytes4[] memory) { +contract PluginBasic is IPRBProxyPlugin, TargetBasic { + function getMethods() external pure override returns (bytes4[] memory) { bytes4[] memory methods = new bytes4[](2); methods[0] = this.foo.selector; methods[1] = this.bar.selector; diff --git a/test/mocks/plugins/PluginCollider.sol b/test/mocks/plugins/PluginCollider.sol index 4e83b6f..21accfd 100644 --- a/test/mocks/plugins/PluginCollider.sol +++ b/test/mocks/plugins/PluginCollider.sol @@ -4,7 +4,7 @@ pragma solidity >=0.8.18; import { IPRBProxyPlugin } from "../../../src/interfaces/IPRBProxyPlugin.sol"; contract PluginCollider is IPRBProxyPlugin { - function methodList() external pure override returns (bytes4[] memory) { + function getMethods() external pure override returns (bytes4[] memory) { bytes4[] memory methods = new bytes4[](1); methods[0] = this.onAddictionFeesRefunded.selector; return methods; diff --git a/test/mocks/plugins/PluginEcho.sol b/test/mocks/plugins/PluginEcho.sol index 13392f5..140eeb1 100644 --- a/test/mocks/plugins/PluginEcho.sol +++ b/test/mocks/plugins/PluginEcho.sol @@ -6,7 +6,7 @@ import { IPRBProxyPlugin } from "../../../src/interfaces/IPRBProxyPlugin.sol"; import { TargetEcho } from "../targets/TargetEcho.sol"; contract PluginEcho is IPRBProxyPlugin, TargetEcho { - function methodList() external pure override returns (bytes4[] memory) { + function getMethods() external pure override returns (bytes4[] memory) { bytes4[] memory methods = new bytes4[](9); methods[0] = this.echoAddress.selector; diff --git a/test/mocks/plugins/PluginEmpty.sol b/test/mocks/plugins/PluginEmpty.sol index 46413ea..f9dec33 100644 --- a/test/mocks/plugins/PluginEmpty.sol +++ b/test/mocks/plugins/PluginEmpty.sol @@ -4,7 +4,7 @@ pragma solidity >=0.8.18; import { IPRBProxyPlugin } from "../../../src/interfaces/IPRBProxyPlugin.sol"; contract PluginEmpty is IPRBProxyPlugin { - function methodList() external pure override returns (bytes4[] memory) { + function getMethods() external pure override returns (bytes4[] memory) { bytes4[] memory methods = new bytes4[](0); return methods; } diff --git a/test/mocks/plugins/PluginPanic.sol b/test/mocks/plugins/PluginPanic.sol index a46f939..d8528bb 100644 --- a/test/mocks/plugins/PluginPanic.sol +++ b/test/mocks/plugins/PluginPanic.sol @@ -5,7 +5,7 @@ import { IPRBProxyPlugin } from "../../../src/interfaces/IPRBProxyPlugin.sol"; import { TargetPanic } from "../targets/TargetPanic.sol"; contract PluginPanic is IPRBProxyPlugin, TargetPanic { - function methodList() external pure override returns (bytes4[] memory) { + function getMethods() external pure override returns (bytes4[] memory) { bytes4[] memory methods = new bytes4[](4); methods[0] = this.failedAssertion.selector; diff --git a/test/mocks/plugins/PluginReverter.sol b/test/mocks/plugins/PluginReverter.sol index fb9fc1e..a9d54bf 100644 --- a/test/mocks/plugins/PluginReverter.sol +++ b/test/mocks/plugins/PluginReverter.sol @@ -6,7 +6,7 @@ import { IPRBProxyPlugin } from "../../../src/interfaces/IPRBProxyPlugin.sol"; import { TargetReverter } from "../targets/TargetReverter.sol"; contract PluginReverter is IPRBProxyPlugin, TargetReverter { - function methodList() external pure override returns (bytes4[] memory) { + function getMethods() external pure override returns (bytes4[] memory) { bytes4[] memory methods = new bytes4[](5); methods[0] = this.withNothing.selector; diff --git a/test/mocks/plugins/PluginSablier.sol b/test/mocks/plugins/PluginSablier.sol index 1e423ff..b05d0f7 100644 --- a/test/mocks/plugins/PluginSablier.sol +++ b/test/mocks/plugins/PluginSablier.sol @@ -4,7 +4,7 @@ pragma solidity >=0.8.18; import { IPRBProxyPlugin } from "../../../src/interfaces/IPRBProxyPlugin.sol"; contract PluginSablier is IPRBProxyPlugin { - function methodList() external pure override returns (bytes4[] memory) { + function getMethods() external pure override returns (bytes4[] memory) { bytes4[] memory methods = new bytes4[](1); methods[0] = this.onStreamCanceled.selector; return methods; diff --git a/test/mocks/plugins/PluginSelfDestructer.sol b/test/mocks/plugins/PluginSelfDestructer.sol index da3f732..7de218b 100644 --- a/test/mocks/plugins/PluginSelfDestructer.sol +++ b/test/mocks/plugins/PluginSelfDestructer.sol @@ -6,7 +6,7 @@ import { IPRBProxyPlugin } from "../../../src/interfaces/IPRBProxyPlugin.sol"; import { TargetSelfDestructer } from "../targets/TargetSelfDestructer.sol"; contract PluginSelfDestructer is IPRBProxyPlugin, TargetSelfDestructer { - function methodList() external pure override returns (bytes4[] memory) { + function getMethods() external pure override returns (bytes4[] memory) { bytes4[] memory methods = new bytes4[](1); methods[0] = this.destroyMe.selector; return methods; diff --git a/test/mocks/targets/TargetDummy.sol b/test/mocks/targets/TargetBasic.sol similarity index 91% rename from test/mocks/targets/TargetDummy.sol rename to test/mocks/targets/TargetBasic.sol index 9d98339..dd0fbe6 100644 --- a/test/mocks/targets/TargetDummy.sol +++ b/test/mocks/targets/TargetBasic.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.18; -contract TargetDummy { +contract TargetBasic { function foo() external pure returns (string memory) { return "foo"; } diff --git a/test/mocks/targets/TargetDummyWithFallback.sol b/test/mocks/targets/TargetDummyWithFallback.sol deleted file mode 100644 index 33bff98..0000000 --- a/test/mocks/targets/TargetDummyWithFallback.sol +++ /dev/null @@ -1,20 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity >=0.8.18 <0.9.0; - -contract TargetDummyWithFallback { - event LogFallback(); - - fallback() external payable { - emit LogFallback(); - } - - receive() external payable { } - - function foo() external pure returns (string memory) { - return "foo"; - } - - function bar() external pure returns (string memory) { - return "bar"; - } -} diff --git a/test/proxy/execute/execute.t.sol b/test/proxy/execute/execute.t.sol index b36d293..37489ab 100644 --- a/test/proxy/execute/execute.t.sol +++ b/test/proxy/execute/execute.t.sol @@ -20,26 +20,26 @@ contract Execute_Test is Proxy_Test { function test_RevertWhen_NoPermission() external whenCallerUnauthorized { changePrank({ msgSender: users.eve }); - bytes memory data = bytes.concat(targets.dummy.foo.selector); + bytes memory data = bytes.concat(targets.basic.foo.selector); vm.expectRevert( abi.encodeWithSelector( - IPRBProxy.PRBProxy_ExecutionUnauthorized.selector, owner, users.eve, address(targets.dummy) + IPRBProxy.PRBProxy_ExecutionUnauthorized.selector, owner, users.eve, address(targets.basic) ) ); - proxy.execute(address(targets.dummy), data); + proxy.execute(address(targets.basic), data); } function test_RevertWhen_PermissionDifferentTarget() external whenCallerUnauthorized { registry.setPermission({ envoy: users.envoy, target: address(targets.echo), permission: true }); changePrank({ msgSender: users.envoy }); - bytes memory data = bytes.concat(targets.dummy.foo.selector); + bytes memory data = bytes.concat(targets.basic.foo.selector); vm.expectRevert( abi.encodeWithSelector( - IPRBProxy.PRBProxy_ExecutionUnauthorized.selector, owner, users.envoy, address(targets.dummy) + IPRBProxy.PRBProxy_ExecutionUnauthorized.selector, owner, users.envoy, address(targets.basic) ) ); - proxy.execute(address(targets.dummy), data); + proxy.execute(address(targets.basic), data); } modifier whenCallerAuthorized() { diff --git a/test/proxy/run-plugin/runPlugin.t.sol b/test/proxy/run-plugin/runPlugin.t.sol index 3ee89ef..523ce5f 100644 --- a/test/proxy/run-plugin/runPlugin.t.sol +++ b/test/proxy/run-plugin/runPlugin.t.sol @@ -5,7 +5,7 @@ import { stdError } from "forge-std/StdError.sol"; import { IPRBProxy } from "src/interfaces/IPRBProxy.sol"; -import { TargetDummy } from "../../mocks/targets/TargetDummy.sol"; +import { TargetBasic } from "../../mocks/targets/TargetBasic.sol"; import { TargetReverter } from "../../mocks/targets/TargetReverter.sol"; import { Proxy_Test } from "../Proxy.t.sol"; @@ -17,10 +17,10 @@ contract RunPlugin_Test is Proxy_Test { function test_RevertWhen_PluginNotInstalled() external { vm.expectRevert( abi.encodeWithSelector( - IPRBProxy.PRBProxy_PluginNotInstalledForMethod.selector, address(owner), plugins.dummy.foo.selector + IPRBProxy.PRBProxy_PluginNotInstalledForMethod.selector, address(owner), plugins.basic.foo.selector ) ); - (bool success,) = address(proxy).call(abi.encodeWithSelector(plugins.dummy.foo.selector)); + (bool success,) = address(proxy).call(abi.encodeWithSelector(plugins.basic.foo.selector)); success; } @@ -148,10 +148,10 @@ contract RunPlugin_Test is Proxy_Test { whenNoEtherSent whenPluginDoesNotSelfDestruct { - registry.installPlugin(plugins.dummy); - (, bytes memory actualResponse) = address(proxy).call(abi.encodeWithSelector(plugins.dummy.foo.selector)); + registry.installPlugin(plugins.basic); + (, bytes memory actualResponse) = address(proxy).call(abi.encodeWithSelector(plugins.basic.foo.selector)); bytes memory expectedResponse = abi.encode(bytes("foo")); - assertEq(actualResponse, expectedResponse, "dummy.foo response mismatch"); + assertEq(actualResponse, expectedResponse, "basic.foo response mismatch"); } function test_RunPlugin_Event() @@ -162,14 +162,14 @@ contract RunPlugin_Test is Proxy_Test { whenNoEtherSent whenPluginDoesNotSelfDestruct { - registry.installPlugin(plugins.dummy); + registry.installPlugin(plugins.basic); vm.expectEmit({ emitter: address(proxy) }); emit RunPlugin({ - plugin: plugins.dummy, - data: abi.encodeWithSelector(TargetDummy.foo.selector), + plugin: plugins.basic, + data: abi.encodeWithSelector(TargetBasic.foo.selector), response: abi.encode(bytes("foo")) }); - (bool success,) = address(proxy).call(abi.encodeWithSelector(plugins.dummy.foo.selector)); + (bool success,) = address(proxy).call(abi.encodeWithSelector(plugins.basic.foo.selector)); success; } } diff --git a/test/registry/get-methods-by-owner/getMethodsByOwner.t.sol b/test/registry/get-methods-by-owner/getMethodsByOwner.t.sol new file mode 100644 index 0000000..dc7c7a0 --- /dev/null +++ b/test/registry/get-methods-by-owner/getMethodsByOwner.t.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity >=0.8.19 <=0.9.0; + +import { Registry_Test } from "../Registry.t.sol"; + +contract GetMethodsByOwner_Test is Registry_Test { + function setUp() public virtual override { + Registry_Test.setUp(); + proxy = registry.deploy(); + } + + function test_GetMethodsByOwner_Unknown() external { + bytes4[] memory actualMethods = registry.getMethodsByOwner({ owner: users.alice, plugin: plugins.basic }); + bytes4[] memory expectedMethods; + assertEq(actualMethods, expectedMethods, "methods not empty array"); + } + + modifier whenPluginKnown() { + registry.installPlugin(plugins.basic); + _; + } + + function test_GetMethodsByOwner() external whenPluginKnown { + bytes4[] memory actualMethods = registry.getMethodsByOwner({ owner: users.alice, plugin: plugins.basic }); + bytes4[] memory expectedMethods = plugins.basic.getMethods(); + assertEq(actualMethods, expectedMethods, "methods mismatch"); + } +} diff --git a/test/registry/get-methods-by-owner/getMethodsByOwner.tree b/test/registry/get-methods-by-owner/getMethodsByOwner.tree new file mode 100644 index 0000000..66a46da --- /dev/null +++ b/test/registry/get-methods-by-owner/getMethodsByOwner.tree @@ -0,0 +1,5 @@ +getMethodsByOwner.t.sol +├── when the plugin is unknown +│ └── it should return an empty array +└── when the plugin is known + └── it should return the methods diff --git a/test/registry/get-methods-by-proxy/getMethodsByProxy.t.sol b/test/registry/get-methods-by-proxy/getMethodsByProxy.t.sol new file mode 100644 index 0000000..f1cd5db --- /dev/null +++ b/test/registry/get-methods-by-proxy/getMethodsByProxy.t.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity >=0.8.19 <=0.9.0; + +import { Registry_Test } from "../Registry.t.sol"; + +contract GetMethodsByProxy_Test is Registry_Test { + function setUp() public virtual override { + Registry_Test.setUp(); + proxy = registry.deploy(); + } + + function test_GetMethodsByProxy_Unknown() external { + bytes4[] memory actualMethods = registry.getMethodsByProxy({ proxy: proxy, plugin: plugins.basic }); + bytes4[] memory expectedMethods; + assertEq(actualMethods, expectedMethods, "methods not empty array"); + } + + modifier whenPluginKnown() { + registry.installPlugin(plugins.basic); + _; + } + + function test_GetMethodsByProxy() external whenPluginKnown { + bytes4[] memory actualMethods = registry.getMethodsByProxy({ proxy: proxy, plugin: plugins.basic }); + bytes4[] memory expectedMethods = plugins.basic.getMethods(); + assertEq(actualMethods, expectedMethods, "methods mismatch"); + } +} diff --git a/test/registry/get-methods-by-proxy/getMethodsByProxy.tree b/test/registry/get-methods-by-proxy/getMethodsByProxy.tree new file mode 100644 index 0000000..a236adc --- /dev/null +++ b/test/registry/get-methods-by-proxy/getMethodsByProxy.tree @@ -0,0 +1,5 @@ +getMethodsByProxy.t.sol +├── when the plugin is unknown +│ └── it should return an empty array +└── when the plugin is known + └── it should return the methods diff --git a/test/registry/get-permission-by-owner/getPermissionByOwner.t.sol b/test/registry/get-permission-by-owner/getPermissionByOwner.t.sol index f2111f4..4f6a27e 100644 --- a/test/registry/get-permission-by-owner/getPermissionByOwner.t.sol +++ b/test/registry/get-permission-by-owner/getPermissionByOwner.t.sol @@ -11,18 +11,18 @@ contract GetPermissionByOwner_Test is Registry_Test { function test_GetPermissionByOwner_EnvoyDoesNotHavePermission() external { bool permission = - registry.getPermissionByProxy({ proxy: proxy, envoy: users.envoy, target: address(targets.dummy) }); + registry.getPermissionByProxy({ proxy: proxy, envoy: users.envoy, target: address(targets.basic) }); assertFalse(permission, "permission mismatch"); } modifier whenEnvoyHasPermission() { - registry.setPermission({ envoy: users.envoy, target: address(targets.dummy), permission: true }); + registry.setPermission({ envoy: users.envoy, target: address(targets.basic), permission: true }); _; } function test_GetPermissionByOwner() external whenEnvoyHasPermission { bool permission = - registry.getPermissionByProxy({ proxy: proxy, envoy: users.envoy, target: address(targets.dummy) }); + registry.getPermissionByProxy({ proxy: proxy, envoy: users.envoy, target: address(targets.basic) }); assertTrue(permission, "permission mismatch"); } } diff --git a/test/registry/get-permission-by-proxy/getPermissionByProxy.t.sol b/test/registry/get-permission-by-proxy/getPermissionByProxy.t.sol index cd17e74..171104a 100644 --- a/test/registry/get-permission-by-proxy/getPermissionByProxy.t.sol +++ b/test/registry/get-permission-by-proxy/getPermissionByProxy.t.sol @@ -11,18 +11,18 @@ contract GetPermissionByProxy_Test is Registry_Test { function test_GetPermissionByProxy_EnvoyDoesNotHavePermission() external { bool permission = - registry.getPermissionByProxy({ proxy: proxy, envoy: users.envoy, target: address(targets.dummy) }); + registry.getPermissionByProxy({ proxy: proxy, envoy: users.envoy, target: address(targets.basic) }); assertFalse(permission, "permission mismatch"); } modifier whenEnvoyHasPermission() { - registry.setPermission({ envoy: users.envoy, target: address(targets.dummy), permission: true }); + registry.setPermission({ envoy: users.envoy, target: address(targets.basic), permission: true }); _; } function test_GetPermissionByProxy() external whenEnvoyHasPermission { bool permission = - registry.getPermissionByProxy({ proxy: proxy, envoy: users.envoy, target: address(targets.dummy) }); + registry.getPermissionByProxy({ proxy: proxy, envoy: users.envoy, target: address(targets.basic) }); assertTrue(permission, "permission mismatch"); } } diff --git a/test/registry/get-plugin-by-owner/getPluginByOwner.t.sol b/test/registry/get-plugin-by-owner/getPluginByOwner.t.sol index e41becb..4b78a50 100644 --- a/test/registry/get-plugin-by-owner/getPluginByOwner.t.sol +++ b/test/registry/get-plugin-by-owner/getPluginByOwner.t.sol @@ -11,20 +11,20 @@ contract GetPluginByOwner_Test is Registry_Test { function test_GetPluginByOwner_Uninstalled() external { address actualPlugin = - address(registry.getPluginByOwner({ owner: users.alice, method: plugins.dummy.foo.selector })); + address(registry.getPluginByOwner({ owner: users.alice, method: plugins.basic.foo.selector })); address expectedPlugin = address(0); assertEq(actualPlugin, expectedPlugin, "plugin not zero address"); } modifier whenPluginInstalled() { - registry.installPlugin(plugins.dummy); + registry.installPlugin(plugins.basic); _; } function test_GetPluginByOwner() external whenPluginInstalled { address actualPlugin = - address(registry.getPluginByOwner({ owner: users.alice, method: plugins.dummy.foo.selector })); - address expectedPlugin = address(plugins.dummy); + address(registry.getPluginByOwner({ owner: users.alice, method: plugins.basic.foo.selector })); + address expectedPlugin = address(plugins.basic); assertEq(actualPlugin, expectedPlugin, "plugin address mismatch"); } } diff --git a/test/registry/get-plugin-by-proxy/getPluginByProxy.t.sol b/test/registry/get-plugin-by-proxy/getPluginByProxy.t.sol index 8b42d43..969bb66 100644 --- a/test/registry/get-plugin-by-proxy/getPluginByProxy.t.sol +++ b/test/registry/get-plugin-by-proxy/getPluginByProxy.t.sol @@ -10,19 +10,19 @@ contract GetPluginByProxy_Test is Registry_Test { } function test_GetPluginByProxy_Uninstalled() external { - address actualPlugin = address(registry.getPluginByProxy({ proxy: proxy, method: plugins.dummy.foo.selector })); + address actualPlugin = address(registry.getPluginByProxy({ proxy: proxy, method: plugins.basic.foo.selector })); address expectedPlugin = address(0); assertEq(actualPlugin, expectedPlugin, "plugin not zero address"); } modifier whenPluginInstalled() { - registry.installPlugin(plugins.dummy); + registry.installPlugin(plugins.basic); _; } function test_GetPluginByProxy() external whenPluginInstalled { - address actualPlugin = address(registry.getPluginByProxy({ proxy: proxy, method: plugins.dummy.foo.selector })); - address expectedPlugin = address(plugins.dummy); + address actualPlugin = address(registry.getPluginByProxy({ proxy: proxy, method: plugins.basic.foo.selector })); + address expectedPlugin = address(plugins.basic); assertEq(actualPlugin, expectedPlugin, "plugin address mismatch"); } } diff --git a/test/registry/install-plugin/installPlugin.t.sol b/test/registry/install-plugin/installPlugin.t.sol index 21db6e0..9029276 100644 --- a/test/registry/install-plugin/installPlugin.t.sol +++ b/test/registry/install-plugin/installPlugin.t.sol @@ -20,18 +20,18 @@ contract InstallPlugin_Test is Registry_Test { _; } - function test_RevertWhen_PluginEmptyMethodList() external whenCallerHasProxy { + function test_RevertWhen_PluginDoesNotImplementAnyMethod() external whenCallerHasProxy { vm.expectRevert( - abi.encodeWithSelector(IPRBProxyRegistry.PRBProxyRegistry_PluginEmptyMethodList.selector, plugins.empty) + abi.encodeWithSelector(IPRBProxyRegistry.PRBProxyRegistry_PluginWithZeroMethods.selector, plugins.empty) ); registry.installPlugin(plugins.empty); } - modifier whenPluginListNotEmpty() { + modifier whenPluginImplementsMethods() { _; } - function test_RevertWhen_MethodCollision() external whenCallerHasProxy whenPluginListNotEmpty { + function test_RevertWhen_MethodCollisions() external whenCallerHasProxy whenPluginImplementsMethods { registry.installPlugin(plugins.sablier); vm.expectRevert( abi.encodeWithSelector( @@ -44,26 +44,48 @@ contract InstallPlugin_Test is Registry_Test { registry.installPlugin(plugins.collider); } - modifier whenNoMethodCollision() { + modifier whenNoMethodCollisions() { _; } - function test_InstallPlugin() external whenCallerHasProxy whenPluginListNotEmpty whenNoMethodCollision { - // Install a dummy plugin that has some methods. - registry.installPlugin(plugins.dummy); + function test_InstallPlugin() external whenCallerHasProxy whenPluginImplementsMethods whenNoMethodCollisions { + // Install a basic plugin with some methods. + registry.installPlugin(plugins.basic); // Assert that every plugin method has been installed. - bytes4[] memory pluginMethods = plugins.dummy.methodList(); + bytes4[] memory pluginMethods = plugins.basic.getMethods(); for (uint256 i = 0; i < pluginMethods.length; ++i) { IPRBProxyPlugin actualPlugin = registry.getPluginByOwner({ owner: users.alice, method: pluginMethods[i] }); - IPRBProxyPlugin expectedPlugin = plugins.dummy; + IPRBProxyPlugin expectedPlugin = plugins.basic; assertEq(actualPlugin, expectedPlugin, "plugin method not installed"); } } - function test_InstallPlugin_Event() external whenCallerHasProxy whenPluginListNotEmpty whenNoMethodCollision { + function test_InstallPlugin_ReverseMapping() + external + whenCallerHasProxy + whenPluginImplementsMethods + whenNoMethodCollisions + { + registry.installPlugin(plugins.basic); + bytes4[] memory actualMethods = registry.getMethodsByOwner({ owner: users.alice, plugin: plugins.basic }); + bytes4[] memory expectedMethods = plugins.basic.getMethods(); + assertEq(actualMethods, expectedMethods, "methods not saved in reverse mapping"); + } + + function test_InstallPlugin_Event() + external + whenCallerHasProxy + whenPluginImplementsMethods + whenNoMethodCollisions + { vm.expectEmit({ emitter: address(registry) }); - emit InstallPlugin({ owner: users.alice, proxy: proxy, plugin: plugins.dummy }); - registry.installPlugin(plugins.dummy); + emit InstallPlugin({ + owner: users.alice, + proxy: proxy, + plugin: plugins.basic, + methods: plugins.basic.getMethods() + }); + registry.installPlugin(plugins.basic); } } diff --git a/test/registry/install-plugin/installPlugin.tree b/test/registry/install-plugin/installPlugin.tree index 7b0c605..6360b3c 100644 --- a/test/registry/install-plugin/installPlugin.tree +++ b/test/registry/install-plugin/installPlugin.tree @@ -2,11 +2,12 @@ installPlugin.t.sol ├── when the caller doesn't have a proxy │ └── it should revert └── when the caller has a proxy - ├── when the plugin list is empty + ├── when the plugin doesn't implement any method │ └── it should revert - └── when the plugin list is not empty - ├── when there is a method collision + └── when the plugin implements at least one method + ├── when there are method collisions │ └── it should revert - └── when there is no method collision + └── when there aren't any method collisions ├── it should install the plugin + ├── it should save the methods in the reverse mapping └── it should emit an {InstallPlugin} event diff --git a/test/registry/set-permission/setPermission.t.sol b/test/registry/set-permission/setPermission.t.sol index 7ab3022..8d10134 100644 --- a/test/registry/set-permission/setPermission.t.sol +++ b/test/registry/set-permission/setPermission.t.sol @@ -11,7 +11,7 @@ contract SetPermission_Test is Registry_Test { abi.encodeWithSelector(IPRBProxyRegistry.PRBProxyRegistry_CallerDoesNotHaveProxy.selector, users.bob) ); changePrank({ msgSender: users.bob }); - registry.setPermission({ envoy: users.envoy, target: address(targets.dummy), permission: true }); + registry.setPermission({ envoy: users.envoy, target: address(targets.basic), permission: true }); } modifier whenCallerHasProxy() { @@ -20,21 +20,21 @@ contract SetPermission_Test is Registry_Test { } function test_SetPermission_PermissionNotSet() external whenCallerHasProxy { - registry.setPermission({ envoy: users.envoy, target: address(targets.dummy), permission: true }); + registry.setPermission({ envoy: users.envoy, target: address(targets.basic), permission: true }); bool permission = - registry.getPermissionByOwner({ owner: users.alice, envoy: users.envoy, target: address(targets.dummy) }); + registry.getPermissionByOwner({ owner: users.alice, envoy: users.envoy, target: address(targets.basic) }); assertTrue(permission, "permission mismatch"); } modifier whenPermissionSet() { - registry.setPermission({ envoy: users.envoy, target: address(targets.dummy), permission: true }); + registry.setPermission({ envoy: users.envoy, target: address(targets.basic), permission: true }); _; } function test_SetPermission_ResetPermission() external whenCallerHasProxy whenPermissionSet { - registry.setPermission({ envoy: users.envoy, target: address(targets.dummy), permission: true }); + registry.setPermission({ envoy: users.envoy, target: address(targets.basic), permission: true }); bool permission = - registry.getPermissionByOwner({ owner: users.alice, envoy: users.envoy, target: address(targets.dummy) }); + registry.getPermissionByOwner({ owner: users.alice, envoy: users.envoy, target: address(targets.basic) }); assertTrue(permission, "permission mismatch"); } @@ -44,14 +44,14 @@ contract SetPermission_Test is Registry_Test { owner: users.alice, proxy: proxy, envoy: users.envoy, - target: address(targets.dummy), + target: address(targets.basic), permission: true }); - registry.setPermission({ envoy: users.envoy, target: address(targets.dummy), permission: true }); + registry.setPermission({ envoy: users.envoy, target: address(targets.basic), permission: true }); } function test_SetPermission_UnsetPermission() external whenCallerHasProxy whenPermissionSet { - registry.setPermission({ envoy: users.envoy, target: address(targets.dummy), permission: false }); + registry.setPermission({ envoy: users.envoy, target: address(targets.basic), permission: false }); } function test_SetPermission_UnsetPermission_Event() external whenCallerHasProxy whenPermissionSet { @@ -60,9 +60,9 @@ contract SetPermission_Test is Registry_Test { owner: users.alice, proxy: proxy, envoy: users.envoy, - target: address(targets.dummy), + target: address(targets.basic), permission: false }); - registry.setPermission({ envoy: users.envoy, target: address(targets.dummy), permission: false }); + registry.setPermission({ envoy: users.envoy, target: address(targets.basic), permission: false }); } } diff --git a/test/registry/uninstall-plugin/uninstallPlugin.t.sol b/test/registry/uninstall-plugin/uninstallPlugin.t.sol index ba82bed..090cbde 100644 --- a/test/registry/uninstall-plugin/uninstallPlugin.t.sol +++ b/test/registry/uninstall-plugin/uninstallPlugin.t.sol @@ -20,23 +20,24 @@ contract UninstallPlugin_Test is Registry_Test { _; } - function test_RevertWhen_PluginEmptyMethodList() external whenCallerHasProxy { + function test_RevertWhen_PluginUnknown() external whenCallerHasProxy { vm.expectRevert( - abi.encodeWithSelector(IPRBProxyRegistry.PRBProxyRegistry_PluginEmptyMethodList.selector, plugins.empty) + abi.encodeWithSelector(IPRBProxyRegistry.PRBProxyRegistry_PluginUnknown.selector, plugins.empty) ); registry.uninstallPlugin(plugins.empty); } - modifier whenPluginHasMethods() { + modifier whenPluginKnown() { + registry.installPlugin(plugins.basic); _; } - function test_UninstallPlugin_PluginNotInstalledBefore() external whenCallerHasProxy whenPluginHasMethods { + function test_UninstallPlugin() external whenCallerHasProxy whenPluginKnown { // Uninstall the plugin. - registry.uninstallPlugin(plugins.dummy); + registry.uninstallPlugin(plugins.basic); - // Assert that every plugin method has remained uninstalled. - bytes4[] memory pluginMethods = plugins.dummy.methodList(); + // Assert that every plugin method has been uninstalled. + bytes4[] memory pluginMethods = plugins.basic.getMethods(); for (uint256 i = 0; i < pluginMethods.length; ++i) { IPRBProxyPlugin actualPlugin = registry.getPluginByOwner({ owner: users.alice, method: pluginMethods[i] }); IPRBProxyPlugin expectedPlugin = IPRBProxyPlugin(address(0)); @@ -44,28 +45,21 @@ contract UninstallPlugin_Test is Registry_Test { } } - modifier whenPluginInstalledBefore() { - // Install the dummy plugin. - registry.installPlugin(plugins.dummy); - _; - } - - function test_UninstallPlugin() external whenCallerHasProxy whenPluginHasMethods whenPluginInstalledBefore { - // Uninstall the plugin. - registry.uninstallPlugin(plugins.dummy); - - // Assert that every plugin method has been 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] }); - IPRBProxyPlugin expectedPlugin = IPRBProxyPlugin(address(0)); - assertEq(actualPlugin, expectedPlugin, "plugin method still installed"); - } + function test_UninstallPlugin_ReverseMapping() external whenCallerHasProxy whenPluginKnown { + registry.uninstallPlugin(plugins.basic); + bytes4[] memory actualMethods = registry.getMethodsByOwner({ owner: users.alice, plugin: plugins.basic }); + bytes4[] memory expectedMethods; + assertEq(actualMethods, expectedMethods, "methods not removed from reverse mapping"); } - function test_UninstallPlugin_Event() external whenCallerHasProxy whenPluginHasMethods whenPluginInstalledBefore { + function test_UninstallPlugin_Event() external whenCallerHasProxy whenPluginKnown { vm.expectEmit({ emitter: address(registry) }); - emit UninstallPlugin({ owner: users.alice, proxy: proxy, plugin: plugins.dummy }); - registry.uninstallPlugin(plugins.dummy); + emit UninstallPlugin({ + owner: users.alice, + proxy: proxy, + plugin: plugins.basic, + methods: plugins.basic.getMethods() + }); + registry.uninstallPlugin(plugins.basic); } } diff --git a/test/registry/uninstall-plugin/uninstallPlugin.tree b/test/registry/uninstall-plugin/uninstallPlugin.tree index 0cb46bb..00d6242 100644 --- a/test/registry/uninstall-plugin/uninstallPlugin.tree +++ b/test/registry/uninstall-plugin/uninstallPlugin.tree @@ -1,12 +1,10 @@ uninstallPlugin.t.sol -├── when the caller is not the owner +├── when the caller does not have a proxy │ └── it should revert -└── when the caller is the owner - ├── when the plugin list is empty +└── when the caller has a proxy + ├── when the plugin is unknown │ └── it should revert - └── 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 before - ├── it should uninstall the plugin - └── it should emit an {UninstallPlugin} event + └── when the plugin is known + ├── it should uninstall the plugin + ├── it should remove the methods from the reverse mapping + └── it should emit an {UninstallPlugin} event diff --git a/test/utils/Assertions.sol b/test/utils/Assertions.sol index f61199f..18493d6 100644 --- a/test/utils/Assertions.sol +++ b/test/utils/Assertions.sol @@ -6,13 +6,31 @@ import { PRBTest } from "@prb/test/PRBTest.sol"; import { IPRBProxyPlugin } from "../../src/interfaces/IPRBProxyPlugin.sol"; abstract contract Assertions is PRBTest { - /// @dev Compares two {IPRBProxyPlugin} addresses. function assertEq(IPRBProxyPlugin a, IPRBProxyPlugin b) internal { assertEq(address(a), address(b)); } - /// @dev Compares two {IPRBProxyPlugin} addresses. function assertEq(IPRBProxyPlugin a, IPRBProxyPlugin b, string memory err) internal { assertEq(address(a), address(b), err); } + + function assertEq(bytes4[] memory a, bytes4[] memory b) internal { + uint256[] memory castedA; + uint256[] memory castedB; + assembly { + castedA := a + castedB := b + } + assertEq(castedA, castedB); + } + + function assertEq(bytes4[] memory a, bytes4[] memory b, string memory err) internal { + uint256[] memory castedA; + uint256[] memory castedB; + assembly { + castedA := a + castedB := b + } + assertEq(castedA, castedB, err); + } } diff --git a/test/utils/Events.sol b/test/utils/Events.sol index 9194a20..e4b0658 100644 --- a/test/utils/Events.sol +++ b/test/utils/Events.sol @@ -20,11 +20,15 @@ abstract contract Events { event DeployProxy(address indexed operator, address indexed owner, IPRBProxy proxy); - event InstallPlugin(address indexed owner, IPRBProxy indexed proxy, IPRBProxyPlugin indexed plugin); + event InstallPlugin( + address indexed owner, IPRBProxy indexed proxy, IPRBProxyPlugin indexed plugin, bytes4[] methods + ); event SetPermission( address indexed owner, IPRBProxy indexed proxy, address indexed envoy, address target, bool permission ); - event UninstallPlugin(address indexed owner, IPRBProxy indexed proxy, IPRBProxyPlugin indexed plugin); + event UninstallPlugin( + address indexed owner, IPRBProxy indexed proxy, IPRBProxyPlugin indexed plugin, bytes4[] methods + ); } diff --git a/test/utils/Precompiles.sol b/test/utils/Precompiles.sol index 2e7ceaa..d99073e 100644 --- a/test/utils/Precompiles.sol +++ b/test/utils/Precompiles.sol @@ -12,7 +12,7 @@ contract Precompiles { //////////////////////////////////////////////////////////////////////////*/ bytes public constant BYTECODE_REGISTRY = - hex"6080806040523461001657611c06908161001c8239f35b600080fdfe60806040818152600491823610156200001757600080fd5b600092833560e01c918263092af813146200095c575081631ddef5b914620008ad5781634bddd93a14620007855781635cabcdf714620006ab57816361be4859146200065157816366b0182d146200054b57816374912cd214620004cc578163775c300c1462000453578163aa4b826a1462000373578163b31d1b991462000308578163b7fba4d314620002c9578163b9678403146200011a575063ffa1ad7414620000c257600080fd5b34620001165781600319360112620001165780516200011291620000e68262000d4b565b600c82526b342e302e302d626574612e3560a01b60208301525191829160208352602083019062000d8b565b0390f35b5080fd5b905034620002c55760209182600319360112620002c1576001600160a01b03823581811694919390859003620002bd573386526005825283838720541615620002a657825163ecdb286560e01b815286818381838a5af19081156200029c57879162000275575b5080519081156200025e57875b828110620001cd57505050506005903386525283205416337f9638c85f58d5866b4eb299b2e5f8f366372bd58e2d6e6e6eb19f3462948f33878480a480f35b6001600160e01b0319620001e2828462000f54565b5116338a52848652868a20818b52865287878b2054166200022757338a52848652868a20908a52855285892080546001600160a01b031916891790556001016200018e565b846064918a898b818f8c33825287815282822090878352522054169051936361a2116b60e11b855284015260248301526044820152fd5b845163b74066bb60e01b8152808401889052602490fd5b6200029591503d8089833e6200028c818362000d68565b81019062000ebd565b3862000181565b84513d89823e3d90fd5b60249083519063963e961b60e01b82523390820152fd5b8580fd5b8380fd5b8280fd5b5050346200011657602036600319011262000116576020916001600160a01b0390829082620002f762000c79565b168152600585522054169051908152f35b50503462000116576060366003190112620001165760ff816020936200032d62000c79565b6200033762000c95565b906200034262000cac565b6001600160a01b03918216845260038852848420928216845291875283832091168252855220549151911615158152f35b905034620002c5576060366003190112620002c5576200039262000c79565b916200039d62000c95565b90604435801515809103620002bd57338652600560209081528287205490946001600160a01b03918216156200043c5750907f4f2bd80fb4928b06abcd76e3b26209a615f0612f98dc4a8b176934b1a389933392913388526003865280838920971696878952865280838920951694858952865282882060ff1981541660ff84161790553388526005865282882054169482519485528401523392a480f35b60249084519063963e961b60e01b82523390820152fd5b828434620004c95780600319360112620004c9573381526005602052819020546001600160a01b0390811692836200049c5760208383620004943362000f7f565b915191168152f35b91516356352f0b60e11b8152339281019283526001600160a01b0390931660208301525081906040010390fd5b80fd5b905034620002c5576020366003190112620002c5578190620004ed62000c79565b6001600160a01b038082168652600560205292909420548216806200051b5750506200049460209362000f7f565b92516356352f0b60e11b81526001600160a01b039485169181019182529390921660208301525081906040010390fd5b505034620001165781600319360112620001165760018060a01b0391828154169160019384541693815192809160025491620005878362000cdb565b80875292828116908115620006225750600114620005d3575b5050508291620005b96200011294606093038462000d68565b805195869586526020860152840152606083019062000d8b565b92506002835260008051602062001be68339815191525b8284106200060957505050820160200181620005b962000112620005a0565b80546020858801810191909152909301928101620005ea565b60ff191660208089019190915293151560051b87019093019350849250620005b99150620001129050620005a0565b905034620002c55781600319360112620002c557816020936200067362000c79565b926200067e62000cc3565b6001600160a01b0394851683529086528282206001600160e01b0319909116825285522054915191168152f35b828434620004c9576060366003190112620004c957620006ca62000c79565b620006d462000c95565b90620006df62000cac565b8451638da5cb5b60e01b815260209691936001600160a01b039390928891839190829087165afa9081156200077b57918386949288969460ff989162000747575b50168452600388528185852091168452875283832091168252855220541690519015158152f35b6200076c91508a3d8c1162000773575b62000763818362000d68565b81019062000dcd565b8a62000720565b503d62000757565b86513d87823e3d90fd5b905034620002c55760209182600319360112620002c1576001600160a01b03823581811694919390859003620002bd573386526005825283838720541615620002a657825163ecdb286560e01b815286818381838a5af19081156200029c5787916200088f575b5080519182156200087857875b8381106200083857505050506005903386525283205416337f381d2d67cec77a56c13de6658effae4585e49ae536458929d53bfd4c3599e0ad8480a480f35b338952818552858920600191906001600160e01b03196200085a838762000f54565b51168b528652868a2080546001600160a01b031916905501620007f9565b845163b74066bb60e01b8152908101879052602490fd5b620008a691503d8089833e6200028c818362000d68565b38620007ec565b828434620004c95781600319360112620004c957620008cb62000c79565b90620008d662000cc3565b835194638da5cb5b60e01b86526020958681838160018060a01b038099165afa908115620009525784939291869188969162000930575b5016835286528282206001600160e01b0319909116825285522054915191168152f35b6200094b9150893d8b11620007735762000763818362000d68565b896200090d565b86513d86823e3d90fd5b90838534620004c95781600319360112620004c9576200097b62000c79565b9060243567ffffffffffffffff94858211620002c55736602383011215620002c5578181013595808711620002c1573660248885010111620002c1573384526020966005885260018060a01b03988988872054168062000c4f575050620009fa87513360601b8a82015260148152620009f48162000d4b565b62000dee565b9362000a7e8a808a5162000a0e8162000d18565b338152818d82019b168b52898d8d5197806024601f199962000a38858c601f860116018d62000d68565b828c5201838b013788010152858c8201525116986001600160601b0360a01b998a8a5416178955511660018060a01b03166001600160601b0360a01b6001541617600155565b81519183831162000c3c5790829162000a9960025462000cdb565b8b601f821162000bef575b50508a91601f841160011462000b7d5750879262000b71575b50508160011b916000199060031b1c1916176002555b8551916109f190818401928484109084111762000b5e5750908291620011f58339039083f594851562000b52578394951693849162000b1162000e2b565b338152600587522091825416179055805182815233907f2d8895d948115783fa362a57339c4c179365fafeafdd7dca66364ae296f50b75853392a351908152f35b508251903d90823e3d90fd5b634e487b7160e01b865260419052602485fd5b015190508a8062000abd565b6002895260008051602062001be68339815191529316885b8c82821062000bd857505090846001959493921062000bbe575b505050811b0160025562000ad3565b015160001960f88460031b161c191690558a808062000baf565b600185968293968601518155019501930162000b95565b62000c299160028b5260008051602062001be683398151915290601f870160051c820192871062000c31575b601f0160051c019062000e12565b8c8b62000aa4565b909150819062000c1b565b634e487b7160e01b875260418552602487fd5b6356352f0b60e11b8252338583019081526001600160a01b03909116602082015281906040010390fd5b600435906001600160a01b038216820362000c9057565b600080fd5b602435906001600160a01b038216820362000c9057565b604435906001600160a01b038216820362000c9057565b602435906001600160e01b03198216820362000c9057565b90600182811c9216801562000d0d575b602083101462000cf757565b634e487b7160e01b600052602260045260246000fd5b91607f169162000ceb565b6060810190811067ffffffffffffffff82111762000d3557604052565b634e487b7160e01b600052604160045260246000fd5b6040810190811067ffffffffffffffff82111762000d3557604052565b90601f8019910116810190811067ffffffffffffffff82111762000d3557604052565b919082519283825260005b84811062000db8575050826000602080949584010152601f8019910116010190565b60208183018101518483018201520162000d96565b9081602091031262000c9057516001600160a01b038116810362000c905790565b60208151910151906020811062000e03575090565b6000199060200360031b1b1690565b81811062000e1e575050565b6000815560010162000e12565b6000808055600181815562000e4260025462000cdb565b908162000e4e57505050565b601f821160011462000e61575050600255565b60028352601f60008051602062001be6833981519152920160051c82017f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5acf5b81811062000eb2575050508160025555565b848155820162000ea0565b90602090818382031262000c9057825167ffffffffffffffff9384821162000c90570181601f8201121562000c9057805193841162000d35578360051b906040519462000f0d8584018762000d68565b8552838086019282010192831162000c90578301905b82821062000f32575050505090565b81516001600160e01b03198116810362000c9057815290830190830162000f23565b805182101562000f695760209160051b010190565b634e487b7160e01b600052603260045260246000fd5b9060409182519060018060a01b0362000fb9818316936020936001600160601b03199060601b168482015260148152620009f48162000d4b565b9085519062000fc88262000d18565b8482528382019260009283855288518681019167ffffffffffffffff9282811084821117620011e057856200103892848e839481528a82528201525116976001600160601b0360a01b9889895416178855511660018060a01b03166001600160601b0360a01b6001541617600155565b805190828211620011095781906200105260025462000cdb565b601f8111620011a0575b508890601f8311600114620011295787926200111d575b50508160011b916000199060031b1c1916176002555b8851906109f1808301918211838310176200110957908291620011f58339039084f58015620010ff57917f2d8895d948115783fa362a57339c4c179365fafeafdd7dca66364ae296f50b759391889316978891620010e662000e2b565b87815260058752209182541617905551918583523392a3565b87513d84823e3d90fd5b634e487b7160e01b86526041600452602486fd5b01519050388062001073565b6002885260008051602062001be68339815191529250601f198416885b8b828210620011895750509084600195949392106200116f575b505050811b0160025562001089565b015160001960f88460031b161c1916905538808062001160565b600185968293968601518155019501930162001146565b620011d9906002895260008051602062001be6833981519152601f850160051c8101918c861062000c3157601f0160051c019062000e12565b386200105c565b634e487b7160e01b87526041600452602487fdfe60c08060405234620000ca573360a0526366b0182d60e01b8152600081600481335afa8015620000c4576000809281926200009b575b506080526001600160a01b03821662000087575b60405161068e9081620003638239608051818181610192015281816102c401526104a5015260a05181818161014d0152818161031301526105250152f35b6200009291620002b2565b50388062000049565b909150620000bb92503d8092823e620000b482620000e5565b016200018e565b90913862000035565b62000221565b600080fd5b634e487b7160e01b600052604160045260246000fd5b60c0601f91909101601f19168101906001600160401b038211908210176200010c57604052565b620000cf565b601f909101601f19168101906001600160401b038211908210176200010c57604052565b60e051906001600160a01b0382168203620000ca57565b6001600160401b0381116200010c57601f01601f191660200190565b60005b8381106200017d5750506000910152565b81810151838201526020016200016c565b606060bf19820112620000ca5760c0516001600160a01b0381168103620000ca5791620001ba62000136565b610100519092906001600160401b038111620000ca578160df82011215620000ca578060c00151620001ec816200014d565b92620001fc604051948562000112565b81845260e08284010111620000ca576200021e9160e060208501910162000169565b90565b6040513d6000823e3d90fd5b3d156200025d573d9062000241826200014d565b9162000251604051938462000112565b82523d6000602084013e565b606090565b906020916200027d8151809281855285808601910162000169565b601f01601f1916010190565b9091620002a36200021e9360408452604084019062000262565b91602081840391015262000262565b9190823b156200034157600080825160208401865af4907fb24ebe141c5f2a744b103bea65fce6c40e0dc65d7341d092c09b160f40447990620002f46200022d565b60405190956001600160a01b0316928190620003139088908362000289565b0390a2156200031e57565b508051156200032f57602081519101fd5b60405163061a160d60e41b8152600490fd5b604051636d17e5ef60e11b81526001600160a01b0384166004820152602490fdfe60806040526004361015610027575b36156100255761001d366102b5565b602081519101f35b005b6000803560e01c9081631cff79cd1461005a575080637b1039991461005557638da5cb5b0361000e5761017c565b610137565b60403660031901126100ca57600435610072816100cd565b60243567ffffffffffffffff928382116100ca57366023830112156100ca5781600401359384116100ca5736602485840101116100ca576100c66100ba8560248501866104a2565b60405191829182610123565b0390f35b80fd5b6001600160a01b038116036100de57565b600080fd5b919082519283825260005b84811061010f575050826000602080949584010152601f8019910116010190565b6020818301810151848301820152016100ee565b9060206101349281815201906100e3565b90565b346100de5760003660031901126100de576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b346100de5760003660031901126100de576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b634e487b7160e01b600052604160045260246000fd5b90601f8019910116810190811067ffffffffffffffff8211176101f957604052565b6101c1565b908160209103126100de5751610134816100cd565b6040513d6000823e3d90fd5b908160008237016000815290565b67ffffffffffffffff81116101f957601f01601f191660200190565b3d15610274573d9061025a8261022d565b9161026860405193846101d7565b82523d6000602084013e565b606090565b606090610134939260408252806040830152806000848401376000838284010152601f80199101168101906020838284030191015201906100e3565b6040516361be485960e01b81527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0381811660048401526001600160e01b03196000351660248401819052939493906020846044817f000000000000000000000000000000000000000000000000000000000000000085165afa93841561044e5760009461041e575b5083169182156103e357505060008060405180610362818961021f565b0390845af4907fc4dabe0d7ef7462e2218f2c398c21ef217803e1c46f5cf802d1a5d1d9b503f2f610391610249565b80966103a260405192839283610279565b0390a2156103ad5750565b8251909150156103c05750805190602001fd5b60405163023c045d60e21b81526001600160a01b03919091166004820152602490fd5b604051638848730f60e01b81523360048201526001600160a01b039190911660248201526001600160e01b0319919091166044820152606490fd5b61044091945060203d8111610447575b61043881836101d7565b8101906101fe565b9238610345565b503d61042e565b610213565b908160209103126100de575180151581036100de5790565b9291926104778261022d565b9161048560405193846101d7565b8294818452818301116100de578281602093846000960137010152565b917f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0333818316036104ee575b505061013492916104e891369161046b565b906105e5565b60405163b31d1b9960e01b81526001600160a01b0383811660048301523360248301528616604482015290602090829060649082907f0000000000000000000000000000000000000000000000000000000000000000165afa90811561044e57600091610592575b501561056257806104d6565b6040516355d1750960e01b81526001600160a01b0391821660048201523360248201529084166044820152606490fd5b6105b3915060203d81116105b9575b6105ab81836101d7565b810190610453565b38610556565b503d6105a1565b90916105d7610134936040845260408401906100e3565b9160208184039101526100e3565b9190823b1561066d57600080825160208401865af4907fb24ebe141c5f2a744b103bea65fce6c40e0dc65d7341d092c09b160f40447990610624610249565b60405190956001600160a01b0316928190610641908890836105c0565b0390a21561064b57565b5080511561065b57602081519101fd5b60405163061a160d60e41b8152600490fd5b604051636d17e5ef60e11b81526001600160a01b0384166004820152602490fd405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace"; + hex"6080806040523461001657612405908161001c8239f35b600080fdfe60806040818152600491823610156200001757600080fd5b60009260e0908435821c928363092af81314620011ab575082631ddef5b914620010f95782634bddd93a1462000db45782635cabcdf71462000cf357826361be48591462000c975782636383afb21462000af957826366b0182d14620009f257826374912cd21462000972578263775c300c14620008f9578263aa4b826a1462000816578263b31d1b9914620007a8578263b7fba4d31462000768578263b9678403146200036457508163fa557e931462000135575063ffa1ad7414620000dd57600080fd5b34620001315781600319360112620001315780516200012d916200010182620015e1565b600c82526b342e302e302d626574612e3560a01b60208301525191829160208352602083019062001621565b0390f35b5080fd5b838334620001315780600319360112620001315762000153620014c8565b926200015e620014e4565b60018060a01b03809516845260209460038652838520911684528452818320908251809486845492838152019381528681205b826007830110620002f5579287926200020195926200012d9989965493838310620002d7575b50828210620002b8575b82821062000299575b8282106200027a575b8282106200025b575b8282106200023e575b82821062000221575b50106200020c575b5090500383620015fe565b51918291826200152a565b6001600160e01b0319168152018087620001f6565b83811b6001600160e01b03191685529093019260010184620001ee565b83891b6001600160e01b03191685529093019260010184620001e5565b606084901b6001600160e01b03191685529093019260010184620001dc565b608084901b6001600160e01b03191685529093019260010184620001d3565b60a084901b6001600160e01b03191685529093019260010184620001ca565b60c084901b6001600160e01b03191685529093019260010184620001c1565b84901b6001600160e01b031916855290930192600101848b620001b7565b80546001600160e01b031981861b8116875260c082811b82168b89015260a083811b83168a8a0152608084811b84166060808c019190915285901b8416908a0152838a1b831690890152828b1b8216908801521685850152610100909401936008919091019060010162000191565b90929150346200076457602080600319360112620007605762000386620014c8565b33865260068252838620546001600160a01b039291908316156200074a57821694845193631abad16560e21b855287858281838b5af19485156200074057889562000671575b50845180156200065a57885b818110620005c75750503388526003835285882087895283528588209085519067ffffffffffffffff8211620005b457680100000000000000008211620005b45750815481835580821062000561575b50838601918952838920908060031c928a5b848110620005135750600719821690910390816200049c575b50505050509060067f71b3c95c0b8611d2f0f9c4e492ebbcf20d034943bccac6ef1a71ae90e0f243fa92338852528386205416925180620004963394826200152a565b0390a480f35b908a948b905b828210620004dc5750505050015560067f71b3c95c0b8611d2f0f9c4e492ebbcf20d034943bccac6ef1a71ae90e0f243fa38808062000453565b9091929587620005076001928951851c908660021b60031b9163ffffffff809116831b921b19161790565b970193920190620004a2565b8b8c5b88600882106200052f575050848201556001016200043a565b62000557859360019396518b1c908760021b60031b9163ffffffff809116831b921b19161790565b9201930162000516565b6200059290838b52858b20600780850160031c820192601c8660021b168062000599575b500160031c0190620016a8565b3862000428565b60001990818601918254918c0360031b1c1690553862000585565b634e487b7160e01b8a5260419052602489fd5b6001600160e01b0319620005dc828962001753565b5116338b526005808752898c20828d528752878a8d2054166200062257338c528652888b20908b528552878a2080546001600160a01b0319168a179055600101620003d8565b338c528652888b20818c528652888b205489516361a2116b60e11b815290881681860152602481018b90526044810191909152606490fd5b86516362d3093d60e11b8152808301899052602490fd5b9094503d8089833e620006858183620015fe565b810183828203126200073c57815167ffffffffffffffff9283821162000721570181601f820112156200073857805192831162000725578260051b90885193620006d287840186620015fe565b8452858085019282010192831162000721578501905b828210620006fb575050509338620003cc565b81516001600160e01b0319811681036200071d578152908501908501620006e8565b8b80fd5b8a80fd5b634e487b7160e01b8a526041875260248afd5b8980fd5b8880fd5b86513d8a823e3d90fd5b845163963e961b60e01b81523381860152602490fd5b8480fd5b8380fd5b505050346200013157602036600319011262000131576020916001600160a01b039082908262000797620014c8565b168152600685522054169051908152f35b915050346200081257606036600319011262000812578160209360ff92620007cf620014c8565b620007d9620014e4565b620007e3620014fb565b6001600160a01b0392831685529288528484209082168452875283832091168252855220549151911615158152f35b8280fd5b9150503462000812576060366003190112620008125762000836620014c8565b9162000841620014e4565b90604435801515809103620008f557338652600660209081528287205490946001600160a01b0391821615620008de57907f4f2bd80fb4928b06abcd76e3b26209a615f0612f98dc4a8b176934b1a3899333939291338952865280838920971696878952865280838920951694858952865282882060ff1981541660ff84161790553388526006865282882054169482519485528401523392a480f35b60249084519063963e961b60e01b82523390820152fd5b8580fd5b8385346200096f57806003193601126200096f573381526006602052819020546001600160a01b0390811692836200094257602083836200093a336200177e565b915191168152f35b91516356352f0b60e11b8152339281019283526001600160a01b0390931660208301525081906040010390fd5b80fd5b91505034620008125760203660031901126200081257819062000994620014c8565b6001600160a01b03808216865260066020529290942054821680620009c25750506200093a6020936200177e565b92516356352f0b60e11b81526001600160a01b039485169181019182529390921660208301525081906040010390fd5b50505034620001315781600319360112620001315760018060a01b039182815416916001938454169381519280916002549162000a2f8362001571565b8087529282811690811562000aca575060011462000a7b575b505050829162000a616200012d946060930384620015fe565b805195869586526020860152840152606083019062001621565b925060028352600080516020620023e58339815191525b82841062000ab15750505082016020018162000a616200012d62000a48565b8054602085880181019190915290930192810162000a92565b60ff191660208089019190915293151560051b8701909301935084925062000a6191506200012d905062000a48565b84848334620008125781600319360112620008125762000b18620014c8565b62000b22620014e4565b8351638da5cb5b60e01b815260209691926001600160a01b03928891839190829086165afa90811562000c8d57908291879162000c59575b5016855260038652838520911684528452818320908251809486845492838152019381528681205b82600783011062000bea579287926200020195926200012d9989965493838310620002d75750828210620002b85782821062000299578282106200027a578282106200025b578282106200023e57828210620002215750106200020c575090500383620015fe565b80546001600160e01b031981861b8116875260c082811b82168b89015260a083811b83168a8a0152608084811b84166060808c019190915285901b8416908a0152838a1b831690890152828b1b8216908801521685850152610100909401936008919091019060010162000b82565b62000c7e9150883d8a1162000c85575b62000c758183620015fe565b81019062001663565b8862000b5a565b503d62000c69565b85513d88823e3d90fd5b5050503462000131578060031936011262000131576020918162000cba620014c8565b9162000cc562001512565b6001600160a01b039384168252600586528282206001600160e01b0319909116825285522054915191168152f35b8385346200096f5760603660031901126200096f5762000d12620014c8565b62000d1c620014e4565b9062000d27620014fb565b91845195638da5cb5b60e01b87526020968781838160018060a01b038098165afa90811562000daa5791848896949288969460ff999162000d88575b5016855288528185852091168452875283832091168252855220541690519015158152f35b62000da391508b3d8d1162000c855762000c758183620015fe565b8b62000d63565b87513d88823e3d90fd5b92915034620007645760209283600319360112620007605762000dd6620014c8565b338652600685528386205490946001600160a01b03929091831615620010e257338752600382528285882096169586885282528487209385519083829687928282549586815201918c52828c20948c5b8b8260078301106200106757508462000e8e97549383831062001049575b508282106200102a575b8282106200100b575b82821062000fec575b82821062000fcd575b82821062000fb0575b82821062000f93575b501062000f7d575b5090500385620015fe565b835190811562000f665750865b81811062000f255750509060067f0971095f3fa0f917fd0ad98319d7cdb8e837d8c98d5e47fb2700e8b68ac961c99233885260038152858820878952815285882080548982558062000f03575b5050338852528386205416925180620004963394826200152a565b62000f1d918a526007838b20910160031c810190620016a8565b388062000ee8565b33885260058352858820600191906001600160e01b031962000f48838962001753565b51168a52845286892080546001600160a01b03191690550162000e9b565b85516338d4b05760e01b8152908101879052602490fd5b6001600160e01b03191681528591013862000e83565b83811b6001600160e01b0319168552909301926001018462000e7b565b838d1b6001600160e01b0319168552909301926001018462000e72565b606084901b6001600160e01b0319168552909301926001018462000e69565b608084901b6001600160e01b0319168552909301926001018462000e60565b60a084901b6001600160e01b0319168552909301926001018462000e57565b60c084901b6001600160e01b0319168552909301926001018462000e4e565b84901b6001600160e01b031916855290930192600101843862000e44565b836008949750600193966101009396928a549283809363ffffffff60e01b809681941b16875260c0938383861b1682890152838360a0928282851b16818c0152608083838d606090818484871b169101521b16908c01521b16908801521b169084015216878201520195019101928692899495929562000e26565b60249085519063963e961b60e01b82523390820152fd5b8385346200096f57816003193601126200096f5762001117620014c8565b906200112262001512565b8351638da5cb5b60e01b81526020956001600160a01b039487918391829088165afa908115620011a15783929185918795916200117f575b50168252600586528282206001600160e01b0319909116825285522054915191168152f35b6200119a9150883d8a1162000c855762000c758183620015fe565b886200115a565b85513d85823e3d90fd5b908486346200096f57816003193601126200096f57620011ca620014c8565b9060243567ffffffffffffffff94858211620008125736602383011215620008125781810135958087116200076457366024888501011162000764573384526020966006885260018060a01b0398898887205416806200149e5750506200124987513360601b8a820152601481526200124381620015e1565b62001684565b93620012cd8a808a516200125d81620015ae565b338152818d82019b168b52898d8d5197806024601f199962001287858c601f860116018d620015fe565b828c5201838b013788010152858c8201525116986001600160601b0360a01b998a8a5416178955511660018060a01b03166001600160601b0360a01b6001541617600155565b8151918383116200148b57908291620012e860025462001571565b8b601f82116200143e575b50508a91601f8411600114620013cc57508792620013c0575b50508160011b916000199060031b1c1916176002555b8551916109f1908184019284841090841117620013ad5750908291620019f48339039083f5948515620013a1578394951693849162001360620016c1565b338152600687522091825416179055805182815233907f2d8895d948115783fa362a57339c4c179365fafeafdd7dca66364ae296f50b75853392a351908152f35b508251903d90823e3d90fd5b634e487b7160e01b865260419052602485fd5b015190508a806200130c565b60028952600080516020620023e58339815191529316885b8c828210620014275750509084600195949392106200140d575b505050811b0160025562001322565b015160001960f88460031b161c191690558a8080620013fe565b6001859682939686015181550195019301620013e4565b620014789160028b52600080516020620023e583398151915290601f870160051c820192871062001480575b601f0160051c0190620016a8565b8c8b620012f3565b90915081906200146a565b634e487b7160e01b875260418552602487fd5b6356352f0b60e11b8252338583019081526001600160a01b03909116602082015281906040010390fd5b600435906001600160a01b0382168203620014df57565b600080fd5b602435906001600160a01b0382168203620014df57565b604435906001600160a01b0382168203620014df57565b602435906001600160e01b031982168203620014df57565b6020908160408183019282815285518094520193019160005b82811062001552575050505090565b83516001600160e01b0319168552938101939281019260010162001543565b90600182811c92168015620015a3575b60208310146200158d57565b634e487b7160e01b600052602260045260246000fd5b91607f169162001581565b6060810190811067ffffffffffffffff821117620015cb57604052565b634e487b7160e01b600052604160045260246000fd5b6040810190811067ffffffffffffffff821117620015cb57604052565b90601f8019910116810190811067ffffffffffffffff821117620015cb57604052565b919082519283825260005b8481106200164e575050826000602080949584010152601f8019910116010190565b6020818301810151848301820152016200162c565b90816020910312620014df57516001600160a01b0381168103620014df5790565b60208151910151906020811062001699575090565b6000199060200360031b1b1690565b818110620016b4575050565b60008155600101620016a8565b60008080556001818155620016d860025462001571565b9081620016e457505050565b601f8211600114620016f7575050600255565b60028352601f600080516020620023e5833981519152920160051c82017f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5acf5b81811062001748575050508160025555565b848155820162001736565b8051821015620017685760209160051b010190565b634e487b7160e01b600052603260045260246000fd5b9060409182519060018060a01b03620017b8818316936020936001600160601b03199060601b1684820152601481526200124381620015e1565b90855190620017c782620015ae565b8482528382019260009283855288518681019167ffffffffffffffff9282811084821117620019df57856200183792848e839481528a82528201525116976001600160601b0360a01b9889895416178855511660018060a01b03166001600160601b0360a01b6001541617600155565b805190828211620019085781906200185160025462001571565b601f81116200199f575b508890601f8311600114620019285787926200191c575b50508160011b916000199060031b1c1916176002555b8851906109f1808301918211838310176200190857908291620019f48339039084f58015620018fe57917f2d8895d948115783fa362a57339c4c179365fafeafdd7dca66364ae296f50b759391889316978891620018e5620016c1565b87815260068752209182541617905551918583523392a3565b87513d84823e3d90fd5b634e487b7160e01b86526041600452602486fd5b01519050388062001872565b60028852600080516020620023e58339815191529250601f198416885b8b828210620019885750509084600195949392106200196e575b505050811b0160025562001888565b015160001960f88460031b161c191690553880806200195f565b600185968293968601518155019501930162001945565b620019d89060028952600080516020620023e5833981519152601f850160051c8101918c86106200148057601f0160051c0190620016a8565b386200185b565b634e487b7160e01b87526041600452602487fdfe60c08060405234620000ca573360a0526366b0182d60e01b8152600081600481335afa8015620000c4576000809281926200009b575b506080526001600160a01b03821662000087575b60405161068e9081620003638239608051818181610192015281816102c401526104a5015260a05181818161014d0152818161031301526105250152f35b6200009291620002b2565b50388062000049565b909150620000bb92503d8092823e620000b482620000e5565b016200018e565b90913862000035565b62000221565b600080fd5b634e487b7160e01b600052604160045260246000fd5b60c0601f91909101601f19168101906001600160401b038211908210176200010c57604052565b620000cf565b601f909101601f19168101906001600160401b038211908210176200010c57604052565b60e051906001600160a01b0382168203620000ca57565b6001600160401b0381116200010c57601f01601f191660200190565b60005b8381106200017d5750506000910152565b81810151838201526020016200016c565b606060bf19820112620000ca5760c0516001600160a01b0381168103620000ca5791620001ba62000136565b610100519092906001600160401b038111620000ca578160df82011215620000ca578060c00151620001ec816200014d565b92620001fc604051948562000112565b81845260e08284010111620000ca576200021e9160e060208501910162000169565b90565b6040513d6000823e3d90fd5b3d156200025d573d9062000241826200014d565b9162000251604051938462000112565b82523d6000602084013e565b606090565b906020916200027d8151809281855285808601910162000169565b601f01601f1916010190565b9091620002a36200021e9360408452604084019062000262565b91602081840391015262000262565b9190823b156200034157600080825160208401865af4907fb24ebe141c5f2a744b103bea65fce6c40e0dc65d7341d092c09b160f40447990620002f46200022d565b60405190956001600160a01b0316928190620003139088908362000289565b0390a2156200031e57565b508051156200032f57602081519101fd5b60405163061a160d60e41b8152600490fd5b604051636d17e5ef60e11b81526001600160a01b0384166004820152602490fdfe60806040526004361015610027575b36156100255761001d366102b5565b602081519101f35b005b6000803560e01c9081631cff79cd1461005a575080637b1039991461005557638da5cb5b0361000e5761017c565b610137565b60403660031901126100ca57600435610072816100cd565b60243567ffffffffffffffff928382116100ca57366023830112156100ca5781600401359384116100ca5736602485840101116100ca576100c66100ba8560248501866104a2565b60405191829182610123565b0390f35b80fd5b6001600160a01b038116036100de57565b600080fd5b919082519283825260005b84811061010f575050826000602080949584010152601f8019910116010190565b6020818301810151848301820152016100ee565b9060206101349281815201906100e3565b90565b346100de5760003660031901126100de576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b346100de5760003660031901126100de576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b634e487b7160e01b600052604160045260246000fd5b90601f8019910116810190811067ffffffffffffffff8211176101f957604052565b6101c1565b908160209103126100de5751610134816100cd565b6040513d6000823e3d90fd5b908160008237016000815290565b67ffffffffffffffff81116101f957601f01601f191660200190565b3d15610274573d9061025a8261022d565b9161026860405193846101d7565b82523d6000602084013e565b606090565b606090610134939260408252806040830152806000848401376000838284010152601f80199101168101906020838284030191015201906100e3565b6040516361be485960e01b81527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0381811660048401526001600160e01b03196000351660248401819052939493906020846044817f000000000000000000000000000000000000000000000000000000000000000085165afa93841561044e5760009461041e575b5083169182156103e357505060008060405180610362818961021f565b0390845af4907fc4dabe0d7ef7462e2218f2c398c21ef217803e1c46f5cf802d1a5d1d9b503f2f610391610249565b80966103a260405192839283610279565b0390a2156103ad5750565b8251909150156103c05750805190602001fd5b60405163023c045d60e21b81526001600160a01b03919091166004820152602490fd5b604051638848730f60e01b81523360048201526001600160a01b039190911660248201526001600160e01b0319919091166044820152606490fd5b61044091945060203d8111610447575b61043881836101d7565b8101906101fe565b9238610345565b503d61042e565b610213565b908160209103126100de575180151581036100de5790565b9291926104778261022d565b9161048560405193846101d7565b8294818452818301116100de578281602093846000960137010152565b917f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0333818316036104ee575b505061013492916104e891369161046b565b906105e5565b60405163b31d1b9960e01b81526001600160a01b0383811660048301523360248301528616604482015290602090829060649082907f0000000000000000000000000000000000000000000000000000000000000000165afa90811561044e57600091610592575b501561056257806104d6565b6040516355d1750960e01b81526001600160a01b0391821660048201523360248201529084166044820152606490fd5b6105b3915060203d81116105b9575b6105ab81836101d7565b810190610453565b38610556565b503d6105a1565b90916105d7610134936040845260408401906100e3565b9160208184039101526100e3565b9190823b1561066d57600080825160208401865af4907fb24ebe141c5f2a744b103bea65fce6c40e0dc65d7341d092c09b160f40447990610624610249565b60405190956001600160a01b0316928190610641908890836105c0565b0390a21561064b57565b5080511561065b57602081519101fd5b60405163061a160d60e41b8152600490fd5b604051636d17e5ef60e11b81526001600160a01b0384166004820152602490fd405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace"; /*////////////////////////////////////////////////////////////////////////// DEPLOYERS From 30cc24c5999ce154905949279e6411002b184b8d Mon Sep 17 00:00:00 2001 From: Paul Razvan Berg Date: Sat, 24 Jun 2023 12:56:50 +0300 Subject: [PATCH 2/2] feat: add "deployAndInstallPlugin" docs: polish NatSpec comments --- src/PRBProxyRegistry.sol | 73 +++++++++++-------- src/interfaces/IPRBProxyRegistry.sol | 26 +++++-- test/mocks/plugins/PluginCollider.sol | 3 +- .../deploy-and-execute/deployAndExecute.tree | 2 +- .../deployAndInstallPlugin.t.sol | 59 +++++++++++++++ .../deployAndInstallPlugin.tree | 9 +++ test/registry/deploy-for/deployFor.tree | 2 +- test/registry/deploy/deploy.tree | 2 +- .../install-plugin/installPlugin.t.sol | 42 +++++++++-- .../install-plugin/installPlugin.tree | 13 ++-- test/utils/Precompiles.sol | 2 +- 11 files changed, 180 insertions(+), 53 deletions(-) create mode 100644 test/registry/deploy-and-install-plugin/deployAndInstallPlugin.t.sol create mode 100644 test/registry/deploy-and-install-plugin/deployAndInstallPlugin.tree diff --git a/src/PRBProxyRegistry.sol b/src/PRBProxyRegistry.sol index 9b11c46..9e2ff69 100644 --- a/src/PRBProxyRegistry.sol +++ b/src/PRBProxyRegistry.sol @@ -178,38 +178,13 @@ contract PRBProxyRegistry is IPRBProxyRegistry { /// @inheritdoc IPRBProxyRegistry function installPlugin(IPRBProxyPlugin plugin) external override onlyCallerWithProxy { - // Retrieve the methods to install. - bytes4[] memory methods = plugin.getMethods(); - - // The plugin must have at least one listed method. - uint256 length = methods.length; - if (length == 0) { - revert PRBProxyRegistry_PluginWithZeroMethods(plugin); - } - - // Install every method in the list. - address owner = msg.sender; - for (uint256 i = 0; i < length;) { - // Check for collisions. - bytes4 method = methods[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; - } - } - - // Set the methods in the reverse mapping. - _methods[owner][plugin] = methods; + _installPlugin(plugin); + } - // Log the plugin installation. - emit InstallPlugin(owner, _proxies[owner], plugin, methods); + /// @inheritdoc IPRBProxyRegistry + function deployAndInstallPlugin(IPRBProxyPlugin plugin) external returns (IPRBProxy proxy) { + proxy = _deploy({ owner: msg.sender }); + _installPlugin(plugin); } /// @inheritdoc IPRBProxyRegistry @@ -268,4 +243,40 @@ contract PRBProxyRegistry is IPRBProxyRegistry { // Log the creation of the proxy. emit DeployProxy({ operator: msg.sender, owner: owner, proxy: proxy }); } + + /// @dev See the documentation for the user-facing functions that call this internal function. + function _installPlugin(IPRBProxyPlugin plugin) internal { + // Retrieve the methods to install. + bytes4[] memory methods = plugin.getMethods(); + + // The plugin must implement at least one method. + uint256 length = methods.length; + if (length == 0) { + revert PRBProxyRegistry_PluginWithZeroMethods(plugin); + } + + // Install every method in the list. + address owner = msg.sender; + for (uint256 i = 0; i < length;) { + // Check for collisions. + bytes4 method = methods[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; + } + } + + // Set the methods in the reverse mapping. + _methods[owner][plugin] = methods; + + // Log the plugin installation. + emit InstallPlugin(owner, _proxies[owner], plugin, methods); + } } diff --git a/src/interfaces/IPRBProxyRegistry.sol b/src/interfaces/IPRBProxyRegistry.sol index 2de0dd1..69081b1 100644 --- a/src/interfaces/IPRBProxyRegistry.sol +++ b/src/interfaces/IPRBProxyRegistry.sol @@ -5,8 +5,9 @@ import { IPRBProxy } from "./IPRBProxy.sol"; import { IPRBProxyPlugin } from "./IPRBProxyPlugin.sol"; /// @title IPRBProxyRegistry -/// @notice Deploys new proxies with CREATE2 and keeps a registry of owners to proxies. Owners can only -/// have only one proxy at a time. +/// @notice Deploys new proxies via CREATE2 and keeps a registry of owners to proxies. Proxies can only be deployed +/// once per owner, and they cannot be transferred. The registry also supports installing plugins, which are used +/// for extending the functionality of the proxy. interface IPRBProxyRegistry { /*////////////////////////////////////////////////////////////////////////// ERRORS @@ -144,7 +145,7 @@ interface IPRBProxyRegistry { NON-CONSTANT FUNCTIONS //////////////////////////////////////////////////////////////////////////*/ - /// @notice Deploys a new proxy with CREATE2, using the caller as the owner. + /// @notice Deploys a new proxy for the caller. /// /// @dev Emits a {DeployProxy} event. /// @@ -154,9 +155,8 @@ interface IPRBProxyRegistry { /// @return proxy The address of the newly deployed proxy. function deploy() external returns (IPRBProxy proxy); - /// @notice Deploys a new proxy via CREATE2, using the caller as the owner. It delegate calls to the provided - /// target by forwarding the data. Then, it returns the data it gets back, and bubbles up any potential - /// revert. + /// @notice Deploys a new proxy for the caller, and delegate calls to the provided target 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. /// @@ -169,7 +169,19 @@ interface IPRBProxyRegistry { /// @return proxy The address of the newly deployed proxy. function deployAndExecute(address target, bytes calldata data) external returns (IPRBProxy proxy); - /// @notice Deploys a new proxy with CREATE2 for the provided owner. + /// @notice Deploys a new proxy for the caller, and installs the provided plugin on the newly deployed proxy. + /// + /// @dev Emits a {DeployProxy} and an {InstallPlugin} event. + /// + /// Requirements: + /// - The caller must not have a proxy. + /// - See the requirements in `installPlugin`. + /// + /// @param plugin The address of the plugin to install. + /// @return proxy The address of the newly deployed proxy. + function deployAndInstallPlugin(IPRBProxyPlugin plugin) external returns (IPRBProxy proxy); + + /// @notice Deploys a new proxy for the provided owner. /// /// @dev Emits a {DeployProxy} event. /// diff --git a/test/mocks/plugins/PluginCollider.sol b/test/mocks/plugins/PluginCollider.sol index 21accfd..8776318 100644 --- a/test/mocks/plugins/PluginCollider.sol +++ b/test/mocks/plugins/PluginCollider.sol @@ -11,7 +11,8 @@ contract PluginCollider is IPRBProxyPlugin { } /// @dev The selector for this method is 0x72eba203, which is the same as the selector for - /// `onStreamCanceled(uint256,address,uint128,uint128)` + /// `onStreamCanceled(uint256,address,uint128,uint128)`. + /// See https://github.com/zobront/4byte-collider function onAddictionFeesRefunded(uint248 loanId, int168, uint192 feeAmount, int248) external pure { loanId; feeAmount; diff --git a/test/registry/deploy-and-execute/deployAndExecute.tree b/test/registry/deploy-and-execute/deployAndExecute.tree index ae3a7dc..1d28f2c 100644 --- a/test/registry/deploy-and-execute/deployAndExecute.tree +++ b/test/registry/deploy-and-execute/deployAndExecute.tree @@ -2,7 +2,7 @@ deployAndExecute.t.sol ├── when the owner has a proxy │ └── it should revert └── when the owner does not have a proxy - ├── it should generate the correct address + ├── it should generate the correct proxy address ├── it should initialize the owner ├── it should update the proxies mapping ├── it should delegate call to the target contract diff --git a/test/registry/deploy-and-install-plugin/deployAndInstallPlugin.t.sol b/test/registry/deploy-and-install-plugin/deployAndInstallPlugin.t.sol new file mode 100644 index 0000000..842eea2 --- /dev/null +++ b/test/registry/deploy-and-install-plugin/deployAndInstallPlugin.t.sol @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity >=0.8.19 <=0.9.0; + +import { IPRBProxy } from "src/interfaces/IPRBProxy.sol"; +import { IPRBProxyPlugin } from "src/interfaces/IPRBProxyPlugin.sol"; +import { IPRBProxyRegistry } from "src/interfaces/IPRBProxyRegistry.sol"; + +import { Registry_Test } from "../Registry.t.sol"; + +/// @dev User roles: +/// - Bob is the origin, operator, and owner of the proxy +contract DeployAndInstallPlugin_Test is Registry_Test { + function setUp() public override { + Registry_Test.setUp(); + } + + function test_RevertWhen_OwnerHasProxy() external { + IPRBProxy proxy = registry.deploy(); + vm.expectRevert( + abi.encodeWithSelector(IPRBProxyRegistry.PRBProxyRegistry_OwnerHasProxy.selector, users.alice, proxy) + ); + registry.deploy(); + } + + modifier whenOwnerDoesNotHaveProxy() { + _; + } + + function testFuzz_DeployAndInstallPlugin_ProxyAddress(address owner) external whenOwnerDoesNotHaveProxy { + changePrank({ msgSender: owner }); + address actualProxy = address(registry.deployAndInstallPlugin(plugins.basic)); + address expectedProxy = computeProxyAddress(owner); + assertEq(actualProxy, expectedProxy, "deployed proxy address mismatch"); + } + + function testFuzz_DeployAndInstallPlugin_PluginMethods() external whenOwnerDoesNotHaveProxy { + registry.deployAndInstallPlugin(plugins.basic); + bytes4[] memory pluginMethods = plugins.basic.getMethods(); + for (uint256 i = 0; i < pluginMethods.length; ++i) { + IPRBProxyPlugin actualPlugin = registry.getPluginByOwner({ owner: users.alice, method: pluginMethods[i] }); + IPRBProxyPlugin expectedPlugin = plugins.basic; + assertEq(actualPlugin, expectedPlugin, "plugin method not installed"); + } + } + + function testFuzz_DeployAndInstallPlugin_ReverseMapping() external whenOwnerDoesNotHaveProxy { + registry.deployAndInstallPlugin(plugins.basic); + bytes4[] memory actualMethods = registry.getMethodsByOwner({ owner: users.alice, plugin: plugins.basic }); + bytes4[] memory expectedMethods = plugins.basic.getMethods(); + assertEq(actualMethods, expectedMethods, "methods not saved in reverse mapping"); + } + + function testFuzz_DeployAndInstallPlugin_Event(address owner) external whenOwnerDoesNotHaveProxy { + changePrank({ msgSender: owner }); + vm.expectEmit({ emitter: address(registry) }); + emit DeployProxy({ operator: owner, owner: owner, proxy: IPRBProxy(computeProxyAddress(owner)) }); + registry.deploy(); + } +} diff --git a/test/registry/deploy-and-install-plugin/deployAndInstallPlugin.tree b/test/registry/deploy-and-install-plugin/deployAndInstallPlugin.tree new file mode 100644 index 0000000..00a0415 --- /dev/null +++ b/test/registry/deploy-and-install-plugin/deployAndInstallPlugin.tree @@ -0,0 +1,9 @@ +deployAndInstallPlugin.t.sol +├── when the owner has a proxy +│ └── it should revert +└── when the owner does not have a proxy + ├── it should generate the correct proxy address + ├── it should install the plugin + ├── it should save the methods in the reverse mapping + ├── it should emit a {DeployProxy} event + └── it should emit an {InstallPlugin} event diff --git a/test/registry/deploy-for/deployFor.tree b/test/registry/deploy-for/deployFor.tree index 7c30243..acdc111 100644 --- a/test/registry/deploy-for/deployFor.tree +++ b/test/registry/deploy-for/deployFor.tree @@ -2,7 +2,7 @@ deployFor.t.sol ├── when the owner has a proxy │ └── it should revert └── when the owner does not have a proxy - ├── it should generate the correct address + ├── it should generate the correct proxy address ├── it should initialize the owner ├── it should update the proxies mapping └── it should emit a {DeployProxy} event diff --git a/test/registry/deploy/deploy.tree b/test/registry/deploy/deploy.tree index bcf86a0..f4da0c6 100644 --- a/test/registry/deploy/deploy.tree +++ b/test/registry/deploy/deploy.tree @@ -2,7 +2,7 @@ deploy.t.sol ├── when the owner has a proxy │ └── it should revert └── when the owner does not have a proxy - ├── it should generate the correct address + ├── it should generate the correct proxy address ├── it should initialize the owner ├── it should update the proxies mapping └── it should emit a {DeployProxy} event diff --git a/test/registry/install-plugin/installPlugin.t.sol b/test/registry/install-plugin/installPlugin.t.sol index 9029276..714802b 100644 --- a/test/registry/install-plugin/installPlugin.t.sol +++ b/test/registry/install-plugin/installPlugin.t.sol @@ -31,7 +31,34 @@ contract InstallPlugin_Test is Registry_Test { _; } - function test_RevertWhen_MethodCollisions() external whenCallerHasProxy whenPluginImplementsMethods { + function test_RevertWhen_PluginAlreadyInstalled() + external + whenCallerHasProxy + whenPluginImplementsMethods + whenPluginNotAlreadyInstalled + { + registry.installPlugin(plugins.basic); + vm.expectRevert( + abi.encodeWithSelector( + IPRBProxyRegistry.PRBProxyRegistry_PluginMethodCollision.selector, + plugins.basic, + plugins.basic, + plugins.basic.foo.selector + ) + ); + registry.installPlugin(plugins.basic); + } + + modifier whenPluginNotAlreadyInstalled() { + _; + } + + function test_RevertWhen_MethodCollisions() + external + whenCallerHasProxy + whenPluginImplementsMethods + whenPluginNotAlreadyInstalled + { registry.installPlugin(plugins.sablier); vm.expectRevert( abi.encodeWithSelector( @@ -48,11 +75,14 @@ contract InstallPlugin_Test is Registry_Test { _; } - function test_InstallPlugin() external whenCallerHasProxy whenPluginImplementsMethods whenNoMethodCollisions { - // Install a basic plugin with some methods. + function test_InstallPlugin() + external + whenCallerHasProxy + whenPluginImplementsMethods + whenPluginNotAlreadyInstalled + whenNoMethodCollisions + { registry.installPlugin(plugins.basic); - - // Assert that every plugin method has been installed. bytes4[] memory pluginMethods = plugins.basic.getMethods(); for (uint256 i = 0; i < pluginMethods.length; ++i) { IPRBProxyPlugin actualPlugin = registry.getPluginByOwner({ owner: users.alice, method: pluginMethods[i] }); @@ -65,6 +95,7 @@ contract InstallPlugin_Test is Registry_Test { external whenCallerHasProxy whenPluginImplementsMethods + whenPluginNotAlreadyInstalled whenNoMethodCollisions { registry.installPlugin(plugins.basic); @@ -77,6 +108,7 @@ contract InstallPlugin_Test is Registry_Test { external whenCallerHasProxy whenPluginImplementsMethods + whenPluginNotAlreadyInstalled whenNoMethodCollisions { vm.expectEmit({ emitter: address(registry) }); diff --git a/test/registry/install-plugin/installPlugin.tree b/test/registry/install-plugin/installPlugin.tree index 6360b3c..7b816e2 100644 --- a/test/registry/install-plugin/installPlugin.tree +++ b/test/registry/install-plugin/installPlugin.tree @@ -5,9 +5,12 @@ installPlugin.t.sol ├── when the plugin doesn't implement any method │ └── it should revert └── when the plugin implements at least one method - ├── when there are method collisions + ├── when the plugin is already installed │ └── it should revert - └── when there aren't any method collisions - ├── it should install the plugin - ├── it should save the methods in the reverse mapping - └── it should emit an {InstallPlugin} event + └── when the plugin is not already installed + ├── when there are method collisions + │ └── it should revert + └── when there aren't any method collisions + ├── it should install the plugin + ├── it should save the methods in the reverse mapping + └── it should emit an {InstallPlugin} event diff --git a/test/utils/Precompiles.sol b/test/utils/Precompiles.sol index d99073e..8c80cc2 100644 --- a/test/utils/Precompiles.sol +++ b/test/utils/Precompiles.sol @@ -12,7 +12,7 @@ contract Precompiles { //////////////////////////////////////////////////////////////////////////*/ bytes public constant BYTECODE_REGISTRY = - hex"6080806040523461001657612405908161001c8239f35b600080fdfe60806040818152600491823610156200001757600080fd5b60009260e0908435821c928363092af81314620011ab575082631ddef5b914620010f95782634bddd93a1462000db45782635cabcdf71462000cf357826361be48591462000c975782636383afb21462000af957826366b0182d14620009f257826374912cd21462000972578263775c300c14620008f9578263aa4b826a1462000816578263b31d1b9914620007a8578263b7fba4d31462000768578263b9678403146200036457508163fa557e931462000135575063ffa1ad7414620000dd57600080fd5b34620001315781600319360112620001315780516200012d916200010182620015e1565b600c82526b342e302e302d626574612e3560a01b60208301525191829160208352602083019062001621565b0390f35b5080fd5b838334620001315780600319360112620001315762000153620014c8565b926200015e620014e4565b60018060a01b03809516845260209460038652838520911684528452818320908251809486845492838152019381528681205b826007830110620002f5579287926200020195926200012d9989965493838310620002d7575b50828210620002b8575b82821062000299575b8282106200027a575b8282106200025b575b8282106200023e575b82821062000221575b50106200020c575b5090500383620015fe565b51918291826200152a565b6001600160e01b0319168152018087620001f6565b83811b6001600160e01b03191685529093019260010184620001ee565b83891b6001600160e01b03191685529093019260010184620001e5565b606084901b6001600160e01b03191685529093019260010184620001dc565b608084901b6001600160e01b03191685529093019260010184620001d3565b60a084901b6001600160e01b03191685529093019260010184620001ca565b60c084901b6001600160e01b03191685529093019260010184620001c1565b84901b6001600160e01b031916855290930192600101848b620001b7565b80546001600160e01b031981861b8116875260c082811b82168b89015260a083811b83168a8a0152608084811b84166060808c019190915285901b8416908a0152838a1b831690890152828b1b8216908801521685850152610100909401936008919091019060010162000191565b90929150346200076457602080600319360112620007605762000386620014c8565b33865260068252838620546001600160a01b039291908316156200074a57821694845193631abad16560e21b855287858281838b5af19485156200074057889562000671575b50845180156200065a57885b818110620005c75750503388526003835285882087895283528588209085519067ffffffffffffffff8211620005b457680100000000000000008211620005b45750815481835580821062000561575b50838601918952838920908060031c928a5b848110620005135750600719821690910390816200049c575b50505050509060067f71b3c95c0b8611d2f0f9c4e492ebbcf20d034943bccac6ef1a71ae90e0f243fa92338852528386205416925180620004963394826200152a565b0390a480f35b908a948b905b828210620004dc5750505050015560067f71b3c95c0b8611d2f0f9c4e492ebbcf20d034943bccac6ef1a71ae90e0f243fa38808062000453565b9091929587620005076001928951851c908660021b60031b9163ffffffff809116831b921b19161790565b970193920190620004a2565b8b8c5b88600882106200052f575050848201556001016200043a565b62000557859360019396518b1c908760021b60031b9163ffffffff809116831b921b19161790565b9201930162000516565b6200059290838b52858b20600780850160031c820192601c8660021b168062000599575b500160031c0190620016a8565b3862000428565b60001990818601918254918c0360031b1c1690553862000585565b634e487b7160e01b8a5260419052602489fd5b6001600160e01b0319620005dc828962001753565b5116338b526005808752898c20828d528752878a8d2054166200062257338c528652888b20908b528552878a2080546001600160a01b0319168a179055600101620003d8565b338c528652888b20818c528652888b205489516361a2116b60e11b815290881681860152602481018b90526044810191909152606490fd5b86516362d3093d60e11b8152808301899052602490fd5b9094503d8089833e620006858183620015fe565b810183828203126200073c57815167ffffffffffffffff9283821162000721570181601f820112156200073857805192831162000725578260051b90885193620006d287840186620015fe565b8452858085019282010192831162000721578501905b828210620006fb575050509338620003cc565b81516001600160e01b0319811681036200071d578152908501908501620006e8565b8b80fd5b8a80fd5b634e487b7160e01b8a526041875260248afd5b8980fd5b8880fd5b86513d8a823e3d90fd5b845163963e961b60e01b81523381860152602490fd5b8480fd5b8380fd5b505050346200013157602036600319011262000131576020916001600160a01b039082908262000797620014c8565b168152600685522054169051908152f35b915050346200081257606036600319011262000812578160209360ff92620007cf620014c8565b620007d9620014e4565b620007e3620014fb565b6001600160a01b0392831685529288528484209082168452875283832091168252855220549151911615158152f35b8280fd5b9150503462000812576060366003190112620008125762000836620014c8565b9162000841620014e4565b90604435801515809103620008f557338652600660209081528287205490946001600160a01b0391821615620008de57907f4f2bd80fb4928b06abcd76e3b26209a615f0612f98dc4a8b176934b1a3899333939291338952865280838920971696878952865280838920951694858952865282882060ff1981541660ff84161790553388526006865282882054169482519485528401523392a480f35b60249084519063963e961b60e01b82523390820152fd5b8580fd5b8385346200096f57806003193601126200096f573381526006602052819020546001600160a01b0390811692836200094257602083836200093a336200177e565b915191168152f35b91516356352f0b60e11b8152339281019283526001600160a01b0390931660208301525081906040010390fd5b80fd5b91505034620008125760203660031901126200081257819062000994620014c8565b6001600160a01b03808216865260066020529290942054821680620009c25750506200093a6020936200177e565b92516356352f0b60e11b81526001600160a01b039485169181019182529390921660208301525081906040010390fd5b50505034620001315781600319360112620001315760018060a01b039182815416916001938454169381519280916002549162000a2f8362001571565b8087529282811690811562000aca575060011462000a7b575b505050829162000a616200012d946060930384620015fe565b805195869586526020860152840152606083019062001621565b925060028352600080516020620023e58339815191525b82841062000ab15750505082016020018162000a616200012d62000a48565b8054602085880181019190915290930192810162000a92565b60ff191660208089019190915293151560051b8701909301935084925062000a6191506200012d905062000a48565b84848334620008125781600319360112620008125762000b18620014c8565b62000b22620014e4565b8351638da5cb5b60e01b815260209691926001600160a01b03928891839190829086165afa90811562000c8d57908291879162000c59575b5016855260038652838520911684528452818320908251809486845492838152019381528681205b82600783011062000bea579287926200020195926200012d9989965493838310620002d75750828210620002b85782821062000299578282106200027a578282106200025b578282106200023e57828210620002215750106200020c575090500383620015fe565b80546001600160e01b031981861b8116875260c082811b82168b89015260a083811b83168a8a0152608084811b84166060808c019190915285901b8416908a0152838a1b831690890152828b1b8216908801521685850152610100909401936008919091019060010162000b82565b62000c7e9150883d8a1162000c85575b62000c758183620015fe565b81019062001663565b8862000b5a565b503d62000c69565b85513d88823e3d90fd5b5050503462000131578060031936011262000131576020918162000cba620014c8565b9162000cc562001512565b6001600160a01b039384168252600586528282206001600160e01b0319909116825285522054915191168152f35b8385346200096f5760603660031901126200096f5762000d12620014c8565b62000d1c620014e4565b9062000d27620014fb565b91845195638da5cb5b60e01b87526020968781838160018060a01b038098165afa90811562000daa5791848896949288969460ff999162000d88575b5016855288528185852091168452875283832091168252855220541690519015158152f35b62000da391508b3d8d1162000c855762000c758183620015fe565b8b62000d63565b87513d88823e3d90fd5b92915034620007645760209283600319360112620007605762000dd6620014c8565b338652600685528386205490946001600160a01b03929091831615620010e257338752600382528285882096169586885282528487209385519083829687928282549586815201918c52828c20948c5b8b8260078301106200106757508462000e8e97549383831062001049575b508282106200102a575b8282106200100b575b82821062000fec575b82821062000fcd575b82821062000fb0575b82821062000f93575b501062000f7d575b5090500385620015fe565b835190811562000f665750865b81811062000f255750509060067f0971095f3fa0f917fd0ad98319d7cdb8e837d8c98d5e47fb2700e8b68ac961c99233885260038152858820878952815285882080548982558062000f03575b5050338852528386205416925180620004963394826200152a565b62000f1d918a526007838b20910160031c810190620016a8565b388062000ee8565b33885260058352858820600191906001600160e01b031962000f48838962001753565b51168a52845286892080546001600160a01b03191690550162000e9b565b85516338d4b05760e01b8152908101879052602490fd5b6001600160e01b03191681528591013862000e83565b83811b6001600160e01b0319168552909301926001018462000e7b565b838d1b6001600160e01b0319168552909301926001018462000e72565b606084901b6001600160e01b0319168552909301926001018462000e69565b608084901b6001600160e01b0319168552909301926001018462000e60565b60a084901b6001600160e01b0319168552909301926001018462000e57565b60c084901b6001600160e01b0319168552909301926001018462000e4e565b84901b6001600160e01b031916855290930192600101843862000e44565b836008949750600193966101009396928a549283809363ffffffff60e01b809681941b16875260c0938383861b1682890152838360a0928282851b16818c0152608083838d606090818484871b169101521b16908c01521b16908801521b169084015216878201520195019101928692899495929562000e26565b60249085519063963e961b60e01b82523390820152fd5b8385346200096f57816003193601126200096f5762001117620014c8565b906200112262001512565b8351638da5cb5b60e01b81526020956001600160a01b039487918391829088165afa908115620011a15783929185918795916200117f575b50168252600586528282206001600160e01b0319909116825285522054915191168152f35b6200119a9150883d8a1162000c855762000c758183620015fe565b886200115a565b85513d85823e3d90fd5b908486346200096f57816003193601126200096f57620011ca620014c8565b9060243567ffffffffffffffff94858211620008125736602383011215620008125781810135958087116200076457366024888501011162000764573384526020966006885260018060a01b0398898887205416806200149e5750506200124987513360601b8a820152601481526200124381620015e1565b62001684565b93620012cd8a808a516200125d81620015ae565b338152818d82019b168b52898d8d5197806024601f199962001287858c601f860116018d620015fe565b828c5201838b013788010152858c8201525116986001600160601b0360a01b998a8a5416178955511660018060a01b03166001600160601b0360a01b6001541617600155565b8151918383116200148b57908291620012e860025462001571565b8b601f82116200143e575b50508a91601f8411600114620013cc57508792620013c0575b50508160011b916000199060031b1c1916176002555b8551916109f1908184019284841090841117620013ad5750908291620019f48339039083f5948515620013a1578394951693849162001360620016c1565b338152600687522091825416179055805182815233907f2d8895d948115783fa362a57339c4c179365fafeafdd7dca66364ae296f50b75853392a351908152f35b508251903d90823e3d90fd5b634e487b7160e01b865260419052602485fd5b015190508a806200130c565b60028952600080516020620023e58339815191529316885b8c828210620014275750509084600195949392106200140d575b505050811b0160025562001322565b015160001960f88460031b161c191690558a8080620013fe565b6001859682939686015181550195019301620013e4565b620014789160028b52600080516020620023e583398151915290601f870160051c820192871062001480575b601f0160051c0190620016a8565b8c8b620012f3565b90915081906200146a565b634e487b7160e01b875260418552602487fd5b6356352f0b60e11b8252338583019081526001600160a01b03909116602082015281906040010390fd5b600435906001600160a01b0382168203620014df57565b600080fd5b602435906001600160a01b0382168203620014df57565b604435906001600160a01b0382168203620014df57565b602435906001600160e01b031982168203620014df57565b6020908160408183019282815285518094520193019160005b82811062001552575050505090565b83516001600160e01b0319168552938101939281019260010162001543565b90600182811c92168015620015a3575b60208310146200158d57565b634e487b7160e01b600052602260045260246000fd5b91607f169162001581565b6060810190811067ffffffffffffffff821117620015cb57604052565b634e487b7160e01b600052604160045260246000fd5b6040810190811067ffffffffffffffff821117620015cb57604052565b90601f8019910116810190811067ffffffffffffffff821117620015cb57604052565b919082519283825260005b8481106200164e575050826000602080949584010152601f8019910116010190565b6020818301810151848301820152016200162c565b90816020910312620014df57516001600160a01b0381168103620014df5790565b60208151910151906020811062001699575090565b6000199060200360031b1b1690565b818110620016b4575050565b60008155600101620016a8565b60008080556001818155620016d860025462001571565b9081620016e457505050565b601f8211600114620016f7575050600255565b60028352601f600080516020620023e5833981519152920160051c82017f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5acf5b81811062001748575050508160025555565b848155820162001736565b8051821015620017685760209160051b010190565b634e487b7160e01b600052603260045260246000fd5b9060409182519060018060a01b03620017b8818316936020936001600160601b03199060601b1684820152601481526200124381620015e1565b90855190620017c782620015ae565b8482528382019260009283855288518681019167ffffffffffffffff9282811084821117620019df57856200183792848e839481528a82528201525116976001600160601b0360a01b9889895416178855511660018060a01b03166001600160601b0360a01b6001541617600155565b805190828211620019085781906200185160025462001571565b601f81116200199f575b508890601f8311600114620019285787926200191c575b50508160011b916000199060031b1c1916176002555b8851906109f1808301918211838310176200190857908291620019f48339039084f58015620018fe57917f2d8895d948115783fa362a57339c4c179365fafeafdd7dca66364ae296f50b759391889316978891620018e5620016c1565b87815260068752209182541617905551918583523392a3565b87513d84823e3d90fd5b634e487b7160e01b86526041600452602486fd5b01519050388062001872565b60028852600080516020620023e58339815191529250601f198416885b8b828210620019885750509084600195949392106200196e575b505050811b0160025562001888565b015160001960f88460031b161c191690553880806200195f565b600185968293968601518155019501930162001945565b620019d89060028952600080516020620023e5833981519152601f850160051c8101918c86106200148057601f0160051c0190620016a8565b386200185b565b634e487b7160e01b87526041600452602487fdfe60c08060405234620000ca573360a0526366b0182d60e01b8152600081600481335afa8015620000c4576000809281926200009b575b506080526001600160a01b03821662000087575b60405161068e9081620003638239608051818181610192015281816102c401526104a5015260a05181818161014d0152818161031301526105250152f35b6200009291620002b2565b50388062000049565b909150620000bb92503d8092823e620000b482620000e5565b016200018e565b90913862000035565b62000221565b600080fd5b634e487b7160e01b600052604160045260246000fd5b60c0601f91909101601f19168101906001600160401b038211908210176200010c57604052565b620000cf565b601f909101601f19168101906001600160401b038211908210176200010c57604052565b60e051906001600160a01b0382168203620000ca57565b6001600160401b0381116200010c57601f01601f191660200190565b60005b8381106200017d5750506000910152565b81810151838201526020016200016c565b606060bf19820112620000ca5760c0516001600160a01b0381168103620000ca5791620001ba62000136565b610100519092906001600160401b038111620000ca578160df82011215620000ca578060c00151620001ec816200014d565b92620001fc604051948562000112565b81845260e08284010111620000ca576200021e9160e060208501910162000169565b90565b6040513d6000823e3d90fd5b3d156200025d573d9062000241826200014d565b9162000251604051938462000112565b82523d6000602084013e565b606090565b906020916200027d8151809281855285808601910162000169565b601f01601f1916010190565b9091620002a36200021e9360408452604084019062000262565b91602081840391015262000262565b9190823b156200034157600080825160208401865af4907fb24ebe141c5f2a744b103bea65fce6c40e0dc65d7341d092c09b160f40447990620002f46200022d565b60405190956001600160a01b0316928190620003139088908362000289565b0390a2156200031e57565b508051156200032f57602081519101fd5b60405163061a160d60e41b8152600490fd5b604051636d17e5ef60e11b81526001600160a01b0384166004820152602490fdfe60806040526004361015610027575b36156100255761001d366102b5565b602081519101f35b005b6000803560e01c9081631cff79cd1461005a575080637b1039991461005557638da5cb5b0361000e5761017c565b610137565b60403660031901126100ca57600435610072816100cd565b60243567ffffffffffffffff928382116100ca57366023830112156100ca5781600401359384116100ca5736602485840101116100ca576100c66100ba8560248501866104a2565b60405191829182610123565b0390f35b80fd5b6001600160a01b038116036100de57565b600080fd5b919082519283825260005b84811061010f575050826000602080949584010152601f8019910116010190565b6020818301810151848301820152016100ee565b9060206101349281815201906100e3565b90565b346100de5760003660031901126100de576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b346100de5760003660031901126100de576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b634e487b7160e01b600052604160045260246000fd5b90601f8019910116810190811067ffffffffffffffff8211176101f957604052565b6101c1565b908160209103126100de5751610134816100cd565b6040513d6000823e3d90fd5b908160008237016000815290565b67ffffffffffffffff81116101f957601f01601f191660200190565b3d15610274573d9061025a8261022d565b9161026860405193846101d7565b82523d6000602084013e565b606090565b606090610134939260408252806040830152806000848401376000838284010152601f80199101168101906020838284030191015201906100e3565b6040516361be485960e01b81527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0381811660048401526001600160e01b03196000351660248401819052939493906020846044817f000000000000000000000000000000000000000000000000000000000000000085165afa93841561044e5760009461041e575b5083169182156103e357505060008060405180610362818961021f565b0390845af4907fc4dabe0d7ef7462e2218f2c398c21ef217803e1c46f5cf802d1a5d1d9b503f2f610391610249565b80966103a260405192839283610279565b0390a2156103ad5750565b8251909150156103c05750805190602001fd5b60405163023c045d60e21b81526001600160a01b03919091166004820152602490fd5b604051638848730f60e01b81523360048201526001600160a01b039190911660248201526001600160e01b0319919091166044820152606490fd5b61044091945060203d8111610447575b61043881836101d7565b8101906101fe565b9238610345565b503d61042e565b610213565b908160209103126100de575180151581036100de5790565b9291926104778261022d565b9161048560405193846101d7565b8294818452818301116100de578281602093846000960137010152565b917f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0333818316036104ee575b505061013492916104e891369161046b565b906105e5565b60405163b31d1b9960e01b81526001600160a01b0383811660048301523360248301528616604482015290602090829060649082907f0000000000000000000000000000000000000000000000000000000000000000165afa90811561044e57600091610592575b501561056257806104d6565b6040516355d1750960e01b81526001600160a01b0391821660048201523360248201529084166044820152606490fd5b6105b3915060203d81116105b9575b6105ab81836101d7565b810190610453565b38610556565b503d6105a1565b90916105d7610134936040845260408401906100e3565b9160208184039101526100e3565b9190823b1561066d57600080825160208401865af4907fb24ebe141c5f2a744b103bea65fce6c40e0dc65d7341d092c09b160f40447990610624610249565b60405190956001600160a01b0316928190610641908890836105c0565b0390a21561064b57565b5080511561065b57602081519101fd5b60405163061a160d60e41b8152600490fd5b604051636d17e5ef60e11b81526001600160a01b0384166004820152602490fd405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace"; + hex"6080806040523461001657612498908161001c8239f35b600080fdfe60806040818152600491823610156200001757600080fd5b60009260e0908435821c928363092af8131462000e6d575082631ddef5b91462000dbb5782634bddd93a1462000a685782635cabcdf714620009a757826361be4859146200094b5782636383afb214620007ad57826366b0182d14620006a657826374912cd21462000626578263775c300c14620005ad578263aa4b826a14620004ca578263b31d1b991462000460578263b7fba4d31462000420578263b967840314620003b857508163fa557e93146200018957508063fb4a4d0814620001415763ffa1ad7414620000e957600080fd5b346200013d57816003193601126200013d57805162000139916200010d82620012a3565b600c82526b342e302e302d626574612e3560a01b602083015251918291602083526020830190620012e3565b0390f35b5080fd5b50346200013d5760203660031901126200013d57602090620001626200118a565b9062000179620001723362001440565b92620016b5565b516001600160a01b039091168152f35b8383346200013d57806003193601126200013d57620001a76200118a565b92620001b2620011a6565b60018060a01b03809516845260209460038652838520911684528452818320908251809486845492838152019381528681205b82600783011062000349579287926200025595926200013999899654938383106200032b575b508282106200030c575b828210620002ed575b828210620002ce575b828210620002af575b82821062000292575b82821062000275575b501062000260575b5090500383620012c0565b5191829182620011ec565b6001600160e01b03191681520180876200024a565b83811b6001600160e01b0319168552909301926001018462000242565b83891b6001600160e01b0319168552909301926001018462000239565b606084901b6001600160e01b0319168552909301926001018462000230565b608084901b6001600160e01b0319168552909301926001018462000227565b60a084901b6001600160e01b031916855290930192600101846200021e565b60c084901b6001600160e01b0319168552909301926001018462000215565b84901b6001600160e01b031916855290930192600101848b6200020b565b80546001600160e01b031981861b8116875260c082811b82168b89015260a083811b83168a8a0152608084811b84166060808c019190915285901b8416908a0152838a1b831690890152828b1b82169088015216858501526101009094019360089190910190600101620001e5565b915050346200041c5760203660031901126200041c57620003d86200118a565b3384526006602052828420549092906001600160a01b0316156200040557836200040284620016b5565b80f35b60249250519063963e961b60e01b82523390820152fd5b8280fd5b505050346200013d5760203660031901126200013d576020916001600160a01b03908290826200044f6200118a565b168152600685522054169051908152f35b915050346200041c5760603660031901126200041c578160209360ff92620004876200118a565b62000491620011a6565b6200049b620011bd565b6001600160a01b0392831685529288528484209082168452875283832091168252855220549151911615158152f35b915050346200041c5760603660031901126200041c57620004ea6200118a565b91620004f5620011a6565b90604435801515809103620005a957338652600660209081528287205490946001600160a01b03918216156200059257907f4f2bd80fb4928b06abcd76e3b26209a615f0612f98dc4a8b176934b1a3899333939291338952865280838920971696878952865280838920951694858952865282882060ff1981541660ff84161790553388526006865282882054169482519485528401523392a480f35b60249084519063963e961b60e01b82523390820152fd5b8580fd5b83853462000623578060031936011262000623573381526006602052819020546001600160a01b039081169283620005f65760208383620005ee3362001440565b915191168152f35b91516356352f0b60e11b8152339281019283526001600160a01b0390931660208301525081906040010390fd5b80fd5b915050346200041c5760203660031901126200041c578190620006486200118a565b6001600160a01b0380821686526006602052929094205482168062000676575050620005ee60209362001440565b92516356352f0b60e11b81526001600160a01b039485169181019182529390921660208301525081906040010390fd5b505050346200013d57816003193601126200013d5760018060a01b0391828154169160019384541693815192809160025491620006e38362001233565b808752928281169081156200077e57506001146200072f575b50505082916200071562000139946060930384620012c0565b8051958695865260208601528401526060830190620012e3565b925060028352600080516020620024788339815191525b82841062000765575050508201602001816200071562000139620006fc565b8054602085880181019190915290930192810162000746565b60ff191660208089019190915293151560051b87019093019350849250620007159150620001399050620006fc565b848483346200041c57816003193601126200041c57620007cc6200118a565b620007d6620011a6565b8351638da5cb5b60e01b815260209691926001600160a01b03928891839190829086165afa908115620009415790829187916200090d575b5016855260038652838520911684528452818320908251809486845492838152019381528681205b8260078301106200089e579287926200025595926200013999899654938383106200032b57508282106200030c57828210620002ed57828210620002ce57828210620002af5782821062000292578282106200027557501062000260575090500383620012c0565b80546001600160e01b031981861b8116875260c082811b82168b89015260a083811b83168a8a0152608084811b84166060808c019190915285901b8416908a0152838a1b831690890152828b1b8216908801521685850152610100909401936008919091019060010162000836565b620009329150883d8a1162000939575b620009298183620012c0565b81019062001325565b886200080e565b503d6200091d565b85513d88823e3d90fd5b505050346200013d57806003193601126200013d57602091816200096e6200118a565b9162000979620011d4565b6001600160a01b039384168252600586528282206001600160e01b0319909116825285522054915191168152f35b838534620006235760603660031901126200062357620009c66200118a565b620009d0620011a6565b90620009db620011bd565b91845195638da5cb5b60e01b87526020968781838160018060a01b038098165afa90811562000a5e5791848896949288969460ff999162000a3c575b5016855288528185852091168452875283832091168252855220541690519015158152f35b62000a5791508b3d8d116200093957620009298183620012c0565b8b62000a17565b87513d88823e3d90fd5b9291503462000db7576020928360031936011262000db35762000a8a6200118a565b338652600685528386205490946001600160a01b0392909183161562000d9c57338752600382528285882096169586885282528487209385519083829687928282549586815201918c52828c20948c5b8b82600783011062000d2157508462000b4297549383831062000d03575b5082821062000ce4575b82821062000cc5575b82821062000ca6575b82821062000c87575b82821062000c6a575b82821062000c4d575b501062000c37575b5090500385620012c0565b835190811562000c205750865b81811062000bdf5750509060067f0971095f3fa0f917fd0ad98319d7cdb8e837d8c98d5e47fb2700e8b68ac961c99233885260038152858820878952815285882080548982558062000bbd575b505033885252838620541692518062000bb7339482620011ec565b0390a480f35b62000bd7918a526007838b20910160031c8101906200136a565b388062000b9c565b33885260058352858820600191906001600160e01b031962000c02838962001415565b51168a52845286892080546001600160a01b03191690550162000b4f565b85516338d4b05760e01b8152908101879052602490fd5b6001600160e01b03191681528591013862000b37565b83811b6001600160e01b0319168552909301926001018462000b2f565b838d1b6001600160e01b0319168552909301926001018462000b26565b606084901b6001600160e01b0319168552909301926001018462000b1d565b608084901b6001600160e01b0319168552909301926001018462000b14565b60a084901b6001600160e01b0319168552909301926001018462000b0b565b60c084901b6001600160e01b0319168552909301926001018462000b02565b84901b6001600160e01b031916855290930192600101843862000af8565b836008949750600193966101009396928a549283809363ffffffff60e01b809681941b16875260c0938383861b1682890152838360a0928282851b16818c0152608083838d606090818484871b169101521b16908c01521b16908801521b169084015216878201520195019101928692899495929562000ada565b60249085519063963e961b60e01b82523390820152fd5b8480fd5b8380fd5b838534620006235781600319360112620006235762000dd96200118a565b9062000de4620011d4565b8351638da5cb5b60e01b81526020956001600160a01b039487918391829088165afa90811562000e6357839291859187959162000e41575b50168252600586528282206001600160e01b0319909116825285522054915191168152f35b62000e5c9150883d8a116200093957620009298183620012c0565b8862000e1c565b85513d85823e3d90fd5b90848634620006235781600319360112620006235762000e8c6200118a565b9060243567ffffffffffffffff948582116200041c57366023830112156200041c57818101359580871162000db757366024888501011162000db7573384526020966006885260018060a01b0398898887205416806200116057505062000f0b87513360601b8a8201526014815262000f0581620012a3565b62001346565b9362000f8f8a808a5162000f1f8162001270565b338152818d82019b168b52898d8d5197806024601f199962000f49858c601f860116018d620012c0565b828c5201838b013788010152858c8201525116986001600160601b0360a01b998a8a5416178955511660018060a01b03166001600160601b0360a01b6001541617600155565b8151918383116200114d5790829162000faa60025462001233565b8b601f821162001100575b50508a91601f84116001146200108e5750879262001082575b50508160011b916000199060031b1c1916176002555b8551916109f19081840192848410908411176200106f575090829162001a878339039083f59485156200106357839495169384916200102262001383565b338152600687522091825416179055805182815233907f2d8895d948115783fa362a57339c4c179365fafeafdd7dca66364ae296f50b75853392a351908152f35b508251903d90823e3d90fd5b634e487b7160e01b865260419052602485fd5b015190508a8062000fce565b60028952600080516020620024788339815191529316885b8c828210620010e9575050908460019594939210620010cf575b505050811b0160025562000fe4565b015160001960f88460031b161c191690558a8080620010c0565b6001859682939686015181550195019301620010a6565b6200113a9160028b526000805160206200247883398151915290601f870160051c820192871062001142575b601f0160051c01906200136a565b8c8b62000fb5565b90915081906200112c565b634e487b7160e01b875260418552602487fd5b6356352f0b60e11b8252338583019081526001600160a01b03909116602082015281906040010390fd5b600435906001600160a01b0382168203620011a157565b600080fd5b602435906001600160a01b0382168203620011a157565b604435906001600160a01b0382168203620011a157565b602435906001600160e01b031982168203620011a157565b6020908160408183019282815285518094520193019160005b82811062001214575050505090565b83516001600160e01b0319168552938101939281019260010162001205565b90600182811c9216801562001265575b60208310146200124f57565b634e487b7160e01b600052602260045260246000fd5b91607f169162001243565b6060810190811067ffffffffffffffff8211176200128d57604052565b634e487b7160e01b600052604160045260246000fd5b6040810190811067ffffffffffffffff8211176200128d57604052565b90601f8019910116810190811067ffffffffffffffff8211176200128d57604052565b919082519283825260005b84811062001310575050826000602080949584010152601f8019910116010190565b602081830181015184830182015201620012ee565b90816020910312620011a157516001600160a01b0381168103620011a15790565b6020815191015190602081106200135b575090565b6000199060200360031b1b1690565b81811062001376575050565b600081556001016200136a565b600080805560018181556200139a60025462001233565b9081620013a657505050565b601f8211600114620013b9575050600255565b60028352601f60008051602062002478833981519152920160051c82017f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5acf5b8181106200140a575050508160025555565b8481558201620013f8565b80518210156200142a5760209160051b010190565b634e487b7160e01b600052603260045260246000fd5b9060409182519060018060a01b036200147a818316936020936001600160601b03199060601b16848201526014815262000f0581620012a3565b90855190620014898262001270565b8482528382019260009283855288518681019167ffffffffffffffff9282811084821117620016a15785620014f992848e839481528a82528201525116976001600160601b0360a01b9889895416178855511660018060a01b03166001600160601b0360a01b6001541617600155565b805190828211620015ca5781906200151360025462001233565b601f811162001661575b508890601f8311600114620015ea578792620015de575b50508160011b916000199060031b1c1916176002555b8851906109f180830191821183831017620015ca5790829162001a878339039084f58015620015c057917f2d8895d948115783fa362a57339c4c179365fafeafdd7dca66364ae296f50b759391889316978891620015a762001383565b87815260068752209182541617905551918583523392a3565b87513d84823e3d90fd5b634e487b7160e01b86526041600452602486fd5b01519050388062001534565b60028852600080516020620024788339815191529250601f198416885b8b8282106200164a57505090846001959493921062001630575b505050811b016002556200154a565b015160001960f88460031b161c1916905538808062001621565b600185968293968601518155019501930162001607565b6200169a906002895260008051602062002478833981519152601f850160051c8101918c86106200114257601f0160051c01906200136a565b386200151d565b634e487b7160e01b87526041600452602487fd5b60408051631abad16560e21b81526001600160a01b0392831692600490600080848481838a5af193841562001a7a578194620019ab575b50835180156200199457815b818110620018ff5750503381526020926003845285822087835284528582209085519067ffffffffffffffff8211620018ec57680100000000000000008211620018ec5750815481835580821062001899575b50908692918587019183528583208160031c91845b8381106200183c57506007198116900380620017bb575b5050505060067f71b3c95c0b8611d2f0f9c4e492ebbcf20d034943bccac6ef1a71ae90e0f243fa9433835252205416925180620017b6339482620011ec565b0390a4565b928493855b818110620017f857505050015560067f71b3c95c0b8611d2f0f9c4e492ebbcf20d034943bccac6ef1a71ae90e0f243fa388062001777565b9195979880975062001829600192949596885160e01c908560021b60031b9163ffffffff809116831b921b19161790565b96019101918a96989795949392620017c0565b9091929394809650865b896008821062001866575050838201558995949392919060010162001760565b6200188f8893600193995160e01c908a60021b60031b9163ffffffff809116831b921b19161790565b9201960162001846565b620018ca90838552868520600780850160031c820192601c8660021b1680620018d1575b500160031c01906200136a565b386200174b565b60001990818601918254918d0360031b1c16905538620018bd565b634e487b7160e01b845260419052602483fd5b6001600160e01b031962001914828862001415565b511633845260056020908082528986208387528252868a872054166200195c5733865281528885209185525286832080546001600160a01b03191689179055600101620016f8565b338652815288852082865290528784205488516361a2116b60e11b815290861681880152602481018a90526044810191909152606490fd5b85516362d3093d60e11b8152808501889052602490fd5b9093503d8085833e620019bf8183620012c0565b81016020908183820312620005a957825167ffffffffffffffff9384821162001a5f570181601f8201121562001a7657805193841162001a63578360051b9088519462001a0f85840187620012c0565b8552838086019282010192831162001a5f578301905b82821062001a3957505050509238620016ec565b81516001600160e01b03198116810362001a5b57815290830190830162001a25565b8880fd5b8780fd5b634e487b7160e01b875260418652602487fd5b8680fd5b508451903d90823e3d90fdfe60c08060405234620000ca573360a0526366b0182d60e01b8152600081600481335afa8015620000c4576000809281926200009b575b506080526001600160a01b03821662000087575b60405161068e9081620003638239608051818181610192015281816102c401526104a5015260a05181818161014d0152818161031301526105250152f35b6200009291620002b2565b50388062000049565b909150620000bb92503d8092823e620000b482620000e5565b016200018e565b90913862000035565b62000221565b600080fd5b634e487b7160e01b600052604160045260246000fd5b60c0601f91909101601f19168101906001600160401b038211908210176200010c57604052565b620000cf565b601f909101601f19168101906001600160401b038211908210176200010c57604052565b60e051906001600160a01b0382168203620000ca57565b6001600160401b0381116200010c57601f01601f191660200190565b60005b8381106200017d5750506000910152565b81810151838201526020016200016c565b606060bf19820112620000ca5760c0516001600160a01b0381168103620000ca5791620001ba62000136565b610100519092906001600160401b038111620000ca578160df82011215620000ca578060c00151620001ec816200014d565b92620001fc604051948562000112565b81845260e08284010111620000ca576200021e9160e060208501910162000169565b90565b6040513d6000823e3d90fd5b3d156200025d573d9062000241826200014d565b9162000251604051938462000112565b82523d6000602084013e565b606090565b906020916200027d8151809281855285808601910162000169565b601f01601f1916010190565b9091620002a36200021e9360408452604084019062000262565b91602081840391015262000262565b9190823b156200034157600080825160208401865af4907fb24ebe141c5f2a744b103bea65fce6c40e0dc65d7341d092c09b160f40447990620002f46200022d565b60405190956001600160a01b0316928190620003139088908362000289565b0390a2156200031e57565b508051156200032f57602081519101fd5b60405163061a160d60e41b8152600490fd5b604051636d17e5ef60e11b81526001600160a01b0384166004820152602490fdfe60806040526004361015610027575b36156100255761001d366102b5565b602081519101f35b005b6000803560e01c9081631cff79cd1461005a575080637b1039991461005557638da5cb5b0361000e5761017c565b610137565b60403660031901126100ca57600435610072816100cd565b60243567ffffffffffffffff928382116100ca57366023830112156100ca5781600401359384116100ca5736602485840101116100ca576100c66100ba8560248501866104a2565b60405191829182610123565b0390f35b80fd5b6001600160a01b038116036100de57565b600080fd5b919082519283825260005b84811061010f575050826000602080949584010152601f8019910116010190565b6020818301810151848301820152016100ee565b9060206101349281815201906100e3565b90565b346100de5760003660031901126100de576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b346100de5760003660031901126100de576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b634e487b7160e01b600052604160045260246000fd5b90601f8019910116810190811067ffffffffffffffff8211176101f957604052565b6101c1565b908160209103126100de5751610134816100cd565b6040513d6000823e3d90fd5b908160008237016000815290565b67ffffffffffffffff81116101f957601f01601f191660200190565b3d15610274573d9061025a8261022d565b9161026860405193846101d7565b82523d6000602084013e565b606090565b606090610134939260408252806040830152806000848401376000838284010152601f80199101168101906020838284030191015201906100e3565b6040516361be485960e01b81527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0381811660048401526001600160e01b03196000351660248401819052939493906020846044817f000000000000000000000000000000000000000000000000000000000000000085165afa93841561044e5760009461041e575b5083169182156103e357505060008060405180610362818961021f565b0390845af4907fc4dabe0d7ef7462e2218f2c398c21ef217803e1c46f5cf802d1a5d1d9b503f2f610391610249565b80966103a260405192839283610279565b0390a2156103ad5750565b8251909150156103c05750805190602001fd5b60405163023c045d60e21b81526001600160a01b03919091166004820152602490fd5b604051638848730f60e01b81523360048201526001600160a01b039190911660248201526001600160e01b0319919091166044820152606490fd5b61044091945060203d8111610447575b61043881836101d7565b8101906101fe565b9238610345565b503d61042e565b610213565b908160209103126100de575180151581036100de5790565b9291926104778261022d565b9161048560405193846101d7565b8294818452818301116100de578281602093846000960137010152565b917f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0333818316036104ee575b505061013492916104e891369161046b565b906105e5565b60405163b31d1b9960e01b81526001600160a01b0383811660048301523360248301528616604482015290602090829060649082907f0000000000000000000000000000000000000000000000000000000000000000165afa90811561044e57600091610592575b501561056257806104d6565b6040516355d1750960e01b81526001600160a01b0391821660048201523360248201529084166044820152606490fd5b6105b3915060203d81116105b9575b6105ab81836101d7565b810190610453565b38610556565b503d6105a1565b90916105d7610134936040845260408401906100e3565b9160208184039101526100e3565b9190823b1561066d57600080825160208401865af4907fb24ebe141c5f2a744b103bea65fce6c40e0dc65d7341d092c09b160f40447990610624610249565b60405190956001600160a01b0316928190610641908890836105c0565b0390a21561064b57565b5080511561065b57602081519101fd5b60405163061a160d60e41b8152600490fd5b604051636d17e5ef60e11b81526001600160a01b0384166004820152602490fd405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace"; /*////////////////////////////////////////////////////////////////////////// DEPLOYERS