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

Add ERC721ABatchTransferable and ERC721ABatchBurnable #486

Merged
merged 13 commits into from
Sep 9, 2024
2 changes: 1 addition & 1 deletion .prettierrc
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@
}
}
]
}
}
381 changes: 325 additions & 56 deletions contracts/ERC721A.sol

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions contracts/IERC721A.sol
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,11 @@ interface IERC721A {
*/
error OwnershipNotInitializedForExtraData();

/**
* The `tokenIds` must be strictly ascending.
*/
error TokenIdsNotStrictlyAscending();

/**
* `_sequentialUpTo()` must be greater than `_startTokenId()`.
*/
Expand Down
19 changes: 19 additions & 0 deletions contracts/extensions/ERC721ABatchBurnable.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// SPDX-License-Identifier: MIT
// ERC721A Contracts v4.2.3
// Creator: Chiru Labs

pragma solidity ^0.8.4;

import './ERC721ABurnable.sol';
import './IERC721ABatchBurnable.sol';

/**
* @title ERC721ABatchBurnable.
*
* @dev ERC721A token optimized for batch burns.
*/
abstract contract ERC721ABatchBurnable is ERC721ABurnable, IERC721ABatchBurnable {
function batchBurn(uint256[] memory tokenIds) public virtual override {
_batchBurn(_msgSenderERC721A(), tokenIds);
}
}
40 changes: 40 additions & 0 deletions contracts/extensions/ERC721ABatchTransferable.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// SPDX-License-Identifier: MIT
// ERC721A Contracts v4.2.3
// Creator: Chiru Labs

pragma solidity ^0.8.4;

import '../ERC721A.sol';
import './IERC721ABatchTransferable.sol';

/**
* @title ERC721ABatchTransferable.
*
* @dev ERC721A token optimized for batch transfers.
*/
abstract contract ERC721ABatchTransferable is ERC721A, IERC721ABatchTransferable {
function batchTransferFrom(
address from,
address to,
uint256[] memory tokenIds
) public payable virtual override {
_batchTransferFrom(_msgSenderERC721A(), from, to, tokenIds);
}

function safeBatchTransferFrom(
address from,
address to,
uint256[] memory tokenIds
) public payable virtual override {
_safeBatchTransferFrom(_msgSenderERC721A(), from, to, tokenIds, '');
}

function safeBatchTransferFrom(
address from,
address to,
uint256[] memory tokenIds,
bytes memory _data
) public payable virtual override {
_safeBatchTransferFrom(_msgSenderERC721A(), from, to, tokenIds, _data);
}
}
14 changes: 14 additions & 0 deletions contracts/extensions/IERC721ABatchBurnable.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// SPDX-License-Identifier: MIT
// ERC721A Contracts v4.2.3
// Creator: Chiru Labs

pragma solidity ^0.8.4;

import './IERC721ABurnable.sol';

/**
* @dev Interface of ERC721ABatchBurnable.
*/
interface IERC721ABatchBurnable is IERC721ABurnable {
function batchBurn(uint256[] memory tokenIds) external;
}
62 changes: 62 additions & 0 deletions contracts/extensions/IERC721ABatchTransferable.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// SPDX-License-Identifier: MIT
// ERC721A Contracts v4.2.3
// Creator: Chiru Labs

pragma solidity ^0.8.4;

import '../IERC721A.sol';

/**
* @dev Interface of ERC721ABatchTransferable.
*/
interface IERC721ABatchTransferable is IERC721A {
/**
* @dev Transfers `tokenIds` in batch from `from` to `to`. See {ERC721A-_batchTransferFrom}.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenIds` tokens must be owned by `from`.
* - If the caller is not `from`, it must be approved to move these tokens
* by either {approve} or {setApprovalForAll}.
*
* Emits a {Transfer} event for each transfer.
*/
function batchTransferFrom(
address from,
address to,
uint256[] memory tokenIds
) external payable;

/**
* @dev Equivalent to `safeBatchTransferFrom(from, to, tokenIds, '')`.
*/
function safeBatchTransferFrom(
address from,
address to,
uint256[] memory tokenIds
) external payable;

/**
* @dev Safely transfers `tokenIds` in batch from `from` to `to`. See {ERC721A-_safeBatchTransferFrom}.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenIds` tokens must be owned by `from`.
* - If the caller is not `from`, it must be approved to move these tokens
* by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement
* {IERC721Receiver-onERC721Received}, which is called for each transferred token.
*
* Emits a {Transfer} event for each transfer.
*/
function safeBatchTransferFrom(
address from,
address to,
uint256[] memory tokenIds,
bytes memory _data
) external payable;
}
7 changes: 7 additions & 0 deletions contracts/interfaces/IERC721ABatchBurnable.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// SPDX-License-Identifier: MIT
// ERC721A Contracts v4.2.3
// Creator: Chiru Labs

pragma solidity ^0.8.4;

import '../extensions/IERC721ABatchBurnable.sol';
7 changes: 7 additions & 0 deletions contracts/interfaces/IERC721ABatchTransferable.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// SPDX-License-Identifier: MIT
// ERC721A Contracts v4.2.3
// Creator: Chiru Labs

pragma solidity ^0.8.4;

import '../extensions/IERC721ABatchTransferable.sol';
40 changes: 40 additions & 0 deletions contracts/mocks/ERC721ABatchBurnableMock.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// SPDX-License-Identifier: MIT
// ERC721A Contracts v4.2.3
// Creators: Chiru Labs

pragma solidity ^0.8.4;

import '../extensions/ERC721ABatchBurnable.sol';
import './DirectBurnBitSetterHelper.sol';

contract ERC721ABatchBurnableMock is ERC721ABatchBurnable, DirectBurnBitSetterHelper {
constructor(string memory name_, string memory symbol_) ERC721A(name_, symbol_) {}

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

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

function getOwnershipAt(uint256 index) public view returns (TokenOwnership memory) {
return _ownershipAt(index);
}

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

function totalBurned() public view returns (uint256) {
return _totalBurned();
}

function numberBurned(address owner) public view returns (uint256) {
return _numberBurned(owner);
}

function initializeOwnershipAt(uint256 index) public {
_initializeOwnershipAt(index);
}
}
64 changes: 64 additions & 0 deletions contracts/mocks/ERC721ABatchTransferableMock.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// SPDX-License-Identifier: MIT
// ERC721A Contracts v4.2.3
// Creators: Chiru Labs

pragma solidity ^0.8.4;

import '../extensions/ERC721ABatchTransferable.sol';

contract ERC721ABatchTransferableMock is ERC721ABatchTransferable {
constructor(string memory name_, string memory symbol_) ERC721A(name_, symbol_) {}

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

function getOwnershipAt(uint256 index) public view returns (TokenOwnership memory) {
return _ownershipAt(index);
}

function initializeOwnershipAt(uint256 index) public {
_initializeOwnershipAt(index);
}

function _extraData(
address,
address,
uint24 previousExtraData
) internal view virtual override returns (uint24) {
return previousExtraData;
}

function setExtraDataAt(uint256 index, uint24 extraData) public {
_setExtraDataAt(index, extraData);
}

function batchTransferFromUnoptimized(
address from,
address to,
uint256[] memory tokenIds
) public {
unchecked {
for (uint256 i; i != tokenIds.length; ++i) {
transferFrom(from, to, tokenIds[i]);
}
}
}

function directBatchTransferFrom(
address by,
address from,
address to,
uint256[] memory tokenIds
) public {
_batchTransferFrom(by, from, to, tokenIds);
}

function directBatchTransferFrom(
address from,
address to,
uint256[] memory tokenIds
) public {
_batchTransferFrom(from, to, tokenIds);
}
}
26 changes: 26 additions & 0 deletions contracts/mocks/ERC721AGasReporterMock.sol
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@ contract ERC721AGasReporterMock is ERC721A {
_mint(to, 10);
}

function safeMintHundred(address to) public {
_safeMint(to, 100);
}

function mintHundred(address to) public {
_mint(to, 100);
}

function transferTenAsc(address to) public {
unchecked {
transferFrom(msg.sender, to, 0);
Expand Down Expand Up @@ -69,4 +77,22 @@ contract ERC721AGasReporterMock is ERC721A {
transferFrom(msg.sender, to, 9);
}
}

function batchTransferHundredUnoptimized(address to) public {
unchecked {
for (uint256 i; i != 100; ++i) {
transferFrom(msg.sender, to, i);
}
}
}

function batchTransferHundredOptimized(address to) public {
unchecked {
uint256[] memory tokenIds = new uint256[](100);
for (uint256 i; i != 100; ++i) {
tokenIds[i] = i;
}
_batchTransferFrom(msg.sender, msg.sender, to, tokenIds);
}
}
}
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

34 changes: 34 additions & 0 deletions test/GasUsage.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,22 @@ describe('ERC721A Gas Usage', function () {
});
});

context('mintHundred', function () {
it('runs mintHundred 2 times', async function () {
for (let i = 0; i < 2; i++) {
await this.erc721a.mintHundred(this.addr1.address);
}
});
});

context('safeMintHundred', function () {
it('runs safeMintHundred 2 times', async function () {
for (let i = 0; i < 2; i++) {
await this.erc721a.safeMintHundred(this.addr1.address);
}
});
});

context('transferFrom', function () {
beforeEach(async function () {
await this.erc721a.mintTen(this.owner.address);
Expand Down Expand Up @@ -67,6 +83,24 @@ describe('ERC721A Gas Usage', function () {
it('transferTen average order', async function () {
await this.erc721a.connect(this.owner).transferTenAvg(this.addr1.address);
});

it('transferTen average order', async function () {
await this.erc721a.connect(this.owner).transferTenAvg(this.addr1.address);
});
});

context('batchTransferFromHundred', function () {
beforeEach(async function () {
await this.erc721a.mintHundred(this.owner.address);
});

it('batchTransferFromHundred unoptimized', async function () {
await this.erc721a.connect(this.owner).batchTransferHundredUnoptimized(this.addr1.address);
});

it('batchTransferFromHundred optimized', async function () {
await this.erc721a.connect(this.owner).batchTransferHundredOptimized(this.addr1.address);
});
});

it('mintOneERC2309', async function () {
Expand Down
Loading
Loading