From 280029732d0fcc2689bbd1b2dd3611ae6f074941 Mon Sep 17 00:00:00 2001 From: Philip Paetz Date: Mon, 7 Oct 2024 18:36:04 +0200 Subject: [PATCH] feat: simplified SafeERC20 Forked OZ's 4.8.3 version and removed unused functions: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.8.3/contracts/token/ERC20/utils/SafeERC20.sol --- contracts/common/SafeERC20.sol | 54 ++++++---------------------------- 1 file changed, 9 insertions(+), 45 deletions(-) diff --git a/contracts/common/SafeERC20.sol b/contracts/common/SafeERC20.sol index 2286277..f53a39e 100644 --- a/contracts/common/SafeERC20.sol +++ b/contracts/common/SafeERC20.sol @@ -2,55 +2,29 @@ pragma solidity ^0.8.0; import { IERC20MintableBurnable as IERC20 } from "contracts/common/IERC20MintableBurnable.sol"; import { Address } from "openzeppelin-contracts-next/contracts/utils/Address.sol"; -//TODO: merge with lattest impl of openzeppelin + /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. - * To use this library you can add a `using SafeERC20 for ERC20;` statement to your contract, + * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { using Address for address; - function safeTransfer(IERC20 token, address to, uint256 value) internal { - callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); - } - function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { - callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); + _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); } function safeMint(IERC20 token, address account, uint256 amount) internal { - callOptionalReturn(token, abi.encodeWithSelector(token.mint.selector, account, amount)); + _callOptionalReturn(token, abi.encodeWithSelector(token.mint.selector, account, amount)); } function safeBurn(IERC20 token, uint256 amount) internal { - callOptionalReturn(token, abi.encodeWithSelector(token.burn.selector, amount)); - } - - function safeApprove(IERC20 token, address spender, uint256 value) internal { - // safeApprove should only be called when setting an initial allowance, - // or when resetting it to zero. To increase and decrease it, use - // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' - // solhint-disable-next-line max-line-length - require( - (value == 0) || (token.allowance(address(this), spender) == 0), - "SafeERC20: approve from non-zero to non-zero allowance" - ); - callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); - } - - function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { - uint256 newAllowance = token.allowance(address(this), spender) + value; - callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); - } - - function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal { - uint256 newAllowance = token.allowance(address(this), spender) - value; - callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); + _callOptionalReturn(token, abi.encodeWithSelector(token.burn.selector, amount)); } /** @@ -59,24 +33,14 @@ library SafeERC20 { * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ - function callOptionalReturn(IERC20 token, bytes memory data) private { + function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since - // we're implementing it ourselves. - - // A Solidity high level call has three parts: - // 1. The target address is checked to verify it contains contract code - // 2. The call itself is made, and success asserted - // 3. The return value is decoded, which in turn checks the size of the returned data. - // solhint-disable-next-line max-line-length - require(address(token).isContract(), "SafeERC20: call to non-contract"); - - // solhint-disable-next-line avoid-low-level-calls - (bool success, bytes memory returndata) = address(token).call(data); - require(success, "SafeERC20: low-level call failed"); + // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that + // the target address contains contract code and also asserts for success in the low-level call. + bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); if (returndata.length > 0) { // Return data is optional - // solhint-disable-next-line max-line-length require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } }