feat: add wrapper function for low level calls (#2264)
* feat: add wrapper function for low level calls * add error message parameter * adding unit tests and required mocks * implement error message on SafeERC20 * fixed variable name in tests * Add missing tests * Improve docs. * Add functionCallWithValue * Add functionCallWithValue * Skip balance check on non-value functionCall variants * Increase out of gas test timeout * Fix compile errors * Apply suggestions from code review Co-authored-by: Francisco Giordano <frangio.1@gmail.com> * Add missing tests * Add changelog entry Co-authored-by: Nicolás Venturo <nicolas.venturo@gmail.com> Co-authored-by: Francisco Giordano <frangio.1@gmail.com>
This commit is contained in:
committed by
GitHub
parent
d9fa59f30a
commit
8b58fc7191
@ -57,4 +57,79 @@ library Address {
|
||||
(bool success, ) = recipient.call{ value: amount }("");
|
||||
require(success, "Address: unable to send value, recipient may have reverted");
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Performs a Solidity function call using a low level `call`. A
|
||||
* plain`call` is an unsafe replacement for a function call: use this
|
||||
* function instead.
|
||||
*
|
||||
* If `target` reverts with a revert reason, it is bubbled up by this
|
||||
* function (like regular Solidity function calls).
|
||||
*
|
||||
* Requirements:
|
||||
*
|
||||
* - `target` must be a contract.
|
||||
* - calling `target` with `data` must not revert.
|
||||
*/
|
||||
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
|
||||
return functionCall(target, data, "Address: low-level call failed");
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Same as {Address-functionCall-address-bytes-}, but with
|
||||
* `errorMessage` as a fallback revert reason when `target` reverts.
|
||||
*/
|
||||
function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
|
||||
return _functionCallWithValue(target, data, 0, errorMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Performs a Solidity function call using a low level `call`,
|
||||
* transferring `value` wei. A plain`call` is an unsafe replacement for a
|
||||
* function call: use this function instead.
|
||||
*
|
||||
* If `target` reverts with a revert reason, it is bubbled up by this
|
||||
* function (like regular Solidity function calls).
|
||||
*
|
||||
* Requirements:
|
||||
*
|
||||
* - `target` must be a contract.
|
||||
* - the calling contract must have an ETH balance of at least `value`.
|
||||
* - calling `target` with `data` must not revert.
|
||||
*/
|
||||
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
|
||||
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Same as {Address-functionCallWithValue-address-bytes-uint256-}, but
|
||||
* with `errorMessage` as a fallback revert reason when `target` reverts.
|
||||
*/
|
||||
function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
|
||||
require(address(this).balance >= value, "Address: insufficient balance for call");
|
||||
return _functionCallWithValue(target, data, value, errorMessage);
|
||||
}
|
||||
|
||||
function _functionCallWithValue(address target, bytes memory data, uint256 weiValue, string memory errorMessage) private returns (bytes memory) {
|
||||
require(isContract(target), "Address: call to non-contract");
|
||||
|
||||
// solhint-disable-next-line avoid-low-level-calls
|
||||
(bool success, bytes memory returndata) = target.call{ value: weiValue }(data);
|
||||
if (success) {
|
||||
return returndata;
|
||||
} else {
|
||||
// Look for revert reason and bubble it up if present
|
||||
if (returndata.length > 0) {
|
||||
// The easiest way to bubble the revert reason is using memory via assembly
|
||||
|
||||
// solhint-disable-next-line no-inline-assembly
|
||||
assembly {
|
||||
let returndata_size := mload(returndata)
|
||||
revert(add(32, returndata), returndata_size)
|
||||
}
|
||||
} else {
|
||||
revert(errorMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user