Skip to content

Commit

Permalink
ERC721 extension for efficient batch minting (#3311)
Browse files Browse the repository at this point in the history
Co-authored-by: Francisco <frangio.1@gmail.com>
  • Loading branch information
Amxx and frangio authored Sep 5, 2022
1 parent 005a35b commit 171fa40
Show file tree
Hide file tree
Showing 17 changed files with 845 additions and 78 deletions.
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 @@ -44,10 +45,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);
}
}
Loading

0 comments on commit 171fa40

Please sign in to comment.