Skip to content

Commit

Permalink
ERC1155 after token transfer hook (#3166)
Browse files Browse the repository at this point in the history
* 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>
  • Loading branch information
3 people committed Feb 14, 2022
1 parent be3c5ca commit 3dfc0a4
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 3 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -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)

Expand Down
53 changes: 50 additions & 3 deletions contracts/token/ERC1155/ERC1155.sol
Expand Up @@ -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");
Expand All @@ -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);
}

/**
Expand Down Expand Up @@ -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);
}

/**
Expand Down Expand Up @@ -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);
}

/**
Expand Down Expand Up @@ -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);
}

/**
Expand All @@ -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");
Expand All @@ -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, "");
}

/**
Expand Down Expand Up @@ -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, "");
}

/**
Expand Down Expand Up @@ -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,
Expand Down

0 comments on commit 3dfc0a4

Please sign in to comment.