From b560d3a2430d84e4ee4a92a720936e02d2961d23 Mon Sep 17 00:00:00 2001 From: TuDo1403 Date: Sun, 29 Sep 2024 17:46:55 +0700 Subject: [PATCH 1/8] BREAKING_CHANGE: adapt openzeppelin v5 --- .gitignore | 2 + .gitmodules | 12 --- foundry.toml | 33 ++++++- lib/forge-std | 1 - lib/openzeppelin-contracts | 1 - lib/openzeppelin-contracts-upgradeable | 1 - remappings.txt | 6 +- soldeer.lock | 27 ++++++ src/ERC721Common.sol | 67 ++++++++------- ...C721PresetMinterPauserAutoIdCustomized.sol | 72 +++++++++------- src/interfaces/IERC721Common.sol | 4 +- ...C721PresetMinterPauserAutoIdCustomized.sol | 4 +- src/interfaces/IERC721State.sol | 9 +- src/interfaces/launchpad/INFTLaunchpad.sol | 8 +- src/launchpad/NFTLaunchpadCommon.sol | 4 +- src/mock/SampleERC721.sol | 8 +- src/mock/launchpad/SampleNFT1155Launchpad.sol | 28 +++--- src/mock/launchpad/SampleNFT721Launchpad.sol | 22 ++--- src/refs/ERC721Nonce.sol | 16 ++-- src/upgradeable/ERC721CommonUpgradeable.sol | 53 +++++++----- ...interPauserAutoIdCustomizedUpgradeable.sol | 86 ++++++++++++------- .../refs/ERC721NonceUpgradeable.sol | 22 ++--- test/foundry/SampleERC721.t.sol | 15 ++-- test/foundry/SampleERC721Upgradeable.t.sol | 47 +++++----- 24 files changed, 322 insertions(+), 226 deletions(-) delete mode 100644 .gitmodules delete mode 160000 lib/forge-std delete mode 160000 lib/openzeppelin-contracts delete mode 160000 lib/openzeppelin-contracts-upgradeable create mode 100644 soldeer.lock diff --git a/.gitignore b/.gitignore index a10fdc8..92e7d0d 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,5 @@ artifacts/ typechain-types/ .idea .env + +dependencies \ No newline at end of file diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 1674f48..0000000 --- a/.gitmodules +++ /dev/null @@ -1,12 +0,0 @@ -[submodule "lib/forge-std"] - path = lib/forge-std - url = https://github.com/foundry-rs/forge-std - branch = v1.5.3 -[submodule "lib/openzeppelin-contracts"] - path = lib/openzeppelin-contracts - url = https://github.com/openzeppelin/openzeppelin-contracts - branch = v4.8.2 -[submodule "lib/openzeppelin-contracts-upgradeable"] - path = lib/openzeppelin-contracts-upgradeable - url = https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable - branch = v4.9.5 diff --git a/foundry.toml b/foundry.toml index f2e1cd0..0dfd096 100644 --- a/foundry.toml +++ b/foundry.toml @@ -11,6 +11,37 @@ extra_output = ["devdoc", "userdoc", "storagelayout"] fs_permissions = [{ access = "read-write", path = "./" }] [fmt] -tab_width = 2 line_length = 120 +tab_width = 2 bracket_spacing = true +multiline_func_header = 'params_first' +single_line_statement_blocks = 'single' +sort_imports = true +number_underscore = "thousands" +wrap_comments = true + +[dependencies] +"openzeppelin" = { version = "5.0.2", url = "https://github.com/OpenZeppelin/openzeppelin-contracts/archive/refs/tags/v5.0.2.zip" } +"openzeppelin-upgradeable" = { version = "5.0.2", url = "https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/archive/refs/tags/v5.0.2.zip" } +contract-template = { version = "0.1.0", url = "https://github.com/axieinfinity/contract-template/archive/refs/tags/release-v0.1.0.zip" } +forge-std = { version = "1.9.3", url = "https://github.com/foundry-rs/forge-std/archive/refs/tags/v1.9.3.zip" } + +[soldeer] +# whether soldeer manages remappings +remappings_generate = false + +# whether soldeer re-generates all remappings when installing, updating or uninstalling deps +remappings_regenerate = false + +# whether to suffix the remapping with the version: `name-a.b.c` +remappings_version = true + +# a prefix to add to the remappings ("@" would give `@name`) +remappings_prefix = "@" + +# where to store the remappings ("txt" for `remappings.txt` or "config" for `foundry.toml`) +# ignored when `soldeer.toml` is used as config (uses `remappings.txt`) +remappings_location = "txt" + +# whether to install sub-dependencies or not. If true this wil install the dependencies of dependencies 1 level down. +recursive_deps = true diff --git a/lib/forge-std b/lib/forge-std deleted file mode 160000 index fc560fa..0000000 --- a/lib/forge-std +++ /dev/null @@ -1 +0,0 @@ -Subproject commit fc560fa34fa12a335a50c35d92e55a6628ca467c diff --git a/lib/openzeppelin-contracts b/lib/openzeppelin-contracts deleted file mode 160000 index d00acef..0000000 --- a/lib/openzeppelin-contracts +++ /dev/null @@ -1 +0,0 @@ -Subproject commit d00acef4059807535af0bd0dd0ddf619747a044b diff --git a/lib/openzeppelin-contracts-upgradeable b/lib/openzeppelin-contracts-upgradeable deleted file mode 160000 index a40cb0b..0000000 --- a/lib/openzeppelin-contracts-upgradeable +++ /dev/null @@ -1 +0,0 @@ -Subproject commit a40cb0bda838c2ef3dfc252c179f5c37c32e80c4 diff --git a/remappings.txt b/remappings.txt index 82ca5a0..935efd3 100644 --- a/remappings.txt +++ b/remappings.txt @@ -1,3 +1,3 @@ -forge-std/=lib/forge-std/src/ -ds-test/=lib/forge-std/lib/ds-test/src/ -@openzeppelin/=./node_modules/@openzeppelin/ +forge-std/=dependencies/forge-std-1.9.3/src/ +@openzeppelin/contracts/=dependencies/openzeppelin-5.0.2/contracts/ +@openzeppelin/contracts-upgradeable/=dependencies/openzeppelin-upgradeable-5.0.2/contracts/ \ No newline at end of file diff --git a/soldeer.lock b/soldeer.lock new file mode 100644 index 0000000..f3a18b5 --- /dev/null +++ b/soldeer.lock @@ -0,0 +1,27 @@ +[[dependencies]] +name = "contract-template" +version = "0.1.0" +source = "https://github.com/axieinfinity/contract-template/archive/refs/tags/release-v0.1.0.zip" +checksum = "56676e92b8825974abab8e381a98d0cc55da7057a18b3d325a13def760fa69d3" +integrity = "9fda9db2e8c24bb37378b1a14f41e1338967d101282bcdc0df9e98d7cf0edcda" + +[[dependencies]] +name = "forge-std" +version = "1.9.3" +source = "https://github.com/foundry-rs/forge-std/archive/refs/tags/v1.9.3.zip" +checksum = "cc61148ae1a47f2415956a95ada670501b10ebd7f65cc00eda6ea7025770f42b" +integrity = "d53734fbd7201cd9e44de730ae045ed8958dd2c7b9493c30b322f6e6c21ca8b2" + +[[dependencies]] +name = "openzeppelin" +version = "5.0.2" +source = "https://github.com/OpenZeppelin/openzeppelin-contracts/archive/refs/tags/v5.0.2.zip" +checksum = "ca49e0776066328da0087977864dfaef0d5e54a0ea9859236a7cd9ad02abb9e5" +integrity = "6f8fc028520b958667ef1e96460c64140f42fef60ff02bc63110780c557b7c7e" + +[[dependencies]] +name = "openzeppelin-upgradeable" +version = "5.0.2" +source = "https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/archive/refs/tags/v5.0.2.zip" +checksum = "4790d4f592cb46c868916ea50613997df1e28fd42b4d32e0999f0a4b9d1a7cf8" +integrity = "085dfc37ec3b96f0ea95ae99d8c044b0b407eab88a78d7cb552284bccf1130ba" diff --git a/src/ERC721Common.sol b/src/ERC721Common.sol index ca99a2a..3cd3bc5 100644 --- a/src/ERC721Common.sol +++ b/src/ERC721Common.sol @@ -1,20 +1,24 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.0; -import "./interfaces/IERC721State.sol"; +import "./ERC721PresetMinterPauserAutoIdCustomized.sol"; import "./interfaces/IERC721Common.sol"; +import "./interfaces/IERC721State.sol"; import "./refs/ERC721Nonce.sol"; -import "./ERC721PresetMinterPauserAutoIdCustomized.sol"; abstract contract ERC721Common is ERC721Nonce, ERC721PresetMinterPauserAutoIdCustomized, IERC721State, IERC721Common { - constructor(string memory name, string memory symbol, string memory baseTokenURI) - ERC721PresetMinterPauserAutoIdCustomized(name, symbol, baseTokenURI) - { } + constructor( + string memory name_, + string memory symbol_, + string memory baseTokenURI + ) ERC721PresetMinterPauserAutoIdCustomized(name_, symbol_, baseTokenURI) { } /// @inheritdoc IERC721State - function stateOf(uint256 _tokenId) external view virtual override returns (bytes memory) { - require(_exists(_tokenId), "ERC721Common: query for non-existent token"); - return abi.encodePacked(ownerOf(_tokenId), nonces[_tokenId], _tokenId); + function stateOf( + uint256 tokenId + ) external view virtual override returns (bytes memory) { + _requireOwned(tokenId); + return abi.encodePacked(ownerOf(tokenId), nonces[tokenId], tokenId); } /** @@ -33,26 +37,32 @@ abstract contract ERC721Common is ERC721Nonce, ERC721PresetMinterPauserAutoIdCus /** * @dev Override `IERC165-supportsInterface`. */ - function supportsInterface(bytes4 interfaceId) - public - view - virtual - override(ERC721, ERC721PresetMinterPauserAutoIdCustomized) - returns (bool) - { + function supportsInterface( + bytes4 interfaceId + ) public view virtual override(ERC721, ERC721PresetMinterPauserAutoIdCustomized) returns (bool) { return interfaceId == type(IERC721Common).interfaceId || interfaceId == type(IERC721State).interfaceId || super.supportsInterface(interfaceId); } /** - * @dev Override `ERC721PresetMinterPauserAutoIdCustomized-_beforeTokenTransfer`. + * @dev Override `ERC721PresetMinterPauserAutoIdCustomized-_update`. */ - function _beforeTokenTransfer(address _from, address _to, uint256 _firstTokenId, uint256 _batchSize) - internal - virtual - override(ERC721Nonce, ERC721PresetMinterPauserAutoIdCustomized) - { - super._beforeTokenTransfer(_from, _to, _firstTokenId, _batchSize); + function _update( + address to, + uint256 tokenId, + address auth + ) internal virtual override(ERC721Nonce, ERC721PresetMinterPauserAutoIdCustomized) returns (address from) { + return super._update(to, tokenId, auth); + } + + /** + * @dev See {ERC721-_increaseBalance}. + */ + function _increaseBalance( + address account, + uint128 amount + ) internal virtual override(ERC721, ERC721PresetMinterPauserAutoIdCustomized) { + super._increaseBalance(account, amount); } /** @@ -66,17 +76,14 @@ abstract contract ERC721Common is ERC721Nonce, ERC721PresetMinterPauserAutoIdCus * * - the caller must have the `MINTER_ROLE`. */ - function bulkMint(address[] calldata _recipients) - external - virtual - onlyRole(MINTER_ROLE) - returns (uint256[] memory _tokenIds) - { + function bulkMint( + address[] calldata _recipients + ) external virtual onlyRole(MINTER_ROLE) returns (uint256[] memory tokenIds) { require(_recipients.length > 0, "ERC721Common: invalid array lengths"); - _tokenIds = new uint256[](_recipients.length); + tokenIds = new uint256[](_recipients.length); for (uint256 _i = 0; _i < _recipients.length; _i++) { - _tokenIds[_i] = _mintFor(_recipients[_i]); + tokenIds[_i] = _mintFor(_recipients[_i]); } } } diff --git a/src/ERC721PresetMinterPauserAutoIdCustomized.sol b/src/ERC721PresetMinterPauserAutoIdCustomized.sol index 113aae7..cd5d046 100644 --- a/src/ERC721PresetMinterPauserAutoIdCustomized.sol +++ b/src/ERC721PresetMinterPauserAutoIdCustomized.sol @@ -1,14 +1,13 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; -import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol"; -import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Burnable.sol"; -import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Pausable.sol"; -import "@openzeppelin/contracts/access/AccessControlEnumerable.sol"; -import "@openzeppelin/contracts/utils/Context.sol"; -import "@openzeppelin/contracts/utils/Counters.sol"; -import "./interfaces/IERC721PresetMinterPauserAutoIdCustomized.sol"; +import { IERC721PresetMinterPauserAutoIdCustomized } from "./interfaces/IERC721PresetMinterPauserAutoIdCustomized.sol"; +import { AccessControlEnumerable } from "@openzeppelin/contracts/access/extensions/AccessControlEnumerable.sol"; +import { ERC721 } from "@openzeppelin/contracts/token/ERC721/ERC721.sol"; +import { ERC721Burnable } from "@openzeppelin/contracts/token/ERC721/extensions/ERC721Burnable.sol"; +import { ERC721Enumerable } from "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol"; +import { ERC721Pausable } from "@openzeppelin/contracts/token/ERC721/extensions/ERC721Pausable.sol"; +import { Context } from "@openzeppelin/contracts/utils/Context.sol"; /** * @dev ERC721PresetMinterPauserAutoId is a customized version of @@ -22,12 +21,10 @@ contract ERC721PresetMinterPauserAutoIdCustomized is ERC721Burnable, ERC721Pausable { - using Counters for Counters.Counter; - bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE"); bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE"); - Counters.Counter internal _tokenIdTracker; + uint256 internal _tokenIdTracker; string internal _baseTokenURI; @@ -41,15 +38,17 @@ contract ERC721PresetMinterPauserAutoIdCustomized is constructor(string memory name, string memory symbol, string memory baseTokenURI) ERC721(name, symbol) { _baseTokenURI = baseTokenURI; - _setupRole(DEFAULT_ADMIN_ROLE, _msgSender()); - _setupRole(MINTER_ROLE, _msgSender()); - _setupRole(PAUSER_ROLE, _msgSender()); + _grantRole(DEFAULT_ADMIN_ROLE, _msgSender()); + _grantRole(MINTER_ROLE, _msgSender()); + _grantRole(PAUSER_ROLE, _msgSender()); // Token id should start from 1. - _tokenIdTracker.increment(); + ++_tokenIdTracker; } - function setBaseURI(string memory baseTokenURI) external onlyRole(DEFAULT_ADMIN_ROLE) { + function setBaseURI( + string memory baseTokenURI + ) external onlyRole(DEFAULT_ADMIN_ROLE) { _baseTokenURI = baseTokenURI; } @@ -68,7 +67,9 @@ contract ERC721PresetMinterPauserAutoIdCustomized is * * - the caller must have the `MINTER_ROLE`. */ - function mint(address to) public virtual returns (uint256 _tokenId) { + function mint( + address to + ) public virtual returns (uint256 _tokenId) { require(hasRole(MINTER_ROLE, _msgSender()), "ERC721PresetMinterPauserAutoId: must have minter role to mint"); return _mintFor(to); } @@ -101,24 +102,27 @@ contract ERC721PresetMinterPauserAutoIdCustomized is _unpause(); } - function _beforeTokenTransfer(address from, address to, uint256 firstTokenId, uint256 batchSize) - internal - virtual - override(ERC721, ERC721Enumerable, ERC721Pausable) - { - super._beforeTokenTransfer(from, to, firstTokenId, batchSize); + function _update( + address to, + uint256 tokenId, + address auth + ) internal virtual override(ERC721, ERC721Pausable, ERC721Enumerable) returns (address from) { + return super._update(to, tokenId, auth); + } + + /** + * @dev See {ERC721-_increaseBalance}. + */ + function _increaseBalance(address account, uint128 amount) internal virtual override(ERC721, ERC721Enumerable) { + super._increaseBalance(account, amount); } /** * @dev See {IERC165-supportsInterface}. */ - function supportsInterface(bytes4 interfaceId) - public - view - virtual - override(AccessControlEnumerable, ERC721, ERC721Enumerable) - returns (bool) - { + function supportsInterface( + bytes4 interfaceId + ) public view virtual override(AccessControlEnumerable, ERC721, ERC721Enumerable) returns (bool) { return interfaceId == type(IERC721PresetMinterPauserAutoIdCustomized).interfaceId || super.supportsInterface(interfaceId); } @@ -129,11 +133,13 @@ contract ERC721PresetMinterPauserAutoIdCustomized is * See {ERC721-_mint}. * */ - function _mintFor(address to) internal virtual returns (uint256 _tokenId) { + function _mintFor( + address to + ) internal virtual returns (uint256 _tokenId) { // We cannot just use balanceOf to create the new tokenId because tokens // can be burned (destroyed), so we need a separate counter. - _tokenId = _tokenIdTracker.current(); + _tokenId = _tokenIdTracker; _mint(to, _tokenId); - _tokenIdTracker.increment(); + ++_tokenIdTracker; } } diff --git a/src/interfaces/IERC721Common.sol b/src/interfaces/IERC721Common.sol index d656010..0ec2f0a 100644 --- a/src/interfaces/IERC721Common.sol +++ b/src/interfaces/IERC721Common.sol @@ -13,5 +13,7 @@ interface IERC721Common { * * - the caller must have the `MINTER_ROLE`. */ - function bulkMint(address[] calldata recipients) external returns (uint256[] memory tokenIds); + function bulkMint( + address[] calldata recipients + ) external returns (uint256[] memory tokenIds); } diff --git a/src/interfaces/IERC721PresetMinterPauserAutoIdCustomized.sol b/src/interfaces/IERC721PresetMinterPauserAutoIdCustomized.sol index 59f4fba..9b66084 100644 --- a/src/interfaces/IERC721PresetMinterPauserAutoIdCustomized.sol +++ b/src/interfaces/IERC721PresetMinterPauserAutoIdCustomized.sol @@ -13,7 +13,9 @@ interface IERC721PresetMinterPauserAutoIdCustomized { * * - the caller must have the `MINTER_ROLE`. */ - function mint(address to) external returns (uint256 tokenId); + function mint( + address to + ) external returns (uint256 tokenId); /** * @dev Pauses all token transfers. diff --git a/src/interfaces/IERC721State.sol b/src/interfaces/IERC721State.sol index 7fb208c..48dffe6 100644 --- a/src/interfaces/IERC721State.sol +++ b/src/interfaces/IERC721State.sol @@ -10,7 +10,8 @@ interface IERC721State { * - The token exists. * * @notice The token state presents the properties of a token at a certain point in time, it should be unique. - * The token state helps other contracts can verify the token properties without getting and selecting properties from the base contract. + * The token state helps other contracts can verify the token properties without getting and selecting properties from + * the base contract. * * For example: * @@ -22,10 +23,12 @@ interface IERC721State { * } * * interface Exchange { - * // Buy NFT with the specificed state of `_tokenId`. + * // Buy NFT with the specified state of `_tokenId`. * function buy(uint256 _tokenId, uint256 _price, bytes calldata _kittyState) external; * } * ``` */ - function stateOf(uint256 _tokenId) external view returns (bytes memory); + function stateOf( + uint256 _tokenId + ) external view returns (bytes memory); } diff --git a/src/interfaces/launchpad/INFTLaunchpad.sol b/src/interfaces/launchpad/INFTLaunchpad.sol index eb9c9c7..e43fa89 100644 --- a/src/interfaces/launchpad/INFTLaunchpad.sol +++ b/src/interfaces/launchpad/INFTLaunchpad.sol @@ -24,7 +24,9 @@ interface INFTLaunchpad { * @return mintedTokenIds The token IDs of the minted NFTs. * @return mintedAmounts The minted amounts according to the `mintedTokenIds`. */ - function mintLaunchpad(address to, uint256 quantity, bytes calldata extraData) - external - returns (uint256[] memory mintedTokenIds, uint256[] memory mintedAmounts); + function mintLaunchpad( + address to, + uint256 quantity, + bytes calldata extraData + ) external returns (uint256[] memory mintedTokenIds, uint256[] memory mintedAmounts); } diff --git a/src/launchpad/NFTLaunchpadCommon.sol b/src/launchpad/NFTLaunchpadCommon.sol index f16f68e..e9458f1 100644 --- a/src/launchpad/NFTLaunchpadCommon.sol +++ b/src/launchpad/NFTLaunchpadCommon.sol @@ -7,7 +7,9 @@ import { INFTLaunchpad } from "../interfaces/launchpad/INFTLaunchpad.sol"; abstract contract NFTLaunchpadCommon is IERC165, INFTLaunchpad { /// @dev Returns whether the contract supports the NFT launchpad interface. - function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) { + function supportsInterface( + bytes4 interfaceId + ) public view virtual returns (bool) { return interfaceId == type(INFTLaunchpad).interfaceId; } } diff --git a/src/mock/SampleERC721.sol b/src/mock/SampleERC721.sol index 2eeda55..f0f7d5c 100644 --- a/src/mock/SampleERC721.sol +++ b/src/mock/SampleERC721.sol @@ -4,7 +4,9 @@ pragma solidity ^0.8.0; import "../ERC721Common.sol"; contract SampleERC721 is ERC721Common { - constructor(string memory name, string memory symbol, string memory baseTokenURI) - ERC721Common(name, symbol, baseTokenURI) - { } + constructor( + string memory name_, + string memory symbol_, + string memory baseTokenURI + ) ERC721Common(name_, symbol_, baseTokenURI) { } } diff --git a/src/mock/launchpad/SampleNFT1155Launchpad.sol b/src/mock/launchpad/SampleNFT1155Launchpad.sol index b070646..d62eb2a 100644 --- a/src/mock/launchpad/SampleNFT1155Launchpad.sol +++ b/src/mock/launchpad/SampleNFT1155Launchpad.sol @@ -1,24 +1,24 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.19; -import { ERC1155 } from "@openzeppelin/contracts/token/ERC1155/ERC1155.sol"; -import { AccessControl } from "@openzeppelin/contracts/access/AccessControl.sol"; import { NFTLaunchpadCommon } from "../../launchpad/NFTLaunchpadCommon.sol"; +import { AccessControl } from "@openzeppelin/contracts/access/AccessControl.sol"; +import { ERC1155 } from "@openzeppelin/contracts/token/ERC1155/ERC1155.sol"; contract SampleNFT1155Launchpad is ERC1155, AccessControl, NFTLaunchpadCommon { bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE"); constructor(address admin, address minter, string memory uri_) ERC1155(uri_) { - _setupRole(DEFAULT_ADMIN_ROLE, admin); - _setupRole(MINTER_ROLE, minter); + _grantRole(DEFAULT_ADMIN_ROLE, admin); + _grantRole(MINTER_ROLE, minter); } /// @dev Mint NFTs for the launchpad. - function mintLaunchpad(address to, uint256 quantity, bytes calldata /* extraData */ ) - external - onlyRole(MINTER_ROLE) - returns (uint256[] memory tokenIds, uint256[] memory amounts) - { + function mintLaunchpad( + address to, + uint256 quantity, + bytes calldata /* extraData */ + ) external onlyRole(MINTER_ROLE) returns (uint256[] memory tokenIds, uint256[] memory amounts) { _mint(to, 3, quantity, ""); _mint(to, 4, 1, ""); @@ -31,13 +31,9 @@ contract SampleNFT1155Launchpad is ERC1155, AccessControl, NFTLaunchpadCommon { amounts[1] = 1; } - function supportsInterface(bytes4 interfaceId) - public - view - virtual - override(ERC1155, AccessControl, NFTLaunchpadCommon) - returns (bool) - { + function supportsInterface( + bytes4 interfaceId + ) public view virtual override(ERC1155, AccessControl, NFTLaunchpadCommon) returns (bool) { return super.supportsInterface(interfaceId); } } diff --git a/src/mock/launchpad/SampleNFT721Launchpad.sol b/src/mock/launchpad/SampleNFT721Launchpad.sol index 6317bee..2d67a64 100644 --- a/src/mock/launchpad/SampleNFT721Launchpad.sol +++ b/src/mock/launchpad/SampleNFT721Launchpad.sol @@ -3,19 +3,19 @@ pragma solidity ^0.8.19; import { SampleERC721 } from "../SampleERC721.sol"; -import { SampleERC721 } from "../SampleERC721.sol"; import { ERC721Common } from "../../ERC721Common.sol"; import { NFTLaunchpadCommon } from "../../launchpad/NFTLaunchpadCommon.sol"; +import { SampleERC721 } from "../SampleERC721.sol"; contract SampleNFT721Launchpad is SampleERC721, NFTLaunchpadCommon { constructor(string memory name_, string memory symbol_, string memory uri_) SampleERC721(name_, symbol_, uri_) { } /// @dev Mint NFTs for the launchpad. - function mintLaunchpad(address to, uint256 quantity, bytes calldata /* extraData */ ) - external - onlyRole(MINTER_ROLE) - returns (uint256[] memory tokenIds, uint256[] memory amounts) - { + function mintLaunchpad( + address to, + uint256 quantity, + bytes calldata /* extraData */ + ) external onlyRole(MINTER_ROLE) returns (uint256[] memory tokenIds, uint256[] memory amounts) { tokenIds = new uint256[](quantity); amounts = new uint256[](quantity); for (uint256 i; i < quantity; ++i) { @@ -24,13 +24,9 @@ contract SampleNFT721Launchpad is SampleERC721, NFTLaunchpadCommon { } } - function supportsInterface(bytes4 interfaceId) - public - view - virtual - override(ERC721Common, NFTLaunchpadCommon) - returns (bool) - { + function supportsInterface( + bytes4 interfaceId + ) public view virtual override(ERC721Common, NFTLaunchpadCommon) returns (bool) { return super.supportsInterface(interfaceId); } } diff --git a/src/refs/ERC721Nonce.sol b/src/refs/ERC721Nonce.sol index 8f7c617..97998fd 100644 --- a/src/refs/ERC721Nonce.sol +++ b/src/refs/ERC721Nonce.sol @@ -5,7 +5,7 @@ import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; /** * @title ERC721Nonce - * @dev This contract provides a nonce that will be increased whenever the token is tranferred. + * @dev This contract provides a nonce that will be increased whenever the token is transferred. */ abstract contract ERC721Nonce is ERC721 { /// @dev Emitted when the token nonce is updated @@ -21,16 +21,10 @@ abstract contract ERC721Nonce is ERC721 { uint256[50] private ______gap; /** - * @dev Override `ERC721-_beforeTokenTransfer`. + * @dev Override `ERC721-_update`. */ - function _beforeTokenTransfer(address _from, address _to, uint256 _firstTokenId, uint256 _batchSize) - internal - virtual - override - { - for (uint256 _tokenId = _firstTokenId; _tokenId < _firstTokenId + _batchSize; _tokenId++) { - emit NonceUpdated(_tokenId, ++nonces[_tokenId]); - } - super._beforeTokenTransfer(_from, _to, _firstTokenId, _batchSize); + function _update(address to, uint256 tokenId, address auth) internal virtual override returns (address) { + emit NonceUpdated(tokenId, ++nonces[tokenId]); + return super._update(to, tokenId, auth); } } diff --git a/src/upgradeable/ERC721CommonUpgradeable.sol b/src/upgradeable/ERC721CommonUpgradeable.sol index 63a1cff..7a28783 100644 --- a/src/upgradeable/ERC721CommonUpgradeable.sol +++ b/src/upgradeable/ERC721CommonUpgradeable.sol @@ -1,12 +1,13 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.19; -import { ERC721Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol"; -import { IERC721State } from "../interfaces/IERC721State.sol"; import { IERC721Common } from "../interfaces/IERC721Common.sol"; -import { ERC721NonceUpgradeable } from "./refs/ERC721NonceUpgradeable.sol"; +import { IERC721State } from "../interfaces/IERC721State.sol"; + import { ERC721PresetMinterPauserAutoIdCustomizedUpgradeable } from "./ERC721PresetMinterPauserAutoIdCustomizedUpgradeable.sol"; +import { ERC721NonceUpgradeable } from "./refs/ERC721NonceUpgradeable.sol"; +import { ERC721Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol"; abstract contract ERC721CommonUpgradeable is ERC721NonceUpgradeable, @@ -29,18 +30,17 @@ abstract contract ERC721CommonUpgradeable is } /// @inheritdoc IERC721State - function stateOf(uint256 tokenId) external view virtual override returns (bytes memory) { - if (!_exists(tokenId)) revert ErrNonExistentToken(); + function stateOf( + uint256 tokenId + ) external view virtual override returns (bytes memory) { + _requireOwned(tokenId); return abi.encodePacked(ownerOf(tokenId), nonces(tokenId), tokenId); } /// @inheritdoc IERC721Common - function bulkMint(address[] calldata recipients) - external - virtual - onlyRole(MINTER_ROLE) - returns (uint256[] memory tokenIds) - { + function bulkMint( + address[] calldata recipients + ) external virtual onlyRole(MINTER_ROLE) returns (uint256[] memory tokenIds) { uint256 length = recipients.length; if (length == 0) revert ErrInvalidArrayLength(); tokenIds = new uint256[](length); @@ -53,13 +53,9 @@ abstract contract ERC721CommonUpgradeable is /** * @dev Override `IERC165-supportsInterface`. */ - function supportsInterface(bytes4 interfaceId) - public - view - virtual - override(ERC721Upgradeable, ERC721PresetMinterPauserAutoIdCustomizedUpgradeable) - returns (bool) - { + function supportsInterface( + bytes4 interfaceId + ) public view virtual override(ERC721Upgradeable, ERC721PresetMinterPauserAutoIdCustomizedUpgradeable) returns (bool) { return interfaceId == type(IERC721State).interfaceId || interfaceId == type(IERC721Common).interfaceId || super.supportsInterface(interfaceId); } @@ -78,13 +74,28 @@ abstract contract ERC721CommonUpgradeable is } /** - * @dev Override `ERC721PresetMinterPauserAutoIdCustomizedUpgradeable-_beforeTokenTransfer`. + * @dev Override `ERC721PresetMinterPauserAutoIdCustomizedUpgradeable-_update`. */ - function _beforeTokenTransfer(address from, address to, uint256 firstTokenId, uint256 batchSize) + function _update( + address to, + uint256 tokenId, + address auth + ) internal virtual override(ERC721NonceUpgradeable, ERC721PresetMinterPauserAutoIdCustomizedUpgradeable) + returns (address from) { - super._beforeTokenTransfer(from, to, firstTokenId, batchSize); + return super._update(to, tokenId, auth); + } + + /** + * @dev See {ERC721Upgradeable-_increaseBalance}. + */ + function _increaseBalance( + address account, + uint128 amount + ) internal virtual override(ERC721Upgradeable, ERC721PresetMinterPauserAutoIdCustomizedUpgradeable) { + super._increaseBalance(account, amount); } } diff --git a/src/upgradeable/ERC721PresetMinterPauserAutoIdCustomizedUpgradeable.sol b/src/upgradeable/ERC721PresetMinterPauserAutoIdCustomizedUpgradeable.sol index f248baf..1bea8ff 100644 --- a/src/upgradeable/ERC721PresetMinterPauserAutoIdCustomizedUpgradeable.sol +++ b/src/upgradeable/ERC721PresetMinterPauserAutoIdCustomizedUpgradeable.sol @@ -1,23 +1,23 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.19; +import { IERC721PresetMinterPauserAutoIdCustomized } from "../interfaces/IERC721PresetMinterPauserAutoIdCustomized.sol"; +import { AccessControlEnumerableUpgradeable } from + "@openzeppelin/contracts-upgradeable/access/extensions/AccessControlEnumerableUpgradeable.sol"; +import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import { ERC721Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol"; -import { ERC721EnumerableUpgradeable } from - "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721EnumerableUpgradeable.sol"; import { ERC721BurnableUpgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721BurnableUpgradeable.sol"; +import { ERC721EnumerableUpgradeable } from + "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721EnumerableUpgradeable.sol"; import { ERC721PausableUpgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721PausableUpgradeable.sol"; -import { AccessControlEnumerableUpgradeable } from - "@openzeppelin/contracts-upgradeable/access/AccessControlEnumerableUpgradeable.sol"; import { ContextUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol"; -import { CountersUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/CountersUpgradeable.sol"; -import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; -import { IERC721PresetMinterPauserAutoIdCustomized } from "../interfaces/IERC721PresetMinterPauserAutoIdCustomized.sol"; /** * @dev ERC721PresetMinterPauserAutoIdCustomizedUpgradeable is a customized version of - * openzeppelin-contracts-upgradeable/contracts/token/ERC721/presets/ERC721PresetMinterPauserAutoIdUpgradeable.sol to change the private counter and + * openzeppelin-contracts-upgradeable/contracts/token/ERC721/presets/ERC721PresetMinterPauserAutoIdUpgradeable.sol to + * change the private counter and * base token URI into internal, mainly to support the inherited contracts. */ contract ERC721PresetMinterPauserAutoIdCustomizedUpgradeable is @@ -31,12 +31,10 @@ contract ERC721PresetMinterPauserAutoIdCustomizedUpgradeable is { error ErrUnauthorizedAccount(address account, bytes32 neededRole); - using CountersUpgradeable for CountersUpgradeable.Counter; - bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE"); bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE"); - CountersUpgradeable.Counter private _tokenIdTracker; + uint256 private _tokenIdTracker; string private _baseTokenURI; @@ -57,31 +55,35 @@ contract ERC721PresetMinterPauserAutoIdCustomizedUpgradeable is * Token URIs will be autogenerated based on `baseURI` and their token IDs. * See {ERC721-tokenURI}. */ - function __ERC721PresetMinterPauserAutoId_init(string memory name, string memory symbol, string memory baseTokenURI) - internal - onlyInitializing - { + function __ERC721PresetMinterPauserAutoId_init( + string memory name, + string memory symbol, + string memory baseTokenURI + ) internal onlyInitializing { __ERC721_init_unchained(name, symbol); __Pausable_init_unchained(); __ERC721PresetMinterPauserAutoId_init_unchained(name, symbol, baseTokenURI); } - function __ERC721PresetMinterPauserAutoId_init_unchained(string memory, string memory, string memory baseTokenURI) - internal - onlyInitializing - { + function __ERC721PresetMinterPauserAutoId_init_unchained( + string memory, + string memory, + string memory baseTokenURI + ) internal onlyInitializing { _baseTokenURI = baseTokenURI; - _setupRole(DEFAULT_ADMIN_ROLE, _msgSender()); + _grantRole(DEFAULT_ADMIN_ROLE, _msgSender()); - _setupRole(MINTER_ROLE, _msgSender()); - _setupRole(PAUSER_ROLE, _msgSender()); + _grantRole(MINTER_ROLE, _msgSender()); + _grantRole(PAUSER_ROLE, _msgSender()); - _tokenIdTracker.increment(); + ++_tokenIdTracker; } /// @inheritdoc IERC721PresetMinterPauserAutoIdCustomized - function mint(address to) public virtual returns (uint256 tokenId) { + function mint( + address to + ) public virtual returns (uint256 tokenId) { address sender = _msgSender(); if (!hasRole(MINTER_ROLE, sender)) revert ErrUnauthorizedAccount(sender, MINTER_ROLE); @@ -107,7 +109,9 @@ contract ERC721PresetMinterPauserAutoIdCustomizedUpgradeable is /** * @dev See {IERC165-supportsInterface}. */ - function supportsInterface(bytes4 interfaceId) + function supportsInterface( + bytes4 interfaceId + ) public view virtual @@ -124,12 +128,14 @@ contract ERC721PresetMinterPauserAutoIdCustomizedUpgradeable is * See {ERC721Upgradeable-_mint}. * */ - function _mintFor(address to) internal virtual returns (uint256 _tokenId) { + function _mintFor( + address to + ) internal virtual returns (uint256 tokenId) { // We cannot just use balanceOf to create the new tokenId because tokens // can be burned (destroyed), so we need a separate counter. - _tokenId = _tokenIdTracker.current(); - _mint(to, _tokenId); - _tokenIdTracker.increment(); + tokenId = _tokenIdTracker; + _mint(to, tokenId); + ++_tokenIdTracker; } /** @@ -142,11 +148,29 @@ contract ERC721PresetMinterPauserAutoIdCustomizedUpgradeable is return _baseTokenURI; } - function _beforeTokenTransfer(address from, address to, uint256 firstTokenId, uint256 batchSize) + /** + * @dev See {ERC721Upgradeable-_update}. + */ + function _update( + address to, + uint256 tokenId, + address auth + ) internal virtual override(ERC721Upgradeable, ERC721EnumerableUpgradeable, ERC721PausableUpgradeable) + returns (address from) { - super._beforeTokenTransfer(from, to, firstTokenId, batchSize); + return super._update(to, tokenId, auth); + } + + /** + * @dev See {ERC721Upgradeable-_increaseBalance}. + */ + function _increaseBalance( + address account, + uint128 amount + ) internal virtual override(ERC721Upgradeable, ERC721EnumerableUpgradeable) { + super._increaseBalance(account, amount); } } diff --git a/src/upgradeable/refs/ERC721NonceUpgradeable.sol b/src/upgradeable/refs/ERC721NonceUpgradeable.sol index 85b8062..6f6a185 100644 --- a/src/upgradeable/refs/ERC721NonceUpgradeable.sol +++ b/src/upgradeable/refs/ERC721NonceUpgradeable.sol @@ -5,7 +5,7 @@ import { ERC721Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC /** * @title ERC721NonceUpgradeable - * @dev This contract provides a nonce that will be increased whenever the token is tranferred. + * @dev This contract provides a nonce that will be increased whenever the token is transferred. */ abstract contract ERC721NonceUpgradeable is ERC721Upgradeable { /// @dev Emitted when the token nonce is updated @@ -20,25 +20,19 @@ abstract contract ERC721NonceUpgradeable is ERC721Upgradeable { */ uint256[50] private __gap; - function nonces(uint256 tokenId) public view returns (uint256) { + function nonces( + uint256 tokenId + ) public view returns (uint256) { return _nonceOf[tokenId]; } /** - * @dev Override `ERC721Upgradeable-_beforeTokenTransfer`. + * @dev Override `ERC721Upgradeable-_update`. */ - function _beforeTokenTransfer(address from, address to, uint256 firstTokenId, uint256 batchSize) - internal - virtual - override - { - uint256 length = firstTokenId + batchSize; + function _update(address to, uint256 tokenId, address auth) internal virtual override returns (address from) { unchecked { - for (uint256 tokenId = firstTokenId; tokenId < length; ++tokenId) { - emit NonceUpdated(tokenId, ++_nonceOf[tokenId]); - } - - super._beforeTokenTransfer(from, to, firstTokenId, batchSize); + emit NonceUpdated(tokenId, ++_nonceOf[tokenId]); + return super._update(to, tokenId, auth); } } } diff --git a/test/foundry/SampleERC721.t.sol b/test/foundry/SampleERC721.t.sol index a2fddf4..0e4f06a 100644 --- a/test/foundry/SampleERC721.t.sol +++ b/test/foundry/SampleERC721.t.sol @@ -1,10 +1,11 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.13; -import "forge-std/Test.sol"; +import { ERC721Common, SampleERC721 } from "../../src/mock/SampleERC721.sol"; import { ERC721Nonce } from "../../src/refs/ERC721Nonce.sol"; import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; -import { SampleERC721, ERC721Common } from "../../src/mock/SampleERC721.sol"; +import "forge-std/Test.sol"; + import { IERC721Common } from "src/interfaces/IERC721Common.sol"; import { IERC721PresetMinterPauserAutoIdCustomized } from "src/interfaces/IERC721PresetMinterPauserAutoIdCustomized.sol"; import { IERC721State } from "src/interfaces/IERC721State.sol"; @@ -37,7 +38,9 @@ contract SampleERC721Test is Test { assertNotEq(_tokenId, 0); } - function testTokenURI(address _from) public virtual { + function testTokenURI( + address _from + ) public virtual { vm.assume(_from.code.length == 0 && _from != address(0)); (uint256 _tokenId,) = _mint(_from); assertEq(_token().tokenURI(_tokenId), string(abi.encodePacked(BASE_URI, _tokenId.toString()))); @@ -72,13 +75,15 @@ contract SampleERC721Test is Test { assertNotEq(_state0, _state1); } - function testSupportInterface() public { + function testSupportInterface() public view { assertEq(_token().supportsInterface(type(IERC721State).interfaceId), true); assertEq(_token().supportsInterface(type(IERC721PresetMinterPauserAutoIdCustomized).interfaceId), true); assertEq(_token().supportsInterface(type(IERC721Common).interfaceId), true); } - function _mint(address _user) internal virtual returns (uint256 _tokenId, uint256 _nonce) { + function _mint( + address _user + ) internal virtual returns (uint256 _tokenId, uint256 _nonce) { _token().mint(_user); uint256 _balance = _token().balanceOf(_user); return (_token().tokenOfOwnerByIndex(_user, _balance - 1), 1); diff --git a/test/foundry/SampleERC721Upgradeable.t.sol b/test/foundry/SampleERC721Upgradeable.t.sol index a8f9212..c3a385e 100644 --- a/test/foundry/SampleERC721Upgradeable.t.sol +++ b/test/foundry/SampleERC721Upgradeable.t.sol @@ -1,24 +1,25 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.13; -import "forge-std/Test.sol"; -import { ERC721NonceUpgradeable } from "src/upgradeable/refs/ERC721NonceUpgradeable.sol"; -import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; -import { TransparentUpgradeableProxy } from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import { IAccessControlEnumerable } from "@openzeppelin/contracts/access/extensions/IAccessControlEnumerable.sol"; + import { ProxyAdmin } from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; -import { IERC721Upgradeable } from "@openzeppelin/contracts-upgradeable/interfaces/IERC721Upgradeable.sol"; -import { IAccessControlEnumerableUpgradeable } from - "@openzeppelin/contracts-upgradeable/access/IAccessControlEnumerableUpgradeable.sol"; -import { IERC721EnumerableUpgradeable } from - "@openzeppelin/contracts-upgradeable/interfaces/IERC721EnumerableUpgradeable.sol"; -import { - SampleERC721Upgradeable, - ERC721CommonUpgradeable, - ERC721PresetMinterPauserAutoIdCustomizedUpgradeable -} from "src/mock/SampleERC721Upgradeable.sol"; +import { TransparentUpgradeableProxy } from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import { IERC721 } from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; +import { IERC721Enumerable } from "@openzeppelin/contracts/token/ERC721/extensions/IERC721Enumerable.sol"; + +import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; +import "forge-std/Test.sol"; + import { IERC721Common } from "src/interfaces/IERC721Common.sol"; import { IERC721PresetMinterPauserAutoIdCustomized } from "src/interfaces/IERC721PresetMinterPauserAutoIdCustomized.sol"; import { IERC721State } from "src/interfaces/IERC721State.sol"; +import { + ERC721CommonUpgradeable, + ERC721PresetMinterPauserAutoIdCustomizedUpgradeable, + SampleERC721Upgradeable +} from "src/mock/SampleERC721Upgradeable.sol"; +import { ERC721NonceUpgradeable } from "src/upgradeable/refs/ERC721NonceUpgradeable.sol"; contract SampleERC721Upgradeable_Test is Test { using Strings for uint256; @@ -34,7 +35,7 @@ contract SampleERC721Upgradeable_Test is Test { ERC721CommonUpgradeable internal _testToken; function setUp() public virtual { - _proxyAdmin = address(new ProxyAdmin()); + _proxyAdmin = address(new ProxyAdmin(address(this))); bytes memory initializeData = abi.encodeCall(ERC721PresetMinterPauserAutoIdCustomizedUpgradeable.initialize, (NAME, SYMBOL, BASE_URI)); @@ -56,7 +57,9 @@ contract SampleERC721Upgradeable_Test is Test { assertNotEq(tokenId, 0); } - function testTokenURI(address from) public virtual { + function testTokenURI( + address from + ) public virtual { vm.assume(from.code.length == 0 && from != address(0)); (uint256 tokenId,) = _mint(from); assertEq(_token().tokenURI(tokenId), string(abi.encodePacked(BASE_URI, tokenId.toString()))); @@ -91,16 +94,18 @@ contract SampleERC721Upgradeable_Test is Test { assertNotEq(_state0, _state1); } - function testSupportInterface() public { - assertEq(_token().supportsInterface(type(IERC721Upgradeable).interfaceId), true); - assertEq(_token().supportsInterface(type(IAccessControlEnumerableUpgradeable).interfaceId), true); - assertEq(_token().supportsInterface(type(IERC721EnumerableUpgradeable).interfaceId), true); + function testSupportInterface() public view { + assertEq(_token().supportsInterface(type(IERC721).interfaceId), true); + assertEq(_token().supportsInterface(type(IAccessControlEnumerable).interfaceId), true); + assertEq(_token().supportsInterface(type(IERC721Enumerable).interfaceId), true); assertEq(_token().supportsInterface(type(IERC721State).interfaceId), true); assertEq(_token().supportsInterface(type(IERC721PresetMinterPauserAutoIdCustomized).interfaceId), true); assertEq(_token().supportsInterface(type(IERC721Common).interfaceId), true); } - function _mint(address _user) internal virtual returns (uint256 tokenId, uint256 nonce) { + function _mint( + address _user + ) internal virtual returns (uint256 tokenId, uint256 nonce) { _token().mint(_user); uint256 _balance = _token().balanceOf(_user); return (_token().tokenOfOwnerByIndex(_user, _balance - 1), 1); From 839feec8bbf5672a92bea212003bc7805b3c27e3 Mon Sep 17 00:00:00 2001 From: TuDo1403 Date: Sun, 29 Sep 2024 18:00:08 +0700 Subject: [PATCH 2/8] refactor: use explicit import --- src/ERC721Common.sol | 9 +++++---- src/mock/SampleERC721.sol | 2 +- src/mock/SampleERC721Upgradeable.sol | 2 +- src/refs/ERC721Nonce.sol | 2 +- test/foundry/SampleERC721Upgradeable.t.sol | 9 ++++----- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/ERC721Common.sol b/src/ERC721Common.sol index 3cd3bc5..1851c53 100644 --- a/src/ERC721Common.sol +++ b/src/ERC721Common.sol @@ -1,10 +1,11 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.0; -import "./ERC721PresetMinterPauserAutoIdCustomized.sol"; -import "./interfaces/IERC721Common.sol"; -import "./interfaces/IERC721State.sol"; -import "./refs/ERC721Nonce.sol"; +import { ERC721PresetMinterPauserAutoIdCustomized } from "./ERC721PresetMinterPauserAutoIdCustomized.sol"; +import { IERC721Common } from "./interfaces/IERC721Common.sol"; +import { IERC721State } from "./interfaces/IERC721State.sol"; +import { ERC721Nonce } from "./refs/ERC721Nonce.sol"; +import { ERC721 } from "@openzeppelin/contracts/token/ERC721/ERC721.sol"; abstract contract ERC721Common is ERC721Nonce, ERC721PresetMinterPauserAutoIdCustomized, IERC721State, IERC721Common { constructor( diff --git a/src/mock/SampleERC721.sol b/src/mock/SampleERC721.sol index f0f7d5c..7ac74c4 100644 --- a/src/mock/SampleERC721.sol +++ b/src/mock/SampleERC721.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.0; -import "../ERC721Common.sol"; +import { ERC721Common } from "../ERC721Common.sol"; contract SampleERC721 is ERC721Common { constructor( diff --git a/src/mock/SampleERC721Upgradeable.sol b/src/mock/SampleERC721Upgradeable.sol index cdacd64..8c3e705 100644 --- a/src/mock/SampleERC721Upgradeable.sol +++ b/src/mock/SampleERC721Upgradeable.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.0; -import "../upgradeable/ERC721CommonUpgradeable.sol"; +import { ERC721CommonUpgradeable } from "../upgradeable/ERC721CommonUpgradeable.sol"; contract SampleERC721Upgradeable is ERC721CommonUpgradeable { } diff --git a/src/refs/ERC721Nonce.sol b/src/refs/ERC721Nonce.sol index 97998fd..7a3b763 100644 --- a/src/refs/ERC721Nonce.sol +++ b/src/refs/ERC721Nonce.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; +import { ERC721 } from "@openzeppelin/contracts/token/ERC721/ERC721.sol"; /** * @title ERC721Nonce diff --git a/test/foundry/SampleERC721Upgradeable.t.sol b/test/foundry/SampleERC721Upgradeable.t.sol index c3a385e..91698b8 100644 --- a/test/foundry/SampleERC721Upgradeable.t.sol +++ b/test/foundry/SampleERC721Upgradeable.t.sol @@ -14,11 +14,10 @@ import "forge-std/Test.sol"; import { IERC721Common } from "src/interfaces/IERC721Common.sol"; import { IERC721PresetMinterPauserAutoIdCustomized } from "src/interfaces/IERC721PresetMinterPauserAutoIdCustomized.sol"; import { IERC721State } from "src/interfaces/IERC721State.sol"; -import { - ERC721CommonUpgradeable, - ERC721PresetMinterPauserAutoIdCustomizedUpgradeable, - SampleERC721Upgradeable -} from "src/mock/SampleERC721Upgradeable.sol"; +import { ERC721CommonUpgradeable, SampleERC721Upgradeable } from "src/mock/SampleERC721Upgradeable.sol"; + +import { ERC721PresetMinterPauserAutoIdCustomizedUpgradeable } from + "src/upgradeable/ERC721PresetMinterPauserAutoIdCustomizedUpgradeable.sol"; import { ERC721NonceUpgradeable } from "src/upgradeable/refs/ERC721NonceUpgradeable.sol"; contract SampleERC721Upgradeable_Test is Test { From 1252459bf02f465f69b82d6a69bf1972892c4b0a Mon Sep 17 00:00:00 2001 From: TuDo1403 Date: Sun, 29 Sep 2024 18:03:14 +0700 Subject: [PATCH 3/8] chore: remove unused package --- foundry.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/foundry.toml b/foundry.toml index 0dfd096..29ea4dc 100644 --- a/foundry.toml +++ b/foundry.toml @@ -23,7 +23,6 @@ wrap_comments = true [dependencies] "openzeppelin" = { version = "5.0.2", url = "https://github.com/OpenZeppelin/openzeppelin-contracts/archive/refs/tags/v5.0.2.zip" } "openzeppelin-upgradeable" = { version = "5.0.2", url = "https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/archive/refs/tags/v5.0.2.zip" } -contract-template = { version = "0.1.0", url = "https://github.com/axieinfinity/contract-template/archive/refs/tags/release-v0.1.0.zip" } forge-std = { version = "1.9.3", url = "https://github.com/foundry-rs/forge-std/archive/refs/tags/v1.9.3.zip" } [soldeer] From e0ec6e80c7fec4777af24cc2e7b5b22d382a9171 Mon Sep 17 00:00:00 2001 From: huyhuynh3103 Date: Sun, 29 Dec 2024 15:34:54 +0700 Subject: [PATCH 4/8] feat: resolve conflicts --- .github/workflows/test.yml | 23 ++- src/ERC1155Common.sol | 145 ++++++++++++++++++ src/ERC721Common.sol | 10 +- src/interfaces/IERC1155Common.sol | 51 ++++++ src/interfaces/launchpad/INFTPresale.sol | 30 ++++ src/launchpad/NFTLaunchpadCommon.sol | 6 +- src/launchpad/NFTPresaleCommon.sol | 13 ++ src/mock/SampleERC1155.sol | 10 ++ src/mock/launchpad/SampleNFT1155Launchpad.sol | 26 ++-- src/mock/launchpad/SampleNFT721Launchpad.sol | 12 +- test/foundry/SampleERC1155.t.sol | 61 ++++++++ test/foundry/SampleNFT1155Launchpad.t.sol | 34 ++++ test/foundry/SampleNFT721Launchpad.t.sol | 31 ++++ 13 files changed, 412 insertions(+), 40 deletions(-) create mode 100644 src/ERC1155Common.sol create mode 100644 src/interfaces/IERC1155Common.sol create mode 100644 src/interfaces/launchpad/INFTPresale.sol create mode 100644 src/launchpad/NFTPresaleCommon.sol create mode 100644 src/mock/SampleERC1155.sol create mode 100644 test/foundry/SampleERC1155.t.sol create mode 100644 test/foundry/SampleNFT1155Launchpad.t.sol create mode 100644 test/foundry/SampleNFT721Launchpad.t.sol diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e230f90..36609de 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -5,14 +5,14 @@ on: branches: - main - dev - - 'feature/*' - - 'features/*' + - "feature/*" + - "features/*" pull_request: branches: - main - dev - - 'feature/*' - - 'features/*' + - "feature/*" + - "features/*" env: FOUNDRY_PROFILE: ci @@ -23,24 +23,21 @@ jobs: fail-fast: true name: Foundry project - runs-on: [self-hosted, dockerize] + runs-on: ubuntu-latest steps: - - id: 'gh-app' - name: 'Get Token' - uses: 'tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a' #v1.7.0 - with: - app_id: ${{ secrets.GH_APP_ID }} - private_key: ${{ secrets.GH_PRIVATE_KEY }} - - uses: actions/checkout@v4.1.1 with: submodules: recursive - token: ${{ steps.gh-app.outputs.token }} - name: Install Foundry uses: foundry-rs/foundry-toolchain@v1 with: version: nightly + + - name: Install dependencies + run: | + forge install + id: install - name: Run Forge build run: | diff --git a/src/ERC1155Common.sol b/src/ERC1155Common.sol new file mode 100644 index 0000000..f8e8c41 --- /dev/null +++ b/src/ERC1155Common.sol @@ -0,0 +1,145 @@ +// SPDX-License-Identifier: MIT +// Compatible with OpenZeppelin Contracts ^5.0.0 +pragma solidity ^0.8.20; + +import { AccessControlEnumerable } from "@openzeppelin/contracts/access/extensions/AccessControlEnumerable.sol"; +import { IERC165 } from "@openzeppelin/contracts/interfaces/IERC165.sol"; +import { ERC1155 } from "@openzeppelin/contracts/token/ERC1155/ERC1155.sol"; + +import { IERC1155Common } from "./interfaces/IERC1155Common.sol"; +import { ERC1155Burnable } from "@openzeppelin/contracts/token/ERC1155/extensions/ERC1155Burnable.sol"; +import { ERC1155Pausable } from "@openzeppelin/contracts/token/ERC1155/extensions/ERC1155Pausable.sol"; +import { ERC1155Supply } from "@openzeppelin/contracts/token/ERC1155/extensions/ERC1155Supply.sol"; +import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; + +contract ERC1155Common is + ERC1155, + AccessControlEnumerable, + ERC1155Pausable, + ERC1155Burnable, + ERC1155Supply, + IERC1155Common +{ + using Strings for uint256; + + bytes32 public constant URI_SETTER_ROLE = keccak256("URI_SETTER_ROLE"); + bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE"); + bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE"); + + string private _name; + string private _symbol; + + constructor(address admin, string memory name_, string memory symbol_, string memory uri_) ERC1155(uri_) { + _grantRole(DEFAULT_ADMIN_ROLE, admin); + _grantRole(PAUSER_ROLE, admin); + _grantRole(MINTER_ROLE, admin); + _grantRole(URI_SETTER_ROLE, admin); + + _name = name_; + _symbol = symbol_; + } + + /** + * @dev Set the URI for all token types. + * Requirements: + * - the caller must have the `URI_SETTER_ROLE`. + */ + function setURI( + string memory newURI + ) external onlyRole(URI_SETTER_ROLE) { + _setURI(newURI); + } + + /** + * @dev Pauses all token transfers. + * Requirements: + * - the caller must have the `PAUSER_ROLE`. + */ + function pause() external onlyRole(PAUSER_ROLE) { + _pause(); + } + + /** + * @dev Unpauses all token transfers. + * Requirements: + * - the caller must have the `PAUSER_ROLE`. + */ + function unpause() external onlyRole(PAUSER_ROLE) { + _unpause(); + } + + /// @inheritdoc IERC1155Common + function mint(address account, uint256 id, uint256 amount, bytes calldata data) public virtual onlyRole(MINTER_ROLE) { + _mint(account, id, amount, data); + } + + /// @inheritdoc IERC1155Common + function mintBatch( + address to, + uint256[] calldata ids, + uint256[] calldata amounts, + bytes calldata data + ) public virtual onlyRole(MINTER_ROLE) { + _mintBatch(to, ids, amounts, data); + } + + /** + * @dev Mint single token to multiple addresses. + * Requirements: + * - the caller must have the `MINTER_ROLE`. + */ + function bulkMint( + uint256 id, + address[] calldata tos, + uint256[] calldata amounts, + bytes[] calldata datas + ) public virtual onlyRole(MINTER_ROLE) { + uint256 length = tos.length; + require(length != 0 && length == amounts.length && length == datas.length, "ERC1155: invalid array lengths"); + + for (uint256 i; i < length; ++i) { + _mint(tos[i], id, amounts[i], datas[i]); + } + } + + /** + * @dev See {ERC1155-uri}. + */ + function uri( + uint256 tokenId + ) public view virtual override returns (string memory) { + string memory uri_ = super.uri(tokenId); + return string.concat(uri_, tokenId.toString()); + } + + /// @inheritdoc IERC1155Common + function name() public view virtual returns (string memory) { + return _name; + } + + /// @inheritdoc IERC1155Common + function symbol() public view virtual returns (string memory) { + return _symbol; + } + + /** + * @dev See {ERC165-supportsInterface}. + */ + function supportsInterface( + bytes4 interfaceId + ) public view virtual override(IERC165, ERC1155, AccessControlEnumerable) returns (bool) { + return interfaceId == type(IERC1155Common).interfaceId || super.supportsInterface(interfaceId); + } + + /** + * @dev See {ERC1155-_update}. + */ + function _update( + address from, + address to, + uint256[] memory ids, + uint256[] memory values + ) internal virtual override(ERC1155, ERC1155Supply, ERC1155Pausable) { + super._update(from, to, ids, values); + } +} diff --git a/src/ERC721Common.sol b/src/ERC721Common.sol index 1851c53..8cf5abb 100644 --- a/src/ERC721Common.sol +++ b/src/ERC721Common.sol @@ -7,12 +7,10 @@ import { IERC721State } from "./interfaces/IERC721State.sol"; import { ERC721Nonce } from "./refs/ERC721Nonce.sol"; import { ERC721 } from "@openzeppelin/contracts/token/ERC721/ERC721.sol"; -abstract contract ERC721Common is ERC721Nonce, ERC721PresetMinterPauserAutoIdCustomized, IERC721State, IERC721Common { - constructor( - string memory name_, - string memory symbol_, - string memory baseTokenURI - ) ERC721PresetMinterPauserAutoIdCustomized(name_, symbol_, baseTokenURI) { } +contract ERC721Common is ERC721Nonce, ERC721PresetMinterPauserAutoIdCustomized, IERC721State, IERC721Common { + constructor(string memory name, string memory symbol, string memory baseTokenURI) + ERC721PresetMinterPauserAutoIdCustomized(name, symbol, baseTokenURI) + { } /// @inheritdoc IERC721State function stateOf( diff --git a/src/interfaces/IERC1155Common.sol b/src/interfaces/IERC1155Common.sol new file mode 100644 index 0000000..1531282 --- /dev/null +++ b/src/interfaces/IERC1155Common.sol @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.0; + +import { IAccessControlEnumerable } from "@openzeppelin/contracts/access/extensions/IAccessControlEnumerable.sol"; +import { IERC1155 } from "@openzeppelin/contracts/token/ERC1155/IERC1155.sol"; + +interface IERC1155Common is IAccessControlEnumerable, IERC1155 { + /// @dev Return the name of the collection. + function name() external view returns (string memory); + + /// @dev Return the symbol of the collection. + function symbol() external view returns (string memory); + + /** + * @dev Mints a single ERC1155 token and assigns it to the specified address. + * + * Requirements: + * - the caller must have the `MINTER_ROLE`. + * + * @param to The address to which the minted token will be assigned. + * @param id The ID of the token to mint. + * @param amount The amount of tokens to mint. + * @param data Additional data with no specified format. + */ + function mint(address to, uint256 id, uint256 amount, bytes calldata data) external; + + /** + * @dev Mints multiple ERC1155 tokens and assigns them to the specified address. + * + * Requirements: + * - the caller must have the `MINTER_ROLE`. + * + * @param to The address to which the minted tokens will be assigned. + * @param ids The IDs of the tokens to mint. + * @param amounts The amounts of tokens to mint. + * @param data Additional data with no specified format. + */ + function mintBatch(address to, uint256[] calldata ids, uint256[] calldata amounts, bytes calldata data) external; + + /** + * @dev Mint single token to multiple addresses. + * Requirements: + * - the caller must have the `MINTER_ROLE`. + * + * @param id The ID of the token to mint. + * @param tos The addresses to which the minted tokens will be assigned. + * @param amounts The amounts of tokens to mint. + * @param datas Additional data with no specified format. + */ + function bulkMint(uint256 id, address[] calldata tos, uint256[] calldata amounts, bytes[] calldata datas) external; +} diff --git a/src/interfaces/launchpad/INFTPresale.sol b/src/interfaces/launchpad/INFTPresale.sol new file mode 100644 index 0000000..d739042 --- /dev/null +++ b/src/interfaces/launchpad/INFTPresale.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +/// @dev Interface for the NFT contract that compatible with the contract MavisPresale. +/// MUST be included ERC165 interface to support the detection of the contract's capabilities. +interface INFTPresale { + /** + * @dev Mint NFTs for the presale. + * + * Requirements: + * - The mintedTokenIds and mintedAmounts should have the same length. + * - The mintedTokenIds array should be unique. + * - For ERC721 NFTs, each minted token's quantity should always be 1. + * - For ERC1155 NFTs, each minted token's quantity should be actual minted amounts. + * - The total of minted amounts can be different from the input `quantity`. + * + * Examples: + * - ERC1155: If mintedTokenIds = [1, 2], then mintedAmounts = [10, 20] + * - ERC721: If mintedTokenIds = [1, 2], then mintedAmounts = [1, 1] + * + * @param to The address to mint the NFTs to. + * @param quantity The quantity of NFTs to mint. + * @param extraData The extra data for further customization. + * @return mintedTokenIds The token IDs of the minted NFTs. + * @return mintedAmounts The minted amounts according to the `mintedTokenIds`. + */ + function mintPresale(address to, uint256 quantity, bytes calldata extraData) + external + returns (uint256[] memory mintedTokenIds, uint256[] memory mintedAmounts); +} diff --git a/src/launchpad/NFTLaunchpadCommon.sol b/src/launchpad/NFTLaunchpadCommon.sol index e9458f1..08a0d73 100644 --- a/src/launchpad/NFTLaunchpadCommon.sol +++ b/src/launchpad/NFTLaunchpadCommon.sol @@ -7,9 +7,7 @@ import { INFTLaunchpad } from "../interfaces/launchpad/INFTLaunchpad.sol"; abstract contract NFTLaunchpadCommon is IERC165, INFTLaunchpad { /// @dev Returns whether the contract supports the NFT launchpad interface. - function supportsInterface( - bytes4 interfaceId - ) public view virtual returns (bool) { - return interfaceId == type(INFTLaunchpad).interfaceId; + function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) { + return interfaceId == type(INFTLaunchpad).interfaceId || interfaceId == type(IERC165).interfaceId; } } diff --git a/src/launchpad/NFTPresaleCommon.sol b/src/launchpad/NFTPresaleCommon.sol new file mode 100644 index 0000000..16ca8f2 --- /dev/null +++ b/src/launchpad/NFTPresaleCommon.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.22; + +import { IERC165 } from "@openzeppelin/contracts/interfaces/IERC165.sol"; + +import { INFTPresale } from "../interfaces/launchpad/INFTPresale.sol"; + +abstract contract NFTPresaleCommon is IERC165, INFTPresale { + /// @dev Returns whether the contract supports the NFT presale interface. + function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) { + return interfaceId == type(INFTPresale).interfaceId || interfaceId == type(IERC165).interfaceId; + } +} diff --git a/src/mock/SampleERC1155.sol b/src/mock/SampleERC1155.sol new file mode 100644 index 0000000..e919819 --- /dev/null +++ b/src/mock/SampleERC1155.sol @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.0; + +import "../ERC1155Common.sol"; + +contract SampleERC1155 is ERC1155Common { + constructor(address admin, string memory name, string memory symbol, string memory uri) + ERC1155Common(admin, name, symbol, uri) + { } +} diff --git a/src/mock/launchpad/SampleNFT1155Launchpad.sol b/src/mock/launchpad/SampleNFT1155Launchpad.sol index d62eb2a..76b2ba1 100644 --- a/src/mock/launchpad/SampleNFT1155Launchpad.sol +++ b/src/mock/launchpad/SampleNFT1155Launchpad.sol @@ -2,16 +2,12 @@ pragma solidity ^0.8.19; import { NFTLaunchpadCommon } from "../../launchpad/NFTLaunchpadCommon.sol"; -import { AccessControl } from "@openzeppelin/contracts/access/AccessControl.sol"; -import { ERC1155 } from "@openzeppelin/contracts/token/ERC1155/ERC1155.sol"; +import { SampleERC1155, ERC1155Common } from "../SampleERC1155.sol"; -contract SampleNFT1155Launchpad is ERC1155, AccessControl, NFTLaunchpadCommon { - bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE"); - - constructor(address admin, address minter, string memory uri_) ERC1155(uri_) { - _grantRole(DEFAULT_ADMIN_ROLE, admin); - _grantRole(MINTER_ROLE, minter); - } +contract SampleNFT1155Launchpad is SampleERC1155, NFTLaunchpadCommon { + constructor(address admin, string memory name, string memory symbol, string memory uri) + SampleERC1155(admin, name, symbol, uri) + { } /// @dev Mint NFTs for the launchpad. function mintLaunchpad( @@ -31,9 +27,13 @@ contract SampleNFT1155Launchpad is ERC1155, AccessControl, NFTLaunchpadCommon { amounts[1] = 1; } - function supportsInterface( - bytes4 interfaceId - ) public view virtual override(ERC1155, AccessControl, NFTLaunchpadCommon) returns (bool) { - return super.supportsInterface(interfaceId); + function supportsInterface(bytes4 interfaceId) + public + view + virtual + override(ERC1155Common, NFTLaunchpadCommon) + returns (bool) + { + return ERC1155Common.supportsInterface(interfaceId) || NFTLaunchpadCommon.supportsInterface(interfaceId); } } diff --git a/src/mock/launchpad/SampleNFT721Launchpad.sol b/src/mock/launchpad/SampleNFT721Launchpad.sol index 2d67a64..cf0b15a 100644 --- a/src/mock/launchpad/SampleNFT721Launchpad.sol +++ b/src/mock/launchpad/SampleNFT721Launchpad.sol @@ -24,9 +24,13 @@ contract SampleNFT721Launchpad is SampleERC721, NFTLaunchpadCommon { } } - function supportsInterface( - bytes4 interfaceId - ) public view virtual override(ERC721Common, NFTLaunchpadCommon) returns (bool) { - return super.supportsInterface(interfaceId); + function supportsInterface(bytes4 interfaceId) + public + view + virtual + override(ERC721Common, NFTLaunchpadCommon) + returns (bool) + { + return ERC721Common.supportsInterface(interfaceId) || NFTLaunchpadCommon.supportsInterface(interfaceId); } } diff --git a/test/foundry/SampleERC1155.t.sol b/test/foundry/SampleERC1155.t.sol new file mode 100644 index 0000000..e6a8564 --- /dev/null +++ b/test/foundry/SampleERC1155.t.sol @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import "forge-std/Test.sol"; +import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; +import { SampleERC1155, ERC1155Common } from "../../src/mock/SampleERC1155.sol"; +import { IERC165 } from "@openzeppelin/contracts/interfaces/IERC165.sol"; +import { IERC1155 } from "@openzeppelin/contracts/interfaces/IERC1155.sol"; +import { IAccessControlEnumerable } from "@openzeppelin/contracts/access/extensions/IAccessControlEnumerable.sol"; +import { IERC1155Common } from "src/interfaces/IERC1155Common.sol"; + +contract SampleERC1155Test is Test { + using Strings for uint256; + + string public constant NAME = "SampleERC1155"; + string public constant SYMBOL = "NFT1155"; + string public constant BASE_URI = "http://example.com/"; + address admin = makeAddr("admin"); + + ERC1155Common internal _t; + + function setUp() public virtual { + _t = new SampleERC1155(admin, NAME, SYMBOL, BASE_URI); + } + + function testName() public virtual { + assertEq(_token().name(), NAME); + } + + function testSymbol() public virtual { + assertEq(_token().symbol(), SYMBOL); + } + + function testURI(address _from) public virtual { + vm.assume(_from.code.length == 0 && _from != address(0)); + assertEq(_token().uri(uint256(50)), string(abi.encodePacked(BASE_URI, uint256(50).toString()))); + } + + function testMint() public virtual { + vm.startPrank(admin); + _token().mint(address(15), 15, 15, ""); + assertEq(_token().totalSupply(15), 15); + assertEq(_token().balanceOf(address(15), 15), 15); + + _token().mint(address(20), 15, 15, ""); + assertEq(_token().totalSupply(15), 30); + assertEq(_token().balanceOf(address(20), 15), 15); + vm.stopPrank(); + } + + function testSupportsInterface() public virtual { + assertEq(_token().supportsInterface(type(IERC1155).interfaceId), true); + assertEq(_token().supportsInterface(type(IAccessControlEnumerable).interfaceId), true); + assertEq(_token().supportsInterface(type(IERC1155Common).interfaceId), true); + assertEq(_token().supportsInterface(type(IERC165).interfaceId), true); + } + + function _token() internal view virtual returns (ERC1155Common) { + return _t; + } +} diff --git a/test/foundry/SampleNFT1155Launchpad.t.sol b/test/foundry/SampleNFT1155Launchpad.t.sol new file mode 100644 index 0000000..04c9392 --- /dev/null +++ b/test/foundry/SampleNFT1155Launchpad.t.sol @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import "forge-std/Test.sol"; +import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; +import { SampleNFT1155Launchpad, SampleERC1155 } from "../../src/mock/launchpad/SampleNFT1155Launchpad.sol"; +import { INFTLaunchpad } from "src/interfaces/launchpad/INFTLaunchpad.sol"; +import { IERC1155Common, IAccessControlEnumerable, IERC1155 } from "src/interfaces/IERC1155Common.sol"; + +contract SampleERC1155LaunchpadTest is Test { + using Strings for uint256; + + address admin = makeAddr("admin"); + string public constant NAME = "SampleERC721"; + string public constant SYMBOL = "NFT"; + string public constant BASE_URI = "http://example.com/"; + + SampleNFT1155Launchpad internal _t; + + function setUp() public virtual { + _t = new SampleNFT1155Launchpad(admin, NAME, SYMBOL, BASE_URI); + } + + function testSupportsInterface() public view { + assertEq(_token().supportsInterface(type(INFTLaunchpad).interfaceId), true); + assertEq(_token().supportsInterface(type(IERC1155Common).interfaceId), true); + assertEq(_token().supportsInterface(type(IAccessControlEnumerable).interfaceId), true); + assertEq(_token().supportsInterface(type(IERC1155).interfaceId), true); + } + + function _token() internal view virtual returns (SampleNFT1155Launchpad) { + return _t; + } +} diff --git a/test/foundry/SampleNFT721Launchpad.t.sol b/test/foundry/SampleNFT721Launchpad.t.sol new file mode 100644 index 0000000..4296cc2 --- /dev/null +++ b/test/foundry/SampleNFT721Launchpad.t.sol @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import "forge-std/Test.sol"; +import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; +import { SampleNFT721Launchpad } from "../../src/mock/launchpad/SampleNFT721Launchpad.sol"; +import { INFTLaunchpad } from "src/interfaces/launchpad/INFTLaunchpad.sol"; +import { IERC721Common } from "src/interfaces/IERC721Common.sol"; + +contract SampleNFT721LaunchpadTest is Test { + using Strings for uint256; + + string public constant NAME = "SampleERC721"; + string public constant SYMBOL = "NFT"; + string public constant BASE_URI = "http://example.com/"; + + SampleNFT721Launchpad internal _t; + + function setUp() public virtual { + _t = new SampleNFT721Launchpad(NAME, SYMBOL, BASE_URI); + } + + function testSupportInterface() public view { + assertEq(_token().supportsInterface(type(INFTLaunchpad).interfaceId), true); + assertEq(_token().supportsInterface(type(IERC721Common).interfaceId), true); + } + + function _token() internal view virtual returns (SampleNFT721Launchpad) { + return _t; + } +} From 5737e358b3144e05780a1f24bf66f2aeb6485abf Mon Sep 17 00:00:00 2001 From: huyhuynh3103 Date: Sun, 29 Dec 2024 16:08:28 +0700 Subject: [PATCH 5/8] feat: add sample presale contracts --- src/mock/launchpad/SampleNFT1155Presale.sol | 38 +++++++++++++++++++++ src/mock/launchpad/SampleNFT721Presale.sol | 32 +++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 src/mock/launchpad/SampleNFT1155Presale.sol create mode 100644 src/mock/launchpad/SampleNFT721Presale.sol diff --git a/src/mock/launchpad/SampleNFT1155Presale.sol b/src/mock/launchpad/SampleNFT1155Presale.sol new file mode 100644 index 0000000..a534469 --- /dev/null +++ b/src/mock/launchpad/SampleNFT1155Presale.sol @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +import { NFTPresaleCommon } from "../../launchpad/NFTPresaleCommon.sol"; +import { ERC1155Common, SampleERC1155 } from "../SampleERC1155.sol"; + +contract SampleNFT1155Presale is SampleERC1155, NFTPresaleCommon { + constructor( + address admin, + string memory name, + string memory symbol, + string memory uri + ) SampleERC1155(admin, name, symbol, uri) { } + + /// @dev Mint NFTs for the launchpad. + function mintPresale( + address to, + uint256 quantity, + bytes calldata /* extraData */ + ) external onlyRole(MINTER_ROLE) returns (uint256[] memory tokenIds, uint256[] memory amounts) { + _mint(to, 3, quantity, ""); + _mint(to, 4, 1, ""); + + tokenIds = new uint256[](2); + amounts = new uint256[](2); + tokenIds[0] = 3; + tokenIds[1] = 4; + + amounts[0] = quantity; + amounts[1] = 1; + } + + function supportsInterface( + bytes4 interfaceId + ) public view virtual override(ERC1155Common, NFTPresaleCommon) returns (bool) { + return ERC1155Common.supportsInterface(interfaceId) || NFTPresaleCommon.supportsInterface(interfaceId); + } +} diff --git a/src/mock/launchpad/SampleNFT721Presale.sol b/src/mock/launchpad/SampleNFT721Presale.sol new file mode 100644 index 0000000..45b4dee --- /dev/null +++ b/src/mock/launchpad/SampleNFT721Presale.sol @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +import { SampleERC721 } from "../SampleERC721.sol"; + +import { ERC721Common } from "../../ERC721Common.sol"; +import { NFTPresaleCommon } from "../../launchpad/NFTPresaleCommon.sol"; +import { SampleERC721 } from "../SampleERC721.sol"; + +contract SampleNFT721Presale is SampleERC721, NFTPresaleCommon { + constructor(string memory name_, string memory symbol_, string memory uri_) SampleERC721(name_, symbol_, uri_) { } + + /// @dev Mint NFTs for the presale. + function mintPresale( + address to, + uint256 quantity, + bytes calldata /* extraData */ + ) external onlyRole(MINTER_ROLE) returns (uint256[] memory tokenIds, uint256[] memory amounts) { + tokenIds = new uint256[](quantity); + amounts = new uint256[](quantity); + for (uint256 i; i < quantity; ++i) { + tokenIds[i] = _mintFor(to); + amounts[i] = 1; + } + } + + function supportsInterface( + bytes4 interfaceId + ) public view virtual override(ERC721Common, NFTPresaleCommon) returns (bool) { + return ERC721Common.supportsInterface(interfaceId) || NFTPresaleCommon.supportsInterface(interfaceId); + } +} From abf469744ab0f37ca815216a2afa7450c6097779 Mon Sep 17 00:00:00 2001 From: huyhuynh3103 Date: Sun, 29 Dec 2024 16:15:18 +0700 Subject: [PATCH 6/8] feat: rename sample contracts --- ...unchpad.sol => SampleERC1155Launchpad.sol} | 2 +- ...55Presale.sol => SampleERC1155Presale.sol} | 2 +- ...aunchpad.sol => SampleERC721Launchpad.sol} | 2 +- ...721Presale.sol => SampleERC721Presale.sol} | 2 +- ...pad.t.sol => SampleERC1155Launchpad.t.sol} | 8 ++--- test/foundry/SampleERC1155Presale.t.sol | 35 +++++++++++++++++++ ...hpad.t.sol => SampleERC721Launchpad.t.sol} | 10 +++--- test/foundry/SampleERC721Presale.t.sol | 32 +++++++++++++++++ 8 files changed, 80 insertions(+), 13 deletions(-) rename src/mock/launchpad/{SampleNFT1155Launchpad.sol => SampleERC1155Launchpad.sol} (94%) rename src/mock/launchpad/{SampleNFT1155Presale.sol => SampleERC1155Presale.sol} (94%) rename src/mock/launchpad/{SampleNFT721Launchpad.sol => SampleERC721Launchpad.sol} (94%) rename src/mock/launchpad/{SampleNFT721Presale.sol => SampleERC721Presale.sol} (94%) rename test/foundry/{SampleNFT1155Launchpad.t.sol => SampleERC1155Launchpad.t.sol} (79%) create mode 100644 test/foundry/SampleERC1155Presale.t.sol rename test/foundry/{SampleNFT721Launchpad.t.sol => SampleERC721Launchpad.t.sol} (72%) create mode 100644 test/foundry/SampleERC721Presale.t.sol diff --git a/src/mock/launchpad/SampleNFT1155Launchpad.sol b/src/mock/launchpad/SampleERC1155Launchpad.sol similarity index 94% rename from src/mock/launchpad/SampleNFT1155Launchpad.sol rename to src/mock/launchpad/SampleERC1155Launchpad.sol index 76b2ba1..77d7106 100644 --- a/src/mock/launchpad/SampleNFT1155Launchpad.sol +++ b/src/mock/launchpad/SampleERC1155Launchpad.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.19; import { NFTLaunchpadCommon } from "../../launchpad/NFTLaunchpadCommon.sol"; import { SampleERC1155, ERC1155Common } from "../SampleERC1155.sol"; -contract SampleNFT1155Launchpad is SampleERC1155, NFTLaunchpadCommon { +contract SampleERC1155Launchpad is SampleERC1155, NFTLaunchpadCommon { constructor(address admin, string memory name, string memory symbol, string memory uri) SampleERC1155(admin, name, symbol, uri) { } diff --git a/src/mock/launchpad/SampleNFT1155Presale.sol b/src/mock/launchpad/SampleERC1155Presale.sol similarity index 94% rename from src/mock/launchpad/SampleNFT1155Presale.sol rename to src/mock/launchpad/SampleERC1155Presale.sol index a534469..f7a9e19 100644 --- a/src/mock/launchpad/SampleNFT1155Presale.sol +++ b/src/mock/launchpad/SampleERC1155Presale.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.19; import { NFTPresaleCommon } from "../../launchpad/NFTPresaleCommon.sol"; import { ERC1155Common, SampleERC1155 } from "../SampleERC1155.sol"; -contract SampleNFT1155Presale is SampleERC1155, NFTPresaleCommon { +contract SampleERC1155Presale is SampleERC1155, NFTPresaleCommon { constructor( address admin, string memory name, diff --git a/src/mock/launchpad/SampleNFT721Launchpad.sol b/src/mock/launchpad/SampleERC721Launchpad.sol similarity index 94% rename from src/mock/launchpad/SampleNFT721Launchpad.sol rename to src/mock/launchpad/SampleERC721Launchpad.sol index cf0b15a..46a69d7 100644 --- a/src/mock/launchpad/SampleNFT721Launchpad.sol +++ b/src/mock/launchpad/SampleERC721Launchpad.sol @@ -7,7 +7,7 @@ import { ERC721Common } from "../../ERC721Common.sol"; import { NFTLaunchpadCommon } from "../../launchpad/NFTLaunchpadCommon.sol"; import { SampleERC721 } from "../SampleERC721.sol"; -contract SampleNFT721Launchpad is SampleERC721, NFTLaunchpadCommon { +contract SampleERC721Launchpad is SampleERC721, NFTLaunchpadCommon { constructor(string memory name_, string memory symbol_, string memory uri_) SampleERC721(name_, symbol_, uri_) { } /// @dev Mint NFTs for the launchpad. diff --git a/src/mock/launchpad/SampleNFT721Presale.sol b/src/mock/launchpad/SampleERC721Presale.sol similarity index 94% rename from src/mock/launchpad/SampleNFT721Presale.sol rename to src/mock/launchpad/SampleERC721Presale.sol index 45b4dee..9f59869 100644 --- a/src/mock/launchpad/SampleNFT721Presale.sol +++ b/src/mock/launchpad/SampleERC721Presale.sol @@ -7,7 +7,7 @@ import { ERC721Common } from "../../ERC721Common.sol"; import { NFTPresaleCommon } from "../../launchpad/NFTPresaleCommon.sol"; import { SampleERC721 } from "../SampleERC721.sol"; -contract SampleNFT721Presale is SampleERC721, NFTPresaleCommon { +contract SampleERC721Presale is SampleERC721, NFTPresaleCommon { constructor(string memory name_, string memory symbol_, string memory uri_) SampleERC721(name_, symbol_, uri_) { } /// @dev Mint NFTs for the presale. diff --git a/test/foundry/SampleNFT1155Launchpad.t.sol b/test/foundry/SampleERC1155Launchpad.t.sol similarity index 79% rename from test/foundry/SampleNFT1155Launchpad.t.sol rename to test/foundry/SampleERC1155Launchpad.t.sol index 04c9392..2f68425 100644 --- a/test/foundry/SampleNFT1155Launchpad.t.sol +++ b/test/foundry/SampleERC1155Launchpad.t.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.13; import "forge-std/Test.sol"; import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; -import { SampleNFT1155Launchpad, SampleERC1155 } from "../../src/mock/launchpad/SampleNFT1155Launchpad.sol"; +import { SampleERC1155Launchpad, SampleERC1155 } from "../../src/mock/launchpad/SampleERC1155Launchpad.sol"; import { INFTLaunchpad } from "src/interfaces/launchpad/INFTLaunchpad.sol"; import { IERC1155Common, IAccessControlEnumerable, IERC1155 } from "src/interfaces/IERC1155Common.sol"; @@ -15,10 +15,10 @@ contract SampleERC1155LaunchpadTest is Test { string public constant SYMBOL = "NFT"; string public constant BASE_URI = "http://example.com/"; - SampleNFT1155Launchpad internal _t; + SampleERC1155Launchpad internal _t; function setUp() public virtual { - _t = new SampleNFT1155Launchpad(admin, NAME, SYMBOL, BASE_URI); + _t = new SampleERC1155Launchpad(admin, NAME, SYMBOL, BASE_URI); } function testSupportsInterface() public view { @@ -28,7 +28,7 @@ contract SampleERC1155LaunchpadTest is Test { assertEq(_token().supportsInterface(type(IERC1155).interfaceId), true); } - function _token() internal view virtual returns (SampleNFT1155Launchpad) { + function _token() internal view virtual returns (SampleERC1155Launchpad) { return _t; } } diff --git a/test/foundry/SampleERC1155Presale.t.sol b/test/foundry/SampleERC1155Presale.t.sol new file mode 100644 index 0000000..866febc --- /dev/null +++ b/test/foundry/SampleERC1155Presale.t.sol @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import { SampleERC1155, SampleERC1155Presale } from "../../src/mock/launchpad/SampleERC1155Presale.sol"; +import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; +import "forge-std/Test.sol"; + +import { IAccessControlEnumerable, IERC1155, IERC1155Common } from "src/interfaces/IERC1155Common.sol"; +import { INFTPresale } from "src/interfaces/launchpad/INFTPresale.sol"; + +contract SampleERC1155PresaleTest is Test { + using Strings for uint256; + + address admin = makeAddr("admin"); + string public constant NAME = "SampleERC721"; + string public constant SYMBOL = "NFT"; + string public constant BASE_URI = "http://example.com/"; + + SampleERC1155Presale internal _t; + + function setUp() public virtual { + _t = new SampleERC1155Presale(admin, NAME, SYMBOL, BASE_URI); + } + + function testSupportsInterface() public view { + assertEq(_token().supportsInterface(type(INFTPresale).interfaceId), true); + assertEq(_token().supportsInterface(type(IERC1155Common).interfaceId), true); + assertEq(_token().supportsInterface(type(IAccessControlEnumerable).interfaceId), true); + assertEq(_token().supportsInterface(type(IERC1155).interfaceId), true); + } + + function _token() internal view virtual returns (SampleERC1155Presale) { + return _t; + } +} diff --git a/test/foundry/SampleNFT721Launchpad.t.sol b/test/foundry/SampleERC721Launchpad.t.sol similarity index 72% rename from test/foundry/SampleNFT721Launchpad.t.sol rename to test/foundry/SampleERC721Launchpad.t.sol index 4296cc2..7ed3cc4 100644 --- a/test/foundry/SampleNFT721Launchpad.t.sol +++ b/test/foundry/SampleERC721Launchpad.t.sol @@ -3,21 +3,21 @@ pragma solidity ^0.8.13; import "forge-std/Test.sol"; import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; -import { SampleNFT721Launchpad } from "../../src/mock/launchpad/SampleNFT721Launchpad.sol"; +import { SampleERC721Launchpad } from "../../src/mock/launchpad/SampleERC721Launchpad.sol"; import { INFTLaunchpad } from "src/interfaces/launchpad/INFTLaunchpad.sol"; import { IERC721Common } from "src/interfaces/IERC721Common.sol"; -contract SampleNFT721LaunchpadTest is Test { +contract SampleERC721LaunchpadTest is Test { using Strings for uint256; string public constant NAME = "SampleERC721"; string public constant SYMBOL = "NFT"; string public constant BASE_URI = "http://example.com/"; - SampleNFT721Launchpad internal _t; + SampleERC721Launchpad internal _t; function setUp() public virtual { - _t = new SampleNFT721Launchpad(NAME, SYMBOL, BASE_URI); + _t = new SampleERC721Launchpad(NAME, SYMBOL, BASE_URI); } function testSupportInterface() public view { @@ -25,7 +25,7 @@ contract SampleNFT721LaunchpadTest is Test { assertEq(_token().supportsInterface(type(IERC721Common).interfaceId), true); } - function _token() internal view virtual returns (SampleNFT721Launchpad) { + function _token() internal view virtual returns (SampleERC721Launchpad) { return _t; } } diff --git a/test/foundry/SampleERC721Presale.t.sol b/test/foundry/SampleERC721Presale.t.sol new file mode 100644 index 0000000..9ab4961 --- /dev/null +++ b/test/foundry/SampleERC721Presale.t.sol @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import { SampleERC721Presale } from "../../src/mock/launchpad/SampleERC721Presale.sol"; +import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; +import "forge-std/Test.sol"; + +import { IERC721Common } from "src/interfaces/IERC721Common.sol"; +import { INFTPresale } from "src/interfaces/launchpad/INFTPresale.sol"; + +contract SampleERC721PresaleTest is Test { + using Strings for uint256; + + string public constant NAME = "SampleERC721"; + string public constant SYMBOL = "NFT"; + string public constant BASE_URI = "http://example.com/"; + + SampleERC721Presale internal _t; + + function setUp() public virtual { + _t = new SampleERC721Presale(NAME, SYMBOL, BASE_URI); + } + + function testSupportInterface() public view { + assertEq(_token().supportsInterface(type(INFTPresale).interfaceId), true); + assertEq(_token().supportsInterface(type(IERC721Common).interfaceId), true); + } + + function _token() internal view virtual returns (SampleERC721Presale) { + return _t; + } +} From 5939e684251364f43150d77995c6e67168e77b03 Mon Sep 17 00:00:00 2001 From: huyhuynh3103 Date: Sun, 29 Dec 2024 16:17:08 +0700 Subject: [PATCH 7/8] fix: unit test on ci --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 36609de..b828d48 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -36,7 +36,7 @@ jobs: - name: Install dependencies run: | - forge install + forge soldeer update id: install - name: Run Forge build From f5db390c1723531d818d159966c53a8cce80f2d0 Mon Sep 17 00:00:00 2001 From: huyhuynh3103 Date: Sun, 29 Dec 2024 16:39:03 +0700 Subject: [PATCH 8/8] fix: remove underscore and useless _requireOwned --- src/ERC721Common.sol | 19 +++++++------ test/foundry/SampleNFT1155Launchpad.t.sol | 34 ----------------------- test/foundry/SampleNFT721Launchpad.t.sol | 31 --------------------- 3 files changed, 10 insertions(+), 74 deletions(-) delete mode 100644 test/foundry/SampleNFT1155Launchpad.t.sol delete mode 100644 test/foundry/SampleNFT721Launchpad.t.sol diff --git a/src/ERC721Common.sol b/src/ERC721Common.sol index 8cf5abb..1e06461 100644 --- a/src/ERC721Common.sol +++ b/src/ERC721Common.sol @@ -8,15 +8,16 @@ import { ERC721Nonce } from "./refs/ERC721Nonce.sol"; import { ERC721 } from "@openzeppelin/contracts/token/ERC721/ERC721.sol"; contract ERC721Common is ERC721Nonce, ERC721PresetMinterPauserAutoIdCustomized, IERC721State, IERC721Common { - constructor(string memory name, string memory symbol, string memory baseTokenURI) - ERC721PresetMinterPauserAutoIdCustomized(name, symbol, baseTokenURI) - { } + constructor( + string memory name, + string memory symbol, + string memory baseTokenURI + ) ERC721PresetMinterPauserAutoIdCustomized(name, symbol, baseTokenURI) { } /// @inheritdoc IERC721State function stateOf( uint256 tokenId ) external view virtual override returns (bytes memory) { - _requireOwned(tokenId); return abi.encodePacked(ownerOf(tokenId), nonces[tokenId], tokenId); } @@ -76,13 +77,13 @@ contract ERC721Common is ERC721Nonce, ERC721PresetMinterPauserAutoIdCustomized, * - the caller must have the `MINTER_ROLE`. */ function bulkMint( - address[] calldata _recipients + address[] calldata recipients ) external virtual onlyRole(MINTER_ROLE) returns (uint256[] memory tokenIds) { - require(_recipients.length > 0, "ERC721Common: invalid array lengths"); - tokenIds = new uint256[](_recipients.length); + require(recipients.length > 0, "ERC721Common: invalid array lengths"); + tokenIds = new uint256[](recipients.length); - for (uint256 _i = 0; _i < _recipients.length; _i++) { - tokenIds[_i] = _mintFor(_recipients[_i]); + for (uint256 i; i < recipients.length; i++) { + tokenIds[i] = _mintFor(recipients[i]); } } } diff --git a/test/foundry/SampleNFT1155Launchpad.t.sol b/test/foundry/SampleNFT1155Launchpad.t.sol deleted file mode 100644 index 2731fa1..0000000 --- a/test/foundry/SampleNFT1155Launchpad.t.sol +++ /dev/null @@ -1,34 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import "forge-std/Test.sol"; -import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; -import { SampleNFT1155Launchpad, SampleERC1155 } from "../../src/mock/launchpad/SampleNFT1155Launchpad.sol"; -import { INFTLaunchpad } from "src/interfaces/launchpad/INFTLaunchpad.sol"; -import { IERC1155Common, IAccessControlEnumerable, IERC1155 } from "src/interfaces/IERC1155Common.sol"; - -contract SampleERC1155LaunchpadTest is Test { - using Strings for uint256; - - address admin = makeAddr("admin"); - string public constant NAME = "SampleERC721"; - string public constant SYMBOL = "NFT"; - string public constant BASE_URI = "http://example.com/"; - - SampleNFT1155Launchpad internal _t; - - function setUp() public virtual { - _t = new SampleNFT1155Launchpad(admin, NAME, SYMBOL, BASE_URI); - } - - function testSupportsInterface() public { - assertEq(_token().supportsInterface(type(INFTLaunchpad).interfaceId), true); - assertEq(_token().supportsInterface(type(IERC1155Common).interfaceId), true); - assertEq(_token().supportsInterface(type(IAccessControlEnumerable).interfaceId), true); - assertEq(_token().supportsInterface(type(IERC1155).interfaceId), true); - } - - function _token() internal view virtual returns (SampleNFT1155Launchpad) { - return _t; - } -} diff --git a/test/foundry/SampleNFT721Launchpad.t.sol b/test/foundry/SampleNFT721Launchpad.t.sol deleted file mode 100644 index 1d5d0c0..0000000 --- a/test/foundry/SampleNFT721Launchpad.t.sol +++ /dev/null @@ -1,31 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import "forge-std/Test.sol"; -import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; -import { SampleNFT721Launchpad } from "../../src/mock/launchpad/SampleNFT721Launchpad.sol"; -import { INFTLaunchpad } from "src/interfaces/launchpad/INFTLaunchpad.sol"; -import { IERC721Common } from "src/interfaces/IERC721Common.sol"; - -contract SampleNFT721LaunchpadTest is Test { - using Strings for uint256; - - string public constant NAME = "SampleERC721"; - string public constant SYMBOL = "NFT"; - string public constant BASE_URI = "http://example.com/"; - - SampleNFT721Launchpad internal _t; - - function setUp() public virtual { - _t = new SampleNFT721Launchpad(NAME, SYMBOL, BASE_URI); - } - - function testSupportInterface() public { - assertEq(_token().supportsInterface(type(INFTLaunchpad).interfaceId), true); - assertEq(_token().supportsInterface(type(IERC721Common).interfaceId), true); - } - - function _token() internal view virtual returns (SampleNFT721Launchpad) { - return _t; - } -}