draft-ERC6909.sol 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. // SPDX-License-Identifier: MIT
  2. // OpenZeppelin Contracts (last updated v5.3.0-rc.0) (token/ERC6909/draft-ERC6909.sol)
  3. pragma solidity ^0.8.20;
  4. import {IERC6909} from "../../interfaces/draft-IERC6909.sol";
  5. import {Context} from "../../utils/Context.sol";
  6. import {IERC165, ERC165} from "../../utils/introspection/ERC165.sol";
  7. /**
  8. * @dev Implementation of ERC-6909.
  9. * See https://eips.ethereum.org/EIPS/eip-6909
  10. */
  11. contract ERC6909 is Context, ERC165, IERC6909 {
  12. mapping(address owner => mapping(uint256 id => uint256)) private _balances;
  13. mapping(address owner => mapping(address operator => bool)) private _operatorApprovals;
  14. mapping(address owner => mapping(address spender => mapping(uint256 id => uint256))) private _allowances;
  15. error ERC6909InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 id);
  16. error ERC6909InsufficientAllowance(address spender, uint256 allowance, uint256 needed, uint256 id);
  17. error ERC6909InvalidApprover(address approver);
  18. error ERC6909InvalidReceiver(address receiver);
  19. error ERC6909InvalidSender(address sender);
  20. error ERC6909InvalidSpender(address spender);
  21. /// @inheritdoc IERC165
  22. function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
  23. return interfaceId == type(IERC6909).interfaceId || super.supportsInterface(interfaceId);
  24. }
  25. /// @inheritdoc IERC6909
  26. function balanceOf(address owner, uint256 id) public view virtual override returns (uint256) {
  27. return _balances[owner][id];
  28. }
  29. /// @inheritdoc IERC6909
  30. function allowance(address owner, address spender, uint256 id) public view virtual override returns (uint256) {
  31. return _allowances[owner][spender][id];
  32. }
  33. /// @inheritdoc IERC6909
  34. function isOperator(address owner, address spender) public view virtual override returns (bool) {
  35. return _operatorApprovals[owner][spender];
  36. }
  37. /// @inheritdoc IERC6909
  38. function approve(address spender, uint256 id, uint256 amount) public virtual override returns (bool) {
  39. _approve(_msgSender(), spender, id, amount);
  40. return true;
  41. }
  42. /// @inheritdoc IERC6909
  43. function setOperator(address spender, bool approved) public virtual override returns (bool) {
  44. _setOperator(_msgSender(), spender, approved);
  45. return true;
  46. }
  47. /// @inheritdoc IERC6909
  48. function transfer(address receiver, uint256 id, uint256 amount) public virtual override returns (bool) {
  49. _transfer(_msgSender(), receiver, id, amount);
  50. return true;
  51. }
  52. /// @inheritdoc IERC6909
  53. function transferFrom(
  54. address sender,
  55. address receiver,
  56. uint256 id,
  57. uint256 amount
  58. ) public virtual override returns (bool) {
  59. address caller = _msgSender();
  60. if (sender != caller && !isOperator(sender, caller)) {
  61. _spendAllowance(sender, caller, id, amount);
  62. }
  63. _transfer(sender, receiver, id, amount);
  64. return true;
  65. }
  66. /**
  67. * @dev Creates `amount` of token `id` and assigns them to `account`, by transferring it from address(0).
  68. * Relies on the `_update` mechanism.
  69. *
  70. * Emits a {Transfer} event with `from` set to the zero address.
  71. *
  72. * NOTE: This function is not virtual, {_update} should be overridden instead.
  73. */
  74. function _mint(address to, uint256 id, uint256 amount) internal {
  75. if (to == address(0)) {
  76. revert ERC6909InvalidReceiver(address(0));
  77. }
  78. _update(address(0), to, id, amount);
  79. }
  80. /**
  81. * @dev Moves `amount` of token `id` from `from` to `to` without checking for approvals. This function verifies
  82. * that neither the sender nor the receiver are address(0), which means it cannot mint or burn tokens.
  83. * Relies on the `_update` mechanism.
  84. *
  85. * Emits a {Transfer} event.
  86. *
  87. * NOTE: This function is not virtual, {_update} should be overridden instead.
  88. */
  89. function _transfer(address from, address to, uint256 id, uint256 amount) internal {
  90. if (from == address(0)) {
  91. revert ERC6909InvalidSender(address(0));
  92. }
  93. if (to == address(0)) {
  94. revert ERC6909InvalidReceiver(address(0));
  95. }
  96. _update(from, to, id, amount);
  97. }
  98. /**
  99. * @dev Destroys a `amount` of token `id` from `account`.
  100. * Relies on the `_update` mechanism.
  101. *
  102. * Emits a {Transfer} event with `to` set to the zero address.
  103. *
  104. * NOTE: This function is not virtual, {_update} should be overridden instead
  105. */
  106. function _burn(address from, uint256 id, uint256 amount) internal {
  107. if (from == address(0)) {
  108. revert ERC6909InvalidSender(address(0));
  109. }
  110. _update(from, address(0), id, amount);
  111. }
  112. /**
  113. * @dev Transfers `amount` of token `id` from `from` to `to`, or alternatively mints (or burns) if `from`
  114. * (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding
  115. * this function.
  116. *
  117. * Emits a {Transfer} event.
  118. */
  119. function _update(address from, address to, uint256 id, uint256 amount) internal virtual {
  120. address caller = _msgSender();
  121. if (from != address(0)) {
  122. uint256 fromBalance = _balances[from][id];
  123. if (fromBalance < amount) {
  124. revert ERC6909InsufficientBalance(from, fromBalance, amount, id);
  125. }
  126. unchecked {
  127. // Overflow not possible: amount <= fromBalance.
  128. _balances[from][id] = fromBalance - amount;
  129. }
  130. }
  131. if (to != address(0)) {
  132. _balances[to][id] += amount;
  133. }
  134. emit Transfer(caller, from, to, id, amount);
  135. }
  136. /**
  137. * @dev Sets `amount` as the allowance of `spender` over the `owner`'s `id` tokens.
  138. *
  139. * This internal function is equivalent to `approve`, and can be used to e.g. set automatic allowances for certain
  140. * subsystems, etc.
  141. *
  142. * Emits an {Approval} event.
  143. *
  144. * Requirements:
  145. *
  146. * - `owner` cannot be the zero address.
  147. * - `spender` cannot be the zero address.
  148. */
  149. function _approve(address owner, address spender, uint256 id, uint256 amount) internal virtual {
  150. if (owner == address(0)) {
  151. revert ERC6909InvalidApprover(address(0));
  152. }
  153. if (spender == address(0)) {
  154. revert ERC6909InvalidSpender(address(0));
  155. }
  156. _allowances[owner][spender][id] = amount;
  157. emit Approval(owner, spender, id, amount);
  158. }
  159. /**
  160. * @dev Approve `spender` to operate on all of `owner`'s tokens
  161. *
  162. * This internal function is equivalent to `setOperator`, and can be used to e.g. set automatic allowances for
  163. * certain subsystems, etc.
  164. *
  165. * Emits an {OperatorSet} event.
  166. *
  167. * Requirements:
  168. *
  169. * - `owner` cannot be the zero address.
  170. * - `spender` cannot be the zero address.
  171. */
  172. function _setOperator(address owner, address spender, bool approved) internal virtual {
  173. if (owner == address(0)) {
  174. revert ERC6909InvalidApprover(address(0));
  175. }
  176. if (spender == address(0)) {
  177. revert ERC6909InvalidSpender(address(0));
  178. }
  179. _operatorApprovals[owner][spender] = approved;
  180. emit OperatorSet(owner, spender, approved);
  181. }
  182. /**
  183. * @dev Updates `owner`'s allowance for `spender` based on spent `amount`.
  184. *
  185. * Does not update the allowance value in case of infinite allowance.
  186. * Revert if not enough allowance is available.
  187. *
  188. * Does not emit an {Approval} event.
  189. */
  190. function _spendAllowance(address owner, address spender, uint256 id, uint256 amount) internal virtual {
  191. uint256 currentAllowance = allowance(owner, spender, id);
  192. if (currentAllowance < type(uint256).max) {
  193. if (currentAllowance < amount) {
  194. revert ERC6909InsufficientAllowance(spender, currentAllowance, amount, id);
  195. }
  196. unchecked {
  197. _allowances[owner][spender][id] = currentAllowance - amount;
  198. }
  199. }
  200. }
  201. }