From ee7774b37bb92b7723123c88d9cbcccf966e5a0d Mon Sep 17 00:00:00 2001 From: Yash Date: Fri, 26 Jan 2024 02:09:18 +0530 Subject: [PATCH 1/3] OpenEditionERC721 with flat fee --- .../open-edition/OpenEditionERC721FlatFee.sol | 290 ++++++++++++++++++ 1 file changed, 290 insertions(+) create mode 100644 contracts/prebuilts/open-edition/OpenEditionERC721FlatFee.sol diff --git a/contracts/prebuilts/open-edition/OpenEditionERC721FlatFee.sol b/contracts/prebuilts/open-edition/OpenEditionERC721FlatFee.sol new file mode 100644 index 000000000..4ad26f927 --- /dev/null +++ b/contracts/prebuilts/open-edition/OpenEditionERC721FlatFee.sol @@ -0,0 +1,290 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.11; + +/// @author thirdweb + +// $$\ $$\ $$\ $$\ $$\ +// $$ | $$ | \__| $$ | $$ | +// $$$$$$\ $$$$$$$\ $$\ $$$$$$\ $$$$$$$ |$$\ $$\ $$\ $$$$$$\ $$$$$$$\ +// \_$$ _| $$ __$$\ $$ |$$ __$$\ $$ __$$ |$$ | $$ | $$ |$$ __$$\ $$ __$$\ +// $$ | $$ | $$ |$$ |$$ | \__|$$ / $$ |$$ | $$ | $$ |$$$$$$$$ |$$ | $$ | +// $$ |$$\ $$ | $$ |$$ |$$ | $$ | $$ |$$ | $$ | $$ |$$ ____|$$ | $$ | +// \$$$$ |$$ | $$ |$$ |$$ | \$$$$$$$ |\$$$$$\$$$$ |\$$$$$$$\ $$$$$$$ | +// \____/ \__| \__|\__|\__| \_______| \_____\____/ \_______|\_______/ + +// ========== External imports ========== + +import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/interfaces/IERC2981Upgradeable.sol"; + +import "../../eip/queryable/ERC721AQueryableUpgradeable.sol"; + +// ========== Internal imports ========== + +import "../../external-deps/openzeppelin/metatx/ERC2771ContextUpgradeable.sol"; +import "../../lib/CurrencyTransferLib.sol"; + +// ========== Features ========== + +import "../../extension/Multicall.sol"; +import "../../extension/ContractMetadata.sol"; +import "../../extension/Royalty.sol"; +import "../../extension/PrimarySale.sol"; +import "../../extension/Ownable.sol"; +import "../../extension/SharedMetadata.sol"; +import "../../extension/PermissionsEnumerable.sol"; +import "../../extension/Drop.sol"; +import "../../extension/PlatformFee.sol"; + +contract OpenEditionERC721FlatFee is + Initializable, + ContractMetadata, + PlatformFee, + Royalty, + PrimarySale, + Ownable, + SharedMetadata, + PermissionsEnumerable, + Drop, + ERC2771ContextUpgradeable, + Multicall, + ERC721AQueryableUpgradeable +{ + using StringsUpgradeable for uint256; + + /*/////////////////////////////////////////////////////////////// + State variables + //////////////////////////////////////////////////////////////*/ + + /// @dev Only transfers to or from TRANSFER_ROLE holders are valid, when transfers are restricted. + bytes32 private transferRole; + /// @dev Only MINTER_ROLE holders can update the shared metadata of tokens. + bytes32 private minterRole; + + /// @dev Max bps in the thirdweb system. + uint256 private constant MAX_BPS = 10_000; + + /*/////////////////////////////////////////////////////////////// + Constructor + initializer logic + //////////////////////////////////////////////////////////////*/ + + constructor() initializer {} + + /// @dev Initializes the contract, like a constructor. + 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 + ) external initializerERC721A initializer { + bytes32 _transferRole = keccak256("TRANSFER_ROLE"); + bytes32 _minterRole = keccak256("MINTER_ROLE"); + + // Initialize inherited contracts, most base-like -> most derived. + __ERC2771Context_init(_trustedForwarders); + __ERC721A_init(_name, _symbol); + + _setupContractURI(_contractURI); + _setupOwner(_defaultAdmin); + + _setupRole(DEFAULT_ADMIN_ROLE, _defaultAdmin); + _setupRole(_minterRole, _defaultAdmin); + _setupRole(_transferRole, _defaultAdmin); + _setupRole(_transferRole, address(0)); + + _setupPlatformFeeInfo(_platformFeeRecipient, _platformFeeBps); + _setupDefaultRoyaltyInfo(_royaltyRecipient, _royaltyBps); + _setupPrimarySaleRecipient(_saleRecipient); + + transferRole = _transferRole; + minterRole = _minterRole; + } + + /*/////////////////////////////////////////////////////////////// + ERC 165 / 721 / 2981 logic + //////////////////////////////////////////////////////////////*/ + + /// @dev Returns the URI for a given tokenId. + function tokenURI( + uint256 _tokenId + ) public view virtual override(ERC721AUpgradeable, IERC721AUpgradeable) returns (string memory) { + if (!_exists(_tokenId)) { + revert("!ID"); + } + + return _getURIFromSharedMetadata(_tokenId); + } + + /// @dev See ERC 165 + function supportsInterface( + bytes4 interfaceId + ) public view virtual override(ERC721AUpgradeable, IERC165, IERC721AUpgradeable) returns (bool) { + return super.supportsInterface(interfaceId) || type(IERC2981Upgradeable).interfaceId == interfaceId; + } + + /// @dev The start token ID for the contract. + function _startTokenId() internal pure override returns (uint256) { + return 1; + } + + function startTokenId() public pure returns (uint256) { + return _startTokenId(); + } + + /*/////////////////////////////////////////////////////////////// + Internal functions + //////////////////////////////////////////////////////////////*/ + + /// @dev Collects and distributes the primary sale value of NFTs being claimed. + function _collectPriceOnClaim( + address _primarySaleRecipient, + uint256 _quantityToClaim, + address _currency, + uint256 _pricePerToken + ) internal override { + if (_pricePerToken == 0) { + require(msg.value == 0, "!Value"); + return; + } + + uint256 totalPrice = _quantityToClaim * _pricePerToken; + uint256 platformFees; + address platformFeeRecipient; + + if (getPlatformFeeType() == IPlatformFee.PlatformFeeType.Flat) { + (platformFeeRecipient, platformFees) = getFlatPlatformFeeInfo(); + } else { + (address recipient, uint16 platformFeeBps) = getPlatformFeeInfo(); + platformFeeRecipient = recipient; + platformFees = ((totalPrice * platformFeeBps) / MAX_BPS); + } + require(totalPrice >= platformFees, "price less than platform fee"); + + bool validMsgValue; + if (_currency == CurrencyTransferLib.NATIVE_TOKEN) { + validMsgValue = msg.value == totalPrice; + } else { + validMsgValue = msg.value == 0; + } + require(validMsgValue, "!V"); + + address saleRecipient = _primarySaleRecipient == address(0) ? primarySaleRecipient() : _primarySaleRecipient; + + CurrencyTransferLib.transferCurrency(_currency, _msgSender(), platformFeeRecipient, platformFees); + CurrencyTransferLib.transferCurrency(_currency, _msgSender(), saleRecipient, totalPrice - platformFees); + } + + /// @dev Transfers the NFTs being claimed. + function _transferTokensOnClaim( + address _to, + uint256 _quantityBeingClaimed + ) internal override returns (uint256 startTokenId_) { + startTokenId_ = _nextTokenId(); + _safeMint(_to, _quantityBeingClaimed); + } + + /// @dev Checks whether primary sale recipient can be set in the given execution context. + function _canSetPrimarySaleRecipient() internal view override returns (bool) { + return hasRole(DEFAULT_ADMIN_ROLE, _msgSender()); + } + + /// @dev Checks whether owner can be set in the given execution context. + function _canSetOwner() internal view override returns (bool) { + return hasRole(DEFAULT_ADMIN_ROLE, _msgSender()); + } + + /// @dev Checks whether royalty info can be set in the given execution context. + function _canSetRoyaltyInfo() internal view override returns (bool) { + return hasRole(DEFAULT_ADMIN_ROLE, _msgSender()); + } + + /// @dev Checks whether contract metadata can be set in the given execution context. + function _canSetContractURI() internal view override returns (bool) { + return hasRole(DEFAULT_ADMIN_ROLE, _msgSender()); + } + + /// @dev Checks whether platform fee info can be set in the given execution context. + function _canSetClaimConditions() internal view override returns (bool) { + return hasRole(DEFAULT_ADMIN_ROLE, _msgSender()); + } + + /// @dev Returns whether the shared metadata of tokens can be set in the given execution context. + function _canSetSharedMetadata() internal view virtual override returns (bool) { + return hasRole(minterRole, _msgSender()); + } + + /// @dev Checks whether platform fee info can be set in the given execution context. + function _canSetPlatformFeeInfo() internal view override returns (bool) { + return hasRole(DEFAULT_ADMIN_ROLE, _msgSender()); + } + + /*/////////////////////////////////////////////////////////////// + Miscellaneous + //////////////////////////////////////////////////////////////*/ + + /** + * Returns the total amount of tokens minted in the contract. + */ + function totalMinted() external view returns (uint256) { + unchecked { + return _nextTokenId() - _startTokenId(); + } + } + + /// @dev The tokenId of the next NFT that will be minted / lazy minted. + function nextTokenIdToMint() external view returns (uint256) { + return _nextTokenId(); + } + + /// @dev The next token ID of the NFT that can be claimed. + function nextTokenIdToClaim() external view returns (uint256) { + return _nextTokenId(); + } + + /// @dev Burns `tokenId`. See {ERC721-_burn}. + function burn(uint256 tokenId) external virtual { + // note: ERC721AUpgradeable's `_burn(uint256,bool)` internally checks for token approvals. + _burn(tokenId, true); + } + + /// @dev See {ERC721-_beforeTokenTransfer}. + function _beforeTokenTransfers( + address from, + address to, + uint256 startTokenId_, + uint256 quantity + ) internal virtual override { + super._beforeTokenTransfers(from, to, startTokenId_, quantity); + + // if transfer is restricted on the contract, we still want to allow burning and minting + if (!hasRole(transferRole, address(0)) && from != address(0) && to != address(0)) { + if (!hasRole(transferRole, from) && !hasRole(transferRole, to)) { + revert("!T"); + } + } + } + + function _dropMsgSender() internal view virtual override returns (address) { + return _msgSender(); + } + + function _msgSenderERC721A() internal view virtual override returns (address) { + return _msgSender(); + } + + function _msgSender() + internal + view + virtual + override(ERC2771ContextUpgradeable, Multicall) + returns (address sender) + { + return ERC2771ContextUpgradeable._msgSender(); + } +} From d63eb9df04cab5345f641a5dba17dc221c85e15e Mon Sep 17 00:00:00 2001 From: Yash Date: Fri, 26 Jan 2024 01:49:47 +0530 Subject: [PATCH 2/3] flat fee support OE --- .../open-edition/OpenEditionERC721.sol | 25 +++++++++++++++++-- src/test/OpenEditionERC721.t.sol | 4 ++- .../_beforeTokenTransfers.t.sol | 4 ++- .../_canSetFunctions/_canSetFunctions.t.sol | 4 ++- .../_collectPriceOnClaim.t.sol | 4 ++- .../_transferTokensOnClaim.t.sol | 4 ++- .../open-edition/initialize/initialize.t.sol | 24 +++++++++++++++++- src/test/open-edition/misc/misc.t.sol | 8 ++++-- 8 files changed, 67 insertions(+), 10 deletions(-) diff --git a/contracts/prebuilts/open-edition/OpenEditionERC721.sol b/contracts/prebuilts/open-edition/OpenEditionERC721.sol index f802b2f14..ad0c04c39 100644 --- a/contracts/prebuilts/open-edition/OpenEditionERC721.sol +++ b/contracts/prebuilts/open-edition/OpenEditionERC721.sol @@ -34,10 +34,12 @@ import "../../extension/Ownable.sol"; import "../../extension/SharedMetadata.sol"; import "../../extension/PermissionsEnumerable.sol"; import "../../extension/Drop.sol"; +import "../../extension/PlatformFee.sol"; contract OpenEditionERC721 is Initializable, ContractMetadata, + PlatformFee, Royalty, PrimarySale, Ownable, @@ -77,7 +79,9 @@ contract OpenEditionERC721 is address[] memory _trustedForwarders, address _saleRecipient, address _royaltyRecipient, - uint128 _royaltyBps + uint128 _royaltyBps, + uint128 _platformFeeBps, + address _platformFeeRecipient ) external initializerERC721A initializer { bytes32 _transferRole = keccak256("TRANSFER_ROLE"); bytes32 _minterRole = keccak256("MINTER_ROLE"); @@ -94,6 +98,7 @@ contract OpenEditionERC721 is _setupRole(_transferRole, _defaultAdmin); _setupRole(_transferRole, address(0)); + _setupPlatformFeeInfo(_platformFeeRecipient, _platformFeeBps); _setupDefaultRoyaltyInfo(_royaltyRecipient, _royaltyBps); _setupPrimarySaleRecipient(_saleRecipient); @@ -149,6 +154,16 @@ contract OpenEditionERC721 is } uint256 totalPrice = _quantityToClaim * _pricePerToken; + uint256 platformFees; + address platformFeeRecipient; + + if (getPlatformFeeType() == IPlatformFee.PlatformFeeType.Flat) { + (platformFeeRecipient, platformFees) = getFlatPlatformFeeInfo(); + } else { + (address recipient, uint16 platformFeeBps) = getPlatformFeeInfo(); + platformFeeRecipient = recipient; + platformFees = ((totalPrice * platformFeeBps) / MAX_BPS); + } bool validMsgValue; if (_currency == CurrencyTransferLib.NATIVE_TOKEN) { @@ -160,7 +175,8 @@ contract OpenEditionERC721 is address saleRecipient = _primarySaleRecipient == address(0) ? primarySaleRecipient() : _primarySaleRecipient; - CurrencyTransferLib.transferCurrency(_currency, _msgSender(), saleRecipient, totalPrice); + CurrencyTransferLib.transferCurrency(_currency, _msgSender(), platformFeeRecipient, platformFees); + CurrencyTransferLib.transferCurrency(_currency, _msgSender(), saleRecipient, totalPrice - platformFees); } /// @dev Transfers the NFTs being claimed. @@ -202,6 +218,11 @@ contract OpenEditionERC721 is return hasRole(minterRole, _msgSender()); } + /// @dev Checks whether platform fee info can be set in the given execution context. + function _canSetPlatformFeeInfo() internal view override returns (bool) { + return hasRole(DEFAULT_ADMIN_ROLE, _msgSender()); + } + /*/////////////////////////////////////////////////////////////// Miscellaneous //////////////////////////////////////////////////////////////*/ diff --git a/src/test/OpenEditionERC721.t.sol b/src/test/OpenEditionERC721.t.sol index fb9310f30..36ae738b8 100644 --- a/src/test/OpenEditionERC721.t.sol +++ b/src/test/OpenEditionERC721.t.sol @@ -42,7 +42,9 @@ contract OpenEditionERC721Test is BaseTest { forwarders(), saleRecipient, royaltyRecipient, - royaltyBps + royaltyBps, + platformFeeBps, + platformFeeRecipient ) ) ) diff --git a/src/test/open-edition/_beforeTokenTransfers/_beforeTokenTransfers.t.sol b/src/test/open-edition/_beforeTokenTransfers/_beforeTokenTransfers.t.sol index 1ed885681..27f1a3763 100644 --- a/src/test/open-edition/_beforeTokenTransfers/_beforeTokenTransfers.t.sol +++ b/src/test/open-edition/_beforeTokenTransfers/_beforeTokenTransfers.t.sol @@ -36,7 +36,9 @@ contract OpenEditionERC721Test_beforeTokenTransfers is BaseTest { forwarders(), saleRecipient, royaltyRecipient, - royaltyBps + royaltyBps, + platformFeeBps, + platformFeeRecipient ) ) ) diff --git a/src/test/open-edition/_canSetFunctions/_canSetFunctions.t.sol b/src/test/open-edition/_canSetFunctions/_canSetFunctions.t.sol index 6903bdae7..8f9b10118 100644 --- a/src/test/open-edition/_canSetFunctions/_canSetFunctions.t.sol +++ b/src/test/open-edition/_canSetFunctions/_canSetFunctions.t.sol @@ -60,7 +60,9 @@ contract OpenEditionERC721Test_canSetFunctions is BaseTest { forwarders(), saleRecipient, royaltyRecipient, - royaltyBps + royaltyBps, + platformFeeBps, + platformFeeRecipient ) ) ) diff --git a/src/test/open-edition/_collectPriceOnClaim/_collectPriceOnClaim.t.sol b/src/test/open-edition/_collectPriceOnClaim/_collectPriceOnClaim.t.sol index 5b49bcdd7..4f26fb1d3 100644 --- a/src/test/open-edition/_collectPriceOnClaim/_collectPriceOnClaim.t.sol +++ b/src/test/open-edition/_collectPriceOnClaim/_collectPriceOnClaim.t.sol @@ -47,7 +47,9 @@ contract OpenEditionERC721Test_collectPrice is BaseTest { forwarders(), saleRecipient, royaltyRecipient, - royaltyBps + royaltyBps, + platformFeeBps, + platformFeeRecipient ) ) ) diff --git a/src/test/open-edition/_transferTokensOnClaim/_transferTokensOnClaim.t.sol b/src/test/open-edition/_transferTokensOnClaim/_transferTokensOnClaim.t.sol index d0d2f2173..d66cf9c71 100644 --- a/src/test/open-edition/_transferTokensOnClaim/_transferTokensOnClaim.t.sol +++ b/src/test/open-edition/_transferTokensOnClaim/_transferTokensOnClaim.t.sol @@ -47,7 +47,9 @@ contract OpenEditionERC721Test_transferTokensOnClaim is BaseTest { forwarders(), saleRecipient, royaltyRecipient, - royaltyBps + royaltyBps, + platformFeeBps, + platformFeeRecipient ) ) ) diff --git a/src/test/open-edition/initialize/initialize.t.sol b/src/test/open-edition/initialize/initialize.t.sol index 270118999..31eff03d0 100644 --- a/src/test/open-edition/initialize/initialize.t.sol +++ b/src/test/open-edition/initialize/initialize.t.sol @@ -26,6 +26,8 @@ contract OpenEditionERC721Test_initialize is BaseTest { address _saleRecipient, address _royaltyRecipient, uint128 _royaltyBps, + uint128 _platformFeeBps, + address _platformFeeRecipient, address _imp ) public { vm.prank(deployer); @@ -43,7 +45,9 @@ contract OpenEditionERC721Test_initialize is BaseTest { _trustedForwarders, _saleRecipient, _royaltyRecipient, - _royaltyBps + _royaltyBps, + _platformFeeBps, + _platformFeeRecipient ) ) ) @@ -70,6 +74,8 @@ contract OpenEditionERC721Test_initialize is BaseTest { saleRecipient, royaltyRecipient, royaltyBps, + platformFeeBps, + platformFeeRecipient, openEditionImpl ); @@ -111,6 +117,8 @@ contract OpenEditionERC721Test_initialize is BaseTest { saleRecipient, royaltyRecipient, _royaltyBps, + platformFeeBps, + platformFeeRecipient, openEditionImpl ); } @@ -127,6 +135,8 @@ contract OpenEditionERC721Test_initialize is BaseTest { saleRecipient, royaltyRecipient, royaltyBps, + platformFeeBps, + platformFeeRecipient, openEditionImpl ); } @@ -143,6 +153,8 @@ contract OpenEditionERC721Test_initialize is BaseTest { saleRecipient, royaltyRecipient, royaltyBps, + platformFeeBps, + platformFeeRecipient, openEditionImpl ); } @@ -160,6 +172,8 @@ contract OpenEditionERC721Test_initialize is BaseTest { saleRecipient, royaltyRecipient, royaltyBps, + platformFeeBps, + platformFeeRecipient, openEditionImpl ); } @@ -177,6 +191,8 @@ contract OpenEditionERC721Test_initialize is BaseTest { saleRecipient, royaltyRecipient, royaltyBps, + platformFeeBps, + platformFeeRecipient, openEditionImpl ); } @@ -194,6 +210,8 @@ contract OpenEditionERC721Test_initialize is BaseTest { saleRecipient, royaltyRecipient, royaltyBps, + platformFeeBps, + platformFeeRecipient, openEditionImpl ); } @@ -211,6 +229,8 @@ contract OpenEditionERC721Test_initialize is BaseTest { saleRecipient, royaltyRecipient, royaltyBps, + platformFeeBps, + platformFeeRecipient, openEditionImpl ); } @@ -227,6 +247,8 @@ contract OpenEditionERC721Test_initialize is BaseTest { saleRecipient, royaltyRecipient, royaltyBps, + platformFeeBps, + platformFeeRecipient, openEditionImpl ); } diff --git a/src/test/open-edition/misc/misc.t.sol b/src/test/open-edition/misc/misc.t.sol index 44e6b20cb..1ee2a71c2 100644 --- a/src/test/open-edition/misc/misc.t.sol +++ b/src/test/open-edition/misc/misc.t.sol @@ -44,7 +44,9 @@ contract OpenEditionERC721Test_misc is BaseTest { forwarders(), saleRecipient, royaltyRecipient, - royaltyBps + royaltyBps, + platformFeeBps, + platformFeeRecipient ) ) ) @@ -75,7 +77,9 @@ contract OpenEditionERC721Test_misc is BaseTest { forwarders(), saleRecipient, royaltyRecipient, - royaltyBps + royaltyBps, + platformFeeBps, + platformFeeRecipient ) ) ) From e4ab1eba7a57675e9dce0a59bdd96c5206571b27 Mon Sep 17 00:00:00 2001 From: Yash Date: Fri, 26 Jan 2024 20:15:38 +0530 Subject: [PATCH 3/3] fix tests --- .../open-edition/OpenEditionERC721.sol | 25 +- src/test/OpenEditionERC721.t.sol | 4 +- src/test/OpenEditionERC721FlatFee.t.sol | 796 ++++++++++++++++++ .../_beforeTokenTransfers.t.sol | 63 ++ .../_beforeTokenTransfers.tree | 12 + .../_canSetFunctions/_canSetFunctions.t.sol | 130 +++ .../_canSetFunctions/_canSetFunctions.tree | 39 + .../_collectPriceOnClaim.t.sol | 209 +++++ .../_collectPriceOnClaim.tree | 37 + .../_transferTokensOnClaim.t.sol | 98 +++ .../_transferTokensOnClaim.tree | 8 + .../initialize/initialize.t.sol | 255 ++++++ .../initialize/initialize.tree | 39 + .../open-edition-flat-fee/misc/misc.t.sol | 233 +++++ src/test/open-edition-flat-fee/misc/misc.tree | 33 + .../_beforeTokenTransfers.t.sol | 4 +- .../_canSetFunctions/_canSetFunctions.t.sol | 4 +- .../_collectPriceOnClaim.t.sol | 4 +- .../_transferTokensOnClaim.t.sol | 4 +- .../open-edition/initialize/initialize.t.sol | 24 +- src/test/open-edition/misc/misc.t.sol | 8 +- 21 files changed, 1962 insertions(+), 67 deletions(-) create mode 100644 src/test/OpenEditionERC721FlatFee.t.sol create mode 100644 src/test/open-edition-flat-fee/_beforeTokenTransfers/_beforeTokenTransfers.t.sol create mode 100644 src/test/open-edition-flat-fee/_beforeTokenTransfers/_beforeTokenTransfers.tree create mode 100644 src/test/open-edition-flat-fee/_canSetFunctions/_canSetFunctions.t.sol create mode 100644 src/test/open-edition-flat-fee/_canSetFunctions/_canSetFunctions.tree create mode 100644 src/test/open-edition-flat-fee/_collectPriceOnClaim/_collectPriceOnClaim.t.sol create mode 100644 src/test/open-edition-flat-fee/_collectPriceOnClaim/_collectPriceOnClaim.tree create mode 100644 src/test/open-edition-flat-fee/_transferTokensOnClaim/_transferTokensOnClaim.t.sol create mode 100644 src/test/open-edition-flat-fee/_transferTokensOnClaim/_transferTokensOnClaim.tree create mode 100644 src/test/open-edition-flat-fee/initialize/initialize.t.sol create mode 100644 src/test/open-edition-flat-fee/initialize/initialize.tree create mode 100644 src/test/open-edition-flat-fee/misc/misc.t.sol create mode 100644 src/test/open-edition-flat-fee/misc/misc.tree diff --git a/contracts/prebuilts/open-edition/OpenEditionERC721.sol b/contracts/prebuilts/open-edition/OpenEditionERC721.sol index ad0c04c39..f802b2f14 100644 --- a/contracts/prebuilts/open-edition/OpenEditionERC721.sol +++ b/contracts/prebuilts/open-edition/OpenEditionERC721.sol @@ -34,12 +34,10 @@ import "../../extension/Ownable.sol"; import "../../extension/SharedMetadata.sol"; import "../../extension/PermissionsEnumerable.sol"; import "../../extension/Drop.sol"; -import "../../extension/PlatformFee.sol"; contract OpenEditionERC721 is Initializable, ContractMetadata, - PlatformFee, Royalty, PrimarySale, Ownable, @@ -79,9 +77,7 @@ contract OpenEditionERC721 is address[] memory _trustedForwarders, address _saleRecipient, address _royaltyRecipient, - uint128 _royaltyBps, - uint128 _platformFeeBps, - address _platformFeeRecipient + uint128 _royaltyBps ) external initializerERC721A initializer { bytes32 _transferRole = keccak256("TRANSFER_ROLE"); bytes32 _minterRole = keccak256("MINTER_ROLE"); @@ -98,7 +94,6 @@ contract OpenEditionERC721 is _setupRole(_transferRole, _defaultAdmin); _setupRole(_transferRole, address(0)); - _setupPlatformFeeInfo(_platformFeeRecipient, _platformFeeBps); _setupDefaultRoyaltyInfo(_royaltyRecipient, _royaltyBps); _setupPrimarySaleRecipient(_saleRecipient); @@ -154,16 +149,6 @@ contract OpenEditionERC721 is } uint256 totalPrice = _quantityToClaim * _pricePerToken; - uint256 platformFees; - address platformFeeRecipient; - - if (getPlatformFeeType() == IPlatformFee.PlatformFeeType.Flat) { - (platformFeeRecipient, platformFees) = getFlatPlatformFeeInfo(); - } else { - (address recipient, uint16 platformFeeBps) = getPlatformFeeInfo(); - platformFeeRecipient = recipient; - platformFees = ((totalPrice * platformFeeBps) / MAX_BPS); - } bool validMsgValue; if (_currency == CurrencyTransferLib.NATIVE_TOKEN) { @@ -175,8 +160,7 @@ contract OpenEditionERC721 is address saleRecipient = _primarySaleRecipient == address(0) ? primarySaleRecipient() : _primarySaleRecipient; - CurrencyTransferLib.transferCurrency(_currency, _msgSender(), platformFeeRecipient, platformFees); - CurrencyTransferLib.transferCurrency(_currency, _msgSender(), saleRecipient, totalPrice - platformFees); + CurrencyTransferLib.transferCurrency(_currency, _msgSender(), saleRecipient, totalPrice); } /// @dev Transfers the NFTs being claimed. @@ -218,11 +202,6 @@ contract OpenEditionERC721 is return hasRole(minterRole, _msgSender()); } - /// @dev Checks whether platform fee info can be set in the given execution context. - function _canSetPlatformFeeInfo() internal view override returns (bool) { - return hasRole(DEFAULT_ADMIN_ROLE, _msgSender()); - } - /*/////////////////////////////////////////////////////////////// Miscellaneous //////////////////////////////////////////////////////////////*/ diff --git a/src/test/OpenEditionERC721.t.sol b/src/test/OpenEditionERC721.t.sol index 36ae738b8..fb9310f30 100644 --- a/src/test/OpenEditionERC721.t.sol +++ b/src/test/OpenEditionERC721.t.sol @@ -42,9 +42,7 @@ contract OpenEditionERC721Test is BaseTest { forwarders(), saleRecipient, royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient + royaltyBps ) ) ) diff --git a/src/test/OpenEditionERC721FlatFee.t.sol b/src/test/OpenEditionERC721FlatFee.t.sol new file mode 100644 index 000000000..4c94b0ae7 --- /dev/null +++ b/src/test/OpenEditionERC721FlatFee.t.sol @@ -0,0 +1,796 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.0; + +import { ERC721AUpgradeable, OpenEditionERC721FlatFee, ISharedMetadata } from "contracts/prebuilts/open-edition/OpenEditionERC721FlatFee.sol"; +import { NFTMetadataRenderer } from "contracts/lib/NFTMetadataRenderer.sol"; +import { TWProxy } from "contracts/infra/TWProxy.sol"; + +// Test imports +import "erc721a-upgradeable/contracts/IERC721AUpgradeable.sol"; +import "./utils/BaseTest.sol"; +import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol"; + +contract OpenEditionERC721FlatFeeTest is BaseTest { + using Strings for uint256; + using Strings for address; + + event SharedMetadataUpdated(string name, string description, string imageURI, string animationURI); + + OpenEditionERC721FlatFee public openEdition; + ISharedMetadata.SharedMetadataInfo public sharedMetadata; + + bytes private emptyEncodedBytes = abi.encode("", ""); + + using stdStorage for StdStorage; + + function setUp() public override { + super.setUp(); + address openEditionImpl = address(new OpenEditionERC721FlatFee()); + + vm.prank(deployer); + openEdition = OpenEditionERC721FlatFee( + address( + new TWProxy( + openEditionImpl, + abi.encodeCall( + OpenEditionERC721FlatFee.initialize, + ( + deployer, + NAME, + SYMBOL, + CONTRACT_URI, + forwarders(), + saleRecipient, + royaltyRecipient, + royaltyBps, + platformFeeBps, + platformFeeRecipient + ) + ) + ) + ) + ); + + sharedMetadata = ISharedMetadata.SharedMetadataInfo({ + name: "Test", + description: "Test", + imageURI: "https://test.com", + animationURI: "https://test.com" + }); + + erc20.mint(deployer, 1_000 ether); + vm.deal(deployer, 1_000 ether); + } + + /*/////////////////////////////////////////////////////////////// + Unit tests: misc. + //////////////////////////////////////////////////////////////*/ + + /** + * note: Tests whether contract reverts when a non-holder renounces a role. + */ + function test_revert_nonHolder_renounceRole() public { + address caller = address(0x123); + bytes32 role = keccak256("MINTER_ROLE"); + + vm.prank(caller); + vm.expectRevert( + abi.encodePacked( + "Permissions: account ", + Strings.toHexString(uint160(caller), 20), + " is missing role ", + Strings.toHexString(uint256(role), 32) + ) + ); + + openEdition.renounceRole(role, caller); + } + + /** + * note: Tests whether contract reverts when a role admin revokes a role for a non-holder. + */ + function test_revert_revokeRoleForNonHolder() public { + address target = address(0x123); + bytes32 role = keccak256("MINTER_ROLE"); + + vm.prank(deployer); + vm.expectRevert( + abi.encodePacked( + "Permissions: account ", + Strings.toHexString(uint160(target), 20), + " is missing role ", + Strings.toHexString(uint256(role), 32) + ) + ); + + openEdition.revokeRole(role, target); + } + + /** + * @dev Tests whether contract reverts when a role is granted to an existent role holder. + */ + function test_revert_grant_role_to_account_with_role() public { + bytes32 role = keccak256("ABC_ROLE"); + address receiver = getActor(0); + + vm.startPrank(deployer); + + openEdition.grantRole(role, receiver); + + vm.expectRevert("Can only grant to non holders"); + openEdition.grantRole(role, receiver); + + vm.stopPrank(); + } + + /** + * @dev Tests contract state for Transfer role. + */ + function test_state_grant_transferRole() public { + bytes32 role = keccak256("TRANSFER_ROLE"); + + // check if admin and address(0) have transfer role in the beginning + bool checkAddressZero = openEdition.hasRole(role, address(0)); + bool checkAdmin = openEdition.hasRole(role, deployer); + assertTrue(checkAddressZero); + assertTrue(checkAdmin); + + // check if transfer role can be granted to a non-holder + address receiver = getActor(0); + vm.startPrank(deployer); + openEdition.grantRole(role, receiver); + + // expect revert when granting to a holder + vm.expectRevert("Can only grant to non holders"); + openEdition.grantRole(role, receiver); + + // check if receiver has transfer role + bool checkReceiver = openEdition.hasRole(role, receiver); + assertTrue(checkReceiver); + + // check if role is correctly revoked + openEdition.revokeRole(role, receiver); + checkReceiver = openEdition.hasRole(role, receiver); + assertFalse(checkReceiver); + openEdition.revokeRole(role, address(0)); + checkAddressZero = openEdition.hasRole(role, address(0)); + assertFalse(checkAddressZero); + + vm.stopPrank(); + } + + /** + * note: Testing transfer of tokens when transfer-role is restricted + */ + function test_claim_transferRole() public { + vm.warp(1); + + address receiver = getActor(0); + bytes32[] memory proofs = new bytes32[](0); + + OpenEditionERC721FlatFee.AllowlistProof memory alp; + alp.proof = proofs; + + OpenEditionERC721FlatFee.ClaimCondition[] memory conditions = new OpenEditionERC721FlatFee.ClaimCondition[](1); + conditions[0].maxClaimableSupply = 100; + conditions[0].quantityLimitPerWallet = 100; + + vm.prank(deployer); + openEdition.setSharedMetadata(sharedMetadata); + vm.prank(deployer); + openEdition.setClaimConditions(conditions, false); + + vm.prank(getActor(5), getActor(5)); + openEdition.claim(receiver, 1, address(0), 0, alp, ""); + + // revoke transfer role from address(0) + vm.prank(deployer); + openEdition.revokeRole(keccak256("TRANSFER_ROLE"), address(0)); + vm.startPrank(receiver); + vm.expectRevert(bytes("!T")); + openEdition.transferFrom(receiver, address(123), 1); + } + + function test_claimCondition_with_startTimestamp() public { + vm.warp(1); + + address receiver = getActor(0); + bytes32[] memory proofs = new bytes32[](0); + + OpenEditionERC721FlatFee.AllowlistProof memory alp; + alp.proof = proofs; + + OpenEditionERC721FlatFee.ClaimCondition[] memory conditions = new OpenEditionERC721FlatFee.ClaimCondition[](1); + conditions[0].startTimestamp = 100; + conditions[0].maxClaimableSupply = 100; + conditions[0].quantityLimitPerWallet = 100; + + vm.prank(deployer); + openEdition.setSharedMetadata(sharedMetadata); + + vm.prank(deployer); + openEdition.setClaimConditions(conditions, false); + + vm.warp(99); + vm.prank(getActor(5), getActor(5)); + vm.expectRevert("!CONDITION."); + openEdition.claim(receiver, 1, address(0), 0, alp, ""); + + vm.warp(100); + vm.prank(getActor(4), getActor(4)); + openEdition.claim(receiver, 1, address(0), 0, alp, ""); + } + + /*/////////////////////////////////////////////////////////////// + Set Shared Metadata Tests + //////////////////////////////////////////////////////////////*/ + + /* + * note: Testing state changes; set shared metadata for tokens. + */ + function test_state_sharedMetadata() public { + // SET METADATA + vm.prank(deployer); + openEdition.setSharedMetadata(sharedMetadata); + + // CLAIM 1 TOKEN + 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); + // revert(); + bytes32 root = abi.decode(result, (bytes32)); + + inputs[1] = "src/test/scripts/getProof.ts"; + result = vm.ffi(inputs); + bytes32[] memory proofs = abi.decode(result, (bytes32[])); + + OpenEditionERC721FlatFee.AllowlistProof memory alp; + alp.proof = proofs; + alp.quantityLimitPerWallet = 300; + alp.pricePerToken = 0; + alp.currency = address(erc20); + + vm.warp(1); + + address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist + + OpenEditionERC721FlatFee.ClaimCondition[] memory conditions = new OpenEditionERC721FlatFee.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.setSharedMetadata(sharedMetadata); + 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, ""); + + string memory uri = openEdition.tokenURI(1); + assertEq( + uri, + NFTMetadataRenderer.createMetadataEdition({ + name: sharedMetadata.name, + description: sharedMetadata.description, + imageURI: sharedMetadata.imageURI, + animationURI: sharedMetadata.animationURI, + tokenOfEdition: 1 + }) + ); + } + + /** + * note: Testing revert condition; an address without MINTER_ROLE calls setSharedMetadata function. + */ + function test_revert_setSharedMetadata_MINTER_ROLE() public { + vm.expectRevert(); + openEdition.setSharedMetadata(sharedMetadata); + } + + /** + * note: Testing event emission; shared metadata set. + */ + function test_event_setSharedMetadata_SharedMetadataUpdated() public { + vm.startPrank(deployer); + + vm.expectEmit(true, false, false, true); + emit SharedMetadataUpdated( + sharedMetadata.name, + sharedMetadata.description, + sharedMetadata.imageURI, + sharedMetadata.animationURI + ); + openEdition.setSharedMetadata(sharedMetadata); + + vm.stopPrank(); + } + + /*/////////////////////////////////////////////////////////////// + Claim Tests + //////////////////////////////////////////////////////////////*/ + + /** + * note: Testing revert condition; exceed max claimable supply. + */ + function test_revert_claimCondition_exceedMaxClaimableSupply() public { + vm.warp(1); + + address receiver = getActor(0); + bytes32[] memory proofs = new bytes32[](0); + + OpenEditionERC721FlatFee.AllowlistProof memory alp; + alp.proof = proofs; + + OpenEditionERC721FlatFee.ClaimCondition[] memory conditions = new OpenEditionERC721FlatFee.ClaimCondition[](1); + conditions[0].maxClaimableSupply = 100; + conditions[0].quantityLimitPerWallet = 200; + + vm.prank(deployer); + openEdition.setSharedMetadata(sharedMetadata); + vm.prank(deployer); + openEdition.setClaimConditions(conditions, false); + + vm.prank(getActor(5), getActor(5)); + openEdition.claim(receiver, 100, address(0), 0, alp, ""); + + vm.expectRevert("!MaxSupply"); + vm.prank(getActor(6), getActor(6)); + openEdition.claim(receiver, 1, address(0), 0, alp, ""); + } + + /** + * note: Testing quantity limit restriction when no allowlist present. + */ + function test_fuzz_claim_noAllowlist(uint256 x) public { + vm.assume(x != 0); + vm.warp(1); + + address receiver = getActor(0); + bytes32[] memory proofs = new bytes32[](0); + + OpenEditionERC721FlatFee.AllowlistProof memory alp; + alp.proof = proofs; + alp.quantityLimitPerWallet = x; + + OpenEditionERC721FlatFee.ClaimCondition[] memory conditions = new OpenEditionERC721FlatFee.ClaimCondition[](1); + conditions[0].maxClaimableSupply = 500; + conditions[0].quantityLimitPerWallet = 100; + + vm.prank(deployer); + openEdition.setSharedMetadata(sharedMetadata); + + vm.prank(deployer); + openEdition.setClaimConditions(conditions, false); + + bytes memory errorQty = "!Qty"; + + vm.prank(getActor(5), getActor(5)); + vm.expectRevert(errorQty); + openEdition.claim(receiver, 0, address(0), 0, alp, ""); + + vm.prank(getActor(5), getActor(5)); + vm.expectRevert(errorQty); + openEdition.claim(receiver, 101, address(0), 0, alp, ""); + + vm.prank(deployer); + openEdition.setClaimConditions(conditions, true); + + vm.prank(getActor(5), getActor(5)); + vm.expectRevert(errorQty); + openEdition.claim(receiver, 101, address(0), 0, alp, ""); + } + + /** + * note: Testing quantity limit restriction + * - allowlist quantity set to some value different than general limit + * - allowlist price set to 0 + */ + function test_state_claim_allowlisted_SetQuantityZeroPrice() public { + 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); + // revert(); + bytes32 root = abi.decode(result, (bytes32)); + + inputs[1] = "src/test/scripts/getProof.ts"; + result = vm.ffi(inputs); + bytes32[] memory proofs = abi.decode(result, (bytes32[])); + + OpenEditionERC721FlatFee.AllowlistProof memory alp; + alp.proof = proofs; + alp.quantityLimitPerWallet = 300; + alp.pricePerToken = 0; + alp.currency = address(erc20); + + vm.warp(1); + + address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist + + OpenEditionERC721FlatFee.ClaimCondition[] memory conditions = new OpenEditionERC721FlatFee.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.setSharedMetadata(sharedMetadata); + 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, ""); // claims for free, because allowlist price is 0 + assertEq(openEdition.getSupplyClaimedByWallet(openEdition.getActiveClaimConditionId(), receiver), 100); + } + + /** + * note: Testing quantity limit restriction + * - allowlist quantity set to some value different than general limit + * - allowlist price set to non-zero value + */ + function test_state_claim_allowlisted_SetQuantityPrice() public { + string[] memory inputs = new string[](5); + + inputs[0] = "node"; + inputs[1] = "src/test/scripts/generateRoot.ts"; + inputs[2] = "300"; + inputs[3] = "5"; + inputs[4] = Strings.toHexString(uint160(address(erc20))); // address of erc20 + + bytes memory result = vm.ffi(inputs); + // revert(); + bytes32 root = abi.decode(result, (bytes32)); + + inputs[1] = "src/test/scripts/getProof.ts"; + result = vm.ffi(inputs); + bytes32[] memory proofs = abi.decode(result, (bytes32[])); + + OpenEditionERC721FlatFee.AllowlistProof memory alp; + alp.proof = proofs; + alp.quantityLimitPerWallet = 300; + alp.pricePerToken = 5; + alp.currency = address(erc20); + + vm.warp(1); + + address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist + + OpenEditionERC721FlatFee.ClaimCondition[] memory conditions = new OpenEditionERC721FlatFee.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.setSharedMetadata(sharedMetadata); + vm.prank(deployer); + openEdition.setClaimConditions(conditions, false); + + vm.prank(receiver, receiver); + vm.expectRevert("!PriceOrCurrency"); + openEdition.claim(receiver, 100, address(erc20), 0, alp, ""); + + erc20.mint(receiver, 10000); + vm.prank(receiver); + erc20.approve(address(openEdition), 10000); + + // vm.prank(getActor(5), getActor(5)); + vm.prank(receiver, receiver); + openEdition.claim(receiver, 100, address(erc20), 5, alp, ""); + assertEq(openEdition.getSupplyClaimedByWallet(openEdition.getActiveClaimConditionId(), receiver), 100); + assertEq(erc20.balanceOf(receiver), 10000 - 500); + } + + /** + * note: Testing quantity limit restriction + * - allowlist quantity set to some value different than general limit + * - allowlist price not set; should default to general price and currency + */ + function test_state_claim_allowlisted_SetQuantityDefaultPrice() public { + string[] memory inputs = new string[](5); + + inputs[0] = "node"; + inputs[1] = "src/test/scripts/generateRoot.ts"; + inputs[2] = "300"; + inputs[3] = Strings.toString(type(uint256).max); // this implies that general price is applicable + inputs[4] = "0x0000000000000000000000000000000000000000"; + + bytes memory result = vm.ffi(inputs); + // revert(); + bytes32 root = abi.decode(result, (bytes32)); + + inputs[1] = "src/test/scripts/getProof.ts"; + result = vm.ffi(inputs); + bytes32[] memory proofs = abi.decode(result, (bytes32[])); + + OpenEditionERC721FlatFee.AllowlistProof memory alp; + alp.proof = proofs; + alp.quantityLimitPerWallet = 300; + alp.pricePerToken = type(uint256).max; + alp.currency = address(0); + + vm.warp(1); + + address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist + + OpenEditionERC721FlatFee.ClaimCondition[] memory conditions = new OpenEditionERC721FlatFee.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.setSharedMetadata(sharedMetadata); + vm.prank(deployer); + openEdition.setClaimConditions(conditions, false); + + erc20.mint(receiver, 10000); + vm.prank(receiver); + erc20.approve(address(openEdition), 10000); + + // vm.prank(getActor(5), getActor(5)); + vm.prank(receiver, receiver); + openEdition.claim(receiver, 100, address(erc20), 10, alp, ""); + assertEq(openEdition.getSupplyClaimedByWallet(openEdition.getActiveClaimConditionId(), receiver), 100); + assertEq(erc20.balanceOf(receiver), 10000 - 1000); + } + + /** + * note: Testing quantity limit restriction + * - allowlist quantity set to 0 => should default to general limit + * - allowlist price set to some value different than general price + */ + function test_state_claim_allowlisted_DefaultQuantitySomePrice() public { + string[] memory inputs = new string[](5); + + inputs[0] = "node"; + inputs[1] = "src/test/scripts/generateRoot.ts"; + inputs[2] = "0"; // this implies that general limit is applicable + inputs[3] = "5"; + inputs[4] = "0x0000000000000000000000000000000000000000"; // general currency will be applicable + + bytes memory result = vm.ffi(inputs); + // revert(); + bytes32 root = abi.decode(result, (bytes32)); + + inputs[1] = "src/test/scripts/getProof.ts"; + result = vm.ffi(inputs); + bytes32[] memory proofs = abi.decode(result, (bytes32[])); + + OpenEditionERC721FlatFee.AllowlistProof memory alp; + alp.proof = proofs; + alp.quantityLimitPerWallet = 0; + alp.pricePerToken = 5; + alp.currency = address(0); + + vm.warp(1); + + address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist + + OpenEditionERC721FlatFee.ClaimCondition[] memory conditions = new OpenEditionERC721FlatFee.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.setSharedMetadata(sharedMetadata); + vm.prank(deployer); + openEdition.setClaimConditions(conditions, false); + + erc20.mint(receiver, 10000); + vm.prank(receiver); + erc20.approve(address(openEdition), 10000); + + bytes memory errorQty = "!Qty"; + vm.prank(receiver, receiver); + vm.expectRevert(errorQty); + openEdition.claim(receiver, 100, address(erc20), 5, alp, ""); // trying to claim more than general limit + + // vm.prank(getActor(5), getActor(5)); + vm.prank(receiver, receiver); + openEdition.claim(receiver, 10, address(erc20), 5, alp, ""); + assertEq(openEdition.getSupplyClaimedByWallet(openEdition.getActiveClaimConditionId(), receiver), 10); + assertEq(erc20.balanceOf(receiver), 10000 - 50); + } + + function test_fuzz_claim_merkleProof(uint256 x) public { + vm.assume(x > 10 && x < 500); + string[] memory inputs = new string[](5); + + inputs[0] = "node"; + inputs[1] = "src/test/scripts/generateRoot.ts"; + inputs[2] = Strings.toString(x); + inputs[3] = "0"; + inputs[4] = "0x0000000000000000000000000000000000000000"; + + bytes memory result = vm.ffi(inputs); + // revert(); + bytes32 root = abi.decode(result, (bytes32)); + + inputs[1] = "src/test/scripts/getProof.ts"; + result = vm.ffi(inputs); + bytes32[] memory proofs = abi.decode(result, (bytes32[])); + + OpenEditionERC721FlatFee.AllowlistProof memory alp; + alp.proof = proofs; + alp.quantityLimitPerWallet = x; + alp.pricePerToken = 0; + alp.currency = address(0); + + vm.warp(1); + + address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); + + // bytes32[] memory proofs = new bytes32[](0); + + OpenEditionERC721FlatFee.ClaimCondition[] memory conditions = new OpenEditionERC721FlatFee.ClaimCondition[](1); + conditions[0].maxClaimableSupply = x; + conditions[0].quantityLimitPerWallet = 1; + conditions[0].merkleRoot = root; + + vm.prank(deployer); + openEdition.setSharedMetadata(sharedMetadata); + vm.prank(deployer); + openEdition.setClaimConditions(conditions, false); + + // vm.prank(getActor(5), getActor(5)); + vm.prank(receiver, receiver); + openEdition.claim(receiver, x - 5, address(0), 0, alp, ""); + assertEq(openEdition.getSupplyClaimedByWallet(openEdition.getActiveClaimConditionId(), receiver), x - 5); + + bytes memory errorQty = "!Qty"; + + vm.prank(receiver, receiver); + vm.expectRevert(errorQty); + openEdition.claim(receiver, 6, address(0), 0, alp, ""); + + vm.prank(receiver, receiver); + openEdition.claim(receiver, 5, address(0), 0, alp, ""); + assertEq(openEdition.getSupplyClaimedByWallet(openEdition.getActiveClaimConditionId(), receiver), x); + + vm.prank(receiver, receiver); + vm.expectRevert(errorQty); + openEdition.claim(receiver, 5, address(0), 0, alp, ""); // quantity limit already claimed + } + + /** + * note: Testing state changes; reset eligibility of claim conditions and claiming again for same condition id. + */ + function test_state_claimCondition_resetEligibility() public { + vm.warp(1); + + address receiver = getActor(0); + bytes32[] memory proofs = new bytes32[](0); + + OpenEditionERC721FlatFee.AllowlistProof memory alp; + alp.proof = proofs; + + OpenEditionERC721FlatFee.ClaimCondition[] memory conditions = new OpenEditionERC721FlatFee.ClaimCondition[](1); + conditions[0].maxClaimableSupply = 500; + conditions[0].quantityLimitPerWallet = 100; + + vm.prank(deployer); + openEdition.setSharedMetadata(sharedMetadata); + + vm.prank(deployer); + openEdition.setClaimConditions(conditions, false); + + vm.prank(getActor(5), getActor(5)); + openEdition.claim(receiver, 100, address(0), 0, alp, ""); + + bytes memory errorQty = "!Qty"; + + vm.prank(getActor(5), getActor(5)); + vm.expectRevert(errorQty); + openEdition.claim(receiver, 100, address(0), 0, alp, ""); + + vm.prank(deployer); + openEdition.setClaimConditions(conditions, true); + + vm.prank(getActor(5), getActor(5)); + openEdition.claim(receiver, 100, address(0), 0, alp, ""); + } + + /*/////////////////////////////////////////////////////////////// + setClaimConditions + //////////////////////////////////////////////////////////////*/ + + function test_claimCondition_startIdAndCount() public { + vm.startPrank(deployer); + + uint256 currentStartId = 0; + uint256 count = 0; + + OpenEditionERC721FlatFee.ClaimCondition[] memory conditions = new OpenEditionERC721FlatFee.ClaimCondition[](2); + conditions[0].startTimestamp = 0; + conditions[0].maxClaimableSupply = 10; + conditions[1].startTimestamp = 1; + conditions[1].maxClaimableSupply = 10; + + openEdition.setClaimConditions(conditions, false); + (currentStartId, count) = openEdition.claimCondition(); + assertEq(currentStartId, 0); + assertEq(count, 2); + + openEdition.setClaimConditions(conditions, false); + (currentStartId, count) = openEdition.claimCondition(); + assertEq(currentStartId, 0); + assertEq(count, 2); + + openEdition.setClaimConditions(conditions, true); + (currentStartId, count) = openEdition.claimCondition(); + assertEq(currentStartId, 2); + assertEq(count, 2); + + openEdition.setClaimConditions(conditions, true); + (currentStartId, count) = openEdition.claimCondition(); + assertEq(currentStartId, 4); + assertEq(count, 2); + } + + function test_claimCondition_startPhase() public { + vm.startPrank(deployer); + + uint256 activeConditionId = 0; + + OpenEditionERC721FlatFee.ClaimCondition[] memory conditions = new OpenEditionERC721FlatFee.ClaimCondition[](3); + conditions[0].startTimestamp = 10; + conditions[0].maxClaimableSupply = 11; + conditions[0].quantityLimitPerWallet = 12; + conditions[1].startTimestamp = 20; + conditions[1].maxClaimableSupply = 21; + conditions[1].quantityLimitPerWallet = 22; + conditions[2].startTimestamp = 30; + conditions[2].maxClaimableSupply = 31; + conditions[2].quantityLimitPerWallet = 32; + openEdition.setClaimConditions(conditions, false); + + vm.expectRevert("!CONDITION."); + openEdition.getActiveClaimConditionId(); + + vm.warp(10); + activeConditionId = openEdition.getActiveClaimConditionId(); + assertEq(activeConditionId, 0); + assertEq(openEdition.getClaimConditionById(activeConditionId).startTimestamp, 10); + assertEq(openEdition.getClaimConditionById(activeConditionId).maxClaimableSupply, 11); + assertEq(openEdition.getClaimConditionById(activeConditionId).quantityLimitPerWallet, 12); + + vm.warp(20); + activeConditionId = openEdition.getActiveClaimConditionId(); + assertEq(activeConditionId, 1); + assertEq(openEdition.getClaimConditionById(activeConditionId).startTimestamp, 20); + assertEq(openEdition.getClaimConditionById(activeConditionId).maxClaimableSupply, 21); + assertEq(openEdition.getClaimConditionById(activeConditionId).quantityLimitPerWallet, 22); + + vm.warp(30); + activeConditionId = openEdition.getActiveClaimConditionId(); + assertEq(activeConditionId, 2); + assertEq(openEdition.getClaimConditionById(activeConditionId).startTimestamp, 30); + assertEq(openEdition.getClaimConditionById(activeConditionId).maxClaimableSupply, 31); + assertEq(openEdition.getClaimConditionById(activeConditionId).quantityLimitPerWallet, 32); + + vm.warp(40); + assertEq(openEdition.getActiveClaimConditionId(), 2); + } +} diff --git a/src/test/open-edition-flat-fee/_beforeTokenTransfers/_beforeTokenTransfers.t.sol b/src/test/open-edition-flat-fee/_beforeTokenTransfers/_beforeTokenTransfers.t.sol new file mode 100644 index 000000000..4ea304d13 --- /dev/null +++ b/src/test/open-edition-flat-fee/_beforeTokenTransfers/_beforeTokenTransfers.t.sol @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.0; + +import { OpenEditionERC721FlatFee } from "contracts/prebuilts/open-edition/OpenEditionERC721FlatFee.sol"; +import { TWProxy } from "contracts/infra/TWProxy.sol"; + +// Test imports +import "src/test/utils/BaseTest.sol"; + +contract OpenEditionERC721FlatFeeHarness is OpenEditionERC721FlatFee { + function beforeTokenTransfers(address from, address to, uint256 startTokenId_, uint256 quantity) public { + _beforeTokenTransfers(from, to, startTokenId_, quantity); + } +} + +contract OpenEditionERC721FlatFeeTest_beforeTokenTransfers is BaseTest { + OpenEditionERC721FlatFeeHarness public openEdition; + + address private openEditionImpl; + + function setUp() public override { + super.setUp(); + openEditionImpl = address(new OpenEditionERC721FlatFeeHarness()); + vm.prank(deployer); + openEdition = OpenEditionERC721FlatFeeHarness( + address( + new TWProxy( + openEditionImpl, + abi.encodeCall( + OpenEditionERC721FlatFee.initialize, + ( + deployer, + NAME, + SYMBOL, + CONTRACT_URI, + forwarders(), + saleRecipient, + royaltyRecipient, + royaltyBps, + platformFeeBps, + platformFeeRecipient + ) + ) + ) + ) + ); + } + + /*/////////////////////////////////////////////////////////////// + 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-flat-fee/_beforeTokenTransfers/_beforeTokenTransfers.tree b/src/test/open-edition-flat-fee/_beforeTokenTransfers/_beforeTokenTransfers.tree new file mode 100644 index 000000000..078bd6a79 --- /dev/null +++ b/src/test/open-edition-flat-fee/_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 ✅ diff --git a/src/test/open-edition-flat-fee/_canSetFunctions/_canSetFunctions.t.sol b/src/test/open-edition-flat-fee/_canSetFunctions/_canSetFunctions.t.sol new file mode 100644 index 000000000..c45ca2514 --- /dev/null +++ b/src/test/open-edition-flat-fee/_canSetFunctions/_canSetFunctions.t.sol @@ -0,0 +1,130 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.0; + +import { OpenEditionERC721FlatFee } from "contracts/prebuilts/open-edition/OpenEditionERC721FlatFee.sol"; +import { TWProxy } from "contracts/infra/TWProxy.sol"; + +// Test imports +import "src/test/utils/BaseTest.sol"; + +contract OpenEditionERC721FlatFeeHarness is OpenEditionERC721FlatFee { + 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 OpenEditionERC721FlatFeeTest_canSetFunctions is BaseTest { + OpenEditionERC721FlatFeeHarness public openEdition; + + address private openEditionImpl; + + function setUp() public override { + super.setUp(); + openEditionImpl = address(new OpenEditionERC721FlatFeeHarness()); + vm.prank(deployer); + openEdition = OpenEditionERC721FlatFeeHarness( + address( + new TWProxy( + openEditionImpl, + abi.encodeCall( + OpenEditionERC721FlatFee.initialize, + ( + deployer, + NAME, + SYMBOL, + CONTRACT_URI, + forwarders(), + saleRecipient, + royaltyRecipient, + royaltyBps, + platformFeeBps, + platformFeeRecipient + ) + ) + ) + ) + ); + } + + /*/////////////////////////////////////////////////////////////// + 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-flat-fee/_canSetFunctions/_canSetFunctions.tree b/src/test/open-edition-flat-fee/_canSetFunctions/_canSetFunctions.tree new file mode 100644 index 000000000..1ccb478fd --- /dev/null +++ b/src/test/open-edition-flat-fee/_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 ✅ diff --git a/src/test/open-edition-flat-fee/_collectPriceOnClaim/_collectPriceOnClaim.t.sol b/src/test/open-edition-flat-fee/_collectPriceOnClaim/_collectPriceOnClaim.t.sol new file mode 100644 index 000000000..bc165f600 --- /dev/null +++ b/src/test/open-edition-flat-fee/_collectPriceOnClaim/_collectPriceOnClaim.t.sol @@ -0,0 +1,209 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.0; + +import { OpenEditionERC721FlatFee } from "contracts/prebuilts/open-edition/OpenEditionERC721FlatFee.sol"; +import { TWProxy } from "contracts/infra/TWProxy.sol"; + +// Test imports +import "src/test/utils/BaseTest.sol"; + +contract OpenEditionERC721FlatFeeHarness is OpenEditionERC721FlatFee { + function collectPriceOnClaim( + address _primarySaleRecipient, + uint256 _quantityToClaim, + address _currency, + uint256 _pricePerToken + ) external payable { + _collectPriceOnClaim(_primarySaleRecipient, _quantityToClaim, _currency, _pricePerToken); + } +} + +contract OpenEditionERC721FlatFeeTest_collectPrice is BaseTest { + OpenEditionERC721FlatFeeHarness 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 OpenEditionERC721FlatFeeHarness()); + vm.prank(deployer); + openEdition = OpenEditionERC721FlatFeeHarness( + address( + new TWProxy( + openEditionImpl, + abi.encodeCall( + OpenEditionERC721FlatFee.initialize, + ( + deployer, + NAME, + SYMBOL, + CONTRACT_URI, + forwarders(), + saleRecipient, + royaltyRecipient, + royaltyBps, + platformFeeBps, + platformFeeRecipient + ) + ) + ) + ) + ); + } + + /*/////////////////////////////////////////////////////////////// + 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 platformFeeVal = (msgValue * platformFeeBps) / 10_000; + uint256 primarySaleRecipientVal = msgValue - platformFeeVal; + + 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 platformFeeVal = (1 ether * platformFeeBps) / 10_000; + uint256 primarySaleRecipientVal = 1 ether - platformFeeVal; + + 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 platformFeeVal = (1 ether * platformFeeBps) / 10_000; + uint256 primarySaleRecipientVal = 1 ether - platformFeeVal; + + 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 platformFeeVal = (msgValue * platformFeeBps) / 10_000; + uint256 primarySaleRecipientVal = msgValue - platformFeeVal; + + assertEq(beforeBalancePrimarySaleRecipient + primarySaleRecipientVal, afterBalancePrimarySaleRecipient); + } +} diff --git a/src/test/open-edition-flat-fee/_collectPriceOnClaim/_collectPriceOnClaim.tree b/src/test/open-edition-flat-fee/_collectPriceOnClaim/_collectPriceOnClaim.tree new file mode 100644 index 000000000..2054cf049 --- /dev/null +++ b/src/test/open-edition-flat-fee/_collectPriceOnClaim/_collectPriceOnClaim.tree @@ -0,0 +1,37 @@ +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 totalPrice to primarySaleRecipient in native token ✅ + │ │ └── when currency is not native 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 ✅ + │ │ └── when msg.value does equal totalPrice + │ │ └── it should transfer totalPrice to saleRecipient for _tokenId in native token ✅ + │ └── when currency is not native 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 ✅ + │ └── when msg.value does equal totalPrice + │ └── it should transfer totalPrice to _primarySaleRecipient in native token ✅ + └── when currency is not native token + └── it should transfer totalPrice to _primarySaleRecipient in _currency token ✅ \ No newline at end of file diff --git a/src/test/open-edition-flat-fee/_transferTokensOnClaim/_transferTokensOnClaim.t.sol b/src/test/open-edition-flat-fee/_transferTokensOnClaim/_transferTokensOnClaim.t.sol new file mode 100644 index 000000000..681ca6caa --- /dev/null +++ b/src/test/open-edition-flat-fee/_transferTokensOnClaim/_transferTokensOnClaim.t.sol @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.0; + +import { OpenEditionERC721FlatFee, IERC721AUpgradeable } from "contracts/prebuilts/open-edition/OpenEditionERC721FlatFee.sol"; +import { TWProxy } from "contracts/infra/TWProxy.sol"; + +// Test imports +import "src/test/utils/BaseTest.sol"; + +contract OpenEditionERC721FlatFeeHarness is OpenEditionERC721FlatFee { + 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 OpenEditionERC721FlatFeeTest_transferTokensOnClaim is BaseTest { + OpenEditionERC721FlatFeeHarness public openEdition; + + MockERC721NotReceiver private notReceiver; + MockERC721Receiver private receiver; + + address private openEditionImpl; + + function setUp() public override { + super.setUp(); + openEditionImpl = address(new OpenEditionERC721FlatFeeHarness()); + vm.prank(deployer); + openEdition = OpenEditionERC721FlatFeeHarness( + address( + new TWProxy( + openEditionImpl, + abi.encodeCall( + OpenEditionERC721FlatFee.initialize, + ( + deployer, + NAME, + SYMBOL, + CONTRACT_URI, + forwarders(), + saleRecipient, + royaltyRecipient, + royaltyBps, + platformFeeBps, + platformFeeRecipient + ) + ) + ) + ) + ); + + 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-flat-fee/_transferTokensOnClaim/_transferTokensOnClaim.tree b/src/test/open-edition-flat-fee/_transferTokensOnClaim/_transferTokensOnClaim.tree new file mode 100644 index 000000000..bddcf87f6 --- /dev/null +++ b/src/test/open-edition-flat-fee/_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 diff --git a/src/test/open-edition-flat-fee/initialize/initialize.t.sol b/src/test/open-edition-flat-fee/initialize/initialize.t.sol new file mode 100644 index 000000000..61c1f3a22 --- /dev/null +++ b/src/test/open-edition-flat-fee/initialize/initialize.t.sol @@ -0,0 +1,255 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.0; + +import { OpenEditionERC721FlatFee } from "contracts/prebuilts/open-edition/OpenEditionERC721FlatFee.sol"; +import { TWProxy } from "contracts/infra/TWProxy.sol"; + +// Test imports +import "src/test/utils/BaseTest.sol"; + +contract OpenEditionERC721FlatFeeTest_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); + + OpenEditionERC721FlatFee 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, + uint128 _platformFeeBps, + address _platformFeeRecipient, + address _imp + ) public { + vm.prank(deployer); + openEdition = OpenEditionERC721FlatFee( + address( + new TWProxy( + _imp, + abi.encodeCall( + OpenEditionERC721FlatFee.initialize, + ( + _defaultAdmin, + _name, + _symbol, + _contractURI, + _trustedForwarders, + _saleRecipient, + _royaltyRecipient, + _royaltyBps, + _platformFeeBps, + _platformFeeRecipient + ) + ) + ) + ) + ); + } + + function setUp() public override { + super.setUp(); + openEditionImpl = address(new OpenEditionERC721FlatFee()); + } + + /*/////////////////////////////////////////////////////////////// + Unit tests: initialize + //////////////////////////////////////////////////////////////*/ + + function test_state() public { + deployOpenEdition( + deployer, + NAME, + SYMBOL, + CONTRACT_URI, + forwarders(), + saleRecipient, + royaltyRecipient, + royaltyBps, + platformFeeBps, + platformFeeRecipient, + 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, + platformFeeBps, + platformFeeRecipient, + 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, + platformFeeBps, + platformFeeRecipient, + 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, + platformFeeBps, + platformFeeRecipient, + 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, + platformFeeBps, + platformFeeRecipient, + 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, + platformFeeBps, + platformFeeRecipient, + 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, + platformFeeBps, + platformFeeRecipient, + 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, + platformFeeBps, + platformFeeRecipient, + 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, + platformFeeBps, + platformFeeRecipient, + openEditionImpl + ); + } +} diff --git a/src/test/open-edition-flat-fee/initialize/initialize.tree b/src/test/open-edition-flat-fee/initialize/initialize.tree new file mode 100644 index 000000000..f56ad144b --- /dev/null +++ b/src/test/open-edition-flat-fee/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") ✅ diff --git a/src/test/open-edition-flat-fee/misc/misc.t.sol b/src/test/open-edition-flat-fee/misc/misc.t.sol new file mode 100644 index 000000000..760373600 --- /dev/null +++ b/src/test/open-edition-flat-fee/misc/misc.t.sol @@ -0,0 +1,233 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.0; + +import { IERC721AUpgradeable, OpenEditionERC721FlatFee, ISharedMetadata } from "contracts/prebuilts/open-edition/OpenEditionERC721FlatFee.sol"; +import { NFTMetadataRenderer } from "contracts/lib/NFTMetadataRenderer.sol"; +import { TWProxy } from "contracts/infra/TWProxy.sol"; + +// Test imports +import "src/test/utils/BaseTest.sol"; +import "@openzeppelin/contracts-upgradeable/interfaces/IERC2981Upgradeable.sol"; + +contract HarnessOpenEditionERC721FlatFee is OpenEditionERC721FlatFee { + function msgData() public view returns (bytes memory) { + return _msgData(); + } +} + +contract OpenEditionERC721FlatFeeTest_misc is BaseTest { + OpenEditionERC721FlatFee public openEdition; + HarnessOpenEditionERC721FlatFee public harnessOpenEdition; + + address private openEditionImpl; + address private harnessImpl; + + address private receiver = 0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd; + + ISharedMetadata.SharedMetadataInfo public sharedMetadata; + + function setUp() public override { + super.setUp(); + openEditionImpl = address(new OpenEditionERC721FlatFee()); + vm.prank(deployer); + openEdition = OpenEditionERC721FlatFee( + address( + new TWProxy( + openEditionImpl, + abi.encodeCall( + OpenEditionERC721FlatFee.initialize, + ( + deployer, + NAME, + SYMBOL, + CONTRACT_URI, + forwarders(), + saleRecipient, + royaltyRecipient, + royaltyBps, + platformFeeBps, + platformFeeRecipient + ) + ) + ) + ) + ); + + sharedMetadata = ISharedMetadata.SharedMetadataInfo({ + name: "Test", + description: "Test", + imageURI: "https://test.com", + animationURI: "https://test.com" + }); + } + + function deployHarness() internal { + harnessImpl = address(new HarnessOpenEditionERC721FlatFee()); + harnessOpenEdition = HarnessOpenEditionERC721FlatFee( + address( + new TWProxy( + harnessImpl, + abi.encodeCall( + OpenEditionERC721FlatFee.initialize, + ( + deployer, + NAME, + SYMBOL, + CONTRACT_URI, + forwarders(), + saleRecipient, + royaltyRecipient, + royaltyBps, + platformFeeBps, + platformFeeRecipient + ) + ) + ) + ) + ); + } + + /*/////////////////////////////////////////////////////////////// + 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[])); + + OpenEditionERC721FlatFee.AllowlistProof memory alp; + alp.proof = proofs; + alp.quantityLimitPerWallet = 300; + alp.pricePerToken = 0; + alp.currency = address(erc20); + + vm.warp(1); + + OpenEditionERC721FlatFee.ClaimCondition[] memory conditions = new OpenEditionERC721FlatFee.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(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); + } + + function test_msgData_returnValue() public { + deployHarness(); + bytes memory msgData = harnessOpenEdition.msgData(); + bytes4 expectedData = harnessOpenEdition.msgData.selector; + assertEq(bytes4(msgData), expectedData); + } +} diff --git a/src/test/open-edition-flat-fee/misc/misc.tree b/src/test/open-edition-flat-fee/misc/misc.tree new file mode 100644 index 000000000..07abb950c --- /dev/null +++ b/src/test/open-edition-flat-fee/misc/misc.tree @@ -0,0 +1,33 @@ +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) +├── 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 ✅ + +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 diff --git a/src/test/open-edition/_beforeTokenTransfers/_beforeTokenTransfers.t.sol b/src/test/open-edition/_beforeTokenTransfers/_beforeTokenTransfers.t.sol index 27f1a3763..1ed885681 100644 --- a/src/test/open-edition/_beforeTokenTransfers/_beforeTokenTransfers.t.sol +++ b/src/test/open-edition/_beforeTokenTransfers/_beforeTokenTransfers.t.sol @@ -36,9 +36,7 @@ contract OpenEditionERC721Test_beforeTokenTransfers is BaseTest { forwarders(), saleRecipient, royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient + royaltyBps ) ) ) diff --git a/src/test/open-edition/_canSetFunctions/_canSetFunctions.t.sol b/src/test/open-edition/_canSetFunctions/_canSetFunctions.t.sol index 8f9b10118..6903bdae7 100644 --- a/src/test/open-edition/_canSetFunctions/_canSetFunctions.t.sol +++ b/src/test/open-edition/_canSetFunctions/_canSetFunctions.t.sol @@ -60,9 +60,7 @@ contract OpenEditionERC721Test_canSetFunctions is BaseTest { forwarders(), saleRecipient, royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient + royaltyBps ) ) ) diff --git a/src/test/open-edition/_collectPriceOnClaim/_collectPriceOnClaim.t.sol b/src/test/open-edition/_collectPriceOnClaim/_collectPriceOnClaim.t.sol index 4f26fb1d3..5b49bcdd7 100644 --- a/src/test/open-edition/_collectPriceOnClaim/_collectPriceOnClaim.t.sol +++ b/src/test/open-edition/_collectPriceOnClaim/_collectPriceOnClaim.t.sol @@ -47,9 +47,7 @@ contract OpenEditionERC721Test_collectPrice is BaseTest { forwarders(), saleRecipient, royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient + royaltyBps ) ) ) diff --git a/src/test/open-edition/_transferTokensOnClaim/_transferTokensOnClaim.t.sol b/src/test/open-edition/_transferTokensOnClaim/_transferTokensOnClaim.t.sol index d66cf9c71..d0d2f2173 100644 --- a/src/test/open-edition/_transferTokensOnClaim/_transferTokensOnClaim.t.sol +++ b/src/test/open-edition/_transferTokensOnClaim/_transferTokensOnClaim.t.sol @@ -47,9 +47,7 @@ contract OpenEditionERC721Test_transferTokensOnClaim is BaseTest { forwarders(), saleRecipient, royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient + royaltyBps ) ) ) diff --git a/src/test/open-edition/initialize/initialize.t.sol b/src/test/open-edition/initialize/initialize.t.sol index 31eff03d0..270118999 100644 --- a/src/test/open-edition/initialize/initialize.t.sol +++ b/src/test/open-edition/initialize/initialize.t.sol @@ -26,8 +26,6 @@ contract OpenEditionERC721Test_initialize is BaseTest { address _saleRecipient, address _royaltyRecipient, uint128 _royaltyBps, - uint128 _platformFeeBps, - address _platformFeeRecipient, address _imp ) public { vm.prank(deployer); @@ -45,9 +43,7 @@ contract OpenEditionERC721Test_initialize is BaseTest { _trustedForwarders, _saleRecipient, _royaltyRecipient, - _royaltyBps, - _platformFeeBps, - _platformFeeRecipient + _royaltyBps ) ) ) @@ -74,8 +70,6 @@ contract OpenEditionERC721Test_initialize is BaseTest { saleRecipient, royaltyRecipient, royaltyBps, - platformFeeBps, - platformFeeRecipient, openEditionImpl ); @@ -117,8 +111,6 @@ contract OpenEditionERC721Test_initialize is BaseTest { saleRecipient, royaltyRecipient, _royaltyBps, - platformFeeBps, - platformFeeRecipient, openEditionImpl ); } @@ -135,8 +127,6 @@ contract OpenEditionERC721Test_initialize is BaseTest { saleRecipient, royaltyRecipient, royaltyBps, - platformFeeBps, - platformFeeRecipient, openEditionImpl ); } @@ -153,8 +143,6 @@ contract OpenEditionERC721Test_initialize is BaseTest { saleRecipient, royaltyRecipient, royaltyBps, - platformFeeBps, - platformFeeRecipient, openEditionImpl ); } @@ -172,8 +160,6 @@ contract OpenEditionERC721Test_initialize is BaseTest { saleRecipient, royaltyRecipient, royaltyBps, - platformFeeBps, - platformFeeRecipient, openEditionImpl ); } @@ -191,8 +177,6 @@ contract OpenEditionERC721Test_initialize is BaseTest { saleRecipient, royaltyRecipient, royaltyBps, - platformFeeBps, - platformFeeRecipient, openEditionImpl ); } @@ -210,8 +194,6 @@ contract OpenEditionERC721Test_initialize is BaseTest { saleRecipient, royaltyRecipient, royaltyBps, - platformFeeBps, - platformFeeRecipient, openEditionImpl ); } @@ -229,8 +211,6 @@ contract OpenEditionERC721Test_initialize is BaseTest { saleRecipient, royaltyRecipient, royaltyBps, - platformFeeBps, - platformFeeRecipient, openEditionImpl ); } @@ -247,8 +227,6 @@ contract OpenEditionERC721Test_initialize is BaseTest { saleRecipient, royaltyRecipient, royaltyBps, - platformFeeBps, - platformFeeRecipient, openEditionImpl ); } diff --git a/src/test/open-edition/misc/misc.t.sol b/src/test/open-edition/misc/misc.t.sol index 1ee2a71c2..44e6b20cb 100644 --- a/src/test/open-edition/misc/misc.t.sol +++ b/src/test/open-edition/misc/misc.t.sol @@ -44,9 +44,7 @@ contract OpenEditionERC721Test_misc is BaseTest { forwarders(), saleRecipient, royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient + royaltyBps ) ) ) @@ -77,9 +75,7 @@ contract OpenEditionERC721Test_misc is BaseTest { forwarders(), saleRecipient, royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient + royaltyBps ) ) )