-
-
Notifications
You must be signed in to change notification settings - Fork 48
/
PRBProxy.sol
152 lines (125 loc) · 6.79 KB
/
PRBProxy.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
// 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";
/*
██████╗ ██████╗ ██████╗ ██████╗ ██████╗ ██████╗ ██╗ ██╗██╗ ██╗
██╔══██╗██╔══██╗██╔══██╗██╔══██╗██╔══██╗██╔═══██╗╚██╗██╔╝╚██╗ ██╔╝
██████╔╝██████╔╝██████╔╝██████╔╝██████╔╝██║ ██║ ╚███╔╝ ╚████╔╝
██╔═══╝ ██╔══██╗██╔══██╗██╔═══╝ ██╔══██╗██║ ██║ ██╔██╗ ╚██╔╝
██║ ██║ ██║██████╔╝██║ ██║ ██║╚██████╔╝██╔╝ ██╗ ██║
╚═╝ ╚═╝ ╚═╝╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝
*/
/// @title PRBProxy
/// @dev See the documentation in {IPRBProxy}.
contract PRBProxy is
PRBProxyStorage, // 1 inherited component
IPRBProxy // 1 inherited component
{
/*//////////////////////////////////////////////////////////////////////////
CONSTANTS
//////////////////////////////////////////////////////////////////////////*/
/// @inheritdoc IPRBProxy
IPRBProxyRegistry public immutable override registry;
/*//////////////////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////////////////*/
/// @notice Constructs the proxy by fetching the constructor parameters from the registry.
/// @dev This is implemented like this to make it easy to precompute the CREATE2 address of the proxy.
constructor() {
minGasReserve = 5000;
registry = IPRBProxyRegistry(msg.sender);
owner = registry.transientProxyOwner();
}
/*//////////////////////////////////////////////////////////////////////////
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 signature exists in the installed plugins mapping.
IPRBProxyPlugin plugin = plugins[msg.sig];
if (address(plugin) == address(0)) {
revert PRBProxy_PluginNotInstalledForMethod(msg.sender, msg.sig);
}
// Delegate call to the plugin.
bool success;
(success, response) = _safeDelegateCall(address(plugin), 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 call reverted with a reason or a custom error.
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 && !permissions[msg.sender][target]) {
revert PRBProxy_ExecutionUnauthorized({ owner: owner, caller: msg.sender, target: target });
}
// 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) = _safeDelegateCall(target, 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 call reverted with a reason or a custom error.
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();
}
}
}
/// @inheritdoc IPRBProxy
function transferOwnership(address newOwner) external override {
// Check that the caller is the registry.
if (address(registry) != msg.sender) {
revert PRBProxy_CallerNotRegistry({ registry: registry, caller: msg.sender });
}
// Effects: update the owner.
owner = newOwner;
}
/*//////////////////////////////////////////////////////////////////////////
INTERNAL NON-CONSTANT FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/
/// @notice Performs a DELEGATECALL to the provided address with the provided data.
/// @dev Shared logic between the {execute} and the {fallback} functions.
function _safeDelegateCall(address to, bytes memory data) internal returns (bool success, bytes memory response) {
// Save the owner address in memory so that this variable cannot be modified during the DELEGATECALL.
address owner_ = owner;
// Reserve some gas to ensure that the contract call will not run out of gas.
uint256 stipend = gasleft() - minGasReserve;
// Delegate call to the provided contract.
(success, response) = to.delegatecall{ gas: stipend }(data);
// Check that the owner has not been changed.
if (owner_ != owner) {
revert PRBProxy_OwnerChanged(owner_, owner);
}
}
}