Преглед изворни кода

ERC1155 after token transfer hook (#3166)

* add Hooks _afterTokenTransfer

* avoid duplicate call to _asSingleton

* add changelog entry

* update changelog link to PR

* Update CHANGELOG.md

Co-authored-by: Francisco Giordano <frangio.1@gmail.com>

Co-authored-by: CryptoV8 <91189073+CryptoV8@users.noreply.github.com>
Co-authored-by: Francisco Giordano <frangio.1@gmail.com>
Hadrien Croubois пре 3 година
родитељ
комит
3dfc0a44f8
2 измењених фајлова са 51 додато и 3 уклоњено
  1. 1 0
      CHANGELOG.md
  2. 50 3
      contracts/token/ERC1155/ERC1155.sol

+ 1 - 0
CHANGELOG.md

@@ -4,6 +4,7 @@
 
  * `AccessControl`: add a virtual `_checkRole(bytes32)` function that can be overriden to alter the `onlyRole` modifier behavior. ([#3137](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3137))
  * `EnumerableMap`: add new `AddressToUintMap` map type. ([#3150](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3150))
+ * `ERC1155`: Add a `_afterTokenTransfer` hook for improved extensibility. ([#3166](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3166))
 
 ## 4.5.0 (2022-02-09)
 

+ 50 - 3
contracts/token/ERC1155/ERC1155.sol

@@ -167,8 +167,10 @@ contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI {
         require(to != address(0), "ERC1155: transfer to the zero address");
 
         address operator = _msgSender();
+        uint256[] memory ids = _asSingletonArray(id);
+        uint256[] memory amounts = _asSingletonArray(amount);
 
-        _beforeTokenTransfer(operator, from, to, _asSingletonArray(id), _asSingletonArray(amount), data);
+        _beforeTokenTransfer(operator, from, to, ids, amounts, data);
 
         uint256 fromBalance = _balances[id][from];
         require(fromBalance >= amount, "ERC1155: insufficient balance for transfer");
@@ -180,6 +182,8 @@ contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI {
         emit TransferSingle(operator, from, to, id, amount);
 
         _doSafeTransferAcceptanceCheck(operator, from, to, id, amount, data);
+
+        _afterTokenTransfer(operator, from, to, ids, amounts, data);
     }
 
     /**
@@ -221,6 +225,8 @@ contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI {
         emit TransferBatch(operator, from, to, ids, amounts);
 
         _doSafeBatchTransferAcceptanceCheck(operator, from, to, ids, amounts, data);
+
+        _afterTokenTransfer(operator, from, to, ids, amounts, data);
     }
 
     /**
@@ -266,13 +272,17 @@ contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI {
         require(to != address(0), "ERC1155: mint to the zero address");
 
         address operator = _msgSender();
+        uint256[] memory ids = _asSingletonArray(id);
+        uint256[] memory amounts = _asSingletonArray(amount);
 
-        _beforeTokenTransfer(operator, address(0), to, _asSingletonArray(id), _asSingletonArray(amount), data);
+        _beforeTokenTransfer(operator, address(0), to, ids, amounts, data);
 
         _balances[id][to] += amount;
         emit TransferSingle(operator, address(0), to, id, amount);
 
         _doSafeTransferAcceptanceCheck(operator, address(0), to, id, amount, data);
+
+        _afterTokenTransfer(operator, address(0), to, ids, amounts, data);
     }
 
     /**
@@ -304,6 +314,8 @@ contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI {
         emit TransferBatch(operator, address(0), to, ids, amounts);
 
         _doSafeBatchTransferAcceptanceCheck(operator, address(0), to, ids, amounts, data);
+
+        _afterTokenTransfer(operator, address(0), to, ids, amounts, data);
     }
 
     /**
@@ -322,8 +334,10 @@ contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI {
         require(from != address(0), "ERC1155: burn from the zero address");
 
         address operator = _msgSender();
+        uint256[] memory ids = _asSingletonArray(id);
+        uint256[] memory amounts = _asSingletonArray(amount);
 
-        _beforeTokenTransfer(operator, from, address(0), _asSingletonArray(id), _asSingletonArray(amount), "");
+        _beforeTokenTransfer(operator, from, address(0), ids, amounts, "");
 
         uint256 fromBalance = _balances[id][from];
         require(fromBalance >= amount, "ERC1155: burn amount exceeds balance");
@@ -332,6 +346,8 @@ contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI {
         }
 
         emit TransferSingle(operator, from, address(0), id, amount);
+
+        _afterTokenTransfer(operator, from, address(0), ids, amounts, "");
     }
 
     /**
@@ -365,6 +381,8 @@ contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI {
         }
 
         emit TransferBatch(operator, from, address(0), ids, amounts);
+
+        _afterTokenTransfer(operator, from, address(0), ids, amounts, "");
     }
 
     /**
@@ -411,6 +429,35 @@ contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI {
         bytes memory data
     ) internal virtual {}
 
+    /**
+     * @dev Hook that is called after any token transfer. This includes minting
+     * and burning, as well as batched variants.
+     *
+     * The same hook is called on both single and batched variants. For single
+     * transfers, the length of the `id` and `amount` arrays will be 1.
+     *
+     * Calling conditions (for each `id` and `amount` pair):
+     *
+     * - When `from` and `to` are both non-zero, `amount` of ``from``'s tokens
+     * of token type `id` will be  transferred to `to`.
+     * - When `from` is zero, `amount` tokens of token type `id` will be minted
+     * for `to`.
+     * - when `to` is zero, `amount` of ``from``'s tokens of token type `id`
+     * will be burned.
+     * - `from` and `to` are never both zero.
+     * - `ids` and `amounts` have the same, non-zero length.
+     *
+     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
+     */
+    function _afterTokenTransfer(
+        address operator,
+        address from,
+        address to,
+        uint256[] memory ids,
+        uint256[] memory amounts,
+        bytes memory data
+    ) internal virtual {}
+
     function _doSafeTransferAcceptanceCheck(
         address operator,
         address from,