├── batchedNiceToken.sol ├── LICENSE ├── README.md ├── ownedRanges.sol └── erc2309.sol /batchedNiceToken.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.6.2 <0.8.0; 2 | 3 | import "./ERC2309.sol"; 4 | 5 | contract BatchedNiceToken is ERC2309 { 6 | constructor () ERC2309("MyNFT", "MNFT") public { 7 | _init(msg.sender, 10000); 8 | } 9 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Göran Sandström 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ERC2309 2 | Experimental WARNING! ERC2309 prototype! Do not use in production! 3 | 4 | I take NO RESPONSIBILITY whatsover for lost funds of any kind if you use this code, this is ONLY meant as a demonstration of the carbon emission reduction possibilities of ERC2309 5 | 6 | # Notes on implementation 7 | 8 | Batch minting happens in init function for now, but could be a call that can add more NFTs in a batch, with a new owner of those NFTs, so you could have 9 | 10 | Alice mints 10k on init 11 | 12 | Bob mints 5k 13 | 14 | etc 15 | 16 | 17 | And those would own the ranges, 18 | 19 | alice: 0-9999 20 | 21 | bob: 10000-14999 22 | 23 | 24 | Ranges get broken up as NFTs are sold / transferred: 25 | 26 | Charlie buys 500 from alice, now: 27 | 28 | 29 | alice: 0-499, 501-9999, 30 | 31 | charlie: 500-500, 32 | 33 | bob: 10000-14999 34 | 35 | 36 | 37 | As ranges are broken up to a certain degree, it becomes slow to lookup owner of a certain NFT by looping over the array, so you can set a saturation point for optimistic lookup. 38 | Further improvement would be too lookup and search around that region if a hit isn't found on optimistic lookup. 39 | -------------------------------------------------------------------------------- /ownedRanges.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.6.2 <0.8.0; 2 | 3 | import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.4.0/contracts/math/SafeMath.sol"; 4 | import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.4.0/contracts/utils/Address.sol"; 5 | import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.4.0/contracts/utils/EnumerableSet.sol"; 6 | import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.4.0/contracts/utils/EnumerableMap.sol"; 7 | 8 | library OwnedRanges { 9 | using SafeMath for uint256; 10 | using Address for address; 11 | using EnumerableSet for EnumerableSet.UintSet; 12 | using EnumerableMap for EnumerableMap.UintToAddressMap; 13 | struct OwnedRangesMapping { 14 | mapping (address => EnumerableSet.UintSet) _rangeStartHolders; 15 | mapping (uint256 => uint256) _rangeStartsToEnds; 16 | EnumerableMap.UintToAddressMap _rangeStartReverseMapping; 17 | uint256 maxIndex; 18 | bool initialized; 19 | uint256 saturationPoint; 20 | } 21 | 22 | function init(OwnedRangesMapping storage rmap, address owner, uint256 number_of_items, uint256 saturationPoint) internal returns (bool) { 23 | require(!rmap.initialized); 24 | rmap._rangeStartHolders[owner].add(0); 25 | rmap._rangeStartsToEnds[0] = number_of_items; 26 | rmap._rangeStartReverseMapping.set(0, owner); 27 | rmap.maxIndex = number_of_items; 28 | rmap.initialized = true; 29 | rmap.saturationPoint = saturationPoint; 30 | return true; 31 | } 32 | 33 | function ownerOf(OwnedRangesMapping storage rmap, uint256 idx) internal view returns (address) { 34 | uint256 revMapLen = rmap._rangeStartReverseMapping.length(); 35 | if (revMapLen > rmap.saturationPoint) { 36 | (bool hit, address owner) = rmap._rangeStartReverseMapping.tryGet(idx); 37 | if(hit) { 38 | return owner; 39 | } 40 | } 41 | uint256 currentEnd = rmap._rangeStartsToEnds[0]; 42 | if (idx < currentEnd) { 43 | return rmap._rangeStartReverseMapping.get(0); 44 | } 45 | for (uint i = 0; i < revMapLen-1; i++) { 46 | uint256 currentStart = currentEnd; 47 | currentEnd = rmap._rangeStartsToEnds[currentStart]; 48 | if (idx < currentEnd) { 49 | return rmap._rangeStartReverseMapping.get(currentStart); 50 | } 51 | } 52 | return address(0); 53 | } 54 | 55 | function _setOwner(OwnedRangesMapping storage rmap, uint256 idx, uint256 rangeStart, uint256 rangeEnd, address currentOwner, address newOwner) internal returns (bool) { 56 | 57 | uint256 nextRangeStart = idx+1; 58 | ///Handle when range len == 1 59 | if (nextRangeStart == rangeEnd) { 60 | //No range, simple transfer 61 | rmap._rangeStartHolders[currentOwner].remove(idx); 62 | rmap._rangeStartHolders[newOwner].add(idx); 63 | rmap._rangeStartReverseMapping.set(idx, newOwner); 64 | return true; 65 | } 66 | 67 | //Handle when range len > 1 68 | 69 | //We split the map into three, (start, n-idx), (idx) (idx+1, end) 70 | 71 | rmap._rangeStartsToEnds[rangeStart] = idx; 72 | rmap._rangeStartsToEnds[idx] = nextRangeStart; 73 | rmap._rangeStartsToEnds[nextRangeStart] = rangeEnd; 74 | 75 | rmap._rangeStartReverseMapping.set(idx, newOwner); 76 | rmap._rangeStartReverseMapping.set(nextRangeStart, currentOwner); 77 | 78 | rmap._rangeStartHolders[currentOwner].add(nextRangeStart); 79 | rmap._rangeStartHolders[newOwner].add(idx); 80 | return true; 81 | } 82 | 83 | function setOwner(OwnedRangesMapping storage rmap, uint256 idx, address newOwner) internal returns (bool) { 84 | uint256 revMapLen = rmap._rangeStartReverseMapping.length(); 85 | if (revMapLen > rmap.saturationPoint) { 86 | (bool hit, address owner) = rmap._rangeStartReverseMapping.tryGet(idx); 87 | if(hit) { 88 | uint256 currentEnd = rmap._rangeStartsToEnds[idx]; 89 | return _setOwner(rmap, idx, idx, currentEnd, owner, newOwner); 90 | } 91 | } 92 | address currentAddr = rmap._rangeStartReverseMapping.get(0); 93 | uint256 currentEnd = rmap._rangeStartsToEnds[0]; 94 | if (idx < currentEnd) { 95 | return _setOwner(rmap, idx, 0, currentEnd, currentAddr, newOwner); 96 | } 97 | for (uint i = 0; i < revMapLen-1; i++) { 98 | uint256 currentStart = currentEnd; 99 | currentEnd = rmap._rangeStartsToEnds[currentStart]; 100 | if (idx < currentEnd) { 101 | return _setOwner(rmap, idx, currentStart, currentEnd, rmap._rangeStartReverseMapping.get(currentStart), newOwner); 102 | } 103 | } 104 | return false; 105 | } 106 | 107 | function ownedIndexToIdx(OwnedRangesMapping storage rmap, address owner, uint256 ownedIndex) internal view returns (uint256) { 108 | uint256 currentStart = rmap._rangeStartHolders[owner].at(0); 109 | uint256 currentEnd = rmap._rangeStartsToEnds[currentStart]; 110 | if (ownedIndex < (currentEnd - currentStart)) { 111 | return currentStart + ownedIndex; 112 | } 113 | ownedIndex -= currentEnd - currentStart; 114 | if (ownedIndex < 1) { 115 | require(false); 116 | } 117 | for (uint i = 1; i < rmap._rangeStartHolders[owner].length(); i++) { 118 | currentStart = rmap._rangeStartHolders[owner].at(i); 119 | currentEnd = rmap._rangeStartsToEnds[currentStart]; 120 | if (ownedIndex < (currentEnd - currentStart)) { 121 | return currentStart + ownedIndex; 122 | } 123 | ownedIndex -= currentEnd - currentStart; 124 | if (ownedIndex < 1) { 125 | require(false); 126 | } 127 | } 128 | require(false); 129 | return 0; 130 | } 131 | 132 | function ownerBalance(OwnedRangesMapping storage rmap, address owner) internal view returns (uint256) { 133 | uint256 balance = 0; 134 | for (uint i = 0; i < rmap._rangeStartHolders[owner].length(); i++) { 135 | uint256 currentStart = rmap._rangeStartHolders[owner].at(i); 136 | uint256 currentEnd = rmap._rangeStartsToEnds[currentStart]; 137 | balance += (currentEnd - currentStart); 138 | } 139 | return balance; 140 | } 141 | 142 | function length(OwnedRangesMapping storage rmap) internal view returns (uint256) { 143 | return rmap.maxIndex; 144 | } 145 | } -------------------------------------------------------------------------------- /erc2309.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.6.2 <0.8.0; 2 | 3 | import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.4.0/contracts/token/ERC721/ERC721.sol"; 4 | import "./ownedRanges.sol"; 5 | 6 | interface IERC2309 { 7 | 8 | event ConsecutiveTransfer(uint256 indexed fromTokenId, uint256 toTokenId, address indexed fromAddress, address indexed toAddress); 9 | } 10 | 11 | /** 12 | * @title ERC721 Non-Fungible Token Standard basic implementation 13 | * @dev see https://eips.ethereum.org/EIPS/eip-721 14 | */ 15 | contract ERC2309 is Context, ERC165, IERC721, IERC721Metadata, IERC721Enumerable, IERC2309 { 16 | using SafeMath for uint256; 17 | using Address for address; 18 | using EnumerableSet for EnumerableSet.UintSet; 19 | using EnumerableMap for EnumerableMap.UintToAddressMap; 20 | using Strings for uint256; 21 | using OwnedRanges for OwnedRanges.OwnedRangesMapping; 22 | 23 | // Equals to `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))` 24 | // which can be also obtained as `IERC721Receiver(0).onERC721Received.selector` 25 | bytes4 private constant _ERC721_RECEIVED = 0x150b7a02; 26 | 27 | OwnedRanges.OwnedRangesMapping private owners; 28 | 29 | 30 | // Mapping from token ID to approved address 31 | mapping (uint256 => address) private _tokenApprovals; 32 | 33 | // Mapping from owner to operator approvals 34 | mapping (address => mapping (address => bool)) private _operatorApprovals; 35 | 36 | // Token name 37 | string private _name; 38 | 39 | // Token symbol 40 | string private _symbol; 41 | 42 | // Optional mapping for token URIs 43 | mapping (uint256 => string) private _tokenURIs; 44 | 45 | // Base URI 46 | string private _baseURI; 47 | 48 | /* 49 | * bytes4(keccak256('balanceOf(address)')) == 0x70a08231 50 | * bytes4(keccak256('ownerOf(uint256)')) == 0x6352211e 51 | * bytes4(keccak256('approve(address,uint256)')) == 0x095ea7b3 52 | * bytes4(keccak256('getApproved(uint256)')) == 0x081812fc 53 | * bytes4(keccak256('setApprovalForAll(address,bool)')) == 0xa22cb465 54 | * bytes4(keccak256('isApprovedForAll(address,address)')) == 0xe985e9c5 55 | * bytes4(keccak256('transferFrom(address,address,uint256)')) == 0x23b872dd 56 | * bytes4(keccak256('safeTransferFrom(address,address,uint256)')) == 0x42842e0e 57 | * bytes4(keccak256('safeTransferFrom(address,address,uint256,bytes)')) == 0xb88d4fde 58 | * 59 | * => 0x70a08231 ^ 0x6352211e ^ 0x095ea7b3 ^ 0x081812fc ^ 60 | * 0xa22cb465 ^ 0xe985e9c5 ^ 0x23b872dd ^ 0x42842e0e ^ 0xb88d4fde == 0x80ac58cd 61 | */ 62 | bytes4 private constant _INTERFACE_ID_ERC721 = 0x80ac58cd; 63 | 64 | /* 65 | * bytes4(keccak256('name()')) == 0x06fdde03 66 | * bytes4(keccak256('symbol()')) == 0x95d89b41 67 | * bytes4(keccak256('tokenURI(uint256)')) == 0xc87b56dd 68 | * 69 | * => 0x06fdde03 ^ 0x95d89b41 ^ 0xc87b56dd == 0x5b5e139f 70 | */ 71 | bytes4 private constant _INTERFACE_ID_ERC721_METADATA = 0x5b5e139f; 72 | 73 | /* 74 | * bytes4(keccak256('totalSupply()')) == 0x18160ddd 75 | * bytes4(keccak256('tokenOfOwnerByIndex(address,uint256)')) == 0x2f745c59 76 | * bytes4(keccak256('tokenByIndex(uint256)')) == 0x4f6ccce7 77 | * 78 | * => 0x18160ddd ^ 0x2f745c59 ^ 0x4f6ccce7 == 0x780e9d63 79 | */ 80 | bytes4 private constant _INTERFACE_ID_ERC721_ENUMERABLE = 0x780e9d63; 81 | 82 | /** 83 | * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection. 84 | */ 85 | constructor (string memory name_, string memory symbol_) public { 86 | _name = name_; 87 | _symbol = symbol_; 88 | 89 | // register the supported interfaces to conform to ERC721 via ERC165 90 | _registerInterface(_INTERFACE_ID_ERC721); 91 | _registerInterface(_INTERFACE_ID_ERC721_METADATA); 92 | _registerInterface(_INTERFACE_ID_ERC721_ENUMERABLE); 93 | } 94 | 95 | /** 96 | * @dev See {IERC721-balanceOf}. 97 | */ 98 | function balanceOf(address owner) public view virtual override returns (uint256) { 99 | require(owner != address(0), "ERC721: balance query for the zero address"); 100 | return owners.ownerBalance(owner); 101 | } 102 | 103 | /** 104 | * @dev See {IERC721-ownerOf}. 105 | */ 106 | function ownerOf(uint256 tokenId) public view virtual override returns (address) { 107 | //(bool success, bytes32 value) = _tokenOwners.tryGet(tokenId) 108 | //return _tokenOwners.get(tokenId, "ERC721: owner query for nonexistent token"); 109 | return owners.ownerOf(tokenId); 110 | } 111 | 112 | /** 113 | * @dev See {IERC721Metadata-name}. 114 | */ 115 | function name() public view virtual override returns (string memory) { 116 | return _name; 117 | } 118 | 119 | /** 120 | * @dev See {IERC721Metadata-symbol}. 121 | */ 122 | function symbol() public view virtual override returns (string memory) { 123 | return _symbol; 124 | } 125 | 126 | /** 127 | * @dev See {IERC721Metadata-tokenURI}. 128 | */ 129 | function tokenURI(uint256 tokenId) public view virtual override returns (string memory) { 130 | require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token"); 131 | 132 | string memory _tokenURI = _tokenURIs[tokenId]; 133 | string memory base = baseURI(); 134 | 135 | // If there is no base URI, return the token URI. 136 | if (bytes(base).length == 0) { 137 | return _tokenURI; 138 | } 139 | // If both are set, concatenate the baseURI and tokenURI (via abi.encodePacked). 140 | if (bytes(_tokenURI).length > 0) { 141 | return string(abi.encodePacked(base, _tokenURI)); 142 | } 143 | // If there is a baseURI but no tokenURI, concatenate the tokenID to the baseURI. 144 | return string(abi.encodePacked(base, tokenId.toString())); 145 | } 146 | 147 | /** 148 | * @dev Returns the base URI set via {_setBaseURI}. This will be 149 | * automatically added as a prefix in {tokenURI} to each token's URI, or 150 | * to the token ID if no specific URI is set for that token ID. 151 | */ 152 | function baseURI() public view virtual returns (string memory) { 153 | return _baseURI; 154 | } 155 | 156 | /** 157 | * @dev See {IERC721Enumerable-tokenOfOwnerByIndex}. 158 | */ 159 | function tokenOfOwnerByIndex(address owner, uint256 index) public view virtual override returns (uint256) { 160 | return owners.ownedIndexToIdx(owner, index); 161 | } 162 | 163 | /** 164 | * @dev See {IERC721Enumerable-totalSupply}. 165 | */ 166 | function totalSupply() public view virtual override returns (uint256) { 167 | // _tokenOwners are indexed by tokenIds, so .length() returns the number of tokenIds 168 | return owners.length(); 169 | } 170 | 171 | /** 172 | * @dev See {IERC721Enumerable-tokenByIndex}. 173 | */ 174 | function tokenByIndex(uint256 index) public view virtual override returns (uint256) { 175 | return index; 176 | } 177 | 178 | /** 179 | * @dev See {IERC721-approve}. 180 | */ 181 | function approve(address to, uint256 tokenId) public virtual override { 182 | address owner = ERC2309.ownerOf(tokenId); 183 | require(to != owner, "ERC721: approval to current owner"); 184 | 185 | require(_msgSender() == owner || ERC2309.isApprovedForAll(owner, _msgSender()), 186 | "ERC721: approve caller is not owner nor approved for all" 187 | ); 188 | 189 | _approve(to, tokenId); 190 | } 191 | 192 | /** 193 | * @dev See {IERC721-getApproved}. 194 | */ 195 | function getApproved(uint256 tokenId) public view virtual override returns (address) { 196 | require(_exists(tokenId), "ERC721: approved query for nonexistent token"); 197 | 198 | return _tokenApprovals[tokenId]; 199 | } 200 | 201 | /** 202 | * @dev See {IERC721-setApprovalForAll}. 203 | */ 204 | function setApprovalForAll(address operator, bool approved) public virtual override { 205 | require(operator != _msgSender(), "ERC721: approve to caller"); 206 | 207 | _operatorApprovals[_msgSender()][operator] = approved; 208 | emit ApprovalForAll(_msgSender(), operator, approved); 209 | } 210 | 211 | /** 212 | * @dev See {IERC721-isApprovedForAll}. 213 | */ 214 | function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) { 215 | return _operatorApprovals[owner][operator]; 216 | } 217 | 218 | /** 219 | * @dev See {IERC721-transferFrom}. 220 | */ 221 | function transferFrom(address from, address to, uint256 tokenId) public virtual override { 222 | //solhint-disable-next-line max-line-length 223 | require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved"); 224 | 225 | _transfer(from, to, tokenId); 226 | } 227 | 228 | /** 229 | * @dev See {IERC721-safeTransferFrom}. 230 | */ 231 | function safeTransferFrom(address from, address to, uint256 tokenId) public virtual override { 232 | safeTransferFrom(from, to, tokenId, ""); 233 | } 234 | 235 | /** 236 | * @dev See {IERC721-safeTransferFrom}. 237 | */ 238 | function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory _data) public virtual override { 239 | require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved"); 240 | _safeTransfer(from, to, tokenId, _data); 241 | } 242 | 243 | /** 244 | * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients 245 | * are aware of the ERC721 protocol to prevent tokens from being forever locked. 246 | * 247 | * `_data` is additional data, it has no specified format and it is sent in call to `to`. 248 | * 249 | * This internal function is equivalent to {safeTransferFrom}, and can be used to e.g. 250 | * implement alternative mechanisms to perform token transfer, such as signature-based. 251 | * 252 | * Requirements: 253 | * 254 | * - `from` cannot be the zero address. 255 | * - `to` cannot be the zero address. 256 | * - `tokenId` token must exist and be owned by `from`. 257 | * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. 258 | * 259 | * Emits a {Transfer} event. 260 | */ 261 | function _safeTransfer(address from, address to, uint256 tokenId, bytes memory _data) internal virtual { 262 | _transfer(from, to, tokenId); 263 | require(_checkOnERC721Received(from, to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer"); 264 | } 265 | 266 | /** 267 | * @dev Returns whether `tokenId` exists. 268 | * 269 | * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}. 270 | * 271 | * Tokens start existing when they are minted (`_mint`), 272 | * and stop existing when they are burned (`_burn`). 273 | */ 274 | function _exists(uint256 tokenId) internal view virtual returns (bool) { 275 | return owners.length() > tokenId; 276 | } 277 | 278 | /** 279 | * @dev Returns whether `spender` is allowed to manage `tokenId`. 280 | * 281 | * Requirements: 282 | * 283 | * - `tokenId` must exist. 284 | */ 285 | function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) { 286 | require(_exists(tokenId), "ERC721: operator query for nonexistent token"); 287 | address owner = ERC2309.ownerOf(tokenId); 288 | return (spender == owner || getApproved(tokenId) == spender || ERC2309.isApprovedForAll(owner, spender)); 289 | } 290 | 291 | /** 292 | * @dev Safely mints `tokenId` and transfers it to `to`. 293 | * 294 | * Requirements: 295 | d* 296 | * - `tokenId` must not exist. 297 | * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. 298 | * 299 | * Emits a {Transfer} event. 300 | */ 301 | /** 302 | function _safeMint(address to, uint256 tokenId) internal virtual { 303 | _safeMint(to, tokenId, ""); 304 | }**/ 305 | 306 | /** 307 | * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is 308 | * forwarded in {IERC721Receiver-onERC721Received} to contract recipients. 309 | */ 310 | 311 | /** 312 | function _safeMint(address to, uint256 tokenId, bytes memory _data) internal virtual { 313 | _mint(to, tokenId); 314 | require(_checkOnERC721Received(address(0), to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer"); 315 | }**/ 316 | 317 | /** 318 | * @dev Mints `tokenId` and transfers it to `to`. 319 | * 320 | * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible 321 | * 322 | * Requirements: 323 | * 324 | * - `tokenId` must not exist. 325 | * - `to` cannot be the zero address. 326 | * 327 | * Emits a {Transfer} event. 328 | */ 329 | 330 | /** 331 | function _mint(address to, uint256 tokenId) internal virtual { 332 | require(to != address(0), "ERC721: mint to the zero address"); 333 | require(!_exists(tokenId), "ERC721: token already minted"); 334 | 335 | _beforeTokenTransfer(address(0), to, tokenId); 336 | 337 | _holderTokens[to].add(tokenId); 338 | 339 | _tokenOwners.set(tokenId, to); 340 | 341 | emit Transfer(address(0), to, tokenId); 342 | } 343 | **/ 344 | 345 | /** 346 | * @dev Destroys `tokenId`. 347 | * The approval is cleared when the token is burned. 348 | * 349 | * Requirements: 350 | * 351 | * - `tokenId` must exist. 352 | * 353 | * Emits a {Transfer} event. 354 | */ 355 | 356 | /** 357 | function _burn(uint256 tokenId) internal virtual { 358 | address owner = ERC721.ownerOf(tokenId); // internal owner 359 | 360 | _beforeTokenTransfer(owner, address(0), tokenId); 361 | 362 | // Clear approvals 363 | _approve(address(0), tokenId); 364 | 365 | // Clear metadata (if any) 366 | if (bytes(_tokenURIs[tokenId]).length != 0) { 367 | delete _tokenURIs[tokenId]; 368 | } 369 | 370 | _holderTokens[owner].remove(tokenId); 371 | 372 | _tokenOwners.remove(tokenId); 373 | 374 | emit Transfer(owner, address(0), tokenId); 375 | } 376 | **/ 377 | 378 | /** 379 | * @dev Transfers `tokenId` from `from` to `to`. 380 | * As opposed to {transferFrom}, this imposes no restrictions on msg.sender. 381 | * 382 | * Requirements: 383 | * 384 | * - `to` cannot be the zero address. 385 | * - `tokenId` token must be owned by `from`. 386 | * 387 | * Emits a {Transfer} event. 388 | */ 389 | function _transfer(address from, address to, uint256 tokenId) internal virtual { 390 | require(ERC2309.ownerOf(tokenId) == from, "ERC721: transfer of token that is not own"); // internal owner 391 | require(to != address(0), "ERC721: transfer to the zero address"); 392 | 393 | _beforeTokenTransfer(from, to, tokenId); 394 | 395 | // Clear approvals from the previous owner 396 | _approve(address(0), tokenId); 397 | 398 | owners.setOwner(tokenId, to); 399 | 400 | emit Transfer(from, to, tokenId); 401 | } 402 | 403 | /** 404 | * @dev Sets `_tokenURI` as the tokenURI of `tokenId`. 405 | * 406 | * Requirements: 407 | * 408 | * - `tokenId` must exist. 409 | */ 410 | function _setTokenURI(uint256 tokenId, string memory _tokenURI) internal virtual { 411 | require(_exists(tokenId), "ERC721Metadata: URI set of nonexistent token"); 412 | _tokenURIs[tokenId] = _tokenURI; 413 | } 414 | 415 | /** 416 | * @dev Internal function to set the base URI for all token IDs. It is 417 | * automatically added as a prefix to the value returned in {tokenURI}, 418 | * or to the token ID if {tokenURI} is empty. 419 | */ 420 | function _setBaseURI(string memory baseURI_) internal virtual { 421 | _baseURI = baseURI_; 422 | } 423 | 424 | /** 425 | * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address. 426 | * The call is not executed if the target address is not a contract. 427 | * 428 | * @param from address representing the previous owner of the given token ID 429 | * @param to target address that will receive the tokens 430 | * @param tokenId uint256 ID of the token to be transferred 431 | * @param _data bytes optional data to send along with the call 432 | * @return bool whether the call correctly returned the expected magic value 433 | */ 434 | function _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory _data) 435 | private returns (bool) 436 | { 437 | if (!to.isContract()) { 438 | return true; 439 | } 440 | bytes memory returndata = to.functionCall(abi.encodeWithSelector( 441 | IERC721Receiver(to).onERC721Received.selector, 442 | _msgSender(), 443 | from, 444 | tokenId, 445 | _data 446 | ), "ERC721: transfer to non ERC721Receiver implementer"); 447 | bytes4 retval = abi.decode(returndata, (bytes4)); 448 | return (retval == _ERC721_RECEIVED); 449 | } 450 | 451 | function _approve(address to, uint256 tokenId) private { 452 | _tokenApprovals[tokenId] = to; 453 | emit Approval(ERC2309.ownerOf(tokenId), to, tokenId); // internal owner 454 | } 455 | 456 | /** 457 | * @dev Hook that is called before any token transfer. This includes minting 458 | * and burning. 459 | * 460 | * Calling conditions: 461 | * 462 | * - When `from` and `to` are both non-zero, ``from``'s `tokenId` will be 463 | * transferred to `to`. 464 | * - When `from` is zero, `tokenId` will be minted for `to`. 465 | * - When `to` is zero, ``from``'s `tokenId` will be burned. 466 | * - `from` cannot be the zero address. 467 | * - `to` cannot be the zero address. 468 | * 469 | * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. 470 | */ 471 | function _beforeTokenTransfer(address from, address to, uint256 tokenId) internal virtual { } 472 | 473 | 474 | function _init(address to, uint256 number_of_tokens) internal virtual { 475 | 476 | owners.init(to, number_of_tokens, number_of_tokens / 3); 477 | emit ConsecutiveTransfer(0, number_of_tokens, address(0), to); 478 | } 479 | 480 | 481 | } 482 | --------------------------------------------------------------------------------