Files
openzeppelin-contracts/contracts/utils/Address.sol

145 lines
5.7 KiB
Solidity
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.2.0) (utils/Address.sol)
pragma solidity ^0.8.20;
import {Errors} from "./Errors.sol";
/**
* @dev 与地址类型相关的函数集合
*/
library Address {
/**
* @dev 在 `target` 处没有代码(它不是一个合约)。
*/
error AddressEmptyCode(address target);
/**
* @dev Solidity 的 `transfer` 的替代品:向 `recipient` 发送 `amount` wei
* 转发所有可用的 gas 并在出错时回退。
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] 增加了某些操作码的 gas 成本,
* 可能使合约超过 `transfer` 施加的 2300 gas 限制,使它们无法通过 `transfer` 接收资金。
* {sendValue} 移除了这个限制。
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[了解更多]。
*
* 重要:因为控制权转移到 `recipient`,必须注意不要创建重入漏洞。
* 考虑使用 {ReentrancyGuard} 或
* https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[检查-效果-交互模式]。
*/
function sendValue(address payable recipient, uint256 amount) internal {
if (address(this).balance < amount) {
revert Errors.InsufficientBalance(address(this).balance, amount);
}
(bool success, bytes memory returndata) = recipient.call{value: amount}("");
if (!success) {
_revert(returndata);
}
}
/**
* @dev 使用低级 `call` 执行 Solidity 函数调用。普通的 `call` 是函数调用的不安全替代品:
* 请使用此函数替代。
*
* 如果 `target` 使用回退原因或自定义错误回退,则此函数会将其冒泡(如常规 Solidity 函数调用)。
* 但是,如果调用没有返回原因就回退,则此函数会使用 {Errors.FailedCall} 错误回退。
*
* 返回原始返回数据。要转换为期望的返回值,请使用
* https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]。
*
* 要求:
*
* - `target` 必须是一个合约。
* - 使用 `data` 调用 `target` 不能回退。
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
if (address(this).balance < value) {
revert Errors.InsufficientBalance(address(this).balance, value);
}
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
* was not a contract or bubbling up the revert reason (falling back to {Errors.FailedCall}) in case
* of an unsuccessful call.
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata
) internal view returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
// only check if target is a contract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
if (returndata.length == 0 && target.code.length == 0) {
revert AddressEmptyCode(target);
}
return returndata;
}
}
/**
* @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
* revert reason or with a default {Errors.FailedCall} error.
*/
function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
return returndata;
}
}
/**
* @dev Reverts with returndata if present. Otherwise reverts with {Errors.FailedCall}.
*/
function _revert(bytes memory returndata) 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
assembly ("memory-safe") {
revert(add(returndata, 0x20), mload(returndata))
}
} else {
revert Errors.FailedCall();
}
}
}