From 8393f28c127fff3f20ed51002524ae7a792ff4a0 Mon Sep 17 00:00:00 2001 From: David Dada Date: Sat, 14 Mar 2026 13:54:05 +0100 Subject: [PATCH 1/6] Forge fmt --- src/libs/LibMarketplace.sol | 40 +++++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/src/libs/LibMarketplace.sol b/src/libs/LibMarketplace.sol index 2aaaae5..a69e40b 100644 --- a/src/libs/LibMarketplace.sol +++ b/src/libs/LibMarketplace.sol @@ -48,12 +48,18 @@ library LibMarketplace { ExtraTicketData memory ticketData = _ticketId._getExtraTicketData(); uint48 time = block.timestamp.toUint48(); - if (time < ticketData.purchaseStartTime) revert PurchaseTimeNotReached(); + if (time < ticketData.purchaseStartTime) { + revert PurchaseTimeNotReached(); + } if (time > ticketData.endTime) revert PurchaseTimeNotReached(); - if (ticketData.soldTickets == ticketData.maxTickets) revert TicketSoldOut(); + if (ticketData.soldTickets == ticketData.maxTickets) { + revert TicketSoldOut(); + } ITicket ticket = ITicket(ticketData.ticketAddress); - if (ticket.balanceOf(_buyer) > ticketData.maxTicketsPerUser) revert MaxTicketsHeld(); + if (ticket.balanceOf(_buyer) > ticketData.maxTicketsPerUser) { + revert MaxTicketsHeld(); + } MarketplaceStorage storage ms = _marketplaceStorage(); (uint256 fee, uint256 hostItFee, uint256 totalFee) = _getFees(ms, _ticketId, _feeType); @@ -61,7 +67,9 @@ library LibMarketplace { if (!_isFeeEnabled(ms, _ticketId, _feeType)) revert FeeNotEnabled(); if (_feeType == FeeType.ETH) { - if (msg.value < totalFee) revert InsufficientBalance(address(0), _feeType, totalFee); + if (msg.value < totalFee) { + revert InsufficientBalance(address(0), _feeType, totalFee); + } } else { _payWithToken(ms, _feeType, totalFee); } @@ -86,12 +94,16 @@ library LibMarketplace { _ticketId._checkTicketExists(); uint256 feeTypesLength = _feeTypes.length; - if (feeTypesLength != _fees.length && feeTypesLength > 0) revert InvalidFeeConfig(); + if (feeTypesLength != _fees.length && feeTypesLength > 0) { + revert InvalidFeeConfig(); + } LibFactory._factoryStorage().ticketIdToData[_ticketId].isFree = false; MarketplaceStorage storage ms = _marketplaceStorage(); for (uint256 i; i < feeTypesLength; ++i) { - if (_isFeeEnabled(ms, _ticketId, _feeTypes[i])) revert FeeAlreadySet(); + if (_isFeeEnabled(ms, _ticketId, _feeTypes[i])) { + revert FeeAlreadySet(); + } if (_fees[i] == 0) revert ZeroFee(); ms.feeEnabled[_ticketId][_feeTypes[i]] = true; @@ -110,7 +122,9 @@ library LibMarketplace { uint48 time = block.timestamp.toUint48(); if (time < ticketData.endTime) revert RefundPeriodNotReached(); - if (time > ticketData.endTime + REFUND_PERIOD) revert RefundPeriodExpired(); + if (time > ticketData.endTime + REFUND_PERIOD) { + revert RefundPeriodExpired(); + } address caller = LibContext._msgSender(); ITicket ticket = ITicket(ticketData.ticketAddress); @@ -143,7 +157,9 @@ library LibMarketplace { ExtraTicketData memory ticketData = _ticketId._getExtraTicketData(); if (ticketData.isRefundable) { - if (block.timestamp < ticketData.endTime + REFUND_PERIOD) revert WithdrawPeriodNotReached(); + if (block.timestamp < ticketData.endTime + REFUND_PERIOD) { + revert WithdrawPeriodNotReached(); + } } uint256 balance = _getTicketBalance(_ticketId, _feeType); @@ -187,7 +203,9 @@ library LibMarketplace { address tokenAddress = _getFeeTokenAddress(_ms, _feeType); IERC20 token = IERC20(tokenAddress); - if (token.balanceOf(caller) < _totalFee) revert InsufficientBalance(tokenAddress, _feeType, _totalFee); + if (token.balanceOf(caller) < _totalFee) { + revert InsufficientBalance(tokenAddress, _feeType, _totalFee); + } if (token.allowance(caller, address(this)) < _totalFee) { revert InsufficientAllowance(tokenAddress, _feeType, _totalFee); } @@ -215,7 +233,9 @@ library LibMarketplace { function _setFeeTokenAddresses(FeeType[] calldata _feeTypes, address[] calldata _tokenAddresses) internal { uint256 feeTypesLength = _feeTypes.length; - if (feeTypesLength != _tokenAddresses.length && feeTypesLength > 0) revert InvalidFeeConfig(); + if (feeTypesLength != _tokenAddresses.length && feeTypesLength > 0) { + revert InvalidFeeConfig(); + } for (uint256 i; i < feeTypesLength; ++i) { if (_tokenAddresses[i] == address(0)) revert TokenAddressZero(); _marketplaceStorage().feeTokenAddress[_feeTypes[i]] = _tokenAddresses[i]; From 488cc1f62cc6e748b3845b4fa1f051dd2b367911 Mon Sep 17 00:00:00 2001 From: David Dada Date: Fri, 3 Apr 2026 05:39:06 +0100 Subject: [PATCH 2/6] chore: clean up imports and refactor test helpers Remove unused imports from deploy scripts, extract fee constants, inline facet deployment, add vm labels, and simplify mintTicketETH helper. --- script/DeployHostItTickets.s.sol | 5 --- script/helpers/DeployHostItTicketsHelper.sol | 4 +- test/states/DeployedHostItTickets.sol | 40 +++++++++++--------- 3 files changed, 26 insertions(+), 23 deletions(-) diff --git a/script/DeployHostItTickets.s.sol b/script/DeployHostItTickets.s.sol index b1f420f..74bb9a6 100644 --- a/script/DeployHostItTickets.s.sol +++ b/script/DeployHostItTickets.s.sol @@ -1,15 +1,10 @@ // SPDX-License-Identifier: AGPL-3.0-only pragma solidity 0.8.30; -import {DiamondCutFacet} from "@diamond/facets/DiamondCutFacet.sol"; -import {DiamondLoupeFacet} from "@diamond/facets/DiamondLoupeFacet.sol"; -import {OwnableRolesFacet} from "@diamond/facets/OwnableRolesFacet.sol"; -import {DiamondInit} from "@diamond/initializers/DiamondInit.sol"; import {IDiamondCut} from "@diamond/interfaces/IDiamondCut.sol"; import {UpgradeableBeacon} from "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol"; import {DeployHostItTicketsHelper} from "@ticket-script/helpers/DeployHostItTicketsHelper.sol"; import {LibAddressesAndFees} from "@ticket-script/helpers/LibAddressesAndFees.sol"; -import {HostItTickets} from "@ticket/HostItTickets.sol"; import {CheckInFacet} from "@ticket/facets/CheckInFacet.sol"; import {FactoryFacet} from "@ticket/facets/FactoryFacet.sol"; import {MarketplaceFacet} from "@ticket/facets/MarketplaceFacet.sol"; diff --git a/script/helpers/DeployHostItTicketsHelper.sol b/script/helpers/DeployHostItTicketsHelper.sol index d7e00f8..cde4b73 100644 --- a/script/helpers/DeployHostItTicketsHelper.sol +++ b/script/helpers/DeployHostItTicketsHelper.sol @@ -75,7 +75,9 @@ abstract contract DeployHostItTicketsHelper is GetSelectors, Context { function _getHostItTickets() internal returns (address) { return HOST_IT_TICKETS.code.length == 0 ? address( - new HostItTickets{salt: HOST_IT_SALT}( + new HostItTickets{ + salt: HOST_IT_SALT + }( _createInitFacetCuts(_getDiamondCutFacet(), _getDiamondLoupeFacet(), _getOwnableRolesFacet()), _getDiamondInit(), abi.encodeWithSignature("initDiamond(address)", _msgSender()) diff --git a/test/states/DeployedHostItTickets.sol b/test/states/DeployedHostItTickets.sol index 15a03cc..c363103 100644 --- a/test/states/DeployedHostItTickets.sol +++ b/test/states/DeployedHostItTickets.sol @@ -1,7 +1,6 @@ // SPDX-License-Identifier: AGPL-3.0-only pragma solidity 0.8.30; -import {FacetCut} from "@diamond-storage/DiamondStorage.sol"; import {IDiamondCut} from "@diamond/interfaces/IDiamondCut.sol"; import {IDiamondLoupe} from "@diamond/interfaces/IDiamondLoupe.sol"; import {ERC20Mock} from "@openzeppelin/contracts/mocks/token/ERC20Mock.sol"; @@ -56,6 +55,10 @@ abstract contract DeployedHostItTickets is Test, DeployHostItTicketsHelper { uint48 public _currentTime = uint48(block.timestamp); + uint256 constant ETH_FEE = 25e14; + uint256 constant USDT_FEE = 10e18; + uint256 constant USDC_FEE = 10e6; + /// @notice Deploys the Diamond contract and initializes interface references and facet addresses. /// @dev This function is intended to be called in a test setup phase (e.g., `setUp()` in Foundry). function setUp() public virtual { @@ -67,11 +70,6 @@ abstract contract DeployedHostItTickets is Test, DeployHostItTicketsHelper { ) ); - // Deploy HostIt facets - address factoryFacetAddr = address(new FactoryFacet()); - address marketplaceFacetAddr = address(new MarketplaceFacet()); - address checkInFacetAddr = address(new CheckInFacet()); - // Deploy initializer address hostItInit = address(new HostItInit()); @@ -91,7 +89,9 @@ abstract contract DeployedHostItTickets is Test, DeployHostItTicketsHelper { // Initialize HostItTickets - called directly from the test (which is the owner) IDiamondCut(hostIt) .diamondCut( - _createHostItFacetCuts(factoryFacetAddr, marketplaceFacetAddr, checkInFacetAddr), + _createHostItFacetCuts( + address(new FactoryFacet()), address(new MarketplaceFacet()), address(new CheckInFacet()) + ), hostItInit, abi.encodeWithSelector(HostItInit.initHostIt.selector, ticketProxy, feeTypes, addresses) ); @@ -105,6 +105,16 @@ abstract contract DeployedHostItTickets is Test, DeployHostItTicketsHelper { facetAddresses = diamondLoupe.facetAddresses(); vm.etch(ERC6551_REGISTRY, address(new ERC6551Registry()).code); + vm.label(alice, "ALICE"); + vm.label(bob, "BOB"); + vm.label(charlie, "CHARLIE"); + vm.label(withdrawer, "WITHDRAWER"); + vm.label(hostIt, "HOSTIT"); + vm.label(ticketProxy, "TICKET_PROXY"); + vm.label(ticketImpl, "TICKET_IMPL"); + vm.label(ticketBeacon, "TICKET_BEACON"); + vm.label(hostItInit, "HOSTIT_INIT"); + vm.label(owner, "TEST_ADDRESS"); } function _mintTicketFree() internal returns (uint64 ticketId_, uint40 tokenId_) { @@ -123,11 +133,7 @@ abstract contract DeployedHostItTickets is Test, DeployHostItTicketsHelper { hoax(alice, totalFee); vm.expectEmit(true, true, true, true, hostIt); emit TicketMinted(ticketId_, FeeType.ETH, totalFee, 1); - (bool success, bytes memory result) = address(marketplaceFacet).call{value: totalFee}( - abi.encodeWithSelector(marketplaceFacet.mintTicket.selector, ticketId_, FeeType.ETH, alice) - ); - assertTrue(success); - tokenId_ = abi.decode(result, (uint40)); + tokenId_ = marketplaceFacet.mintTicket{value: totalFee}(ticketId_, FeeType.ETH, alice); fee_ = fee; hostItFee_ = hostItFee; } @@ -226,7 +232,7 @@ abstract contract DeployedHostItTickets is Test, DeployHostItTicketsHelper { maxTickets: type(uint40).max, maxTicketsPerUser: 0, isFree: false, - isRefundable: true, + isRefundable: false, name: "Paid Ticket", symbol: "", uri: "ipfs://$" @@ -241,7 +247,7 @@ abstract contract DeployedHostItTickets is Test, DeployHostItTicketsHelper { maxTickets: type(uint40).max, maxTicketsPerUser: 0, isFree: false, - isRefundable: true, + isRefundable: false, name: "Updated Paid Ticket", symbol: "UPT", uri: "ipfs://$$" @@ -265,8 +271,8 @@ abstract contract DeployedHostItTickets is Test, DeployHostItTicketsHelper { function _getFees() internal pure returns (uint256[] memory fees_) { fees_ = new uint256[](3); - fees_[0] = 25e14; - fees_[1] = 10e18; - fees_[2] = 10e6; + fees_[0] = ETH_FEE; + fees_[1] = USDT_FEE; + fees_[2] = USDC_FEE; } } From 848b1efbd23cbc24e4610506f687b9fec0c0342c Mon Sep 17 00:00:00 2001 From: David Dada Date: Fri, 3 Apr 2026 05:39:17 +0100 Subject: [PATCH 3/6] feat: direct payment to organizer for non-refundable tickets Split payment flow: refundable tickets escrow funds in the contract, non-refundable tickets send the ticket fee directly to the organizer. For ERC20 non-refundable payments, the fee is sent to the organizer and the hostIt fee is sent to the contract separately. Also updates _payWithToken to accept a destination address parameter. --- src/libs/LibMarketplace.sol | 51 +++++++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 19 deletions(-) diff --git a/src/libs/LibMarketplace.sol b/src/libs/LibMarketplace.sol index a69e40b..c6da564 100644 --- a/src/libs/LibMarketplace.sol +++ b/src/libs/LibMarketplace.sol @@ -47,13 +47,15 @@ library LibMarketplace { ExtraTicketData memory ticketData = _ticketId._getExtraTicketData(); - uint48 time = block.timestamp.toUint48(); - if (time < ticketData.purchaseStartTime) { - revert PurchaseTimeNotReached(); - } - if (time > ticketData.endTime) revert PurchaseTimeNotReached(); - if (ticketData.soldTickets == ticketData.maxTickets) { - revert TicketSoldOut(); + { + uint48 time = block.timestamp.toUint48(); + if (time < ticketData.purchaseStartTime) { + revert PurchaseTimeNotReached(); + } + if (time > ticketData.endTime) revert PurchaseTimeNotReached(); + if (ticketData.soldTickets == ticketData.maxTickets) { + revert TicketSoldOut(); + } } ITicket ticket = ITicket(ticketData.ticketAddress); @@ -66,15 +68,26 @@ library LibMarketplace { if (!ticketData.isFree) { if (!_isFeeEnabled(ms, _ticketId, _feeType)) revert FeeNotEnabled(); - if (_feeType == FeeType.ETH) { - if (msg.value < totalFee) { - revert InsufficientBalance(address(0), _feeType, totalFee); + if (ticketData.isRefundable) { + if (_feeType == FeeType.ETH) { + if (msg.value < totalFee) { + revert TicketPurchaseFailed(_feeType, totalFee); + } + } else { + _payWithToken(ms, _feeType, totalFee, address(this)); } + ms.ticketBalance[_ticketId][_feeType] += fee; } else { - _payWithToken(ms, _feeType, totalFee); + if (_feeType == FeeType.ETH) { + if (msg.value < totalFee) { + revert TicketPurchaseFailed(_feeType, totalFee); + } + ticketData.ticketAdmin.forceSafeTransferETH(fee); + } else { + _payWithToken(ms, _feeType, fee, ticketData.ticketAdmin); + _payWithToken(ms, _feeType, hostItFee, address(this)); + } } - - ms.ticketBalance[_ticketId][_feeType] += fee; ms.hostItBalance[_feeType] += hostItFee; } @@ -191,14 +204,14 @@ library LibMarketplace { delete _marketplaceStorage().hostItBalance[_feeType]; if (_feeType == FeeType.ETH) { - _to.safeTransferETH(balance); + _to.forceSafeTransferETH(balance); } else { _getFeeTokenAddress(_feeType).safeTransfer(_to, balance); } emit HostItBalanceWithdrawn(_feeType, balance, _to); } - function _payWithToken(MarketplaceStorage storage _ms, FeeType _feeType, uint256 _totalFee) internal { + function _payWithToken(MarketplaceStorage storage _ms, FeeType _feeType, uint256 _totalFee, address _to) internal { address caller = LibContext._msgSender(); address tokenAddress = _getFeeTokenAddress(_ms, _feeType); @@ -209,16 +222,16 @@ library LibMarketplace { if (token.allowance(caller, address(this)) < _totalFee) { revert InsufficientAllowance(tokenAddress, _feeType, _totalFee); } - if (!tokenAddress.trySafeTransferFrom(caller, address(this), _totalFee)) { + if (!tokenAddress.trySafeTransferFrom(caller, _to, _totalFee)) { revert TicketPurchaseFailed(_feeType, _totalFee); } } function _createErc6551Account(address _ticketAddress, uint256 _tokenId) internal { try IERC6551Registry(ERC6551_REGISTRY) - .createAccount(ACCOUNT_V3_IMPLEMENTATION, "", block.chainid, _ticketAddress, _tokenId) returns ( - address account - ) { + .createAccount( + ACCOUNT_V3_IMPLEMENTATION, "", block.chainid, _ticketAddress, _tokenId + ) returns (address account) { if (account == address(0)) { revert CreateERC6551AccountFailed(); } From 4e87e41c51f532f7711f6cca52ed24dbcc43d5c5 Mon Sep 17 00:00:00 2001 From: David Dada Date: Fri, 3 Apr 2026 05:39:27 +0100 Subject: [PATCH 4/6] test: update marketplace tests for direct payment flow Update assertions for non-refundable tickets where fees go directly to the organizer. Comment out refund and ticket balance withdrawal tests (no longer applicable for non-refundable tickets). Add receive() to test contract for ETH transfers. --- test/Marketplace.t.sol | 222 +++++++++++++++++++++-------------------- 1 file changed, 112 insertions(+), 110 deletions(-) diff --git a/test/Marketplace.t.sol b/test/Marketplace.t.sol index 3030a80..800ccb7 100644 --- a/test/Marketplace.t.sol +++ b/test/Marketplace.t.sol @@ -26,7 +26,7 @@ contract MarketplaceTest is DeployedHostItTickets, ERC721Holder { ITicket ticket = ITicket(fullTicketData.ticketAddress); assertEq(ticket.ownerOf(tokenId), alice); assertEq(fullTicketData.soldTickets, 1); - assertEq(marketplaceFacet.getTicketBalance(ticketId, FeeType.ETH), fee); + assertEq(alice.balance, 0); assertEq(marketplaceFacet.getHostItBalance(FeeType.ETH), hostItFee); } @@ -36,7 +36,7 @@ contract MarketplaceTest is DeployedHostItTickets, ERC721Holder { ITicket ticket = ITicket(fullTicketData.ticketAddress); assertEq(ticket.ownerOf(tokenId), alice); assertEq(fullTicketData.soldTickets, 1); - assertEq(marketplaceFacet.getTicketBalance(ticketId, FeeType.USDT), fee); + // assertEq(marketplaceFacet.getTicketBalance(ticketId, FeeType.USDT), fee); assertEq(marketplaceFacet.getHostItBalance(FeeType.USDT), hostItFee); } @@ -46,7 +46,7 @@ contract MarketplaceTest is DeployedHostItTickets, ERC721Holder { ITicket ticket = ITicket(fullTicketData.ticketAddress); assertEq(ticket.ownerOf(tokenId), alice); assertEq(fullTicketData.soldTickets, 1); - assertEq(marketplaceFacet.getTicketBalance(ticketId, FeeType.USDC), fee); + // assertEq(marketplaceFacet.getTicketBalance(ticketId, FeeType.USDC), fee); assertEq(marketplaceFacet.getHostItBalance(FeeType.USDC), hostItFee); } @@ -59,118 +59,118 @@ contract MarketplaceTest is DeployedHostItTickets, ERC721Holder { assertEq(marketplaceFacet.getTicketFee(ticketId, FeeType.USDC), _getFees()[2]); } - function test_claimRefundETH() public { - (uint64 ticketId, uint40 tokenId, uint256 ethFee, uint256 hostItFee) = _mintTicketETH(); - FullTicketData memory fullTicketData = factoryFacet.ticketData(ticketId); - ITicket ticket = ITicket(fullTicketData.ticketAddress); - assertEq(ticket.ownerOf(tokenId), alice); - assertEq(fullTicketData.soldTickets, 1); - assertEq(marketplaceFacet.getTicketBalance(ticketId, FeeType.ETH), ethFee); - assertEq(marketplaceFacet.getHostItBalance(FeeType.ETH), hostItFee); - vm.prank(alice); - ticket.approve(hostIt, tokenId); - vm.prank(alice); - vm.warp(fullTicketData.endTime); - vm.expectEmit(true, true, true, true, hostIt); - emit TicketRefunded(ticketId, FeeType.ETH, ethFee, bob); - marketplaceFacet.claimRefund(ticketId, FeeType.ETH, tokenId, bob); - assertEq(ticket.ownerOf(tokenId), owner); - assertEq(fullTicketData.soldTickets, 1); - assertEq(bob.balance, ethFee); - assertEq(marketplaceFacet.getTicketBalance(ticketId, FeeType.ETH), 0); - assertEq(marketplaceFacet.getHostItBalance(FeeType.ETH), hostItFee); - } + // function test_claimRefundETH() public { + // (uint64 ticketId, uint40 tokenId, uint256 ethFee, uint256 hostItFee) = _mintTicketETH(); + // FullTicketData memory fullTicketData = factoryFacet.ticketData(ticketId); + // ITicket ticket = ITicket(fullTicketData.ticketAddress); + // assertEq(ticket.ownerOf(tokenId), alice); + // assertEq(fullTicketData.soldTickets, 1); + // assertEq(marketplaceFacet.getTicketBalance(ticketId, FeeType.ETH), ethFee); + // assertEq(marketplaceFacet.getHostItBalance(FeeType.ETH), hostItFee); + // vm.prank(alice); + // ticket.approve(hostIt, tokenId); + // vm.prank(alice); + // vm.warp(fullTicketData.endTime); + // vm.expectEmit(true, true, true, true, hostIt); + // emit TicketRefunded(ticketId, FeeType.ETH, ethFee, bob); + // marketplaceFacet.claimRefund(ticketId, FeeType.ETH, tokenId, bob); + // assertEq(ticket.ownerOf(tokenId), owner); + // assertEq(fullTicketData.soldTickets, 1); + // assertEq(bob.balance, ethFee); + // assertEq(marketplaceFacet.getTicketBalance(ticketId, FeeType.ETH), 0); + // assertEq(marketplaceFacet.getHostItBalance(FeeType.ETH), hostItFee); + // } - function test_claimRefundUSDT() public { - (uint64 ticketId, uint40 tokenId, uint256 usdtFee, uint256 hostItFee, ERC20Mock usdt) = _mintTicketUSDT(); - FullTicketData memory fullTicketData = factoryFacet.ticketData(ticketId); - ITicket ticket = ITicket(fullTicketData.ticketAddress); - assertEq(ticket.ownerOf(tokenId), alice); - assertEq(fullTicketData.soldTickets, 1); - assertEq(marketplaceFacet.getTicketBalance(ticketId, FeeType.USDT), usdtFee); - assertEq(marketplaceFacet.getHostItBalance(FeeType.USDT), hostItFee); - vm.prank(alice); - ticket.approve(hostIt, tokenId); - vm.prank(alice); - vm.warp(fullTicketData.endTime); - vm.expectEmit(true, true, true, true, hostIt); - emit TicketRefunded(ticketId, FeeType.USDT, usdtFee, bob); - marketplaceFacet.claimRefund(ticketId, FeeType.USDT, tokenId, bob); - assertEq(ticket.ownerOf(tokenId), owner); - assertEq(fullTicketData.soldTickets, 1); - assertEq(usdt.balanceOf(bob), usdtFee); - assertEq(marketplaceFacet.getTicketBalance(ticketId, FeeType.USDT), 0); - assertEq(marketplaceFacet.getHostItBalance(FeeType.USDT), hostItFee); - } + // function test_claimRefundUSDT() public { + // (uint64 ticketId, uint40 tokenId, uint256 usdtFee, uint256 hostItFee, ERC20Mock usdt) = _mintTicketUSDT(); + // FullTicketData memory fullTicketData = factoryFacet.ticketData(ticketId); + // ITicket ticket = ITicket(fullTicketData.ticketAddress); + // assertEq(ticket.ownerOf(tokenId), alice); + // assertEq(fullTicketData.soldTickets, 1); + // assertEq(marketplaceFacet.getTicketBalance(ticketId, FeeType.USDT), usdtFee); + // assertEq(marketplaceFacet.getHostItBalance(FeeType.USDT), hostItFee); + // vm.prank(alice); + // ticket.approve(hostIt, tokenId); + // vm.prank(alice); + // vm.warp(fullTicketData.endTime); + // vm.expectEmit(true, true, true, true, hostIt); + // emit TicketRefunded(ticketId, FeeType.USDT, usdtFee, bob); + // marketplaceFacet.claimRefund(ticketId, FeeType.USDT, tokenId, bob); + // assertEq(ticket.ownerOf(tokenId), owner); + // assertEq(fullTicketData.soldTickets, 1); + // assertEq(usdt.balanceOf(bob), usdtFee); + // assertEq(marketplaceFacet.getTicketBalance(ticketId, FeeType.USDT), 0); + // assertEq(marketplaceFacet.getHostItBalance(FeeType.USDT), hostItFee); + // } - function test_claimRefundUSDC() public { - (uint64 ticketId, uint40 tokenId, uint256 usdcFee, uint256 hostItFee, ERC20Mock usdc) = _mintTicketUSDC(); - FullTicketData memory fullTicketData = factoryFacet.ticketData(ticketId); - ITicket ticket = ITicket(fullTicketData.ticketAddress); - assertEq(ticket.ownerOf(tokenId), alice); - assertEq(fullTicketData.soldTickets, 1); - assertEq(marketplaceFacet.getTicketBalance(ticketId, FeeType.USDC), usdcFee); - assertEq(marketplaceFacet.getHostItBalance(FeeType.USDC), hostItFee); - vm.prank(alice); - ticket.approve(hostIt, tokenId); - vm.prank(alice); - vm.warp(fullTicketData.endTime); - vm.expectEmit(true, true, true, true, hostIt); - emit TicketRefunded(ticketId, FeeType.USDC, usdcFee, bob); - marketplaceFacet.claimRefund(ticketId, FeeType.USDC, tokenId, bob); - assertEq(ticket.ownerOf(tokenId), owner); - assertEq(fullTicketData.soldTickets, 1); - assertEq(usdc.balanceOf(bob), usdcFee); - assertEq(marketplaceFacet.getTicketBalance(ticketId, FeeType.USDC), 0); - assertEq(marketplaceFacet.getHostItBalance(FeeType.USDC), hostItFee); - } + // function test_claimRefundUSDC() public { + // (uint64 ticketId, uint40 tokenId, uint256 usdcFee, uint256 hostItFee, ERC20Mock usdc) = _mintTicketUSDC(); + // FullTicketData memory fullTicketData = factoryFacet.ticketData(ticketId); + // ITicket ticket = ITicket(fullTicketData.ticketAddress); + // assertEq(ticket.ownerOf(tokenId), alice); + // assertEq(fullTicketData.soldTickets, 1); + // assertEq(marketplaceFacet.getTicketBalance(ticketId, FeeType.USDC), usdcFee); + // assertEq(marketplaceFacet.getHostItBalance(FeeType.USDC), hostItFee); + // vm.prank(alice); + // ticket.approve(hostIt, tokenId); + // vm.prank(alice); + // vm.warp(fullTicketData.endTime); + // vm.expectEmit(true, true, true, true, hostIt); + // emit TicketRefunded(ticketId, FeeType.USDC, usdcFee, bob); + // marketplaceFacet.claimRefund(ticketId, FeeType.USDC, tokenId, bob); + // assertEq(ticket.ownerOf(tokenId), owner); + // assertEq(fullTicketData.soldTickets, 1); + // assertEq(usdc.balanceOf(bob), usdcFee); + // assertEq(marketplaceFacet.getTicketBalance(ticketId, FeeType.USDC), 0); + // assertEq(marketplaceFacet.getHostItBalance(FeeType.USDC), hostItFee); + // } - function test_withdrawTicketBalanceETH() public { - (uint64 ticketId,, uint256 ethFee,) = _mintTicketETH(); - // Check platform balances before withdraw - // Withdraw - FullTicketData memory fullTicketData = factoryFacet.ticketData(ticketId); - vm.warp(fullTicketData.endTime + marketplaceFacet.getRefundPeriod()); - vm.expectEmit(true, true, true, true, hostIt); - emit TicketBalanceWithdrawn(ticketId, FeeType.ETH, ethFee, withdrawer); - marketplaceFacet.withdrawTicketBalance(ticketId, FeeType.ETH, withdrawer); - // Check platform balances after withdraw - assertEq(marketplaceFacet.getTicketBalance(ticketId, FeeType.ETH), 0); - // Check vault balances after withdraw - assertEq(withdrawer.balance, ethFee); - } + // function test_withdrawTicketBalanceETH() public { + // (uint64 ticketId,, uint256 ethFee,) = _mintTicketETH(); + // // Check platform balances before withdraw + // // Withdraw + // FullTicketData memory fullTicketData = factoryFacet.ticketData(ticketId); + // vm.warp(fullTicketData.endTime + marketplaceFacet.getRefundPeriod()); + // vm.expectEmit(true, true, true, true, hostIt); + // emit TicketBalanceWithdrawn(ticketId, FeeType.ETH, ethFee, withdrawer); + // marketplaceFacet.withdrawTicketBalance(ticketId, FeeType.ETH, withdrawer); + // // Check platform balances after withdraw + // assertEq(marketplaceFacet.getTicketBalance(ticketId, FeeType.ETH), 0); + // // Check vault balances after withdraw + // assertEq(withdrawer.balance, ethFee); + // } - function test_withdrawTicketBalanceUSDT() public { - (uint64 ticketId,, uint256 usdtFee,, ERC20Mock usdt) = _mintTicketUSDT(); - // Check platform balances before withdraw - assertEq(marketplaceFacet.getTicketBalance(ticketId, FeeType.USDT), usdtFee); - // Withdraw - FullTicketData memory fullTicketData = factoryFacet.ticketData(ticketId); - vm.warp(fullTicketData.endTime + marketplaceFacet.getRefundPeriod()); - vm.expectEmit(true, true, true, true, hostIt); - emit TicketBalanceWithdrawn(ticketId, FeeType.USDT, usdtFee, withdrawer); - marketplaceFacet.withdrawTicketBalance(ticketId, FeeType.USDT, withdrawer); - // Check platform balances after withdraw - assertEq(marketplaceFacet.getTicketBalance(ticketId, FeeType.USDT), 0); - // Check owner balances after withdraw - assertEq(usdt.balanceOf(withdrawer), usdtFee); - } + // function test_withdrawTicketBalanceUSDT() public { + // (uint64 ticketId,, uint256 usdtFee,, ERC20Mock usdt) = _mintTicketUSDT(); + // // Check platform balances before withdraw + // assertEq(marketplaceFacet.getTicketBalance(ticketId, FeeType.USDT), usdtFee); + // // Withdraw + // FullTicketData memory fullTicketData = factoryFacet.ticketData(ticketId); + // vm.warp(fullTicketData.endTime + marketplaceFacet.getRefundPeriod()); + // vm.expectEmit(true, true, true, true, hostIt); + // emit TicketBalanceWithdrawn(ticketId, FeeType.USDT, usdtFee, withdrawer); + // marketplaceFacet.withdrawTicketBalance(ticketId, FeeType.USDT, withdrawer); + // // Check platform balances after withdraw + // assertEq(marketplaceFacet.getTicketBalance(ticketId, FeeType.USDT), 0); + // // Check owner balances after withdraw + // assertEq(usdt.balanceOf(withdrawer), usdtFee); + // } - function test_withdrawTicketBalanceUSDC() public { - (uint64 ticketId,, uint256 usdcFee,, ERC20Mock usdc) = _mintTicketUSDC(); - // Check platform balances before withdraw - FullTicketData memory fullTicketData = factoryFacet.ticketData(ticketId); - vm.warp(fullTicketData.endTime + marketplaceFacet.getRefundPeriod()); - assertEq(marketplaceFacet.getTicketBalance(ticketId, FeeType.USDC), usdcFee); - // Withdraw - vm.expectEmit(true, true, true, true, hostIt); - emit TicketBalanceWithdrawn(ticketId, FeeType.USDC, usdcFee, withdrawer); - marketplaceFacet.withdrawTicketBalance(ticketId, FeeType.USDC, withdrawer); - // Check platform balances after withdraw - assertEq(marketplaceFacet.getTicketBalance(ticketId, FeeType.USDC), 0); - // Check owner balances after withdraw - assertEq(usdc.balanceOf(withdrawer), usdcFee); - } + // function test_withdrawTicketBalanceUSDC() public { + // (uint64 ticketId,, uint256 usdcFee,, ERC20Mock usdc) = _mintTicketUSDC(); + // // Check platform balances before withdraw + // FullTicketData memory fullTicketData = factoryFacet.ticketData(ticketId); + // vm.warp(fullTicketData.endTime + marketplaceFacet.getRefundPeriod()); + // assertEq(marketplaceFacet.getTicketBalance(ticketId, FeeType.USDC), usdcFee); + // // Withdraw + // vm.expectEmit(true, true, true, true, hostIt); + // emit TicketBalanceWithdrawn(ticketId, FeeType.USDC, usdcFee, withdrawer); + // marketplaceFacet.withdrawTicketBalance(ticketId, FeeType.USDC, withdrawer); + // // Check platform balances after withdraw + // assertEq(marketplaceFacet.getTicketBalance(ticketId, FeeType.USDC), 0); + // // Check owner balances after withdraw + // assertEq(usdc.balanceOf(withdrawer), usdcFee); + // } function test_withdrawHostItBalanceETH() public { (,,, uint256 hostItFee) = _mintTicketETH(); @@ -198,4 +198,6 @@ contract MarketplaceTest is DeployedHostItTickets, ERC721Holder { assertEq(marketplaceFacet.getHostItBalance(FeeType.USDC), 0); assertEq(usdc.balanceOf(withdrawer), hostItFee); } + + receive() external payable {} } From 801561b1b7c6a469741b7a0a8ceacb7bd2a262b4 Mon Sep 17 00:00:00 2001 From: David Dada Date: Fri, 3 Apr 2026 05:50:52 +0100 Subject: [PATCH 5/6] test: add extensive marketplace tests for direct payment and refundable flows Expand from 8 to 58 marketplace tests covering: - Non-refundable direct payment: organizer receives fee, contract holds only hostIt fee, no escrow (ETH, USDT, USDC) - Refundable escrow: funds escrowed, refund claims, withdraw after refund period, time-window revert cases (ETH, USDT, USDC) - Revert cases: insufficient balance/allowance, refund on non-refundable, withdraw zero balance, disabled fee type - Mixed scenarios: hostIt fee accumulation across ticket types - Add refundable ticket helpers to DeployedHostItTickets base --- test/Marketplace.t.sol | 644 ++++++++++++++++++++------ test/states/DeployedHostItTickets.sol | 75 +++ 2 files changed, 585 insertions(+), 134 deletions(-) diff --git a/test/Marketplace.t.sol b/test/Marketplace.t.sol index 800ccb7..72c131c 100644 --- a/test/Marketplace.t.sol +++ b/test/Marketplace.t.sol @@ -9,8 +9,14 @@ import {DeployedHostItTickets} from "@ticket-test/states/DeployedHostItTickets.s import {ITicket} from "@ticket/interfaces/ITicket.sol"; /// forge-lint: disable-next-line(unaliased-plain-import) import "@ticket-logs/MarketplaceLogs.sol"; +/// forge-lint: disable-next-line(unaliased-plain-import) +import "@ticket-errors/MarketplaceErrors.sol"; contract MarketplaceTest is DeployedHostItTickets, ERC721Holder { + // ====================================================================== + // FREE TICKET MINTING + // ====================================================================== + function test_mintFreeTicket() public { vm.prank(alice); (uint64 ticketId, uint40 tokenId) = _mintTicketFree(); @@ -20,159 +26,234 @@ contract MarketplaceTest is DeployedHostItTickets, ERC721Holder { assertEq(fullTicketData.soldTickets, 1); } - function test_mintPaidTicketETH() public { - (uint64 ticketId, uint40 tokenId, uint256 fee, uint256 hostItFee) = _mintTicketETH(); - FullTicketData memory fullTicketData = factoryFacet.ticketData(ticketId); - ITicket ticket = ITicket(fullTicketData.ticketAddress); - assertEq(ticket.ownerOf(tokenId), alice); - assertEq(fullTicketData.soldTickets, 1); + function test_mintFreeTicket_noBalanceChanges() public { + vm.prank(alice); + (uint64 ticketId,) = _mintTicketFree(); + assertEq(marketplaceFacet.getTicketBalance(ticketId, FeeType.ETH), 0); + assertEq(marketplaceFacet.getHostItBalance(FeeType.ETH), 0); + } + + // ====================================================================== + // NON-REFUNDABLE DIRECT PAYMENT: ETH + // ====================================================================== + + function test_directPayment_ETH_organizerReceivesFee() public { + uint256 ownerBalanceBefore = owner.balance; + (,, uint256 fee,) = _mintTicketETH(); + assertEq(owner.balance - ownerBalanceBefore, fee); + } + + function test_directPayment_ETH_buyerSpendsFullAmount() public { + _mintTicketETH(); assertEq(alice.balance, 0); + } + + function test_directPayment_ETH_hostItFeeAccumulated() public { + (,,, uint256 hostItFee) = _mintTicketETH(); assertEq(marketplaceFacet.getHostItBalance(FeeType.ETH), hostItFee); } - function test_mintPaidTicketUSDT() public { - (uint64 ticketId, uint40 tokenId, uint256 fee, uint256 hostItFee,) = _mintTicketUSDT(); + function test_directPayment_ETH_noTicketBalanceEscrowed() public { + (uint64 ticketId,,,) = _mintTicketETH(); + assertEq(marketplaceFacet.getTicketBalance(ticketId, FeeType.ETH), 0); + } + + function test_directPayment_ETH_ticketMintedToBuyer() public { + (uint64 ticketId, uint40 tokenId,,) = _mintTicketETH(); FullTicketData memory fullTicketData = factoryFacet.ticketData(ticketId); ITicket ticket = ITicket(fullTicketData.ticketAddress); assertEq(ticket.ownerOf(tokenId), alice); assertEq(fullTicketData.soldTickets, 1); - // assertEq(marketplaceFacet.getTicketBalance(ticketId, FeeType.USDT), fee); + } + + function test_directPayment_ETH_contractHoldsOnlyHostItFee() public { + uint256 contractBalanceBefore = hostIt.balance; + (,,, uint256 hostItFee) = _mintTicketETH(); + assertEq(hostIt.balance - contractBalanceBefore, hostItFee); + } + + function test_directPayment_ETH_emitsTicketMinted() public { + _createPaidTicket(); + uint64 ticketId = factoryFacet.ticketCount(); + (,, uint256 totalFee) = marketplaceFacet.getAllFees(ticketId, FeeType.ETH); + hoax(alice, totalFee); + vm.expectEmit(true, true, true, true, hostIt); + emit TicketMinted(ticketId, FeeType.ETH, totalFee, 1); + marketplaceFacet.mintTicket{value: totalFee}(ticketId, FeeType.ETH, alice); + } + + function test_directPayment_ETH_multipleBuyersAccumulateHostItFees() public { + _createPaidTicket(); + uint64 ticketId = factoryFacet.ticketCount(); + (, uint256 hostItFee, uint256 totalFee) = marketplaceFacet.getAllFees(ticketId, FeeType.ETH); + + hoax(alice, totalFee); + marketplaceFacet.mintTicket{value: totalFee}(ticketId, FeeType.ETH, alice); + + hoax(bob, totalFee); + marketplaceFacet.mintTicket{value: totalFee}(ticketId, FeeType.ETH, bob); + + assertEq(marketplaceFacet.getHostItBalance(FeeType.ETH), hostItFee * 2); + } + + function test_directPayment_ETH_multipleBuyersPayOrganizer() public { + _createPaidTicket(); + uint64 ticketId = factoryFacet.ticketCount(); + (uint256 fee,, uint256 totalFee) = marketplaceFacet.getAllFees(ticketId, FeeType.ETH); + + uint256 ownerBalanceBefore = owner.balance; + + hoax(alice, totalFee); + marketplaceFacet.mintTicket{value: totalFee}(ticketId, FeeType.ETH, alice); + + hoax(bob, totalFee); + marketplaceFacet.mintTicket{value: totalFee}(ticketId, FeeType.ETH, bob); + + assertEq(owner.balance - ownerBalanceBefore, fee * 2); + } + + function test_directPayment_ETH_revertsInsufficientValue() public { + _createPaidTicket(); + uint64 ticketId = factoryFacet.ticketCount(); + (,, uint256 totalFee) = marketplaceFacet.getAllFees(ticketId, FeeType.ETH); + hoax(alice, totalFee); + vm.expectRevert(abi.encodeWithSelector(TicketPurchaseFailed.selector, FeeType.ETH, totalFee)); + marketplaceFacet.mintTicket{value: totalFee - 1}(ticketId, FeeType.ETH, alice); + } + + // ====================================================================== + // NON-REFUNDABLE DIRECT PAYMENT: USDT + // ====================================================================== + + function test_directPayment_USDT_organizerReceivesFee() public { + (,, uint256 fee,,) = _mintTicketUSDT(); + ERC20Mock usdt = ERC20Mock(marketplaceFacet.getFeeTokenAddress(FeeType.USDT)); + assertEq(usdt.balanceOf(owner), fee); + } + + function test_directPayment_USDT_hostItFeeAccumulated() public { + (,,, uint256 hostItFee,) = _mintTicketUSDT(); assertEq(marketplaceFacet.getHostItBalance(FeeType.USDT), hostItFee); } - function test_mintPaidTicketUSDC() public { - (uint64 ticketId, uint40 tokenId, uint256 fee, uint256 hostItFee,) = _mintTicketUSDC(); + function test_directPayment_USDT_noTicketBalanceEscrowed() public { + (uint64 ticketId,,,,) = _mintTicketUSDT(); + assertEq(marketplaceFacet.getTicketBalance(ticketId, FeeType.USDT), 0); + } + + function test_directPayment_USDT_contractHoldsHostItFee() public { + (,,, uint256 hostItFee, ERC20Mock usdt) = _mintTicketUSDT(); + assertEq(usdt.balanceOf(hostIt), hostItFee); + } + + function test_directPayment_USDT_buyerBalanceZero() public { + (,,,, ERC20Mock usdt) = _mintTicketUSDT(); + assertEq(usdt.balanceOf(alice), 0); + } + + function test_directPayment_USDT_ticketMintedToBuyer() public { + (uint64 ticketId, uint40 tokenId,,,) = _mintTicketUSDT(); FullTicketData memory fullTicketData = factoryFacet.ticketData(ticketId); ITicket ticket = ITicket(fullTicketData.ticketAddress); assertEq(ticket.ownerOf(tokenId), alice); assertEq(fullTicketData.soldTickets, 1); - // assertEq(marketplaceFacet.getTicketBalance(ticketId, FeeType.USDC), fee); + } + + // ====================================================================== + // NON-REFUNDABLE DIRECT PAYMENT: USDC + // ====================================================================== + + function test_directPayment_USDC_organizerReceivesFee() public { + (,, uint256 fee,,) = _mintTicketUSDC(); + ERC20Mock usdc = ERC20Mock(marketplaceFacet.getFeeTokenAddress(FeeType.USDC)); + assertEq(usdc.balanceOf(owner), fee); + } + + function test_directPayment_USDC_hostItFeeAccumulated() public { + (,,, uint256 hostItFee,) = _mintTicketUSDC(); assertEq(marketplaceFacet.getHostItBalance(FeeType.USDC), hostItFee); } - function test_setTicketFees() public { - _createFreeTicket(); + function test_directPayment_USDC_noTicketBalanceEscrowed() public { + (uint64 ticketId,,,,) = _mintTicketUSDC(); + assertEq(marketplaceFacet.getTicketBalance(ticketId, FeeType.USDC), 0); + } + + function test_directPayment_USDC_contractHoldsHostItFee() public { + (,,, uint256 hostItFee, ERC20Mock usdc) = _mintTicketUSDC(); + assertEq(usdc.balanceOf(hostIt), hostItFee); + } + + function test_directPayment_USDC_ticketMintedToBuyer() public { + (uint64 ticketId, uint40 tokenId,,,) = _mintTicketUSDC(); + FullTicketData memory fullTicketData = factoryFacet.ticketData(ticketId); + ITicket ticket = ITicket(fullTicketData.ticketAddress); + assertEq(ticket.ownerOf(tokenId), alice); + assertEq(fullTicketData.soldTickets, 1); + } + + // ====================================================================== + // NON-REFUNDABLE: TOKEN REVERT CASES + // ====================================================================== + + function test_directPayment_USDT_revertsInsufficientBalance() public { + _createPaidTicket(); uint64 ticketId = factoryFacet.ticketCount(); - marketplaceFacet.setTicketFees(ticketId, _getFeeTypes(), _getFees()); - assertEq(marketplaceFacet.getTicketFee(ticketId, FeeType.ETH), _getFees()[0]); - assertEq(marketplaceFacet.getTicketFee(ticketId, FeeType.USDT), _getFees()[1]); - assertEq(marketplaceFacet.getTicketFee(ticketId, FeeType.USDC), _getFees()[2]); + (, uint256 hostItFee, uint256 totalFee) = marketplaceFacet.getAllFees(ticketId, FeeType.USDT); + ERC20Mock usdt = ERC20Mock(marketplaceFacet.getFeeTokenAddress(FeeType.USDT)); + // Mint just under totalFee so balance check fails on the second _payWithToken call (hostItFee) + usdt.mint(alice, totalFee - 1); + vm.prank(alice); + usdt.approve(address(marketplaceFacet), totalFee); + vm.prank(alice); + vm.expectRevert(abi.encodeWithSelector(InsufficientBalance.selector, address(usdt), FeeType.USDT, hostItFee)); + marketplaceFacet.mintTicket(ticketId, FeeType.USDT, alice); + } + + function test_directPayment_USDT_revertsInsufficientAllowance() public { + _createPaidTicket(); + uint64 ticketId = factoryFacet.ticketCount(); + (, uint256 hostItFee, uint256 totalFee) = marketplaceFacet.getAllFees(ticketId, FeeType.USDT); + ERC20Mock usdt = ERC20Mock(marketplaceFacet.getFeeTokenAddress(FeeType.USDT)); + usdt.mint(alice, totalFee); + vm.prank(alice); + // Approve just under totalFee so allowance check fails on the second _payWithToken call (hostItFee) + usdt.approve(address(marketplaceFacet), totalFee - 1); + vm.prank(alice); + vm.expectRevert(abi.encodeWithSelector(InsufficientAllowance.selector, address(usdt), FeeType.USDT, hostItFee)); + marketplaceFacet.mintTicket(ticketId, FeeType.USDT, alice); + } + + // ====================================================================== + // NON-REFUNDABLE: REFUND NOT ALLOWED + // ====================================================================== + + function test_directPayment_claimRefundRevertsForNonRefundable() public { + (uint64 ticketId, uint40 tokenId,,) = _mintTicketETH(); + FullTicketData memory fullTicketData = factoryFacet.ticketData(ticketId); + vm.warp(fullTicketData.endTime); + vm.prank(alice); + vm.expectRevert(RefundNotEnabled.selector); + marketplaceFacet.claimRefund(ticketId, FeeType.ETH, tokenId, alice); + } + + // ====================================================================== + // NON-REFUNDABLE: WITHDRAW TICKET BALANCE REVERTS + // ====================================================================== + + function test_directPayment_withdrawTicketBalanceRevertsNoBalance() public { + (uint64 ticketId,,,) = _mintTicketETH(); + FullTicketData memory fullTicketData = factoryFacet.ticketData(ticketId); + vm.warp(fullTicketData.endTime + marketplaceFacet.getRefundPeriod()); + vm.expectRevert(InsufficientWithdrawBalance.selector); + marketplaceFacet.withdrawTicketBalance(ticketId, FeeType.ETH, withdrawer); } - // function test_claimRefundETH() public { - // (uint64 ticketId, uint40 tokenId, uint256 ethFee, uint256 hostItFee) = _mintTicketETH(); - // FullTicketData memory fullTicketData = factoryFacet.ticketData(ticketId); - // ITicket ticket = ITicket(fullTicketData.ticketAddress); - // assertEq(ticket.ownerOf(tokenId), alice); - // assertEq(fullTicketData.soldTickets, 1); - // assertEq(marketplaceFacet.getTicketBalance(ticketId, FeeType.ETH), ethFee); - // assertEq(marketplaceFacet.getHostItBalance(FeeType.ETH), hostItFee); - // vm.prank(alice); - // ticket.approve(hostIt, tokenId); - // vm.prank(alice); - // vm.warp(fullTicketData.endTime); - // vm.expectEmit(true, true, true, true, hostIt); - // emit TicketRefunded(ticketId, FeeType.ETH, ethFee, bob); - // marketplaceFacet.claimRefund(ticketId, FeeType.ETH, tokenId, bob); - // assertEq(ticket.ownerOf(tokenId), owner); - // assertEq(fullTicketData.soldTickets, 1); - // assertEq(bob.balance, ethFee); - // assertEq(marketplaceFacet.getTicketBalance(ticketId, FeeType.ETH), 0); - // assertEq(marketplaceFacet.getHostItBalance(FeeType.ETH), hostItFee); - // } - - // function test_claimRefundUSDT() public { - // (uint64 ticketId, uint40 tokenId, uint256 usdtFee, uint256 hostItFee, ERC20Mock usdt) = _mintTicketUSDT(); - // FullTicketData memory fullTicketData = factoryFacet.ticketData(ticketId); - // ITicket ticket = ITicket(fullTicketData.ticketAddress); - // assertEq(ticket.ownerOf(tokenId), alice); - // assertEq(fullTicketData.soldTickets, 1); - // assertEq(marketplaceFacet.getTicketBalance(ticketId, FeeType.USDT), usdtFee); - // assertEq(marketplaceFacet.getHostItBalance(FeeType.USDT), hostItFee); - // vm.prank(alice); - // ticket.approve(hostIt, tokenId); - // vm.prank(alice); - // vm.warp(fullTicketData.endTime); - // vm.expectEmit(true, true, true, true, hostIt); - // emit TicketRefunded(ticketId, FeeType.USDT, usdtFee, bob); - // marketplaceFacet.claimRefund(ticketId, FeeType.USDT, tokenId, bob); - // assertEq(ticket.ownerOf(tokenId), owner); - // assertEq(fullTicketData.soldTickets, 1); - // assertEq(usdt.balanceOf(bob), usdtFee); - // assertEq(marketplaceFacet.getTicketBalance(ticketId, FeeType.USDT), 0); - // assertEq(marketplaceFacet.getHostItBalance(FeeType.USDT), hostItFee); - // } - - // function test_claimRefundUSDC() public { - // (uint64 ticketId, uint40 tokenId, uint256 usdcFee, uint256 hostItFee, ERC20Mock usdc) = _mintTicketUSDC(); - // FullTicketData memory fullTicketData = factoryFacet.ticketData(ticketId); - // ITicket ticket = ITicket(fullTicketData.ticketAddress); - // assertEq(ticket.ownerOf(tokenId), alice); - // assertEq(fullTicketData.soldTickets, 1); - // assertEq(marketplaceFacet.getTicketBalance(ticketId, FeeType.USDC), usdcFee); - // assertEq(marketplaceFacet.getHostItBalance(FeeType.USDC), hostItFee); - // vm.prank(alice); - // ticket.approve(hostIt, tokenId); - // vm.prank(alice); - // vm.warp(fullTicketData.endTime); - // vm.expectEmit(true, true, true, true, hostIt); - // emit TicketRefunded(ticketId, FeeType.USDC, usdcFee, bob); - // marketplaceFacet.claimRefund(ticketId, FeeType.USDC, tokenId, bob); - // assertEq(ticket.ownerOf(tokenId), owner); - // assertEq(fullTicketData.soldTickets, 1); - // assertEq(usdc.balanceOf(bob), usdcFee); - // assertEq(marketplaceFacet.getTicketBalance(ticketId, FeeType.USDC), 0); - // assertEq(marketplaceFacet.getHostItBalance(FeeType.USDC), hostItFee); - // } - - // function test_withdrawTicketBalanceETH() public { - // (uint64 ticketId,, uint256 ethFee,) = _mintTicketETH(); - // // Check platform balances before withdraw - // // Withdraw - // FullTicketData memory fullTicketData = factoryFacet.ticketData(ticketId); - // vm.warp(fullTicketData.endTime + marketplaceFacet.getRefundPeriod()); - // vm.expectEmit(true, true, true, true, hostIt); - // emit TicketBalanceWithdrawn(ticketId, FeeType.ETH, ethFee, withdrawer); - // marketplaceFacet.withdrawTicketBalance(ticketId, FeeType.ETH, withdrawer); - // // Check platform balances after withdraw - // assertEq(marketplaceFacet.getTicketBalance(ticketId, FeeType.ETH), 0); - // // Check vault balances after withdraw - // assertEq(withdrawer.balance, ethFee); - // } - - // function test_withdrawTicketBalanceUSDT() public { - // (uint64 ticketId,, uint256 usdtFee,, ERC20Mock usdt) = _mintTicketUSDT(); - // // Check platform balances before withdraw - // assertEq(marketplaceFacet.getTicketBalance(ticketId, FeeType.USDT), usdtFee); - // // Withdraw - // FullTicketData memory fullTicketData = factoryFacet.ticketData(ticketId); - // vm.warp(fullTicketData.endTime + marketplaceFacet.getRefundPeriod()); - // vm.expectEmit(true, true, true, true, hostIt); - // emit TicketBalanceWithdrawn(ticketId, FeeType.USDT, usdtFee, withdrawer); - // marketplaceFacet.withdrawTicketBalance(ticketId, FeeType.USDT, withdrawer); - // // Check platform balances after withdraw - // assertEq(marketplaceFacet.getTicketBalance(ticketId, FeeType.USDT), 0); - // // Check owner balances after withdraw - // assertEq(usdt.balanceOf(withdrawer), usdtFee); - // } - - // function test_withdrawTicketBalanceUSDC() public { - // (uint64 ticketId,, uint256 usdcFee,, ERC20Mock usdc) = _mintTicketUSDC(); - // // Check platform balances before withdraw - // FullTicketData memory fullTicketData = factoryFacet.ticketData(ticketId); - // vm.warp(fullTicketData.endTime + marketplaceFacet.getRefundPeriod()); - // assertEq(marketplaceFacet.getTicketBalance(ticketId, FeeType.USDC), usdcFee); - // // Withdraw - // vm.expectEmit(true, true, true, true, hostIt); - // emit TicketBalanceWithdrawn(ticketId, FeeType.USDC, usdcFee, withdrawer); - // marketplaceFacet.withdrawTicketBalance(ticketId, FeeType.USDC, withdrawer); - // // Check platform balances after withdraw - // assertEq(marketplaceFacet.getTicketBalance(ticketId, FeeType.USDC), 0); - // // Check owner balances after withdraw - // assertEq(usdc.balanceOf(withdrawer), usdcFee); - // } - - function test_withdrawHostItBalanceETH() public { + // ====================================================================== + // NON-REFUNDABLE: WITHDRAW HOSTIT BALANCE + // ====================================================================== + + function test_directPayment_withdrawHostItBalanceETH() public { (,,, uint256 hostItFee) = _mintTicketETH(); vm.expectEmit(true, true, true, true, hostIt); emit HostItBalanceWithdrawn(FeeType.ETH, hostItFee, withdrawer); @@ -181,7 +262,7 @@ contract MarketplaceTest is DeployedHostItTickets, ERC721Holder { assertEq(withdrawer.balance, hostItFee); } - function test_withdrawHostItBalanceUSDT() public { + function test_directPayment_withdrawHostItBalanceUSDT() public { (,,, uint256 hostItFee, ERC20Mock usdt) = _mintTicketUSDT(); vm.expectEmit(true, true, true, true, hostIt); emit HostItBalanceWithdrawn(FeeType.USDT, hostItFee, withdrawer); @@ -190,7 +271,7 @@ contract MarketplaceTest is DeployedHostItTickets, ERC721Holder { assertEq(usdt.balanceOf(withdrawer), hostItFee); } - function test_withdrawHostItBalanceUSDC() public { + function test_directPayment_withdrawHostItBalanceUSDC() public { (,,, uint256 hostItFee, ERC20Mock usdc) = _mintTicketUSDC(); vm.expectEmit(true, true, true, true, hostIt); emit HostItBalanceWithdrawn(FeeType.USDC, hostItFee, withdrawer); @@ -199,5 +280,300 @@ contract MarketplaceTest is DeployedHostItTickets, ERC721Holder { assertEq(usdc.balanceOf(withdrawer), hostItFee); } + function test_directPayment_withdrawHostItBalanceRevertsIfZero() public { + vm.expectRevert(InsufficientWithdrawBalance.selector); + marketplaceFacet.withdrawHostItBalance(FeeType.ETH, withdrawer); + } + + // ====================================================================== + // REFUNDABLE ESCROW: ETH + // ====================================================================== + + function test_refundable_ETH_fundsEscrowed() public { + (uint64 ticketId,, uint256 fee, uint256 hostItFee) = _mintTicketETHRefundable(); + assertEq(marketplaceFacet.getTicketBalance(ticketId, FeeType.ETH), fee); + assertEq(marketplaceFacet.getHostItBalance(FeeType.ETH), hostItFee); + } + + function test_refundable_ETH_organizerDoesNotReceiveFee() public { + uint256 ownerBalanceBefore = owner.balance; + _mintTicketETHRefundable(); + assertEq(owner.balance, ownerBalanceBefore); + } + + function test_refundable_ETH_contractHoldsTotalFee() public { + uint256 contractBalanceBefore = hostIt.balance; + (,, uint256 fee, uint256 hostItFee) = _mintTicketETHRefundable(); + assertEq(hostIt.balance - contractBalanceBefore, fee + hostItFee); + } + + function test_refundable_ETH_ticketMintedToBuyer() public { + (uint64 ticketId, uint40 tokenId,,) = _mintTicketETHRefundable(); + FullTicketData memory fullTicketData = factoryFacet.ticketData(ticketId); + ITicket ticket = ITicket(fullTicketData.ticketAddress); + assertEq(ticket.ownerOf(tokenId), alice); + assertEq(fullTicketData.soldTickets, 1); + } + + function test_refundable_ETH_claimRefund() public { + (uint64 ticketId, uint40 tokenId, uint256 fee, uint256 hostItFee) = _mintTicketETHRefundable(); + FullTicketData memory fullTicketData = factoryFacet.ticketData(ticketId); + ITicket ticket = ITicket(fullTicketData.ticketAddress); + + vm.prank(alice); + ticket.approve(hostIt, tokenId); + + vm.warp(fullTicketData.endTime); + vm.prank(alice); + vm.expectEmit(true, true, true, true, hostIt); + emit TicketRefunded(ticketId, FeeType.ETH, fee, bob); + marketplaceFacet.claimRefund(ticketId, FeeType.ETH, tokenId, bob); + + assertEq(ticket.ownerOf(tokenId), owner); + assertEq(bob.balance, fee); + assertEq(marketplaceFacet.getTicketBalance(ticketId, FeeType.ETH), 0); + assertEq(marketplaceFacet.getHostItBalance(FeeType.ETH), hostItFee); + } + + function test_refundable_ETH_claimRefundRevertsBeforeEndTime() public { + (uint64 ticketId, uint40 tokenId,,) = _mintTicketETHRefundable(); + FullTicketData memory fullTicketData = factoryFacet.ticketData(ticketId); + ITicket ticket = ITicket(fullTicketData.ticketAddress); + + vm.prank(alice); + ticket.approve(hostIt, tokenId); + + vm.warp(fullTicketData.endTime - 1); + vm.prank(alice); + vm.expectRevert(RefundPeriodNotReached.selector); + marketplaceFacet.claimRefund(ticketId, FeeType.ETH, tokenId, bob); + } + + function test_refundable_ETH_claimRefundRevertsAfterRefundPeriod() public { + (uint64 ticketId, uint40 tokenId,,) = _mintTicketETHRefundable(); + FullTicketData memory fullTicketData = factoryFacet.ticketData(ticketId); + ITicket ticket = ITicket(fullTicketData.ticketAddress); + + vm.prank(alice); + ticket.approve(hostIt, tokenId); + + vm.warp(fullTicketData.endTime + marketplaceFacet.getRefundPeriod() + 1); + vm.prank(alice); + vm.expectRevert(RefundPeriodExpired.selector); + marketplaceFacet.claimRefund(ticketId, FeeType.ETH, tokenId, bob); + } + + function test_refundable_ETH_claimRefundRevertsNonOwner() public { + (uint64 ticketId, uint40 tokenId,,) = _mintTicketETHRefundable(); + FullTicketData memory fullTicketData = factoryFacet.ticketData(ticketId); + + vm.warp(fullTicketData.endTime); + vm.prank(bob); + vm.expectRevert(abi.encodeWithSelector(TicketNotOwned.selector, tokenId)); + marketplaceFacet.claimRefund(ticketId, FeeType.ETH, tokenId, bob); + } + + function test_refundable_ETH_withdrawTicketBalanceAfterRefundPeriod() public { + (uint64 ticketId,, uint256 fee,) = _mintTicketETHRefundable(); + FullTicketData memory fullTicketData = factoryFacet.ticketData(ticketId); + + vm.warp(fullTicketData.endTime + marketplaceFacet.getRefundPeriod()); + vm.expectEmit(true, true, true, true, hostIt); + emit TicketBalanceWithdrawn(ticketId, FeeType.ETH, fee, withdrawer); + marketplaceFacet.withdrawTicketBalance(ticketId, FeeType.ETH, withdrawer); + + assertEq(marketplaceFacet.getTicketBalance(ticketId, FeeType.ETH), 0); + assertEq(withdrawer.balance, fee); + } + + function test_refundable_ETH_withdrawTicketBalanceRevertsBeforeRefundPeriod() public { + (uint64 ticketId,,,) = _mintTicketETHRefundable(); + FullTicketData memory fullTicketData = factoryFacet.ticketData(ticketId); + + vm.warp(fullTicketData.endTime + marketplaceFacet.getRefundPeriod() - 1); + vm.expectRevert(WithdrawPeriodNotReached.selector); + marketplaceFacet.withdrawTicketBalance(ticketId, FeeType.ETH, withdrawer); + } + + // ====================================================================== + // REFUNDABLE ESCROW: USDT + // ====================================================================== + + function test_refundable_USDT_fundsEscrowed() public { + (uint64 ticketId,, uint256 fee, uint256 hostItFee,) = _mintTicketUSDTRefundable(); + assertEq(marketplaceFacet.getTicketBalance(ticketId, FeeType.USDT), fee); + assertEq(marketplaceFacet.getHostItBalance(FeeType.USDT), hostItFee); + } + + function test_refundable_USDT_organizerDoesNotReceiveFee() public { + (,,,, ERC20Mock usdt) = _mintTicketUSDTRefundable(); + assertEq(usdt.balanceOf(owner), 0); + } + + function test_refundable_USDT_contractHoldsTotalFee() public { + (,, uint256 fee, uint256 hostItFee, ERC20Mock usdt) = _mintTicketUSDTRefundable(); + assertEq(usdt.balanceOf(hostIt), fee + hostItFee); + } + + function test_refundable_USDT_claimRefund() public { + (uint64 ticketId, uint40 tokenId, uint256 fee, uint256 hostItFee, ERC20Mock usdt) = _mintTicketUSDTRefundable(); + FullTicketData memory fullTicketData = factoryFacet.ticketData(ticketId); + ITicket ticket = ITicket(fullTicketData.ticketAddress); + + vm.prank(alice); + ticket.approve(hostIt, tokenId); + + vm.warp(fullTicketData.endTime); + vm.prank(alice); + vm.expectEmit(true, true, true, true, hostIt); + emit TicketRefunded(ticketId, FeeType.USDT, fee, bob); + marketplaceFacet.claimRefund(ticketId, FeeType.USDT, tokenId, bob); + + assertEq(ticket.ownerOf(tokenId), owner); + assertEq(usdt.balanceOf(bob), fee); + assertEq(marketplaceFacet.getTicketBalance(ticketId, FeeType.USDT), 0); + assertEq(marketplaceFacet.getHostItBalance(FeeType.USDT), hostItFee); + } + + function test_refundable_USDT_withdrawTicketBalanceAfterRefundPeriod() public { + (uint64 ticketId,, uint256 fee,, ERC20Mock usdt) = _mintTicketUSDTRefundable(); + FullTicketData memory fullTicketData = factoryFacet.ticketData(ticketId); + + vm.warp(fullTicketData.endTime + marketplaceFacet.getRefundPeriod()); + vm.expectEmit(true, true, true, true, hostIt); + emit TicketBalanceWithdrawn(ticketId, FeeType.USDT, fee, withdrawer); + marketplaceFacet.withdrawTicketBalance(ticketId, FeeType.USDT, withdrawer); + + assertEq(marketplaceFacet.getTicketBalance(ticketId, FeeType.USDT), 0); + assertEq(usdt.balanceOf(withdrawer), fee); + } + + // ====================================================================== + // REFUNDABLE ESCROW: USDC + // ====================================================================== + + function test_refundable_USDC_fundsEscrowed() public { + (uint64 ticketId,, uint256 fee, uint256 hostItFee,) = _mintTicketUSDCRefundable(); + assertEq(marketplaceFacet.getTicketBalance(ticketId, FeeType.USDC), fee); + assertEq(marketplaceFacet.getHostItBalance(FeeType.USDC), hostItFee); + } + + function test_refundable_USDC_organizerDoesNotReceiveFee() public { + (,,,, ERC20Mock usdc) = _mintTicketUSDCRefundable(); + assertEq(usdc.balanceOf(owner), 0); + } + + function test_refundable_USDC_contractHoldsTotalFee() public { + (,, uint256 fee, uint256 hostItFee, ERC20Mock usdc) = _mintTicketUSDCRefundable(); + assertEq(usdc.balanceOf(hostIt), fee + hostItFee); + } + + function test_refundable_USDC_claimRefund() public { + (uint64 ticketId, uint40 tokenId, uint256 fee, uint256 hostItFee, ERC20Mock usdc) = _mintTicketUSDCRefundable(); + FullTicketData memory fullTicketData = factoryFacet.ticketData(ticketId); + ITicket ticket = ITicket(fullTicketData.ticketAddress); + + vm.prank(alice); + ticket.approve(hostIt, tokenId); + + vm.warp(fullTicketData.endTime); + vm.prank(alice); + vm.expectEmit(true, true, true, true, hostIt); + emit TicketRefunded(ticketId, FeeType.USDC, fee, bob); + marketplaceFacet.claimRefund(ticketId, FeeType.USDC, tokenId, bob); + + assertEq(ticket.ownerOf(tokenId), owner); + assertEq(usdc.balanceOf(bob), fee); + assertEq(marketplaceFacet.getTicketBalance(ticketId, FeeType.USDC), 0); + assertEq(marketplaceFacet.getHostItBalance(FeeType.USDC), hostItFee); + } + + function test_refundable_USDC_withdrawTicketBalanceAfterRefundPeriod() public { + (uint64 ticketId,, uint256 fee,, ERC20Mock usdc) = _mintTicketUSDCRefundable(); + FullTicketData memory fullTicketData = factoryFacet.ticketData(ticketId); + + vm.warp(fullTicketData.endTime + marketplaceFacet.getRefundPeriod()); + vm.expectEmit(true, true, true, true, hostIt); + emit TicketBalanceWithdrawn(ticketId, FeeType.USDC, fee, withdrawer); + marketplaceFacet.withdrawTicketBalance(ticketId, FeeType.USDC, withdrawer); + + assertEq(marketplaceFacet.getTicketBalance(ticketId, FeeType.USDC), 0); + assertEq(usdc.balanceOf(withdrawer), fee); + } + + // ====================================================================== + // REFUNDABLE: HOSTIT BALANCE WITHDRAW AFTER ESCROW + // ====================================================================== + + function test_refundable_withdrawHostItBalanceETH() public { + (,,, uint256 hostItFee) = _mintTicketETHRefundable(); + vm.expectEmit(true, true, true, true, hostIt); + emit HostItBalanceWithdrawn(FeeType.ETH, hostItFee, withdrawer); + marketplaceFacet.withdrawHostItBalance(FeeType.ETH, withdrawer); + assertEq(marketplaceFacet.getHostItBalance(FeeType.ETH), 0); + assertEq(withdrawer.balance, hostItFee); + } + + function test_refundable_withdrawHostItBalanceUSDT() public { + (,,, uint256 hostItFee, ERC20Mock usdt) = _mintTicketUSDTRefundable(); + vm.expectEmit(true, true, true, true, hostIt); + emit HostItBalanceWithdrawn(FeeType.USDT, hostItFee, withdrawer); + marketplaceFacet.withdrawHostItBalance(FeeType.USDT, withdrawer); + assertEq(marketplaceFacet.getHostItBalance(FeeType.USDT), 0); + assertEq(usdt.balanceOf(withdrawer), hostItFee); + } + + function test_refundable_withdrawHostItBalanceUSDC() public { + (,,, uint256 hostItFee, ERC20Mock usdc) = _mintTicketUSDCRefundable(); + vm.expectEmit(true, true, true, true, hostIt); + emit HostItBalanceWithdrawn(FeeType.USDC, hostItFee, withdrawer); + marketplaceFacet.withdrawHostItBalance(FeeType.USDC, withdrawer); + assertEq(marketplaceFacet.getHostItBalance(FeeType.USDC), 0); + assertEq(usdc.balanceOf(withdrawer), hostItFee); + } + + // ====================================================================== + // FEE CONFIGURATION + // ====================================================================== + + function test_setTicketFees() public { + _createFreeTicket(); + uint64 ticketId = factoryFacet.ticketCount(); + marketplaceFacet.setTicketFees(ticketId, _getFeeTypes(), _getFees()); + assertEq(marketplaceFacet.getTicketFee(ticketId, FeeType.ETH), _getFees()[0]); + assertEq(marketplaceFacet.getTicketFee(ticketId, FeeType.USDT), _getFees()[1]); + assertEq(marketplaceFacet.getTicketFee(ticketId, FeeType.USDC), _getFees()[2]); + } + + function test_feeCalculation() public { + _createPaidTicket(); + uint64 ticketId = factoryFacet.ticketCount(); + (uint256 fee, uint256 hostItFee, uint256 totalFee) = marketplaceFacet.getAllFees(ticketId, FeeType.ETH); + assertEq(fee, ETH_FEE); + assertEq(hostItFee, (ETH_FEE * 300) / 10_000); + assertEq(totalFee, fee + hostItFee); + } + + function test_feeNotEnabled_reverts() public { + _createPaidTicket(); + uint64 ticketId = factoryFacet.ticketCount(); + hoax(alice, 1 ether); + vm.expectRevert(FeeNotEnabled.selector); + marketplaceFacet.mintTicket{value: 1 ether}(ticketId, FeeType.WETH, alice); + } + + // ====================================================================== + // MIXED: REFUNDABLE + NON-REFUNDABLE HOSTIT ACCUMULATION + // ====================================================================== + + function test_hostItBalanceAccumulatesAcrossTickets() public { + // Non-refundable ticket + (,,, uint256 hostItFee1) = _mintTicketETH(); + // Refundable ticket + (,,, uint256 hostItFee2) = _mintTicketETHRefundable(); + + assertEq(marketplaceFacet.getHostItBalance(FeeType.ETH), hostItFee1 + hostItFee2); + } + receive() external payable {} } diff --git a/test/states/DeployedHostItTickets.sol b/test/states/DeployedHostItTickets.sol index c363103..7ac27d3 100644 --- a/test/states/DeployedHostItTickets.sol +++ b/test/states/DeployedHostItTickets.sol @@ -178,6 +178,62 @@ abstract contract DeployedHostItTickets is Test, DeployHostItTicketsHelper { hostItFee_ = hostItFee; } + /// forge-lint: disable-next-line(mixed-case-function) + function _mintTicketETHRefundable() + internal + returns (uint64 ticketId_, uint40 tokenId_, uint256 fee_, uint256 hostItFee_) + { + _createRefundablePaidTicket(); + ticketId_ = factoryFacet.ticketCount(); + (uint256 fee, uint256 hostItFee, uint256 totalFee) = marketplaceFacet.getAllFees(ticketId_, FeeType.ETH); + hoax(alice, totalFee); + vm.expectEmit(true, true, true, true, hostIt); + emit TicketMinted(ticketId_, FeeType.ETH, totalFee, 1); + tokenId_ = marketplaceFacet.mintTicket{value: totalFee}(ticketId_, FeeType.ETH, alice); + fee_ = fee; + hostItFee_ = hostItFee; + } + + /// forge-lint: disable-next-line(mixed-case-function) + function _mintTicketUSDTRefundable() + internal + returns (uint64 ticketId_, uint40 tokenId_, uint256 fee_, uint256 hostItFee_, ERC20Mock usdt_) + { + _createRefundablePaidTicket(); + ticketId_ = factoryFacet.ticketCount(); + (uint256 fee, uint256 hostItFee, uint256 totalFee) = marketplaceFacet.getAllFees(ticketId_, FeeType.USDT); + usdt_ = ERC20Mock(marketplaceFacet.getFeeTokenAddress(FeeType.USDT)); + usdt_.mint(alice, totalFee); + vm.prank(alice); + usdt_.approve(address(marketplaceFacet), totalFee); + vm.prank(alice); + vm.expectEmit(true, true, true, true, hostIt); + emit TicketMinted(ticketId_, FeeType.USDT, totalFee, 1); + tokenId_ = marketplaceFacet.mintTicket(ticketId_, FeeType.USDT, alice); + fee_ = fee; + hostItFee_ = hostItFee; + } + + /// forge-lint: disable-next-line(mixed-case-function) + function _mintTicketUSDCRefundable() + internal + returns (uint64 ticketId_, uint40 tokenId_, uint256 fee_, uint256 hostItFee_, ERC20Mock usdc_) + { + _createRefundablePaidTicket(); + ticketId_ = factoryFacet.ticketCount(); + (uint256 fee, uint256 hostItFee, uint256 totalFee) = marketplaceFacet.getAllFees(ticketId_, FeeType.USDC); + usdc_ = ERC20Mock(marketplaceFacet.getFeeTokenAddress(FeeType.USDC)); + usdc_.mint(alice, totalFee); + vm.prank(alice); + usdc_.approve(address(marketplaceFacet), totalFee); + vm.prank(alice); + vm.expectEmit(true, true, true, true, hostIt); + emit TicketMinted(ticketId_, FeeType.USDC, totalFee, 1); + tokenId_ = marketplaceFacet.mintTicket(ticketId_, FeeType.USDC, alice); + fee_ = fee; + hostItFee_ = hostItFee; + } + function _createFreeTicket() internal { factoryFacet.createTicket(_getFreeTicketData(), _getZeroFeeType(), _getZeroFee()); } @@ -190,6 +246,10 @@ abstract contract DeployedHostItTickets is Test, DeployHostItTicketsHelper { factoryFacet.createTicket(_getPaidTicketData(), _getFeeTypes(), _getFees()); } + function _createRefundablePaidTicket() internal { + factoryFacet.createTicket(_getRefundablePaidTicketData(), _getFeeTypes(), _getFees()); + } + function _updatePaidTicket(uint40 _ticketId) internal { factoryFacet.updateTicket(_getPaidUpdatedTicketData(), _ticketId); } @@ -239,6 +299,21 @@ abstract contract DeployedHostItTickets is Test, DeployHostItTicketsHelper { }); } + function _getRefundablePaidTicketData() internal view returns (TicketData memory ticketData_) { + ticketData_ = TicketData({ + startTime: uint40(block.timestamp + 1 days), + endTime: uint40(block.timestamp + 2 days), + purchaseStartTime: _currentTime, + maxTickets: type(uint40).max, + maxTicketsPerUser: 0, + isFree: false, + isRefundable: true, + name: "Refundable Paid Ticket", + symbol: "", + uri: "ipfs://refundable" + }); + } + function _getPaidUpdatedTicketData() internal view returns (TicketData memory ticketData_) { ticketData_ = TicketData({ startTime: uint40(block.timestamp + 1 days), From 1598434332e469418f9539736250600680cc32ff Mon Sep 17 00:00:00 2001 From: David Dada Date: Fri, 3 Apr 2026 06:06:19 +0100 Subject: [PATCH 6/6] fmt: forge fmt --- script/helpers/DeployHostItTicketsHelper.sol | 4 +--- src/libs/LibMarketplace.sol | 6 +++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/script/helpers/DeployHostItTicketsHelper.sol b/script/helpers/DeployHostItTicketsHelper.sol index cde4b73..d7e00f8 100644 --- a/script/helpers/DeployHostItTicketsHelper.sol +++ b/script/helpers/DeployHostItTicketsHelper.sol @@ -75,9 +75,7 @@ abstract contract DeployHostItTicketsHelper is GetSelectors, Context { function _getHostItTickets() internal returns (address) { return HOST_IT_TICKETS.code.length == 0 ? address( - new HostItTickets{ - salt: HOST_IT_SALT - }( + new HostItTickets{salt: HOST_IT_SALT}( _createInitFacetCuts(_getDiamondCutFacet(), _getDiamondLoupeFacet(), _getOwnableRolesFacet()), _getDiamondInit(), abi.encodeWithSignature("initDiamond(address)", _msgSender()) diff --git a/src/libs/LibMarketplace.sol b/src/libs/LibMarketplace.sol index c6da564..fdff203 100644 --- a/src/libs/LibMarketplace.sol +++ b/src/libs/LibMarketplace.sol @@ -229,9 +229,9 @@ library LibMarketplace { function _createErc6551Account(address _ticketAddress, uint256 _tokenId) internal { try IERC6551Registry(ERC6551_REGISTRY) - .createAccount( - ACCOUNT_V3_IMPLEMENTATION, "", block.chainid, _ticketAddress, _tokenId - ) returns (address account) { + .createAccount(ACCOUNT_V3_IMPLEMENTATION, "", block.chainid, _ticketAddress, _tokenId) returns ( + address account + ) { if (account == address(0)) { revert CreateERC6551AccountFailed(); }