Skip to content

Commit

Permalink
Optimize Address.functionCall removing redundant isContract check (#3469
Browse files Browse the repository at this point in the history
)

Co-authored-by: Francisco <frangio.1@gmail.com>
  • Loading branch information
ZumZoom and frangio authored Jun 27, 2022
1 parent e8c60f9 commit 6f88199
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 25 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
* `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))
* `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

Expand Down
2 changes: 1 addition & 1 deletion contracts/mocks/crosschain/bridges.sol
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ abstract contract BaseRelayMock {
_currentSender = sender;

(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;
}
Expand Down
64 changes: 43 additions & 21 deletions contracts/utils/Address.sol
Original file line number Diff line number Diff line change
Expand Up @@ -132,10 +132,8 @@ library Address {
string memory errorMessage
) internal returns (bytes memory) {
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);
return verifyCallResult(success, returndata, errorMessage);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}

/**
Expand All @@ -159,10 +157,8 @@ library Address {
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
require(isContract(target), "Address: static call to non-contract");

(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResult(success, returndata, errorMessage);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}

/**
Expand All @@ -186,15 +182,37 @@ library Address {
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
require(isContract(target), "Address: delegate call to non-contract");

(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResult(success, returndata, errorMessage);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}

/**
* @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
* 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 verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason using the provided one.
* @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._
*/
Expand All @@ -206,17 +224,21 @@ library Address {
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
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
_revert(returndata, errorMessage);
}
}

function _revert(bytes memory returndata, string memory errorMessage) private pure {
// 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
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
6 changes: 3 additions & 3 deletions test/utils/Address.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ contract('Address', function (accounts) {
}, []);

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',
);
});
Expand Down Expand Up @@ -329,7 +329,7 @@ contract('Address', function (accounts) {
}, []);
await expectRevert(
this.mock.functionStaticCall(recipient, abiEncodedCall),
'Address: static call to non-contract',
'Address: call to non-contract',
);
});
});
Expand Down Expand Up @@ -375,7 +375,7 @@ contract('Address', function (accounts) {
}, []);
await expectRevert(
this.mock.functionDelegateCall(recipient, abiEncodedCall),
'Address: delegate call to non-contract',
'Address: call to non-contract',
);
});
});
Expand Down

0 comments on commit 6f88199

Please sign in to comment.