Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Checkout plugin and proxy #598

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,6 @@
[submodule "lib/dynamic-contracts"]
path = lib/dynamic-contracts
url = https://github.com/thirdweb-dev/dynamic-contracts
[submodule "lib/prb-proxy"]
path = lib/prb-proxy
url = https://github.com/PaulRBerg/prb-proxy
136 changes: 136 additions & 0 deletions contracts/prebuilts/unaudited/checkout/PRBProxy.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.18;

import { IPRBProxy } from "./interfaces/IPRBProxy.sol";
import { IPRBProxyPlugin } from "./interfaces/IPRBProxyPlugin.sol";
import { IPRBProxyRegistry } from "./interfaces/IPRBProxyRegistry.sol";

/*

██████╗ ██████╗ ██████╗ ██████╗ ██████╗ ██████╗ ██╗ ██╗██╗ ██╗
██╔══██╗██╔══██╗██╔══██╗██╔══██╗██╔══██╗██╔═══██╗╚██╗██╔╝╚██╗ ██╔╝
██████╔╝██████╔╝██████╔╝██████╔╝██████╔╝██║ ██║ ╚███╔╝ ╚████╔╝
██╔═══╝ ██╔══██╗██╔══██╗██╔═══╝ ██╔══██╗██║ ██║ ██╔██╗ ╚██╔╝
██║ ██║ ██║██████╔╝██║ ██║ ██║╚██████╔╝██╔╝ ██╗ ██║
╚═╝ ╚═╝ ╚═╝╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝

*/

/// @title PRBProxy
/// @dev See the documentation in {IPRBProxy}.
contract PRBProxy is IPRBProxy {
/*//////////////////////////////////////////////////////////////////////////
CONSTANTS
//////////////////////////////////////////////////////////////////////////*/

/// @inheritdoc IPRBProxy
address public immutable override owner;

/// @inheritdoc IPRBProxy
IPRBProxyRegistry public immutable override registry;

/*//////////////////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////////////////*/

/// @notice Creates the proxy by fetching the constructor params from the registry, optionally delegate calling
/// to a target contract if one is provided.
/// @dev The rationale of this approach is to have the proxy's CREATE2 address not depend on any constructor params.
constructor() {
registry = IPRBProxyRegistry(msg.sender);
(address owner_, address target, bytes memory data) = registry.constructorParams();
owner = owner_;
if (target != address(0)) {
_execute(target, data);
}
}

/*//////////////////////////////////////////////////////////////////////////
FALLBACK FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/

/// @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 selector 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, owner: owner, method: msg.sig });
}

// Delegate call to the plugin.
bool success;
(success, response) = address(plugin).delegatecall(data);

// Log the plugin run.
emit RunPlugin(plugin, data, response);

// Check if the call was successful or not.
if (!success) {
// If there is return data, the delegate call reverted with a reason or a custom error, which we bubble up.
if (response.length > 0) {
assembly {
let returndata_size := mload(response)
revert(add(32, response), returndata_size)
}
} else {
revert PRBProxy_PluginReverted(plugin);
}
}
}

/// @dev Called when `msg.value` is not zero and the call data is empty.
receive() external payable { }

/*//////////////////////////////////////////////////////////////////////////
USER-FACING NON-CONSTANT FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/

/// @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) {
bool permission = registry.getPermissionByOwner({ owner: owner, envoy: msg.sender, target: target });
if (!permission) {
revert PRBProxy_ExecutionUnauthorized({ owner: owner, caller: msg.sender, target: target });
}
}

// Delegate call to the target contract, and handle the response.
response = _execute(target, data);
}

/*//////////////////////////////////////////////////////////////////////////
INTERNAL NON-CONSTANT FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/

/// @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.
if (target.code.length == 0) {
revert PRBProxy_TargetNotContract(target);
}

// Delegate call to the target contract.
bool success;
(success, response) = target.delegatecall(data);

// Log the execution.
emit Execute(target, data, response);

// Check if the call was successful or not.
if (!success) {
// If there is return data, the delegate call reverted with a reason or a custom error, which we bubble up.
if (response.length > 0) {
assembly {
// The length of the data is at `response`, while the actual data is at `response + 32`.
let returndata_size := mload(response)
revert(add(response, 32), returndata_size)
}
} else {
revert PRBProxy_ExecutionReverted();
}
}
}
}
251 changes: 251 additions & 0 deletions contracts/prebuilts/unaudited/checkout/PRBProxyRegistryModified.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,251 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.18;

Check warning

Code scanning / Slither

Different pragma directives are used Warning

Different versions of Solidity are used:
- Version used: ['>=0.8.18', '>=0.8.4', '^0.8.0', '^0.8.1', '^0.8.10', '^0.8.11', '^0.8.12', '^0.8.2', '^0.8.4', '^0.8.8', '^0.8.9']
- >=0.8.18
- >=0.8.18
- >=0.8.4
- >=0.8.4
- >=0.8.4
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- [^0.8.0](contracts/extension/SignatureAction.sol#L2

import { IPRBProxy } from "./interfaces/IPRBProxy.sol";
import { IPRBProxyPlugin } from "./interfaces/IPRBProxyPlugin.sol";
import { IPRBProxyRegistry } from "./interfaces/IPRBProxyRegistry.sol";
import { PRBProxy } from "./PRBProxy.sol";

/// @author Modified from prb-proxy (https://github.com/PaulRBerg/prb-proxy/blob/main/src/PRBProxyRegistry.sol)
/// @title PRBProxyRegistry
/// @dev See the documentation in {IPRBProxyRegistry}.
contract PRBProxyRegistryModified is IPRBProxyRegistry {
/*//////////////////////////////////////////////////////////////////////////
CONSTANTS
//////////////////////////////////////////////////////////////////////////*/

/// @inheritdoc IPRBProxyRegistry
string public constant override VERSION = "4.0.2";

/// @dev Magic value to override target permissions. Holders can execute on any target.
address public constant MAGIC_TARGET = address(0x42);

/*//////////////////////////////////////////////////////////////////////////
USER-FACING STORAGE
//////////////////////////////////////////////////////////////////////////*/

/// @inheritdoc IPRBProxyRegistry
ConstructorParams public override constructorParams;

/*//////////////////////////////////////////////////////////////////////////
INTERNAL STORAGE
//////////////////////////////////////////////////////////////////////////*/

mapping(address owner => mapping(IPRBProxyPlugin plugin => bytes4[] methods)) internal _methods;

mapping(address owner => mapping(address envoy => mapping(address target => bool permission)))
internal _permissions;

mapping(address owner => mapping(bytes4 method => IPRBProxyPlugin plugin)) internal _plugins;

mapping(address owner => IPRBProxy proxy) internal _proxies;

/*//////////////////////////////////////////////////////////////////////////
MODIFIERS
//////////////////////////////////////////////////////////////////////////*/

/// @notice Checks that the caller has a proxy.
modifier onlyCallerWithProxy() {
if (address(_proxies[msg.sender]) == address(0)) {
revert PRBProxyRegistry_UserDoesNotHaveProxy(msg.sender);
}
_;
}

/// @notice Check that the user does not have a proxy.
modifier onlyNonProxyOwner(address user) {
IPRBProxy proxy = _proxies[user];
if (address(proxy) != address(0)) {
revert PRBProxyRegistry_UserHasProxy(user, proxy);
}
_;
}

/*//////////////////////////////////////////////////////////////////////////
USER-FACING CONSTANT FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/

/// @inheritdoc IPRBProxyRegistry
function getMethodsByOwner(address owner, IPRBProxyPlugin plugin) external view returns (bytes4[] memory methods) {
methods = _methods[owner][plugin];
}

/// @inheritdoc IPRBProxyRegistry
function getMethodsByProxy(
IPRBProxy proxy,
IPRBProxyPlugin plugin
) external view returns (bytes4[] memory methods) {
methods = _methods[proxy.owner()][plugin];
}

/// @inheritdoc IPRBProxyRegistry
function getPermissionByOwner(
address owner,
address envoy,
address target
) external view returns (bool permission) {
permission = _permissions[owner][envoy][MAGIC_TARGET] || _permissions[owner][envoy][target];
}

/// @inheritdoc IPRBProxyRegistry
function getPermissionByProxy(
IPRBProxy proxy,
address envoy,
address target
) external view returns (bool permission) {
permission = _permissions[proxy.owner()][envoy][MAGIC_TARGET] || _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 user) external view returns (IPRBProxy proxy) {
proxy = _proxies[user];
}

/*//////////////////////////////////////////////////////////////////////////
USER-FACING NON-CONSTANT FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/

/// @inheritdoc IPRBProxyRegistry
function deploy() external override onlyNonProxyOwner(msg.sender) returns (IPRBProxy proxy) {
proxy = _deploy({ owner: msg.sender, target: address(0), data: "" });
}

/// @inheritdoc IPRBProxyRegistry
function deployAndExecute(
address target,
bytes calldata data
) external override onlyNonProxyOwner(msg.sender) returns (IPRBProxy proxy) {
proxy = _deploy({ owner: msg.sender, target: target, data: data });
}

/// @inheritdoc IPRBProxyRegistry
function deployFor(address user) external override onlyNonProxyOwner(user) returns (IPRBProxy proxy) {
proxy = _deploy({ owner: user, target: address(0), data: "" });
}

/// @inheritdoc IPRBProxyRegistry
function deployAndExecuteAndInstallPlugin(
address target,
bytes calldata data,
IPRBProxyPlugin plugin
) external override onlyNonProxyOwner(msg.sender) returns (IPRBProxy proxy) {
proxy = _deploy({ owner: msg.sender, target: target, data: data });
_installPlugin(plugin);
}
Comment on lines +137 to +144

/// @inheritdoc IPRBProxyRegistry
function deployAndInstallPlugin(
IPRBProxyPlugin plugin
) external onlyNonProxyOwner(msg.sender) returns (IPRBProxy proxy) {
proxy = _deploy({ owner: msg.sender, target: address(0), data: "" });
_installPlugin(plugin);
}
Comment on lines +147 to +152
Comment on lines +147 to +152

/// @inheritdoc IPRBProxyRegistry
function installPlugin(IPRBProxyPlugin plugin) external override onlyCallerWithProxy {
_installPlugin(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 {
// Retrieve the methods originally installed by this plugin.
address owner = msg.sender;
bytes4[] memory methods = _methods[owner][plugin];

// The plugin must be a known, previously installed plugin.
uint256 length = methods.length;
if (length == 0) {
revert PRBProxyRegistry_PluginUnknown(plugin);
}

// Uninstall every method in the list.
for (uint256 i = 0; i < length; ) {
delete _plugins[owner][methods[i]];
unchecked {
i += 1;
}
}

// Remove the methods from the reverse mapping.
delete _methods[owner][plugin];

// Log the plugin uninstallation.
emit UninstallPlugin(owner, _proxies[owner], plugin, methods);
}

/*//////////////////////////////////////////////////////////////////////////
INTERNAL NON-CONSTANT FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/

/// @dev See the documentation for the user-facing functions that call this internal function.
function _deploy(address owner, address target, bytes memory data) internal returns (IPRBProxy proxy) {
// Use the address of the owner as the CREATE2 salt.
bytes32 salt = bytes32(abi.encodePacked(owner));

// Set the owner and empty out the target and the data to prevent reentrancy.
constructorParams = ConstructorParams({ owner: owner, target: target, data: data });

// Deploy the proxy with CREATE2.
proxy = new PRBProxy{ salt: salt }();
delete constructorParams;

// Associate the owner and the proxy.
_proxies[owner] = proxy;

// Log the creation of the proxy.
emit DeployProxy({ operator: msg.sender, owner: owner, proxy: proxy });
}
Comment on lines +198 to +214

Check notice

Code scanning / Slither

Reentrancy vulnerabilities Low

Reentrancy in PRBProxyRegistryModified._deploy(address,address,bytes):
External calls:
- proxy = new PRBProxy()
State variables written after the call(s):
- _proxies[owner] = proxy
- delete constructorParams
Comment on lines +198 to +214

Check notice

Code scanning / Slither

Reentrancy vulnerabilities Low


/// @dev See the documentation for the user-facing functions that call this internal function.
function _installPlugin(IPRBProxyPlugin plugin) internal {
// Retrieve the methods to install.
bytes4[] memory methods = plugin.getMethods();

// The plugin must implement at least one method.
uint256 length = methods.length;
if (length == 0) {
revert PRBProxyRegistry_PluginWithZeroMethods(plugin);
}

// Install every method in the list.
address owner = msg.sender;
for (uint256 i = 0; i < length; ) {
// Check for collisions.
bytes4 method = methods[i];
if (address(_plugins[owner][method]) != address(0)) {
revert PRBProxyRegistry_PluginMethodCollision({
currentPlugin: _plugins[owner][method],
newPlugin: plugin,
method: method
});
}
_plugins[owner][method] = plugin;
unchecked {
i += 1;
}
}

// Set the methods in the reverse mapping.
_methods[owner][plugin] = methods;

// Log the plugin installation.
emit InstallPlugin(owner, _proxies[owner], plugin, methods);
}
Comment on lines +217 to +250

Check notice

Code scanning / Slither

Reentrancy vulnerabilities Low

Comment on lines +217 to +250

Check notice

Code scanning / Slither

Reentrancy vulnerabilities Low

}
Loading