diff --git a/contracts/modules/SuperMinterV1_1.sol b/contracts/modules/SuperMinterV1_1.sol index f6862705..b5770df2 100644 --- a/contracts/modules/SuperMinterV1_1.sol +++ b/contracts/modules/SuperMinterV1_1.sol @@ -972,7 +972,7 @@ contract SuperMinterV1_1 is ISuperMinterV1_1, EIP712 { c.perTxFlat > MAX_PLATFORM_PER_TX_FLAT_FEE, c.perMintFlat > MAX_PLATFORM_PER_MINT_FLAT_FEE, c.perMintBPS > MAX_PLATFORM_PER_MINT_FEE_BPS, - incentiveSum > c.perTxFlat + incentiveSum > c.perMintFlat ) ) revert InvalidPlatformFeeConfig(); } diff --git a/tests/modules/SuperMinterV1_1.t.sol b/tests/modules/SuperMinterV1_1.t.sol index 2a389879..cbeee8a3 100644 --- a/tests/modules/SuperMinterV1_1.t.sol +++ b/tests/modules/SuperMinterV1_1.t.sol @@ -757,128 +757,129 @@ contract SuperMinterV1_1Tests is TestConfigV2 { } } - // function testMintWithAffiliateFlatFees(uint256) public { - // SuperMinterV1_1Constants memory smc = _superMinterConstants(); - // address[] memory feeRecipients = _twoRandomUniqueAddresses(); - - // // Create a tier 1 mint schedule, without any affiliate root. - // ISuperMinterV1_1.MintCreation memory c; - // c.maxMintable = type(uint32).max; - // c.platform = _randomNonZeroAddress(); - // c.edition = address(edition); - // c.tier = 1; - // c.price = uint96(_bound(_random(), 0, type(uint96).max)); - // c.affiliateFeeBPS = uint16(_bound(_random(), 0, smc.MAX_AFFILIATE_FEE_BPS)); - // c.startTime = 0; - // c.endTime = uint32(block.timestamp + 1000); - // c.maxMintablePerAccount = type(uint32).max; - // assertEq(sm.createEditionMint(c), 0); - - // // Set the tier 1 platform fee config. - // ISuperMinterV1_1.PlatformFeeConfig memory pfc; - // pfc.affiliatePerMintFlat = uint96(_bound(_random(), 0, smc.MAX_PLATFORM_PER_MINT_FLAT_FEE)); - // pfc.perTxFlat = uint96(_bound(_random(), 0, smc.MAX_PLATFORM_PER_TX_FLAT_FEE)); - // pfc.perMintFlat = uint96(_bound(_random(), 0, smc.MAX_PLATFORM_PER_MINT_FLAT_FEE)); - // pfc.perMintBPS = uint16(_bound(_random(), 0, smc.MAX_PLATFORM_PER_MINT_FEE_BPS)); - // pfc.active = true; - // vm.prank(c.platform); - // sm.setPlatformFeeConfig(1, pfc); - - // // Prepare the MintTo struct witha a random quantity. - // ISuperMinterV1_1.MintTo memory p; - // p.edition = address(edition); - // p.tier = 1; - // p.scheduleNum = 0; - // p.to = address(this); - // p.quantity = uint32(_bound(_random(), 0, type(uint32).max)); - - // ISuperMinterV1_1.TotalPriceAndFees memory tpaf; - // tpaf = sm.totalPriceAndFees(address(edition), 1, 0, p.quantity); - // assertEq(tpaf.affiliateFlatFee, pfc.affiliatePerMintFlat * p.quantity); - // assertEq(tpaf.affiliateFee, tpaf.affiliateFlatFee + tpaf.affiliateBPSFee); - - // // Use a lower, non-zero quantity for mint testing. - // p.quantity = uint32(_bound(_random(), 1, 8)); - // tpaf = sm.totalPriceAndFees(address(edition), 1, 0, p.quantity); - // assertEq(tpaf.affiliateFlatFee, pfc.affiliatePerMintFlat * p.quantity); - // assertEq(tpaf.affiliateFee, tpaf.affiliateFlatFee + tpaf.affiliateBPSFee); - - // // Just to ensure we have enough ETH to mint. - // vm.deal(address(this), type(uint192).max); - - // // Test the affiliated path. - // if (_random() % 2 == 0) { - // p.affiliate = _randomNonZeroAddress(); - - // vm.expectEmit(true, true, true, true); - // ISuperMinterV1_1.MintedLogData memory l; - // l.quantity = p.quantity; - // l.fromTokenId = 1; - // l.affiliate = p.affiliate; - // l.affiliated = true; - // l.requiredEtherValue = tpaf.total; - // l.unitPrice = tpaf.unitPrice; - // l.platformFee = tpaf.platformFee; - // l.platformFlatFee = tpaf.platformFlatFee; - // l.affiliateFee = tpaf.affiliateFee; - // l.affiliateFlatFee = tpaf.affiliateFlatFee; - // emit Minted(address(edition), 1, 0, address(this), l, 0); - - // sm.mintTo{ value: tpaf.total }(p); - // assertEq(sm.platformFeesAccrued(c.platform), tpaf.platformFee); - // assertEq(sm.affiliateFeesAccrued(p.affiliate), tpaf.affiliateFee); - // assertEq(address(sm).balance, tpaf.affiliateFee + tpaf.platformFee); - // assertEq(address(edition).balance, (tpaf.total - tpaf.platformFee - tpaf.affiliateFee)); - - // // Perform the withdrawals and check if the balances tally. - // vm.prank(c.platform); - // sm.setPlatformFeeAddress(feeRecipients[0]); - // assertEq(sm.platformFeeAddress(c.platform), feeRecipients[0]); - - // uint256 balanceBefore = address(p.affiliate).balance; - // sm.withdrawForAffiliate(p.affiliate); - // assertEq(address(p.affiliate).balance, balanceBefore + tpaf.affiliateFee); - // assertEq(address(sm).balance, tpaf.platformFee); - - // balanceBefore = address(feeRecipients[0]).balance; - // sm.withdrawForPlatform(c.platform); - // assertEq(address(feeRecipients[0]).balance, balanceBefore + tpaf.platformFee); - // assertEq(sm.platformFeeAddress(c.platform), feeRecipients[0]); - // assertEq(address(sm).balance, 0); - // } else { - // p.affiliate = address(0); - - // vm.expectEmit(true, true, true, true); - // ISuperMinterV1_1.MintedLogData memory l; - // l.quantity = p.quantity; - // l.fromTokenId = 1; - // l.affiliate = address(0); - // l.affiliated = false; - // l.requiredEtherValue = tpaf.total; - // l.unitPrice = tpaf.unitPrice; - // l.platformFee = tpaf.platformFee + tpaf.affiliateFlatFee; - // l.platformFlatFee = tpaf.platformFlatFee + tpaf.affiliateFlatFee; - // l.affiliateFee = 0; - // l.affiliateFlatFee = 0; - // emit Minted(address(edition), 1, 0, address(this), l, 0); - // sm.mintTo{ value: tpaf.total }(p); - - // assertEq(sm.platformFeesAccrued(c.platform), tpaf.affiliateFlatFee + tpaf.platformFee); - // assertEq(address(sm).balance, tpaf.affiliateFlatFee + tpaf.platformFee); - // assertEq(address(edition).balance, (tpaf.total - tpaf.platformFee - tpaf.affiliateFlatFee)); - - // // Perform the withdrawals and check if the balances tally. - // vm.prank(c.platform); - // sm.setPlatformFeeAddress(feeRecipients[0]); - // assertEq(sm.platformFeeAddress(c.platform), feeRecipients[0]); - - // uint256 balanceBefore = address(feeRecipients[0]).balance; - // sm.withdrawForPlatform(c.platform); - // assertEq(address(feeRecipients[0]).balance, balanceBefore + tpaf.affiliateFlatFee + tpaf.platformFee); - // assertEq(sm.platformFeeAddress(c.platform), feeRecipients[0]); - // assertEq(address(sm).balance, 0); - // } - // } + function testMintWithAffiliateIncentive(uint256) public { + SuperMinterV1_1Constants memory smc = _superMinterConstants(); + address[] memory feeRecipients = _twoRandomUniqueAddresses(); + + // Create a tier 1 mint schedule, without any affiliate root. + ISuperMinterV1_1.MintCreation memory c; + c.maxMintable = type(uint32).max; + c.platform = _randomNonZeroAddress(); + c.edition = address(edition); + c.tier = 1; + c.price = uint96(_bound(_random(), 0, type(uint96).max)); + c.affiliateFeeBPS = uint16(_bound(_random(), 0, smc.MAX_AFFILIATE_FEE_BPS)); + c.startTime = 0; + c.endTime = uint32(block.timestamp + 1000); + c.maxMintablePerAccount = type(uint32).max; + assertEq(sm.createEditionMint(c), 0); + + // Set the tier 1 platform fee config. + ISuperMinterV1_1.PlatformFeeConfig memory pfc; + + pfc.perTxFlat = uint96(_bound(_random(), 0, smc.MAX_PLATFORM_PER_TX_FLAT_FEE)); + pfc.perMintFlat = uint96(_bound(_random(), 0, smc.MAX_PLATFORM_PER_MINT_FLAT_FEE)); + pfc.perMintBPS = uint16(_bound(_random(), 0, smc.MAX_PLATFORM_PER_MINT_FEE_BPS)); + pfc.affiliateIncentive = uint96(_bound(_random(), 0, pfc.perMintFlat)); + pfc.active = true; + vm.prank(c.platform); + sm.setPlatformFeeConfig(1, pfc); + + // Prepare the MintTo struct witha a random quantity. + ISuperMinterV1_1.MintTo memory p; + p.edition = address(edition); + p.tier = 1; + p.scheduleNum = 0; + p.to = address(this); + p.quantity = uint32(_bound(_random(), 0, type(uint32).max)); + + ISuperMinterV1_1.TotalPriceAndFees memory tpaf; + tpaf = sm.totalPriceAndFees(address(edition), 1, 0, p.quantity); + assertEq(tpaf.affiliateIncentive, pfc.affiliateIncentive * uint256(p.quantity)); + + // Use a lower, non-zero quantity for mint testing. + p.quantity = uint32(_bound(_random(), 1, 8)); + tpaf = sm.totalPriceAndFees(address(edition), 1, 0, p.quantity); + assertEq(tpaf.affiliateIncentive, pfc.affiliateIncentive * uint256(p.quantity)); + + // Just to ensure we have enough ETH to mint. + vm.deal(address(this), type(uint192).max); + + // Test the affiliated path. + if (_random() % 2 == 0) { + p.affiliate = _randomNonZeroAddress(); + + vm.expectEmit(true, true, true, true); + ISuperMinterV1_1.MintedLogData memory l; + l.quantity = p.quantity; + l.fromTokenId = 1; + l.affiliate = p.affiliate; + l.affiliated = true; + l.requiredEtherValue = tpaf.total; + l.unitPrice = tpaf.unitPrice; + l.finalArtistFee = tpaf.total - tpaf.platformFee - tpaf.affiliateFee; + l.finalPlatformFee = tpaf.platformFee - tpaf.affiliateIncentive; + l.finalAffiliateFee = tpaf.affiliateFee + tpaf.affiliateIncentive; + l.finalFreeMintFee = 0; + l.finalFirstCollectorFee = 0; + emit Minted(address(edition), 1, 0, address(this), l, 0); + + sm.mintTo{ value: tpaf.total }(p); + assertEq(sm.platformFeesAccrued(c.platform), l.finalPlatformFee); + assertEq(sm.affiliateFeesAccrued(p.affiliate), l.finalAffiliateFee); + assertEq(address(sm).balance, l.finalPlatformFee + l.finalAffiliateFee); + assertEq(address(edition).balance, l.finalArtistFee); + + // Perform the withdrawals and check if the balances tally. + vm.prank(c.platform); + sm.setPlatformFeeAddress(feeRecipients[0]); + assertEq(sm.platformFeeAddress(c.platform), feeRecipients[0]); + + uint256 balanceBefore = address(p.affiliate).balance; + sm.withdrawForAffiliate(p.affiliate); + assertEq(address(p.affiliate).balance, balanceBefore + l.finalAffiliateFee); + assertEq(address(sm).balance, l.finalPlatformFee); + + balanceBefore = address(feeRecipients[0]).balance; + sm.withdrawForPlatform(c.platform); + assertEq(address(feeRecipients[0]).balance, balanceBefore + l.finalPlatformFee); + assertEq(sm.platformFeeAddress(c.platform), feeRecipients[0]); + assertEq(address(sm).balance, 0); + } else { + p.affiliate = address(0); + + vm.expectEmit(true, true, true, true); + ISuperMinterV1_1.MintedLogData memory l; + l.quantity = p.quantity; + l.fromTokenId = 1; + l.affiliate = address(0); + l.affiliated = false; + l.requiredEtherValue = tpaf.total; + l.unitPrice = tpaf.unitPrice; + l.finalArtistFee = tpaf.total - tpaf.platformFee; + l.finalPlatformFee = tpaf.platformFee; + l.finalAffiliateFee = 0; + l.finalFreeMintFee = 0; + l.finalFirstCollectorFee = 0; + emit Minted(address(edition), 1, 0, address(this), l, 0); + sm.mintTo{ value: tpaf.total }(p); + + assertEq(sm.platformFeesAccrued(c.platform), l.finalPlatformFee); + assertEq(address(sm).balance, l.finalPlatformFee); + assertEq(address(edition).balance, l.finalArtistFee); + + // Perform the withdrawals and check if the balances tally. + vm.prank(c.platform); + sm.setPlatformFeeAddress(feeRecipients[0]); + assertEq(sm.platformFeeAddress(c.platform), feeRecipients[0]); + + uint256 balanceBefore = address(feeRecipients[0]).balance; + sm.withdrawForPlatform(c.platform); + assertEq(address(feeRecipients[0]).balance, balanceBefore + l.finalPlatformFee); + assertEq(sm.platformFeeAddress(c.platform), feeRecipients[0]); + assertEq(address(sm).balance, 0); + } + } function _checkTotalPriceAndFees( ISuperMinterV1_1.TotalPriceAndFees memory tpaf,