SafeERC20.sol 3.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. // SPDX-License-Identifier: MIT
  2. pragma solidity ^0.8.0;
  3. import "../IERC20.sol";
  4. import "../../../utils/Address.sol";
  5. /**
  6. * @title SafeERC20
  7. * @dev Wrappers around ERC20 operations that throw on failure (when the token
  8. * contract returns false). Tokens that return no value (and instead revert or
  9. * throw on failure) are also supported, non-reverting calls are assumed to be
  10. * successful.
  11. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
  12. * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
  13. */
  14. library SafeERC20 {
  15. using Address for address;
  16. function safeTransfer(
  17. IERC20 token,
  18. address to,
  19. uint256 value
  20. ) internal {
  21. _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
  22. }
  23. function safeTransferFrom(
  24. IERC20 token,
  25. address from,
  26. address to,
  27. uint256 value
  28. ) internal {
  29. _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
  30. }
  31. /**
  32. * @dev Deprecated. This function has issues similar to the ones found in
  33. * {IERC20-approve}, and its usage is discouraged.
  34. *
  35. * Whenever possible, use {safeIncreaseAllowance} and
  36. * {safeDecreaseAllowance} instead.
  37. */
  38. function safeApprove(
  39. IERC20 token,
  40. address spender,
  41. uint256 value
  42. ) internal {
  43. // safeApprove should only be called when setting an initial allowance,
  44. // or when resetting it to zero. To increase and decrease it, use
  45. // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
  46. require(
  47. (value == 0) || (token.allowance(address(this), spender) == 0),
  48. "SafeERC20: approve from non-zero to non-zero allowance"
  49. );
  50. _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
  51. }
  52. function safeIncreaseAllowance(
  53. IERC20 token,
  54. address spender,
  55. uint256 value
  56. ) internal {
  57. uint256 newAllowance = token.allowance(address(this), spender) + value;
  58. _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
  59. }
  60. function safeDecreaseAllowance(
  61. IERC20 token,
  62. address spender,
  63. uint256 value
  64. ) internal {
  65. unchecked {
  66. uint256 oldAllowance = token.allowance(address(this), spender);
  67. require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
  68. uint256 newAllowance = oldAllowance - value;
  69. _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
  70. }
  71. }
  72. /**
  73. * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
  74. * on the return value: the return value is optional (but if data is returned, it must not be false).
  75. * @param token The token targeted by the call.
  76. * @param data The call data (encoded using abi.encode or one of its variants).
  77. */
  78. function _callOptionalReturn(IERC20 token, bytes memory data) private {
  79. // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
  80. // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
  81. // the target address contains contract code and also asserts for success in the low-level call.
  82. bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
  83. if (returndata.length > 0) {
  84. // Return data is optional
  85. require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
  86. }
  87. }
  88. }