@1inch/solidity-utils 2.1.1 → 2.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  <div align="center">
2
- <img src="https://github.com/1inch/farming/blob/master/.github/1inch_github_w.svg#gh-light-mode-only">
3
- <img src="https://github.com/1inch/farming/blob/master/.github/1inch_github_b.svg#gh-dark-mode-only">
2
+ <img src="https://github.com/1inch/solidity-utils/blob/master/.github/1inch_github_w.svg#gh-light-mode-only">
3
+ <img src="https://github.com/1inch/solidity-utils/blob/master/.github/1inch_github_b.svg#gh-dark-mode-only">
4
4
  </div>
5
5
 
6
6
  # Utils library for contracts and tests
@@ -65,6 +65,66 @@ Add to `package.json` file solidity compiler version and shortcut to run command
65
65
 
66
66
  ...
67
67
 
68
+ #### Dependencies list (imports-list)
69
+
70
+ Lists all imports recursively for the given solidity contract file.
71
+
72
+ ##### Usage
73
+ ```
74
+ npx imports-list -i <solidity file> [-a <alias list>]
75
+ ```
76
+
77
+ Available parameters
78
+ ```
79
+ Options:
80
+ -i, --input <input> file to get dependencies for
81
+ -a, --alias [alias...] projects alias list
82
+ -h, --help display help for command
83
+ ```
84
+ Aliases are used to provide source code for third-party projects.
85
+ For example, your contract uses imports from your other project and import is defined as
86
+ ```
87
+ import "@1inch/otherproject/contracts/dependency.sol";
88
+ ```
89
+ and you've got source code for `@1inch/otherproject` locally. Then you provide local path for the project to rip dependencies from `dependency.sol` as well.
90
+ If there are several dependencies they should be provided using space as separator.
91
+
92
+ ##### Example
93
+ File imports
94
+ ```Solidity
95
+ #rootFile.sol
96
+ import '@1inch/otherproject/contracts/dependency.sol'
97
+
98
+ #@1inch/otherproject/contracts/dependency.sol
99
+ import 'helpers/helper.sol'
100
+ ```
101
+ File and folder structure
102
+ ```
103
+ rootFolder/
104
+
105
+ -- mainProject/
106
+ ---- contracts/
107
+ ------ rootFile.sol
108
+
109
+ -- dependencyProject/
110
+ ---- helpers/
111
+ ------ helper.sol
112
+ ---- dependency.sol
113
+ ```
114
+ Command
115
+ ```
116
+ rootFolder/mainProject % npx imports-list -i './contracts/rootFile.sol' -a '@1inch/otherproject' '../dependencyProject'
117
+ ```
118
+ Output
119
+ ```
120
+ Project => root
121
+ not set
122
+
123
+ Project => @1inch/otherproject
124
+ ../otherproject/contracts/dependency.sol
125
+ ../otherproject/contracts/helpers/helper.sol
126
+ ```
127
+
68
128
  #### Test documentation generator (test-docgen)
69
129
  Script generates documentation for tests in markdown format.
70
130
  Give descriptions for `describe` and `it` sections and build documentation using these descriptions.
@@ -9,7 +9,9 @@ contract GasChecker {
9
9
  modifier checkGasCost(uint256 expected) {
10
10
  uint256 gas = gasleft();
11
11
  _;
12
- unchecked { gas -= gasleft(); }
12
+ unchecked {
13
+ gas -= gasleft();
14
+ }
13
15
  if (expected > 0 && gas != expected) revert GasCostDiffers(expected, gas);
14
16
  }
15
17
  }
@@ -6,7 +6,7 @@ pragma abicoder v1;
6
6
  import "./EthReceiver.sol";
7
7
 
8
8
  abstract contract OnlyWethReceiver is EthReceiver {
9
- address private immutable _WETH; // solhint-disable-line var-name-mixedcase
9
+ address private immutable _WETH; // solhint-disable-line var-name-mixedcase
10
10
 
11
11
  constructor(address weth) {
12
12
  _WETH = address(weth);
@@ -3,7 +3,15 @@
3
3
  pragma solidity ^0.8.0;
4
4
  pragma abicoder v1;
5
5
 
6
-
7
6
  interface IDaiLikePermit {
8
- function permit(address holder, address spender, uint256 nonce, uint256 expiry, bool allowed, uint8 v, bytes32 r, bytes32 s) external;
7
+ function permit(
8
+ address holder,
9
+ address spender,
10
+ uint256 nonce,
11
+ uint256 expiry,
12
+ bool allowed,
13
+ uint8 v,
14
+ bytes32 r,
15
+ bytes32 s
16
+ ) external;
9
17
  }
@@ -3,8 +3,8 @@
3
3
  pragma solidity ^0.8.0;
4
4
  pragma abicoder v1;
5
5
 
6
-
7
6
  interface IERC20MetadataUppercase {
8
- function NAME() external view returns (string memory); // solhint-disable-line func-name-mixedcase
9
- function SYMBOL() external view returns (string memory); // solhint-disable-line func-name-mixedcase
7
+ function NAME() external view returns (string memory); // solhint-disable-line func-name-mixedcase
8
+
9
+ function SYMBOL() external view returns (string memory); // solhint-disable-line func-name-mixedcase
10
10
  }
@@ -5,8 +5,8 @@ pragma abicoder v1;
5
5
 
6
6
  import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
7
7
 
8
-
9
8
  interface IWETH is IERC20 {
10
9
  function deposit() external payable;
10
+
11
11
  function withdraw(uint256 amount) external;
12
12
  }
@@ -3,40 +3,50 @@
3
3
  pragma solidity ^0.8.0;
4
4
  pragma abicoder v1;
5
5
 
6
+ /// @title Library that implements address array on mapping, stores array length at 0 index.
6
7
  library AddressArray {
7
8
  error IndexOutOfBounds();
8
9
  error PopFromEmptyArray();
9
10
  error OutputArrayTooSmall();
10
11
 
12
+ /// @dev Data struct containing raw mapping.
11
13
  struct Data {
12
14
  mapping(uint256 => uint256) _raw;
13
15
  }
14
16
 
15
- function length(Data storage self) internal view returns(uint256) {
17
+ /// @dev Length of array.
18
+ function length(Data storage self) internal view returns (uint256) {
16
19
  return self._raw[0] >> 160;
17
20
  }
18
21
 
19
- function at(Data storage self, uint i) internal view returns(address) {
22
+ /// @dev Returns data item from `self` storage at `i`.
23
+ function at(Data storage self, uint256 i) internal view returns (address) {
20
24
  return address(uint160(self._raw[i]));
21
25
  }
22
26
 
23
- function get(Data storage self) internal view returns(address[] memory arr) {
27
+ /// @dev Returns list of addresses from storage `self`.
28
+ function get(Data storage self) internal view returns (address[] memory arr) {
24
29
  uint256 lengthAndFirst = self._raw[0];
25
30
  arr = new address[](lengthAndFirst >> 160);
26
31
  _get(self, arr, lengthAndFirst);
27
32
  }
28
33
 
29
- function get(Data storage self, address[] memory output) internal view returns(address[] memory) {
34
+ /// @dev Puts list of addresses from `self` storage into `output` array.
35
+ function get(Data storage self, address[] memory output) internal view returns (address[] memory) {
30
36
  return _get(self, output, self._raw[0]);
31
37
  }
32
38
 
33
- function _get(Data storage self, address[] memory output, uint256 lengthAndFirst) private view returns(address[] memory) {
39
+ function _get(
40
+ Data storage self,
41
+ address[] memory output,
42
+ uint256 lengthAndFirst
43
+ ) private view returns (address[] memory) {
34
44
  uint256 len = lengthAndFirst >> 160;
35
45
  if (len > output.length) revert OutputArrayTooSmall();
36
46
  if (len > 0) {
37
47
  output[0] = address(uint160(lengthAndFirst));
38
48
  unchecked {
39
- for (uint i = 1; i < len; i++) {
49
+ for (uint256 i = 1; i < len; i++) {
40
50
  output[i] = address(uint160(self._raw[i]));
41
51
  }
42
52
  }
@@ -44,14 +54,14 @@ library AddressArray {
44
54
  return output;
45
55
  }
46
56
 
47
- function push(Data storage self, address account) internal returns(uint256) {
57
+ /// @dev Array push back `account` operation on storage `self`.
58
+ function push(Data storage self, address account) internal returns (uint256) {
48
59
  unchecked {
49
60
  uint256 lengthAndFirst = self._raw[0];
50
61
  uint256 len = lengthAndFirst >> 160;
51
62
  if (len == 0) {
52
63
  self._raw[0] = (1 << 160) + uint160(account);
53
- }
54
- else {
64
+ } else {
55
65
  self._raw[0] = lengthAndFirst + (1 << 160);
56
66
  self._raw[len] = uint160(account);
57
67
  }
@@ -59,6 +69,7 @@ library AddressArray {
59
69
  }
60
70
  }
61
71
 
72
+ /// @dev Array pop back operation for storage `self`.
62
73
  function pop(Data storage self) internal {
63
74
  unchecked {
64
75
  uint256 lengthAndFirst = self._raw[0];
@@ -71,14 +82,18 @@ library AddressArray {
71
82
  }
72
83
  }
73
84
 
74
- function set(Data storage self, uint256 index, address account) internal {
85
+ /// @dev Set element for storage `self` at `index` to `account`.
86
+ function set(
87
+ Data storage self,
88
+ uint256 index,
89
+ address account
90
+ ) internal {
75
91
  uint256 len = length(self);
76
92
  if (index >= len) revert IndexOutOfBounds();
77
93
 
78
94
  if (index == 0) {
79
95
  self._raw[0] = (len << 160) | uint160(account);
80
- }
81
- else {
96
+ } else {
82
97
  self._raw[index] = uint160(account);
83
98
  }
84
99
  }
@@ -5,27 +5,40 @@ pragma abicoder v1;
5
5
 
6
6
  import "./AddressArray.sol";
7
7
 
8
+ /** @title Library that is using AddressArray library for AddressArray.Data
9
+ * and allows Set operations on address storage data:
10
+ * 1. add
11
+ * 2. remove
12
+ * 3. contains
13
+ */
8
14
  library AddressSet {
9
15
  using AddressArray for AddressArray.Data;
10
16
 
17
+ /** @dev Data struct from AddressArray.Data items
18
+ * and lookup mapping address => index in data array.
19
+ */
11
20
  struct Data {
12
21
  AddressArray.Data items;
13
22
  mapping(address => uint256) lookup;
14
23
  }
15
24
 
16
- function length(Data storage s) internal view returns(uint) {
25
+ /// @dev Length of data storage.
26
+ function length(Data storage s) internal view returns (uint256) {
17
27
  return s.items.length();
18
28
  }
19
29
 
20
- function at(Data storage s, uint index) internal view returns(address) {
30
+ /// @dev Returns data item from `s` storage at `index`.
31
+ function at(Data storage s, uint256 index) internal view returns (address) {
21
32
  return s.items.at(index);
22
33
  }
23
34
 
24
- function contains(Data storage s, address item) internal view returns(bool) {
35
+ /// @dev Returns true if storage `s` has `item`.
36
+ function contains(Data storage s, address item) internal view returns (bool) {
25
37
  return s.lookup[item] != 0;
26
38
  }
27
39
 
28
- function add(Data storage s, address item) internal returns(bool) {
40
+ /// @dev Adds `item` into storage `s` and returns true if successful.
41
+ function add(Data storage s, address item) internal returns (bool) {
29
42
  if (s.lookup[item] > 0) {
30
43
  return false;
31
44
  }
@@ -33,8 +46,9 @@ library AddressSet {
33
46
  return true;
34
47
  }
35
48
 
36
- function remove(Data storage s, address item) internal returns(bool) {
37
- uint index = s.lookup[item];
49
+ /// @dev Removes `item` from storage `s` and returns true if successful.
50
+ function remove(Data storage s, address item) internal returns (bool) {
51
+ uint256 index = s.lookup[item];
38
52
  if (index == 0) {
39
53
  return false;
40
54
  }
@@ -19,7 +19,12 @@ library ECDSA {
19
19
  uint256 private constant _COMPACT_S_MASK = 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff;
20
20
  uint256 private constant _COMPACT_V_SHIFT = 255;
21
21
 
22
- function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal view returns(address signer) {
22
+ function recover(
23
+ bytes32 hash,
24
+ uint8 v,
25
+ bytes32 r,
26
+ bytes32 s
27
+ ) internal view returns (address signer) {
23
28
  /// @solidity memory-safe-assembly
24
29
  assembly { // solhint-disable-line no-inline-assembly
25
30
  if lt(s, _S_BOUNDARY) {
@@ -36,7 +41,11 @@ library ECDSA {
36
41
  }
37
42
  }
38
43
 
39
- function recover(bytes32 hash, bytes32 r, bytes32 vs) internal view returns(address signer) {
44
+ function recover(
45
+ bytes32 hash,
46
+ bytes32 r,
47
+ bytes32 vs
48
+ ) internal view returns (address signer) {
40
49
  /// @solidity memory-safe-assembly
41
50
  assembly { // solhint-disable-line no-inline-assembly
42
51
  let s := and(vs, _COMPACT_S_MASK)
@@ -54,14 +63,14 @@ library ECDSA {
54
63
  }
55
64
  }
56
65
 
57
- /// WARNING!!!
66
+ /// @dev WARNING!!!
58
67
  /// There is a known signature malleability issue with two representations of signatures!
59
68
  /// Even though this function is able to verify both standard 65-byte and compact 64-byte EIP-2098 signatures
60
69
  /// one should never use raw signatures for any kind of invalidation logic in their code.
61
70
  /// As the standard and compact representations are interchangeable any invalidation logic that relies on
62
71
  /// signature uniqueness will get rekt.
63
72
  /// More info: https://github.com/OpenZeppelin/openzeppelin-contracts/security/advisories/GHSA-4h98-2769-gh6h
64
- function recover(bytes32 hash, bytes calldata signature) internal view returns(address signer) {
73
+ function recover(bytes32 hash, bytes calldata signature) internal view returns (address signer) {
65
74
  /// @solidity memory-safe-assembly
66
75
  assembly { // solhint-disable-line no-inline-assembly
67
76
  let ptr := mload(0x40)
@@ -97,7 +106,11 @@ library ECDSA {
97
106
  }
98
107
  }
99
108
 
100
- function recoverOrIsValidSignature(address signer, bytes32 hash, bytes calldata signature) internal view returns(bool success) {
109
+ function recoverOrIsValidSignature(
110
+ address signer,
111
+ bytes32 hash,
112
+ bytes calldata signature
113
+ ) internal view returns (bool success) {
101
114
  if (signer == address(0)) return false;
102
115
  if ((signature.length == 64 || signature.length == 65) && recover(hash, signature) == signer) {
103
116
  return true;
@@ -105,7 +118,13 @@ library ECDSA {
105
118
  return isValidSignature(signer, hash, signature);
106
119
  }
107
120
 
108
- function recoverOrIsValidSignature(address signer, bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal view returns(bool success) {
121
+ function recoverOrIsValidSignature(
122
+ address signer,
123
+ bytes32 hash,
124
+ uint8 v,
125
+ bytes32 r,
126
+ bytes32 s
127
+ ) internal view returns (bool success) {
109
128
  if (signer == address(0)) return false;
110
129
  if (recover(hash, v, r, s) == signer) {
111
130
  return true;
@@ -113,7 +132,12 @@ library ECDSA {
113
132
  return isValidSignature(signer, hash, v, r, s);
114
133
  }
115
134
 
116
- function recoverOrIsValidSignature(address signer, bytes32 hash, bytes32 r, bytes32 vs) internal view returns(bool success) {
135
+ function recoverOrIsValidSignature(
136
+ address signer,
137
+ bytes32 hash,
138
+ bytes32 r,
139
+ bytes32 vs
140
+ ) internal view returns (bool success) {
117
141
  if (signer == address(0)) return false;
118
142
  if (recover(hash, r, vs) == signer) {
119
143
  return true;
@@ -121,7 +145,12 @@ library ECDSA {
121
145
  return isValidSignature(signer, hash, r, vs);
122
146
  }
123
147
 
124
- function recoverOrIsValidSignature65(address signer, bytes32 hash, bytes32 r, bytes32 vs) internal view returns(bool success) {
148
+ function recoverOrIsValidSignature65(
149
+ address signer,
150
+ bytes32 hash,
151
+ bytes32 r,
152
+ bytes32 vs
153
+ ) internal view returns (bool success) {
125
154
  if (signer == address(0)) return false;
126
155
  if (recover(hash, r, vs) == signer) {
127
156
  return true;
@@ -129,7 +158,11 @@ library ECDSA {
129
158
  return isValidSignature65(signer, hash, r, vs);
130
159
  }
131
160
 
132
- function isValidSignature(address signer, bytes32 hash, bytes calldata signature) internal view returns(bool success) {
161
+ function isValidSignature(
162
+ address signer,
163
+ bytes32 hash,
164
+ bytes calldata signature
165
+ ) internal view returns (bool success) {
133
166
  // (bool success, bytes memory data) = signer.staticcall(abi.encodeWithSelector(IERC1271.isValidSignature.selector, hash, signature));
134
167
  // return success && data.length >= 4 && abi.decode(data, (bytes4)) == IERC1271.isValidSignature.selector;
135
168
  bytes4 selector = IERC1271.isValidSignature.selector;
@@ -148,7 +181,13 @@ library ECDSA {
148
181
  }
149
182
  }
150
183
 
151
- function isValidSignature(address signer, bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal view returns(bool success) {
184
+ function isValidSignature(
185
+ address signer,
186
+ bytes32 hash,
187
+ uint8 v,
188
+ bytes32 r,
189
+ bytes32 s
190
+ ) internal view returns (bool success) {
152
191
  bytes4 selector = IERC1271.isValidSignature.selector;
153
192
  /// @solidity memory-safe-assembly
154
193
  assembly { // solhint-disable-line no-inline-assembly
@@ -167,7 +206,12 @@ library ECDSA {
167
206
  }
168
207
  }
169
208
 
170
- function isValidSignature(address signer, bytes32 hash, bytes32 r, bytes32 vs) internal view returns(bool success) {
209
+ function isValidSignature(
210
+ address signer,
211
+ bytes32 hash,
212
+ bytes32 r,
213
+ bytes32 vs
214
+ ) internal view returns (bool success) {
171
215
  // (bool success, bytes memory data) = signer.staticcall(abi.encodeWithSelector(IERC1271.isValidSignature.selector, hash, abi.encodePacked(r, vs)));
172
216
  // return success && data.length >= 4 && abi.decode(data, (bytes4)) == IERC1271.isValidSignature.selector;
173
217
  bytes4 selector = IERC1271.isValidSignature.selector;
@@ -187,7 +231,12 @@ library ECDSA {
187
231
  }
188
232
  }
189
233
 
190
- function isValidSignature65(address signer, bytes32 hash, bytes32 r, bytes32 vs) internal view returns(bool success) {
234
+ function isValidSignature65(
235
+ address signer,
236
+ bytes32 hash,
237
+ bytes32 r,
238
+ bytes32 vs
239
+ ) internal view returns (bool success) {
191
240
  // (bool success, bytes memory data) = signer.staticcall(abi.encodeWithSelector(IERC1271.isValidSignature.selector, hash, abi.encodePacked(r, vs & ~uint256(1 << 255), uint8(vs >> 255))));
192
241
  // return success && data.length >= 4 && abi.decode(data, (bytes4)) == IERC1271.isValidSignature.selector;
193
242
  bytes4 selector = IERC1271.isValidSignature.selector;
@@ -3,7 +3,9 @@
3
3
  pragma solidity ^0.8.0;
4
4
  pragma abicoder v1;
5
5
 
6
+ /// @title Revert reason forwarder.
6
7
  library RevertReasonForwarder {
8
+ /// @dev Forwards latest externall call revert.
7
9
  function reRevert() internal pure {
8
10
  // bubble up revert reason from latest external call
9
11
  /// @solidity memory-safe-assembly
@@ -6,21 +6,22 @@ pragma abicoder v1;
6
6
  import "./StringUtil.sol";
7
7
 
8
8
  /** @title Library that allows to parse unsuccessful arbitrary calls revert reasons.
9
- * See https://solidity.readthedocs.io/en/latest/control-structures.html#revert for details.
10
- * Note that we assume revert reason being abi-encoded as Error(string) so it may fail to parse reason
11
- * if structured reverts appear in the future.
12
- *
13
- * All unsuccessful parsings get encoded as Unknown(data) string
14
- */
9
+ * See https://solidity.readthedocs.io/en/latest/control-structures.html#revert for details.
10
+ * Note that we assume revert reason being abi-encoded as Error(string) so it may fail to parse reason
11
+ * if structured reverts appear in the future.
12
+ *
13
+ * All unsuccessful parsings get encoded as Unknown(data) string
14
+ */
15
15
  library RevertReasonParser {
16
16
  using StringUtil for uint256;
17
17
  using StringUtil for bytes;
18
18
 
19
19
  error InvalidRevertReason();
20
20
 
21
- bytes4 constant private _ERROR_SELECTOR = bytes4(keccak256("Error(string)"));
22
- bytes4 constant private _PANIC_SELECTOR = bytes4(keccak256("Panic(uint256)"));
21
+ bytes4 private constant _ERROR_SELECTOR = bytes4(keccak256("Error(string)"));
22
+ bytes4 private constant _PANIC_SELECTOR = bytes4(keccak256("Panic(uint256)"));
23
23
 
24
+ /// @dev Parses error `data` and returns actual with `prefix`.
24
25
  function parse(bytes memory data, string memory prefix) internal pure returns (string memory) {
25
26
  // https://solidity.readthedocs.io/en/latest/control-structures.html#revert
26
27
  // We assume that revert reason is abi-encoded as Error(string)
@@ -8,6 +8,7 @@ import "@openzeppelin/contracts/token/ERC20/extensions/draft-IERC20Permit.sol";
8
8
  import "../interfaces/IDaiLikePermit.sol";
9
9
  import "../libraries/RevertReasonForwarder.sol";
10
10
 
11
+ /// @title Implements efficient safe methods for ERC20 interface.
11
12
  library SafeERC20 {
12
13
  error SafeTransferFailed();
13
14
  error SafeTransferFromFailed();
@@ -16,8 +17,13 @@ library SafeERC20 {
16
17
  error SafeDecreaseAllowanceFailed();
17
18
  error SafePermitBadLength();
18
19
 
19
- // Ensures method do not revert or return boolean `true`, admits call to non-smart-contract
20
- function safeTransferFrom(IERC20 token, address from, address to, uint256 amount) internal {
20
+ /// @dev Ensures method do not revert or return boolean `true`, admits call to non-smart-contract.
21
+ function safeTransferFrom(
22
+ IERC20 token,
23
+ address from,
24
+ address to,
25
+ uint256 amount
26
+ ) internal {
21
27
  bytes4 selector = token.transferFrom.selector;
22
28
  bool success;
23
29
  /// @solidity memory-safe-assembly
@@ -31,43 +37,67 @@ library SafeERC20 {
31
37
  success := call(gas(), token, 0, data, 100, 0x0, 0x20)
32
38
  if success {
33
39
  switch returndatasize()
34
- case 0 { success := gt(extcodesize(token), 0) }
35
- default { success := and(gt(returndatasize(), 31), eq(mload(0), 1)) }
40
+ case 0 {
41
+ success := gt(extcodesize(token), 0)
42
+ }
43
+ default {
44
+ success := and(gt(returndatasize(), 31), eq(mload(0), 1))
45
+ }
36
46
  }
37
47
  }
38
48
  if (!success) revert SafeTransferFromFailed();
39
49
  }
40
50
 
41
- // Ensures method do not revert or return boolean `true`, admits call to non-smart-contract
42
- function safeTransfer(IERC20 token, address to, uint256 value) internal {
51
+ /// @dev Ensures method do not revert or return boolean `true`, admits call to non-smart-contract.
52
+ function safeTransfer(
53
+ IERC20 token,
54
+ address to,
55
+ uint256 value
56
+ ) internal {
43
57
  if (!_makeCall(token, token.transfer.selector, to, value)) {
44
58
  revert SafeTransferFailed();
45
59
  }
46
60
  }
47
61
 
48
- // If `approve(from, to, amount)` fails, try to `approve(from, to, 0)` before retry
49
- function forceApprove(IERC20 token, address spender, uint256 value) internal {
62
+ /// @dev If `approve(from, to, amount)` fails, try to `approve(from, to, 0)` before retry.
63
+ function forceApprove(
64
+ IERC20 token,
65
+ address spender,
66
+ uint256 value
67
+ ) internal {
50
68
  if (!_makeCall(token, token.approve.selector, spender, value)) {
51
- if (!_makeCall(token, token.approve.selector, spender, 0) ||
52
- !_makeCall(token, token.approve.selector, spender, value))
53
- {
69
+ if (
70
+ !_makeCall(token, token.approve.selector, spender, 0) ||
71
+ !_makeCall(token, token.approve.selector, spender, value)
72
+ ) {
54
73
  revert ForceApproveFailed();
55
74
  }
56
75
  }
57
76
  }
58
77
 
59
- function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
78
+ /// @dev Allowance increase with safe math check.
79
+ function safeIncreaseAllowance(
80
+ IERC20 token,
81
+ address spender,
82
+ uint256 value
83
+ ) internal {
60
84
  uint256 allowance = token.allowance(address(this), spender);
61
85
  if (value > type(uint256).max - allowance) revert SafeIncreaseAllowanceFailed();
62
86
  forceApprove(token, spender, allowance + value);
63
87
  }
64
88
 
65
- function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
89
+ /// @dev Allowance decrease with safe math check.
90
+ function safeDecreaseAllowance(
91
+ IERC20 token,
92
+ address spender,
93
+ uint256 value
94
+ ) internal {
66
95
  uint256 allowance = token.allowance(address(this), spender);
67
96
  if (value > allowance) revert SafeDecreaseAllowanceFailed();
68
97
  forceApprove(token, spender, allowance - value);
69
98
  }
70
99
 
100
+ /// @dev Calls either ERC20 or Dai `permit` for `token`, if unsuccessful forwards revert from external call.
71
101
  function safePermit(IERC20 token, bytes calldata permit) internal {
72
102
  bool success;
73
103
  if (permit.length == 32 * 7) {
@@ -80,7 +110,12 @@ library SafeERC20 {
80
110
  if (!success) RevertReasonForwarder.reRevert();
81
111
  }
82
112
 
83
- function _makeCall(IERC20 token, bytes4 selector, address to, uint256 amount) private returns(bool success) {
113
+ function _makeCall(
114
+ IERC20 token,
115
+ bytes4 selector,
116
+ address to,
117
+ uint256 amount
118
+ ) private returns (bool success) {
84
119
  /// @solidity memory-safe-assembly
85
120
  assembly { // solhint-disable-line no-inline-assembly
86
121
  let data := mload(0x40)
@@ -91,13 +126,21 @@ library SafeERC20 {
91
126
  success := call(gas(), token, 0, data, 0x44, 0x0, 0x20)
92
127
  if success {
93
128
  switch returndatasize()
94
- case 0 { success := gt(extcodesize(token), 0) }
95
- default { success := and(gt(returndatasize(), 31), eq(mload(0), 1)) }
129
+ case 0 {
130
+ success := gt(extcodesize(token), 0)
131
+ }
132
+ default {
133
+ success := and(gt(returndatasize(), 31), eq(mload(0), 1))
134
+ }
96
135
  }
97
136
  }
98
137
  }
99
138
 
100
- function _makeCalldataCall(IERC20 token, bytes4 selector, bytes calldata args) private returns(bool success) {
139
+ function _makeCalldataCall(
140
+ IERC20 token,
141
+ bytes4 selector,
142
+ bytes calldata args
143
+ ) private returns (bool success) {
101
144
  /// @solidity memory-safe-assembly
102
145
  assembly { // solhint-disable-line no-inline-assembly
103
146
  let len := add(4, args.length)
@@ -108,8 +151,12 @@ library SafeERC20 {
108
151
  success := call(gas(), token, 0, data, len, 0x0, 0x20)
109
152
  if success {
110
153
  switch returndatasize()
111
- case 0 { success := gt(extcodesize(token), 0) }
112
- default { success := and(gt(returndatasize(), 31), eq(mload(0), 1)) }
154
+ case 0 {
155
+ success := gt(extcodesize(token), 0)
156
+ }
157
+ default {
158
+ success := and(gt(returndatasize(), 31), eq(mload(0), 1))
159
+ }
113
160
  }
114
161
  }
115
162
  }