-
Notifications
You must be signed in to change notification settings - Fork 47
/
Copy pathApprovalReceiver.sol
108 lines (98 loc) · 3.76 KB
/
ApprovalReceiver.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
pragma solidity ^0.5.17;
import "./ErrorDecoder.sol";
import "../token/IApproveAndCall.sol";
/**
* @title Base contract for receiving approval from SOV token.
*/
contract ApprovalReceiver is ErrorDecoder, IApproveAndCall {
modifier onlyThisContract() {
// Accepts calls only from receiveApproval function.
require(msg.sender == address(this), "unauthorized");
_;
}
/**
* @notice Receives approval from SOV token.
* @param _data The data will be used for low level call.
*/
function receiveApproval(
address _sender,
uint256 _amount,
address _token,
bytes calldata _data
) external {
// Accepts calls only from SOV token.
require(msg.sender == _getToken(), "unauthorized");
require(msg.sender == _token, "unauthorized");
// Only allowed methods.
bool isAllowed = false;
bytes4[] memory selectors = _getSelectors();
bytes4 sig = _getSig(_data);
for (uint256 i = 0; i < selectors.length; i++) {
if (sig == selectors[i]) {
isAllowed = true;
break;
}
}
require(isAllowed, "method is not allowed");
// Check sender and amount.
address sender;
uint256 amount;
(, sender, amount) = abi.decode(
abi.encodePacked(bytes28(0), _data),
(bytes32, address, uint256)
);
require(sender == _sender, "sender mismatch");
require(amount == _amount, "amount mismatch");
_call(_data);
}
/**
* @notice Returns token address, only this address can be a sender for receiveApproval.
* @dev Should be overridden in child contracts, otherwise error will be thrown.
* @return By default, 0x. When overriden, the token address making the call.
*/
function _getToken() internal view returns (address) {
return address(0);
}
/**
* @notice Returns list of function selectors allowed to be invoked.
* @dev Should be overridden in child contracts, otherwise error will be thrown.
* @return By default, empty array. When overriden, allowed selectors.
*/
function _getSelectors() internal pure returns (bytes4[] memory) {
return new bytes4[](0);
}
/**
* @notice Makes call and reverts w/ enhanced error message.
* @param _data Error message as bytes.
*/
function _call(bytes memory _data) internal {
(bool success, bytes memory returnData) = address(this).call(_data);
if (!success) {
if (returnData.length <= ERROR_MESSAGE_SHIFT) {
revert("receiveApproval: Transaction execution reverted.");
} else {
revert(_addErrorMessage("receiveApproval: ", string(returnData)));
}
}
}
/**
* @notice Extracts the called function selector, a hash of the signature.
* @dev The first four bytes of the call data for a function call specifies
* the function to be called. It is the first (left, high-order in big-endian)
* four bytes of the Keccak-256 (SHA-3) hash of the signature of the function.
* Solidity doesn't yet support a casting of byte[4] to bytes4.
* Example:
* msg.data:
* 0xcdcd77c000000000000000000000000000000000000000000000000000000000000
* 000450000000000000000000000000000000000000000000000000000000000000001
* selector (or method ID): 0xcdcd77c0
* signature: baz(uint32,bool)
* @param _data The msg.data from the low level call.
* @return sig First 4 bytes of msg.data i.e. the selector, hash of the signature.
*/
function _getSig(bytes memory _data) internal pure returns (bytes4 sig) {
assembly {
sig := mload(add(_data, 32))
}
}
}