Optimize Address.functionCall removing redundant isContract check (#3469)
Co-authored-by: Francisco <frangio.1@gmail.com>
This commit is contained in:
@ -20,6 +20,7 @@
|
|||||||
* `PaymentSplitter`: add `releasable` getters. ([#3350](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3350))
|
* `PaymentSplitter`: add `releasable` getters. ([#3350](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3350))
|
||||||
* `Initializable`: refactored implementation of modifiers for easier understanding. ([#3450](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3450))
|
* `Initializable`: refactored implementation of modifiers for easier understanding. ([#3450](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3450))
|
||||||
* `Proxies`: remove runtime check of ERC1967 storage slots. ([#3455](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3455))
|
* `Proxies`: remove runtime check of ERC1967 storage slots. ([#3455](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3455))
|
||||||
|
* `Address`: optimize `functionCall` functions by checking contract size only if there is no returned data. ([#3469](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3469))
|
||||||
|
|
||||||
### Breaking changes
|
### Breaking changes
|
||||||
|
|
||||||
|
|||||||
@ -22,7 +22,7 @@ abstract contract BaseRelayMock {
|
|||||||
_currentSender = sender;
|
_currentSender = sender;
|
||||||
|
|
||||||
(bool success, bytes memory returndata) = target.call(data);
|
(bool success, bytes memory returndata) = target.call(data);
|
||||||
Address.verifyCallResult(success, returndata, "low-level call reverted");
|
Address.verifyCallResultFromTarget(target, success, returndata, "low-level call reverted");
|
||||||
|
|
||||||
_currentSender = previousSender;
|
_currentSender = previousSender;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -132,10 +132,8 @@ library Address {
|
|||||||
string memory errorMessage
|
string memory errorMessage
|
||||||
) internal returns (bytes memory) {
|
) internal returns (bytes memory) {
|
||||||
require(address(this).balance >= value, "Address: insufficient balance for call");
|
require(address(this).balance >= value, "Address: insufficient balance for call");
|
||||||
require(isContract(target), "Address: call to non-contract");
|
|
||||||
|
|
||||||
(bool success, bytes memory returndata) = target.call{value: value}(data);
|
(bool success, bytes memory returndata) = target.call{value: value}(data);
|
||||||
return verifyCallResult(success, returndata, errorMessage);
|
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -159,10 +157,8 @@ library Address {
|
|||||||
bytes memory data,
|
bytes memory data,
|
||||||
string memory errorMessage
|
string memory errorMessage
|
||||||
) internal view returns (bytes memory) {
|
) internal view returns (bytes memory) {
|
||||||
require(isContract(target), "Address: static call to non-contract");
|
|
||||||
|
|
||||||
(bool success, bytes memory returndata) = target.staticcall(data);
|
(bool success, bytes memory returndata) = target.staticcall(data);
|
||||||
return verifyCallResult(success, returndata, errorMessage);
|
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -186,15 +182,37 @@ library Address {
|
|||||||
bytes memory data,
|
bytes memory data,
|
||||||
string memory errorMessage
|
string memory errorMessage
|
||||||
) internal returns (bytes memory) {
|
) internal returns (bytes memory) {
|
||||||
require(isContract(target), "Address: delegate call to non-contract");
|
|
||||||
|
|
||||||
(bool success, bytes memory returndata) = target.delegatecall(data);
|
(bool success, bytes memory returndata) = target.delegatecall(data);
|
||||||
return verifyCallResult(success, returndata, errorMessage);
|
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
|
* @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
|
||||||
* revert reason using the provided one.
|
* the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
|
||||||
|
*
|
||||||
|
* _Available since v4.8._
|
||||||
|
*/
|
||||||
|
function verifyCallResultFromTarget(
|
||||||
|
address target,
|
||||||
|
bool success,
|
||||||
|
bytes memory returndata,
|
||||||
|
string memory errorMessage
|
||||||
|
) internal view returns (bytes memory) {
|
||||||
|
if (success) {
|
||||||
|
if (returndata.length == 0) {
|
||||||
|
// only check isContract if the call was successful and the return data is empty
|
||||||
|
// otherwise we already know that it was a contract
|
||||||
|
require(isContract(target), "Address: call to non-contract");
|
||||||
|
}
|
||||||
|
return returndata;
|
||||||
|
} else {
|
||||||
|
_revert(returndata, errorMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
|
||||||
|
* revert reason or using the provided one.
|
||||||
*
|
*
|
||||||
* _Available since v4.3._
|
* _Available since v4.3._
|
||||||
*/
|
*/
|
||||||
@ -206,17 +224,21 @@ library Address {
|
|||||||
if (success) {
|
if (success) {
|
||||||
return returndata;
|
return returndata;
|
||||||
} else {
|
} else {
|
||||||
// Look for revert reason and bubble it up if present
|
_revert(returndata, errorMessage);
|
||||||
if (returndata.length > 0) {
|
}
|
||||||
// The easiest way to bubble the revert reason is using memory via assembly
|
}
|
||||||
/// @solidity memory-safe-assembly
|
|
||||||
assembly {
|
function _revert(bytes memory returndata, string memory errorMessage) private pure {
|
||||||
let returndata_size := mload(returndata)
|
// Look for revert reason and bubble it up if present
|
||||||
revert(add(32, returndata), returndata_size)
|
if (returndata.length > 0) {
|
||||||
}
|
// The easiest way to bubble the revert reason is using memory via assembly
|
||||||
} else {
|
/// @solidity memory-safe-assembly
|
||||||
revert(errorMessage);
|
assembly {
|
||||||
|
let returndata_size := mload(returndata)
|
||||||
|
revert(add(32, returndata), returndata_size)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
revert(errorMessage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -143,7 +143,7 @@ contract('Address', function (accounts) {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
await expectRevert(
|
await expectRevert(
|
||||||
this.mock.functionCall(this.contractRecipient.address, abiEncodedCall, { gas: '100000' }),
|
this.mock.functionCall(this.contractRecipient.address, abiEncodedCall, { gas: '120000' }),
|
||||||
'Address: low-level call failed',
|
'Address: low-level call failed',
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -329,7 +329,7 @@ contract('Address', function (accounts) {
|
|||||||
}, []);
|
}, []);
|
||||||
await expectRevert(
|
await expectRevert(
|
||||||
this.mock.functionStaticCall(recipient, abiEncodedCall),
|
this.mock.functionStaticCall(recipient, abiEncodedCall),
|
||||||
'Address: static call to non-contract',
|
'Address: call to non-contract',
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -375,7 +375,7 @@ contract('Address', function (accounts) {
|
|||||||
}, []);
|
}, []);
|
||||||
await expectRevert(
|
await expectRevert(
|
||||||
this.mock.functionDelegateCall(recipient, abiEncodedCall),
|
this.mock.functionDelegateCall(recipient, abiEncodedCall),
|
||||||
'Address: delegate call to non-contract',
|
'Address: call to non-contract',
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user