draft-ERC6909.sol 8.0 KB

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