From 39844bc9e58412aeac50de265644126964ad372b Mon Sep 17 00:00:00 2001 From: WhiteOakKong Date: Mon, 16 Oct 2023 22:40:32 -0400 Subject: [PATCH 01/14] init file structure and trees --- .../open-edition/_beforeTokenTransfers/_beforeTokenTransfers.tree | 0 src/test/open-edition/_canSetFunctions/_canSetFunctions.tree | 0 .../open-edition/_collectPriceOnClaim/_collectPriceOnClaim.tree | 0 .../_transferTokensOnClaim/_transferTokensOnClaim.tree | 0 src/test/open-edition/initialize/initialize.tree | 0 src/test/open-edition/misc/misc.tree | 0 6 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/test/open-edition/_beforeTokenTransfers/_beforeTokenTransfers.tree create mode 100644 src/test/open-edition/_canSetFunctions/_canSetFunctions.tree create mode 100644 src/test/open-edition/_collectPriceOnClaim/_collectPriceOnClaim.tree create mode 100644 src/test/open-edition/_transferTokensOnClaim/_transferTokensOnClaim.tree create mode 100644 src/test/open-edition/initialize/initialize.tree create mode 100644 src/test/open-edition/misc/misc.tree diff --git a/src/test/open-edition/_beforeTokenTransfers/_beforeTokenTransfers.tree b/src/test/open-edition/_beforeTokenTransfers/_beforeTokenTransfers.tree new file mode 100644 index 000000000..e69de29bb diff --git a/src/test/open-edition/_canSetFunctions/_canSetFunctions.tree b/src/test/open-edition/_canSetFunctions/_canSetFunctions.tree new file mode 100644 index 000000000..e69de29bb diff --git a/src/test/open-edition/_collectPriceOnClaim/_collectPriceOnClaim.tree b/src/test/open-edition/_collectPriceOnClaim/_collectPriceOnClaim.tree new file mode 100644 index 000000000..e69de29bb diff --git a/src/test/open-edition/_transferTokensOnClaim/_transferTokensOnClaim.tree b/src/test/open-edition/_transferTokensOnClaim/_transferTokensOnClaim.tree new file mode 100644 index 000000000..e69de29bb diff --git a/src/test/open-edition/initialize/initialize.tree b/src/test/open-edition/initialize/initialize.tree new file mode 100644 index 000000000..e69de29bb diff --git a/src/test/open-edition/misc/misc.tree b/src/test/open-edition/misc/misc.tree new file mode 100644 index 000000000..e69de29bb From 63d084bc7491cff9c00982ad02263da190902916 Mon Sep 17 00:00:00 2001 From: WhiteOakKong Date: Tue, 17 Oct 2023 09:58:18 -0400 Subject: [PATCH 02/14] tree: initialize --- .../open-edition/initialize/initialize.tree | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/test/open-edition/initialize/initialize.tree b/src/test/open-edition/initialize/initialize.tree index e69de29bb..1920a2338 100644 --- a/src/test/open-edition/initialize/initialize.tree +++ b/src/test/open-edition/initialize/initialize.tree @@ -0,0 +1,39 @@ +function initialize( + address _defaultAdmin, + string memory _name, + string memory _symbol, + string memory _contractURI, + address[] memory _trustedForwarders, + address _saleRecipient, + address _royaltyRecipient, + uint128 _royaltyBps, + uint128 _platformFeeBps, + address _platformFeeRecipient +) +├── when _trustedForwarders.length > 0 +│ └── it should set _trustedForwarder[_trustedForwarders[i]] as true for each address in _trustedForwarders +├── it should set _name as the value provided in _name +├── it should set _symbol as the value provided in _symbol +├── it should set _currentIndex as 0 +├── it should set contractURI as _contractURI +├── it should emit ContractURIUpdated with the parameters: prevURI, _uri +├── it should set _defaultAdmin as the owner of the contract +├── it should emit OwnerUpdated with the parameters: _prevOwner, _defaultAdmin +├── it should assign the role DEFAULT_ADMIN_ROLE to _defaultAdmin +├── it should emit RoleGranted with the parameters: DEFAULT_ADMIN_ROLE, _defaultAdmin, msg.sender +├── it should assign the role _minterRole to _defaultAdmin +├── it should emit RoleGranted with the parameters: _minterRole, _defaultAdmin, msg.sender +├── it should assign the role _transferRole to _defaultAdmin +├── it should emit RoleGranted with the parameters: _transferRole, _defaultAdmin, msg.sender +├── it should assign the role _transferRole to address(0) +├── it should emit RoleGranted with the parameters: _transferRole, address(0), msg.sender +├── when _royaltyBps is greater than 10_000 +│ └── it should revert +├── when _royaltyBps is less than or equal to 10_000 +│ ├── it should set royaltyRecipient as _royaltyRecipient +│ ├── it should set royaltyBps as uint16(_royaltyBps) +│ └── it should emit DefaultRoyalty with the parameters _royaltyRecipient, _royaltyBps +├── it should set recipient as _primarySaleRecipient +├── it should emit PrimarySaleRecipientUpdated with the parameters _primarySaleRecipient +├── it should set transferRole as keccak256("TRANSFER_ROLE") +└── it should set minterRole as keccak256("MINTER_ROLE") From 8e1f067b0607950227e4ca51f166279d21eac0b6 Mon Sep 17 00:00:00 2001 From: WhiteOakKong Date: Tue, 17 Oct 2023 10:09:57 -0400 Subject: [PATCH 03/14] tree misc --- src/test/open-edition/misc/misc.tree | 31 ++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/test/open-edition/misc/misc.tree b/src/test/open-edition/misc/misc.tree index e69de29bb..9bf5a4c35 100644 --- a/src/test/open-edition/misc/misc.tree +++ b/src/test/open-edition/misc/misc.tree @@ -0,0 +1,31 @@ +function tokenURI(uint256 _tokenId) +├── when _tokenId does not exist +│ └── it should revert +└── when _tokenID does exist + └── it should return the shared metadata + +function supportsInterface(bytes4 interfaceId) + +function _startTokenId() +└── it should return 1 + +function startTokenId() +└── it should return _startTokenId (1) + +function totalminted() +└── it should return the total number of NFTs minted + +function nextTokenIdToMint() +└── it should return the next token ID to mint (last minted + 1) + +function nextTokenIdToClaim() +└── it should return the next token ID to mint (last minted + 1) + +function burn(uint256 tokenId) +├── when caller is not the owner of tokenId +│ ├── when caller is not an approved operator of the owner of tokenId +│ │ └── it should revert +│ └── when caller is an approved operator of the owner of tokenId +│ └── it should burn the token +└── when caller is the owner of tokenId + └── it should burn the token \ No newline at end of file From 740f2683cdc347f6e7dda0122ff06269ede99806 Mon Sep 17 00:00:00 2001 From: WhiteOakKong Date: Tue, 17 Oct 2023 10:16:25 -0400 Subject: [PATCH 04/14] tree _transferTokensOnClaim --- .../_transferTokensOnClaim/_transferTokensOnClaim.tree | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/test/open-edition/_transferTokensOnClaim/_transferTokensOnClaim.tree b/src/test/open-edition/_transferTokensOnClaim/_transferTokensOnClaim.tree index e69de29bb..9bddf5d09 100644 --- a/src/test/open-edition/_transferTokensOnClaim/_transferTokensOnClaim.tree +++ b/src/test/open-edition/_transferTokensOnClaim/_transferTokensOnClaim.tree @@ -0,0 +1,8 @@ +function _transferTokensOnClaim(address _to, uint256 _quantityBeingClaimed) +# when _to is a smart contract +## when _to has not implemented ERC721Receiver +### it should revert +## when _to has implemented ERC721Receiver +### it should mint _quantityBeingClaimed tokens to _to +# when _to is an EOA +## it should mint _quantityBeingClaimed tokens to _to \ No newline at end of file From 0eebb4a5e54aa4011575dc8ac34d3245f5bd02a3 Mon Sep 17 00:00:00 2001 From: WhiteOakKong Date: Tue, 17 Oct 2023 10:21:27 -0400 Subject: [PATCH 05/14] tree _beforeTokenTransfer --- .../_beforeTokenTransfers/_beforeTokenTransfers.tree | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/test/open-edition/_beforeTokenTransfers/_beforeTokenTransfers.tree b/src/test/open-edition/_beforeTokenTransfers/_beforeTokenTransfers.tree index e69de29bb..15d2e0f60 100644 --- a/src/test/open-edition/_beforeTokenTransfers/_beforeTokenTransfers.tree +++ b/src/test/open-edition/_beforeTokenTransfers/_beforeTokenTransfers.tree @@ -0,0 +1,12 @@ +function _beforeTokenTransfers( + address from, + address to, + uint256 startTokenId_, + uint256 quantity +) +└── when address(0) does not have the transfer role + └── when from does not equal address(0) + └── when to does not equal address(0) + └── when from does not have the transfer role + └── when to does not have the transfer role + └── it should revert From eb35a46674c68e169c9cf9a1ff1fbe493e817199 Mon Sep 17 00:00:00 2001 From: WhiteOakKong Date: Tue, 17 Oct 2023 10:25:20 -0400 Subject: [PATCH 06/14] tree canSet --- .../_canSetFunctions/_canSetFunctions.tree | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/test/open-edition/_canSetFunctions/_canSetFunctions.tree b/src/test/open-edition/_canSetFunctions/_canSetFunctions.tree index e69de29bb..6f08cd7ba 100644 --- a/src/test/open-edition/_canSetFunctions/_canSetFunctions.tree +++ b/src/test/open-edition/_canSetFunctions/_canSetFunctions.tree @@ -0,0 +1,39 @@ +function _canSetPrimarySaleRecipient() +├── when _msgSender has DEFAULT_ADMIN_ROLE +│ └── it should return true +└── when _msgSender does not have DEFAULT_ADMIN_ROLE + └── it should return false + +function _canSetOwner() +├── when _msgSender has DEFAULT_ADMIN_ROLE +│ └── it should return true +└── when _msgSender does not have DEFAULT_ADMIN_ROLE + └── it should return false + + +function _canSetRoyaltyInfo() +├── when _msgSender has DEFAULT_ADMIN_ROLE +│ └── it should return true +└── when _msgSender does not have DEFAULT_ADMIN_ROLE + └── it should return false + + +function _canSetContractURI() +├── when _msgSender has DEFAULT_ADMIN_ROLE +│ └── it should return true +└── when _msgSender does not have DEFAULT_ADMIN_ROLE + └── it should return false + + +function _canSetClaimConditions() +├── when _msgSender has DEFAULT_ADMIN_ROLE +│ └── it should return true +└── when _msgSender does not have DEFAULT_ADMIN_ROLE + └── it should return false + + +function _canSetSharedMetadata() +├── when _msgSender has minter role +│ └── it should return true +└── when _msgSender does not have minter role + └── it should return false From f96ad8279841dd6987823055ca1271a289aa9d6a Mon Sep 17 00:00:00 2001 From: WhiteOakKong Date: Tue, 17 Oct 2023 10:27:19 -0400 Subject: [PATCH 07/14] tree _collectPriceOnClaim --- .../_collectPriceOnClaim.tree | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/src/test/open-edition/_collectPriceOnClaim/_collectPriceOnClaim.tree b/src/test/open-edition/_collectPriceOnClaim/_collectPriceOnClaim.tree index e69de29bb..76e1f08a0 100644 --- a/src/test/open-edition/_collectPriceOnClaim/_collectPriceOnClaim.tree +++ b/src/test/open-edition/_collectPriceOnClaim/_collectPriceOnClaim.tree @@ -0,0 +1,43 @@ +function _collectPriceOnClaim( + address _primarySaleRecipient, + uint256 _quantityToClaim, + address _currency, + uint256 _pricePerToken +) +├── when _pricePerToken is equal to zero +│ ├── when msg.value does not equal to zero +│ │ └── it should revert +│ └── when msg.value is equal to zero +│ └── it should return +└── when _pricePerToken is not equal to zero + ├── when _primarySaleRecipient is equal to address(0) + │ ├── when saleRecipient for _tokenId is equal to address(0) + │ │ ├── when currency is native token + │ │ │ ├── when msg.value does not equal totalPrice + │ │ │ │ └── it should revert + │ │ │ └── when msg.value does equal totalPrice + │ │ │ ├── it should transfer platformFees to platformFeeRecipient in native token + │ │ │ └── it should transfer totalPrice - platformFees to primarySaleRecipient in native token + │ │ └── when currency is not native token + │ │ ├── it should transfer platformFees to platformFeeRecipient in _currency token + │ │ └── it should transfer totalPrice - platformFees to primarySaleRecipient in _currency token + │ └── when salerecipient for _tokenId is not equal to address(0) + │ ├── when currency is native token + │ │ ├── when msg.value does not equal totalPrice + │ │ │ └── it should revert + │ │ └── when msg.value does equal totalPrice + │ │ ├── it should transfer platformFees to platformFeeRecipient in native token + │ │ └── it should transfer totalPrice - platformFees to saleRecipient for _tokenId in native token + │ └── when currency is not native token + │ ├── it should transfer platformFees to platformFeeRecipient in _currency token + │ └── it should transfer totalPrice - platformFees to saleRecipient for _tokenId in _currency token + └── when _primarySaleRecipient is not equal to address(0) + ├── when currency is native token + │ ├── when msg.value does not equal totalPrice + │ │ └── it should revert + │ └── when msg.value does equal totalPrice + │ ├── it should transfer platformFees to platformFeeRecipient in native token + │ └── it should transfer totalPrice - platformFees to _primarySaleRecipient in native token + └── when currency is not native token + ├── it should transfer platformFees to platformFeeRecipient in _currency token + └── it should transfer totalPrice - platformFees to _primarySaleRecipient in _currency token \ No newline at end of file From 715cdb283bc155627871f10773229dbb05fa0eee Mon Sep 17 00:00:00 2001 From: WhiteOakKong Date: Wed, 18 Oct 2023 16:09:08 -0500 Subject: [PATCH 08/14] test: initialize --- .../open-edition/initialize/initialize.t.sol | 233 ++++++++++++++++++ .../open-edition/initialize/initialize.tree | 48 ++-- 2 files changed, 257 insertions(+), 24 deletions(-) create mode 100644 src/test/open-edition/initialize/initialize.t.sol diff --git a/src/test/open-edition/initialize/initialize.t.sol b/src/test/open-edition/initialize/initialize.t.sol new file mode 100644 index 000000000..270118999 --- /dev/null +++ b/src/test/open-edition/initialize/initialize.t.sol @@ -0,0 +1,233 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.0; + +import { OpenEditionERC721 } from "contracts/prebuilts/open-edition/OpenEditionERC721.sol"; +import { TWProxy } from "contracts/infra/TWProxy.sol"; + +// Test imports +import "src/test/utils/BaseTest.sol"; + +contract OpenEditionERC721Test_initialize is BaseTest { + event ContractURIUpdated(string prevURI, string newURI); + event OwnerUpdated(address indexed prevOwner, address indexed newOwner); + event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); + event PrimarySaleRecipientUpdated(address indexed recipient); + + OpenEditionERC721 public openEdition; + + address private openEditionImpl; + + function deployOpenEdition( + address _defaultAdmin, + string memory _name, + string memory _symbol, + string memory _contractURI, + address[] memory _trustedForwarders, + address _saleRecipient, + address _royaltyRecipient, + uint128 _royaltyBps, + address _imp + ) public { + vm.prank(deployer); + openEdition = OpenEditionERC721( + address( + new TWProxy( + _imp, + abi.encodeCall( + OpenEditionERC721.initialize, + ( + _defaultAdmin, + _name, + _symbol, + _contractURI, + _trustedForwarders, + _saleRecipient, + _royaltyRecipient, + _royaltyBps + ) + ) + ) + ) + ); + } + + function setUp() public override { + super.setUp(); + openEditionImpl = address(new OpenEditionERC721()); + } + + /*/////////////////////////////////////////////////////////////// + Unit tests: initialize + //////////////////////////////////////////////////////////////*/ + + function test_state() public { + deployOpenEdition( + deployer, + NAME, + SYMBOL, + CONTRACT_URI, + forwarders(), + saleRecipient, + royaltyRecipient, + royaltyBps, + openEditionImpl + ); + + address _saleRecipient = openEdition.primarySaleRecipient(); + (address _royaltyRecipient, uint16 _royaltyBps) = openEdition.getDefaultRoyaltyInfo(); + string memory _name = openEdition.name(); + string memory _symbol = openEdition.symbol(); + string memory _contractURI = openEdition.contractURI(); + address _owner = openEdition.owner(); + + assertEq(_name, NAME); + assertEq(_symbol, SYMBOL); + assertEq(_contractURI, CONTRACT_URI); + assertEq(_saleRecipient, saleRecipient); + assertEq(_royaltyRecipient, royaltyRecipient); + assertEq(_royaltyBps, royaltyBps); + assertEq(_owner, deployer); + + for (uint256 i = 0; i < forwarders().length; i++) { + assertEq(openEdition.isTrustedForwarder(forwarders()[i]), true); + } + + assertTrue(openEdition.hasRole(openEdition.DEFAULT_ADMIN_ROLE(), deployer)); + assertTrue(openEdition.hasRole(keccak256("TRANSFER_ROLE"), deployer)); + assertTrue(openEdition.hasRole(keccak256("MINTER_ROLE"), deployer)); + assertTrue(openEdition.hasRole(keccak256("TRANSFER_ROLE"), address(0))); + } + + function test_revert_RoyaltyTooHigh() public { + uint128 _royaltyBps = 10001; + + vm.expectRevert("Exceeds max bps"); + deployOpenEdition( + deployer, + NAME, + SYMBOL, + CONTRACT_URI, + forwarders(), + saleRecipient, + royaltyRecipient, + _royaltyBps, + openEditionImpl + ); + } + + function test_event_ContractURIUpdated() public { + vm.expectEmit(false, false, false, true); + emit ContractURIUpdated("", CONTRACT_URI); + deployOpenEdition( + deployer, + NAME, + SYMBOL, + CONTRACT_URI, + forwarders(), + saleRecipient, + royaltyRecipient, + royaltyBps, + openEditionImpl + ); + } + + function test_event_OwnerUpdated() public { + vm.expectEmit(true, true, false, false); + emit OwnerUpdated(address(0), deployer); + deployOpenEdition( + deployer, + NAME, + SYMBOL, + CONTRACT_URI, + forwarders(), + saleRecipient, + royaltyRecipient, + royaltyBps, + openEditionImpl + ); + } + + function test_event_TransferRoleAddressZero() public { + bytes32 role = keccak256("TRANSFER_ROLE"); + vm.expectEmit(true, true, false, false); + emit RoleGranted(role, address(0), deployer); + deployOpenEdition( + deployer, + NAME, + SYMBOL, + CONTRACT_URI, + forwarders(), + saleRecipient, + royaltyRecipient, + royaltyBps, + openEditionImpl + ); + } + + function test_event_TransferRoleAdmin() public { + bytes32 role = keccak256("TRANSFER_ROLE"); + vm.expectEmit(true, true, false, false); + emit RoleGranted(role, deployer, deployer); + deployOpenEdition( + deployer, + NAME, + SYMBOL, + CONTRACT_URI, + forwarders(), + saleRecipient, + royaltyRecipient, + royaltyBps, + openEditionImpl + ); + } + + function test_event_MinterRoleAdmin() public { + bytes32 role = keccak256("MINTER_ROLE"); + vm.expectEmit(true, true, false, false); + emit RoleGranted(role, deployer, deployer); + deployOpenEdition( + deployer, + NAME, + SYMBOL, + CONTRACT_URI, + forwarders(), + saleRecipient, + royaltyRecipient, + royaltyBps, + openEditionImpl + ); + } + + function test_event_DefaultAdminRoleAdmin() public { + bytes32 role = bytes32(0x00); + vm.expectEmit(true, true, false, false); + emit RoleGranted(role, deployer, deployer); + deployOpenEdition( + deployer, + NAME, + SYMBOL, + CONTRACT_URI, + forwarders(), + saleRecipient, + royaltyRecipient, + royaltyBps, + openEditionImpl + ); + } + + function test_event_PrimarysaleRecipientUpdated() public { + vm.expectEmit(true, false, false, false); + emit PrimarySaleRecipientUpdated(saleRecipient); + deployOpenEdition( + deployer, + NAME, + SYMBOL, + CONTRACT_URI, + forwarders(), + saleRecipient, + royaltyRecipient, + royaltyBps, + openEditionImpl + ); + } +} diff --git a/src/test/open-edition/initialize/initialize.tree b/src/test/open-edition/initialize/initialize.tree index 1920a2338..f56ad144b 100644 --- a/src/test/open-edition/initialize/initialize.tree +++ b/src/test/open-edition/initialize/initialize.tree @@ -11,29 +11,29 @@ function initialize( address _platformFeeRecipient ) ├── when _trustedForwarders.length > 0 -│ └── it should set _trustedForwarder[_trustedForwarders[i]] as true for each address in _trustedForwarders -├── it should set _name as the value provided in _name -├── it should set _symbol as the value provided in _symbol -├── it should set _currentIndex as 0 -├── it should set contractURI as _contractURI -├── it should emit ContractURIUpdated with the parameters: prevURI, _uri -├── it should set _defaultAdmin as the owner of the contract -├── it should emit OwnerUpdated with the parameters: _prevOwner, _defaultAdmin -├── it should assign the role DEFAULT_ADMIN_ROLE to _defaultAdmin -├── it should emit RoleGranted with the parameters: DEFAULT_ADMIN_ROLE, _defaultAdmin, msg.sender -├── it should assign the role _minterRole to _defaultAdmin -├── it should emit RoleGranted with the parameters: _minterRole, _defaultAdmin, msg.sender -├── it should assign the role _transferRole to _defaultAdmin -├── it should emit RoleGranted with the parameters: _transferRole, _defaultAdmin, msg.sender -├── it should assign the role _transferRole to address(0) -├── it should emit RoleGranted with the parameters: _transferRole, address(0), msg.sender +│ └── it should set _trustedForwarder[_trustedForwarders[i]] as true for each address in _trustedForwarders ✅ +├── it should set _name as the value provided in _name ✅ +├── it should set _symbol as the value provided in _symbol ✅ +├── it should set _currentIndex as 0 ✅ +├── it should set contractURI as _contractURI ✅ +├── it should emit ContractURIUpdated with the parameters: prevURI, _uri ✅ +├── it should set _defaultAdmin as the owner of the contract ✅ +├── it should emit OwnerUpdated with the parameters: _prevOwner, _defaultAdmin ✅ +├── it should assign the role DEFAULT_ADMIN_ROLE to _defaultAdmin ✅ +├── it should emit RoleGranted with the parameters: DEFAULT_ADMIN_ROLE, _defaultAdmin, msg.sender ✅ +├── it should assign the role _minterRole to _defaultAdmin ✅ +├── it should emit RoleGranted with the parameters: _minterRole, _defaultAdmin, msg.sender ✅ +├── it should assign the role _transferRole to _defaultAdmin ✅ +├── it should emit RoleGranted with the parameters: _transferRole, _defaultAdmin, msg.sender ✅ +├── it should assign the role _transferRole to address(0) ✅ +├── it should emit RoleGranted with the parameters: _transferRole, address(0), msg.sender ✅ ├── when _royaltyBps is greater than 10_000 -│ └── it should revert +│ └── it should revert ✅ ├── when _royaltyBps is less than or equal to 10_000 -│ ├── it should set royaltyRecipient as _royaltyRecipient -│ ├── it should set royaltyBps as uint16(_royaltyBps) -│ └── it should emit DefaultRoyalty with the parameters _royaltyRecipient, _royaltyBps -├── it should set recipient as _primarySaleRecipient -├── it should emit PrimarySaleRecipientUpdated with the parameters _primarySaleRecipient -├── it should set transferRole as keccak256("TRANSFER_ROLE") -└── it should set minterRole as keccak256("MINTER_ROLE") +│ ├── it should set royaltyRecipient as _royaltyRecipient ✅ +│ ├── it should set royaltyBps as uint16(_royaltyBps) ✅ +│ └── it should emit DefaultRoyalty with the parameters _royaltyRecipient, _royaltyBps +├── it should set recipient as _primarySaleRecipient ✅ +├── it should emit PrimarySaleRecipientUpdated with the parameters _primarySaleRecipient ✅ +├── it should set transferRole as keccak256("TRANSFER_ROLE") ✅ +└── it should set minterRole as keccak256("MINTER_ROLE") ✅ From 11cd2e502133ff5f9c022d4497e0120f3941140b Mon Sep 17 00:00:00 2001 From: WhiteOakKong Date: Wed, 18 Oct 2023 16:59:43 -0500 Subject: [PATCH 09/14] test misc --- src/test/open-edition/misc/misc.t.sol | 191 ++++++++++++++++++++++++++ src/test/open-edition/misc/misc.tree | 22 +-- 2 files changed, 203 insertions(+), 10 deletions(-) create mode 100644 src/test/open-edition/misc/misc.t.sol diff --git a/src/test/open-edition/misc/misc.t.sol b/src/test/open-edition/misc/misc.t.sol new file mode 100644 index 000000000..479907ff2 --- /dev/null +++ b/src/test/open-edition/misc/misc.t.sol @@ -0,0 +1,191 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.0; + +import { IERC721AUpgradeable, OpenEditionERC721, ISharedMetadata } from "contracts/prebuilts/open-edition/OpenEditionERC721.sol"; +import { NFTMetadataRenderer } from "contracts/lib/NFTMetadataRendererLib.sol"; +import { TWProxy } from "contracts/infra/TWProxy.sol"; + +// Test imports +import "src/test/utils/BaseTest.sol"; +import "@openzeppelin/contracts-upgradeable/interfaces/IERC2981Upgradeable.sol"; + +contract OpenEditionERC721Test_misc is BaseTest { + OpenEditionERC721 public openEdition; + + address private openEditionImpl; + + address private receiver = 0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3; + + ISharedMetadata.SharedMetadataInfo public sharedMetadata; + + function setUp() public override { + super.setUp(); + openEditionImpl = address(new OpenEditionERC721()); + vm.prank(deployer); + openEdition = OpenEditionERC721( + address( + new TWProxy( + openEditionImpl, + abi.encodeCall( + OpenEditionERC721.initialize, + ( + deployer, + NAME, + SYMBOL, + CONTRACT_URI, + forwarders(), + saleRecipient, + royaltyRecipient, + royaltyBps + ) + ) + ) + ) + ); + + sharedMetadata = ISharedMetadata.SharedMetadataInfo({ + name: "Test", + description: "Test", + imageURI: "https://test.com", + animationURI: "https://test.com" + }); + } + + /*/////////////////////////////////////////////////////////////// + Unit tests: misc + //////////////////////////////////////////////////////////////*/ + + modifier claimTokens() { + string[] memory inputs = new string[](5); + + inputs[0] = "node"; + inputs[1] = "src/test/scripts/generateRoot.ts"; + inputs[2] = "300"; + inputs[3] = "0"; + inputs[4] = Strings.toHexString(uint160(address(erc20))); // address of erc20 + + bytes memory result = vm.ffi(inputs); + bytes32 root = abi.decode(result, (bytes32)); + + inputs[1] = "src/test/scripts/getProof.ts"; + result = vm.ffi(inputs); + bytes32[] memory proofs = abi.decode(result, (bytes32[])); + + OpenEditionERC721.AllowlistProof memory alp; + alp.proof = proofs; + alp.quantityLimitPerWallet = 300; + alp.pricePerToken = 0; + alp.currency = address(erc20); + + vm.warp(1); + + OpenEditionERC721.ClaimCondition[] memory conditions = new OpenEditionERC721.ClaimCondition[](1); + conditions[0].maxClaimableSupply = 500; + conditions[0].quantityLimitPerWallet = 10; + conditions[0].merkleRoot = root; + conditions[0].pricePerToken = 10; + conditions[0].currency = address(erc20); + + vm.prank(deployer); + openEdition.setClaimConditions(conditions, false); + + // vm.prank(getActor(5), getActor(5)); + vm.prank(receiver, receiver); + openEdition.claim(receiver, 100, address(erc20), 0, alp, ""); + _; + } + + modifier callerOwner() { + vm.startPrank(receiver); + _; + } + + modifier callerNotOwner() { + _; + } + + function test_tokenURI_revert_tokenDoesNotExist() public { + vm.expectRevert(bytes("!ID")); + openEdition.tokenURI(1); + } + + function test_tokenURI_returnMetadata() public claimTokens { + vm.prank(deployer); + openEdition.setSharedMetadata(sharedMetadata); + + string memory uri = openEdition.tokenURI(1); + assertEq( + uri, + NFTMetadataRenderer.createMetadataEdition({ + name: sharedMetadata.name, + description: sharedMetadata.description, + imageURI: sharedMetadata.imageURI, + animationURI: sharedMetadata.animationURI, + tokenOfEdition: 1 + }) + ); + } + + function test_startTokenId_returnOne() public { + assertEq(openEdition.startTokenId(), 1); + } + + function test_totalMinted_returnZero() public { + assertEq(openEdition.totalMinted(), 0); + } + + function test_totalMinted_returnOneHundred() public claimTokens { + assertEq(openEdition.totalMinted(), 100); + } + + function test_nextTokenIdToMint_returnOne() public { + assertEq(openEdition.nextTokenIdToMint(), 1); + } + + function test_nextTokenIdToMint_returnOneHundredAndOne() public claimTokens { + assertEq(openEdition.nextTokenIdToMint(), 101); + } + + function test_nextTokenIdToClaim_returnOne() public { + assertEq(openEdition.nextTokenIdToClaim(), 1); + } + + function test_nextTokenIdToClaim_returnOneHundredAndOne() public claimTokens { + assertEq(openEdition.nextTokenIdToClaim(), 101); + } + + function test_burn_revert_callerNotOwner() public claimTokens callerNotOwner { + vm.expectRevert(IERC721AUpgradeable.TransferCallerNotOwnerNorApproved.selector); + openEdition.burn(1); + } + + function test_burn_state_callerOwner() public claimTokens callerOwner { + uint256 balanceBeforeBurn = openEdition.balanceOf(receiver); + + openEdition.burn(1); + + uint256 balanceAfterBurn = openEdition.balanceOf(receiver); + + assertEq(balanceBeforeBurn - balanceAfterBurn, 1); + } + + function test_burn_state_callerApproved() public claimTokens { + uint256 balanceBeforeBurn = openEdition.balanceOf(receiver); + + vm.prank(receiver); + openEdition.setApprovalForAll(deployer, true); + + vm.prank(deployer); + openEdition.burn(1); + + uint256 balanceAfterBurn = openEdition.balanceOf(receiver); + + assertEq(balanceBeforeBurn - balanceAfterBurn, 1); + } + + function test_supportsInterface() public { + assertEq(openEdition.supportsInterface(type(IERC2981Upgradeable).interfaceId), true); + bytes4 invalidId = bytes4(0); + assertEq(openEdition.supportsInterface(invalidId), false); + } +} diff --git a/src/test/open-edition/misc/misc.tree b/src/test/open-edition/misc/misc.tree index 9bf5a4c35..07abb950c 100644 --- a/src/test/open-edition/misc/misc.tree +++ b/src/test/open-edition/misc/misc.tree @@ -1,31 +1,33 @@ function tokenURI(uint256 _tokenId) ├── when _tokenId does not exist -│ └── it should revert +│ └── it should revert ✅ └── when _tokenID does exist - └── it should return the shared metadata + └── it should return the shared metadata ✅ function supportsInterface(bytes4 interfaceId) +├── it should return true for any of the listed interface ids ✅ +└── it should return false for any interfaces ids that are not listed ✅ function _startTokenId() -└── it should return 1 +└── it should return 1 ✅ function startTokenId() -└── it should return _startTokenId (1) +└── it should return _startTokenId (1) ✅ function totalminted() -└── it should return the total number of NFTs minted +└── it should return the total number of NFTs minted ✅ function nextTokenIdToMint() -└── it should return the next token ID to mint (last minted + 1) +└── it should return the next token ID to mint (last minted + 1) ✅ function nextTokenIdToClaim() -└── it should return the next token ID to mint (last minted + 1) +└── it should return the next token ID to mint (last minted + 1) ✅ function burn(uint256 tokenId) ├── when caller is not the owner of tokenId │ ├── when caller is not an approved operator of the owner of tokenId -│ │ └── it should revert +│ │ └── it should revert ✅ │ └── when caller is an approved operator of the owner of tokenId -│ └── it should burn the token +│ └── it should burn the token ✅ └── when caller is the owner of tokenId - └── it should burn the token \ No newline at end of file + └── it should burn the token ✅ \ No newline at end of file From 343af8000b8566ce227939694a6d85f538f2833f Mon Sep 17 00:00:00 2001 From: WhiteOakKong Date: Wed, 18 Oct 2023 17:23:21 -0500 Subject: [PATCH 10/14] test _beforeTokenTransfers --- .../_beforeTokenTransfers.t.sol | 61 +++++++++++++++++++ .../_beforeTokenTransfers.tree | 2 +- 2 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 src/test/open-edition/_beforeTokenTransfers/_beforeTokenTransfers.t.sol diff --git a/src/test/open-edition/_beforeTokenTransfers/_beforeTokenTransfers.t.sol b/src/test/open-edition/_beforeTokenTransfers/_beforeTokenTransfers.t.sol new file mode 100644 index 000000000..1ed885681 --- /dev/null +++ b/src/test/open-edition/_beforeTokenTransfers/_beforeTokenTransfers.t.sol @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.0; + +import { OpenEditionERC721 } from "contracts/prebuilts/open-edition/OpenEditionERC721.sol"; +import { TWProxy } from "contracts/infra/TWProxy.sol"; + +// Test imports +import "src/test/utils/BaseTest.sol"; + +contract OpenEditionERC721Harness is OpenEditionERC721 { + function beforeTokenTransfers(address from, address to, uint256 startTokenId_, uint256 quantity) public { + _beforeTokenTransfers(from, to, startTokenId_, quantity); + } +} + +contract OpenEditionERC721Test_beforeTokenTransfers is BaseTest { + OpenEditionERC721Harness public openEdition; + + address private openEditionImpl; + + function setUp() public override { + super.setUp(); + openEditionImpl = address(new OpenEditionERC721Harness()); + vm.prank(deployer); + openEdition = OpenEditionERC721Harness( + address( + new TWProxy( + openEditionImpl, + abi.encodeCall( + OpenEditionERC721.initialize, + ( + deployer, + NAME, + SYMBOL, + CONTRACT_URI, + forwarders(), + saleRecipient, + royaltyRecipient, + royaltyBps + ) + ) + ) + ) + ); + } + + /*/////////////////////////////////////////////////////////////// + Unit tests: misc + //////////////////////////////////////////////////////////////*/ + + function test_revert_transfersRestricted() public { + address from = address(0x1); + address to = address(0x2); + bytes32 role = keccak256("TRANSFER_ROLE"); + vm.prank(deployer); + openEdition.revokeRole(role, address(0)); + + vm.expectRevert(bytes("!T")); + openEdition.beforeTokenTransfers(from, to, 0, 1); + } +} diff --git a/src/test/open-edition/_beforeTokenTransfers/_beforeTokenTransfers.tree b/src/test/open-edition/_beforeTokenTransfers/_beforeTokenTransfers.tree index 15d2e0f60..078bd6a79 100644 --- a/src/test/open-edition/_beforeTokenTransfers/_beforeTokenTransfers.tree +++ b/src/test/open-edition/_beforeTokenTransfers/_beforeTokenTransfers.tree @@ -9,4 +9,4 @@ function _beforeTokenTransfers( └── when to does not equal address(0) └── when from does not have the transfer role └── when to does not have the transfer role - └── it should revert + └── it should revert ✅ From c26ad36bd654a34b000938d2e9a3823801c40e97 Mon Sep 17 00:00:00 2001 From: WhiteOakKong Date: Wed, 18 Oct 2023 20:32:10 -0500 Subject: [PATCH 11/14] test transferTokensOnClaim --- .../_transferTokensOnClaim.t.sol | 98 +++++++++++++++++++ .../_transferTokensOnClaim.tree | 14 +-- 2 files changed, 105 insertions(+), 7 deletions(-) create mode 100644 src/test/open-edition/_transferTokensOnClaim/_transferTokensOnClaim.t.sol diff --git a/src/test/open-edition/_transferTokensOnClaim/_transferTokensOnClaim.t.sol b/src/test/open-edition/_transferTokensOnClaim/_transferTokensOnClaim.t.sol new file mode 100644 index 000000000..e93645b55 --- /dev/null +++ b/src/test/open-edition/_transferTokensOnClaim/_transferTokensOnClaim.t.sol @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.0; + +import { OpenEditionERC721, IERC721AUpgradeable } from "contracts/prebuilts/open-edition/OpenEditionERC721.sol"; +import { TWProxy } from "contracts/infra/TWProxy.sol"; + +// Test imports +import "src/test/utils/BaseTest.sol"; + +contract OpenEditionERC721Harness is OpenEditionERC721 { + function transferTokensOnClaim(address _to, uint256 quantityBeingClaimed) public { + _transferTokensOnClaim(_to, quantityBeingClaimed); + } +} + +contract MockERC721Receiver { + function onERC721Received(address, address, uint256, bytes memory) external pure returns (bytes4) { + return this.onERC721Received.selector; + } +} + +contract MockERC721NotReceiver {} + +contract OpenEditionERC721Test_transferTokensOnClaim is BaseTest { + OpenEditionERC721Harness public openEdition; + + MockERC721NotReceiver private notReceiver; + MockERC721Receiver private receiver; + + address private openEditionImpl; + + function setUp() public override { + super.setUp(); + openEditionImpl = address(new OpenEditionERC721Harness()); + vm.prank(deployer); + openEdition = OpenEditionERC721Harness( + address( + new TWProxy( + openEditionImpl, + abi.encodeCall( + OpenEditionERC721.initialize, + ( + deployer, + NAME, + SYMBOL, + CONTRACT_URI, + forwarders(), + saleRecipient, + royaltyRecipient, + royaltyBps + ) + ) + ) + ) + ); + + receiver = new MockERC721Receiver(); + notReceiver = new MockERC721NotReceiver(); + } + + /*/////////////////////////////////////////////////////////////// + Unit tests: misc + //////////////////////////////////////////////////////////////*/ + + function test_revert_TransferToNonReceiverContract() public { + vm.expectRevert(IERC721AUpgradeable.TransferToNonERC721ReceiverImplementer.selector); + openEdition.transferTokensOnClaim(address(notReceiver), 1); + } + + function test_state_transferToReceiverContract() public { + + uint256 receiverBalanceBefore = openEdition.balanceOf(address(receiver)); + uint256 nextTokenToMintBefore = openEdition.nextTokenIdToMint(); + + openEdition.transferTokensOnClaim(address(receiver), 1); + + uint256 receiverBalanceAfter = openEdition.balanceOf(address(receiver)); + uint256 nextTokenToMintAfter = openEdition.nextTokenIdToMint(); + + assertEq(receiverBalanceAfter, receiverBalanceBefore + 1); + assertEq(nextTokenToMintAfter, nextTokenToMintBefore + 1); + } + + function test_state_transferToEOA() public { + address to = address(0x01); + uint256 receiverBalanceBefore = openEdition.balanceOf(to); + uint256 nextTokenToMintBefore = openEdition.nextTokenIdToMint(); + + openEdition.transferTokensOnClaim(to, 1); + + uint256 receiverBalanceAfter = openEdition.balanceOf(to); + uint256 nextTokenToMintAfter = openEdition.nextTokenIdToMint(); + + assertEq(receiverBalanceAfter, receiverBalanceBefore + 1); + assertEq(nextTokenToMintAfter, nextTokenToMintBefore + 1); + + } +} diff --git a/src/test/open-edition/_transferTokensOnClaim/_transferTokensOnClaim.tree b/src/test/open-edition/_transferTokensOnClaim/_transferTokensOnClaim.tree index 9bddf5d09..bddcf87f6 100644 --- a/src/test/open-edition/_transferTokensOnClaim/_transferTokensOnClaim.tree +++ b/src/test/open-edition/_transferTokensOnClaim/_transferTokensOnClaim.tree @@ -1,8 +1,8 @@ function _transferTokensOnClaim(address _to, uint256 _quantityBeingClaimed) -# when _to is a smart contract -## when _to has not implemented ERC721Receiver -### it should revert -## when _to has implemented ERC721Receiver -### it should mint _quantityBeingClaimed tokens to _to -# when _to is an EOA -## it should mint _quantityBeingClaimed tokens to _to \ No newline at end of file +├── when _to is a smart contract +│ ├── when _to has not implemented ERC721Receiver +│ │ └── it should revert ✅ +│ └── when _to has implemented ERC721Receiver +│ └── it should mint _quantityBeingClaimed tokens to _to ✅ +└── when _to is an EOA + └── it should mint _quantityBeingClaimed tokens to _to ✅ \ No newline at end of file From 699792a7ffab151b84a31775257ad671b25ccd4e Mon Sep 17 00:00:00 2001 From: WhiteOakKong Date: Wed, 18 Oct 2023 20:32:25 -0500 Subject: [PATCH 12/14] test _canSetFunctions --- .../_canSetFunctions/_canSetFunctions.t.sol | 129 ++++++++++++++++++ .../_canSetFunctions/_canSetFunctions.tree | 24 ++-- 2 files changed, 141 insertions(+), 12 deletions(-) create mode 100644 src/test/open-edition/_canSetFunctions/_canSetFunctions.t.sol diff --git a/src/test/open-edition/_canSetFunctions/_canSetFunctions.t.sol b/src/test/open-edition/_canSetFunctions/_canSetFunctions.t.sol new file mode 100644 index 000000000..8a0956d0b --- /dev/null +++ b/src/test/open-edition/_canSetFunctions/_canSetFunctions.t.sol @@ -0,0 +1,129 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.0; + +import { OpenEditionERC721, IERC721AUpgradeable } from "contracts/prebuilts/open-edition/OpenEditionERC721.sol"; +import { TWProxy } from "contracts/infra/TWProxy.sol"; + +// Test imports +import "src/test/utils/BaseTest.sol"; + +contract OpenEditionERC721Harness is OpenEditionERC721 { + function canSetPrimarySaleRecipient() external view returns (bool) { + return _canSetPrimarySaleRecipient(); + } + + function canSetOwner() external view returns (bool) { + return _canSetOwner(); + } + + /// @dev Checks whether royalty info can be set in the given execution context. + function canSetRoyaltyInfo() external view returns (bool) { + return _canSetRoyaltyInfo(); + } + + /// @dev Checks whether contract metadata can be set in the given execution context. + function canSetContractURI() external view returns (bool) { + return _canSetContractURI(); + } + + /// @dev Checks whether platform fee info can be set in the given execution context. + function canSetClaimConditions() external view returns (bool) { + return _canSetClaimConditions(); + } + + /// @dev Returns whether the shared metadata of tokens can be set in the given execution context. + function canSetSharedMetadata() external view virtual returns (bool) { + return _canSetSharedMetadata(); + } +} + +contract OpenEditionERC721Test_canSetFunctions is BaseTest { + OpenEditionERC721Harness public openEdition; + + address private openEditionImpl; + + function setUp() public override { + super.setUp(); + openEditionImpl = address(new OpenEditionERC721Harness()); + vm.prank(deployer); + openEdition = OpenEditionERC721Harness( + address( + new TWProxy( + openEditionImpl, + abi.encodeCall( + OpenEditionERC721.initialize, + ( + deployer, + NAME, + SYMBOL, + CONTRACT_URI, + forwarders(), + saleRecipient, + royaltyRecipient, + royaltyBps + ) + ) + ) + ) + ); + } + + /*/////////////////////////////////////////////////////////////// + Unit tests: misc + //////////////////////////////////////////////////////////////*/ + + function test_canSetPrimarySaleRecipient_returnTrue() public { + vm.prank(deployer); + assertTrue(openEdition.canSetPrimarySaleRecipient()); + } + + function test_canSetPrimarySaleRecipient_returnFalse() public { + assertFalse(openEdition.canSetPrimarySaleRecipient()); + } + + function test_canSetOwner_returnTrue() public { + vm.prank(deployer); + assertTrue(openEdition.canSetOwner()); + } + + function test_canSetOwner_returnFalse() public { + assertFalse(openEdition.canSetOwner()); + } + + function test_canSetRoyaltyInfo_returnTrue() public { + vm.prank(deployer); + assertTrue(openEdition.canSetRoyaltyInfo()); + } + + function test_canSetRoyaltyInfo_returnFalse() public { + assertFalse(openEdition.canSetRoyaltyInfo()); + } + + function test_canSetContractURI_returnTrue() public { + vm.prank(deployer); + assertTrue(openEdition.canSetContractURI()); + } + + function test_canSetContractURI_returnFalse() public { + assertFalse(openEdition.canSetContractURI()); + } + + function test_canSetClaimConditions_returnTrue() public { + vm.prank(deployer); + assertTrue(openEdition.canSetClaimConditions()); + } + + function test_canSetClaimConditions_returnFalse() public { + assertFalse(openEdition.canSetClaimConditions()); + } + + function test_canSetSharedMetadata_returnTrue() public { + vm.prank(deployer); + assertTrue(openEdition.canSetSharedMetadata()); + } + + function test_canSetSharedMetadata_returnFalse() public { + assertFalse(openEdition.canSetSharedMetadata()); + } + +} diff --git a/src/test/open-edition/_canSetFunctions/_canSetFunctions.tree b/src/test/open-edition/_canSetFunctions/_canSetFunctions.tree index 6f08cd7ba..1ccb478fd 100644 --- a/src/test/open-edition/_canSetFunctions/_canSetFunctions.tree +++ b/src/test/open-edition/_canSetFunctions/_canSetFunctions.tree @@ -1,39 +1,39 @@ function _canSetPrimarySaleRecipient() ├── when _msgSender has DEFAULT_ADMIN_ROLE -│ └── it should return true +│ └── it should return true ✅ └── when _msgSender does not have DEFAULT_ADMIN_ROLE - └── it should return false + └── it should return false ✅ function _canSetOwner() ├── when _msgSender has DEFAULT_ADMIN_ROLE -│ └── it should return true +│ └── it should return true ✅ └── when _msgSender does not have DEFAULT_ADMIN_ROLE - └── it should return false + └── it should return false ✅ function _canSetRoyaltyInfo() ├── when _msgSender has DEFAULT_ADMIN_ROLE -│ └── it should return true +│ └── it should return true ✅ └── when _msgSender does not have DEFAULT_ADMIN_ROLE - └── it should return false + └── it should return false ✅ function _canSetContractURI() ├── when _msgSender has DEFAULT_ADMIN_ROLE -│ └── it should return true +│ └── it should return true ✅ └── when _msgSender does not have DEFAULT_ADMIN_ROLE - └── it should return false + └── it should return false ✅ function _canSetClaimConditions() ├── when _msgSender has DEFAULT_ADMIN_ROLE -│ └── it should return true +│ └── it should return true ✅ └── when _msgSender does not have DEFAULT_ADMIN_ROLE - └── it should return false + └── it should return false ✅ function _canSetSharedMetadata() ├── when _msgSender has minter role -│ └── it should return true +│ └── it should return true ✅ └── when _msgSender does not have minter role - └── it should return false + └── it should return false ✅ From a2e643dba715092ed948bc3c0a78ce2486634994 Mon Sep 17 00:00:00 2001 From: WhiteOakKong Date: Wed, 18 Oct 2023 21:44:35 -0500 Subject: [PATCH 13/14] update tests --- .../_collectPriceOnClaim.t.sol | 203 ++++++++++++++++++ src/test/open-edition/misc/misc.t.sol | 39 ++++ 2 files changed, 242 insertions(+) create mode 100644 src/test/open-edition/_collectPriceOnClaim/_collectPriceOnClaim.t.sol diff --git a/src/test/open-edition/_collectPriceOnClaim/_collectPriceOnClaim.t.sol b/src/test/open-edition/_collectPriceOnClaim/_collectPriceOnClaim.t.sol new file mode 100644 index 000000000..97af49a16 --- /dev/null +++ b/src/test/open-edition/_collectPriceOnClaim/_collectPriceOnClaim.t.sol @@ -0,0 +1,203 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.0; + +import { OpenEditionERC721, IERC721AUpgradeable } from "contracts/prebuilts/open-edition/OpenEditionERC721.sol"; +import { TWProxy } from "contracts/infra/TWProxy.sol"; + +// Test imports +import "src/test/utils/BaseTest.sol"; + +contract OpenEditionERC721Harness is OpenEditionERC721 { + function collectPriceOnClaim( + address _primarySaleRecipient, + uint256 _quantityToClaim, + address _currency, + uint256 _pricePerToken + ) external payable { + _collectPriceOnClaim(_primarySaleRecipient, _quantityToClaim, _currency, _pricePerToken); + } +} + +contract OpenEditionERC721Test_collectPrice is BaseTest { + OpenEditionERC721Harness public openEdition; + + address private openEditionImpl; + + address private currency; + address private primarySaleRecipient; + uint256 private msgValue; + uint256 private pricePerToken; + uint256 private qty = 1; + + function setUp() public override { + super.setUp(); + openEditionImpl = address(new OpenEditionERC721Harness()); + vm.prank(deployer); + openEdition = OpenEditionERC721Harness( + address( + new TWProxy( + openEditionImpl, + abi.encodeCall( + OpenEditionERC721.initialize, + ( + deployer, + NAME, + SYMBOL, + CONTRACT_URI, + forwarders(), + saleRecipient, + royaltyRecipient, + royaltyBps + ) + ) + ) + ) + ); + } + + /*/////////////////////////////////////////////////////////////// + Unit tests: misc + //////////////////////////////////////////////////////////////*/ + + modifier pricePerTokenZero() { + _; + } + + modifier pricePerTokenNotZero() { + pricePerToken = 1 ether; + _; + } + + modifier msgValueZero() { + _; + } + + modifier msgValueNotZero() { + msgValue = 1 ether; + _; + } + + modifier valuePriceMismatch() { + msgValue = 1 ether; + pricePerToken = 2 ether; + _; + } + + modifier primarySaleRecipientZeroAddress() { + primarySaleRecipient = address(0); + _; + } + + modifier primarySaleRecipientNotZeroAddress() { + primarySaleRecipient = address(0x0999); + _; + } + + modifier currencyNativeToken() { + currency = NATIVE_TOKEN; + _; + } + + modifier currencyNotNativeToken() { + currency = address(erc20); + _; + } + + function test_revert_pricePerTokenZeroMsgValueNotZero() public pricePerTokenZero msgValueNotZero { + vm.expectRevert("!Value"); + openEdition.collectPriceOnClaim{ value: msgValue }(primarySaleRecipient, qty, currency, pricePerToken); + } + + function test_revert_nativeCurrencyValuePriceMismatch() public currencyNativeToken valuePriceMismatch { + vm.expectRevert(bytes("!V")); + openEdition.collectPriceOnClaim{ value: msgValue }(primarySaleRecipient, qty, currency, pricePerToken); + } + + function test_revert_erc20ValuePriceMismatch() public currencyNotNativeToken valuePriceMismatch { + vm.expectRevert(bytes("!V")); + openEdition.collectPriceOnClaim{ value: msgValue }(primarySaleRecipient, qty, currency, pricePerToken); + } + + function test_state_nativeCurrency() + public + currencyNativeToken + pricePerTokenNotZero + msgValueNotZero + primarySaleRecipientNotZeroAddress + { + uint256 beforeBalancePrimarySaleRecipient = address(primarySaleRecipient).balance; + + openEdition.collectPriceOnClaim{ value: msgValue }(primarySaleRecipient, qty, currency, pricePerToken); + + uint256 afterBalancePrimarySaleRecipient = address(primarySaleRecipient).balance; + + uint256 primarySaleRecipientVal = msgValue; + + assertEq(beforeBalancePrimarySaleRecipient + primarySaleRecipientVal, afterBalancePrimarySaleRecipient); + } + + function test_revert_erc20_msgValueNotZero() + public + currencyNotNativeToken + msgValueNotZero + primarySaleRecipientNotZeroAddress + { + vm.expectRevert("!Value"); + openEdition.collectPriceOnClaim{ value: msgValue }(primarySaleRecipient, qty, currency, pricePerToken); + } + + function test_state_erc20() public currencyNotNativeToken pricePerTokenNotZero primarySaleRecipientNotZeroAddress { + erc20.mint(address(this), pricePerToken); + ERC20(erc20).approve(address(openEdition), pricePerToken); + uint256 beforeBalancePrimarySaleRecipient = erc20.balanceOf(primarySaleRecipient); + + openEdition.collectPriceOnClaim(primarySaleRecipient, qty, currency, pricePerToken); + + uint256 afterBalancePrimarySaleRecipient = erc20.balanceOf(primarySaleRecipient); + + uint256 primarySaleRecipientVal = 1 ether; + + assertEq(beforeBalancePrimarySaleRecipient + primarySaleRecipientVal, afterBalancePrimarySaleRecipient); + } + + function test_state_erc20StoredPrimarySaleRecipient() + public + currencyNotNativeToken + pricePerTokenNotZero + primarySaleRecipientZeroAddress + { + address storedPrimarySaleRecipient = openEdition.primarySaleRecipient(); + + erc20.mint(address(this), pricePerToken); + ERC20(erc20).approve(address(openEdition), pricePerToken); + uint256 beforeBalancePrimarySaleRecipient = erc20.balanceOf(storedPrimarySaleRecipient); + + openEdition.collectPriceOnClaim(primarySaleRecipient, qty, currency, pricePerToken); + + uint256 afterBalancePrimarySaleRecipient = erc20.balanceOf(storedPrimarySaleRecipient); + + uint256 primarySaleRecipientVal = 1 ether; + + assertEq(beforeBalancePrimarySaleRecipient + primarySaleRecipientVal, afterBalancePrimarySaleRecipient); + } + + function test_state_nativeCurrencyStoredPrimarySaleRecipient() + public + currencyNativeToken + pricePerTokenNotZero + primarySaleRecipientZeroAddress + msgValueNotZero + { + address storedPrimarySaleRecipient = openEdition.primarySaleRecipient(); + + uint256 beforeBalancePrimarySaleRecipient = address(storedPrimarySaleRecipient).balance; + + openEdition.collectPriceOnClaim{ value: msgValue }(primarySaleRecipient, qty, currency, pricePerToken); + + uint256 afterBalancePrimarySaleRecipient = address(storedPrimarySaleRecipient).balance; + + uint256 primarySaleRecipientVal = msgValue; + + assertEq(beforeBalancePrimarySaleRecipient + primarySaleRecipientVal, afterBalancePrimarySaleRecipient); + } +} \ No newline at end of file diff --git a/src/test/open-edition/misc/misc.t.sol b/src/test/open-edition/misc/misc.t.sol index 479907ff2..1de7790e5 100644 --- a/src/test/open-edition/misc/misc.t.sol +++ b/src/test/open-edition/misc/misc.t.sol @@ -9,10 +9,18 @@ import { TWProxy } from "contracts/infra/TWProxy.sol"; import "src/test/utils/BaseTest.sol"; import "@openzeppelin/contracts-upgradeable/interfaces/IERC2981Upgradeable.sol"; +contract HarnessOpenEditionERC721 is OpenEditionERC721 { + function msgData() public view returns (bytes memory) { + return _msgData(); + } +} + contract OpenEditionERC721Test_misc is BaseTest { OpenEditionERC721 public openEdition; + HarnessOpenEditionERC721 public harnessOpenEdition; address private openEditionImpl; + address private harnessImpl; address private receiver = 0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3; @@ -51,6 +59,30 @@ contract OpenEditionERC721Test_misc is BaseTest { }); } + function deployHarness() public { + harnessImpl = address(new HarnessOpenEditionERC721()); + harnessOpenEdition = HarnessOpenEditionERC721( + address( + new TWProxy( + harnessImpl, + abi.encodeCall( + OpenEditionERC721.initialize, + ( + deployer, + NAME, + SYMBOL, + CONTRACT_URI, + forwarders(), + saleRecipient, + royaltyRecipient, + royaltyBps + ) + ) + ) + ) + ); + } + /*/////////////////////////////////////////////////////////////// Unit tests: misc //////////////////////////////////////////////////////////////*/ @@ -188,4 +220,11 @@ contract OpenEditionERC721Test_misc is BaseTest { bytes4 invalidId = bytes4(0); assertEq(openEdition.supportsInterface(invalidId), false); } + + function test_msgData_returnValue() public { + deployHarness(); + bytes memory msgData = harnessOpenEdition.msgData(); + bytes4 expectedData = harnessOpenEdition.msgData.selector; + assertEq(bytes4(msgData), expectedData); + } } From 302ee95f5e9981669edd7eac72d1d130bfb1d19c Mon Sep 17 00:00:00 2001 From: WhiteOakKong Date: Thu, 19 Oct 2023 10:43:18 -0500 Subject: [PATCH 14/14] clean tests + lint --- .../_beforeTokenTransfers.t.sol | 7 ++++- .../_canSetFunctions/_canSetFunctions.t.sol | 5 ++-- .../_collectPriceOnClaim.t.sol | 4 +-- .../_collectPriceOnClaim.tree | 28 ++++++++----------- .../_transferTokensOnClaim.t.sol | 9 ++++-- src/test/open-edition/misc/misc.t.sol | 3 +- 6 files changed, 28 insertions(+), 28 deletions(-) diff --git a/src/test/open-edition/_beforeTokenTransfers/_beforeTokenTransfers.t.sol b/src/test/open-edition/_beforeTokenTransfers/_beforeTokenTransfers.t.sol index 1ed885681..673899df5 100644 --- a/src/test/open-edition/_beforeTokenTransfers/_beforeTokenTransfers.t.sol +++ b/src/test/open-edition/_beforeTokenTransfers/_beforeTokenTransfers.t.sol @@ -8,7 +8,12 @@ import { TWProxy } from "contracts/infra/TWProxy.sol"; import "src/test/utils/BaseTest.sol"; contract OpenEditionERC721Harness is OpenEditionERC721 { - function beforeTokenTransfers(address from, address to, uint256 startTokenId_, uint256 quantity) public { + function beforeTokenTransfers( + address from, + address to, + uint256 startTokenId_, + uint256 quantity + ) public { _beforeTokenTransfers(from, to, startTokenId_, quantity); } } diff --git a/src/test/open-edition/_canSetFunctions/_canSetFunctions.t.sol b/src/test/open-edition/_canSetFunctions/_canSetFunctions.t.sol index 8a0956d0b..6903bdae7 100644 --- a/src/test/open-edition/_canSetFunctions/_canSetFunctions.t.sol +++ b/src/test/open-edition/_canSetFunctions/_canSetFunctions.t.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.0; -import { OpenEditionERC721, IERC721AUpgradeable } from "contracts/prebuilts/open-edition/OpenEditionERC721.sol"; +import { OpenEditionERC721 } from "contracts/prebuilts/open-edition/OpenEditionERC721.sol"; import { TWProxy } from "contracts/infra/TWProxy.sol"; // Test imports @@ -124,6 +124,5 @@ contract OpenEditionERC721Test_canSetFunctions is BaseTest { function test_canSetSharedMetadata_returnFalse() public { assertFalse(openEdition.canSetSharedMetadata()); - } - + } } diff --git a/src/test/open-edition/_collectPriceOnClaim/_collectPriceOnClaim.t.sol b/src/test/open-edition/_collectPriceOnClaim/_collectPriceOnClaim.t.sol index 97af49a16..5b49bcdd7 100644 --- a/src/test/open-edition/_collectPriceOnClaim/_collectPriceOnClaim.t.sol +++ b/src/test/open-edition/_collectPriceOnClaim/_collectPriceOnClaim.t.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.0; -import { OpenEditionERC721, IERC721AUpgradeable } from "contracts/prebuilts/open-edition/OpenEditionERC721.sol"; +import { OpenEditionERC721 } from "contracts/prebuilts/open-edition/OpenEditionERC721.sol"; import { TWProxy } from "contracts/infra/TWProxy.sol"; // Test imports @@ -200,4 +200,4 @@ contract OpenEditionERC721Test_collectPrice is BaseTest { assertEq(beforeBalancePrimarySaleRecipient + primarySaleRecipientVal, afterBalancePrimarySaleRecipient); } -} \ No newline at end of file +} diff --git a/src/test/open-edition/_collectPriceOnClaim/_collectPriceOnClaim.tree b/src/test/open-edition/_collectPriceOnClaim/_collectPriceOnClaim.tree index 76e1f08a0..2054cf049 100644 --- a/src/test/open-edition/_collectPriceOnClaim/_collectPriceOnClaim.tree +++ b/src/test/open-edition/_collectPriceOnClaim/_collectPriceOnClaim.tree @@ -6,38 +6,32 @@ function _collectPriceOnClaim( ) ├── when _pricePerToken is equal to zero │ ├── when msg.value does not equal to zero -│ │ └── it should revert +│ │ └── it should revert ✅ │ └── when msg.value is equal to zero -│ └── it should return +│ └── it should return ✅ └── when _pricePerToken is not equal to zero ├── when _primarySaleRecipient is equal to address(0) │ ├── when saleRecipient for _tokenId is equal to address(0) │ │ ├── when currency is native token │ │ │ ├── when msg.value does not equal totalPrice - │ │ │ │ └── it should revert + │ │ │ │ └── it should revert ✅ │ │ │ └── when msg.value does equal totalPrice - │ │ │ ├── it should transfer platformFees to platformFeeRecipient in native token - │ │ │ └── it should transfer totalPrice - platformFees to primarySaleRecipient in native token + │ │ │ └── it should transfer totalPrice to primarySaleRecipient in native token ✅ │ │ └── when currency is not native token - │ │ ├── it should transfer platformFees to platformFeeRecipient in _currency token - │ │ └── it should transfer totalPrice - platformFees to primarySaleRecipient in _currency token + │ │ └── it should transfer totalPrice to primarySaleRecipient in _currency token ✅ │ └── when salerecipient for _tokenId is not equal to address(0) │ ├── when currency is native token │ │ ├── when msg.value does not equal totalPrice - │ │ │ └── it should revert + │ │ │ └── it should revert ✅ │ │ └── when msg.value does equal totalPrice - │ │ ├── it should transfer platformFees to platformFeeRecipient in native token - │ │ └── it should transfer totalPrice - platformFees to saleRecipient for _tokenId in native token + │ │ └── it should transfer totalPrice to saleRecipient for _tokenId in native token ✅ │ └── when currency is not native token - │ ├── it should transfer platformFees to platformFeeRecipient in _currency token - │ └── it should transfer totalPrice - platformFees to saleRecipient for _tokenId in _currency token + │ └── it should transfer totalPrice to saleRecipient for _tokenId in _currency token ✅ └── when _primarySaleRecipient is not equal to address(0) ├── when currency is native token │ ├── when msg.value does not equal totalPrice - │ │ └── it should revert + │ │ └── it should revert ✅ │ └── when msg.value does equal totalPrice - │ ├── it should transfer platformFees to platformFeeRecipient in native token - │ └── it should transfer totalPrice - platformFees to _primarySaleRecipient in native token + │ └── it should transfer totalPrice to _primarySaleRecipient in native token ✅ └── when currency is not native token - ├── it should transfer platformFees to platformFeeRecipient in _currency token - └── it should transfer totalPrice - platformFees to _primarySaleRecipient in _currency token \ No newline at end of file + └── it should transfer totalPrice to _primarySaleRecipient in _currency token ✅ \ No newline at end of file diff --git a/src/test/open-edition/_transferTokensOnClaim/_transferTokensOnClaim.t.sol b/src/test/open-edition/_transferTokensOnClaim/_transferTokensOnClaim.t.sol index e93645b55..619f49b77 100644 --- a/src/test/open-edition/_transferTokensOnClaim/_transferTokensOnClaim.t.sol +++ b/src/test/open-edition/_transferTokensOnClaim/_transferTokensOnClaim.t.sol @@ -14,7 +14,12 @@ contract OpenEditionERC721Harness is OpenEditionERC721 { } contract MockERC721Receiver { - function onERC721Received(address, address, uint256, bytes memory) external pure returns (bytes4) { + function onERC721Received( + address, + address, + uint256, + bytes memory + ) external pure returns (bytes4) { return this.onERC721Received.selector; } } @@ -68,7 +73,6 @@ contract OpenEditionERC721Test_transferTokensOnClaim is BaseTest { } function test_state_transferToReceiverContract() public { - uint256 receiverBalanceBefore = openEdition.balanceOf(address(receiver)); uint256 nextTokenToMintBefore = openEdition.nextTokenIdToMint(); @@ -93,6 +97,5 @@ contract OpenEditionERC721Test_transferTokensOnClaim is BaseTest { assertEq(receiverBalanceAfter, receiverBalanceBefore + 1); assertEq(nextTokenToMintAfter, nextTokenToMintBefore + 1); - } } diff --git a/src/test/open-edition/misc/misc.t.sol b/src/test/open-edition/misc/misc.t.sol index 1de7790e5..82943215d 100644 --- a/src/test/open-edition/misc/misc.t.sol +++ b/src/test/open-edition/misc/misc.t.sol @@ -59,7 +59,7 @@ contract OpenEditionERC721Test_misc is BaseTest { }); } - function deployHarness() public { + function deployHarness() internal { harnessImpl = address(new HarnessOpenEditionERC721()); harnessOpenEdition = HarnessOpenEditionERC721( address( @@ -121,7 +121,6 @@ contract OpenEditionERC721Test_misc is BaseTest { vm.prank(deployer); openEdition.setClaimConditions(conditions, false); - // vm.prank(getActor(5), getActor(5)); vm.prank(receiver, receiver); openEdition.claim(receiver, 100, address(erc20), 0, alp, ""); _;