Skip to content

Commit 493bb4d

Browse files
feat: add OpenZeppelin SafeERC20 and Address dependencies (#859)
* feat: add SafeERC20 library * fix: use v4.4.1 of Address library * fix: prettier --------- Co-authored-by: miguelmtz <[email protected]>
1 parent 792c23e commit 493bb4d

File tree

2 files changed

+257
-11
lines changed

2 files changed

+257
-11
lines changed

contracts/dependencies/openzeppelin/contracts/Address.sol

Lines changed: 170 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
// SPDX-License-Identifier: AGPL-3.0
2-
pragma solidity 0.8.10;
1+
// SPDX-License-Identifier: MIT
2+
// OpenZeppelin Contracts v4.4.1 (utils/Address.sol)
3+
4+
pragma solidity ^0.8.0;
35

46
/**
57
* @dev Collection of functions related to the address type
@@ -23,16 +25,15 @@ library Address {
2325
* ====
2426
*/
2527
function isContract(address account) internal view returns (bool) {
26-
// According to EIP-1052, 0x0 is the value returned for not-yet created accounts
27-
// and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
28-
// for accounts without code, i.e. `keccak256('')`
29-
bytes32 codehash;
30-
bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
31-
// solhint-disable-next-line no-inline-assembly
28+
// This method relies on extcodesize, which returns 0 for contracts in
29+
// construction, since the code is only stored at the end of the
30+
// constructor execution.
31+
32+
uint256 size;
3233
assembly {
33-
codehash := extcodehash(account)
34+
size := extcodesize(account)
3435
}
35-
return (codehash != accountHash && codehash != 0x0);
36+
return size > 0;
3637
}
3738

3839
/**
@@ -54,8 +55,166 @@ library Address {
5455
function sendValue(address payable recipient, uint256 amount) internal {
5556
require(address(this).balance >= amount, 'Address: insufficient balance');
5657

57-
// solhint-disable-next-line avoid-low-level-calls, avoid-call-value
5858
(bool success, ) = recipient.call{value: amount}('');
5959
require(success, 'Address: unable to send value, recipient may have reverted');
6060
}
61+
62+
/**
63+
* @dev Performs a Solidity function call using a low level `call`. A
64+
* plain `call` is an unsafe replacement for a function call: use this
65+
* function instead.
66+
*
67+
* If `target` reverts with a revert reason, it is bubbled up by this
68+
* function (like regular Solidity function calls).
69+
*
70+
* Returns the raw returned data. To convert to the expected return value,
71+
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
72+
*
73+
* Requirements:
74+
*
75+
* - `target` must be a contract.
76+
* - calling `target` with `data` must not revert.
77+
*
78+
* _Available since v3.1._
79+
*/
80+
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
81+
return functionCall(target, data, 'Address: low-level call failed');
82+
}
83+
84+
/**
85+
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
86+
* `errorMessage` as a fallback revert reason when `target` reverts.
87+
*
88+
* _Available since v3.1._
89+
*/
90+
function functionCall(
91+
address target,
92+
bytes memory data,
93+
string memory errorMessage
94+
) internal returns (bytes memory) {
95+
return functionCallWithValue(target, data, 0, errorMessage);
96+
}
97+
98+
/**
99+
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
100+
* but also transferring `value` wei to `target`.
101+
*
102+
* Requirements:
103+
*
104+
* - the calling contract must have an ETH balance of at least `value`.
105+
* - the called Solidity function must be `payable`.
106+
*
107+
* _Available since v3.1._
108+
*/
109+
function functionCallWithValue(
110+
address target,
111+
bytes memory data,
112+
uint256 value
113+
) internal returns (bytes memory) {
114+
return functionCallWithValue(target, data, value, 'Address: low-level call with value failed');
115+
}
116+
117+
/**
118+
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
119+
* with `errorMessage` as a fallback revert reason when `target` reverts.
120+
*
121+
* _Available since v3.1._
122+
*/
123+
function functionCallWithValue(
124+
address target,
125+
bytes memory data,
126+
uint256 value,
127+
string memory errorMessage
128+
) internal returns (bytes memory) {
129+
require(address(this).balance >= value, 'Address: insufficient balance for call');
130+
require(isContract(target), 'Address: call to non-contract');
131+
132+
(bool success, bytes memory returndata) = target.call{value: value}(data);
133+
return verifyCallResult(success, returndata, errorMessage);
134+
}
135+
136+
/**
137+
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
138+
* but performing a static call.
139+
*
140+
* _Available since v3.3._
141+
*/
142+
function functionStaticCall(
143+
address target,
144+
bytes memory data
145+
) internal view returns (bytes memory) {
146+
return functionStaticCall(target, data, 'Address: low-level static call failed');
147+
}
148+
149+
/**
150+
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
151+
* but performing a static call.
152+
*
153+
* _Available since v3.3._
154+
*/
155+
function functionStaticCall(
156+
address target,
157+
bytes memory data,
158+
string memory errorMessage
159+
) internal view returns (bytes memory) {
160+
require(isContract(target), 'Address: static call to non-contract');
161+
162+
(bool success, bytes memory returndata) = target.staticcall(data);
163+
return verifyCallResult(success, returndata, errorMessage);
164+
}
165+
166+
/**
167+
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
168+
* but performing a delegate call.
169+
*
170+
* _Available since v3.4._
171+
*/
172+
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
173+
return functionDelegateCall(target, data, 'Address: low-level delegate call failed');
174+
}
175+
176+
/**
177+
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
178+
* but performing a delegate call.
179+
*
180+
* _Available since v3.4._
181+
*/
182+
function functionDelegateCall(
183+
address target,
184+
bytes memory data,
185+
string memory errorMessage
186+
) internal returns (bytes memory) {
187+
require(isContract(target), 'Address: delegate call to non-contract');
188+
189+
(bool success, bytes memory returndata) = target.delegatecall(data);
190+
return verifyCallResult(success, returndata, errorMessage);
191+
}
192+
193+
/**
194+
* @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
195+
* revert reason using the provided one.
196+
*
197+
* _Available since v4.3._
198+
*/
199+
function verifyCallResult(
200+
bool success,
201+
bytes memory returndata,
202+
string memory errorMessage
203+
) internal pure returns (bytes memory) {
204+
if (success) {
205+
return returndata;
206+
} else {
207+
// Look for revert reason and bubble it up if present
208+
if (returndata.length > 0) {
209+
// The easiest way to bubble the revert reason is using memory via assembly
210+
211+
assembly {
212+
let returndata_size := mload(returndata)
213+
revert(add(32, returndata), returndata_size)
214+
}
215+
} else {
216+
revert(errorMessage);
217+
}
218+
}
219+
}
61220
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
// SPDX-License-Identifier: MIT
2+
// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)
3+
4+
pragma solidity ^0.8.0;
5+
6+
import './IERC20.sol';
7+
import './Address.sol';
8+
9+
/**
10+
* @title SafeERC20
11+
* @dev Wrappers around ERC20 operations that throw on failure (when the token
12+
* contract returns false). Tokens that return no value (and instead revert or
13+
* throw on failure) are also supported, non-reverting calls are assumed to be
14+
* successful.
15+
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
16+
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
17+
*/
18+
library SafeERC20 {
19+
using Address for address;
20+
21+
function safeTransfer(IERC20 token, address to, uint256 value) internal {
22+
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
23+
}
24+
25+
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
26+
_callOptionalReturn(
27+
token,
28+
abi.encodeWithSelector(token.transferFrom.selector, from, to, value)
29+
);
30+
}
31+
32+
/**
33+
* @dev Deprecated. This function has issues similar to the ones found in
34+
* {IERC20-approve}, and its usage is discouraged.
35+
*
36+
* Whenever possible, use {safeIncreaseAllowance} and
37+
* {safeDecreaseAllowance} instead.
38+
*/
39+
function safeApprove(IERC20 token, address spender, uint256 value) internal {
40+
// safeApprove should only be called when setting an initial allowance,
41+
// or when resetting it to zero. To increase and decrease it, use
42+
// 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
43+
require(
44+
(value == 0) || (token.allowance(address(this), spender) == 0),
45+
'SafeERC20: approve from non-zero to non-zero allowance'
46+
);
47+
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
48+
}
49+
50+
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
51+
uint256 newAllowance = token.allowance(address(this), spender) + value;
52+
_callOptionalReturn(
53+
token,
54+
abi.encodeWithSelector(token.approve.selector, spender, newAllowance)
55+
);
56+
}
57+
58+
function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
59+
unchecked {
60+
uint256 oldAllowance = token.allowance(address(this), spender);
61+
require(oldAllowance >= value, 'SafeERC20: decreased allowance below zero');
62+
uint256 newAllowance = oldAllowance - value;
63+
_callOptionalReturn(
64+
token,
65+
abi.encodeWithSelector(token.approve.selector, spender, newAllowance)
66+
);
67+
}
68+
}
69+
70+
/**
71+
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
72+
* on the return value: the return value is optional (but if data is returned, it must not be false).
73+
* @param token The token targeted by the call.
74+
* @param data The call data (encoded using abi.encode or one of its variants).
75+
*/
76+
function _callOptionalReturn(IERC20 token, bytes memory data) private {
77+
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
78+
// we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
79+
// the target address contains contract code and also asserts for success in the low-level call.
80+
81+
bytes memory returndata = address(token).functionCall(data, 'SafeERC20: low-level call failed');
82+
if (returndata.length > 0) {
83+
// Return data is optional
84+
require(abi.decode(returndata, (bool)), 'SafeERC20: ERC20 operation did not succeed');
85+
}
86+
}
87+
}

0 commit comments

Comments
 (0)