@@ -76,9 +76,8 @@ contract ERC721Enumerable is ERC165, ERC721, IERC721Enumerable {
76
76
*/
77
77
function _addTokenTo (address to , uint256 tokenId ) internal {
78
78
super ._addTokenTo (to, tokenId);
79
- uint256 length = _ownedTokens[to].length ;
80
- _ownedTokens[to].push (tokenId);
81
- _ownedTokensIndex[tokenId] = length;
79
+
80
+ _addTokenToOwnerEnumeration (to, tokenId);
82
81
}
83
82
84
83
/**
@@ -92,22 +91,26 @@ contract ERC721Enumerable is ERC165, ERC721, IERC721Enumerable {
92
91
function _removeTokenFrom (address from , uint256 tokenId ) internal {
93
92
super ._removeTokenFrom (from, tokenId);
94
93
95
- // To prevent a gap in the array, we store the last token in the index of the token to delete, and
96
- // then delete the last slot.
97
- uint256 tokenIndex = _ownedTokensIndex[tokenId];
98
- uint256 lastTokenIndex = _ownedTokens[from].length .sub (1 );
99
- uint256 lastToken = _ownedTokens[from][lastTokenIndex];
100
-
101
- _ownedTokens[from][tokenIndex] = lastToken;
102
- // This also deletes the contents at the last position of the array
103
- _ownedTokens[from].length -- ;
104
-
105
- // Note that this will handle single-element arrays. In that case, both tokenIndex and lastTokenIndex are going to
106
- // be zero. Then we can make sure that we will remove tokenId from the ownedTokens list since we are first swapping
107
- // the lastToken to the first position, and then dropping the element placed in the last position of the list
94
+ _removeTokenFromOwnerEnumeration (from, tokenId);
108
95
96
+ // Since the token is being destroyed, we also clear its index
97
+ // TODO(nventuro): 0 is still a valid index, so arguably this isnt really helpful, remove?
109
98
_ownedTokensIndex[tokenId] = 0 ;
110
- _ownedTokensIndex[lastToken] = tokenIndex;
99
+ }
100
+
101
+ /**
102
+ * @dev Internal function to transfer ownership of a given token ID to another address.
103
+ * As opposed to transferFrom, this imposes no restrictions on msg.sender.
104
+ * @param from current owner of the token
105
+ * @param to address to receive the ownership of the given token ID
106
+ * @param tokenId uint256 ID of the token to be transferred
107
+ */
108
+ function _transferFrom (address from , address to , uint256 tokenId ) internal {
109
+ super ._transferFrom (from, to, tokenId);
110
+
111
+ _removeTokenFromOwnerEnumeration (from, tokenId);
112
+
113
+ _addTokenToOwnerEnumeration (to, tokenId);
111
114
}
112
115
113
116
/**
@@ -144,7 +147,7 @@ contract ERC721Enumerable is ERC165, ERC721, IERC721Enumerable {
144
147
_allTokensIndex[tokenId] = 0 ;
145
148
_allTokensIndex[lastToken] = tokenIndex;
146
149
}
147
-
150
+
148
151
/**
149
152
* @dev Gets the list of token IDs of the requested owner
150
153
* @param owner address owning the tokens
@@ -153,4 +156,46 @@ contract ERC721Enumerable is ERC165, ERC721, IERC721Enumerable {
153
156
function _tokensOfOwner (address owner ) internal view returns (uint256 [] storage ) {
154
157
return _ownedTokens[owner];
155
158
}
159
+
160
+ /**
161
+ * @dev Private function to add a token to this extension's ownership-tracking data structures.
162
+ * @param to address representing the new owner of the given token ID
163
+ * @param tokenId uint256 ID of the token to be added to the tokens list of the given address
164
+ */
165
+ function _addTokenToOwnerEnumeration (address to , uint256 tokenId ) private {
166
+ uint256 newOwnedTokensLength = _ownedTokens[to].push (tokenId);
167
+ // No need to use SafeMath since the length after a push cannot be zero
168
+ _ownedTokensIndex[tokenId] = newOwnedTokensLength - 1 ;
169
+ }
170
+
171
+ /**
172
+ * @dev Private function to remove a token from this extension's ownership-tracking data structures. Note that
173
+ * while the token is not assigned a new owner, the _ownedTokensIndex mapping is _not_ updated: this allows for
174
+ * gas optimizations e.g. when performing a transfer operation (avoiding double writes).
175
+ * This has O(1) time complexity, but alters the order of the _ownedTokens array.
176
+ * @param from address representing the previous owner of the given token ID
177
+ * @param tokenId uint256 ID of the token to be removed from the tokens list of the given address
178
+ */
179
+ function _removeTokenFromOwnerEnumeration (address from , uint256 tokenId ) private {
180
+ // To prevent a gap in from's tokens array, we store the last token in the index of the token to delete, and
181
+ // then delete the last slot (swap and pop).
182
+
183
+ uint256 lastTokenIndex = _ownedTokens[from].length .sub (1 );
184
+ uint256 lastTokenId = _ownedTokens[from][lastTokenIndex];
185
+
186
+ uint256 tokenIndex = _ownedTokensIndex[tokenId];
187
+
188
+ _ownedTokens[from][tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token
189
+ _ownedTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index
190
+
191
+ // Note that this will handle single-element arrays. In that case, both tokenIndex and lastTokenIndex are going
192
+ // to be zero. The swap operation will therefore have no effect, but the token _will_ be deleted during the
193
+ // 'pop' operation.
194
+
195
+ // This also deletes the contents at the last position of the array
196
+ _ownedTokens[from].length -- ;
197
+
198
+ // Note that _ownedTokensIndex[tokenId] hasn't been cleared: it still points to the old slot (now occcupied by
199
+ // lasTokenId).
200
+ }
156
201
}
0 commit comments