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

ERC721 extension for efficient batch minting #3311

Merged
merged 51 commits into from
Sep 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
4176852
move mock
Amxx Jul 28, 2022
63edb74
Expand Checkpoint library
Amxx Jul 28, 2022
da0ea31
rename upperLookupExpEnd → upperLookupRecent
Amxx Jul 28, 2022
ed81388
sanity check when pushing checkpoints
Amxx Jul 28, 2022
b91832d
Skip redundant optimisation in VotesQuorumFraction
Amxx Jul 29, 2022
d2a829c
Merge branch 'master' into feature/checkpoint
Amxx Jul 29, 2022
2a6adb3
reset previous history lookup algorithm and reenable quorum checks
Amxx Jul 29, 2022
28f6e6f
Use unsafeAccess to save gas
Amxx Jul 29, 2022
b7e2f64
Merge branch 'master' into feature/checkpoint
Amxx Jul 29, 2022
616c51b
Regenerate contracts
Amxx Jul 29, 2022
3fb185b
Add changelog entry
Amxx Jul 29, 2022
ce0c7a6
use StorageSlot for unsafeAccess returns
Amxx Jul 29, 2022
33741e7
minor rewrite for readability
Amxx Jul 29, 2022
8425e4a
fix typo
Amxx Jul 29, 2022
74fef95
testing unsafeAccess
Amxx Jul 29, 2022
df3a9b1
fix lint
Amxx Jul 29, 2022
e182730
Update CHANGELOG.md
Amxx Jul 29, 2022
03835b3
Rebase ERC721Consecutive work on Checkpoints
Amxx Aug 10, 2022
0a5d397
improvement & cleanup
Amxx Aug 10, 2022
fb48fcf
documentation
Amxx Aug 10, 2022
564d85b
Merge branch 'master' into feature/ERC721/sequential
Amxx Aug 30, 2022
dac9814
fix merge
Amxx Aug 30, 2022
76f914b
simplify mocks
Amxx Aug 31, 2022
70b0ba5
don't revert on no-op
Amxx Aug 31, 2022
d3c7bd6
Apply suggestions from code review
Amxx Sep 1, 2022
7f625ef
Change params of _xxxxConsecutiveTokenTransfer hooks
Amxx Sep 1, 2022
7fbbf8d
Merge remote-tracking branch 'amxx/feature/ERC721/sequential' into fe…
Amxx Sep 1, 2022
ceeb038
address comments from the PR
Amxx Sep 1, 2022
bb21552
more testing
Amxx Sep 1, 2022
cb8dfcf
fix lint
Amxx Sep 1, 2022
8d198ee
spelling
Amxx Sep 1, 2022
7060d60
use hooks for explicit burning + docs
Amxx Sep 2, 2022
0b07112
add changelog entry
Amxx Sep 2, 2022
7b30f04
fix mock
Amxx Sep 2, 2022
a204f8e
disable enumerability of consecutivelly minted tokens
Amxx Sep 4, 2022
2ef65e0
fix lint
Amxx Sep 4, 2022
dfc692d
extend Checkpoint library
Amxx Sep 4, 2022
ed7668d
fix lint
Amxx Sep 4, 2022
031278c
improve latestCheckpoint() function
Amxx Sep 5, 2022
828b910
Add an IERC2309 interface
Amxx Sep 5, 2022
4e428c2
Merge branch 'master' into feature/ERC721/sequential
Amxx Sep 5, 2022
b8c48c8
add compatibility note
frangio Sep 5, 2022
f6d6c6e
Revert "add compatibility note"
frangio Sep 5, 2022
b34f0c9
add compatibility note
frangio Sep 5, 2022
1ecf1b8
fix typo weither -> wether
frangio Sep 5, 2022
86d8ef0
fix typos
frangio Sep 5, 2022
b590352
grammar
frangio Sep 5, 2022
d23313d
reorder inheritance
frangio Sep 5, 2022
e35e4f6
grammar
frangio Sep 5, 2022
3c85862
grammar
frangio Sep 5, 2022
b7b9434
test batch of 5001 not allowed
frangio Sep 5, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
* `ERC721`: optimize burn by making approval clearing implicit instead of emitting an event. ([#3538](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3538))
* `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))
* `ERC721`: use unchecked arithmetic for balance updates. ([#3524](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3524))
* `ERC721Consecutive`: Implementation of EIP-2309 that allows batch minting of ERC721 tokens during construction. ([#3311](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3311))
* `ReentrancyGuard`: Reduce code size impact of the modifier by using internal functions. ([#3515](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3515))
* `SafeCast`: optimize downcasting of signed integers. ([#3565](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3565))
* `ECDSA`: Remove redundant check on the `v` value. ([#3591](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3591))
Expand Down Expand Up @@ -43,10 +44,12 @@
+import "@openzeppelin/contracts/utils/cryptography/EIP712.sol";
```

### Compatibility Note
### ERC-721 Compatibility Note

ERC-721 integrators that interpret contract state from events should make sure that they implement the clearing of approval that is implicit in every transfer according to the EIP. Previous versions of OpenZeppelin Contracts emitted an explicit `Approval` event even though it was not required by the specification, and this is no longer the case.

With the new `ERC721Consecutive` extension, the internal workings of `ERC721` are slightly changed. Custom extensions to ERC721 should be reviewed to ensure they remain correct. The new internal functions that should be considered are `_ownerOf`, `_beforeConsecutiveTokenTransfer`, and `_afterConsecutiveTokenTransfer`, and the existing internal functions that should be reviewed are `_exists`, `_beforeTokenTransfer`, and `_afterTokenTransfer`.

## 4.7.3

### Breaking changes
Expand Down
2 changes: 1 addition & 1 deletion contracts/governance/IGovernor.sol
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ abstract contract IGovernor is IERC165 {

/**
* @notice module:voting
* @dev Returns weither `account` has cast a vote on `proposalId`.
* @dev Returns whether `account` has cast a vote on `proposalId`.
*/
function hasVoted(uint256 proposalId, address account) public view virtual returns (bool);

Expand Down
20 changes: 20 additions & 0 deletions contracts/interfaces/IERC2309.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
* @dev ERC-2309: ERC-721 Consecutive Transfer Extension.
*
* _Available since v4.8._
*/
interface IERC2309 {
/**
* @dev Emitted when the tokens from `fromTokenId` to `toTokenId` are transferred from `fromAddress` to `toAddress`.
*/
event ConsecutiveTransfer(
uint256 indexed fromTokenId,
uint256 toTokenId,
address indexed fromAddress,
address indexed toAddress
);
}
60 changes: 48 additions & 12 deletions contracts/mocks/CheckpointsMock.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,22 @@ contract CheckpointsMock {
return _totalCheckpoints.latest();
}

function latestCheckpoint()
public
view
returns (
bool,
uint256,
uint256
)
{
return _totalCheckpoints.latestCheckpoint();
}

function length() public view returns (uint256) {
return _totalCheckpoints.length();
}

function push(uint256 value) public returns (uint256, uint256) {
return _totalCheckpoints.push(value);
}
Expand All @@ -25,10 +41,6 @@ contract CheckpointsMock {
function getAtProbablyRecentBlock(uint256 blockNumber) public view returns (uint256) {
return _totalCheckpoints.getAtProbablyRecentBlock(blockNumber);
}

function length() public view returns (uint256) {
return _totalCheckpoints._checkpoints.length;
}
}

contract Checkpoints224Mock {
Expand All @@ -40,6 +52,22 @@ contract Checkpoints224Mock {
return _totalCheckpoints.latest();
}

function latestCheckpoint()
public
view
returns (
bool,
uint32,
uint224
)
{
return _totalCheckpoints.latestCheckpoint();
}

function length() public view returns (uint256) {
return _totalCheckpoints.length();
}

function push(uint32 key, uint224 value) public returns (uint224, uint224) {
return _totalCheckpoints.push(key, value);
}
Expand All @@ -51,10 +79,6 @@ contract Checkpoints224Mock {
function upperLookup(uint32 key) public view returns (uint224) {
return _totalCheckpoints.upperLookup(key);
}

function length() public view returns (uint256) {
return _totalCheckpoints._checkpoints.length;
}
}

contract Checkpoints160Mock {
Expand All @@ -66,6 +90,22 @@ contract Checkpoints160Mock {
return _totalCheckpoints.latest();
}

function latestCheckpoint()
public
view
returns (
bool,
uint96,
uint160
)
{
return _totalCheckpoints.latestCheckpoint();
}

function length() public view returns (uint256) {
return _totalCheckpoints.length();
}

function push(uint96 key, uint160 value) public returns (uint160, uint160) {
return _totalCheckpoints.push(key, value);
}
Expand All @@ -77,8 +117,4 @@ contract Checkpoints160Mock {
function upperLookup(uint96 key) public view returns (uint160) {
return _totalCheckpoints.upperLookup(key);
}

function length() public view returns (uint256) {
return _totalCheckpoints._checkpoints.length;
}
}
158 changes: 158 additions & 0 deletions contracts/mocks/ERC721ConsecutiveMock.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "../token/ERC721/extensions/ERC721Burnable.sol";
import "../token/ERC721/extensions/ERC721Consecutive.sol";
import "../token/ERC721/extensions/ERC721Enumerable.sol";
import "../token/ERC721/extensions/ERC721Pausable.sol";
import "../token/ERC721/extensions/draft-ERC721Votes.sol";

/**
* @title ERC721ConsecutiveMock
*/
contract ERC721ConsecutiveMock is ERC721Burnable, ERC721Consecutive, ERC721Pausable, ERC721Votes {
constructor(
string memory name,
string memory symbol,
address[] memory delegates,
address[] memory receivers,
uint96[] memory amounts
) ERC721(name, symbol) EIP712(name, "1") {
for (uint256 i = 0; i < delegates.length; ++i) {
_delegate(delegates[i], delegates[i]);
}

for (uint256 i = 0; i < receivers.length; ++i) {
_mintConsecutive(receivers[i], amounts[i]);
}
}

function pause() external {
_pause();
}

function unpause() external {
_unpause();
}

function exists(uint256 tokenId) public view returns (bool) {
return _exists(tokenId);
}

function mint(address to, uint256 tokenId) public {
_mint(to, tokenId);
}

function mintConsecutive(address to, uint96 amount) public {
_mintConsecutive(to, amount);
}

function safeMint(address to, uint256 tokenId) public {
_safeMint(to, tokenId);
}

function _ownerOf(uint256 tokenId) internal view virtual override(ERC721, ERC721Consecutive) returns (address) {
return super._ownerOf(tokenId);
}

function _mint(address to, uint256 tokenId) internal virtual override(ERC721, ERC721Consecutive) {
super._mint(to, tokenId);
}

function _beforeTokenTransfer(
address from,
address to,
uint256 tokenId
) internal virtual override(ERC721, ERC721Pausable) {
super._beforeTokenTransfer(from, to, tokenId);
}

function _afterTokenTransfer(
address from,
address to,
uint256 tokenId
) internal virtual override(ERC721, ERC721Votes, ERC721Consecutive) {
super._afterTokenTransfer(from, to, tokenId);
}

function _beforeConsecutiveTokenTransfer(
address from,
address to,
uint256 first,
uint96 size
) internal virtual override(ERC721, ERC721Pausable) {
super._beforeConsecutiveTokenTransfer(from, to, first, size);
}

function _afterConsecutiveTokenTransfer(
address from,
address to,
uint256 first,
uint96 size
) internal virtual override(ERC721, ERC721Votes) {
super._afterConsecutiveTokenTransfer(from, to, first, size);
}
}

contract ERC721ConsecutiveEnumerableMock is ERC721Consecutive, ERC721Enumerable {
constructor(
string memory name,
string memory symbol,
address[] memory receivers,
uint96[] memory amounts
) ERC721(name, symbol) {
for (uint256 i = 0; i < receivers.length; ++i) {
_mintConsecutive(receivers[i], amounts[i]);
}
}

function supportsInterface(bytes4 interfaceId)
public
view
virtual
override(ERC721, ERC721Enumerable)
returns (bool)
{
return super.supportsInterface(interfaceId);
}

function _ownerOf(uint256 tokenId) internal view virtual override(ERC721, ERC721Consecutive) returns (address) {
return super._ownerOf(tokenId);
}

function _mint(address to, uint256 tokenId) internal virtual override(ERC721, ERC721Consecutive) {
super._mint(to, tokenId);
}

function _beforeTokenTransfer(
address from,
address to,
uint256 tokenId
) internal virtual override(ERC721, ERC721Enumerable) {
super._beforeTokenTransfer(from, to, tokenId);
}

function _afterTokenTransfer(
address from,
address to,
uint256 tokenId
) internal virtual override(ERC721, ERC721Consecutive) {
super._afterTokenTransfer(from, to, tokenId);
}

function _beforeConsecutiveTokenTransfer(
address from,
address to,
uint256 first,
uint96 size
) internal virtual override(ERC721, ERC721Enumerable) {
super._beforeConsecutiveTokenTransfer(from, to, first, size);
}
}

contract ERC721ConsecutiveNoConstructorMintMock is ERC721Consecutive {
constructor(string memory name, string memory symbol) ERC721(name, symbol) {
_mint(msg.sender, 0);
}
}