forked from PaulRBerg/prb-proxy
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathPRBProxy.sol
137 lines (109 loc) · 4.44 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
// SPDX-License-Identifier: Unlicense
pragma solidity >=0.8.4;
import "./IPRBProxy.sol";
import { ERC1155Holder } from "@openzeppelin/contracts/token/ERC1155/utils/ERC1155Holder.sol";
import { ERC721Holder } from "@openzeppelin/contracts/token/ERC721/utils/ERC721Holder.sol";
/// @notice Emitted when the caller is not the owner.
error PRBProxy__ExecutionNotAuthorized(address owner, address caller, address target, bytes4 selector);
/// @notice Emitted when execution reverted with no reason.
error PRBProxy__ExecutionReverted();
/// @notice Emitted when the caller is not the owner.
error PRBProxy__NotOwner(address owner, address caller);
/// @notice Emitted when the owner is changed during the DELEGATECALL.
error PRBProxy__OwnerChanged(address originalOwner, address newOwner);
/// @notice Emitted when passing an EOA or an undeployed contract as the target.
error PRBProxy__TargetInvalid(address target);
/// @title PRBProxy
/// @author Paul Razvan Berg
contract PRBProxy is IPRBProxy, ERC1155Holder, ERC721Holder {
/// PUBLIC STORAGE ///
/// @inheritdoc IPRBProxy
address public override owner;
/// @inheritdoc IPRBProxy
uint256 public override minGasReserve;
/// INTERNAL STORAGE ///
/// @notice Maps envoys to target contracts to function selectors to boolean flags.
mapping(address => mapping(address => mapping(bytes4 => bool))) internal permissions;
/// CONSTRUCTOR ///
constructor() {
minGasReserve = 5_000;
owner = msg.sender;
emit TransferOwnership(address(0), msg.sender);
}
/// FALLBACK FUNCTION ///
/// @dev Called when Ether is sent and the call data is empty.
receive() external payable {}
/// PUBLIC CONSTANT FUNCTIONS ///
/// @inheritdoc IPRBProxy
function getPermission(
address envoy,
address target,
bytes4 selector
) external view override returns (bool) {
return permissions[envoy][target][selector];
}
/// PUBLIC 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.
if (owner != msg.sender) {
bytes4 selector;
assembly {
selector := calldataload(data.offset)
}
if (!permissions[msg.sender][target][selector]) {
revert PRBProxy__ExecutionNotAuthorized(owner, msg.sender, target, selector);
}
}
// Check that the target is a valid contract.
if (target.code.length == 0) {
revert PRBProxy__TargetInvalid(target);
}
// Save the owner address in memory. This local variable cannot be modified during the DELEGATECALL.
address owner_ = owner;
// Reserve some gas to ensure that the function has enough to finish the execution.
uint256 stipend = gasleft() - minGasReserve;
// Delegate call to the target contract.
bool success;
(success, response) = target.delegatecall{ gas: stipend }(data);
// Check that the owner has not been changed.
if (owner_ != owner) {
revert PRBProxy__OwnerChanged(owner_, owner);
}
// 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 {
let returndata_size := mload(response)
revert(add(32, response), returndata_size)
}
} else {
revert PRBProxy__ExecutionReverted();
}
}
}
/// @inheritdoc IPRBProxy
function setPermission(
address envoy,
address target,
bytes4 selector,
bool permission
) external override {
if (owner != msg.sender) {
revert PRBProxy__NotOwner(owner, msg.sender);
}
permissions[envoy][target][selector] = permission;
}
/// @inheritdoc IPRBProxy
function transferOwnership(address newOwner) external override {
address oldOwner = owner;
if (oldOwner != msg.sender) {
revert PRBProxy__NotOwner(oldOwner, msg.sender);
}
owner = newOwner;
emit TransferOwnership(oldOwner, newOwner);
}
}