ERC1363.sol 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. // SPDX-License-Identifier: MIT
  2. pragma solidity ^0.8.20;
  3. import {ERC20} from "../ERC20.sol";
  4. import {IERC165, ERC165} from "../../../utils/introspection/ERC165.sol";
  5. import {IERC1363} from "../../../interfaces/IERC1363.sol";
  6. import {IERC1363Receiver} from "../../../interfaces/IERC1363Receiver.sol";
  7. import {IERC1363Spender} from "../../../interfaces/IERC1363Spender.sol";
  8. /**
  9. * @title ERC1363
  10. * @dev Extension of {ERC20} tokens that adds support for code execution after transfers and approvals
  11. * on recipient contracts. Calls after transfers are enabled through the {ERC1363-transferAndCall} and
  12. * {ERC1363-transferFromAndCall} methods while calls after approvals can be made with {ERC1363-approveAndCall}
  13. */
  14. abstract contract ERC1363 is ERC20, ERC165, IERC1363 {
  15. /**
  16. * @dev Indicates a failure with the token `receiver`. Used in transfers.
  17. * @param receiver Address to which tokens are being transferred.
  18. */
  19. error ERC1363InvalidReceiver(address receiver);
  20. /**
  21. * @dev Indicates a failure with the token `spender`. Used in approvals.
  22. * @param spender Address that may be allowed to operate on tokens without being their owner.
  23. */
  24. error ERC1363InvalidSpender(address spender);
  25. /**
  26. * @dev Indicates a failure within the {transfer} part of a transferAndCall operation.
  27. */
  28. error ERC1363TransferFailed(address to, uint256 value);
  29. /**
  30. * @dev Indicates a failure within the {transferFrom} part of a transferFromAndCall operation.
  31. */
  32. error ERC1363TransferFromFailed(address from, address to, uint256 value);
  33. /**
  34. * @dev Indicates a failure within the {approve} part of a approveAndCall operation.
  35. */
  36. error ERC1363ApproveFailed(address spender, uint256 value);
  37. /**
  38. * @inheritdoc IERC165
  39. */
  40. function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
  41. return interfaceId == type(IERC1363).interfaceId || super.supportsInterface(interfaceId);
  42. }
  43. /**
  44. * @dev Moves a `value` amount of tokens from the caller's account to `to`
  45. * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
  46. *
  47. * Requirements:
  48. *
  49. * - The target has code (i.e. is a contract).
  50. * - The target `to` must implement the {IERC1363Receiver} interface.
  51. * - The target should return the {IERC1363Receiver} interface id.
  52. * - The internal {transfer} must succeed (returned `true`).
  53. */
  54. function transferAndCall(address to, uint256 value) public returns (bool) {
  55. return transferAndCall(to, value, "");
  56. }
  57. /**
  58. * @dev Variant of {transferAndCall} that accepts an additional `data` parameter with
  59. * no specified format.
  60. */
  61. function transferAndCall(address to, uint256 value, bytes memory data) public virtual returns (bool) {
  62. if (!transfer(to, value)) {
  63. revert ERC1363TransferFailed(to, value);
  64. }
  65. _checkOnTransferReceived(_msgSender(), to, value, data);
  66. return true;
  67. }
  68. /**
  69. * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
  70. * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
  71. *
  72. * Requirements:
  73. *
  74. * - The target has code (i.e. is a contract).
  75. * - The target `to` must implement the {IERC1363Receiver} interface.
  76. * - The target should return the {IERC1363Receiver} interface id.
  77. * - The internal {transferFrom} must succeed (returned `true`).
  78. */
  79. function transferFromAndCall(address from, address to, uint256 value) public returns (bool) {
  80. return transferFromAndCall(from, to, value, "");
  81. }
  82. /**
  83. * @dev Variant of {transferFromAndCall} that accepts an additional `data` parameter with
  84. * no specified format.
  85. */
  86. function transferFromAndCall(
  87. address from,
  88. address to,
  89. uint256 value,
  90. bytes memory data
  91. ) public virtual returns (bool) {
  92. if (!transferFrom(from, to, value)) {
  93. revert ERC1363TransferFromFailed(from, to, value);
  94. }
  95. _checkOnTransferReceived(from, to, value, data);
  96. return true;
  97. }
  98. /**
  99. * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
  100. * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
  101. *
  102. * Requirements:
  103. *
  104. * - The target has code (i.e. is a contract).
  105. * - The target `to` must implement the {IERC1363Spender} interface.
  106. * - The target should return the {IERC1363Spender} interface id.
  107. * - The internal {approve} must succeed (returned `true`).
  108. */
  109. function approveAndCall(address spender, uint256 value) public returns (bool) {
  110. return approveAndCall(spender, value, "");
  111. }
  112. /**
  113. * @dev Variant of {approveAndCall} that accepts an additional `data` parameter with
  114. * no specified format.
  115. */
  116. function approveAndCall(address spender, uint256 value, bytes memory data) public virtual returns (bool) {
  117. if (!approve(spender, value)) {
  118. revert ERC1363ApproveFailed(spender, value);
  119. }
  120. _checkOnApprovalReceived(spender, value, data);
  121. return true;
  122. }
  123. /**
  124. * @dev Performs a call to {IERC1363Receiver-onTransferReceived} on a target address.
  125. *
  126. * Requirements:
  127. *
  128. * - The target has code (i.e. is a contract).
  129. * - The target `to` must implement the {IERC1363Receiver} interface.
  130. * - The target should return the {IERC1363Receiver} interface id.
  131. */
  132. function _checkOnTransferReceived(address from, address to, uint256 value, bytes memory data) private {
  133. if (to.code.length == 0) {
  134. revert ERC1363InvalidReceiver(to);
  135. }
  136. try IERC1363Receiver(to).onTransferReceived(_msgSender(), from, value, data) returns (bytes4 retval) {
  137. if (retval != IERC1363Receiver.onTransferReceived.selector) {
  138. revert ERC1363InvalidReceiver(to);
  139. }
  140. } catch (bytes memory reason) {
  141. if (reason.length == 0) {
  142. revert ERC1363InvalidReceiver(to);
  143. } else {
  144. /// @solidity memory-safe-assembly
  145. assembly {
  146. revert(add(32, reason), mload(reason))
  147. }
  148. }
  149. }
  150. }
  151. /**
  152. * @dev Performs a call to {IERC1363Spender-onApprovalReceived} on a target address.
  153. *
  154. * Requirements:
  155. *
  156. * - The target has code (i.e. is a contract).
  157. * - The target `to` must implement the {IERC1363Spender} interface.
  158. * - The target should return the {IERC1363Spender} interface id.
  159. */
  160. function _checkOnApprovalReceived(address spender, uint256 value, bytes memory data) private {
  161. if (spender.code.length == 0) {
  162. revert ERC1363InvalidSpender(spender);
  163. }
  164. try IERC1363Spender(spender).onApprovalReceived(_msgSender(), value, data) returns (bytes4 retval) {
  165. if (retval != IERC1363Spender.onApprovalReceived.selector) {
  166. revert ERC1363InvalidSpender(spender);
  167. }
  168. } catch (bytes memory reason) {
  169. if (reason.length == 0) {
  170. revert ERC1363InvalidSpender(spender);
  171. } else {
  172. /// @solidity memory-safe-assembly
  173. assembly {
  174. revert(add(32, reason), mload(reason))
  175. }
  176. }
  177. }
  178. }
  179. }