diff --git a/.solhint.json b/.solhint.json index 5ac1f31..0202669 100644 --- a/.solhint.json +++ b/.solhint.json @@ -10,7 +10,7 @@ "contract-name-camelcase": "off", "func-name-mixedcase": "off", "func-visibility": ["error", { "ignoreConstructors": true }], - "max-line-length": ["error", 120], + "max-line-length": ["error", 123], "no-complex-fallback": "off", "no-empty-blocks": "off", "no-inline-assembly": "off", diff --git a/README.md b/README.md index abaa6a0..214a1db 100644 --- a/README.md +++ b/README.md @@ -90,15 +90,8 @@ There are multiple ways to deploy a proxy: - Call the `deployFor` function, which accepts the owner as an argument. - Call the `deployAndExecute` function, which deploys the proxy and executes a delegate call to a target contract in a single transaction. -Once the proxy is deployed, you can start interacting with target contracts. PRBProxy ships with an "enshrined" target contract called -`PRBProxyAnnex`, which provides several auxiliary functions, including: - -- `installPlugin` -- `setPermission` -- `uninstallPlugin` - -You call the functions above by ABI-encoding their calldata and calling `execute` on the proxy. The logic in `PRBProxyAnnex` is kept separate from the -proxy itself to reduce deployment costs. +Once the proxy is deployed, you can start interacting with target contracts by ABI-encoding the target's functions and pass the data to the `execute` +function on the proxy. ### Addresses @@ -107,7 +100,6 @@ The registry and the enshrined target are deployed at the same address on the fo | Contract | Chain | [Chain ID](https://chainlist.org/) | Address | | -------- | ----------------------- | ---------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | | Registry | Ethereum Goerli Testnet | 5 | [0xa87bc4C1Bc54E1C1B28d2dD942A094A6B665B8C9](https://goerli.etherscan.io/address/0xa87bc4C1Bc54E1C1B28d2dD942A094A6B665B8C9#code) | -| Annex | Ethereum Goerli Testnet | 5 | [0x0254C4467cBbdbe8d5E01e68de0DF7b20dD2A167](https://goerli.etherscan.io/address/0x0254C4467cBbdbe8d5E01e68de0DF7b20dD2A167#code) | ### Targets diff --git a/foundry.toml b/foundry.toml index 9843a9f..75db65e 100644 --- a/foundry.toml +++ b/foundry.toml @@ -4,7 +4,7 @@ cbor_metadata = false evm_version = "paris" fs_permissions = [{ access = "read", path = "./out-optimized" }] - gas_reports = ["PRBProxy", "PRBProxyAnnex", "PRBProxyRegistry"] + gas_reports = ["PRBProxy", "PRBProxyRegistry"] ignored_error_codes = [ 5159, # ignore selfdestruct warning ] diff --git a/script/DeployAnnex.s.sol b/script/DeployAnnex.s.sol deleted file mode 100644 index d9526b0..0000000 --- a/script/DeployAnnex.s.sol +++ /dev/null @@ -1,12 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity >=0.8.19 <0.9.0; - -import { PRBProxyAnnex } from "../src/PRBProxyAnnex.sol"; - -import { BaseScript } from "./Base.s.sol"; - -contract DeployAnnex is BaseScript { - function run() public virtual broadcaster returns (PRBProxyAnnex annex) { - annex = new PRBProxyAnnex(); - } -} diff --git a/script/DeployDeterministicAnnex.s.sol b/script/DeployDeterministicAnnex.s.sol deleted file mode 100644 index ee3f261..0000000 --- a/script/DeployDeterministicAnnex.s.sol +++ /dev/null @@ -1,16 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity >=0.8.19 <0.9.0; - -import { PRBProxyAnnex } from "../src/PRBProxyAnnex.sol"; - -import { BaseScript } from "./Base.s.sol"; - -/// @notice Deploys {PRBProxyAnnex} at a deterministic address across all chains. -/// @dev Reverts if the contract has already been deployed. -contract DeployDeterministicAnnex is BaseScript { - /// @dev The presence of the salt instructs Forge to deploy contracts via this deterministic CREATE2 factory: - /// https://github.com/Arachnid/deterministic-deployment-proxy - function run(uint256 create2Salt) public virtual broadcaster returns (PRBProxyAnnex annex) { - annex = new PRBProxyAnnex{ salt: bytes32(create2Salt) }(); - } -} diff --git a/script/DeployDeterministicSystem.s.sol b/script/DeployDeterministicSystem.s.sol deleted file mode 100644 index ebcbbe3..0000000 --- a/script/DeployDeterministicSystem.s.sol +++ /dev/null @@ -1,23 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity >=0.8.19 <0.9.0; - -import { PRBProxyAnnex } from "../src/PRBProxyAnnex.sol"; -import { PRBProxyRegistry } from "../src/PRBProxyRegistry.sol"; - -import { BaseScript } from "./Base.s.sol"; - -/// @dev Deploys {PRBProxyAnnex} and {PRBProxyRegistry} at deterministic addresses across chains. -/// @dev Reverts if any contract has already been deployed. -contract DeployDeterministicSystem is BaseScript { - /// @dev The presence of the salt instructs Forge to deploy contracts via this deterministic CREATE2 factory: - /// https://github.com/Arachnid/deterministic-deployment-proxy - function run(uint256 create2Salt) - public - virtual - broadcaster - returns (PRBProxyAnnex annex, PRBProxyRegistry registry) - { - annex = new PRBProxyAnnex{ salt: bytes32(create2Salt) }(); - registry = new PRBProxyRegistry{ salt: bytes32(create2Salt) }(); - } -} diff --git a/script/DeployProxyViaRegistry.s.sol b/script/DeployProxyViaRegistry.s.sol index bf8d211..a09f607 100644 --- a/script/DeployProxyViaRegistry.s.sol +++ b/script/DeployProxyViaRegistry.s.sol @@ -2,7 +2,6 @@ pragma solidity >=0.8.19 <0.9.0; import { IPRBProxy } from "../src/interfaces/IPRBProxy.sol"; -import { PRBProxyAnnex } from "../src/PRBProxyAnnex.sol"; import { IPRBProxyRegistry } from "../src/interfaces/IPRBProxyRegistry.sol"; import { BaseScript } from "./Base.s.sol"; diff --git a/script/DeploySystem.s.sol b/script/DeploySystem.s.sol deleted file mode 100644 index cce79f1..0000000 --- a/script/DeploySystem.s.sol +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity >=0.8.19 <0.9.0; - -import { PRBProxyAnnex } from "../src/PRBProxyAnnex.sol"; -import { PRBProxyRegistry } from "../src/PRBProxyRegistry.sol"; - -import { BaseScript } from "./Base.s.sol"; - -contract DeploySystem is BaseScript { - function run() public virtual broadcaster returns (PRBProxyAnnex annex, PRBProxyRegistry registry) { - annex = new PRBProxyAnnex(); - registry = new PRBProxyRegistry(); - } -} diff --git a/script/SetPermission.s.sol b/script/SetPermission.s.sol index 5815d2b..551a1b4 100644 --- a/script/SetPermission.s.sol +++ b/script/SetPermission.s.sol @@ -1,28 +1,14 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.19 <0.9.0; -import { IPRBProxy } from "../src/interfaces/IPRBProxy.sol"; -import { IPRBProxyAnnex } from "../src/interfaces/IPRBProxyAnnex.sol"; +import { IPRBProxyRegistry } from "../src/interfaces/IPRBProxyRegistry.sol"; import { BaseScript } from "./Base.s.sol"; -/// @notice Bootstraps the proxy system by giving permission to an envoy and installing a plugin. +/// @notice Permits an envoy to delegate call to a target contract. contract SetPermission is BaseScript { - function run( - IPRBProxy proxy, - IPRBProxyAnnex annex, - address target - ) - public - broadcaster - returns (bytes memory response) - { - // ABI encode the call to `setPermission`. + function run(IPRBProxyRegistry registry, address target, bool permission) public broadcaster { address envoy = vm.addr(vm.deriveKey(mnemonic, 1)); - bool permission = true; - bytes memory data = abi.encodeCall(annex.setPermission, (envoy, target, permission)); - - // Execute the call to the annex. - response = proxy.execute(address(annex), data); + registry.setPermission({ envoy: envoy, target: target, permission: permission }); } } diff --git a/shell/prepare-artifacts.sh b/shell/prepare-artifacts.sh index de06764..848b03d 100755 --- a/shell/prepare-artifacts.sh +++ b/shell/prepare-artifacts.sh @@ -22,15 +22,12 @@ FOUNDRY_PROFILE=optimized forge build # Copy the production artifacts cp out-optimized/PRBProxy.sol/PRBProxy.json $artifacts -cp out-optimized/PRBProxyAnnex.sol/PRBProxyAnnex.json $artifacts cp out-optimized/PRBProxyRegistry.sol/PRBProxyRegistry.json $artifacts interfaces=./artifacts/interfaces cp out-optimized/IPRBProxy.sol/IPRBProxy.json $interfaces -cp out-optimized/IPRBProxyAnnex.sol/IPRBProxyAnnex.json $interfaces cp out-optimized/IPRBProxyPlugin.sol/IPRBProxyPlugin.json $interfaces cp out-optimized/IPRBProxyRegistry.sol/IPRBProxyRegistry.json $interfaces -cp out-optimized/IPRBProxyStorage.sol/IPRBProxyStorage.json $interfaces # Format the artifacts with Prettier pnpm prettier --write ./artifacts diff --git a/shell/update-precompiles.sh b/shell/update-precompiles.sh index c39805b..ea9c4b0 100755 --- a/shell/update-precompiles.sh +++ b/shell/update-precompiles.sh @@ -12,7 +12,6 @@ set -euo pipefail FOUNDRY_PROFILE=optimized forge build # Retrieve the raw bytecodes, removing the "0x" prefix -annex=$(cat out-optimized/PRBProxyAnnex.sol/PRBProxyAnnex.json | jq -r '.bytecode.object' | cut -c 3-) registry=$(cat out-optimized/PRBProxyRegistry.sol/PRBProxyRegistry.json | jq -r '.bytecode.object' | cut -c 3-) precompiles_path="test/utils/Precompiles.sol" @@ -22,7 +21,6 @@ if [ ! -f $precompiles_path ]; then fi # Replace the current bytecodes -sd "(BYTECODE_ANNEX =)[^;]+;" "\$1 hex\"$annex\";" $precompiles_path sd "(BYTECODE_REGISTRY =)[^;]+;" "\$1 hex\"$registry\";" $precompiles_path # Reformat the code with Forge diff --git a/src/PRBProxy.sol b/src/PRBProxy.sol index 11f0f8f..0624f89 100644 --- a/src/PRBProxy.sol +++ b/src/PRBProxy.sol @@ -1,7 +1,6 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.18; -import { PRBProxyStorage } from "./abstracts/PRBProxyStorage.sol"; import { IPRBProxy } from "./interfaces/IPRBProxy.sol"; import { IPRBProxyPlugin } from "./interfaces/IPRBProxyPlugin.sol"; import { IPRBProxyRegistry } from "./interfaces/IPRBProxyRegistry.sol"; @@ -19,10 +18,7 @@ import { IPRBProxyRegistry } from "./interfaces/IPRBProxyRegistry.sol"; /// @title PRBProxy /// @dev See the documentation in {IPRBProxy}. -contract PRBProxy is - PRBProxyStorage, // 1 inherited component - IPRBProxy // 1 inherited component -{ +contract PRBProxy is IPRBProxy { /*////////////////////////////////////////////////////////////////////////// CONSTANTS //////////////////////////////////////////////////////////////////////////*/ @@ -56,10 +52,10 @@ contract PRBProxy is /// @notice Fallback function used to run plugins. /// @dev WARNING: anyone can call this function and thus run any installed plugin. fallback(bytes calldata data) external payable returns (bytes memory response) { - // Check if the function signature exists in the installed plugins mapping. - IPRBProxyPlugin plugin = plugins[msg.sig]; + // Check if the function signature points to a known installed plugin. + IPRBProxyPlugin plugin = registry.getPluginByOwner({ owner: owner, method: msg.sig }); if (address(plugin) == address(0)) { - revert PRBProxy_PluginNotInstalledForMethod({ caller: msg.sender, selector: msg.sig }); + revert PRBProxy_PluginNotInstalledForMethod({ caller: msg.sender, owner: owner, method: msg.sig }); } // Delegate call to the plugin. @@ -93,7 +89,8 @@ contract PRBProxy is /// @inheritdoc IPRBProxy function execute(address target, bytes calldata data) external payable override returns (bytes memory response) { // Check that the caller is either the owner or an envoy with permission. - if (owner != msg.sender && !permissions[msg.sender][target]) { + bool permission = registry.getPermissionByOwner({ owner: owner, envoy: msg.sender, target: target }); + if (owner != msg.sender && !permission) { revert PRBProxy_ExecutionUnauthorized({ owner: owner, caller: msg.sender, target: target }); } @@ -105,7 +102,7 @@ contract PRBProxy is INTERNAL NON-CONSTANT FUNCTIONS //////////////////////////////////////////////////////////////////////////*/ - /// @notice Executes a DELEGATECALL to the provided `target` with the provided `data`. + /// @notice Executes a DELEGATECALL to the provided target with the provided data. /// @dev Shared logic between the constructor and the `execute` function. function _execute(address target, bytes memory data) internal returns (bytes memory response) { // Check that the target is a contract. diff --git a/src/PRBProxyAnnex.sol b/src/PRBProxyAnnex.sol deleted file mode 100644 index cb806dd..0000000 --- a/src/PRBProxyAnnex.sol +++ /dev/null @@ -1,94 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity >=0.8.18; - -import { PRBProxyStorage } from "./abstracts/PRBProxyStorage.sol"; -import { IPRBProxyAnnex } from "./interfaces/IPRBProxyAnnex.sol"; -import { IPRBProxyPlugin } from "./interfaces/IPRBProxyPlugin.sol"; - -/* - -██████╗ ██████╗ ██████╗ ██████╗ ██████╗ ██████╗ ██╗ ██╗██╗ ██╗ -██╔══██╗██╔══██╗██╔══██╗██╔══██╗██╔══██╗██╔═══██╗╚██╗██╔╝╚██╗ ██╔╝ -██████╔╝██████╔╝██████╔╝██████╔╝██████╔╝██║ ██║ ╚███╔╝ ╚████╔╝ -██╔═══╝ ██╔══██╗██╔══██╗██╔═══╝ ██╔══██╗██║ ██║ ██╔██╗ ╚██╔╝ -██║ ██║ ██║██████╔╝██║ ██║ ██║╚██████╔╝██╔╝ ██╗ ██║ -╚═╝ ╚═╝ ╚═╝╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ - - █████╗ ███╗ ██╗███╗ ██╗███████╗██╗ ██╗ -██╔══██╗████╗ ██║████╗ ██║██╔════╝╚██╗██╔╝ -███████║██╔██╗ ██║██╔██╗ ██║█████╗ ╚███╔╝ -██╔══██║██║╚██╗██║██║╚██╗██║██╔══╝ ██╔██╗ -██║ ██║██║ ╚████║██║ ╚████║███████╗██╔╝ ██╗ -╚═╝ ╚═╝╚═╝ ╚═══╝╚═╝ ╚═══╝╚══════╝╚═╝ ╚═╝ - -*/ - -/// @title PRBProxyAnnex -/// @dev See the documentation in {IPRBProxyAnnex}. -contract PRBProxyAnnex is - IPRBProxyAnnex, // 0 inherited components - PRBProxyStorage // 1 inherited component -{ - /*////////////////////////////////////////////////////////////////////////// - USER-FACING STORAGE - //////////////////////////////////////////////////////////////////////////*/ - - /// @inheritdoc IPRBProxyAnnex - string public constant override VERSION = "4.0.0-beta.5"; - - /*////////////////////////////////////////////////////////////////////////// - NON-CONSTANT FUNCTIONS - //////////////////////////////////////////////////////////////////////////*/ - - /// @inheritdoc IPRBProxyAnnex - function installPlugin(IPRBProxyPlugin plugin) external override { - // Get the method list to install. - bytes4[] memory methodList = plugin.methodList(); - - // The plugin must have at least one listed method. - uint256 length = methodList.length; - if (length == 0) { - revert PRBProxy_NoPluginMethods(plugin); - } - - // Enable every method in the list. - for (uint256 i = 0; i < length;) { - plugins[methodList[i]] = plugin; - unchecked { - i += 1; - } - } - - // Log the plugin installation. - emit InstallPlugin(plugin); - } - - /// @inheritdoc IPRBProxyAnnex - function setPermission(address envoy, address target, bool permission) external override { - permissions[envoy][target] = permission; - emit SetPermission(envoy, target, permission); - } - - /// @inheritdoc IPRBProxyAnnex - function uninstallPlugin(IPRBProxyPlugin plugin) external { - // Get the method list to uninstall. - bytes4[] memory methodList = plugin.methodList(); - - // The plugin must have at least one listed method. - uint256 length = methodList.length; - if (length == 0) { - revert PRBProxy_NoPluginMethods(plugin); - } - - // Disable every method in the list. - for (uint256 i = 0; i < length;) { - delete plugins[methodList[i]]; - unchecked { - i += 1; - } - } - - // Log the plugin uninstallation. - emit UninstallPlugin(plugin); - } -} diff --git a/src/PRBProxyRegistry.sol b/src/PRBProxyRegistry.sol index b4b1357..72c161e 100644 --- a/src/PRBProxyRegistry.sol +++ b/src/PRBProxyRegistry.sol @@ -2,6 +2,7 @@ pragma solidity >=0.8.18; import { IPRBProxy } from "./interfaces/IPRBProxy.sol"; +import { IPRBProxyPlugin } from "./interfaces/IPRBProxyPlugin.sol"; import { IPRBProxyRegistry } from "./interfaces/IPRBProxyRegistry.sol"; import { PRBProxy } from "./PRBProxy.sol"; @@ -47,7 +48,10 @@ contract PRBProxyRegistry is IPRBProxyRegistry { INTERNAL STORAGE //////////////////////////////////////////////////////////////////////////*/ - /// @dev Maps owner addresses to proxy contracts. + mapping(address owner => mapping(address envoy => mapping(address target => bool permission))) internal _permissions; + + mapping(address owner => mapping(bytes4 method => IPRBProxyPlugin plugin)) internal _plugins; + mapping(address owner => IPRBProxy proxy) internal _proxies; /*////////////////////////////////////////////////////////////////////////// @@ -63,10 +67,54 @@ contract PRBProxyRegistry is IPRBProxyRegistry { _; } + /// @notice Checks that the caller has a proxy. + modifier onlyCallerWithProxy() { + if (address(_proxies[msg.sender]) == address(0)) { + revert PRBProxyRegistry_CallerDoesNotHaveProxy(msg.sender); + } + _; + } + /*////////////////////////////////////////////////////////////////////////// USER-FACING CONSTANT FUNCTIONS //////////////////////////////////////////////////////////////////////////*/ + /// @inheritdoc IPRBProxyRegistry + function getPermissionByOwner( + address owner, + address envoy, + address target + ) + external + view + returns (bool permission) + { + permission = _permissions[owner][envoy][target]; + } + + /// @inheritdoc IPRBProxyRegistry + function getPermissionByProxy( + IPRBProxy proxy, + address envoy, + address target + ) + external + view + returns (bool permission) + { + permission = _permissions[proxy.owner()][envoy][target]; + } + + /// @inheritdoc IPRBProxyRegistry + function getPluginByOwner(address owner, bytes4 method) external view returns (IPRBProxyPlugin plugin) { + plugin = _plugins[owner][method]; + } + + /// @inheritdoc IPRBProxyRegistry + function getPluginByProxy(IPRBProxy proxy, bytes4 method) external view returns (IPRBProxyPlugin plugin) { + plugin = _plugins[proxy.owner()][method]; + } + /// @inheritdoc IPRBProxyRegistry function getProxy(address owner) external view returns (IPRBProxy proxy) { proxy = _proxies[owner]; @@ -129,6 +177,61 @@ contract PRBProxyRegistry is IPRBProxyRegistry { proxy = _deploy(owner); } + /// @inheritdoc IPRBProxyRegistry + function installPlugin(IPRBProxyPlugin plugin) external override onlyCallerWithProxy { + // Get the method list to install. + bytes4[] memory methodList = plugin.methodList(); + + // The plugin must have at least one listed method. + uint256 length = methodList.length; + if (length == 0) { + revert PRBProxyRegistry_PluginEmptyMethodList(plugin); + } + + // Install every method in the list. + address owner = msg.sender; + for (uint256 i = 0; i < length;) { + _plugins[owner][methodList[i]] = plugin; + unchecked { + i += 1; + } + } + + // Log the plugin installation. + emit InstallPlugin(owner, _proxies[owner], plugin); + } + + /// @inheritdoc IPRBProxyRegistry + function setPermission(address envoy, address target, bool permission) external override onlyCallerWithProxy { + address owner = msg.sender; + _permissions[owner][envoy][target] = permission; + emit SetPermission(owner, _proxies[owner], envoy, target, permission); + } + + /// @inheritdoc IPRBProxyRegistry + function uninstallPlugin(IPRBProxyPlugin plugin) external override onlyCallerWithProxy { + // Get the method list to uninstall. + bytes4[] memory methodList = plugin.methodList(); + + // The plugin must have at least one listed method. + uint256 length = methodList.length; + if (length == 0) { + revert PRBProxyRegistry_PluginEmptyMethodList(plugin); + } + + // Uninstall every method in the list. + address owner = msg.sender; + for (uint256 i = 0; i < length;) { + delete _plugins[owner][methodList[i]]; + unchecked { + i += 1; + } + } + + // Log the plugin uninstallation. + emit UninstallPlugin(owner, _proxies[owner], plugin); + } + /*////////////////////////////////////////////////////////////////////////// INTERNAL NON-CONSTANT FUNCTIONS //////////////////////////////////////////////////////////////////////////*/ diff --git a/src/abstracts/PRBProxyPlugin.sol b/src/abstracts/PRBProxyPlugin.sol deleted file mode 100644 index 9ac10b0..0000000 --- a/src/abstracts/PRBProxyPlugin.sol +++ /dev/null @@ -1,9 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity >=0.8.18; - -import { IPRBProxyPlugin } from "../interfaces/IPRBProxyPlugin.sol"; -import { PRBProxyStorage } from "./PRBProxyStorage.sol"; - -/// @title PRBProxyPlugin -/// @dev This is meant to be inherited by plugins. See the documentation in {IPRBProxyPlugin}. -abstract contract PRBProxyPlugin is IPRBProxyPlugin, PRBProxyStorage { } diff --git a/src/abstracts/PRBProxyStorage.sol b/src/abstracts/PRBProxyStorage.sol deleted file mode 100644 index f9b76aa..0000000 --- a/src/abstracts/PRBProxyStorage.sol +++ /dev/null @@ -1,15 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity >=0.8.18; - -import { IPRBProxyPlugin } from "../interfaces/IPRBProxyPlugin.sol"; -import { IPRBProxyStorage } from "../interfaces/IPRBProxyStorage.sol"; - -/// @title PRBProxyStorage -/// @dev This is meant to be inherited by plugins and targets. See the documentation in {IPRBProxyStorage}. -abstract contract PRBProxyStorage is IPRBProxyStorage { - /// @inheritdoc IPRBProxyStorage - mapping(bytes4 method => IPRBProxyPlugin plugin) public plugins; - - /// @inheritdoc IPRBProxyStorage - mapping(address envoy => mapping(address target => bool permission)) public permissions; -} diff --git a/src/interfaces/IPRBProxy.sol b/src/interfaces/IPRBProxy.sol index d326039..bb3ff37 100644 --- a/src/interfaces/IPRBProxy.sol +++ b/src/interfaces/IPRBProxy.sol @@ -3,11 +3,10 @@ pragma solidity >=0.8.4; import { IPRBProxyPlugin } from "./IPRBProxyPlugin.sol"; import { IPRBProxyRegistry } from "./IPRBProxyRegistry.sol"; -import { IPRBProxyStorage } from "./IPRBProxyStorage.sol"; /// @title IPRBProxy -/// @notice Proxy contract to compose transactions on owner's behalf. -interface IPRBProxy is IPRBProxyStorage { +/// @notice Proxy contract to compose transactions on behalf of the owner. +interface IPRBProxy { /*////////////////////////////////////////////////////////////////////////// ERRORS //////////////////////////////////////////////////////////////////////////*/ @@ -21,12 +20,12 @@ interface IPRBProxy is IPRBProxyStorage { /// @notice Thrown when the caller to be the owner. error PRBProxy_ExecutionUnauthorized(address owner, address caller, address target); + /// @notice Thrown when the fallback function fails to find an installed plugin for the method selector. + error PRBProxy_PluginNotInstalledForMethod(address caller, address owner, bytes4 method); + /// @notice Thrown when a plugin execution reverts without a specified reason. error PRBProxy_PluginReverted(IPRBProxyPlugin plugin); - /// @notice Thrown when the fallback function fails to find an installed plugin for the called method. - error PRBProxy_PluginNotInstalledForMethod(address caller, bytes4 selector); - /// @notice Thrown when a non-contract address is passed as the target. error PRBProxy_TargetNotContract(address target); diff --git a/src/interfaces/IPRBProxyAnnex.sol b/src/interfaces/IPRBProxyAnnex.sol deleted file mode 100644 index 4540491..0000000 --- a/src/interfaces/IPRBProxyAnnex.sol +++ /dev/null @@ -1,85 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity >=0.8.4; - -import { IPRBProxyPlugin } from "./IPRBProxyPlugin.sol"; - -/// @title IPRBProxyAnnex -/// @notice An enshrined target contract, which implements helper functions for the following operations: -/// - Installing plugins on the proxy. -/// - Updating the minimum gas reserve. -/// - Permitting envoys to call target contracts on behalf of the proxy. -/// - Uninstalling plugins on the proxy. -interface IPRBProxyAnnex { - /*////////////////////////////////////////////////////////////////////////// - ERRORS - //////////////////////////////////////////////////////////////////////////*/ - - /// @notice Thrown when installing or uninstall a plugin, and the plugin doesn't implement any method. - error PRBProxy_NoPluginMethods(IPRBProxyPlugin plugin); - - /*////////////////////////////////////////////////////////////////////////// - EVENTS - //////////////////////////////////////////////////////////////////////////*/ - - /// @notice Emitted when a plugin is installed. - event InstallPlugin(IPRBProxyPlugin indexed plugin); - - /// @notice Emitted when the permission is updated for an (envoy,target) tuple. - event SetPermission(address indexed envoy, address indexed target, bool permission); - - /// @notice Emitted when a plugin is uninstalled. - event UninstallPlugin(IPRBProxyPlugin indexed plugin); - - /*////////////////////////////////////////////////////////////////////////// - CONSTANT FUNCTIONS - //////////////////////////////////////////////////////////////////////////*/ - - /// @notice The semantic version of the {PRBProxy} release. - /// @dev This is mirrored here to serve as a link to the proxy registry. - function VERSION() external view returns (string memory); - - /*////////////////////////////////////////////////////////////////////////// - NON-CONSTANT FUNCTIONS - //////////////////////////////////////////////////////////////////////////*/ - - /// @notice Installs the provided plugin contract. - /// - /// @dev Emits an {InstallPlugin} event. - /// - /// Notes: - /// - Does not revert if the plugin is installed. - /// - Installing a plugin is a potentially dangerous operation, because anyone can then call the plugin's methods. - /// - /// Requirements: - /// - The plugin must have at least one implemented method. - /// - By design, the plugin cannot implement any method that is also implemented by the proxy itself. - /// - /// @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 owner. - /// - /// @dev Emits a {SetPermission} event. - /// - /// Notes: - /// - It is not an error to set the same permission. - /// - /// @param envoy The address of the envoy account. - /// @param target The address of the target contract. - /// @param permission The boolean permission to set. - function setPermission(address envoy, address target, bool permission) external; - - /// @notice Uninstalls the provided plugin contract. - /// - /// @dev Emits an {UninstallPlugin} event. - /// - /// Notes: - /// - Does not revert if the plugin is not installed. - /// - /// Requirements: - /// - The plugin must have at least one implemented method. - /// - /// @param plugin The address of the plugin to uninstall. - function uninstallPlugin(IPRBProxyPlugin plugin) external; -} diff --git a/src/interfaces/IPRBProxyPlugin.sol b/src/interfaces/IPRBProxyPlugin.sol index 64324a0..7169d69 100644 --- a/src/interfaces/IPRBProxyPlugin.sol +++ b/src/interfaces/IPRBProxyPlugin.sol @@ -1,19 +1,13 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.4; -import { IPRBProxyStorage } from "./IPRBProxyStorage.sol"; - /// @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. /// -/// A couple of notes about this interface: -/// -/// - It is not meant to be inherited by plugin implementations, which should inherit from {PRBProxyPlugin}. -/// - It should be used only for casting addresses to the interface type. -/// - It inherits from {IPRBProxyStorage} to enable plugins to access the proxy's storage. -interface IPRBProxyPlugin is IPRBProxyStorage { +/// 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. /// diff --git a/src/interfaces/IPRBProxyRegistry.sol b/src/interfaces/IPRBProxyRegistry.sol index c834d8d..77b15b5 100644 --- a/src/interfaces/IPRBProxyRegistry.sol +++ b/src/interfaces/IPRBProxyRegistry.sol @@ -2,6 +2,7 @@ pragma solidity >=0.8.4; 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 @@ -11,11 +12,14 @@ interface IPRBProxyRegistry { ERRORS //////////////////////////////////////////////////////////////////////////*/ + /// @notice Thrown when an action requires the caller to have a proxy. + error PRBProxyRegistry_CallerDoesNotHaveProxy(address caller); + /// @notice Thrown when an action requires the owner to not have a proxy. error PRBProxyRegistry_OwnerHasProxy(address owner, IPRBProxy proxy); - /// @notice Thrown when an action requires the owner to have a proxy. - error PRBProxyRegistry_OwnerDoesNotHaveProxy(address owner); + /// @notice Thrown when installing or uninstall a plugin, and the plugin doesn't implement any method. + error PRBProxyRegistry_PluginEmptyMethodList(IPRBProxyPlugin plugin); /*////////////////////////////////////////////////////////////////////////// EVENTS @@ -31,6 +35,17 @@ interface IPRBProxyRegistry { IPRBProxy proxy ); + /// @notice Emitted when a plugin is installed. + event InstallPlugin(address indexed owner, IPRBProxy indexed proxy, IPRBProxyPlugin indexed plugin); + + /// @notice Emitted when an envoy permission is updated. + event SetPermission( + address indexed owner, IPRBProxy indexed proxy, address indexed envoy, address target, bool permission + ); + + /// @notice Emitted when a plugin is uninstalled. + event UninstallPlugin(address indexed owner, IPRBProxy indexed proxy, IPRBProxyPlugin indexed plugin); + /*////////////////////////////////////////////////////////////////////////// STRUCTS //////////////////////////////////////////////////////////////////////////*/ @@ -48,7 +63,7 @@ interface IPRBProxyRegistry { CONSTANT FUNCTIONS //////////////////////////////////////////////////////////////////////////*/ - /// @notice The release version of the proxy system. + /// @notice The release version of the proxy system, which applies to both the registry and deployed proxies. /// @dev This is stored in the registry rather than the proxy to save gas for end users. function VERSION() external view returns (string memory); @@ -57,8 +72,48 @@ interface IPRBProxyRegistry { /// @dev The proxy constructor fetches these parameters. function constructorParams() external view returns (address owner, address target, bytes memory data); + /// @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. + function getPermissionByOwner( + address owner, + address envoy, + address target + ) + external + view + 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. + function getPermissionByProxy( + IPRBProxy proxy, + address envoy, + address target + ) + external + 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. + 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. + function getPluginByProxy(IPRBProxy proxy, bytes4 method) external view returns (IPRBProxyPlugin plugin); + /// @notice Retrieves the proxy for the provided owner. - /// @param owner The address of the user to make the query for. + /// @param owner The user address to make the query for. function getProxy(address owner) external view returns (IPRBProxy proxy); /// @notice The seed that will be used to deploy the next proxy for the provided origin. @@ -103,4 +158,50 @@ interface IPRBProxyRegistry { /// @param owner The owner of the proxy. /// @return proxy The address of the newly deployed proxy contract. function deployFor(address owner) external returns (IPRBProxy proxy); + + /// @notice Installs the provided plugin contract on the proxy belonging to the caller. + /// + /// @dev Emits an {InstallPlugin} event. + /// + /// Notes: + /// - Does not revert if the plugin is installed. + /// - Installing a plugin is a potentially dangerous operation, because anyone will be able to run the plugin. + /// + /// Requirements: + /// - The caller must have a proxy. + /// - The plugin must have at least one implemented method. + /// - By design, the plugin cannot implement any method that is also implemented by the proxy itself. + /// + /// @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. + /// + /// @dev Emits a {SetPermission} event. + /// + /// Notes: + /// - It is not an error to set the same permission. + /// + /// Requirements: + /// - The caller must have a proxy. + /// + /// @param envoy The address of the envoy account. + /// @param target The address of the target contract. + /// @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. + /// + /// @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. + /// + /// @param plugin The address of the plugin to uninstall. + function uninstallPlugin(IPRBProxyPlugin plugin) external; } diff --git a/src/interfaces/IPRBProxyStorage.sol b/src/interfaces/IPRBProxyStorage.sol deleted file mode 100644 index 30a3e03..0000000 --- a/src/interfaces/IPRBProxyStorage.sol +++ /dev/null @@ -1,16 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity >=0.8.4; - -import { IPRBProxyPlugin } from "./IPRBProxyPlugin.sol"; - -/// @title IPRBProxyStorage -/// @dev Interface for accessing the proxy's storage. -interface IPRBProxyStorage { - /// @notice The address of the plugin contract installed for the provided method. - /// @dev The zero address is returned if no plugin contract is installed. - /// @param method The method's signature for the query. - function plugins(bytes4 method) external view returns (IPRBProxyPlugin plugin); - - /// @notice A boolean flag that indicates whether the envoy has permission to call the provided target contract. - function permissions(address envoy, address target) external view returns (bool permission); -} diff --git a/test/Base.t.sol b/test/Base.t.sol index aec6d85..37d11f7 100644 --- a/test/Base.t.sol +++ b/test/Base.t.sol @@ -5,13 +5,9 @@ import { eqString } from "@prb/test/Helpers.sol"; import { StdCheats } from "forge-std/StdCheats.sol"; import { StdUtils } from "forge-std/StdUtils.sol"; -import { DeploySystem } from "../script/DeploySystem.s.sol"; import { IPRBProxy } from "../src/interfaces/IPRBProxy.sol"; -import { IPRBProxyAnnex } from "../src/interfaces/IPRBProxyAnnex.sol"; -import { IPRBProxyPlugin } from "../src/interfaces/IPRBProxyPlugin.sol"; import { IPRBProxyRegistry } from "../src/interfaces/IPRBProxyRegistry.sol"; import { PRBProxy } from "../src/PRBProxy.sol"; -import { PRBProxyAnnex } from "../src/PRBProxyAnnex.sol"; import { PRBProxyRegistry } from "../src/PRBProxyRegistry.sol"; import { PluginDummy } from "./mocks/plugins/PluginDummy.sol"; @@ -81,7 +77,6 @@ abstract contract Base_Test is Assertions, Events, StdCheats, StdUtils { TEST CONTRACTS //////////////////////////////////////////////////////////////////////////*/ - IPRBProxyAnnex internal annex; IPRBProxy internal proxy; IPRBProxyRegistry internal registry; @@ -118,8 +113,8 @@ abstract contract Base_Test is Assertions, Events, StdCheats, StdUtils { selfDestructer: new TargetSelfDestructer() }); - // Deploy the proxy system. - deploySystemConditionally(); + // Deploy the proxy registry. + deployRegistryConditionally(); // Make Alice both the caller and the origin. vm.startPrank({ msgSender: users.alice, txOrigin: users.alice }); @@ -143,26 +138,19 @@ abstract contract Base_Test is Assertions, Events, StdCheats, StdUtils { vm.deal({ account: addr, newBalance: 100 ether }); } - /// @dev Deploys {PRBProxyAnnex} from a source precompiled with `--via-ir`. - function deployPrecompiledAnnex() internal returns (IPRBProxyAnnex annex_) { - annex_ = IPRBProxyAnnex(deployCode("out-optimized/PRBProxyAnnex.sol/PRBProxyAnnex.json")); - } - /// @dev Deploys {PRBProxyRegistry} from a source precompiled with `--via-ir`. function deployPrecompiledRegistry() internal returns (IPRBProxyRegistry registry_) { registry_ = IPRBProxyRegistry(deployCode("out-optimized/PRBProxyRegistry.sol/PRBProxyRegistry.json")); } - /// @dev Conditionally deploy the proxy system either normally or from a source precompiled with `--via-ir`. - function deploySystemConditionally() internal { + /// @dev Conditionally deploy the registry either normally or from a source precompiled with `--via-ir`. + function deployRegistryConditionally() internal { if (!isTestOptimizedProfile()) { - (annex, registry) = new DeploySystem().run(); + registry = new PRBProxyRegistry(); } else { - annex = deployPrecompiledAnnex(); registry = deployPrecompiledRegistry(); } - vm.label({ account: address(annex), newLabel: "Annex" }); vm.label({ account: address(registry), newLabel: "Registry" }); } @@ -180,26 +168,4 @@ abstract contract Base_Test is Assertions, Events, StdCheats, StdUtils { string memory profile = vm.envOr("FOUNDRY_PROFILE", string("")); return eqString(profile, "test-optimized"); } - - /*////////////////////////////////////////////////////////////////////////// - ABI ENCODERS - //////////////////////////////////////////////////////////////////////////*/ - - /// @dev ABI encodes the parameters and calls {PRBProxyAnnex.installPlugin}. - function installPlugin(IPRBProxyPlugin plugin) internal { - bytes memory data = abi.encodeCall(annex.installPlugin, (plugin)); - proxy.execute({ target: address(annex), data: data }); - } - - /// @dev ABI encodes the parameters and calls {PRBProxyAnnex.setPermission}. - function setPermission(address envoy, address target, bool permission) internal { - bytes memory data = abi.encodeCall(annex.setPermission, (envoy, target, permission)); - proxy.execute({ target: address(annex), data: data }); - } - - /// @dev ABI encodes the parameters and calls {PRBProxyAnnex.uninstallPlugin}. - function uninstallPlugin(IPRBProxyPlugin plugin) internal { - bytes memory data = abi.encodeCall(annex.uninstallPlugin, (plugin)); - proxy.execute({ target: address(annex), data: data }); - } } diff --git a/test/annex/Annex.t.sol b/test/annex/Annex.t.sol deleted file mode 100644 index cdbbe43..0000000 --- a/test/annex/Annex.t.sol +++ /dev/null @@ -1,27 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity >=0.8.19 <=0.9.0; - -import { Base_Test } from "../Base.t.sol"; - -contract Annex_Test is Base_Test { - /*////////////////////////////////////////////////////////////////////////// - VARIABLES - //////////////////////////////////////////////////////////////////////////*/ - - address internal owner; - - /*////////////////////////////////////////////////////////////////////////// - SET-UP FUNCTION - //////////////////////////////////////////////////////////////////////////*/ - - function setUp() public virtual override { - Base_Test.setUp(); - - // Make Alice the owner of the default proxy. - owner = users.alice; - - // Deploy and label the default proxy. - proxy = registry.deployFor({ owner: users.alice }); - vm.label({ account: address(proxy), newLabel: "Default Proxy" }); - } -} diff --git a/test/annex/install-plugin/installPlugin.t.sol b/test/annex/install-plugin/installPlugin.t.sol deleted file mode 100644 index d2e1363..0000000 --- a/test/annex/install-plugin/installPlugin.t.sol +++ /dev/null @@ -1,60 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity >=0.8.18 <0.9.0; - -import { IPRBProxyAnnex } from "src/interfaces/IPRBProxyAnnex.sol"; -import { IPRBProxyPlugin } from "src/interfaces/IPRBProxyPlugin.sol"; - -import { Annex_Test } from "../Annex.t.sol"; - -contract InstallPlugin_Test is Annex_Test { - function test_RevertWhen_PluginHasNoMethods() external { - vm.expectRevert(abi.encodeWithSelector(IPRBProxyAnnex.PRBProxy_NoPluginMethods.selector, plugins.empty)); - installPlugin(plugins.empty); - } - - modifier whenPluginHasMethods() { - _; - } - - function test_InstallPlugin_PluginInstalledBefore() external whenPluginHasMethods whenPluginNotInstalled { - // Install a dummy plugin that has some methods. - installPlugin(plugins.dummy); - - // Install the same plugin again. - installPlugin(plugins.dummy); - - // Assert that every plugin method has been installed. - bytes4[] memory pluginMethods = plugins.dummy.methodList(); - for (uint256 i = 0; i < pluginMethods.length; ++i) { - IPRBProxyPlugin actualPlugin = proxy.plugins(pluginMethods[i]); - IPRBProxyPlugin expectedPlugin = plugins.dummy; - assertEq(actualPlugin, expectedPlugin, "plugin method not installed"); - } - } - - modifier whenPluginNotInstalled() { - _; - } - - function test_InstallPlugin() external whenPluginHasMethods whenPluginNotInstalled { - // Install a dummy plugin that has some methods. - installPlugin(plugins.dummy); - - // Assert that every plugin method has been installed. - bytes4[] memory pluginMethods = plugins.dummy.methodList(); - for (uint256 i = 0; i < pluginMethods.length; ++i) { - IPRBProxyPlugin actualPlugin = proxy.plugins(pluginMethods[i]); - IPRBProxyPlugin expectedPlugin = plugins.dummy; - assertEq(actualPlugin, expectedPlugin, "plugin method not installed"); - } - } - - function test_InstallPlugin_Event() external whenPluginHasMethods whenPluginNotInstalled { - // Expect an {InstallPlugin} event. - vm.expectEmit({ emitter: address(proxy) }); - emit InstallPlugin(plugins.dummy); - - // Install the dummy plugin. - installPlugin(plugins.dummy); - } -} diff --git a/test/annex/install-plugin/installPlugin.tree b/test/annex/install-plugin/installPlugin.tree deleted file mode 100644 index a874b78..0000000 --- a/test/annex/install-plugin/installPlugin.tree +++ /dev/null @@ -1,9 +0,0 @@ -installPlugin.t.sol -├── when the plugin has no methods -│ └── it should revert -└── when the plugin has methods - ├── when the plugin has been installed before - │ └── it should do nothing - └── when the plugin has not been installed - ├── it should install the plugin - └── it should emit an {InstallPlugin} event diff --git a/test/annex/set-permission/setPermission.t.sol b/test/annex/set-permission/setPermission.t.sol deleted file mode 100644 index 2eb71ac..0000000 --- a/test/annex/set-permission/setPermission.t.sol +++ /dev/null @@ -1,39 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity >=0.8.19 <=0.9.0; - -import { Annex_Test } from "../Annex.t.sol"; - -contract SetPermission_Test is Annex_Test { - function test_SetPermission_PermissionNotSet() external { - setPermission({ envoy: users.envoy, target: address(targets.dummy), permission: true }); - bool permission = proxy.permissions({ envoy: users.envoy, target: address(targets.dummy) }); - assertTrue(permission, "permission mismatch"); - } - - modifier whenPermissionSet() { - setPermission({ envoy: users.envoy, target: address(targets.dummy), permission: true }); - _; - } - - function test_SetPermission_ResetPermission() external whenPermissionSet { - setPermission({ envoy: users.envoy, target: address(targets.dummy), permission: true }); - bool permission = proxy.permissions({ envoy: users.envoy, target: address(targets.dummy) }); - assertTrue(permission, "permission mismatch"); - } - - function test_SetPermission_ResetPermission_Event() external whenPermissionSet { - vm.expectEmit({ emitter: address(proxy) }); - emit SetPermission({ envoy: users.envoy, target: address(targets.dummy), permission: true }); - setPermission({ envoy: users.envoy, target: address(targets.dummy), permission: true }); - } - - function test_SetPermission_UnsetPermission() external whenPermissionSet { - setPermission({ envoy: users.envoy, target: address(targets.dummy), permission: false }); - } - - function test_SetPermission_UnsetPermission_Event() external whenPermissionSet { - vm.expectEmit({ emitter: address(proxy) }); - emit SetPermission({ envoy: users.envoy, target: address(targets.dummy), permission: false }); - setPermission({ envoy: users.envoy, target: address(targets.dummy), permission: false }); - } -} diff --git a/test/annex/uninstall-plugin/uninstallPlugin.t.sol b/test/annex/uninstall-plugin/uninstallPlugin.t.sol deleted file mode 100644 index 79a60ce..0000000 --- a/test/annex/uninstall-plugin/uninstallPlugin.t.sol +++ /dev/null @@ -1,59 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity >=0.8.18 <0.9.0; - -import { IPRBProxyAnnex } from "src/interfaces/IPRBProxyAnnex.sol"; -import { IPRBProxyPlugin } from "src/interfaces/IPRBProxyPlugin.sol"; - -import { Annex_Test } from "../Annex.t.sol"; - -contract UninstallPlugin_Test is Annex_Test { - function test_RevertWhen_PluginHasNoMethods() external { - vm.expectRevert(abi.encodeWithSelector(IPRBProxyAnnex.PRBProxy_NoPluginMethods.selector, plugins.empty)); - uninstallPlugin(plugins.empty); - } - - modifier whenPluginHasMethods() { - _; - } - - function test_UninstallPlugin_PluginNotInstalledBefore() external whenPluginHasMethods { - // Uninstall the plugin. - 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 = proxy.plugins(pluginMethods[i]); - IPRBProxyPlugin expectedPlugin = IPRBProxyPlugin(address(0)); - assertEq(actualPlugin, expectedPlugin, "plugin method still installed"); - } - } - - modifier whenPluginInstalled() { - // Install the dummy plugin. - installPlugin(plugins.dummy); - _; - } - - function test_UninstallPlugin() external whenPluginHasMethods whenPluginInstalled { - // Uninstall the plugin. - 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 = proxy.plugins(pluginMethods[i]); - IPRBProxyPlugin expectedPlugin = IPRBProxyPlugin(address(0)); - assertEq(actualPlugin, expectedPlugin, "plugin method still installed"); - } - } - - function test_UninstallPlugin_Event() external whenPluginHasMethods whenPluginInstalled { - // Expect an {UninstallPlugin} event to be emitted. - vm.expectEmit({ emitter: address(proxy) }); - emit UninstallPlugin(plugins.dummy); - - // Uninstall the dummy plugin. - uninstallPlugin(plugins.dummy); - } -} diff --git a/test/annex/version/version.t.sol b/test/annex/version/version.t.sol deleted file mode 100644 index 450af61..0000000 --- a/test/annex/version/version.t.sol +++ /dev/null @@ -1,12 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity >=0.8.19 <=0.9.0; - -import { Annex_Test } from "../Annex.t.sol"; - -contract Version_Test is Annex_Test { - function test_Version() external { - string memory actualVersion = annex.VERSION(); - string memory expectedVersion = "4.0.0-beta.5"; - assertEq(actualVersion, expectedVersion, "annex version mismatch"); - } -} diff --git a/test/mocks/plugins/PluginDummy.sol b/test/mocks/plugins/PluginDummy.sol index 81e3133..fcf87c6 100644 --- a/test/mocks/plugins/PluginDummy.sol +++ b/test/mocks/plugins/PluginDummy.sol @@ -1,11 +1,11 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.18; -import { PRBProxyPlugin } from "../../../src/abstracts/PRBProxyPlugin.sol"; +import { IPRBProxyPlugin } from "../../../src/interfaces/IPRBProxyPlugin.sol"; import { TargetDummy } from "../targets/TargetDummy.sol"; -contract PluginDummy is PRBProxyPlugin, TargetDummy { +contract PluginDummy is IPRBProxyPlugin, TargetDummy { function methodList() external pure override returns (bytes4[] memory) { bytes4[] memory methods = new bytes4[](2); methods[0] = this.foo.selector; diff --git a/test/mocks/plugins/PluginEcho.sol b/test/mocks/plugins/PluginEcho.sol index 2b80301..13392f5 100644 --- a/test/mocks/plugins/PluginEcho.sol +++ b/test/mocks/plugins/PluginEcho.sol @@ -1,11 +1,11 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.18; -import { PRBProxyPlugin } from "../../../src/abstracts/PRBProxyPlugin.sol"; +import { IPRBProxyPlugin } from "../../../src/interfaces/IPRBProxyPlugin.sol"; import { TargetEcho } from "../targets/TargetEcho.sol"; -contract PluginEcho is PRBProxyPlugin, TargetEcho { +contract PluginEcho is IPRBProxyPlugin, TargetEcho { function methodList() external pure override returns (bytes4[] memory) { bytes4[] memory methods = new bytes4[](9); diff --git a/test/mocks/plugins/PluginEmpty.sol b/test/mocks/plugins/PluginEmpty.sol index 9728487..46413ea 100644 --- a/test/mocks/plugins/PluginEmpty.sol +++ b/test/mocks/plugins/PluginEmpty.sol @@ -1,9 +1,9 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.18; -import { PRBProxyPlugin } from "../../../src/abstracts/PRBProxyPlugin.sol"; +import { IPRBProxyPlugin } from "../../../src/interfaces/IPRBProxyPlugin.sol"; -contract PluginEmpty is PRBProxyPlugin { +contract PluginEmpty is IPRBProxyPlugin { function methodList() 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 f946287..a46f939 100644 --- a/test/mocks/plugins/PluginPanic.sol +++ b/test/mocks/plugins/PluginPanic.sol @@ -1,10 +1,10 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.18; -import { PRBProxyPlugin } from "../../../src/abstracts/PRBProxyPlugin.sol"; +import { IPRBProxyPlugin } from "../../../src/interfaces/IPRBProxyPlugin.sol"; import { TargetPanic } from "../targets/TargetPanic.sol"; -contract PluginPanic is PRBProxyPlugin, TargetPanic { +contract PluginPanic is IPRBProxyPlugin, TargetPanic { function methodList() external pure override returns (bytes4[] memory) { bytes4[] memory methods = new bytes4[](4); diff --git a/test/mocks/plugins/PluginReverter.sol b/test/mocks/plugins/PluginReverter.sol index 1fdb2b3..fb9fc1e 100644 --- a/test/mocks/plugins/PluginReverter.sol +++ b/test/mocks/plugins/PluginReverter.sol @@ -1,11 +1,11 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.18; -import { PRBProxyPlugin } from "../../../src/abstracts/PRBProxyPlugin.sol"; +import { IPRBProxyPlugin } from "../../../src/interfaces/IPRBProxyPlugin.sol"; import { TargetReverter } from "../targets/TargetReverter.sol"; -contract PluginReverter is PRBProxyPlugin, TargetReverter { +contract PluginReverter is IPRBProxyPlugin, TargetReverter { function methodList() external pure override returns (bytes4[] memory) { bytes4[] memory methods = new bytes4[](5); diff --git a/test/mocks/plugins/PluginSelfDestructer.sol b/test/mocks/plugins/PluginSelfDestructer.sol index 9ec842f..da3f732 100644 --- a/test/mocks/plugins/PluginSelfDestructer.sol +++ b/test/mocks/plugins/PluginSelfDestructer.sol @@ -1,11 +1,11 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.18; -import { PRBProxyPlugin } from "../../../src/abstracts/PRBProxyPlugin.sol"; +import { IPRBProxyPlugin } from "../../../src/interfaces/IPRBProxyPlugin.sol"; import { TargetSelfDestructer } from "../targets/TargetSelfDestructer.sol"; -contract PluginSelfDestructer is PRBProxyPlugin, TargetSelfDestructer { +contract PluginSelfDestructer is IPRBProxyPlugin, TargetSelfDestructer { function methodList() external pure override returns (bytes4[] memory) { bytes4[] memory methods = new bytes4[](1); methods[0] = this.destroyMe.selector; diff --git a/test/mocks/targets/TargetDummy.sol b/test/mocks/targets/TargetDummy.sol index 4da1f57..9d98339 100644 --- a/test/mocks/targets/TargetDummy.sol +++ b/test/mocks/targets/TargetDummy.sol @@ -1,9 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.18; -import { PRBProxyStorage } from "../../../src/abstracts/PRBProxyStorage.sol"; - -contract TargetDummy is PRBProxyStorage { +contract TargetDummy { function foo() external pure returns (string memory) { return "foo"; } diff --git a/test/mocks/targets/TargetDummyWithFallback.sol b/test/mocks/targets/TargetDummyWithFallback.sol index 7225299..33bff98 100644 --- a/test/mocks/targets/TargetDummyWithFallback.sol +++ b/test/mocks/targets/TargetDummyWithFallback.sol @@ -1,9 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.18 <0.9.0; -import { PRBProxyStorage } from "../../../src/abstracts/PRBProxyStorage.sol"; - -contract TargetDummyWithFallback is PRBProxyStorage { +contract TargetDummyWithFallback { event LogFallback(); fallback() external payable { diff --git a/test/mocks/targets/TargetEcho.sol b/test/mocks/targets/TargetEcho.sol index 579a7d6..62a1848 100644 --- a/test/mocks/targets/TargetEcho.sol +++ b/test/mocks/targets/TargetEcho.sol @@ -1,9 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.18; -import { PRBProxyStorage } from "../../../src/abstracts/PRBProxyStorage.sol"; - -contract TargetEcho is PRBProxyStorage { +contract TargetEcho { struct SomeStruct { uint256 foo; uint256 bar; diff --git a/test/mocks/targets/TargetPanic.sol b/test/mocks/targets/TargetPanic.sol index bb742ff..44c87e6 100644 --- a/test/mocks/targets/TargetPanic.sol +++ b/test/mocks/targets/TargetPanic.sol @@ -1,9 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.18; -import { PRBProxyStorage } from "../../../src/abstracts/PRBProxyStorage.sol"; - -contract TargetPanic is PRBProxyStorage { +contract TargetPanic { function failedAssertion() external pure { assert(false); } diff --git a/test/mocks/targets/TargetPayable.sol b/test/mocks/targets/TargetPayable.sol index 650b0b9..4f63552 100644 --- a/test/mocks/targets/TargetPayable.sol +++ b/test/mocks/targets/TargetPayable.sol @@ -1,9 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.18 <0.9.0; -import { PRBProxyStorage } from "../../../src/abstracts/PRBProxyStorage.sol"; - -contract TargetPayable is PRBProxyStorage { +contract TargetPayable { function revertLackPayableModifier() external payable returns (uint256) { return 0; } diff --git a/test/mocks/targets/TargetReverter.sol b/test/mocks/targets/TargetReverter.sol index 03d2057..295e7e7 100644 --- a/test/mocks/targets/TargetReverter.sol +++ b/test/mocks/targets/TargetReverter.sol @@ -1,9 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.18; -import { PRBProxyStorage } from "../../../src/abstracts/PRBProxyStorage.sol"; - -contract TargetReverter is PRBProxyStorage { +contract TargetReverter { error SomeError(); function withNothing() external pure { diff --git a/test/mocks/targets/TargetSelfDestructer.sol b/test/mocks/targets/TargetSelfDestructer.sol index 17314d8..404a55d 100644 --- a/test/mocks/targets/TargetSelfDestructer.sol +++ b/test/mocks/targets/TargetSelfDestructer.sol @@ -1,9 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.18; -import { PRBProxyStorage } from "../../../src/abstracts/PRBProxyStorage.sol"; - -contract TargetSelfDestructer is PRBProxyStorage { +contract TargetSelfDestructer { function destroyMe(address payable recipient) external { selfdestruct(recipient); } diff --git a/test/proxy/execute/execute.t.sol b/test/proxy/execute/execute.t.sol index bcba1e2..7c9b736 100644 --- a/test/proxy/execute/execute.t.sol +++ b/test/proxy/execute/execute.t.sol @@ -31,7 +31,7 @@ contract Execute_Test is Proxy_Test { } function test_RevertWhen_PermissionDifferentTarget() external whenCallerUnauthorized { - setPermission({ envoy: users.envoy, target: address(targets.echo), permission: true }); + registry.setPermission({ envoy: users.envoy, target: address(targets.echo), permission: true }); changePrank({ msgSender: users.envoy }); bytes memory data = bytes.concat(targets.dummy.foo.selector); @@ -200,7 +200,7 @@ contract Execute_Test is Proxy_Test { } modifier whenTargetDoesNotSelfDestruct() { - setPermission({ envoy: users.envoy, target: address(targets.echo), permission: true }); + registry.setPermission({ envoy: users.envoy, target: address(targets.echo), permission: true }); _; } diff --git a/test/proxy/run-plugin/runPlugin.t.sol b/test/proxy/run-plugin/runPlugin.t.sol index d313c88..3ee89ef 100644 --- a/test/proxy/run-plugin/runPlugin.t.sol +++ b/test/proxy/run-plugin/runPlugin.t.sol @@ -33,56 +33,56 @@ contract RunPlugin_Test is Proxy_Test { } function test_RevertWhen_Panic_FailedAssertion() external whenPluginInstalled whenDelegateCallReverts { - installPlugin(plugins.panic); + registry.installPlugin(plugins.panic); vm.expectRevert(stdError.assertionError); (bool success,) = address(proxy).call(abi.encodeWithSelector(plugins.panic.failedAssertion.selector)); success; } function test_RevertWhen_Panic_ArithmeticOverflow() external whenPluginInstalled whenDelegateCallReverts { - installPlugin(plugins.panic); + registry.installPlugin(plugins.panic); vm.expectRevert(stdError.arithmeticError); (bool success,) = address(proxy).call(abi.encodeWithSelector(plugins.panic.arithmeticOverflow.selector)); success; } function test_RevertWhen_Panic_DivisionByZero() external whenPluginInstalled whenDelegateCallReverts { - installPlugin(plugins.panic); + registry.installPlugin(plugins.panic); vm.expectRevert(stdError.arithmeticError); (bool success,) = address(proxy).call(abi.encodeWithSelector(plugins.panic.divisionByZero.selector)); success; } function test_RevertWhen_Panic_IndexOOB() external whenPluginInstalled whenDelegateCallReverts { - installPlugin(plugins.panic); + registry.installPlugin(plugins.panic); vm.expectRevert(stdError.arithmeticError); (bool success,) = address(proxy).call(abi.encodeWithSelector(plugins.panic.indexOOB.selector)); success; } function test_RevertWhen_Error_EmptyRevertStatement() external whenPluginInstalled whenDelegateCallReverts { - installPlugin(plugins.reverter); + registry.installPlugin(plugins.reverter); vm.expectRevert(IPRBProxy.PRBProxy_PluginReverted.selector); (bool success,) = address(proxy).call(abi.encodeWithSelector(plugins.reverter.withNothing.selector)); success; } function test_RevertWhen_Error_CustomError() external whenPluginInstalled whenDelegateCallReverts { - installPlugin(plugins.reverter); + registry.installPlugin(plugins.reverter); vm.expectRevert(TargetReverter.SomeError.selector); (bool success,) = address(proxy).call(abi.encodeWithSelector(plugins.reverter.withCustomError.selector)); success; } function test_RevertWhen_Error_Require() external whenPluginInstalled whenDelegateCallReverts { - installPlugin(plugins.reverter); + registry.installPlugin(plugins.reverter); vm.expectRevert(TargetReverter.SomeError.selector); (bool success,) = address(proxy).call(abi.encodeWithSelector(plugins.reverter.withRequire.selector)); success; } function test_RevertWhen_Error_ReasonString() external whenPluginInstalled whenDelegateCallReverts { - installPlugin(plugins.reverter); + registry.installPlugin(plugins.reverter); vm.expectRevert("You shall not pass"); (bool success,) = address(proxy).call(abi.encodeWithSelector(plugins.reverter.withReasonString.selector)); success; @@ -98,7 +98,7 @@ contract RunPlugin_Test is Proxy_Test { whenDelegateCallReverts whenDelegateCallDoesNotRevert { - installPlugin(plugins.echo); + registry.installPlugin(plugins.echo); uint256 amount = 0.1 ether; (, bytes memory actualResponse) = address(proxy).call{ value: amount }(abi.encodeWithSelector(plugins.echo.echoMsgValue.selector)); @@ -125,7 +125,7 @@ contract RunPlugin_Test is Proxy_Test { vm.deal({ account: address(proxy), newBalance: proxyBalance }); // Install the plugin and run it. - installPlugin(plugins.selfDestructer); + registry.installPlugin(plugins.selfDestructer); (bool success,) = address(proxy).call(abi.encodeWithSelector(plugins.selfDestructer.destroyMe.selector, users.bob)); success; @@ -148,7 +148,7 @@ contract RunPlugin_Test is Proxy_Test { whenNoEtherSent whenPluginDoesNotSelfDestruct { - installPlugin(plugins.dummy); + registry.installPlugin(plugins.dummy); (, bytes memory actualResponse) = address(proxy).call(abi.encodeWithSelector(plugins.dummy.foo.selector)); bytes memory expectedResponse = abi.encode(bytes("foo")); assertEq(actualResponse, expectedResponse, "dummy.foo response mismatch"); @@ -162,7 +162,7 @@ contract RunPlugin_Test is Proxy_Test { whenNoEtherSent whenPluginDoesNotSelfDestruct { - installPlugin(plugins.dummy); + registry.installPlugin(plugins.dummy); vm.expectEmit({ emitter: address(proxy) }); emit RunPlugin({ plugin: plugins.dummy, diff --git a/test/registry/get-permission-by-owner/getPermissionByOwner.t.sol b/test/registry/get-permission-by-owner/getPermissionByOwner.t.sol new file mode 100644 index 0000000..f2111f4 --- /dev/null +++ b/test/registry/get-permission-by-owner/getPermissionByOwner.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 GetPermissionByOwner_Test is Registry_Test { + function setUp() public virtual override { + Registry_Test.setUp(); + proxy = registry.deploy(); + } + + function test_GetPermissionByOwner_EnvoyDoesNotHavePermission() external { + bool permission = + registry.getPermissionByProxy({ proxy: proxy, envoy: users.envoy, target: address(targets.dummy) }); + assertFalse(permission, "permission mismatch"); + } + + modifier whenEnvoyHasPermission() { + registry.setPermission({ envoy: users.envoy, target: address(targets.dummy), permission: true }); + _; + } + + function test_GetPermissionByOwner() external whenEnvoyHasPermission { + bool permission = + registry.getPermissionByProxy({ proxy: proxy, envoy: users.envoy, target: address(targets.dummy) }); + assertTrue(permission, "permission mismatch"); + } +} diff --git a/test/registry/get-permission-by-owner/getPermissionByOwner.tree b/test/registry/get-permission-by-owner/getPermissionByOwner.tree new file mode 100644 index 0000000..5283f05 --- /dev/null +++ b/test/registry/get-permission-by-owner/getPermissionByOwner.tree @@ -0,0 +1,5 @@ +getPermissionByOwner.t.sol +├── when the envoy does not have permission +│ └── it should return false +└── when the envoy has permission + └── it should return true diff --git a/test/registry/get-permission-by-proxy/getPermissionByProxy.t.sol b/test/registry/get-permission-by-proxy/getPermissionByProxy.t.sol new file mode 100644 index 0000000..cd17e74 --- /dev/null +++ b/test/registry/get-permission-by-proxy/getPermissionByProxy.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 GetPermissionByProxy_Test is Registry_Test { + function setUp() public virtual override { + Registry_Test.setUp(); + proxy = registry.deploy(); + } + + function test_GetPermissionByProxy_EnvoyDoesNotHavePermission() external { + bool permission = + registry.getPermissionByProxy({ proxy: proxy, envoy: users.envoy, target: address(targets.dummy) }); + assertFalse(permission, "permission mismatch"); + } + + modifier whenEnvoyHasPermission() { + registry.setPermission({ envoy: users.envoy, target: address(targets.dummy), permission: true }); + _; + } + + function test_GetPermissionByProxy() external whenEnvoyHasPermission { + bool permission = + registry.getPermissionByProxy({ proxy: proxy, envoy: users.envoy, target: address(targets.dummy) }); + assertTrue(permission, "permission mismatch"); + } +} diff --git a/test/registry/get-permission-by-proxy/getPermissionByProxy.tree b/test/registry/get-permission-by-proxy/getPermissionByProxy.tree new file mode 100644 index 0000000..1eeaf69 --- /dev/null +++ b/test/registry/get-permission-by-proxy/getPermissionByProxy.tree @@ -0,0 +1,5 @@ +getPermissionByProxy.t.sol +├── when the envoy does not have permission +│ └── it should return false +└── when the envoy has permission + └── it should return true diff --git a/test/registry/get-plugin-by-owner/getPluginByOwner.t.sol b/test/registry/get-plugin-by-owner/getPluginByOwner.t.sol new file mode 100644 index 0000000..e41becb --- /dev/null +++ b/test/registry/get-plugin-by-owner/getPluginByOwner.t.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity >=0.8.19 <=0.9.0; + +import { Registry_Test } from "../Registry.t.sol"; + +contract GetPluginByOwner_Test is Registry_Test { + function setUp() public virtual override { + Registry_Test.setUp(); + proxy = registry.deploy(); + } + + function test_GetPluginByOwner_Uninstalled() external { + address actualPlugin = + address(registry.getPluginByOwner({ owner: users.alice, method: plugins.dummy.foo.selector })); + address expectedPlugin = address(0); + assertEq(actualPlugin, expectedPlugin, "plugin not zero address"); + } + + modifier whenPluginInstalled() { + registry.installPlugin(plugins.dummy); + _; + } + + function test_GetPluginByOwner() external whenPluginInstalled { + address actualPlugin = + address(registry.getPluginByOwner({ owner: users.alice, method: plugins.dummy.foo.selector })); + address expectedPlugin = address(plugins.dummy); + assertEq(actualPlugin, expectedPlugin, "plugin address mismatch"); + } +} diff --git a/test/registry/get-plugin-by-owner/getPluginByOwner.tree b/test/registry/get-plugin-by-owner/getPluginByOwner.tree new file mode 100644 index 0000000..2e681a9 --- /dev/null +++ b/test/registry/get-plugin-by-owner/getPluginByOwner.tree @@ -0,0 +1,5 @@ +getPluginByOwner.t.sol +├── when the plugin is not installed +│ └── it should return the zero address +└── when the plugin is installed + └── it should return the plugin diff --git a/test/registry/get-plugin-by-proxy/getPluginByProxy.t.sol b/test/registry/get-plugin-by-proxy/getPluginByProxy.t.sol new file mode 100644 index 0000000..8b42d43 --- /dev/null +++ b/test/registry/get-plugin-by-proxy/getPluginByProxy.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 GetPluginByProxy_Test is Registry_Test { + function setUp() public virtual override { + Registry_Test.setUp(); + proxy = registry.deploy(); + } + + function test_GetPluginByProxy_Uninstalled() external { + address actualPlugin = address(registry.getPluginByProxy({ proxy: proxy, method: plugins.dummy.foo.selector })); + address expectedPlugin = address(0); + assertEq(actualPlugin, expectedPlugin, "plugin not zero address"); + } + + modifier whenPluginInstalled() { + registry.installPlugin(plugins.dummy); + _; + } + + function test_GetPluginByProxy() external whenPluginInstalled { + address actualPlugin = address(registry.getPluginByProxy({ proxy: proxy, method: plugins.dummy.foo.selector })); + address expectedPlugin = address(plugins.dummy); + assertEq(actualPlugin, expectedPlugin, "plugin address mismatch"); + } +} diff --git a/test/registry/get-plugin-by-proxy/getPluginByProxy.tree b/test/registry/get-plugin-by-proxy/getPluginByProxy.tree new file mode 100644 index 0000000..d664a7f --- /dev/null +++ b/test/registry/get-plugin-by-proxy/getPluginByProxy.tree @@ -0,0 +1,5 @@ +getPluginByProxy.t.sol +├── when the plugin is not installed +│ └── it should return the zero address +└── when the plugin is installed + └── it should return the plugin diff --git a/test/registry/install-plugin/installPlugin.t.sol b/test/registry/install-plugin/installPlugin.t.sol new file mode 100644 index 0000000..1324fde --- /dev/null +++ b/test/registry/install-plugin/installPlugin.t.sol @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity >=0.8.18 <0.9.0; + +import { IPRBProxyPlugin } from "src/interfaces/IPRBProxyPlugin.sol"; +import { IPRBProxyRegistry } from "src/interfaces/IPRBProxyRegistry.sol"; + +import { Registry_Test } from "../Registry.t.sol"; + +contract InstallPlugin_Test is Registry_Test { + function test_RevertWhen_CallerDoesNotHaveProxy() external { + vm.expectRevert( + abi.encodeWithSelector(IPRBProxyRegistry.PRBProxyRegistry_CallerDoesNotHaveProxy.selector, users.bob) + ); + changePrank({ msgSender: users.bob }); + registry.installPlugin(plugins.empty); + } + + modifier whenCallerHasProxy() { + proxy = registry.deploy(); + _; + } + + function test_RevertWhen_PluginEmptyMethodList() external whenCallerHasProxy { + vm.expectRevert( + abi.encodeWithSelector(IPRBProxyRegistry.PRBProxyRegistry_PluginEmptyMethodList.selector, plugins.empty) + ); + registry.installPlugin(plugins.empty); + } + + modifier whenPluginListNotEmpty() { + _; + } + + function test_InstallPlugin_PluginInstalledBefore() + external + whenCallerHasProxy + whenPluginListNotEmpty + whenPluginNotInstalled + { + // Install a dummy plugin that has some methods. + registry.installPlugin(plugins.dummy); + + // Install the same plugin again. + registry.installPlugin(plugins.dummy); + + // Assert that every plugin method has been installed. + bytes4[] memory pluginMethods = plugins.dummy.methodList(); + for (uint256 i = 0; i < pluginMethods.length; ++i) { + IPRBProxyPlugin actualPlugin = registry.getPluginByOwner({ owner: users.alice, method: pluginMethods[i] }); + IPRBProxyPlugin expectedPlugin = plugins.dummy; + assertEq(actualPlugin, expectedPlugin, "plugin method not installed"); + } + } + + modifier whenPluginNotInstalled() { + _; + } + + function test_InstallPlugin() external whenCallerHasProxy whenPluginListNotEmpty whenPluginNotInstalled { + // Install a dummy plugin that has some methods. + registry.installPlugin(plugins.dummy); + + // Assert that every plugin method has been installed. + bytes4[] memory pluginMethods = plugins.dummy.methodList(); + for (uint256 i = 0; i < pluginMethods.length; ++i) { + IPRBProxyPlugin actualPlugin = registry.getPluginByOwner({ owner: users.alice, method: pluginMethods[i] }); + IPRBProxyPlugin expectedPlugin = plugins.dummy; + assertEq(actualPlugin, expectedPlugin, "plugin method not installed"); + } + } + + // TODO: re-enable this test once this bug is fixed https://github.com/foundry-rs/foundry/pull/4920 + function test_InstallPlugin_Event() private whenCallerHasProxy whenPluginListNotEmpty whenPluginNotInstalled { + vm.expectEmit({ emitter: address(registry) }); + emit InstallPlugin({ owner: users.alice, proxy: proxy, plugin: plugins.dummy }); + registry.installPlugin(plugins.dummy); + } +} diff --git a/test/registry/install-plugin/installPlugin.tree b/test/registry/install-plugin/installPlugin.tree new file mode 100644 index 0000000..6d6aa7c --- /dev/null +++ b/test/registry/install-plugin/installPlugin.tree @@ -0,0 +1,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 + │ └── it should revert + └── when the plugin list is not empty + ├── when the plugin has been installed before + │ └── it should do nothing + └── when the plugin has not been installed + ├── it should install the plugin + └── it should emit an {InstallPlugin} event diff --git a/test/registry/set-permission/setPermission.t.sol b/test/registry/set-permission/setPermission.t.sol new file mode 100644 index 0000000..65602b1 --- /dev/null +++ b/test/registry/set-permission/setPermission.t.sol @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity >=0.8.19 <=0.9.0; + +import { IPRBProxyRegistry } from "src/interfaces/IPRBProxyRegistry.sol"; + +import { Registry_Test } from "../Registry.t.sol"; + +contract SetPermission_Test is Registry_Test { + function test_RevertWhen_CallerDoesNotHaveProxy() external { + vm.expectRevert( + abi.encodeWithSelector(IPRBProxyRegistry.PRBProxyRegistry_CallerDoesNotHaveProxy.selector, users.bob) + ); + changePrank({ msgSender: users.bob }); + registry.setPermission({ envoy: users.envoy, target: address(targets.dummy), permission: true }); + } + + modifier whenCallerHasProxy() { + proxy = registry.deploy(); + _; + } + + function test_SetPermission_PermissionNotSet() external whenCallerHasProxy { + registry.setPermission({ envoy: users.envoy, target: address(targets.dummy), permission: true }); + bool permission = + registry.getPermissionByOwner({ owner: users.alice, envoy: users.envoy, target: address(targets.dummy) }); + assertTrue(permission, "permission mismatch"); + } + + modifier whenPermissionSet() { + registry.setPermission({ envoy: users.envoy, target: address(targets.dummy), permission: true }); + _; + } + + function test_SetPermission_ResetPermission() external whenCallerHasProxy whenPermissionSet { + registry.setPermission({ envoy: users.envoy, target: address(targets.dummy), permission: true }); + bool permission = + registry.getPermissionByOwner({ owner: users.alice, envoy: users.envoy, target: address(targets.dummy) }); + assertTrue(permission, "permission mismatch"); + } + + // TODO: re-enable this test once this bug is fixed https://github.com/foundry-rs/foundry/pull/4920 + function test_SetPermission_ResetPermission_Event() private whenCallerHasProxy whenPermissionSet { + vm.expectEmit({ emitter: address(registry) }); + emit SetPermission({ + owner: users.alice, + proxy: proxy, + envoy: users.envoy, + target: address(targets.dummy), + permission: true + }); + registry.setPermission({ envoy: users.envoy, target: address(targets.dummy), permission: true }); + } + + function test_SetPermission_UnsetPermission() external whenCallerHasProxy whenPermissionSet { + registry.setPermission({ envoy: users.envoy, target: address(targets.dummy), permission: false }); + } + + // TODO: re-enable this test once this bug is fixed https://github.com/foundry-rs/foundry/pull/4920 + function test_SetPermission_UnsetPermission_Event() private whenCallerHasProxy whenPermissionSet { + vm.expectEmit({ emitter: address(registry) }); + emit SetPermission({ + owner: users.alice, + proxy: proxy, + envoy: users.envoy, + target: address(targets.dummy), + permission: false + }); + registry.setPermission({ envoy: users.envoy, target: address(targets.dummy), permission: false }); + } +} diff --git a/test/annex/set-permission/setPermission.tree b/test/registry/set-permission/setPermission.tree similarity index 100% rename from test/annex/set-permission/setPermission.tree rename to test/registry/set-permission/setPermission.tree diff --git a/test/registry/uninstall-plugin/uninstallPlugin.t.sol b/test/registry/uninstall-plugin/uninstallPlugin.t.sol new file mode 100644 index 0000000..4b4bf36 --- /dev/null +++ b/test/registry/uninstall-plugin/uninstallPlugin.t.sol @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity >=0.8.18 <0.9.0; + +import { IPRBProxyPlugin } from "src/interfaces/IPRBProxyPlugin.sol"; +import { IPRBProxyRegistry } from "src/interfaces/IPRBProxyRegistry.sol"; + +import { Registry_Test } from "../Registry.t.sol"; + +contract UninstallPlugin_Test is Registry_Test { + function test_RevertWhen_CallerDoesNotHaveProxy() external { + vm.expectRevert( + abi.encodeWithSelector(IPRBProxyRegistry.PRBProxyRegistry_CallerDoesNotHaveProxy.selector, users.bob) + ); + changePrank({ msgSender: users.bob }); + registry.uninstallPlugin(plugins.empty); + } + + modifier whenCallerHasProxy() { + proxy = registry.deploy(); + _; + } + + function test_RevertWhen_PluginEmptyMethodList() external whenCallerHasProxy { + vm.expectRevert( + abi.encodeWithSelector(IPRBProxyRegistry.PRBProxyRegistry_PluginEmptyMethodList.selector, plugins.empty) + ); + registry.uninstallPlugin(plugins.empty); + } + + modifier whenPluginHasMethods() { + _; + } + + function test_UninstallPlugin_PluginNotInstalledBefore() external whenCallerHasProxy whenPluginHasMethods { + // 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"); + } + } + + modifier whenPluginInstalled() { + // Install the dummy plugin. + registry.installPlugin(plugins.dummy); + _; + } + + function test_UninstallPlugin() external whenCallerHasProxy whenPluginHasMethods whenPluginInstalled { + // 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"); + } + } + + // TODO: re-enable this test once this bug is fixed https://github.com/foundry-rs/foundry/pull/4920 + function test_UninstallPlugin_Event() private whenCallerHasProxy whenPluginHasMethods whenPluginInstalled { + vm.expectEmit({ emitter: address(registry) }); + emit UninstallPlugin({ owner: users.alice, proxy: proxy, plugin: plugins.dummy }); + registry.uninstallPlugin(plugins.dummy); + } +} diff --git a/test/annex/uninstall-plugin/uninstallPlugin.tree b/test/registry/uninstall-plugin/uninstallPlugin.tree similarity index 82% rename from test/annex/uninstall-plugin/uninstallPlugin.tree rename to test/registry/uninstall-plugin/uninstallPlugin.tree index f5c3e43..2c9b5a4 100644 --- a/test/annex/uninstall-plugin/uninstallPlugin.tree +++ b/test/registry/uninstall-plugin/uninstallPlugin.tree @@ -2,9 +2,9 @@ uninstallPlugin.t.sol ├── when the caller is not the owner │ └── it should revert └── when the caller is the owner - ├── when the plugin has no methods + ├── when the plugin list is empty │ └── it should revert - └── when the plugin has methods + └── 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 diff --git a/test/utils/Events.sol b/test/utils/Events.sol index 5cedcf3..f3f3d3c 100644 --- a/test/utils/Events.sol +++ b/test/utils/Events.sol @@ -6,16 +6,6 @@ import { IPRBProxyPlugin } from "../../src/interfaces/IPRBProxyPlugin.sol"; /// @notice Abstract contract containing all the events emitted by the protocol. abstract contract Events { - /*////////////////////////////////////////////////////////////////////////// - ANNEX - //////////////////////////////////////////////////////////////////////////*/ - - event InstallPlugin(IPRBProxyPlugin indexed plugin); - - event SetPermission(address indexed envoy, address indexed target, bool permission); - - event UninstallPlugin(IPRBProxyPlugin indexed plugin); - /*////////////////////////////////////////////////////////////////////////// PROXY //////////////////////////////////////////////////////////////////////////*/ @@ -36,4 +26,10 @@ abstract contract Events { bytes32 salt, IPRBProxy proxy ); + + event InstallPlugin(address owner, IPRBProxy proxy, IPRBProxyPlugin indexed plugin); + + event SetPermission(address owner, IPRBProxy proxy, address indexed envoy, address indexed target, bool permission); + + event UninstallPlugin(address owner, IPRBProxy proxy, IPRBProxyPlugin indexed plugin); } diff --git a/test/utils/Precompiles.sol b/test/utils/Precompiles.sol index 2566552..d149f40 100644 --- a/test/utils/Precompiles.sol +++ b/test/utils/Precompiles.sol @@ -2,7 +2,6 @@ // solhint-disable max-line-length pragma solidity >=0.8.18; -import { IPRBProxyAnnex } from "../../src/interfaces/IPRBProxyAnnex.sol"; import { IPRBProxyRegistry } from "../../src/interfaces/IPRBProxyRegistry.sol"; /// @notice This is useful for external integrations seeking to test against the exact deployed bytecode, as recompiling @@ -12,25 +11,13 @@ contract Precompiles { BYTECODES //////////////////////////////////////////////////////////////////////////*/ - bytes public constant BYTECODE_ANNEX = - hex"6080806040523461001657610526908161001c8239f35b600080fdfe6080604081815260048036101561001557600080fd5b600092833560e01c9081631f9838b5146103ba5750806338a40908146103795780634bddd93a146102a3578063aa4b826a14610213578063b96784031461010a5763ffa1ad741461006557600080fd5b346101065782600319360112610106578151908282019082821067ffffffffffffffff8311176100f357508252600c81526020906b342e302e302d626574612e3560a01b8282015282519382859384528251928382860152825b8481106100dd57505050828201840152601f01601f19168101030190f35b81810183015188820188015287955082016100bf565b634e487b7160e01b855260419052602484fd5b8280fd5b5034610106576020918260031936011261020f5781356001600160a01b038116939084900361020b57815163ecdb286560e01b81528581858183895af19081156102015786916101df575b5080519384156101c85750855b8481106101925786867f88b2a910c369b31905e184140cbb1e5ec72817e11c4d6c5993f736716f8546598280a280f35b6001906001600160e01b03196101a882856104fc565b5116885287845284882080546001600160a01b0319168817905501610162565b835163b702f33560e01b8152908101869052602490fd5b6101fb91503d8088833e6101f38183610437565b81019061046f565b38610155565b83513d88823e3d90fd5b8480fd5b8380fd5b50503461029f57606036600319011261029f5761022e610406565b610236610421565b916044359081151580920361020b577f730824739164a9503317b3e878e816553b62d6763c1ba6df1390dea687e4d36c9160209160018060a01b038095169485885260018452818820961695868852835280872060ff1981541660ff841617905551908152a380f35b5080fd5b5034610106576020918260031936011261020f5781356001600160a01b038116939084900361020b57815163ecdb286560e01b81528581858183895af190811561020157869161035f575b5080519384156101c85750855b84811061032b5786867f7207021e3ba5dd0d191feb5efcbb1a8dcf615fa597fc5e1a49ecc13c2b7dd92b8280a280f35b6001906001600160e01b031961034182856104fc565b5116885287845284882080546001600160a01b0319169055016102fb565b61037391503d8088833e6101f38183610437565b386102ee565b5034610106576020366003190112610106573563ffffffff60e01b81168091036101065782526020828152918190205490516001600160a01b039091168152f35b8490843461010657806003193601126101065760ff906020936103db610406565b6103e3610421565b6001600160a01b0391821683526001875283832091168252855220541615158152f35b600435906001600160a01b038216820361041c57565b600080fd5b602435906001600160a01b038216820361041c57565b90601f8019910116810190811067ffffffffffffffff82111761045957604052565b634e487b7160e01b600052604160045260246000fd5b90602090818382031261041c57825167ffffffffffffffff9384821161041c570181601f8201121561041c578051938411610459578360051b90604051946104b985840187610437565b8552838086019282010192831161041c578301905b8282106104dc575050505090565b81516001600160e01b03198116810361041c5781529083019083016104ce565b80518210156105105760209160051b010190565b634e487b7160e01b600052603260045260246000fd"; - bytes public constant BYTECODE_REGISTRY = - hex"60808060405234610016576112fe908161001c8239f35b600080fdfe60808060405260043610156200001457600080fd5b60003560e01c908163092af813146200035e575080632c27a01f146200032057806366b0182d146200022057806374912cd214620001ab578063775c300c1462000137578063b7fba4d314620000f85763ffa1ad74146200007457600080fd5b34620000f3576000366003190112620000f357604051604081019080821067ffffffffffffffff831117620000dd57620000d991604052600c81526b342e302e302d626574612e3560a01b602082015260405191829160208352602083019062000797565b0390f35b634e487b7160e01b600052604160045260246000fd5b600080fd5b34620000f3576020366003190112620000f35760206001600160a01b03806200012062000720565b166000526004825260406000205416604051908152f35b34620000f3576000366003190112620000f357336000908152600460205260409020546001600160a01b0390811680620001825760208262000179336200086b565b60405191168152f35b6040516356352f0b60e11b81523360048201526001600160a01b03919091166024820152604490fd5b34620000f3576020366003190112620000f357620001c862000720565b60018060a01b03908181166000526004602052816040600020541680620001f75760208362000179846200086b565b6040516356352f0b60e11b81526001600160a01b03928316600482015291166024820152604490fd5b34620000f3576000366003190112620000f35760018060a01b0380600054166001918254169160405190600090600254906200025c8262000737565b80855291818116908115620002f75750600114620002a9575b5050906200028a81620000d993038262000774565b6040519384938452602084015260606040840152606083019062000797565b600260009081529250600080516020620012de8339815191525b828410620002de5750505081016020016200028a8262000275565b80546020858701810191909152909301928101620002c3565b60ff191660208087019190915292151560051b850190920192506200028a915083905062000275565b34620000f3576020366003190112620000f3576001600160a01b036200034562000720565b1660005260036020526020604060002054604051908152f35b34620000f3576040366003190112620000f3576200037b62000720565b6024359167ffffffffffffffff8311620000f35736602384011215620000f35782600401359067ffffffffffffffff8211620000f3573660248386010111620000f357336000908152600460205260409020546001600160a01b031680620006fd57505032600081815260036020908152604091829020548251918201938452918101829052909291906200041e81606081015b03601f19810183528262000774565b5190209260405191606083019083821067ffffffffffffffff831117620000dd5760009260209260405233855260018060a01b031682850152806024604051986200047385601f19601f860116018b62000774565b828a52018389013786010152604081018490528051600080546001600160a01b03199081166001600160a01b0393841617909155602090920151600180549093169116178155835190919067ffffffffffffffff8111620000dd57620004db60025462000737565b601f811162000685575b50602094601f82116001146200060f5794819293949560009262000603575b5050600019600383901b1c191690831b176002555b60405161093d908181019181831067ffffffffffffffff841117620000dd5785928291620009a1833903906000f5928315620005f7576020936001600160a01b03169262000566620007d9565b33600052600485526040600020846bffffffffffffffffffffffff60a01b82541617905532600052600385528201604060002055604051917f6aafca263a35a9d2a6e4e4659a84688092f4ae153df2f95cd7659508d95c1870339380620005ec8733963296849192604091949360608401958452602084015260018060a01b0316910152565b0390a4604051908152f35b6040513d6000823e3d90fd5b01519050858062000504565b601f198216956002600052600080516020620012de8339815191529160005b8881106200066e5750838697989695961062000654575b505050811b0160025562000519565b015160001960f88460031b161c1916905585808062000645565b81830151845592860192602092830192016200062e565b6002600052601f820160051c600080516020620012de833981519152019060208310620006e5575b601f85910160051c600080516020620012de83398151915201915b828110620006d8575050620004e5565b60008155018490620006c8565b600080516020620012de8339815191529150620006ad565b6356352f0b60e11b82523360048301526001600160a01b03166024820152604490fd5b600435906001600160a01b0382168203620000f357565b90600182811c9216801562000769575b60208310146200075357565b634e487b7160e01b600052602260045260246000fd5b91607f169162000747565b90601f8019910116810190811067ffffffffffffffff821117620000dd57604052565b919082519283825260005b848110620007c4575050826000602080949584010152601f8019910116010190565b602081830181015184830182015201620007a2565b60008080556001818155620007f060025462000737565b9081620007fc57505050565b601f82116001146200080f575050600255565b60028352601f600080516020620012de833981519152920160051c82017f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5acf5b81811062000860575050508160025555565b84815582016200084e565b3260008181526003602090815260408083205481519283019485528282018190529495949293909291620008a381606081016200040f565b5190209160018060a01b03809616936bffffffffffffffffffffffff60a01b968588835416178255825161093d8082019082821067ffffffffffffffff8311176200098c579180918893620009a18339039084f58015620009825716967f6aafca263a35a9d2a6e4e4659a84688092f4ae153df2f95cd7659508d95c18709291906200092e620007d9565b868252600460205288838320918254161790553281526003602052816001850191205551806200097d8833963296849192604091949360608401958452602084015260018060a01b0316910152565b0390a4565b83513d84823e3d90fd5b634e487b7160e01b85526041600452602485fdfe60c08060405234620000b5573360a0526366b0182d60e01b8152600081600481335afa8015620000af5760008092819262000086575b506080526001600160a01b03821662000072575b6040516105ef90816200034e823960805181818161025b0152610467015260a051816102160152f35b6200007d916200029d565b50388062000049565b909150620000a692503d8092823e6200009f82620000d0565b0162000179565b90913862000035565b6200020c565b600080fd5b634e487b7160e01b600052604160045260246000fd5b60c0601f91909101601f19168101906001600160401b03821190821017620000f757604052565b620000ba565b601f909101601f19168101906001600160401b03821190821017620000f757604052565b60e051906001600160a01b0382168203620000b557565b6001600160401b038111620000f757601f01601f191660200190565b60005b838110620001685750506000910152565b818101518382015260200162000157565b606060bf19820112620000b55760c0516001600160a01b0381168103620000b55791620001a562000121565b610100519092906001600160401b038111620000b5578160df82011215620000b5578060c00151620001d78162000138565b92620001e76040519485620000fd565b81845260e08284010111620000b557620002099160e060208501910162000154565b90565b6040513d6000823e3d90fd5b3d1562000248573d906200022c8262000138565b916200023c6040519384620000fd565b82523d6000602084013e565b606090565b90602091620002688151809281855285808601910162000154565b601f01601f1916010190565b90916200028e62000209936040845260408401906200024d565b9160208184039101526200024d565b9190823b156200032c57600080825160208401865af4907fb24ebe141c5f2a744b103bea65fce6c40e0dc65d7341d092c09b160f40447990620002df62000218565b60405190956001600160a01b0316928190620002fe9088908362000274565b0390a2156200030957565b508051156200031a57602081519101fd5b60405163061a160d60e41b8152600490fd5b604051636d17e5ef60e11b81526001600160a01b0384166004820152602490fdfe60806040526004361015610027575b36156100255761001d3661035c565b602081519101f35b005b6000803560e01c9081631cff79cd1461007a575080631f9838b51461007557806338a40908146100705780637b1039991461006b57638da5cb5b0361000e57610245565b610200565b6101bb565b610158565b60403660031901126100e65761008e6100e9565b60243567ffffffffffffffff928382116100e657366023830112156100e65781600401359384116100e65736602485840101116100e6576100e26100d685602485018661045a565b60405191829182610144565b0390f35b80fd5b600435906001600160a01b03821682036100ff57565b600080fd5b919082519283825260005b848110610130575050826000602080949584010152601f8019910116010190565b60208183018101518483018201520161010f565b906020610155928181520190610104565b90565b346100ff5760403660031901126100ff576101716100e9565b602435906001600160a01b039081831683036100ff571660009081526001602090815260408083206001600160a01b03909416835292815291902060ff9054166040519015158152f35b346100ff5760203660031901126100ff5760043563ffffffff60e01b81168091036100ff576000526000602052602060018060a01b0360406000205416604051908152f35b346100ff5760003660031901126100ff576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b346100ff5760003660031901126100ff576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b908160008237016000815290565b634e487b7160e01b600052604160045260246000fd5b6040519190601f01601f1916820167ffffffffffffffff8111838210176102d457604052565b610298565b67ffffffffffffffff81116102d457601f01601f191660200190565b3d1561031b573d9061030e610309836102d9565b6102ae565b9182523d6000602084013e565b606090565b606090610155939260408252806040830152806000848401376000838284010152601f8019910116810190602083828403019101520190610104565b600080356001600160e01b0319168082526020829052604090912091929161038b90546001600160a01b031690565b906001600160a01b0382169081156104305750600080604051806103af818961028a565b0390845af4907fc4dabe0d7ef7462e2218f2c398c21ef217803e1c46f5cf802d1a5d1d9b503f2f6103de6102f5565b80966103ef60405192839283610320565b0390a2156103fa5750565b82519091501561040d5750805190602001fd5b60405163023c045d60e21b81526001600160a01b03919091166004820152602490fd5b60405163300eff3960e21b81523360048201526001600160e01b0319919091166024820152604490fd5b9091906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081169033821415806104ed575b6104c95750506104a5610309836102d9565b9180835236818501116100ff57602081600092610155968387013784010152610546565b606492604051926355d1750960e01b84526004840152336024840152166044820152fd5b5033600052600160205260ff6105198460406000209060018060a01b0316600052602052604060002090565b541615610493565b909161053861015593604084526040840190610104565b916020818403910152610104565b9190823b156105ce57600080825160208401865af4907fb24ebe141c5f2a744b103bea65fce6c40e0dc65d7341d092c09b160f404479906105856102f5565b60405190956001600160a01b03169281906105a290889083610521565b0390a2156105ac57565b508051156105bc57602081519101fd5b60405163061a160d60e41b8152600490fd5b604051636d17e5ef60e11b81526001600160a01b0384166004820152602490fd405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace"; + hex"6080806040523461001657611ad1908161001c8239f35b600080fdfe60808060405260043610156200001457600080fd5b60003560e01c908163092af813146200095c575080631ddef5b914620008ad5780632c27a01f146200086f5780634bddd93a146200074f5780635cabcdf7146200067f57806361be4859146200062057806366b0182d146200052057806374912cd214620004ab578063775c300c1462000437578063aa4b826a1462000368578063b31d1b9914620002f3578063b7fba4d314620002b4578063b9678403146200014c5763ffa1ad7414620000c857600080fd5b34620001475760003660031901126200014757604051604081019080821067ffffffffffffffff83111762000131576200012d91604052600c81526b342e302e302d626574612e3560a01b602082015260405191829160208352602083019062000dd9565b0390f35b634e487b7160e01b600052604160045260246000fd5b600080fd5b34620001475760208060031936011262000147576001600160a01b036004358181169290839003620001475733600052600681528160406000205416156200029c5760405163ecdb286560e01b815260008160048183885af1908115620002905760009162000268575b5080519081156200024f5760005b8281106200020657505050600690336000525260406000205416337f9638c85f58d5866b4eb299b2e5f8f366372bd58e2d6e6e6eb19f3462948f3387600080a4005b6001903360005260058552604060002063ffffffff60e01b6200022a838662000f65565b511660005285526040600020876001600160601b0360a01b82541617905501620001c4565b60405163b74066bb60e01b815260048101869052602490fd5b6200028991503d806000833e62000280818362000db6565b81019062000ece565b84620001b6565b6040513d6000823e3d90fd5b60405163963e961b60e01b8152336004820152602490fd5b3462000147576020366003190112620001475760206001600160a01b0380620002dc62000d1c565b166000526006825260406000205416604051908152f35b346200014757606036600319011262000147576200031062000d1c565b6200031a62000d33565b906200032562000d4a565b9160018060a01b038092166000526004602052816040600020911660005260205260406000209116600052602052602060ff604060002054166040519015158152f35b346200014757606036600319011262000147576200038562000d1c565b6200038f62000d33565b604435908115158092036200014757336000526020916006835260018060a01b038060406000205416156200029c573360005260048452806040600020951694856000528452806040600020931692836000528452604060002060ff1981541660ff8416179055336000526006845260406000205416926040519283528201527f4f2bd80fb4928b06abcd76e3b26209a615f0612f98dc4a8b176934b1a389933360403392a4005b34620001475760003660031901126200014757336000908152600660205260409020546001600160a01b03908116806200048257602082620004793362000f90565b60405191168152f35b6040516356352f0b60e11b81523360048201526001600160a01b03919091166024820152604490fd5b34620001475760203660031901126200014757620004c862000d1c565b60018060a01b03908181166000526006602052816040600020541680620004f757602083620004798462000f90565b6040516356352f0b60e11b81526001600160a01b03928316600482015291166024820152604490fd5b3462000147576000366003190112620001475760018060a01b0380600054166001918254169160405190600090600254906200055c8262000d79565b80855291818116908115620005f75750600114620005a9575b5050906200058a816200012d93038262000db6565b6040519384938452602084015260606040840152606083019062000dd9565b60026000908152925060008051602062001ab18339815191525b828410620005de5750505081016020016200058a8262000575565b80546020858701810191909152909301928101620005c3565b60ff191660208087019190915292151560051b850190920192506200058a915083905062000575565b3462000147576040366003190112620001475760206200063f62000d1c565b6200064962000d61565b6001600160a01b0391821660009081526005845260408082206001600160e01b0319909316825291845281902054905191168152f35b346200014757606036600319011262000147576200069c62000d1c565b620006a662000d33565b90620006b162000d4a565b9060405192638da5cb5b60e01b8452602093848160048160018060a01b038097165afa8015620002905783916000916200071b575b5016600052600484528160406000209116600052835260406000209116600052815260ff604060002054166040519015158152f35b620007409150863d881162000747575b62000737818362000db6565b81019062000e1b565b86620006e6565b503d6200072b565b34620001475760208060031936011262000147576001600160a01b036004358181169290839003620001475733600052600681528160406000205416156200029c5760405163ecdb286560e01b815260008160048183885af1908115620002905760009162000850575b5080519081156200024f5760005b8281106200080957505050600690336000525260406000205416337f381d2d67cec77a56c13de6658effae4585e49ae536458929d53bfd4c3599e0ad600080a4005b6001903360005260058552604060002063ffffffff60e01b6200082d838662000f65565b5116600052855260406000206001600160601b0360a01b815416905501620007c7565b6200086891503d806000833e62000280818362000db6565b84620007b9565b346200014757602036600319011262000147576001600160a01b036200089462000d1c565b1660005260036020526020604060002054604051908152f35b34620001475760403660031901126200014757620008ca62000d1c565b620008d462000d61565b604051638da5cb5b60e01b81526020926001600160a01b0392919084908290600490829087165afa8015620002905783916000916200093a575b50166000526005835260406000209063ffffffff60e01b16600052825260406000205416604051908152f35b620009559150853d8711620007475762000737818362000db6565b856200090e565b346200014757604036600319011262000147576200097962000d1c565b9067ffffffffffffffff602435116200014757366023602435011215620001475767ffffffffffffffff6024356004013511620001475736602480356004013581350101116200014757336000908152600660205260409020546001600160a01b03168062000cf9573260008181526003602090815260409182902054825191820193845291810182905285929062000a2081606081015b03601f19810183528262000db6565b519020604051926060840184811067ffffffffffffffff821117620001315760405233845260018060a01b031660208401526040519262000a726020601f19601f602435600401350116018562000db6565b6024803560048101358087529101602086013760006020602435600401358601015283604082015260018060a01b038151166001600160601b0360a01b6000541617600055602060018060a01b03910151166001600160601b0360a01b6001541617600155825167ffffffffffffffff8111620001315762000af660025462000d79565b601f811162000c84575b506020601f821160011462000c0c578192939460009262000c00575b50508160011b916000199060031b1c1916176002555b6040516109f0908181019181831067ffffffffffffffff841117620001315783928291620010c1833903906000f591821562000290576020926001600160a01b03169162000b7f62000e3c565b33600081815260068652604080822080546001600160a01b03191687179055328083526003885291819020600186019055805194855260208501939093526001600160a01b03851692840192909252918291907f6aafca263a35a9d2a6e4e4659a84688092f4ae153df2f95cd7659508d95c187090606090a4604051908152f35b01519050848062000b1c565b601f19821690600260005260008051602062001ab18339815191529160005b81811062000c6b5750958360019596971062000c51575b505050811b0160025562000b32565b015160001960f88460031b161c1916905584808062000c42565b9192602060018192868b01518155019401920162000c2b565b6002600052601f820160051c60008051602062001ab183398151915201906020831062000ce1575b601f0160051c60008051602062001ab183398151915201905b81811062000cd4575062000b00565b6000815560010162000cc5565b60008051602062001ab1833981519152915062000cac565b6356352f0b60e11b82523360048301526001600160a01b03166024820152604490fd5b600435906001600160a01b03821682036200014757565b602435906001600160a01b03821682036200014757565b604435906001600160a01b03821682036200014757565b602435906001600160e01b0319821682036200014757565b90600182811c9216801562000dab575b602083101462000d9557565b634e487b7160e01b600052602260045260246000fd5b91607f169162000d89565b90601f8019910116810190811067ffffffffffffffff8211176200013157604052565b919082519283825260005b84811062000e06575050826000602080949584010152601f8019910116010190565b60208183018101518483018201520162000de4565b908160209103126200014757516001600160a01b0381168103620001475790565b6000808055600181815562000e5360025462000d79565b908162000e5f57505050565b601f821160011462000e72575050600255565b60028352601f60008051602062001ab1833981519152920160051c82017f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5acf5b81811062000ec3575050508160025555565b848155820162000eb1565b9060209081838203126200014757825167ffffffffffffffff9384821162000147570181601f820112156200014757805193841162000131578360051b906040519462000f1e8584018762000db6565b8552838086019282010192831162000147578301905b82821062000f43575050505090565b81516001600160e01b0319811681036200014757815290830190830162000f34565b805182101562000f7a5760209160051b010190565b634e487b7160e01b600052603260045260246000fd5b326000818152600360209081526040808320548151928301948552828201819052949594929390929162000fc8816060810162000a11565b5190209160018060a01b03809616936001600160601b0360a01b96858883541617825582516109f08082019082821067ffffffffffffffff831117620010ac579180918893620010c18339039084f58015620010a25716967f6aafca263a35a9d2a6e4e4659a84688092f4ae153df2f95cd7659508d95c18709291906200104e62000e3c565b868252600660205288838320918254161790553281526003602052816001850191205551806200109d8833963296849192604091949360608401958452602084015260018060a01b0316910152565b0390a4565b83513d84823e3d90fd5b634e487b7160e01b85526041600452602485fdfe60c08060405234620000ca573360a0526366b0182d60e01b8152600081600481335afa8015620000c4576000809281926200009b575b506080526001600160a01b03821662000087575b60405161068d9081620003638239608051818181610192015281816102c401526104b9015260a05181818161014d0152818161031301526104fb0152f35b6200009291620002b2565b50388062000049565b909150620000bb92503d8092823e620000b482620000e5565b016200018e565b90913862000035565b62000221565b600080fd5b634e487b7160e01b600052604160045260246000fd5b60c0601f91909101601f19168101906001600160401b038211908210176200010c57604052565b620000cf565b601f909101601f19168101906001600160401b038211908210176200010c57604052565b60e051906001600160a01b0382168203620000ca57565b6001600160401b0381116200010c57601f01601f191660200190565b60005b8381106200017d5750506000910152565b81810151838201526020016200016c565b606060bf19820112620000ca5760c0516001600160a01b0381168103620000ca5791620001ba62000136565b610100519092906001600160401b038111620000ca578160df82011215620000ca578060c00151620001ec816200014d565b92620001fc604051948562000112565b81845260e08284010111620000ca576200021e9160e060208501910162000169565b90565b6040513d6000823e3d90fd5b3d156200025d573d9062000241826200014d565b9162000251604051938462000112565b82523d6000602084013e565b606090565b906020916200027d8151809281855285808601910162000169565b601f01601f1916010190565b9091620002a36200021e9360408452604084019062000262565b91602081840391015262000262565b9190823b156200034157600080825160208401865af4907fb24ebe141c5f2a744b103bea65fce6c40e0dc65d7341d092c09b160f40447990620002f46200022d565b60405190956001600160a01b0316928190620003139088908362000289565b0390a2156200031e57565b508051156200032f57602081519101fd5b60405163061a160d60e41b8152600490fd5b604051636d17e5ef60e11b81526001600160a01b0384166004820152602490fdfe60806040526004361015610027575b36156100255761001d366102b5565b602081519101f35b005b6000803560e01c9081631cff79cd1461005a575080637b1039991461005557638da5cb5b0361000e5761017c565b610137565b60403660031901126100ca57600435610072816100cd565b60243567ffffffffffffffff928382116100ca57366023830112156100ca5781600401359384116100ca5736602485840101116100ca576100c66100ba8560248501866104a2565b60405191829182610123565b0390f35b80fd5b6001600160a01b038116036100de57565b600080fd5b919082519283825260005b84811061010f575050826000602080949584010152601f8019910116010190565b6020818301810151848301820152016100ee565b9060206101349281815201906100e3565b90565b346100de5760003660031901126100de576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b346100de5760003660031901126100de576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b634e487b7160e01b600052604160045260246000fd5b90601f8019910116810190811067ffffffffffffffff8211176101f957604052565b6101c1565b908160209103126100de5751610134816100cd565b6040513d6000823e3d90fd5b908160008237016000815290565b67ffffffffffffffff81116101f957601f01601f191660200190565b3d15610274573d9061025a8261022d565b9161026860405193846101d7565b82523d6000602084013e565b606090565b606090610134939260408252806040830152806000848401376000838284010152601f80199101168101906020838284030191015201906100e3565b6040516361be485960e01b81527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0381811660048401526001600160e01b03196000351660248401819052939493906020846044817f000000000000000000000000000000000000000000000000000000000000000085165afa93841561044e5760009461041e575b5083169182156103e357505060008060405180610362818961021f565b0390845af4907fc4dabe0d7ef7462e2218f2c398c21ef217803e1c46f5cf802d1a5d1d9b503f2f610391610249565b80966103a260405192839283610279565b0390a2156103ad5750565b8251909150156103c05750805190602001fd5b60405163023c045d60e21b81526001600160a01b03919091166004820152602490fd5b604051638848730f60e01b81523360048201526001600160a01b039190911660248201526001600160e01b0319919091166044820152606490fd5b61044091945060203d8111610447575b61043881836101d7565b8101906101fe565b9238610345565b503d61042e565b610213565b908160209103126100de575180151581036100de5790565b9291926104778261022d565b9161048560405193846101d7565b8294818452818301116100de578281602093846000960137010152565b60405163b31d1b9960e01b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008181166004840152336024840152838216604484015292949291906020826064817f000000000000000000000000000000000000000000000000000000000000000085165afa91821561044e5760009261058f575b5082163314159081610586575b506105565750610134929161055091369161046b565b906105e4565b6040516355d1750960e01b81526001600160a01b0391821660048201523360248201529084166044820152606490fd5b9050153861053a565b6105b191925060203d81116105b8575b6105a981836101d7565b810190610453565b903861052d565b503d61059f565b90916105d6610134936040845260408401906100e3565b9160208184039101526100e3565b9190823b1561066c57600080825160208401865af4907fb24ebe141c5f2a744b103bea65fce6c40e0dc65d7341d092c09b160f40447990610623610249565b60405190956001600160a01b0316928190610640908890836105bf565b0390a21561064a57565b5080511561065a57602081519101fd5b60405163061a160d60e41b8152600490fd5b604051636d17e5ef60e11b81526001600160a01b0384166004820152602490fd405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace"; /*////////////////////////////////////////////////////////////////////////// DEPLOYERS //////////////////////////////////////////////////////////////////////////*/ - /// @notice Deploys {PRBProxyAnnex} from precompiled bytecode. - function deployAnnex() public returns (IPRBProxyAnnex annex) { - bytes memory bytecode = BYTECODE_ANNEX; - assembly { - annex := create(0, add(bytecode, 0x20), mload(bytecode)) - } - require(address(annex) != address(0), "PRBProxy Precompiles: deployment failed for Annex contract"); - } - /// @notice Deploys {PRBProxyRegistry} from precompiled bytecode. function deployRegistry() public returns (IPRBProxyRegistry registry) { bytes memory bytecode = BYTECODE_REGISTRY; @@ -39,10 +26,4 @@ contract Precompiles { } require(address(registry) != address(0), "PRBProxy Precompiles: deployment failed for Registry contract"); } - - /// @notice Deploys {PRBProxyAnnex} and {PRBProxyRegistry} from precompiled bytecode. - function deploySystem() public returns (IPRBProxyAnnex annex, IPRBProxyRegistry registry) { - annex = deployAnnex(); - registry = deployRegistry(); - } } diff --git a/test/utils/Precompiles.t.sol b/test/utils/Precompiles.t.sol index 2a0b09f..6109bb6 100644 --- a/test/utils/Precompiles.t.sol +++ b/test/utils/Precompiles.t.sol @@ -1,7 +1,6 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity >=0.8.18; -import { IPRBProxyAnnex } from "../../src/interfaces/IPRBProxyAnnex.sol"; import { IPRBProxyRegistry } from "../../src/interfaces/IPRBProxyRegistry.sol"; import { Base_Test } from "../Base.t.sol"; @@ -16,23 +15,9 @@ contract Precompiles_Test is Base_Test { } } - function test_DeployPRBProxyAnnex() external onlyTestOptimizedProfile { - address actualAnnex = address(precompiles.deployAnnex()); - address expectedAnnex = address(deployPrecompiledAnnex()); - assertEq(actualAnnex.code, expectedAnnex.code, "proxy annex bytecodes don't match"); - } - function test_DeployPRBProxyRegistry() external onlyTestOptimizedProfile { address actualRegistry = address(precompiles.deployRegistry()); address expectedRegistry = address(deployPrecompiledRegistry()); assertEq(actualRegistry.code, expectedRegistry.code, "registry bytecodes don't match"); } - - function test_DeployPRBProxySystem() external onlyTestOptimizedProfile { - (IPRBProxyAnnex actualAnnex, IPRBProxyRegistry actualRegistry) = precompiles.deploySystem(); - address expectedAnnex = address(deployPrecompiledAnnex()); - address expectedRegistry = address(deployPrecompiledRegistry()); - assertEq(address(actualAnnex).code, expectedAnnex.code, "proxy annex bytecodes don't match"); - assertEq(address(actualRegistry).code, expectedRegistry.code, "registry bytecodes don't match"); - } }