// 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(); } } }