diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4481ec6..dfd97a0 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -19,6 +19,8 @@ jobs: - name: Install Foundry uses: foundry-rs/foundry-toolchain@v1 + with: + version: nightly - name: Show Forge version run: | diff --git a/.gitignore b/.gitignore index 864e093..239da05 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,11 @@ out/ !/broadcast /broadcast/*/31337/ /broadcast/**/dry-run/ +*broadcast/ + +# Crytic +crytic-export/ +medusa.json # Docs docs/ diff --git a/foundry.toml b/foundry.toml index c6a2dc8..9937afa 100644 --- a/foundry.toml +++ b/foundry.toml @@ -14,34 +14,35 @@ remappings = [ "@tokenbound/=lib/contracts/src/" ] ffi = true -optimizer_runs = 30_000 +optimizer_runs = 999_999 + +[fmt] +sort_imports = true + +[fuzz] +runs = 10_000 +max_test_rejects = 999_999 [rpc_endpoints] -# mainnet -eth-mainnet = "${ETH_MAINNET_URL}" -base = "${BASE_MAINNET_URL}" -lisk = "${LISK_MAINNET_URL}" -arbitrum-one = "${ARBITRUM_MAINNET_URL}" -avalanche = "${AVALANCHE_MAINNET_URL}" -# testnets +mainnet = "${ETH_MAINNET_URL}" sepolia = "${ETH_SEPOLIA_URL}" +base = "${BASE_MAINNET_URL}" base-sepolia = "${BASE_SEPOLIA_URL}" +lisk = "${LISK_MAINNET_URL}" lisk-sepolia = "${LISK_SEPOLIA_URL}" +arbitrum-one = "${ARBITRUM_MAINNET_URL}" arbitrum-sepolia = "${ARBITRUM_SEPOLIA_URL}" +avalanche = "${AVALANCHE_MAINNET_URL}" avalanche-fuji = "${AVALANCHE_FUJI_URL}" [etherscan] -# mainnet -eth-mainnet = { key = "${ETHERSCAN_API_KEY}" } -base = { key = "${ETHERSCAN_API_KEY}" } -lisk = { key = "123", url = "${LISK_VERIFIER_URL}" } -arbitrum-one = { key = "${ETHERSCAN_API_KEY}" } -avalanche = { key = "verifyContract", url = "${AVALANCHE_VERIFIER_URL}" } -# testnets +mainnet = { key = "${ETHERSCAN_API_KEY}" } sepolia = { key = "${ETHERSCAN_API_KEY}" } +base = { key = "${ETHERSCAN_API_KEY}" } base-sepolia = { key = "${ETHERSCAN_API_KEY}" } +lisk = { key = "123", url = "${LISK_VERIFIER_URL}" } lisk-sepolia = { key = "123", url = "${LISK_SEPOLIA_VERIFIER_URL}" } +arbitrum-one = { key = "${ETHERSCAN_API_KEY}" } arbitrum-sepolia = { key = "${ETHERSCAN_API_KEY}" } +avalanche = { key = "verifyContract", url = "${AVALANCHE_VERIFIER_URL}" } avalanche-fuji = { key = "verifyContract", url = "${AVALANCHE_FUJI_VERIFIER_URL}" } - -# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options diff --git a/lib/contracts b/lib/contracts index 2bd70ff..bce75f9 160000 --- a/lib/contracts +++ b/lib/contracts @@ -1 +1 @@ -Subproject commit 2bd70ff3fb5f1c0e562425b4d5312f619d9f2720 +Subproject commit bce75f985558fad3d06ee1b86f224f0cfb783631 diff --git a/lib/erc2535-diamond-template b/lib/erc2535-diamond-template index cbc0de4..ac991b3 160000 --- a/lib/erc2535-diamond-template +++ b/lib/erc2535-diamond-template @@ -1 +1 @@ -Subproject commit cbc0de4c7e72210150a56b69a00bf03f2c22a9b2 +Subproject commit ac991b3dca8fa854a850de039bb94be4886136fc diff --git a/lib/forge-std b/lib/forge-std index 8bbcf6e..551a2d3 160000 --- a/lib/forge-std +++ b/lib/forge-std @@ -1 +1 @@ -Subproject commit 8bbcf6e3f8f62f419e5429a0bd89331c85c37824 +Subproject commit 551a2d30d7fecba7092ab45a587b5268149a48fb diff --git a/lib/solady b/lib/solady index 33b4b98..208e4f3 160000 --- a/lib/solady +++ b/lib/solady @@ -1 +1 @@ -Subproject commit 33b4b98e350bbcba6aa85642957c313e98b5f911 +Subproject commit 208e4f31cfae26e4983eb95c3488a14fdc497ad7 diff --git a/script/DeployHostItTickets.s.sol b/script/DeployHostItTickets.s.sol index 2e6c611..23f4c70 100644 --- a/script/DeployHostItTickets.s.sol +++ b/script/DeployHostItTickets.s.sol @@ -1,22 +1,22 @@ // SPDX-License-Identifier: AGPL-3.0-only pragma solidity 0.8.30; -import {Script} from "forge-std/Script.sol"; import {DiamondCutFacet} from "@diamond/facets/DiamondCutFacet.sol"; import {DiamondLoupeFacet} from "@diamond/facets/DiamondLoupeFacet.sol"; import {OwnableRolesFacet} from "@diamond/facets/OwnableRolesFacet.sol"; -import {MultiInit} from "@diamond/initializers/MultiInit.sol"; import {ERC165Init} from "@diamond/initializers/ERC165Init.sol"; -import {HostItInit} from "@ticket/inits/HostItInit.sol"; +import {MultiInit} from "@diamond/initializers/MultiInit.sol"; +import {UpgradeableBeacon} from "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol"; +import {DeployHostItTicketsHelper} from "@ticket-script/helper/DeployHostItTicketsHelper.sol"; +import {LibAddressesAndFees} from "@ticket-script/helper/LibAddressesAndFees.sol"; import {HostItTickets} from "@ticket/HostItTickets.sol"; -import {FactoryFacet} from "@ticket/facets/FactoryFacet.sol"; import {CheckInFacet} from "@ticket/facets/CheckInFacet.sol"; +import {FactoryFacet} from "@ticket/facets/FactoryFacet.sol"; import {MarketplaceFacet} from "@ticket/facets/MarketplaceFacet.sol"; -import {AddressesAndFees} from "@ticket-script/helper/AddressesAndFees.sol"; -import {UpgradeableBeacon} from "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol"; +import {HostItInit} from "@ticket/inits/HostItInit.sol"; import {Ticket} from "@ticket/libs/Ticket.sol"; import {TicketProxy} from "@ticket/libs/TicketProxy.sol"; -import {DeployHostItTicketsHelper} from "@ticket-script/helper/DeployHostItTicketsHelper.sol"; +import {Script} from "forge-std/Script.sol"; contract DeployHostItTicketsTest is Script, DeployHostItTicketsHelper { function run() public returns (address hostIt_) { @@ -43,20 +43,16 @@ contract DeployHostItTicketsTest is Script, DeployHostItTicketsHelper { // Deploy Ticket Proxy address ticketProxy = address(new TicketProxy(ticketBeacon)); + (address[] memory addresses, uint8[] memory feeTypes) = + LibAddressesAndFees._getAddressesAndFeesByChainId(block.chainid); + // Deploy HostItTickets diamond hostIt_ = address( new HostItTickets( _createFacetCuts( diamondCutFacet, diamondLoupeFacet, ownableRolesFacet, factoryFacet, checkInFacet, marketplaceFacet ), - _createDiamondArgs( - multiInit, - erc165Init, - hostItInit, - ticketProxy, - AddressesAndFees._getMockFeeTypes(), - AddressesAndFees._getMockAddresses() - ) + _createDiamondArgs(multiInit, erc165Init, hostItInit, ticketProxy, feeTypes, addresses) ) ); vm.stopBroadcast(); diff --git a/script/DeployHostItTicketsLisk.s.sol b/script/DeployHostItTicketsLisk.s.sol deleted file mode 100644 index d62c304..0000000 --- a/script/DeployHostItTicketsLisk.s.sol +++ /dev/null @@ -1,65 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity 0.8.30; - -import {Script} from "forge-std/Script.sol"; -import {DiamondCutFacet} from "@diamond/facets/DiamondCutFacet.sol"; -import {DiamondLoupeFacet} from "@diamond/facets/DiamondLoupeFacet.sol"; -import {OwnableRolesFacet} from "@diamond/facets/OwnableRolesFacet.sol"; -import {MultiInit} from "@diamond/initializers/MultiInit.sol"; -import {ERC165Init} from "@diamond/initializers/ERC165Init.sol"; -import {HostItInit} from "@ticket/inits/HostItInit.sol"; -import {HostItTickets} from "@ticket/HostItTickets.sol"; -import {FactoryFacet} from "@ticket/facets/FactoryFacet.sol"; -import {CheckInFacet} from "@ticket/facets/CheckInFacet.sol"; -import {MarketplaceFacet} from "@ticket/facets/MarketplaceFacet.sol"; -import {Ticket} from "@ticket/libs/Ticket.sol"; -import {AddressesAndFees} from "@ticket-script/helper/AddressesAndFees.sol"; -import {UpgradeableBeacon} from "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol"; -import {BeaconProxy} from "@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol"; -import {DeployHostItTicketsHelper} from "@ticket-script/helper/DeployHostItTicketsHelper.sol"; - -contract DeployHostItTicketsLiskSep is Script, DeployHostItTicketsHelper { - function run() public returns (address hostIt_) { - // vm.createSelectFork("lisk-sepolia"); - vm.startBroadcast(); - // Deploy facets - address diamondCutFacet = address(new DiamondCutFacet()); - address diamondLoupeFacet = address(new DiamondLoupeFacet()); - address ownableRolesFacet = address(new OwnableRolesFacet()); - address factoryFacet = address(new FactoryFacet()); - address checkInFacet = address(new CheckInFacet()); - address marketplaceFacet = address(new MarketplaceFacet()); - - // Deploy initializers - address multiInit = address(new MultiInit()); - address erc165Init = address(new ERC165Init()); - address hostItInit = address(new HostItInit()); - - // Deploy Ticket Impl - address ticketImpl = address(new Ticket()); - - // Deploy Ticket Beacon - address ticketBeacon = address(new UpgradeableBeacon(ticketImpl, _msgSender())); - - // Deploy Ticket Proxy - address ticketProxy = address(new BeaconProxy(ticketBeacon, "")); - - // Deploy HostItTickets diamond - hostIt_ = address( - new HostItTickets( - _createFacetCuts( - diamondCutFacet, diamondLoupeFacet, ownableRolesFacet, factoryFacet, checkInFacet, marketplaceFacet - ), - _createDiamondArgs( - multiInit, - erc165Init, - hostItInit, - ticketProxy, - AddressesAndFees._getLiskSepoliaFeeTypes(), - AddressesAndFees._getLiskSepoliaAddresses() - ) - ) - ); - vm.stopBroadcast(); - } -} diff --git a/script/helper/DeployHostItTicketsHelper.sol b/script/helper/DeployHostItTicketsHelper.sol index 786eb37..e7dcd81 100644 --- a/script/helper/DeployHostItTicketsHelper.sol +++ b/script/helper/DeployHostItTicketsHelper.sol @@ -1,11 +1,11 @@ // SPDX-License-Identifier: AGPL-3.0-only pragma solidity 0.8.30; -import {HelperContract} from "@diamond-test/helpers/HelperContract.sol"; +import {DiamondArgs, FacetCut, FacetCutAction} from "@diamond-storage/DiamondStorage.sol"; +import {GetSelectors} from "@diamond-test/helpers/GetSelectors.sol"; import {Context} from "@openzeppelin/contracts/utils/Context.sol"; -import {FacetCut, FacetCutAction, DiamondArgs} from "@diamond-storage/DiamondStorage.sol"; -abstract contract DeployHostItTicketsHelper is HelperContract, Context { +abstract contract DeployHostItTicketsHelper is GetSelectors, Context { function _createFacetCuts( address _diamondCutFacet, address _diamondLoupeFacet, @@ -14,43 +14,38 @@ abstract contract DeployHostItTicketsHelper is HelperContract, Context { address _checkInFacet, address _marketplaceFacet ) internal returns (FacetCut[] memory cuts_) { - // Create an array of FacetCut entries for standard facets cuts_ = new FacetCut[](6); cuts_[0] = FacetCut({ facetAddress: _diamondCutFacet, action: FacetCutAction.Add, - functionSelectors: _generateSelectors("DiamondCutFacet") + functionSelectors: _getSelectors("DiamondCutFacet") }); cuts_[1] = FacetCut({ facetAddress: _diamondLoupeFacet, action: FacetCutAction.Add, - functionSelectors: _generateSelectors("DiamondLoupeFacet") + functionSelectors: _getSelectors("DiamondLoupeFacet") }); cuts_[2] = FacetCut({ facetAddress: _ownableRolesFacet, action: FacetCutAction.Add, - functionSelectors: _generateSelectors("OwnableRolesFacet") + functionSelectors: _getSelectors("OwnableRolesFacet") }); cuts_[3] = FacetCut({ - facetAddress: _factoryFacet, - action: FacetCutAction.Add, - functionSelectors: _generateSelectors("FactoryFacet") + facetAddress: _factoryFacet, action: FacetCutAction.Add, functionSelectors: _getSelectors("FactoryFacet") }); cuts_[4] = FacetCut({ - facetAddress: _checkInFacet, - action: FacetCutAction.Add, - functionSelectors: _generateSelectors("CheckInFacet") + facetAddress: _checkInFacet, action: FacetCutAction.Add, functionSelectors: _getSelectors("CheckInFacet") }); cuts_[5] = FacetCut({ facetAddress: _marketplaceFacet, action: FacetCutAction.Add, - functionSelectors: _generateSelectors("MarketplaceFacet") + functionSelectors: _getSelectors("MarketplaceFacet") }); } @@ -71,7 +66,6 @@ abstract contract DeployHostItTicketsHelper is HelperContract, Context { initData[1] = abi.encodeWithSignature("initHostIt(address,uint8[],address[])", _ticketProxy, _feeTypes, _addresses); - // Prepare DiamondArgs: owner and init data args_ = DiamondArgs({ owner: _msgSender(), init: _multiInit, diff --git a/script/helper/AddressesAndFees.sol b/script/helper/LibAddressesAndFees.sol similarity index 88% rename from script/helper/AddressesAndFees.sol rename to script/helper/LibAddressesAndFees.sol index 60d5ed6..27679a9 100644 --- a/script/helper/AddressesAndFees.sol +++ b/script/helper/LibAddressesAndFees.sol @@ -4,7 +4,49 @@ pragma solidity ^0.8.4; import {ERC20Mock} from "@openzeppelin/contracts/mocks/token/ERC20Mock.sol"; import {FeeType} from "@ticket-storage/MarketplaceStorage.sol"; -library AddressesAndFees { +library LibAddressesAndFees { + function _getAddressesAndFeesByChainId(uint256 _chainId) + internal + returns (address[] memory addresses_, uint8[] memory feeTypes_) + { + if (_chainId == 1) { + addresses_ = _getEthereumAddresses(); + feeTypes_ = _getEthereumFeeTypes(); + } else if (_chainId == 8453) { + addresses_ = _getBaseAddresses(); + feeTypes_ = _getBaseFeeTypes(); + } else if (_chainId == 43114) { + addresses_ = _getAvalancheAddresses(); + feeTypes_ = _getAvalancheFeeTypes(); + } else if (_chainId == 42161) { + addresses_ = _getArbitrumOneAddresses(); + feeTypes_ = _getArbitrumOneFeeTypes(); + } else if (_chainId == 1135) { + addresses_ = _getLiskAddresses(); + feeTypes_ = _getLiskFeeTypes(); + } else if (_chainId == 11155111) { + addresses_ = _getEthereumSepoliaAddresses(); + feeTypes_ = _getEthereumSepoliaFeeTypes(); + } else if (_chainId == 84532) { + addresses_ = _getBaseSepoliaAddresses(); + feeTypes_ = _getBaseSepoliaFeeTypes(); + } else if (_chainId == 43113) { + addresses_ = _getAvalancheFujiAddresses(); + feeTypes_ = _getAvalancheFujiFeeTypes(); + } else if (_chainId == 421614) { + addresses_ = _getArbitrumSepoliaAddresses(); + feeTypes_ = _getArbitrumSepoliaFeeTypes(); + } else if (_chainId == 4202) { + addresses_ = _getLiskSepoliaAddresses(); + feeTypes_ = _getLiskSepoliaFeeTypes(); + } else if (_chainId == 31337) { + addresses_ = _getMockAddresses(); + feeTypes_ = _getMockFeeTypes(); + } else { + revert("Chain not supported"); + } + } + //*////////////////////////////////////////////////////////////////////////// // MAINNET ADDRESSES //////////////////////////////////////////////////////////////////////////*// diff --git a/src/HostItTickets.sol b/src/HostItTickets.sol index 4415814..ece991f 100644 --- a/src/HostItTickets.sol +++ b/src/HostItTickets.sol @@ -1,32 +1,36 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.30; -import {Diamond} from "@diamond/Diamond.sol"; -import {DiamondArgs, FacetCut} from "@diamond-storage/DiamondStorage.sol"; +import {Diamond, DiamondArgs, FacetCut} from "@diamond/Diamond.sol"; /* -⣾⣿⣿⣿⣿⣿⣿⣿⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣰⣿⣿⣿⣿⣿⣿⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ -⣿⣿⣿⡿⠿⢿⣿⣿⣗⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣽⣿⣿⠿⠿⠿⢿⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ -⣿⣿⣿⡇⠀⠈⢿⣿⣿⣷⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣰⣿⣿⣿⠏⠀⠀⠀⢸⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ -⣿⣿⣿⡇⠀⠀⠈⠻⣿⣿⣿⣷⣤⣀⡀⠀⠀⠀⠀⢀⣠⣤⣿⣿⣿⡿⠋⠀⠀⠀⠀⢸⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⣀⣀⣀⣀⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣀⣀⣀⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣀⣀⣀⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ -⣿⣿⣿⡇⠀⠀⠀⠀⠈⠻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠿⠋⠀⠀⠀⠀⠀⠀⢸⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⡅⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣽⣿⣿⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ -⣿⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠉⠛⠻⠿⠿⠿⠿⠿⠟⠛⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⡂⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢾⣿⣿⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠀⠀⠀⠀⠀⠀⠀ -⣿⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⡏⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⠅⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣻⣿⣿⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣟⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⢀⣾⣿⡇⠀⠀⠀⠀⠀⠀ -⣿⣿⣿⡇⠀⠀⠀⢨⠄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡜⠀⠀⠀⠀⢸⣿⣿⡗⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⡃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣽⣿⣿⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣯⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀⢀⣾⣿⣿⡇⠀⠀⠀⠀⠀⠀ -⣿⣿⣿⡇⠀⠀⠀⢨⣛⡤⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣤⣛⠦⠀⠀⠀⠀⢸⣿⣿⡧⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⠆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢾⣿⣿⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣀⣤⣤⣄⣀⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⣠⣤⣤⣀⣀⡀⠀⠀⠀⠀⠀⠀⠀⢠⣾⣿⣿⣿⣷⣀⣀⣀⣀⡀⠀⠀⠀⠀⣿⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀⣠⣿⣿⣿⣿⣇⣀⣀⣀⣀⠀⠀ -⣿⣿⣿⡇⠀⠀⠀⢘⠶⣙⠯⣖⢤⣀⡀⠀⠀⠀⠀⣀⣀⢤⡲⢏⡶⣩⠗⠀⠀⠀⠀⢸⣿⣿⡗⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⣦⣤⣤⣤⣤⣤⣤⣤⣤⣤⣤⣤⣤⣤⣿⣿⣿⣿⣿⡇⠀⠀⠀⠀⠀⠀⢀⣤⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣶⣄⡀⠀⠀⠀⠀⠀⢀⣰⣾⣿⣿⣿⣿⣿⣿⣿⣿⣷⣦⡄⠀⠀⣠⣶⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡅⠀⠀⠀⠀⣿⣿⣿⣿⣿⠀⠀⠀⠀⣠⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠀⠀ -⣿⣿⣿⡇⠀⠀⠀⢸⡹⢭⡳⢎⡗⣮⢹⡭⣛⡼⣹⠖⣭⢞⡱⣏⢶⡹⣚⠀⠀⠀⠀⢸⣿⣿⣏⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇⠀⠀⠀⠀⠀⣴⣿⣿⣿⣿⣿⣿⡿⠿⣿⣿⣿⣿⣿⣿⣷⣂⠀⠀⠀⠀⣼⣿⣿⣿⣿⡿⠿⠿⠿⣿⣿⣿⡟⠁⠀⢸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠆⠀⠀⠀⠀⣿⣿⣿⣿⣿⠀⠀⠀⢘⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠀⠀ -⣿⣿⣿⡇⠀⠀⠀⢰⢫⡳⢭⣛⡼⢎⣧⠳⣝⡼⣱⠻⣜⢮⢳⠭⣞⡱⢧⠀⠀⠀⠀⢸⣿⣿⡧⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇⠀⠀⠀⠀⣼⣿⣿⣿⣿⠟⠉⠀⠀⠀⠀⠀⠙⢿⣿⣿⣿⣿⡄⠀⠀⢈⣿⣿⣿⣿⡇⠀⠀⠀⠀⠀⠉⠉⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀⣻⣿⣿⣿⣿⡇⠀⠀⠀⠀⠀⠀ -⣿⣿⣿⡇⠀⠀⠀⢨⡳⢭⣓⠧⠚⠉⠀⠉⠈⠈⠁⠉⠘⠃⠯⣛⡴⣫⢳⠀⠀⠀⠀⢸⣿⣿⡷⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢾⣿⣿⣿⣿⡇⠀⠀⠀⢰⣿⣿⣿⣿⡏⠀⠀⠀⠀⠀⠀⠀⠀⠀⢿⣿⣿⣿⣯⠀⠀⠀⠹⣿⣿⣿⣿⣷⣶⣤⣄⣀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀⣽⣿⣿⣿⣿⡇⠀⠀⠀⠀⠀⠀ -⣿⣿⣿⡇⠀⠀⠀⢰⢫⠇⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠑⢧⢏⠀⠀⠀⠀⢸⣿⣿⣟⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣻⣿⣿⣿⣿⡇⠀⠀⠀⢸⣿⣿⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣿⠁⠀⠀⠀⠉⠻⢿⣿⣿⣿⣿⣿⣿⣿⣶⣄⠀⠀⠀⠀⠀⢸⣿⣿⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀⢾⣿⣿⣿⣿⡇⠀⠀⠀⠀⠀⠀ -⣿⣿⣿⡇⠀⠀⠀⢈⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠻⠀⠀⠀⠀⢸⣿⣿⣯⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣽⣿⣿⣿⣿⡇⠀⠀⠀⠸⣿⣿⣿⣿⣧⠀⠀⠀⠀⠀⠀⠀⠀⢀⣾⣿⣿⣿⣏⠁⠀⠀⠀⠀⠀⠀⠈⠉⠛⠻⠿⣿⣿⣿⣿⣷⠀⠀⠀⠀⢸⣿⣿⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀⣻⣿⣿⣿⣿⡇⠀⠀⠀⠀⠀⠀ -⣿⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣷⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢾⣿⣿⣿⣿⡇⠀⠀⠀⠀⢻⣿⣿⣿⣿⣷⣄⡀⠀⠀⠀⢀⣠⣾⣿⣿⣿⣿⠁⠀⠀⠀⢀⣤⣀⡀⠀⠀⠀⠀⠀⣹⣿⣿⣿⣿⡇⠀⠀⠀⢸⣿⣿⣿⣿⣿⡀⠀⠀⣀⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀⣽⣿⣿⣿⣿⣧⡀⠀⢀⣀⠀⠀ -⣿⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⢀⣠⣤⣤⣤⣤⣤⣤⣄⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣻⣿⣿⣿⣿⡇⠀⠀⠀⠀⠀⠻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡟⠁⠀⠀⠀⢠⣾⣿⣿⣿⣿⣶⣶⣶⣾⣿⣿⣿⣿⡿⠀⠀⠀⠀⠈⣿⣿⣿⣿⣿⣿⣿⣿⣿⡧⠀⠀⠀⠀⣿⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀⠰⢿⣿⣿⣿⣿⣿⣿⣿⣿⣆⠀ -⣿⣿⣿⡇⠀⠀⠀⠀⠀⣠⣴⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣦⣄⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣽⣿⣿⣿⣿⡇⠀⠀⠀⠀⠀⠀⠈⠛⠿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠟⠁⠀⠀⠀⠀⠀⠉⠛⠿⢿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠛⠀⠀⠀⠀⠀⠀⠈⠻⣿⣿⣿⣿⣿⣿⣿⠿⠃⠀⠀⠀⣿⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀⠘⠿⣿⣿⣿⣿⣿⣿⣿⠿⠂ -⣿⣿⣿⡇⠀⠀⠀⣠⣾⣿⣿⣿⠿⠛⠉⠉⠉⠉⠉⠙⠛⠿⣿⣿⣿⣷⡄⠀⠀⠀⠀⢸⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠈⠉⠉⠉⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠉⠉⠉⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠉⠉⠉⠉⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠉⠉⠉⠉⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠉⠉⠉⠉⠀⠀⠀⠀⠀⠀⠉⠉⠉⠉⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠉⠉⠉⠉⠀⠀⠀ -⣿⣿⣿⡇⠀⠀⣴⣿⣿⣿⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢻⣿⣿⣿⣆⠀⠀⠀⢸⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ -⣿⣿⣿⣷⣶⣾⣿⣿⡟⠂⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⣿⣿⣿⣶⣶⣶⣾⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ -⣿⣿⣿⣿⣿⣿⣿⣿⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣿⣿⣿⣿⣿⡿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⣾⣿⣿⣿⣿⣿⣿⣿⣿⣆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡆ +⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇ +⣿⣿⣿⣿⠉⠉⠹⣿⣿⣿⣿⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣼⣿⣿⣿⡟⠉⠉⠉⢹⣿⣿⣿⡇ +⣿⣿⣿⣿⠀⠀⠀⠹⣿⣿⣿⣿⣧⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣴⣿⣿⣿⣿⠟⠀⠀⠀⠀⢸⣿⣿⣿⡇ +⣿⣿⣿⣿⠀⠀⠀⠀⠈⢿⣿⣿⣿⣿⣷⣦⣄⣀⣀⣀⣀⣀⣀⣀⣤⣶⣿⣿⣿⣿⡿⠋⠀⠀⠀⠀⠀⢸⣿⣿⣿⡇ +⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀⠙⠻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠟⠋⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⡇ +⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠛⠿⠿⣿⣿⣿⣿⣿⣿⠿⠿⠛⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⡇ +⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⡇ +⣿⣿⣿⣿⠀⠀⠀⠀⠀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⠀⠀⠀⠀⠀⢸⣿⣿⣿⡇ +⣿⣿⣿⣿⠀⠀⠀⠀⠀⣿⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣿⠀⠀⠀⠀⠀⢸⣿⣿⣿⡇ +⣿⣿⣿⣿⠀⠀⠀⠀⠀⣿⣿⣶⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣾⣿⣿⠀⠀⠀⠀⠀⢸⣿⣿⣿⡇ +⣿⣿⣿⣿⠀⠀⠀⠀⠀⣿⣿⣿⣿⣷⣦⣄⣀⡀⠀⠀⠀⠀⠀⢀⣀⣤⣶⣿⣿⣿⣿⣿⠀⠀⠀⠀⠀⢸⣿⣿⣿⡇ +⣿⣿⣿⣿⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠀⠀⠀⠀⠀⢸⣿⣿⣿⡇ +⣿⣿⣿⣿⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠀⠀⠀⠀⠀⢸⣿⣿⣿⡇ +⣿⣿⣿⣿⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⣿⣿⣿⠿⠿⠛⠛⠛⠿⠿⣿⣿⣿⣿⣿⣿⣿⣿⠀⠀⠀⠀⠀⢸⣿⣿⣿⡇ +⣿⣿⣿⣿⠀⠀⠀⠀⠀⣿⣿⣿⡿⠟⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠉⠻⢿⣿⣿⣿⠀⠀⠀⠀⠀⢸⣿⣿⣿⡇ +⣿⣿⣿⣿⠀⠀⠀⠀⠀⣿⡿⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢿⣿⠀⠀⠀⠀⠀⢸⣿⣿⣿⡇ +⣿⣿⣿⣿⠀⠀⠀⠀⠀⡟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠻⠀⠀⠀⠀⠀⢸⣿⣿⣿⡇ +⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⡇ +⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⣀⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⡇ +⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣤⣶⣿⣿⣿⣿⣿⣿⣿⣿⣿⣶⣤⣄⡀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⡇ +⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀⢠⣴⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣦⡀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⡇ +⣿⣿⣿⣿⠀⠀⠀⠀⢀⣼⣿⣿⣿⣿⡿⠟⠉⠉⠀⠀⠀⠀⠈⠉⠙⠻⣿⣿⣿⣿⣿⣦⠀⠀⠀⠀⠀⢸⣿⣿⣿⡇ +⣿⣿⣿⣿⠀⠀⠀⣰⣿⣿⣿⣿⠟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢿⣿⣿⣿⣷⡀⠀⠀⠀⢸⣿⣿⣿⡇ +⣿⣿⣿⣿⣄⣀⣰⣿⣿⣿⡿⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢻⣿⣿⣿⣷⣤⣤⣤⣸⣿⣿⣿⡇ +⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇ +⠻⠿⠿⠿⠿⠿⠿⠿⠿⠏⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠻⠿⠿⠿⠿⠿⠿⠿⠿⠿⠃ */ /// @title HostIt Tickets diff --git a/src/facets/CheckInFacet.sol b/src/facets/CheckInFacet.sol index cee36d1..85cd4d6 100644 --- a/src/facets/CheckInFacet.sol +++ b/src/facets/CheckInFacet.sol @@ -1,24 +1,25 @@ // SPDX-License-Identifier: AGPL-3.0-only pragma solidity 0.8.30; +import {ICheckIn} from "@ticket/interfaces/ICheckIn.sol"; import {LibCheckIn} from "@ticket/libs/LibCheckIn.sol"; -contract CheckInFacet { - using LibCheckIn for uint56; +contract CheckInFacet is ICheckIn { + using LibCheckIn for uint64; //*////////////////////////////////////////////////////////////////////////// // EXTERNAL FUNCTIONS //////////////////////////////////////////////////////////////////////////*// - function checkIn(uint56 _ticketId, address _ticketOwner, uint256 _tokenId) external { + function checkIn(uint64 _ticketId, address _ticketOwner, uint256 _tokenId) external { _ticketId._checkin(_ticketOwner, _tokenId); } - function addTicketAdmins(uint56 _ticketId, address[] calldata _admins) external { + function addTicketAdmins(uint64 _ticketId, address[] calldata _admins) external { _ticketId._addTicketAdmins(_admins); } - function removeTicketAdmins(uint56 _ticketId, address[] calldata _admins) external { + function removeTicketAdmins(uint64 _ticketId, address[] calldata _admins) external { _ticketId._removeTicketAdmins(_admins); } @@ -26,19 +27,19 @@ contract CheckInFacet { // VIEW FUNCTIONS //////////////////////////////////////////////////////////////////////////*// - function isCheckedIn(uint56 _ticketId, address _ticketOwner) external view returns (bool) { + function isCheckedIn(uint64 _ticketId, address _ticketOwner) external view returns (bool) { return _ticketId._isCheckedIn(_ticketOwner); } - function isCheckedInForDay(uint56 _ticketId, uint8 _day, address _ticketOwner) external view returns (bool) { + function isCheckedInForDay(uint64 _ticketId, uint8 _day, address _ticketOwner) external view returns (bool) { return _ticketId._isCheckedInForDay(_day, _ticketOwner); } - function getCheckedIn(uint56 _ticketId) external view returns (address[] memory) { + function getCheckedIn(uint64 _ticketId) external view returns (address[] memory) { return _ticketId._getCheckedIn(); } - function getCheckedInForDay(uint56 _ticketId, uint8 _day) external view returns (address[] memory) { + function getCheckedInForDay(uint64 _ticketId, uint8 _day) external view returns (address[] memory) { return _ticketId._getCheckedInForDay(_day); } } diff --git a/src/facets/FactoryFacet.sol b/src/facets/FactoryFacet.sol index f5bb3f0..3636655 100644 --- a/src/facets/FactoryFacet.sol +++ b/src/facets/FactoryFacet.sol @@ -1,11 +1,12 @@ // SPDX-License-Identifier: AGPL-3.0-only pragma solidity 0.8.30; -import {LibFactory} from "@ticket/libs/LibFactory.sol"; -import {TicketData, FullTicketData} from "@ticket-storage/FactoryStorage.sol"; +import {FullTicketData, TicketData} from "@ticket-storage/FactoryStorage.sol"; import {FeeType} from "@ticket-storage/MarketplaceStorage.sol"; +import {IFactory} from "@ticket/interfaces/IFactory.sol"; +import {LibFactory} from "@ticket/libs/LibFactory.sol"; -contract FactoryFacet { +contract FactoryFacet is IFactory { using LibFactory for *; //*////////////////////////////////////////////////////////////////////////// @@ -18,7 +19,7 @@ contract FactoryFacet { _ticketData._createTicket(_feeTypes, _fees); } - function updateTicket(TicketData calldata _ticketData, uint56 _ticketId) external { + function updateTicket(TicketData calldata _ticketData, uint64 _ticketId) external { _ticketData._updateTicket(_ticketId); } @@ -26,15 +27,15 @@ contract FactoryFacet { // VIEW FUNCTIONS //////////////////////////////////////////////////////////////////////////*// - function ticketCount() public view returns (uint56) { + function ticketCount() public view returns (uint64) { return LibFactory._getTicketCount(); } - function ticketExists(uint56 _ticketId) public view returns (bool) { + function ticketExists(uint64 _ticketId) public view returns (bool) { return _ticketId._ticketExists(); } - function ticketData(uint56 _ticketId) public view returns (FullTicketData memory) { + function ticketData(uint64 _ticketId) public view returns (FullTicketData memory) { return _ticketId._getFullTicketData(); } @@ -42,7 +43,7 @@ contract FactoryFacet { return LibFactory._getAllFullTicketData(); } - function adminTickets(address _ticketAdmin) public view returns (uint56[] memory) { + function adminTickets(address _ticketAdmin) public view returns (uint64[] memory) { return _ticketAdmin._getAdminTicketIds(); } @@ -58,15 +59,15 @@ contract FactoryFacet { return LibFactory._getHostItTicketHash(); } - function ticketHash(uint56 _ticketId) public pure returns (bytes32) { + function ticketHash(uint64 _ticketId) public pure returns (bytes32) { return _ticketId._generateTicketHash(); } - function mainAdminRole(uint56 _ticketId) public pure returns (uint256) { + function mainAdminRole(uint64 _ticketId) public pure returns (uint256) { return _ticketId._generateMainTicketAdminRole(); } - function ticketAdminRole(uint56 _ticketId) public pure returns (uint256) { + function ticketAdminRole(uint64 _ticketId) public pure returns (uint256) { return _ticketId._generateTicketAdminRole(); } } diff --git a/src/facets/MarketplaceFacet.sol b/src/facets/MarketplaceFacet.sol index 01c931a..eb3f8ef 100644 --- a/src/facets/MarketplaceFacet.sol +++ b/src/facets/MarketplaceFacet.sol @@ -1,9 +1,9 @@ // SPDX-License-Identifier: AGPL-3.0-only pragma solidity 0.8.30; +import {FeeType} from "@ticket-storage/MarketplaceStorage.sol"; import {IMarketplace} from "@ticket/interfaces/IMarketplace.sol"; import {LibMarketplace} from "@ticket/libs/LibMarketplace.sol"; -import {FeeType} from "@ticket-storage/MarketplaceStorage.sol"; contract MarketplaceFacet is IMarketplace { using LibMarketplace for *; @@ -12,15 +12,15 @@ contract MarketplaceFacet is IMarketplace { // EXTERNAL FUNCTIONS //////////////////////////////////////////////////////////////////////////*// - function mintTicket(uint56 _ticketId, FeeType _feeType, address _buyer) external payable returns (uint40) { + function mintTicket(uint64 _ticketId, FeeType _feeType, address _buyer) external payable returns (uint40) { return _ticketId._mintTicket(_feeType, _buyer); } - function setTicketFees(uint56 _ticketId, FeeType[] calldata _feeTypes, uint256[] calldata _fees) external { + function setTicketFees(uint64 _ticketId, FeeType[] calldata _feeTypes, uint256[] calldata _fees) external { _ticketId._setTicketFees(_feeTypes, _fees); } - function withdrawTicketBalance(uint56 _ticketId, FeeType _feeType, address _to) external { + function withdrawTicketBalance(uint64 _ticketId, FeeType _feeType, address _to) external { _ticketId._withdrawTicketBalance(_feeType, _to); } @@ -32,7 +32,7 @@ contract MarketplaceFacet is IMarketplace { // VIEW FUNCTIONS //////////////////////////////////////////////////////////////////////////*// - function isFeeEnabled(uint56 _ticketId, FeeType _feeType) external view returns (bool) { + function isFeeEnabled(uint64 _ticketId, FeeType _feeType) external view returns (bool) { return _ticketId._isFeeEnabled(_feeType); } @@ -40,11 +40,11 @@ contract MarketplaceFacet is IMarketplace { return _feeType._getFeeTokenAddress(); } - function getTicketFee(uint56 _ticketId, FeeType _feeType) external view returns (uint256) { + function getTicketFee(uint64 _ticketId, FeeType _feeType) external view returns (uint256) { return _ticketId._getTicketFee(_feeType); } - function getAllFees(uint56 _ticketId, FeeType _feeType) + function getAllFees(uint64 _ticketId, FeeType _feeType) external view returns (uint256 ticketFee_, uint256 hostItFee_, uint256 totalFee_) @@ -52,7 +52,7 @@ contract MarketplaceFacet is IMarketplace { return _ticketId._getFees(_feeType); } - function getTicketBalance(uint56 _ticketId, FeeType _feeType) external view returns (uint256) { + function getTicketBalance(uint64 _ticketId, FeeType _feeType) external view returns (uint256) { return _ticketId._getTicketBalance(_feeType); } @@ -60,14 +60,14 @@ contract MarketplaceFacet is IMarketplace { return _feeType._getHostItBalance(); } + function calculateHostItFee(uint256 _fee) external view returns (uint256) { + return _fee._calculateHostItFee(); + } + //*////////////////////////////////////////////////////////////////////////// // PURE FUNCTIONS //////////////////////////////////////////////////////////////////////////*// - function calculateHostItFee(uint256 _fee) external pure returns (uint256) { - return _fee._calculateHostItFee(); - } - function getRefundPeriod() external pure returns (uint256) { return LibMarketplace.REFUND_PERIOD; } diff --git a/src/inits/HostItInit.sol b/src/inits/HostItInit.sol index b3de94c..f032754 100644 --- a/src/inits/HostItInit.sol +++ b/src/inits/HostItInit.sol @@ -2,10 +2,10 @@ pragma solidity 0.8.30; import {LibDiamond} from "@diamond/libraries/LibDiamond.sol"; +import {FeeType} from "@ticket-storage/MarketplaceStorage.sol"; import {ITicket} from "@ticket/interfaces/ITicket.sol"; import {LibFactory} from "@ticket/libs/LibFactory.sol"; import {LibMarketplace} from "@ticket/libs/LibMarketplace.sol"; -import {FeeType} from "@ticket-storage/MarketplaceStorage.sol"; event HostItInitialized(); @@ -14,6 +14,7 @@ contract HostItInit { LibDiamond._diamondStorage().supportedInterfaces[type(ITicket).interfaceId] = true; LibFactory._factoryStorage().ticketProxy = _ticketProxy; LibMarketplace._setFeeTokenAddresses(_feeTypes, _tokens); + LibMarketplace._setHostItFeeBps(300); emit HostItInitialized(); } } diff --git a/src/interfaces/ICheckIn.sol b/src/interfaces/ICheckIn.sol index beec373..b66548c 100644 --- a/src/interfaces/ICheckIn.sol +++ b/src/interfaces/ICheckIn.sol @@ -6,21 +6,21 @@ interface ICheckIn { // EXTERNAL FUNCTIONS //////////////////////////////////////////////////////////////////////////*// - function checkIn(uint56 _ticketId, address _ticketOwner, uint256 _tokenId) external; + function checkIn(uint64 _ticketId, address _ticketOwner, uint256 _tokenId) external; - function addTicketAdmins(uint56 _ticketId, address[] calldata _admins) external; + function addTicketAdmins(uint64 _ticketId, address[] calldata _admins) external; - function removeTicketAdmins(uint56 _ticketId, address[] calldata _admins) external; + function removeTicketAdmins(uint64 _ticketId, address[] calldata _admins) external; //*////////////////////////////////////////////////////////////////////////// // VIEW FUNCTIONS //////////////////////////////////////////////////////////////////////////*// - function isCheckedIn(uint56 _ticketId, address _ticketOwner) external view returns (bool); + function isCheckedIn(uint64 _ticketId, address _ticketOwner) external view returns (bool); - function isCheckedInForDay(uint56 _ticketId, uint8 _day, address _ticketOwner) external view returns (bool); + function isCheckedInForDay(uint64 _ticketId, uint8 _day, address _ticketOwner) external view returns (bool); - function getCheckedIn(uint56 _ticketId) external view returns (address[] memory); + function getCheckedIn(uint64 _ticketId) external view returns (address[] memory); - function getCheckedInForDay(uint56 _ticketId, uint8 _day) external view returns (address[] memory); + function getCheckedInForDay(uint64 _ticketId, uint8 _day) external view returns (address[] memory); } diff --git a/src/interfaces/IFactory.sol b/src/interfaces/IFactory.sol index 19cf6f6..c74b804 100644 --- a/src/interfaces/IFactory.sol +++ b/src/interfaces/IFactory.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: AGPL-3.0-only pragma solidity 0.8.30; -import {TicketData, FullTicketData} from "@ticket-storage/FactoryStorage.sol"; +import {FullTicketData, TicketData} from "@ticket-storage/FactoryStorage.sol"; import {FeeType} from "@ticket-storage/MarketplaceStorage.sol"; interface IFactory { @@ -12,21 +12,21 @@ interface IFactory { function createTicket(TicketData calldata _ticketData, FeeType[] calldata _feeTypes, uint256[] calldata _fees) external; - function updateTicket(TicketData calldata _ticketData, uint56 _ticketId) external; + function updateTicket(TicketData calldata _ticketData, uint64 _ticketId) external; //*////////////////////////////////////////////////////////////////////////// // VIEW FUNCTIONS //////////////////////////////////////////////////////////////////////////*// - function ticketCount() external view returns (uint56); + function ticketCount() external view returns (uint64); - function ticketExists(uint56 _ticketId) external view returns (bool); + function ticketExists(uint64 _ticketId) external view returns (bool); - function ticketData(uint56 _ticketId) external view returns (FullTicketData memory); + function ticketData(uint64 _ticketId) external view returns (FullTicketData memory); function allTicketData() external view returns (FullTicketData[] memory); - function adminTickets(address _ticketAdmin) external view returns (uint56[] memory); + function adminTickets(address _ticketAdmin) external view returns (uint64[] memory); function adminTicketData(address _ticketAdmin) external view returns (FullTicketData[] memory); @@ -36,9 +36,9 @@ interface IFactory { function hostItTicketHash() external pure returns (bytes32); - function ticketHash(uint56 _ticketId) external pure returns (bytes32); + function ticketHash(uint64 _ticketId) external pure returns (bytes32); - function mainAdminRole(uint56 _ticketId) external pure returns (uint256); + function mainAdminRole(uint64 _ticketId) external pure returns (uint256); - function ticketAdminRole(uint56 _ticketId) external pure returns (uint256); + function ticketAdminRole(uint64 _ticketId) external pure returns (uint256); } diff --git a/src/interfaces/IMarketplace.sol b/src/interfaces/IMarketplace.sol index f4457ee..9d60913 100644 --- a/src/interfaces/IMarketplace.sol +++ b/src/interfaces/IMarketplace.sol @@ -8,11 +8,11 @@ interface IMarketplace { // EXTERNAL FUNCTIONS //////////////////////////////////////////////////////////////////////////*// - function mintTicket(uint56 ticketId, FeeType feeType, address buyer) external payable returns (uint40); + function mintTicket(uint64 ticketId, FeeType feeType, address buyer) external payable returns (uint40); - function setTicketFees(uint56 ticketId, FeeType[] calldata feeTypes, uint256[] calldata fees) external; + function setTicketFees(uint64 ticketId, FeeType[] calldata feeTypes, uint256[] calldata fees) external; - function withdrawTicketBalance(uint56 ticketId, FeeType feeType, address to) external; + function withdrawTicketBalance(uint64 ticketId, FeeType feeType, address to) external; function withdrawHostItBalance(FeeType feeType, address to) external; @@ -20,24 +20,25 @@ interface IMarketplace { // VIEW FUNCTIONS //////////////////////////////////////////////////////////////////////////*// - function isFeeEnabled(uint56 ticketId, FeeType feeType) external view returns (bool); + function isFeeEnabled(uint64 ticketId, FeeType feeType) external view returns (bool); function getFeeTokenAddress(FeeType feeType) external view returns (address); - function getTicketFee(uint56 ticketId, FeeType feeType) external view returns (uint256); - function getAllFees(uint56 ticketId, FeeType feeType) + function getTicketFee(uint64 ticketId, FeeType feeType) external view returns (uint256); + function getAllFees(uint64 ticketId, FeeType feeType) external view returns (uint256 ticketFee, uint256 hostItFee, uint256 totalFee); - function getTicketBalance(uint56 ticketId, FeeType feeType) external view returns (uint256); + function getTicketBalance(uint64 ticketId, FeeType feeType) external view returns (uint256); function getHostItBalance(FeeType feeType) external view returns (uint256); + function calculateHostItFee(uint256 fee) external view returns (uint256); + //*////////////////////////////////////////////////////////////////////////// // PURE FUNCTIONS //////////////////////////////////////////////////////////////////////////*// - function calculateHostItFee(uint256 fee) external pure returns (uint256); function getRefundPeriod() external pure returns (uint256); } diff --git a/src/interfaces/ITicket.sol b/src/interfaces/ITicket.sol index 0ef43dd..e626ba1 100644 --- a/src/interfaces/ITicket.sol +++ b/src/interfaces/ITicket.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: AGPL-3.0-only pragma solidity 0.8.30; -import {IERC721Metadata} from "@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol"; import {IERC721Enumerable} from "@openzeppelin/contracts/token/ERC721/extensions/IERC721Enumerable.sol"; +import {IERC721Metadata} from "@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol"; interface ITicket is IERC721Metadata, IERC721Enumerable { /// @notice Emitted when the base URI is updated diff --git a/src/libs/LibCheckIn.sol b/src/libs/LibCheckIn.sol index 82299e1..fbaab62 100644 --- a/src/libs/LibCheckIn.sol +++ b/src/libs/LibCheckIn.sol @@ -3,17 +3,17 @@ pragma solidity 0.8.30; import {LibOwnableRoles} from "@diamond/libraries/LibOwnableRoles.sol"; import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; -import {CheckInStorage, CHECKIN_STORAGE_LOCATION} from "@ticket-storage/CheckInStorage.sol"; -import {LibFactory} from "@ticket/libs/LibFactory.sol"; -import {ITicket} from "@ticket/interfaces/ITicket.sol"; +import {CHECKIN_STORAGE_LOCATION, CheckInStorage} from "@ticket-storage/CheckInStorage.sol"; import {ExtraTicketData} from "@ticket-storage/FactoryStorage.sol"; +import {ITicket} from "@ticket/interfaces/ITicket.sol"; +import {LibFactory} from "@ticket/libs/LibFactory.sol"; /// forge-lint: disable-next-line(unaliased-plain-import) import "@ticket-errors/CheckInErrors.sol"; /// forge-lint: disable-next-line(unaliased-plain-import) import "@ticket-logs/CheckInLogs.sol"; library LibCheckIn { - using LibFactory for uint56; + using LibFactory for uint64; using LibOwnableRoles for *; using EnumerableSet for EnumerableSet.AddressSet; @@ -31,7 +31,7 @@ library LibCheckIn { // INTERNAL FUNCTIONS //////////////////////////////////////////////////////////////////////////*// - function _checkin(uint56 _ticketId, address _ticketOwner, uint256 _tokenId) internal onlyTicketAdmin(_ticketId) { + function _checkin(uint64 _ticketId, address _ticketOwner, uint256 _tokenId) internal onlyTicketAdmin(_ticketId) { _ticketId._checkTicketExists(); uint40 time = uint40(block.timestamp); @@ -60,7 +60,7 @@ library LibCheckIn { emit CheckedIn(_ticketId, _ticketOwner, _tokenId); } - function _addTicketAdmins(uint56 _ticketId, address[] calldata _admins) internal onlyMainTicketAdmin(_ticketId) { + function _addTicketAdmins(uint64 _ticketId, address[] calldata _admins) internal onlyMainTicketAdmin(_ticketId) { _ticketId._checkTicketExists(); uint256 adminsLength = _admins.length; @@ -73,10 +73,7 @@ library LibCheckIn { } } - function _removeTicketAdmins(uint56 _ticketId, address[] calldata _admins) - internal - onlyMainTicketAdmin(_ticketId) - { + function _removeTicketAdmins(uint64 _ticketId, address[] calldata _admins) internal onlyMainTicketAdmin(_ticketId) { _ticketId._checkTicketExists(); uint256 adminsLength = _admins.length; @@ -92,19 +89,19 @@ library LibCheckIn { //*////////////////////////////////////////////////////////////////////////// // VIEW FUNCTIONS //////////////////////////////////////////////////////////////////////////*// - function _isCheckedIn(uint56 _ticketId, address _ticketOwner) internal view returns (bool) { + function _isCheckedIn(uint64 _ticketId, address _ticketOwner) internal view returns (bool) { return _checkInStorage().checkedIn[_ticketId].contains(_ticketOwner); } - function _isCheckedInForDay(uint56 _ticketId, uint8 _day, address _ticketOwner) internal view returns (bool) { + function _isCheckedInForDay(uint64 _ticketId, uint8 _day, address _ticketOwner) internal view returns (bool) { return _checkInStorage().checkedInByDay[_ticketId][_day].contains(_ticketOwner); } - function _getCheckedIn(uint56 _ticketId) internal view returns (address[] memory) { + function _getCheckedIn(uint64 _ticketId) internal view returns (address[] memory) { return _checkInStorage().checkedIn[_ticketId].values(); } - function _getCheckedInForDay(uint56 _ticketId, uint8 _day) internal view returns (address[] memory) { + function _getCheckedInForDay(uint64 _ticketId, uint8 _day) internal view returns (address[] memory) { return _checkInStorage().checkedInByDay[_ticketId][_day].values(); } @@ -112,12 +109,12 @@ library LibCheckIn { // MODIFIERS //////////////////////////////////////////////////////////////////////////*// - modifier onlyMainTicketAdmin(uint56 _ticketId) { + modifier onlyMainTicketAdmin(uint64 _ticketId) { LibFactory._checkMainTicketAdminRole(_ticketId); _; } - modifier onlyTicketAdmin(uint56 _ticketId) { + modifier onlyTicketAdmin(uint64 _ticketId) { LibFactory._checkTicketAdminRole(_ticketId); _; } diff --git a/src/libs/LibFactory.sol b/src/libs/LibFactory.sol index a5b1b2f..b49cb24 100644 --- a/src/libs/LibFactory.sol +++ b/src/libs/LibFactory.sol @@ -1,21 +1,21 @@ // SPDX-License-Identifier: AGPL-3.0-only pragma solidity 0.8.30; +import {LibOwnableRoles} from "@diamond/libraries/LibOwnableRoles.sol"; +import {Clones} from "@openzeppelin/contracts/proxy/Clones.sol"; +import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; import {TicketCreated, TicketUpdated} from "@ticket-logs/FactoryLogs.sol"; import { - FactoryStorage, - TicketData, ExtraTicketData, + FACTORY_STORAGE_LOCATION, + FactoryStorage, FullTicketData, - FACTORY_STORAGE_LOCATION + TicketData } from "@ticket-storage/FactoryStorage.sol"; -import {LibMarketplace} from "@ticket/libs/LibMarketplace.sol"; import {FeeType, MarketplaceStorage} from "@ticket-storage/MarketplaceStorage.sol"; -import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; -import {LibOwnableRoles} from "@diamond/libraries/LibOwnableRoles.sol"; -import {Clones} from "@openzeppelin/contracts/proxy/Clones.sol"; +import {ITicket} from "@ticket/interfaces/ITicket.sol"; import {LibContext} from "@ticket/libs/LibContext.sol"; -import {Ticket} from "@ticket/libs/Ticket.sol"; +import {LibMarketplace} from "@ticket/libs/LibMarketplace.sol"; /// forge-lint: disable-next-line(unaliased-plain-import) import "@ticket-errors/FactoryErrors.sol"; @@ -50,32 +50,42 @@ library LibFactory { function _createTicket(TicketData calldata _ticketData, FeeType[] calldata _feeTypes, uint256[] calldata _fees) internal { - if (bytes(_ticketData.name).length == 0) revert EmptyName(); - if (bytes(_ticketData.uri).length == 0) revert EmptyURI(); + { + if (bytes(_ticketData.name).length == 0) revert EmptyName(); + if (bytes(_ticketData.uri).length == 0) revert EmptyURI(); - if (_ticketData.startTime < block.timestamp) revert StartTimeShouldBeAhead(); - if (_ticketData.endTime < _ticketData.startTime + 1 days) revert EndTimeShouldBeOneDayAfterStartTime(); - if (_ticketData.purchaseStartTime > _ticketData.startTime - 1 days) { - revert PurchaseStartTimeShouldBeOneDayBeforeStartTime(); + if (_ticketData.startTime < block.timestamp) { + revert StartTimeShouldBeAhead(); + } + if (_ticketData.endTime < _ticketData.startTime + 1 days) { + revert EndTimeShouldBeOneDayAfterStartTime(); + } + if (_ticketData.purchaseStartTime > _ticketData.startTime - 1 days) { + revert PurchaseStartTimeShouldBeOneDayBeforeStartTime(); + } + if (_ticketData.maxTickets == 0) revert MaxTicketsIsZero(); } - if (_ticketData.maxTickets == 0) revert MaxTicketsIsZero(); - FactoryStorage storage $ = _factoryStorage(); - uint56 ticketId = ++$.ticketId; + FactoryStorage storage fs = _factoryStorage(); + uint64 ticketId = ++fs.ticketId; address ticketAdmin = LibContext._msgSender(); _grantTicketAdminRoles(ticketAdmin, ticketId); - ExtraTicketData memory extraTicketData = _createExtraTicketData(_ticketData, ticketId, ticketAdmin); - $.ticketIdToData[ticketId] = extraTicketData; - $.adminTicketIds[ticketAdmin].add(ticketId); + ExtraTicketData memory extraTicketData = _createExtraTicketData(fs, _ticketData, ticketId, ticketAdmin); + fs.ticketIdToData[ticketId] = extraTicketData; + fs.adminTicketIds[ticketAdmin].add(ticketId); if (!_ticketData.isFree) { uint256 feeTypesLength = _feeTypes.length; - if (feeTypesLength == 0 || feeTypesLength != _fees.length) revert ArrayMismatch(); + if (feeTypesLength == 0 || feeTypesLength != _fees.length) { + revert ArrayMismatch(); + } MarketplaceStorage storage mps = LibMarketplace._marketplaceStorage(); for (uint256 i; i < feeTypesLength; ++i) { FeeType feeType = _feeTypes[i]; - if (mps.feeEnabled[ticketId][feeType]) revert FeeAlreadySet(feeType); + if (mps.feeEnabled[ticketId][feeType]) { + revert FeeAlreadySet(feeType); + } if (_fees[i] == 0) revert ZeroFee(feeType); mps.feeEnabled[ticketId][feeType] = true; @@ -86,19 +96,23 @@ library LibFactory { emit TicketCreated(ticketId, ticketAdmin, extraTicketData); } - function _updateTicket(TicketData calldata _ticketData, uint56 _ticketId) internal { + function _updateTicket(TicketData calldata _ticketData, uint64 _ticketId) internal { _checkTicketExists(_ticketId); _generateMainTicketAdminRole(_ticketId)._checkRoles(); ExtraTicketData memory extraTicketData = _getExtraTicketData(_ticketId); if (_ticketData.startTime > 0) { - if (_ticketData.startTime < uint40(block.timestamp)) revert StartTimeShouldBeAhead(); + if (_ticketData.startTime < uint40(block.timestamp)) { + revert StartTimeShouldBeAhead(); + } extraTicketData.startTime = _ticketData.startTime; } if (_ticketData.endTime > 0) { - if (_ticketData.endTime < _ticketData.startTime + 1 days) revert EndTimeShouldBeOneDayAfterStartTime(); + if (_ticketData.endTime < _ticketData.startTime + 1 days) { + revert EndTimeShouldBeOneDayAfterStartTime(); + } extraTicketData.endTime = _ticketData.endTime; } @@ -109,9 +123,11 @@ library LibFactory { extraTicketData.purchaseStartTime = _ticketData.purchaseStartTime; } - Ticket ticket = Ticket(extraTicketData.ticketAddress); + ITicket ticket = ITicket(extraTicketData.ticketAddress); if (_ticketData.maxTickets > 0) { - if (_ticketData.maxTickets < ticket.totalSupply()) revert MaxTicketsShouldEqualSupply(); + if (_ticketData.maxTickets < ticket.totalSupply()) { + revert MaxTicketsShouldEqualSupply(); + } extraTicketData.maxTickets = _ticketData.maxTickets; } @@ -142,32 +158,43 @@ library LibFactory { emit TicketUpdated(_ticketId, LibContext._msgSender(), extraTicketData); } - function _grantTicketAdminRoles(address _ticketAdmin, uint56 _ticketId) internal { + //*////////////////////////////////////////////////////////////////////////// + // INTERNAL HELPER FUNCTIONS + //////////////////////////////////////////////////////////////////////////*// + + function _grantTicketAdminRoles(address _ticketAdmin, uint64 _ticketId) private { _ticketAdmin._grantRoles(_generateMainTicketAdminRole(_ticketId)); _ticketAdmin._grantRoles(_generateTicketAdminRole(_ticketId)); } - function _createExtraTicketData(TicketData calldata _ticketData, uint56 _ticketId, address _ticketAdmin) - internal - returns (ExtraTicketData memory extraTicketData_) - { - address ticketProxy = _factoryStorage().ticketProxy; + function _createExtraTicketData( + FactoryStorage storage _fs, + TicketData calldata _ticketData, + uint64 _ticketId, + address _ticketAdmin + ) private returns (ExtraTicketData memory extraTicketData_) { + address ticketProxy = _fs.ticketProxy; if (ticketProxy.code.length == 0) revert TicketImplementationNotSet(); address ticketAddress = ticketProxy.cloneDeterministic(_generateTicketHash(_ticketId)); - Ticket(ticketAddress).initialize(address(this), _ticketData.name, _ticketData.symbol, _ticketData.uri); + try ITicket(ticketAddress).initialize(address(this), _ticketData.name, _ticketData.symbol, _ticketData.uri) {} + catch { + revert TicketInitializationFailed(); + } extraTicketData_ = ExtraTicketData({ id: _ticketId, - createdAt: uint40(block.timestamp), + createdAt: uint48(block.timestamp), updatedAt: 0, - startTime: uint40(_ticketData.startTime), - endTime: uint40(_ticketData.endTime), + startTime: _ticketData.startTime, + endTime: _ticketData.endTime, purchaseStartTime: _ticketData.purchaseStartTime, - maxTickets: uint40(_ticketData.maxTickets), + maxTickets: _ticketData.maxTickets, soldTickets: 0, + maxTicketsPerUser: _ticketData.maxTicketsPerUser, + isFree: _ticketData.isFree, + isRefundable: _ticketData.isRefundable, ticketAdmin: _ticketAdmin, - ticketAddress: ticketAddress, - isFree: _ticketData.isFree + ticketAddress: ticketAddress }); } @@ -175,23 +202,27 @@ library LibFactory { // VIEW FUNCTIONS //////////////////////////////////////////////////////////////////////////*// - function _getTicketCount() internal view returns (uint56) { + function _getTicketCount() internal view returns (uint64) { return LibFactory._factoryStorage().ticketId; } - function _ticketExists(uint56 _ticketId) internal view returns (bool) { + function _ticketExists(uint64 _ticketId) internal view returns (bool) { return _ticketId > 0 && _ticketId <= _getTicketCount(); } - function _getExtraTicketData(uint56 _ticketId) internal view returns (ExtraTicketData memory extraTicketData_) { + function _checkTicketExists(uint64 _ticketId) internal view { + if (!_ticketExists(_ticketId)) revert TicketDoesNotExist(_ticketId); + } + + function _getExtraTicketData(uint64 _ticketId) internal view returns (ExtraTicketData memory) { _checkTicketExists(_ticketId); - extraTicketData_ = _factoryStorage().ticketIdToData[_ticketId]; + return _factoryStorage().ticketIdToData[_ticketId]; } - function _getFullTicketData(uint56 _ticketId) internal view returns (FullTicketData memory fullTicketData_) { + function _getFullTicketData(uint64 _ticketId) internal view returns (FullTicketData memory) { ExtraTicketData memory extraTicketData = _getExtraTicketData(_ticketId); - Ticket ticket = Ticket(extraTicketData.ticketAddress); - fullTicketData_ = FullTicketData({ + ITicket ticket = ITicket(extraTicketData.ticketAddress); + return FullTicketData({ id: extraTicketData.id, createdAt: extraTicketData.createdAt, updatedAt: extraTicketData.updatedAt, @@ -200,7 +231,9 @@ library LibFactory { purchaseStartTime: extraTicketData.purchaseStartTime, maxTickets: extraTicketData.maxTickets, soldTickets: extraTicketData.soldTickets, + maxTicketsPerUser: extraTicketData.maxTicketsPerUser, isFree: extraTicketData.isFree, + isRefundable: extraTicketData.isRefundable, ticketAdmin: extraTicketData.ticketAdmin, ticketAddress: extraTicketData.ticketAddress, name: ticket.name(), @@ -210,15 +243,15 @@ library LibFactory { } function _getAllFullTicketData() internal view returns (FullTicketData[] memory fullTicketData_) { - uint56 ticketCount = _getTicketCount(); + uint64 ticketCount = _getTicketCount(); fullTicketData_ = new FullTicketData[](ticketCount); - for (uint56 i; i < ticketCount; ++i) { + for (uint64 i; i < ticketCount; ++i) { fullTicketData_[i] = _getFullTicketData(i + 1); } } - function _getAdminTicketIds(address _ticketAdmin) internal view returns (uint56[] memory adminTicketIds_) { + function _getAdminTicketIds(address _ticketAdmin) internal view returns (uint64[] memory adminTicketIds_) { uint256[] memory adminTicketIds = _factoryStorage().adminTicketIds[_ticketAdmin].values(); assembly { adminTicketIds_ := adminTicketIds @@ -230,27 +263,23 @@ library LibFactory { view returns (FullTicketData[] memory fullTicketData_) { - uint56[] memory adminTicketIds = _getAdminTicketIds(_ticketAdmin); - uint56 ticketCount = uint56(adminTicketIds.length); + uint64[] memory adminTicketIds = _getAdminTicketIds(_ticketAdmin); + uint64 ticketCount = uint64(adminTicketIds.length); fullTicketData_ = new FullTicketData[](ticketCount); - for (uint56 i; i < ticketCount; ++i) { + for (uint64 i; i < ticketCount; ++i) { fullTicketData_[i] = _getFullTicketData(adminTicketIds[i]); } } - function _checkTicketExists(uint56 _ticketId) internal view { - if (!_ticketExists(_ticketId)) revert TicketDoesNotExist(_ticketId); - } - - function _isTicketFree(uint56 _ticketId) internal view returns (bool) { + function _isTicketFree(uint64 _ticketId) internal view returns (bool) { return _factoryStorage().ticketIdToData[_ticketId].isFree; } - function _checkMainTicketAdminRole(uint56 _ticketId) internal view { + function _checkMainTicketAdminRole(uint64 _ticketId) internal view { _generateMainTicketAdminRole(_ticketId)._checkRoles(); } - function _checkTicketAdminRole(uint56 _ticketId) internal view { + function _checkTicketAdminRole(uint64 _ticketId) internal view { _generateTicketAdminRole(_ticketId)._checkRoles(); } @@ -262,7 +291,7 @@ library LibFactory { return HOST_IT_TICKET; } - function _generateTicketHash(uint56 _ticketId) internal pure returns (bytes32 ticketHash_) { + function _generateTicketHash(uint64 _ticketId) internal pure returns (bytes32 ticketHash_) { assembly { let ptr := mload(0x40) mstore(ptr, HOST_IT_TICKET) @@ -271,7 +300,7 @@ library LibFactory { } } - function _generateMainTicketAdminRole(uint56 _ticketId) internal pure returns (uint256 mainTicketAdminRole_) { + function _generateMainTicketAdminRole(uint64 _ticketId) internal pure returns (uint256 mainTicketAdminRole_) { assembly { let ptr := mload(0x40) mstore(ptr, HOST_IT_MAIN_TICKET_ADMIN) @@ -280,7 +309,7 @@ library LibFactory { } } - function _generateTicketAdminRole(uint56 _ticketId) internal pure returns (uint256 ticketAdminRole_) { + function _generateTicketAdminRole(uint64 _ticketId) internal pure returns (uint256 ticketAdminRole_) { assembly { let ptr := mload(0x40) mstore(ptr, HOST_IT_TICKET_ADMIN) diff --git a/src/libs/LibMarketplace.sol b/src/libs/LibMarketplace.sol index 26a3190..499ba61 100644 --- a/src/libs/LibMarketplace.sol +++ b/src/libs/LibMarketplace.sol @@ -1,24 +1,26 @@ // SPDX-License-Identifier: AGPL-3.0-only pragma solidity 0.8.30; -import {SafeTransferLib} from "solady/utils/SafeTransferLib.sol"; +import {LibOwnableRoles} from "@diamond/libraries/LibOwnableRoles.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; -import {LibOwnableRoles} from "@diamond/libraries/LibOwnableRoles.sol"; +import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol"; +import {ACCOUNT_V3_IMPLEMENTATION, ERC6551_REGISTRY} from "@ticket-script/helper/LibAddressesAndFees.sol"; +import {ExtraTicketData} from "@ticket-storage/FactoryStorage.sol"; +import {FeeType, MARKETPLACE_STORAGE_LOCATION, MarketplaceStorage} from "@ticket-storage/MarketplaceStorage.sol"; import {ITicket} from "@ticket/interfaces/ITicket.sol"; import {LibContext} from "@ticket/libs/LibContext.sol"; import {LibFactory} from "@ticket/libs/LibFactory.sol"; -import {ExtraTicketData} from "@ticket-storage/FactoryStorage.sol"; -import {FeeType, MarketplaceStorage, MARKETPLACE_STORAGE_LOCATION} from "@ticket-storage/MarketplaceStorage.sol"; import {IERC6551Registry} from "erc6551/src/interfaces/IERC6551Registry.sol"; -import {ACCOUNT_V3_IMPLEMENTATION, ERC6551_REGISTRY} from "@ticket-script/helper/AddressesAndFees.sol"; +import {SafeTransferLib} from "solady/utils/SafeTransferLib.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"; library LibMarketplace { - using LibFactory for uint56; + using LibFactory for uint64; + using SafeCast for uint256; using SafeTransferLib for address; using SafeERC20 for IERC20; @@ -26,11 +28,10 @@ library LibMarketplace { // STORAGE //////////////////////////////////////////////////////////////////////////*// - uint256 private constant MAX_TICKETS_PER_HOLDER = 0; uint256 internal constant REFUND_PERIOD = 3 days; - uint256 private constant HOSTIT_FEE_BPS = 3_000; // 3% fee in basis points - uint256 private constant FEE_BASIS_POINTS = 100_000; // 100,000 basis points + uint256 private constant HOSTIT_FEE_BPS = 300; // 3% fee in basis points + uint256 private constant FEE_BASIS_POINTS = 10_000; // 10,000 basis points function _marketplaceStorage() internal pure returns (MarketplaceStorage storage ms_) { assembly { @@ -42,30 +43,30 @@ library LibMarketplace { // INTERNAL FUNCTIONS //////////////////////////////////////////////////////////////////////////*// - function _mintTicket(uint56 _ticketId, FeeType _feeType, address _buyer) internal returns (uint40 tokenId_) { + function _mintTicket(uint64 _ticketId, FeeType _feeType, address _buyer) internal returns (uint40 tokenId_) { _ticketId._checkTicketExists(); ExtraTicketData memory ticketData = _ticketId._getExtraTicketData(); - uint40 time = uint40(block.timestamp); + 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); - if (ticket.balanceOf(_buyer) > MAX_TICKETS_PER_HOLDER) revert MaxTicketsHeld(); + if (ticket.balanceOf(_buyer) > ticketData.maxTicketsPerUser) revert MaxTicketsHeld(); - (uint256 fee, uint256 hostItFee, uint256 totalFee) = _getFees(_ticketId, _feeType); + MarketplaceStorage storage ms = _marketplaceStorage(); + (uint256 fee, uint256 hostItFee, uint256 totalFee) = _getFees(ms, _ticketId, _feeType); if (!ticketData.isFree) { - if (!_isFeeEnabled(_ticketId, _feeType)) revert FeeNotEnabled(); + 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(_feeType, totalFee); + _payWithToken(ms, _feeType, totalFee); } - MarketplaceStorage storage ms = _marketplaceStorage(); ms.ticketBalance[_ticketId][_feeType] += fee; ms.hostItBalance[_feeType] += hostItFee; } @@ -73,18 +74,26 @@ library LibMarketplace { tokenId_ = uint40(ticket.mint(_buyer)); ++LibFactory._factoryStorage().ticketIdToData[_ticketId].soldTickets; if (tokenId_ != LibFactory._factoryStorage().ticketIdToData[_ticketId].soldTickets) { - revert FatalErrorTicketMismatch(); + revert TicketAccountingMismatch(); } - // Create ERC6551 Account - IERC6551Registry(ERC6551_REGISTRY).createAccount( - ACCOUNT_V3_IMPLEMENTATION, "", block.chainid, ticketData.ticketAddress, tokenId_ - ); - emit TicketMinted(_ticketId, _feeType, totalFee, tokenId_); } - function _setTicketFees(uint56 _ticketId, FeeType[] calldata _feeTypes, uint256[] calldata _fees) + function _createErc6551Account(address _ticketAddress, uint256 _tokenId) internal { + try IERC6551Registry(ERC6551_REGISTRY) + .createAccount( + ACCOUNT_V3_IMPLEMENTATION, "", block.chainid, _ticketAddress, _tokenId + ) returns (address account) { + if (account == address(0)) { + revert CreateERC6551AccountFailed(); + } + } catch { + revert CreateERC6551AccountFailed(); + } + } + + function _setTicketFees(uint64 _ticketId, FeeType[] calldata _feeTypes, uint256[] calldata _fees) internal onlyMainTicketAdmin(_ticketId) { @@ -107,14 +116,14 @@ library LibMarketplace { } // TODO - // function _requestRefund(uint56 _ticketId, FeeType _feeType, uint256 _tokenId) internal { - // _ticketId._checkTicketExists(); - // } + function _requestRefund(uint64 _ticketId, FeeType _feeType, uint256 _tokenId) internal { + _ticketId._checkTicketExists(); + } // TODO - // function _fulfillRefund(uint56 _ticketId, FeeType _feeType) internal onlyRoleOrOwner {} + // function _fulfillRefund(uint64 _ticketId, FeeType _feeType) internal onlyRoleOrOwner {} - function _withdrawTicketBalance(uint56 _ticketId, FeeType _feeType, address _to) + function _withdrawTicketBalance(uint64 _ticketId, FeeType _feeType, address _to) internal onlyMainTicketAdmin(_ticketId) { @@ -160,6 +169,22 @@ library LibMarketplace { emit HostItBalanceWithdrawn(_feeType, balance, _to); } + function _payWithToken(MarketplaceStorage storage _ms, FeeType _feeType, uint256 _totalFee) internal { + address caller = LibContext._msgSender(); + IERC20 token = IERC20(_getFeeTokenAddress(_ms, _feeType)); + if (token.balanceOf(caller) < _totalFee) revert InsufficientBalance(address(token), _feeType, _totalFee); + if (token.allowance(caller, address(this)) < _totalFee) { + revert InsufficientAllowance(address(token), _feeType, _totalFee); + } + if (!token.trySafeTransferFrom(caller, address(this), _totalFee)) { + revert TicketPurchaseFailed(_feeType, _totalFee); + } + } + + //*////////////////////////////////////////////////////////////////////////// + // ADMIN FUNCTIONS + //////////////////////////////////////////////////////////////////////////*// + function _setFeeTokenAddresses(FeeType[] calldata _feeTypes, address[] calldata _tokenAddresses) internal { uint256 feeTypesLength = _feeTypes.length; if (feeTypesLength != _tokenAddresses.length && feeTypesLength > 0) revert InvalidFeeConfig(); @@ -170,36 +195,54 @@ library LibMarketplace { } } - function _payWithToken(FeeType _feeType, uint256 _totalFee) internal { - address buyer = LibContext._msgSender(); - IERC20 token = IERC20(_getFeeTokenAddress(_feeType)); - if (token.balanceOf(buyer) < _totalFee) revert InsufficientBalance(address(token), _feeType, _totalFee); - if (token.allowance(buyer, address(this)) < _totalFee) { - revert InsufficientAllowance(address(token), _feeType, _totalFee); - } - if (!token.trySafeTransferFrom(buyer, address(this), _totalFee)) { - revert TicketPurchaseFailed(_feeType, _totalFee); - } + function _setHostItFeeBps(uint16 _hostItFeePercentage) internal { + if (_hostItFeePercentage > HOSTIT_FEE_BPS) revert InvalidHostItFeeBps(); + _marketplaceStorage().hostItFeeBps = _hostItFeePercentage; + emit HostItFeeBpsSet(_hostItFeePercentage); } //*////////////////////////////////////////////////////////////////////////// // VIEW FUNCTIONS //////////////////////////////////////////////////////////////////////////*// - function _isFeeEnabled(uint56 _ticketId, FeeType _feeType) internal view returns (bool) { - return _marketplaceStorage().feeEnabled[_ticketId][_feeType]; + function _isFeeEnabled(uint64 _ticketId, FeeType _feeType) internal view returns (bool) { + return _isFeeEnabled(_marketplaceStorage(), _ticketId, _feeType); } - function _getFeeTokenAddress(FeeType _feeType) internal view returns (address tokenAddress_) { - tokenAddress_ = _marketplaceStorage().feeTokenAddress[_feeType]; + function _isFeeEnabled(MarketplaceStorage storage _ms, uint64 _ticketId, FeeType _feeType) + internal + view + returns (bool) + { + return _ms.feeEnabled[_ticketId][_feeType]; + } + + function _getFeeTokenAddress(FeeType _feeType) internal view returns (address) { + return _getFeeTokenAddress(_marketplaceStorage(), _feeType); + } + + function _getFeeTokenAddress(MarketplaceStorage storage _ms, FeeType _feeType) + internal + view + returns (address tokenAddress_) + { + tokenAddress_ = _ms.feeTokenAddress[_feeType]; if (tokenAddress_ == address(0)) revert TokenAddressZero(); } - function _getTicketFee(uint56 _ticketId, FeeType _feeType) internal view returns (uint256) { - return _marketplaceStorage().ticketFee[_ticketId][_feeType]; + function _getTicketFee(uint64 _ticketId, FeeType _feeType) internal view returns (uint256) { + return _getTicketFee(_marketplaceStorage(), _ticketId, _feeType); } - function _getFees(uint56 _ticketId, FeeType _feeType) + function _getTicketFee(MarketplaceStorage storage _ms, uint64 _ticketId, FeeType _feeType) + internal + view + returns (uint256) + { + return _ms.ticketFee[_ticketId][_feeType]; + } + + function _getFees(uint64 _ticketId, FeeType _feeType) internal view returns (uint256 ticketFee_, uint256 hostItFee_, uint256 totalFee_) @@ -209,31 +252,53 @@ library LibMarketplace { totalFee_ = ticketFee_ + hostItFee_; } - function _getTicketBalance(uint56 _ticketId, FeeType _feeType) internal view returns (uint256) { - return _marketplaceStorage().ticketBalance[_ticketId][_feeType]; + function _getFees(MarketplaceStorage storage _ms, uint64 _ticketId, FeeType _feeType) + internal + view + returns (uint256 ticketFee_, uint256 hostItFee_, uint256 totalFee_) + { + ticketFee_ = _getTicketFee(_ms, _ticketId, _feeType); + hostItFee_ = _calculateHostItFee(_ms, ticketFee_); + totalFee_ = ticketFee_ + hostItFee_; + } + + function _getTicketBalance(uint64 _ticketId, FeeType _feeType) internal view returns (uint256) { + return _getTicketBalance(_marketplaceStorage(), _ticketId, _feeType); + } + + function _getTicketBalance(MarketplaceStorage storage _ms, uint64 _ticketId, FeeType _feeType) + internal + view + returns (uint256) + { + return _ms.ticketBalance[_ticketId][_feeType]; } function _getHostItBalance(FeeType _feeType) internal view returns (uint256) { - return _marketplaceStorage().hostItBalance[_feeType]; + return _getHostItBalance(_marketplaceStorage(), _feeType); + } + + function _getHostItBalance(MarketplaceStorage storage _ms, FeeType _feeType) internal view returns (uint256) { + return _ms.hostItBalance[_feeType]; } function _checkIfContract(address _address) internal view { if (_address.code.length > 0) revert ContractNotAllowed(); } - //*////////////////////////////////////////////////////////////////////////// - // PURE FUNCTIONS - //////////////////////////////////////////////////////////////////////////*// + function _calculateHostItFee(uint256 _fee) internal view returns (uint256) { + return _calculateHostItFee(_marketplaceStorage(), _fee); + } - function _calculateHostItFee(uint256 _fee) internal pure returns (uint256) { - return (_fee * HOSTIT_FEE_BPS / FEE_BASIS_POINTS); + function _calculateHostItFee(MarketplaceStorage storage _ms, uint256 _fee) internal view returns (uint256) { + return ((_fee * _ms.hostItFeeBps) / FEE_BASIS_POINTS); } //*////////////////////////////////////////////////////////////////////////// // MODIFIERS //////////////////////////////////////////////////////////////////////////*// - modifier onlyMainTicketAdmin(uint56 _ticketId) { + modifier onlyMainTicketAdmin(uint64 _ticketId) { LibFactory._checkMainTicketAdminRole(_ticketId); _; } diff --git a/src/libs/Ticket.sol b/src/libs/Ticket.sol index 01008fc..d3f994c 100644 --- a/src/libs/Ticket.sol +++ b/src/libs/Ticket.sol @@ -1,17 +1,19 @@ // SPDX-License-Identifier: AGPL-3.0-only pragma solidity 0.8.30; -import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; -import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; -import {IERC721Metadata} from "@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol"; -import {ERC721Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol"; -import {ERC721EnumerableUpgradeable} from - "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721EnumerableUpgradeable.sol"; -import {ERC721RoyaltyUpgradeable} from - "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721RoyaltyUpgradeable.sol"; -import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol"; import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; +import {ERC721Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol"; +import { + ERC721EnumerableUpgradeable +} from "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721EnumerableUpgradeable.sol"; +import { + ERC721RoyaltyUpgradeable +} from "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721RoyaltyUpgradeable.sol"; +import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol"; +import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; +import {IERC721Metadata} from "@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol"; +import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; import {ITicket} from "@ticket/interfaces/ITicket.sol"; /* @@ -181,14 +183,16 @@ contract Ticket is super.safeTransferFrom(from, to, tokenId, data); } - function paused() public view override(ITicket, PausableUpgradeable) returns (bool) { - return PausableUpgradeable.paused(); - } - //*////////////////////////////////////////////////////////////////////////// // VIEW FUNCTIONS //////////////////////////////////////////////////////////////////////////*// + /// @notice Returns whether the contract is paused + /// @return Whether the contract is paused + function paused() public view override(ITicket, PausableUpgradeable) returns (bool) { + return PausableUpgradeable.paused(); + } + /// @notice Returns the metadata URI for the TicketNFT /// @dev This function returns the base URI set for the NFT collection, which is used /// @param _tokenId The ID of the token @@ -208,7 +212,7 @@ contract Ticket is /// @dev This function returns the base URI set for the NFT collection, which is used /// @return The URI pointing to the collection's metadata /// forge-lint: disable-next-line(mixed-case-function) - function baseURI() external view returns (string memory) { + function baseURI() public view returns (string memory) { return _baseURI(); } diff --git a/src/libs/TicketProxy.sol b/src/libs/TicketProxy.sol index 62bb165..5615ffd 100644 --- a/src/libs/TicketProxy.sol +++ b/src/libs/TicketProxy.sol @@ -4,28 +4,33 @@ pragma solidity 0.8.30; import {BeaconProxy} from "@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol"; /* -⣾⣿⣿⣿⣿⣿⣿⣿⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣰⣿⣿⣿⣿⣿⣿⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ -⣿⣿⣿⡿⠿⢿⣿⣿⣗⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣽⣿⣿⠿⠿⠿⢿⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ -⣿⣿⣿⡇⠀⠈⢿⣿⣿⣷⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣰⣿⣿⣿⠏⠀⠀⠀⢸⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ -⣿⣿⣿⡇⠀⠀⠈⠻⣿⣿⣿⣷⣤⣀⡀⠀⠀⠀⠀⢀⣠⣤⣿⣿⣿⡿⠋⠀⠀⠀⠀⢸⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⣀⣀⣀⣀⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣀⣀⣀⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣀⣀⣀⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ -⣿⣿⣿⡇⠀⠀⠀⠀⠈⠻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠿⠋⠀⠀⠀⠀⠀⠀⢸⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⡅⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣽⣿⣿⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ -⣿⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠉⠛⠻⠿⠿⠿⠿⠿⠟⠛⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⡂⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢾⣿⣿⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠀⠀⠀⠀⠀⠀⠀ -⣿⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⡏⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⠅⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣻⣿⣿⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣟⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⢀⣾⣿⡇⠀⠀⠀⠀⠀⠀ -⣿⣿⣿⡇⠀⠀⠀⢨⠄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡜⠀⠀⠀⠀⢸⣿⣿⡗⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⡃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣽⣿⣿⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣯⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀⢀⣾⣿⣿⡇⠀⠀⠀⠀⠀⠀ -⣿⣿⣿⡇⠀⠀⠀⢨⣛⡤⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣤⣛⠦⠀⠀⠀⠀⢸⣿⣿⡧⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⠆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢾⣿⣿⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣀⣤⣤⣄⣀⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⣠⣤⣤⣀⣀⡀⠀⠀⠀⠀⠀⠀⠀⢠⣾⣿⣿⣿⣷⣀⣀⣀⣀⡀⠀⠀⠀⠀⣿⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀⣠⣿⣿⣿⣿⣇⣀⣀⣀⣀⠀⠀ -⣿⣿⣿⡇⠀⠀⠀⢘⠶⣙⠯⣖⢤⣀⡀⠀⠀⠀⠀⣀⣀⢤⡲⢏⡶⣩⠗⠀⠀⠀⠀⢸⣿⣿⡗⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⣦⣤⣤⣤⣤⣤⣤⣤⣤⣤⣤⣤⣤⣤⣿⣿⣿⣿⣿⡇⠀⠀⠀⠀⠀⠀⢀⣤⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣶⣄⡀⠀⠀⠀⠀⠀⢀⣰⣾⣿⣿⣿⣿⣿⣿⣿⣿⣷⣦⡄⠀⠀⣠⣶⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡅⠀⠀⠀⠀⣿⣿⣿⣿⣿⠀⠀⠀⠀⣠⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠀⠀ -⣿⣿⣿⡇⠀⠀⠀⢸⡹⢭⡳⢎⡗⣮⢹⡭⣛⡼⣹⠖⣭⢞⡱⣏⢶⡹⣚⠀⠀⠀⠀⢸⣿⣿⣏⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇⠀⠀⠀⠀⠀⣴⣿⣿⣿⣿⣿⣿⡿⠿⣿⣿⣿⣿⣿⣿⣷⣂⠀⠀⠀⠀⣼⣿⣿⣿⣿⡿⠿⠿⠿⣿⣿⣿⡟⠁⠀⢸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠆⠀⠀⠀⠀⣿⣿⣿⣿⣿⠀⠀⠀⢘⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠀⠀ -⣿⣿⣿⡇⠀⠀⠀⢰⢫⡳⢭⣛⡼⢎⣧⠳⣝⡼⣱⠻⣜⢮⢳⠭⣞⡱⢧⠀⠀⠀⠀⢸⣿⣿⡧⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇⠀⠀⠀⠀⣼⣿⣿⣿⣿⠟⠉⠀⠀⠀⠀⠀⠙⢿⣿⣿⣿⣿⡄⠀⠀⢈⣿⣿⣿⣿⡇⠀⠀⠀⠀⠀⠉⠉⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀⣻⣿⣿⣿⣿⡇⠀⠀⠀⠀⠀⠀ -⣿⣿⣿⡇⠀⠀⠀⢨⡳⢭⣓⠧⠚⠉⠀⠉⠈⠈⠁⠉⠘⠃⠯⣛⡴⣫⢳⠀⠀⠀⠀⢸⣿⣿⡷⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢾⣿⣿⣿⣿⡇⠀⠀⠀⢰⣿⣿⣿⣿⡏⠀⠀⠀⠀⠀⠀⠀⠀⠀⢿⣿⣿⣿⣯⠀⠀⠀⠹⣿⣿⣿⣿⣷⣶⣤⣄⣀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀⣽⣿⣿⣿⣿⡇⠀⠀⠀⠀⠀⠀ -⣿⣿⣿⡇⠀⠀⠀⢰⢫⠇⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠑⢧⢏⠀⠀⠀⠀⢸⣿⣿⣟⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣻⣿⣿⣿⣿⡇⠀⠀⠀⢸⣿⣿⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣿⠁⠀⠀⠀⠉⠻⢿⣿⣿⣿⣿⣿⣿⣿⣶⣄⠀⠀⠀⠀⠀⢸⣿⣿⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀⢾⣿⣿⣿⣿⡇⠀⠀⠀⠀⠀⠀ -⣿⣿⣿⡇⠀⠀⠀⢈⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠻⠀⠀⠀⠀⢸⣿⣿⣯⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣽⣿⣿⣿⣿⡇⠀⠀⠀⠸⣿⣿⣿⣿⣧⠀⠀⠀⠀⠀⠀⠀⠀⢀⣾⣿⣿⣿⣏⠁⠀⠀⠀⠀⠀⠀⠈⠉⠛⠻⠿⣿⣿⣿⣿⣷⠀⠀⠀⠀⢸⣿⣿⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀⣻⣿⣿⣿⣿⡇⠀⠀⠀⠀⠀⠀ -⣿⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣷⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢾⣿⣿⣿⣿⡇⠀⠀⠀⠀⢻⣿⣿⣿⣿⣷⣄⡀⠀⠀⠀⢀⣠⣾⣿⣿⣿⣿⠁⠀⠀⠀⢀⣤⣀⡀⠀⠀⠀⠀⠀⣹⣿⣿⣿⣿⡇⠀⠀⠀⢸⣿⣿⣿⣿⣿⡀⠀⠀⣀⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀⣽⣿⣿⣿⣿⣧⡀⠀⢀⣀⠀⠀ -⣿⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⢀⣠⣤⣤⣤⣤⣤⣤⣄⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣻⣿⣿⣿⣿⡇⠀⠀⠀⠀⠀⠻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡟⠁⠀⠀⠀⢠⣾⣿⣿⣿⣿⣶⣶⣶⣾⣿⣿⣿⣿⡿⠀⠀⠀⠀⠈⣿⣿⣿⣿⣿⣿⣿⣿⣿⡧⠀⠀⠀⠀⣿⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀⠰⢿⣿⣿⣿⣿⣿⣿⣿⣿⣆⠀ -⣿⣿⣿⡇⠀⠀⠀⠀⠀⣠⣴⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣦⣄⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣽⣿⣿⣿⣿⡇⠀⠀⠀⠀⠀⠀⠈⠛⠿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠟⠁⠀⠀⠀⠀⠀⠉⠛⠿⢿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠛⠀⠀⠀⠀⠀⠀⠈⠻⣿⣿⣿⣿⣿⣿⣿⠿⠃⠀⠀⠀⣿⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀⠘⠿⣿⣿⣿⣿⣿⣿⣿⠿⠂ -⣿⣿⣿⡇⠀⠀⠀⣠⣾⣿⣿⣿⠿⠛⠉⠉⠉⠉⠉⠙⠛⠿⣿⣿⣿⣷⡄⠀⠀⠀⠀⢸⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠈⠉⠉⠉⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠉⠉⠉⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠉⠉⠉⠉⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠉⠉⠉⠉⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠉⠉⠉⠉⠀⠀⠀⠀⠀⠀⠉⠉⠉⠉⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠉⠉⠉⠉⠀⠀⠀ -⣿⣿⣿⡇⠀⠀⣴⣿⣿⣿⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢻⣿⣿⣿⣆⠀⠀⠀⢸⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ -⣿⣿⣿⣷⣶⣾⣿⣿⡟⠂⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⣿⣿⣿⣶⣶⣶⣾⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ -⣿⣿⣿⣿⣿⣿⣿⣿⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣿⣿⣿⣿⣿⡿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⣾⣿⣿⣿⣿⣿⣿⣿⣿⣆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡆ +⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇ +⣿⣿⣿⣿⠉⠉⠹⣿⣿⣿⣿⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣼⣿⣿⣿⡟⠉⠉⠉⢹⣿⣿⣿⡇ +⣿⣿⣿⣿⠀⠀⠀⠹⣿⣿⣿⣿⣧⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣴⣿⣿⣿⣿⠟⠀⠀⠀⠀⢸⣿⣿⣿⡇ +⣿⣿⣿⣿⠀⠀⠀⠀⠈⢿⣿⣿⣿⣿⣷⣦⣄⣀⣀⣀⣀⣀⣀⣀⣤⣶⣿⣿⣿⣿⡿⠋⠀⠀⠀⠀⠀⢸⣿⣿⣿⡇ +⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀⠙⠻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠟⠋⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⡇ +⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠛⠿⠿⣿⣿⣿⣿⣿⣿⠿⠿⠛⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⡇ +⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⡇ +⣿⣿⣿⣿⠀⠀⠀⠀⠀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⠀⠀⠀⠀⠀⢸⣿⣿⣿⡇ +⣿⣿⣿⣿⠀⠀⠀⠀⠀⣿⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣿⠀⠀⠀⠀⠀⢸⣿⣿⣿⡇ +⣿⣿⣿⣿⠀⠀⠀⠀⠀⣿⣿⣶⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣾⣿⣿⠀⠀⠀⠀⠀⢸⣿⣿⣿⡇ +⣿⣿⣿⣿⠀⠀⠀⠀⠀⣿⣿⣿⣿⣷⣦⣄⣀⡀⠀⠀⠀⠀⠀⢀⣀⣤⣶⣿⣿⣿⣿⣿⠀⠀⠀⠀⠀⢸⣿⣿⣿⡇ +⣿⣿⣿⣿⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠀⠀⠀⠀⠀⢸⣿⣿⣿⡇ +⣿⣿⣿⣿⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠀⠀⠀⠀⠀⢸⣿⣿⣿⡇ +⣿⣿⣿⣿⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⣿⣿⣿⠿⠿⠛⠛⠛⠿⠿⣿⣿⣿⣿⣿⣿⣿⣿⠀⠀⠀⠀⠀⢸⣿⣿⣿⡇ +⣿⣿⣿⣿⠀⠀⠀⠀⠀⣿⣿⣿⡿⠟⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠉⠻⢿⣿⣿⣿⠀⠀⠀⠀⠀⢸⣿⣿⣿⡇ +⣿⣿⣿⣿⠀⠀⠀⠀⠀⣿⡿⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢿⣿⠀⠀⠀⠀⠀⢸⣿⣿⣿⡇ +⣿⣿⣿⣿⠀⠀⠀⠀⠀⡟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠻⠀⠀⠀⠀⠀⢸⣿⣿⣿⡇ +⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⡇ +⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⣀⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⡇ +⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣤⣶⣿⣿⣿⣿⣿⣿⣿⣿⣿⣶⣤⣄⡀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⡇ +⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀⢠⣴⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣦⡀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⡇ +⣿⣿⣿⣿⠀⠀⠀⠀⢀⣼⣿⣿⣿⣿⡿⠟⠉⠉⠀⠀⠀⠀⠈⠉⠙⠻⣿⣿⣿⣿⣿⣦⠀⠀⠀⠀⠀⢸⣿⣿⣿⡇ +⣿⣿⣿⣿⠀⠀⠀⣰⣿⣿⣿⣿⠟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢿⣿⣿⣿⣷⡀⠀⠀⠀⢸⣿⣿⣿⡇ +⣿⣿⣿⣿⣄⣀⣰⣿⣿⣿⡿⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢻⣿⣿⣿⣷⣤⣤⣤⣸⣿⣿⣿⡇ +⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇ +⠻⠿⠿⠿⠿⠿⠿⠿⠿⠏⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠻⠿⠿⠿⠿⠿⠿⠿⠿⠿⠃ */ /// @title TicketProxy diff --git a/src/libs/errors/FactoryErrors.sol b/src/libs/errors/FactoryErrors.sol index 5678805..255981b 100644 --- a/src/libs/errors/FactoryErrors.sol +++ b/src/libs/errors/FactoryErrors.sol @@ -12,10 +12,11 @@ error MaxTicketsIsZero(); error ArrayMismatch(); error FeeAlreadySet(FeeType); error ZeroFee(FeeType); -error TicketDoesNotExist(uint56); +error TicketDoesNotExist(uint64); error TicketUseHasCommenced(); error MaxTicketsShouldEqualSupply(); error TicketImplementationNotSet(); error UpdateNameFailed(); error UpdateSymbolFailed(); error UpdateURIFailed(); +error TicketInitializationFailed(); diff --git a/src/libs/errors/MarketplaceErrors.sol b/src/libs/errors/MarketplaceErrors.sol index 0bd870d..3cfa869 100644 --- a/src/libs/errors/MarketplaceErrors.sol +++ b/src/libs/errors/MarketplaceErrors.sol @@ -17,5 +17,7 @@ error InsufficientAllowance(address, FeeType, uint256); error WithdrawPeriodNotReached(); error InsufficientWithdrawBalance(); error TicketPurchaseFailed(FeeType, uint256); -error FatalErrorTicketMismatch(); +error TicketAccountingMismatch(); error TicketUnpauseFailed(); +error CreateERC6551AccountFailed(); +error InvalidHostItFeeBps(); diff --git a/src/libs/logs/CheckInLogs.sol b/src/libs/logs/CheckInLogs.sol index 4009f34..4526b09 100644 --- a/src/libs/logs/CheckInLogs.sol +++ b/src/libs/logs/CheckInLogs.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: AGPL-3.0-only pragma solidity 0.8.30; -event CheckedIn(uint56 indexed ticketId, address indexed ticketOwner, uint256 tokenId); +event CheckedIn(uint64 indexed ticketId, address indexed ticketOwner, uint256 tokenId); -event TicketAdminAdded(uint56 indexed ticketId, address indexed admin); +event TicketAdminAdded(uint64 indexed ticketId, address indexed admin); -event TicketAdminRemoved(uint56 indexed ticketId, address indexed admin); +event TicketAdminRemoved(uint64 indexed ticketId, address indexed admin); diff --git a/src/libs/logs/FactoryLogs.sol b/src/libs/logs/FactoryLogs.sol index 6fd0cc3..e73d9e1 100644 --- a/src/libs/logs/FactoryLogs.sol +++ b/src/libs/logs/FactoryLogs.sol @@ -3,6 +3,6 @@ pragma solidity 0.8.30; import {ExtraTicketData} from "@ticket-storage/FactoryStorage.sol"; -event TicketCreated(uint56 indexed ticketId, address indexed ticketAdmin, ExtraTicketData ticketData); +event TicketCreated(uint64 indexed ticketId, address indexed ticketAdmin, ExtraTicketData ticketData); -event TicketUpdated(uint56 indexed ticketId, address indexed ticketAdmin, ExtraTicketData ticketData); +event TicketUpdated(uint64 indexed ticketId, address indexed ticketAdmin, ExtraTicketData ticketData); diff --git a/src/libs/logs/MarketplaceLogs.sol b/src/libs/logs/MarketplaceLogs.sol index cc1ec38..ffa5488 100644 --- a/src/libs/logs/MarketplaceLogs.sol +++ b/src/libs/logs/MarketplaceLogs.sol @@ -3,13 +3,15 @@ pragma solidity 0.8.30; import {FeeType} from "@ticket-storage/MarketplaceStorage.sol"; -event TicketFeeSet(uint56 indexed ticketId, FeeType indexed feeType, uint256 fee); +event TicketFeeSet(uint64 indexed ticketId, FeeType indexed feeType, uint256 fee); + +event HostItFeeBpsSet(uint16 indexed hostItFeeBps); event TicketFeeAddressSet(FeeType indexed feeType, address indexed token); -event TicketMinted(uint56 indexed ticketId, FeeType indexed feeType, uint256 fee, uint40 tokenId); +event TicketMinted(uint64 indexed ticketId, FeeType indexed feeType, uint256 fee, uint40 tokenId); -event TicketBalanceWithdrawn(uint56 indexed ticketId, FeeType indexed feeType, uint256 fee, address indexed to); +event TicketBalanceWithdrawn(uint64 indexed ticketId, FeeType indexed feeType, uint256 fee, address indexed to); event HostItBalanceWithdrawn(FeeType indexed feeType, uint256 fee, address indexed to); diff --git a/src/libs/storage/CheckInStorage.sol b/src/libs/storage/CheckInStorage.sol index ffb3435..f42e1d3 100644 --- a/src/libs/storage/CheckInStorage.sol +++ b/src/libs/storage/CheckInStorage.sol @@ -7,6 +7,6 @@ import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet bytes32 constant CHECKIN_STORAGE_LOCATION = 0xe193d680ae43ded63724eb4ee4d68fd7efbded9778d44414c0bab0177a079700; struct CheckInStorage { - mapping(uint56 => EnumerableSet.AddressSet) checkedIn; - mapping(uint56 => mapping(uint8 => EnumerableSet.AddressSet)) checkedInByDay; + mapping(uint64 => EnumerableSet.AddressSet) checkedIn; + mapping(uint64 => mapping(uint8 => EnumerableSet.AddressSet)) checkedInByDay; } diff --git a/src/libs/storage/FactoryStorage.sol b/src/libs/storage/FactoryStorage.sol index db8d042..7d7d139 100644 --- a/src/libs/storage/FactoryStorage.sol +++ b/src/libs/storage/FactoryStorage.sol @@ -8,46 +8,52 @@ bytes32 constant FACTORY_STORAGE_LOCATION = 0x610b7ed6689c503e651500bb8179583591 struct FactoryStorage { address ticketProxy; - uint56 ticketId; - mapping(uint56 => ExtraTicketData) ticketIdToData; + uint64 ticketId; + mapping(uint64 => ExtraTicketData) ticketIdToData; mapping(address => EnumerableSet.UintSet) adminTicketIds; } struct TicketData { - uint40 startTime; - uint40 endTime; - uint40 purchaseStartTime; + uint48 startTime; + uint48 endTime; + uint48 purchaseStartTime; uint40 maxTickets; + uint8 maxTicketsPerUser; bool isFree; + bool isRefundable; string name; string symbol; string uri; } struct ExtraTicketData { - uint56 id; - uint40 createdAt; - uint40 updatedAt; - uint40 startTime; - uint40 endTime; - uint40 purchaseStartTime; + uint64 id; + uint48 createdAt; + uint48 updatedAt; + uint48 startTime; + uint48 endTime; + uint48 purchaseStartTime; uint40 maxTickets; uint40 soldTickets; + uint8 maxTicketsPerUser; bool isFree; + bool isRefundable; address ticketAdmin; address ticketAddress; } struct FullTicketData { - uint56 id; - uint40 createdAt; - uint40 updatedAt; - uint40 startTime; - uint40 endTime; - uint40 purchaseStartTime; + uint64 id; + uint48 createdAt; + uint48 updatedAt; + uint48 startTime; + uint48 endTime; + uint48 purchaseStartTime; uint40 maxTickets; uint40 soldTickets; + uint8 maxTicketsPerUser; bool isFree; + bool isRefundable; address ticketAdmin; address ticketAddress; string name; diff --git a/src/libs/storage/MarketplaceStorage.sol b/src/libs/storage/MarketplaceStorage.sol index 7b321e2..cf93a83 100644 --- a/src/libs/storage/MarketplaceStorage.sol +++ b/src/libs/storage/MarketplaceStorage.sol @@ -5,11 +5,12 @@ pragma solidity 0.8.30; bytes32 constant MARKETPLACE_STORAGE_LOCATION = 0x3f09c55b469305b27ecae2a46b3f364669f622316549d801837d9eeba9778d00; struct MarketplaceStorage { - mapping(uint56 => mapping(FeeType => bool)) feeEnabled; - mapping(uint56 => mapping(FeeType => uint256)) ticketFee; + mapping(uint64 => mapping(FeeType => bool)) feeEnabled; + mapping(uint64 => mapping(FeeType => uint256)) ticketFee; mapping(FeeType => address) feeTokenAddress; - mapping(uint56 => mapping(FeeType => uint256)) ticketBalance; + mapping(uint64 => mapping(FeeType => uint256)) ticketBalance; mapping(FeeType => uint256) hostItBalance; + uint16 hostItFeeBps; } enum FeeType { @@ -18,10 +19,9 @@ enum FeeType { WETH, USDT, USDC, - EURC, USDT0, + EURC, GHO, LINK, - LSK, - DISCOUNT + LSK } diff --git a/test/CheckIn.t.sol b/test/CheckIn.t.sol index be7a9e2..bbc4b30 100644 --- a/test/CheckIn.t.sol +++ b/test/CheckIn.t.sol @@ -7,7 +7,7 @@ import "@ticket-logs/CheckInLogs.sol"; contract CheckInTest is DeployedHostItTickets { function test_checkIn() public { - (uint56 ticketId, uint40 tokenId) = _mintTicketFree(); + (uint64 ticketId, uint40 tokenId) = _mintTicketFree(); vm.warp(1 days + 1); vm.expectEmit(true, true, true, true, hostIt); emit CheckedIn(ticketId, alice, tokenId); @@ -23,7 +23,7 @@ contract CheckInTest is DeployedHostItTickets { } function test_addTicketAdmins() public { - (uint56 ticketId,) = _mintTicketFree(); + (uint64 ticketId,) = _mintTicketFree(); address[] memory admins = new address[](2); admins[0] = bob; admins[1] = charlie; @@ -41,7 +41,7 @@ contract CheckInTest is DeployedHostItTickets { } function test_removeTicketAdmins() public { - (uint56 ticketId,) = _mintTicketFree(); + (uint64 ticketId,) = _mintTicketFree(); address[] memory admins = new address[](1); admins[0] = bob; checkInFacet.addTicketAdmins(ticketId, admins); diff --git a/test/Factory.t.sol b/test/Factory.t.sol index 8b717e6..8d09e0b 100644 --- a/test/Factory.t.sol +++ b/test/Factory.t.sol @@ -1,9 +1,9 @@ // SPDX-License-Identifier: AGPL-3.0-only pragma solidity 0.8.30; -import {TicketData, ExtraTicketData, FullTicketData} from "@ticket-storage/FactoryStorage.sol"; -import {DeployedHostItTickets} from "@ticket-test/states/DeployedHostItTickets.sol"; import {TicketCreated, TicketUpdated} from "@ticket-logs/FactoryLogs.sol"; +import {ExtraTicketData, FullTicketData, TicketData} from "@ticket-storage/FactoryStorage.sol"; +import {DeployedHostItTickets} from "@ticket-test/states/DeployedHostItTickets.sol"; contract FactoryTest is DeployedHostItTickets { function test_createFreeTicket() public { @@ -11,7 +11,7 @@ contract FactoryTest is DeployedHostItTickets { vm.expectEmit(true, true, true, false, hostIt); emit TicketCreated(1, owner, extraTicketData); _createFreeTicket(); - uint56 ticketId = factoryFacet.ticketCount(); + uint64 ticketId = factoryFacet.ticketCount(); TicketData memory ticketData = _getFreeTicketData(); FullTicketData memory fullTicketData = factoryFacet.ticketData(ticketId); assertTrue(factoryFacet.ticketExists(ticketId)); @@ -34,7 +34,7 @@ contract FactoryTest is DeployedHostItTickets { function test_updateFreeTicket() public { _createFreeTicket(); - uint56 ticketId = factoryFacet.ticketCount(); + uint64 ticketId = factoryFacet.ticketCount(); TicketData memory ticketData = _getFreeUpdatedTicketData(); vm.warp(10000); ExtraTicketData memory extraTicketData; @@ -58,7 +58,7 @@ contract FactoryTest is DeployedHostItTickets { vm.expectEmit(true, true, true, false, hostIt); emit TicketCreated(1, owner, extraTicketData); _createPaidTicket(); - uint56 ticketId = factoryFacet.ticketCount(); + uint64 ticketId = factoryFacet.ticketCount(); TicketData memory ticketData = _getPaidTicketData(); FullTicketData memory fullTicketData = factoryFacet.ticketData(ticketId); assertEq(fullTicketData.name, ticketData.name); @@ -74,7 +74,7 @@ contract FactoryTest is DeployedHostItTickets { function test_updatePaidTicket() public { _createPaidTicket(); - uint56 ticketId = factoryFacet.ticketCount(); + uint64 ticketId = factoryFacet.ticketCount(); TicketData memory ticketData = _getPaidUpdatedTicketData(); vm.warp(10000); ExtraTicketData memory extraTicketData; @@ -117,6 +117,20 @@ contract FactoryTest is DeployedHostItTickets { assertEq(fullTicketDatas[2].id, 3); } + function test_adminTickets() public { + _createFreeTicket(); + _createFreeTicket(); + vm.prank(alice); + _createPaidTicket(); + uint64[] memory ownerTickets = factoryFacet.adminTickets(owner); + uint64[] memory aliceTickets = factoryFacet.adminTickets(alice); + assertEq(ownerTickets.length, 2); + assertEq(aliceTickets.length, 1); + assertEq(ownerTickets[0], 1); + assertEq(ownerTickets[1], 2); + assertEq(aliceTickets[0], 3); + } + function test_adminTicketData() public { _createFreeTicket(); _createFreeTicket(); @@ -131,40 +145,50 @@ contract FactoryTest is DeployedHostItTickets { assertEq(aliceTicketDatas[0].id, 3); } - function test_adminTickets() public { - _createFreeTicket(); - _createFreeTicket(); - vm.prank(alice); - _createPaidTicket(); - uint56[] memory ownerTickets = factoryFacet.adminTickets(owner); - uint56[] memory aliceTickets = factoryFacet.adminTickets(alice); - assertEq(ownerTickets.length, 2); - assertEq(aliceTickets.length, 1); - assertEq(ownerTickets[0], 1); - assertEq(ownerTickets[1], 2); - assertEq(aliceTickets[0], 3); - } - function test_hostItTicketHash() public view { bytes32 hostItTicketHash = factoryFacet.hostItTicketHash(); assertEq(hostItTicketHash, keccak256("host.it.ticket")); } function test_ticketHash() public view { - uint56 ticketId = factoryFacet.ticketCount(); + uint64 ticketId = factoryFacet.ticketCount(); bytes32 ticketHash = factoryFacet.ticketHash(ticketId); assertEq(ticketHash, keccak256(abi.encode(keccak256("host.it.ticket"), ticketId))); } function test_mainAdminRole() public view { - uint56 ticketId = factoryFacet.ticketCount(); + uint64 ticketId = factoryFacet.ticketCount(); uint256 mainAdminRole = factoryFacet.mainAdminRole(ticketId); assertEq(mainAdminRole, uint256(keccak256(abi.encode(keccak256("host.it.ticket.main.admin"), ticketId)))); } function test_ticketAdminRole() public view { - uint56 ticketId = factoryFacet.ticketCount(); + uint64 ticketId = factoryFacet.ticketCount(); uint256 ticketAdminRole = factoryFacet.ticketAdminRole(ticketId); assertEq(ticketAdminRole, uint256(keccak256(abi.encode(keccak256("host.it.ticket.admin"), ticketId)))); } + + //*////////////////////////////////////////////////////////////////////////// + // FUZZ TESTS + //////////////////////////////////////////////////////////////////////////*// + + // /// forge-config: default.fuzz.runs = 20 + // /// forge-config: default.fuzz.max-test-rejects = 100_000_000 + // function test_fuzz_createTicket(TicketData memory _ticketData) public { + // vm.assume(_ticketData.endTime > bound(_ticketData.startTime, 0, type(uint48).max - 2 days)); + // bound(_ticketData.purchaseStartTime, 0, _currentTime); + // bound(_ticketData.maxTickets, 0, type(uint8).max); + // factoryFacet.createTicket(_ticketData, _getFeeTypes(), _getFees()); + // } + + // /// forge-config: default.fuzz.runs = 256 + // /// forge-config: default.fuzz.max-test-rejects = 4_000_000 + // function test_fuzz_updateTicket(TicketData memory _ticketData) public { + // bound(_ticketData.startTime, 0, type(uint48).max - 2 days); + // vm.assume(_ticketData.endTime == type(uint48).max - 1 days); + // bound(_ticketData.purchaseStartTime, 0, _currentTime); + // bound(_ticketData.maxTickets, 0, type(uint8).max); + // _createPaidTicket(); + // factoryFacet.updateTicket(_ticketData, 1); + // } } diff --git a/test/Marketplace.t.sol b/test/Marketplace.t.sol index 69b0a89..eff3dbc 100644 --- a/test/Marketplace.t.sol +++ b/test/Marketplace.t.sol @@ -2,17 +2,17 @@ pragma solidity 0.8.30; import {ERC20Mock} from "@openzeppelin/contracts/mocks/token/ERC20Mock.sol"; -import {ITicket} from "@ticket/interfaces/ITicket.sol"; -import {FeeType} from "@ticket-storage/MarketplaceStorage.sol"; import {FullTicketData} from "@ticket-storage/FactoryStorage.sol"; +import {FeeType} from "@ticket-storage/MarketplaceStorage.sol"; import {DeployedHostItTickets} from "@ticket-test/states/DeployedHostItTickets.sol"; +import {ITicket} from "@ticket/interfaces/ITicket.sol"; /// forge-lint: disable-next-line(unaliased-plain-import) import "@ticket-logs/MarketplaceLogs.sol"; contract MarketplaceTest is DeployedHostItTickets { function test_mintFreeTicket() public { vm.prank(alice); - (uint56 ticketId, uint40 tokenId) = _mintTicketFree(); + (uint64 ticketId, uint40 tokenId) = _mintTicketFree(); FullTicketData memory fullTicketData = factoryFacet.ticketData(ticketId); ITicket ticket = ITicket(fullTicketData.ticketAddress); assertEq(ticket.ownerOf(tokenId), alice); @@ -20,7 +20,7 @@ contract MarketplaceTest is DeployedHostItTickets { } function test_mintPaidTicketETH() public { - (uint56 ticketId, uint40 tokenId, uint256 fee, uint256 hostItFee) = _mintTicketETH(); + (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); @@ -30,7 +30,7 @@ contract MarketplaceTest is DeployedHostItTickets { } function test_mintPaidTicketUSDT() public { - (uint56 ticketId, uint40 tokenId, uint256 fee, uint256 hostItFee,) = _mintTicketUSDT(); + (uint64 ticketId, uint40 tokenId, uint256 fee, uint256 hostItFee,) = _mintTicketUSDT(); FullTicketData memory fullTicketData = factoryFacet.ticketData(ticketId); ITicket ticket = ITicket(fullTicketData.ticketAddress); assertEq(ticket.ownerOf(tokenId), alice); @@ -40,7 +40,7 @@ contract MarketplaceTest is DeployedHostItTickets { } function test_mintPaidTicketUSDC() public { - (uint56 ticketId, uint40 tokenId, uint256 fee, uint256 hostItFee,) = _mintTicketUSDC(); + (uint64 ticketId, uint40 tokenId, uint256 fee, uint256 hostItFee,) = _mintTicketUSDC(); FullTicketData memory fullTicketData = factoryFacet.ticketData(ticketId); ITicket ticket = ITicket(fullTicketData.ticketAddress); assertEq(ticket.ownerOf(tokenId), alice); @@ -51,7 +51,7 @@ contract MarketplaceTest is DeployedHostItTickets { function test_setTicketFees() public { _createFreeTicket(); - uint56 ticketId = factoryFacet.ticketCount(); + 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]); @@ -59,7 +59,7 @@ contract MarketplaceTest is DeployedHostItTickets { } function test_withdrawTicketBalanceETH() public { - (uint56 ticketId,, uint256 ethFee,) = _mintTicketETH(); + (uint64 ticketId,, uint256 ethFee,) = _mintTicketETH(); // Check platform balances before withdraw // Withdraw FullTicketData memory fullTicketData = factoryFacet.ticketData(ticketId); @@ -74,7 +74,7 @@ contract MarketplaceTest is DeployedHostItTickets { } function test_withdrawTicketBalanceUSDT() public { - (uint56 ticketId,, uint256 usdtFee,, ERC20Mock usdt) = _mintTicketUSDT(); + (uint64 ticketId,, uint256 usdtFee,, ERC20Mock usdt) = _mintTicketUSDT(); // Check platform balances before withdraw assertEq(marketplaceFacet.getTicketBalance(ticketId, FeeType.USDT), usdtFee); // Withdraw @@ -90,7 +90,7 @@ contract MarketplaceTest is DeployedHostItTickets { } function test_withdrawTicketBalanceUSDC() public { - (uint56 ticketId,, uint256 usdcFee,, ERC20Mock usdc) = _mintTicketUSDC(); + (uint64 ticketId,, uint256 usdcFee,, ERC20Mock usdc) = _mintTicketUSDC(); // Check platform balances before withdraw FullTicketData memory fullTicketData = factoryFacet.ticketData(ticketId); vm.warp(fullTicketData.endTime + marketplaceFacet.getRefundPeriod()); diff --git a/test/Ticket.t.sol b/test/Ticket.t.sol index 892169b..48975ca 100644 --- a/test/Ticket.t.sol +++ b/test/Ticket.t.sol @@ -1,13 +1,13 @@ // SPDX-License-Identifier: AGPL-3.0-only pragma solidity 0.8.30; -import {Test} from "forge-std/Test.sol"; -import {Ticket} from "@ticket/libs/Ticket.sol"; -import {ITicket} from "@ticket/interfaces/ITicket.sol"; import {Clones} from "@openzeppelin/contracts/proxy/Clones.sol"; import {Initializable} from "@openzeppelin/contracts/proxy/utils/Initializable.sol"; -import {Pausable} from "@openzeppelin/contracts/utils/Pausable.sol"; import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; +import {Pausable} from "@openzeppelin/contracts/utils/Pausable.sol"; +import {ITicket} from "@ticket/interfaces/ITicket.sol"; +import {Ticket} from "@ticket/libs/Ticket.sol"; +import {Test} from "forge-std/Test.sol"; contract TicketTest is Test { using Clones for address; diff --git a/test/states/DeployedHostItTickets.sol b/test/states/DeployedHostItTickets.sol index 544bf3b..754222d 100644 --- a/test/states/DeployedHostItTickets.sol +++ b/test/states/DeployedHostItTickets.sol @@ -1,22 +1,22 @@ // SPDX-License-Identifier: AGPL-3.0-only pragma solidity 0.8.30; -import {ERC20Mock} from "@openzeppelin/contracts/mocks/token/ERC20Mock.sol"; import {IDiamondCut} from "@diamond/interfaces/IDiamondCut.sol"; import {IDiamondLoupe} from "@diamond/interfaces/IDiamondLoupe.sol"; -import {IFactory} from "@ticket/interfaces/IFactory.sol"; +import {ERC20Mock} from "@openzeppelin/contracts/mocks/token/ERC20Mock.sol"; +import {DeployHostItTicketsTest} from "@ticket-script/DeployHostItTickets.s.sol"; +import {ERC6551_REGISTRY} from "@ticket-script/helper/LibAddressesAndFees.sol"; +import {TicketData} from "@ticket-storage/FactoryStorage.sol"; +import {FeeType} from "@ticket-storage/MarketplaceStorage.sol"; import {ICheckIn} from "@ticket/interfaces/ICheckIn.sol"; +import {IFactory} from "@ticket/interfaces/IFactory.sol"; import {IMarketplace} from "@ticket/interfaces/IMarketplace.sol"; -import {FeeType} from "@ticket-storage/MarketplaceStorage.sol"; -import {TicketData} from "@ticket-storage/FactoryStorage.sol"; -import {DeployHostItTicketsTest} from "@ticket-script/DeployHostItTickets.s.sol"; -import {HelperContract} from "@diamond-test/helpers/HelperContract.sol"; import {ERC6551Registry} from "erc6551/src/ERC6551Registry.sol"; -import {ERC6551_REGISTRY} from "@ticket-script/helper/AddressesAndFees.sol"; +import {Test} from "forge-std/Test.sol"; /// forge-lint: disable-next-line(unaliased-plain-import) import "@ticket-logs/MarketplaceLogs.sol"; -abstract contract DeployedHostItTickets is HelperContract { +abstract contract DeployedHostItTickets is Test { address public hostIt; DeployHostItTicketsTest public deployHostItTickets; IFactory public factoryFacet; @@ -34,12 +34,7 @@ abstract contract DeployedHostItTickets is HelperContract { /// @notice List of facet contract names used in deployment. string[6] public facetNames = [ - "DiamondCutFacet", - "DiamondLoupeFacet", - "OwnableRolesFacet", - "FactoryFacet", - "CheckInFacet", - "MarketplaceFacet" + "DiamondCutFacet", "DiamondLoupeFacet", "OwnableRolesFacet", "FactoryFacet", "CheckInFacet", "MarketplaceFacet" ]; address owner = address(this); @@ -67,23 +62,7 @@ abstract contract DeployedHostItTickets is HelperContract { vm.etch(ERC6551_REGISTRY, address(new ERC6551Registry()).code); } - function _createFreeTicket() internal { - factoryFacet.createTicket(_getFreeTicketData(), _getZeroFeeType(), _getZeroFee()); - } - - function _updateFreeTicket(uint40 _ticketId) internal { - factoryFacet.updateTicket(_getFreeUpdatedTicketData(), _ticketId); - } - - function _createPaidTicket() internal { - factoryFacet.createTicket(_getPaidTicketData(), _getFeeTypes(), _getFees()); - } - - function _updatePaidTicket(uint40 _ticketId) internal { - factoryFacet.updateTicket(_getPaidUpdatedTicketData(), _ticketId); - } - - function _mintTicketFree() internal returns (uint56 ticketId_, uint40 tokenId_) { + function _mintTicketFree() internal returns (uint64 ticketId_, uint40 tokenId_) { _createFreeTicket(); ticketId_ = factoryFacet.ticketCount(); vm.expectEmit(true, true, true, true, hostIt); @@ -92,16 +71,17 @@ abstract contract DeployedHostItTickets is HelperContract { } /// forge-lint: disable-next-line(mixed-case-function) - function _mintTicketETH() internal returns (uint56 ticketId_, uint40 tokenId_, uint256 fee_, uint256 hostItFee_) { + function _mintTicketETH() internal returns (uint64 ticketId_, uint40 tokenId_, uint256 fee_, uint256 hostItFee_) { _createPaidTicket(); 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); - (bool success, bytes memory result) = address(marketplaceFacet).call{value: totalFee}( - abi.encodeWithSelector(marketplaceFacet.mintTicket.selector, ticketId_, FeeType.ETH, alice) - ); + (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)); fee_ = fee; @@ -111,7 +91,7 @@ abstract contract DeployedHostItTickets is HelperContract { /// forge-lint: disable-next-line(mixed-case-function) function _mintTicketUSDT() internal - returns (uint56 ticketId_, uint40 tokenId_, uint256 fee_, uint256 hostItFee_, ERC20Mock usdt_) + returns (uint64 ticketId_, uint40 tokenId_, uint256 fee_, uint256 hostItFee_, ERC20Mock usdt_) { _createPaidTicket(); ticketId_ = factoryFacet.ticketCount(); @@ -131,7 +111,7 @@ abstract contract DeployedHostItTickets is HelperContract { /// forge-lint: disable-next-line(mixed-case-function) function _mintTicketUSDC() internal - returns (uint56 ticketId_, uint40 tokenId_, uint256 fee_, uint256 hostItFee_, ERC20Mock usdc_) + returns (uint64 ticketId_, uint40 tokenId_, uint256 fee_, uint256 hostItFee_, ERC20Mock usdc_) { _createPaidTicket(); ticketId_ = factoryFacet.ticketCount(); @@ -148,13 +128,31 @@ abstract contract DeployedHostItTickets is HelperContract { hostItFee_ = hostItFee; } + function _createFreeTicket() internal { + factoryFacet.createTicket(_getFreeTicketData(), _getZeroFeeType(), _getZeroFee()); + } + + function _updateFreeTicket(uint40 _ticketId) internal { + factoryFacet.updateTicket(_getFreeUpdatedTicketData(), _ticketId); + } + + function _createPaidTicket() internal { + factoryFacet.createTicket(_getPaidTicketData(), _getFeeTypes(), _getFees()); + } + + function _updatePaidTicket(uint40 _ticketId) internal { + factoryFacet.updateTicket(_getPaidUpdatedTicketData(), _ticketId); + } + function _getFreeTicketData() 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: true, + isRefundable: false, name: "Free Ticket", symbol: "", uri: "ipfs://" @@ -167,7 +165,9 @@ abstract contract DeployedHostItTickets is HelperContract { endTime: uint40(block.timestamp + 2 days), purchaseStartTime: _currentTime, maxTickets: 100, + maxTicketsPerUser: 1, isFree: true, + isRefundable: false, name: "Updated Free Ticket", symbol: "UFT", uri: "ipfs://2" @@ -180,7 +180,9 @@ abstract contract DeployedHostItTickets is HelperContract { endTime: uint40(block.timestamp + 2 days), purchaseStartTime: _currentTime, maxTickets: type(uint40).max, + maxTicketsPerUser: 0, isFree: false, + isRefundable: false, name: "Paid Ticket", symbol: "", uri: "ipfs://$" @@ -193,7 +195,9 @@ abstract contract DeployedHostItTickets is HelperContract { endTime: uint40(block.timestamp + 2 days), purchaseStartTime: _currentTime, maxTickets: type(uint40).max, + maxTicketsPerUser: 0, isFree: false, + isRefundable: true, name: "Updated Paid Ticket", symbol: "UPT", uri: "ipfs://$$"