Skip to content

Commit 98c3a79

Browse files
Amxxfrangio
andauthored
Change execution order to avoid reentry through the _beforeTokenTransfer hook (#3611)
Co-authored-by: Francisco <frangio.1@gmail.com>
1 parent 17bc2da commit 98c3a79

File tree

2 files changed

+11
-1
lines changed

2 files changed

+11
-1
lines changed

CHANGELOG.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,12 @@
66
* `ERC165Checker`: add `supportsERC165InterfaceUnchecked` for consulting individual interfaces without the full ERC165 protocol. ([#3339](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3339))
77
* `Address`: optimize `functionCall` by calling `functionCallWithValue` directly. ([#3468](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3468))
88
* `Address`: optimize `functionCall` functions by checking contract size only if there is no returned data. ([#3469](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3469))
9-
* `GovernorCompatibilityBravo`: remove unused `using` statements ([#3506](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3506))
9+
* `GovernorCompatibilityBravo`: remove unused `using` statements. ([#3506](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3506))
1010
* `ERC20`: optimize `_transfer`, `_mint` and `_burn` by using `unchecked` arithmetic when possible. ([#3513](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3513))
1111
* `ERC20FlashMint`: add an internal `_flashFee` function for overriding. ([#3551](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3551))
1212
* `ERC721`: optimize transfers by making approval clearing implicit instead of emitting an event. ([#3481](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3481))
1313
* `ERC721`: optimize burn by making approval clearing implicit instead of emitting an event. ([#3538](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3538))
14+
* `ERC721`: Fix balance accounting when a custom `_beforeTokenTransfer` hook results in a transfer of the token under consideration. ([#3611](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3611))
1415
* `ReentrancyGuard`: Reduce code size impact of the modifier by using internal functions. ([#3515](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3515))
1516
* `SafeCast`: optimize downcasting of signed integers. ([#3565](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3565))
1617
* `VestingWallet`: remove unused library `Math.sol`. ([#3605](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3605))

contracts/token/ERC721/ERC721.sol

+9
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,9 @@ contract ERC721 is Context, ERC165, IERC721, IERC721Metadata {
282282

283283
_beforeTokenTransfer(address(0), to, tokenId);
284284

285+
// Check that tokenId was not minted by `_beforeTokenTransfer` hook
286+
require(!_exists(tokenId), "ERC721: token already minted");
287+
285288
_balances[to] += 1;
286289
_owners[tokenId] = to;
287290

@@ -306,6 +309,9 @@ contract ERC721 is Context, ERC165, IERC721, IERC721Metadata {
306309

307310
_beforeTokenTransfer(owner, address(0), tokenId);
308311

312+
// Update ownership in case tokenId was transfered by `_beforeTokenTransfer` hook
313+
owner = ERC721.ownerOf(tokenId);
314+
309315
// Clear approvals
310316
delete _tokenApprovals[tokenId];
311317

@@ -338,6 +344,9 @@ contract ERC721 is Context, ERC165, IERC721, IERC721Metadata {
338344

339345
_beforeTokenTransfer(from, to, tokenId);
340346

347+
// Check that tokenId was not transfered by `_beforeTokenTransfer` hook
348+
require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer from incorrect owner");
349+
341350
// Clear approvals from the previous owner
342351
delete _tokenApprovals[tokenId];
343352

0 commit comments

Comments
 (0)