diff --git a/contracts/extension/interface/IMintFeeManager.sol b/contracts/extension/interface/IMintFeeManager.sol new file mode 100644 index 000000000..771689efe --- /dev/null +++ b/contracts/extension/interface/IMintFeeManager.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.20; + +interface IMintFeeManager { + function calculatePlatformFeeAndRecipient( + uint256 _price + ) external view returns (uint256 platformFee, address feeRecipient); +} diff --git a/contracts/prebuilts/drop/DropERC721.sol b/contracts/prebuilts/drop/DropERC721.sol index f7bbb7e79..80d9e589e 100644 --- a/contracts/prebuilts/drop/DropERC721.sol +++ b/contracts/prebuilts/drop/DropERC721.sol @@ -37,6 +37,8 @@ import "../../extension/LazyMint.sol"; import "../../extension/PermissionsEnumerable.sol"; import "../../extension/Drop.sol"; +import "../../extension/interface/IMintFeeManager.sol"; + contract DropERC721 is Initializable, ContractMetadata, @@ -68,12 +70,11 @@ contract DropERC721 is /// @dev Max bps in the thirdweb system. uint256 private constant MAX_BPS = 10_000; - address public constant DEFAULT_FEE_RECIPIENT = 0x1Af20C6B23373350aD464700B5965CE4B0D2aD94; - uint16 private constant DEFAULT_FEE_BPS = 100; - /// @dev Global max total supply of NFTs. uint256 public maxTotalSupply; + address public immutable mintFeeManager; + /// @dev Emitted when the global max supply of tokens is updated. event MaxTotalSupplyUpdated(uint256 maxTotalSupply); @@ -81,7 +82,9 @@ contract DropERC721 is Constructor + initializer logic //////////////////////////////////////////////////////////////*/ - constructor() initializer {} + constructor(address _mintFeeManager) initializer { + mintFeeManager = _mintFeeManager; + } /// @dev Initializes the contract, like a constructor. function initialize( @@ -264,8 +267,16 @@ contract DropERC721 is address saleRecipient = _primarySaleRecipient == address(0) ? primarySaleRecipient() : _primarySaleRecipient; uint256 totalPrice = _quantityToClaim * _pricePerToken; - uint256 platformFeesTw = (totalPrice * DEFAULT_FEE_BPS) / MAX_BPS; + uint256 platformFees = (totalPrice * platformFeeBps) / MAX_BPS; + address _mintFeeManager = mintFeeManager; + uint256 platformFeesTw = 0; + address feeRecipientTw; + if (_mintFeeManager != address(0)) { + (platformFeesTw, feeRecipientTw) = IMintFeeManager(_mintFeeManager).calculatePlatformFeeAndRecipient( + totalPrice + ); + } bool validMsgValue; if (_currency == CurrencyTransferLib.NATIVE_TOKEN) { @@ -275,7 +286,7 @@ contract DropERC721 is } require(validMsgValue, "!V"); - CurrencyTransferLib.transferCurrency(_currency, _msgSender(), DEFAULT_FEE_RECIPIENT, platformFeesTw); + CurrencyTransferLib.transferCurrency(_currency, _msgSender(), feeRecipientTw, platformFeesTw); CurrencyTransferLib.transferCurrency(_currency, _msgSender(), platformFeeRecipient, platformFees); CurrencyTransferLib.transferCurrency( _currency, diff --git a/src/test/drop/drop-erc721/_collectPriceOnClaim/_collectPriceOnClaim.t.sol b/src/test/drop/drop-erc721/_collectPriceOnClaim/_collectPriceOnClaim.t.sol index 6b345cf7a..850d74a99 100644 --- a/src/test/drop/drop-erc721/_collectPriceOnClaim/_collectPriceOnClaim.t.sol +++ b/src/test/drop/drop-erc721/_collectPriceOnClaim/_collectPriceOnClaim.t.sol @@ -8,6 +8,8 @@ import { TWProxy } from "contracts/infra/TWProxy.sol"; import "../../../utils/BaseTest.sol"; contract HarnessDropERC721 is DropERC721 { + constructor() DropERC721(address(new MockMintFeeManager(0x1Af20C6B23373350aD464700B5965CE4B0D2aD94, 100))) {} + function collectionPriceOnClaim( address _primarySaleRecipient, uint256 _quantityToClaim, @@ -50,7 +52,7 @@ contract DropERC721Test_collectPrice is BaseTest { dropImp = address(new HarnessDropERC721()); proxy = HarnessDropERC721(address(new TWProxy(dropImp, initializeData))); - defaultFeeRecipient = proxy.DEFAULT_FEE_RECIPIENT(); + defaultFeeRecipient = DEFAULT_FEE_RECIPIENT; } modifier pricePerTokenZero() { diff --git a/src/test/drop/drop-erc721/_transferTokensOnClaim/_transferTokensOnClaim.t.sol b/src/test/drop/drop-erc721/_transferTokensOnClaim/_transferTokensOnClaim.t.sol index 87e98d606..55c8b1c53 100644 --- a/src/test/drop/drop-erc721/_transferTokensOnClaim/_transferTokensOnClaim.t.sol +++ b/src/test/drop/drop-erc721/_transferTokensOnClaim/_transferTokensOnClaim.t.sol @@ -9,6 +9,8 @@ import "erc721a-upgradeable/contracts/IERC721AUpgradeable.sol"; import "../../../utils/BaseTest.sol"; contract HarnessDropERC721 is DropERC721 { + constructor() DropERC721(address(new MockMintFeeManager(0x1Af20C6B23373350aD464700B5965CE4B0D2aD94, 100))) {} + function transferTokensOnClaim(address _to, uint256 _quantityToClaim) public payable { _transferTokensOnClaim(_to, _quantityToClaim); } diff --git a/src/test/drop/drop-erc721/miscellaneous/miscellaneous.t.sol b/src/test/drop/drop-erc721/miscellaneous/miscellaneous.t.sol index 9ec52d8f8..2043aec16 100644 --- a/src/test/drop/drop-erc721/miscellaneous/miscellaneous.t.sol +++ b/src/test/drop/drop-erc721/miscellaneous/miscellaneous.t.sol @@ -189,6 +189,8 @@ contract DropERC721Test_misc is BaseTest { } contract HarnessDropERC721MsgData is DropERC721 { + constructor() DropERC721(address(new MockMintFeeManager(0x1Af20C6B23373350aD464700B5965CE4B0D2aD94, 100))) {} + function msgData() public view returns (bytes memory) { return _msgData(); } diff --git a/src/test/mocks/MockMintFeeManager.sol b/src/test/mocks/MockMintFeeManager.sol new file mode 100644 index 000000000..3e7688609 --- /dev/null +++ b/src/test/mocks/MockMintFeeManager.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.11; + +contract MockMintFeeManager { + address public feeRecipient; + uint256 public feeBps; + + constructor(address _feeRecipient, uint256 _feeBps) { + feeRecipient = _feeRecipient; + feeBps = _feeBps; + } + + function calculatePlatformFeeAndRecipient( + uint256 _price + ) external view returns (uint256 _platformFee, address _feeRecipient) { + _platformFee = (_price * feeBps) / 10_000; + _feeRecipient = feeRecipient; + } +} diff --git a/src/test/utils/BaseTest.sol b/src/test/utils/BaseTest.sol index a26ff27ad..d79df67d1 100644 --- a/src/test/utils/BaseTest.sol +++ b/src/test/utils/BaseTest.sol @@ -49,12 +49,15 @@ import { ERC721Holder, IERC721Receiver } from "@openzeppelin/contracts/token/ERC import { Clones } from "@openzeppelin/contracts/proxy/Clones.sol"; import { Strings } from "contracts/lib/Strings.sol"; import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; +import { MockMintFeeManager } from "../mocks/MockMintFeeManager.sol"; abstract contract BaseTest is DSTest, Test { string public constant NAME = "NAME"; string public constant SYMBOL = "SYMBOL"; string public constant CONTRACT_URI = "CONTRACT_URI"; address public constant NATIVE_TOKEN = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; + address public constant DEFAULT_FEE_RECIPIENT = 0x1Af20C6B23373350aD464700B5965CE4B0D2aD94; + uint16 public constant DEFAULT_FEE_BPS = 100; MockERC20 public erc20; MockERC20 public erc20Aux; @@ -63,6 +66,7 @@ abstract contract BaseTest is DSTest, Test { MockERC721NonBurnable public erc721NonBurnable; MockERC1155NonBurnable public erc1155NonBurnable; WETH9 public weth; + MockMintFeeManager public mintFeeManager; address public forwarder; address public eoaForwarder; @@ -121,6 +125,8 @@ abstract contract BaseTest is DSTest, Test { contractPublisher = address(new ContractPublisher(factoryAdmin, forwarders(), new MockContractPublisher())); linkToken = address(new Link()); vrfV2Wrapper = address(new VRFV2Wrapper()); + mintFeeManager = new MockMintFeeManager(DEFAULT_FEE_RECIPIENT, DEFAULT_FEE_BPS); + TWRegistry(registry).grantRole(TWRegistry(registry).OPERATOR_ROLE(), factory); TWRegistry(registry).grantRole(TWRegistry(registry).OPERATOR_ROLE(), contractPublisher); @@ -129,7 +135,7 @@ abstract contract BaseTest is DSTest, Test { TWFactory(factory).addImplementation(address(new TokenERC1155())); TWFactory(factory).addImplementation(address(new DropERC20())); TWFactory(factory).addImplementation(address(new MockContract(bytes32("DropERC721"), 1))); - TWFactory(factory).addImplementation(address(new DropERC721())); + TWFactory(factory).addImplementation(address(new DropERC721(address(mintFeeManager)))); TWFactory(factory).addImplementation(address(new MockContract(bytes32("DropERC1155"), 1))); TWFactory(factory).addImplementation(address(new DropERC1155())); TWFactory(factory).addImplementation(address(new MockContract(bytes32("SignatureDrop"), 1)));