Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Start Sequentially Minting at _startTokenId() instead of hardcoded 0 #66

Merged
merged 48 commits into from
Feb 26, 2022
Merged
Show file tree
Hide file tree
Changes from 47 commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
8ca9711
replaces currentIndex with _nextTokenId which starts at 1 instead of 0
Pczek Feb 2, 2022
d1900dd
adapts test to new starting index of 1
Pczek Feb 2, 2022
715fae5
makes `ERC721AOwnersExplicit` test work with new starting index
Pczek Feb 3, 2022
6c22e6f
explicitly sets `nextOwnerToExplicitlySet` to 1 because it's initiali…
Pczek Feb 3, 2022
78bf5cd
remove vscode files
Pczek Feb 6, 2022
e56e7b5
implements @jpegditials suggestions
Pczek Feb 6, 2022
0f0145f
adds tests for `tokenByIndex` and `tokenOfOwnerByIndex`
Pczek Feb 6, 2022
949050b
prevents tokenId 0 to be an existing token
Pczek Feb 6, 2022
836fc13
adapts `tokenOfOwnerByIndex` for starting token index 1 and making te…
Pczek Feb 6, 2022
a4b90eb
renames variable to make it more clear, it's index by the tokenId
Pczek Feb 6, 2022
e4f3539
adds unchecked scopes to save on gas as suggested by @Vectorized
Pczek Feb 7, 2022
826ed21
more gas optimizations by @Vectorized
Pczek Feb 7, 2022
9c12d8f
implements suggested `_startTokenId` function to override to set star…
Pczek Feb 7, 2022
91c1736
minor adaptions to work with _startTokenId() properly
Pczek Feb 7, 2022
a002ad1
brings back original tests
Pczek Feb 7, 2022
9e562e6
creates copy of original test with startTokenId set to 1
Pczek Feb 7, 2022
c45bdd0
adapts comment to reflect flexibility of the starting tokenId
Pczek Feb 7, 2022
1febae2
Merge github.com:chiru-labs/ERC721A
Pczek Feb 9, 2022
74379a0
adapts solidity version of tests
Pczek Feb 9, 2022
493867e
Replaces revert messages with new contract errors
Pczek Feb 9, 2022
d25bf0e
fix merge hiccup
Pczek Feb 9, 2022
72691d3
Merge branch 'main' into main
Pczek Feb 10, 2022
42e2ad3
optimisations to save on gas used during deployment
Pczek Feb 10, 2022
d34d4ac
Added #61
Vectorized Feb 13, 2022
fbb39c2
Edited comments
Vectorized Feb 13, 2022
a9d8541
Merge branch 'main' into feature/startIndex
Vectorized Feb 13, 2022
62953b8
Edited README roadmap
Vectorized Feb 13, 2022
02dff65
Removed burn test with one-index mock
Vectorized Feb 13, 2022
ca96c35
Changed _startTokenId to _currentIndex
Vectorized Feb 13, 2022
f51b943
Merge pull request #1 from Vectorized/feature/startIndex
Pczek Feb 14, 2022
c830b22
Merge branch 'main' into main
Pczek Feb 15, 2022
ab40ed7
Merge branch 'main' into main
Pczek Feb 18, 2022
686dfdb
adapting test and mock related to index starting at 1 to make them wo…
Pczek Feb 18, 2022
cff958d
add _totalMinted function
ahbanavi Feb 20, 2022
fc75155
add tests for _totalMinted
ahbanavi Feb 20, 2022
8618b66
Merge pull request #3 from ahbanavi/feature/totalMinted
Pczek Feb 23, 2022
f0a5bb4
removes uint128 cast, simplifies comment and lints file
Pczek Feb 23, 2022
cdd2ddb
rename `ERC721AExplicitOwnershipMock` to `ERC721AOwnersExplicitMock` …
Pczek Feb 24, 2022
1473219
extend from already mocked ERC721A versions to dry code.
Pczek Feb 24, 2022
ac31bd0
aligns file naming and contract naming
Pczek Feb 24, 2022
582ee1d
creates new ERC721A mock which enables the user to specify the startT…
Pczek Feb 24, 2022
2ba810d
converts ERC721A test to a parameterized test to work with other mock…
Pczek Feb 24, 2022
9cb1337
extracts Helper contract to it's own file
Pczek Feb 25, 2022
b89514f
parameterises ERC721A Explicit Owner Tests to also test a Version of …
Pczek Feb 25, 2022
b775d00
makes it more transparent where the startTokenId value is coming from
Pczek Feb 25, 2022
e16a79a
creates a ERC721ABurnable Mock with custom startTokenId and uses it i…
Pczek Feb 25, 2022
5af67ea
removes code used during development
Pczek Feb 25, 2022
1f51747
unifies code style across all tests
Pczek Feb 25, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ contract Azuki is ERC721A {

## Roadmap

- [] Add flexibility for the first token id to not start at 0
- [] Support ERC721 Upgradeable
- [] Add more documentation on benefits of using ERC721A
- [] Increase test coverage
Expand Down
39 changes: 30 additions & 9 deletions contracts/ERC721A.sol
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ error URIQueryForNonexistentToken();
* @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including
* the Metadata extension. Built to optimize for lower gas during batch mints.
*
* Assumes serials are sequentially minted starting at 0 (e.g. 0, 1, 2, 3..).
* Assumes serials are sequentially minted starting at _startTokenId() (defaults to 0, e.g. 0, 1, 2, 3..).
*
* Assumes that an owner cannot have more than 2**64 - 1 (max value of uint64) of supply.
*
Expand Down Expand Up @@ -64,7 +64,7 @@ contract ERC721A is Context, ERC165, IERC721, IERC721Metadata {
// Keeps track of burn count with minimal overhead for tokenomics.
uint64 numberBurned;
// For miscellaneous variable(s) pertaining to the address
// (e.g. number of whitelist mint slots used).
// (e.g. number of whitelist mint slots used).
// If there are multiple variables, please pack them into a uint64.
uint64 aux;
}
Expand Down Expand Up @@ -97,16 +97,36 @@ contract ERC721A is Context, ERC165, IERC721, IERC721Metadata {
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
_currentIndex = _startTokenId();
}

/**
* To change the starting tokenId, please override this function.
*/
function _startTokenId() internal view virtual returns (uint256) {
return 0;
}

/**
* @dev See {IERC721Enumerable-totalSupply}.
* @dev Burned tokens are calculated here, use _totalMinted() if you want to count just minted tokens.
*/
function totalSupply() public view returns (uint256) {
// Counter underflow is impossible as _burnCounter cannot be incremented
// more than _currentIndex times
// more than _currentIndex - _startTokenId() times
unchecked {
return _currentIndex - _burnCounter;
return _currentIndex - _burnCounter - _startTokenId();
}
}

/**
* Returns the total amount of tokens minted in the contract.
*/
function _totalMinted() internal view returns (uint256) {
// Counter underflow is impossible as _currentIndex does not decrement,
// and it is initialized to _startTokenId()
unchecked {
return _currentIndex - _startTokenId();
}
}

Expand Down Expand Up @@ -169,14 +189,14 @@ contract ERC721A is Context, ERC165, IERC721, IERC721Metadata {
uint256 curr = tokenId;

unchecked {
if (curr < _currentIndex) {
if (_startTokenId() <= curr && curr < _currentIndex) {
TokenOwnership memory ownership = _ownerships[curr];
if (!ownership.burned) {
if (ownership.addr != address(0)) {
return ownership;
}
// Invariant:
// There will always be an ownership that has an address and is not burned
// Invariant:
// There will always be an ownership that has an address and is not burned
// before an ownership that does not have an address and is not burned.
// Hence, curr will not underflow.
while (true) {
Expand Down Expand Up @@ -317,7 +337,8 @@ contract ERC721A is Context, ERC165, IERC721, IERC721Metadata {
* Tokens start existing when they are minted (`_mint`),
*/
function _exists(uint256 tokenId) internal view returns (bool) {
return tokenId < _currentIndex && !_ownerships[tokenId].burned;
return _startTokenId() <= tokenId && tokenId < _currentIndex &&
!_ownerships[tokenId].burned;
}

function _safeMint(address to, uint256 quantity) internal {
Expand Down Expand Up @@ -493,7 +514,7 @@ contract ERC721A is Context, ERC165, IERC721, IERC721Metadata {
_afterTokenTransfers(prevOwnership.addr, address(0), tokenId, 1);

// Overflow not possible, as _burnCounter cannot be exceed _currentIndex times.
unchecked {
unchecked {
_burnCounter++;
}
}
Expand Down
5 changes: 4 additions & 1 deletion contracts/extensions/ERC721AOwnersExplicit.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,11 @@ abstract contract ERC721AOwnersExplicit is ERC721A {
*/
function _setOwnersExplicit(uint256 quantity) internal {
if (quantity == 0) revert QuantityMustBeNonZero();
if (_currentIndex == 0) revert NoTokensMintedYet();
if (_currentIndex == _startTokenId()) revert NoTokensMintedYet();
uint256 _nextOwnerToExplicitlySet = nextOwnerToExplicitlySet;
if (_nextOwnerToExplicitlySet == 0) {
_nextOwnerToExplicitlySet = _startTokenId();
}
if (_nextOwnerToExplicitlySet >= _currentIndex) revert AllOwnershipsHaveBeenSet();

// Index underflow is impossible.
Expand Down
4 changes: 4 additions & 0 deletions contracts/mocks/ERC721ABurnableMock.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,8 @@ contract ERC721ABurnableMock is ERC721A, ERC721ABurnable {
function getOwnershipAt(uint256 index) public view returns (TokenOwnership memory) {
return _ownerships[index];
}

function totalMinted() public view returns (uint256) {
return _totalMinted();
}
}
19 changes: 19 additions & 0 deletions contracts/mocks/ERC721ABurnableStartTokenIdMock.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// SPDX-License-Identifier: MIT
// Creators: Chiru Labs

pragma solidity ^0.8.4;

import './ERC721ABurnableMock.sol';
import './StartTokenIdHelper.sol';

contract ERC721ABurnableStartTokenIdMock is StartTokenIdHelper, ERC721ABurnableMock {
constructor(
string memory name_,
string memory symbol_,
uint256 startTokenId_
) StartTokenIdHelper(startTokenId_) ERC721ABurnableMock(name_, symbol_) {}

function _startTokenId() internal view override returns (uint256) {
return startTokenId;
}
}
4 changes: 4 additions & 0 deletions contracts/mocks/ERC721AMock.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ contract ERC721AMock is ERC721A {
return _numberMinted(owner);
}

function totalMinted() public view returns (uint256) {
return _totalMinted();
}

function getAux(address owner) public view returns (uint64) {
return _getAux(owner);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ contract ERC721AOwnersExplicitMock is ERC721AOwnersExplicit {
_setOwnersExplicit(quantity);
}

function getOwnershipAt(uint256 index) public view returns (TokenOwnership memory) {
return _ownerships[index];
function getOwnershipAt(uint256 tokenId) public view returns (TokenOwnership memory) {
return _ownerships[tokenId];
}
}
19 changes: 19 additions & 0 deletions contracts/mocks/ERC721AOwnersExplicitStartTokenIdMock.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// SPDX-License-Identifier: MIT
// Creators: Chiru Labs

pragma solidity ^0.8.4;

import './ERC721AOwnersExplicitMock.sol';
import './StartTokenIdHelper.sol';

contract ERC721AOwnersExplicitStartTokenIdMock is StartTokenIdHelper, ERC721AOwnersExplicitMock {
constructor(
string memory name_,
string memory symbol_,
uint256 startTokenId_
) StartTokenIdHelper(startTokenId_) ERC721AOwnersExplicitMock(name_, symbol_) {}

function _startTokenId() internal view override returns (uint256) {
return startTokenId;
}
}
19 changes: 19 additions & 0 deletions contracts/mocks/ERC721AStartTokenIdMock.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// SPDX-License-Identifier: MIT
// Creators: Chiru Labs

pragma solidity ^0.8.4;

import './ERC721AMock.sol';
import './StartTokenIdHelper.sol';

contract ERC721AStartTokenIdMock is StartTokenIdHelper, ERC721AMock {
constructor(
string memory name_,
string memory symbol_,
uint256 startTokenId_
) StartTokenIdHelper(startTokenId_) ERC721AMock(name_, symbol_) {}

function _startTokenId() internal view override returns (uint256) {
return startTokenId;
}
}
17 changes: 17 additions & 0 deletions contracts/mocks/StartTokenIdHelper.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// SPDX-License-Identifier: MIT
// Creators: Chiru Labs

pragma solidity ^0.8.4;

/**
* This Helper is used to return a dynmamic value in the overriden _startTokenId() function.
* Extending this Helper before the ERC721A contract give us access to the herein set `startTokenId`
* to be returned by the overriden `_startTokenId()` function of ERC721A in the ERC721AStartTokenId mocks.
*/
contract StartTokenIdHelper {
uint256 public immutable startTokenId;

constructor(uint256 startTokenId_) {
startTokenId = startTokenId_;
}
}
Loading