diff --git a/packages/nfts/contracts/eventRegister/EventRegister.sol b/packages/nfts/contracts/eventRegister/EventRegister.sol new file mode 100644 index 00000000000..72b01d77e8b --- /dev/null +++ b/packages/nfts/contracts/eventRegister/EventRegister.sol @@ -0,0 +1,269 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24.0; + +import "@openzeppelin/contracts/access/AccessControl.sol"; + +/** + * @title EventRegister + * @notice A contract that allows authorized managers to create events, manage user registrations, + * and track user participation using role-based access control. + * @dev Utilizes OpenZeppelin's AccessControl for role management. The contract does not hold any + * Ether. + */ +contract EventRegister is AccessControl { + /** + * @dev The role identifier for event managers. This role allows accounts to create events + * and manage registrations. + */ + bytes32 public constant EVENT_MANAGER_ROLE = keccak256("EVENT_MANAGER_ROLE"); + + /** + * @dev Represents an event with its associated details. + */ + struct Event { + ///< Unique identifier for the event. + uint256 id; + ///< Name of the event. + string name; + ///< Flag indicating whether the event exists. + bool exists; + ///< Flag indicating whether registrations are open for the event. + bool registrationOpen; + } + + /** + * @dev Mapping from event ID to Event details. + */ + mapping(uint256 eventId => Event eventData) public events; + + /** + * @dev Mapping from event ID to a mapping of user addresses to their registration status. + * Indicates whether a user has registered for a specific event. + */ + mapping(uint256 eventId => mapping(address registrant => bool isRegistered)) public + registrations; + + /** + * @dev Emitted when a new event is created. + * @param id The unique identifier of the created event. + * @param name The name of the created event. + */ + event EventCreated(uint256 id, string name); + + /** + * @dev Emitted when a user registers for an event. + * @param registrant The address of the user who registered. + * @param eventId The unique identifier of the event for which the user registered. + */ + event Registered(address indexed registrant, uint256 eventId); + + /** + * @dev Emitted when a user unregisters for an event. + * @param registrant The address of the user who unregistered. + * @param eventId The unique identifier of the event for which the user unregistered. + */ + event Unregistered(address indexed registrant, uint256 eventId); + + /** + * @dev Emitted when registrations are opened for an event. + * @param eventId The unique identifier of the event whose registrations are opened. + */ + event RegistrationOpened(uint256 eventId); + + /** + * @dev Emitted when registrations are closed for an event. + * @param eventId The unique identifier of the event whose registrations are closed. + */ + event RegistrationClosed(uint256 eventId); + + /** + * @dev Counter for assigning unique event IDs. + */ + uint256 private nextEventId; + + /** + * @notice Initializes the contract by granting the deployer the default admin role. + * @dev The deployer of the contract is granted the DEFAULT_ADMIN_ROLE, allowing them to manage + * roles. + */ + constructor() { + _grantRole(DEFAULT_ADMIN_ROLE, _msgSender()); + _grantRole(EVENT_MANAGER_ROLE, _msgSender()); + } + + /** + * @notice Grants the EVENT_MANAGER_ROLE to a specified account. + * @dev Only accounts with the DEFAULT_ADMIN_ROLE can call this function. + * @param account The address to be granted the EVENT_MANAGER_ROLE. + * + * Requirements: + * + * - The caller must have the DEFAULT_ADMIN_ROLE. + */ + function grantEventManagerRole(address account) external onlyRole(DEFAULT_ADMIN_ROLE) { + grantRole(EVENT_MANAGER_ROLE, account); + } + + /** + * @notice Revokes the EVENT_MANAGER_ROLE from a specified account. + * @dev Only accounts with the DEFAULT_ADMIN_ROLE can call this function. + * @param account The address from which the EVENT_MANAGER_ROLE will be revoked. + * + * Requirements: + * + * - The caller must have the DEFAULT_ADMIN_ROLE. + */ + function revokeEventManagerRole(address account) external onlyRole(DEFAULT_ADMIN_ROLE) { + revokeRole(EVENT_MANAGER_ROLE, account); + } + + /** + * @notice Creates a new event with the given name. + * @dev Only accounts with the EVENT_MANAGER_ROLE can call this function. + * Emits EventCreated and RegistrationOpened events upon successful creation. + * @param _name The name of the event to be created. + * + * Requirements: + * + * - The caller must have the EVENT_MANAGER_ROLE. + */ + function createEvent(string memory _name) external onlyRole(EVENT_MANAGER_ROLE) { + uint256 eventId = nextEventId; + events[eventId] = Event({ id: eventId, name: _name, exists: true, registrationOpen: true }); + emit EventCreated(eventId, _name); + emit RegistrationOpened(eventId); // Emit event indicating registrations are open + nextEventId++; + } + + /** + * @notice Opens registrations for a specific event. + * @dev Only accounts with the EVENT_MANAGER_ROLE can call this function. + * Emits a RegistrationOpened event upon successful operation. + * @param _eventId The unique identifier of the event for which to open registrations. + * + * Requirements: + * + * - The event with `_eventId` must exist. + * - Registrations for the event must currently be closed. + * - The caller must have the EVENT_MANAGER_ROLE. + */ + function openRegistration(uint256 _eventId) external onlyRole(EVENT_MANAGER_ROLE) { + require(events[_eventId].exists, "Event not found"); + require(!events[_eventId].registrationOpen, "Already open"); + + events[_eventId].registrationOpen = true; + emit RegistrationOpened(_eventId); + } + + /** + * @notice Closes registrations for a specific event. + * @dev Only accounts with the EVENT_MANAGER_ROLE can call this function. + * Emits a RegistrationClosed event upon successful operation. + * @param _eventId The unique identifier of the event for which to close registrations. + * + * Requirements: + * + * - The event with `_eventId` must exist. + * - Registrations for the event must currently be open. + * - The caller must have the EVENT_MANAGER_ROLE. + */ + function closeRegistration(uint256 _eventId) external onlyRole(EVENT_MANAGER_ROLE) { + require(events[_eventId].exists, "Event not found"); + require(events[_eventId].registrationOpen, "Already closed"); + + events[_eventId].registrationOpen = false; + emit RegistrationClosed(_eventId); + } + + /** + * @notice Allows a user to register for a specific event. + * @dev Emits a Registered event upon successful registration. + * @param _eventId The unique identifier of the event to register for. + * + * Requirements: + * + * - The event with `_eventId` must exist. + * - Registrations for the event must be open. + * - The caller must not have already registered for the event. + */ + function register(uint256 _eventId) external { + Event memory currentEvent = events[_eventId]; + require(currentEvent.exists, "Event not found"); + require(currentEvent.registrationOpen, "Registrations closed"); + require(!registrations[_eventId][msg.sender], "Already registered"); + + registrations[_eventId][msg.sender] = true; + + emit Registered(msg.sender, _eventId); + } + + /** + * @notice Allows the event manager to unregister a user from a specific event. + * @dev Emits an Unregistered event upon successful un-registration. + * @param _eventId The unique identifier of the event to unregister from. + * @param _user The address of the user to unregister. + * + * Requirements: + * - The event with `_eventId` must exist. + * - Registrations for the event must be open. + * - The user must be registered for the event. + */ + function unregister(uint256 _eventId, address _user) external onlyRole(EVENT_MANAGER_ROLE) { + Event memory currentEvent = events[_eventId]; + require(currentEvent.exists, "Event not found"); + require(currentEvent.registrationOpen, "Registrations closed"); + require(registrations[_eventId][_user], "Not registered"); + + registrations[_eventId][_user] = false; + emit Unregistered(_user, _eventId); + } + + /** + * @notice Retrieves all event IDs for which a user has registered. + * @dev Iterates through all existing events to compile a list of registrations. + * @param _user The address of the user whose registrations are to be retrieved. + * @return An array of event IDs that the user has registered for. + * + */ + function getRegisteredEvents(address _user) external view returns (uint256[] memory) { + uint256[] memory temp = new uint256[](nextEventId); + uint256 count = 0; + + for (uint256 i = 0; i < nextEventId; i++) { + if (registrations[i][_user]) { + temp[count] = i; + count++; + } + } + + // Create a fixed-size array to return + uint256[] memory registeredEvents = new uint256[](count); + for (uint256 j = 0; j < count; j++) { + registeredEvents[j] = temp[j]; + } + + return registeredEvents; + } + + /** + * @notice Retrieves the details of a specific event. + * @dev Returns the event's ID, name, and registration status. + * @param _eventId The unique identifier of the event to retrieve. + * @return id The unique identifier of the event. + * @return name The name of the event. + * @return registrationOpen_ A boolean indicating whether registrations are open for the event. + * + * Requirements: + * + * - The event with `_eventId` must exist. + */ + function getEvent(uint256 _eventId) + external + view + returns (uint256 id, string memory name, bool registrationOpen_) + { + require(events[_eventId].exists, "Event not found"); + Event memory e = events[_eventId]; + return (e.id, e.name, e.registrationOpen); + } +} diff --git a/packages/nfts/script/trailblazer/eventRegister/Deploy.sol b/packages/nfts/script/trailblazer/eventRegister/Deploy.sol new file mode 100644 index 00000000000..647511ea4d6 --- /dev/null +++ b/packages/nfts/script/trailblazer/eventRegister/Deploy.sol @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import { Script, console } from "forge-std/src/Script.sol"; +import { EventRegister } from "../../../contracts/eventRegister/EventRegister.sol"; + +contract DeployEventRegisterScript is Script { + function run() public { + uint256 deployerPrivateKey = vm.envUint("MAINNET_PRIVATE_KEY"); + address deployerAddress = vm.addr(deployerPrivateKey); + + vm.startBroadcast(deployerPrivateKey); + + EventRegister eventRegister = new EventRegister(); + + console.log("Deployed EventRegister to:", address(eventRegister), "from", deployerAddress); + + vm.stopBroadcast(); + } +} diff --git a/packages/nfts/script/galxe/Deploy.s.sol b/packages/nfts/script/trailblazer/galxe/Deploy.s.sol similarity index 88% rename from packages/nfts/script/galxe/Deploy.s.sol rename to packages/nfts/script/trailblazer/galxe/Deploy.s.sol index 7517fb18744..c33708d9f73 100644 --- a/packages/nfts/script/galxe/Deploy.s.sol +++ b/packages/nfts/script/trailblazer/galxe/Deploy.s.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.0; import { Script, console } from "forge-std/src/Script.sol"; -import { RegisterGalxePoints } from "../../contracts/galxe/RegisterGalxePoints.sol"; +import { RegisterGalxePoints } from "../../../contracts/galxe/RegisterGalxePoints.sol"; contract DeployRegisterMyGalaxyPointScript is Script { function run() public { diff --git a/packages/nfts/script/trailblazers-badges/sol/Deploy.s.sol b/packages/nfts/script/trailblazer/trailblazers-badges/sol/Deploy.s.sol similarity index 96% rename from packages/nfts/script/trailblazers-badges/sol/Deploy.s.sol rename to packages/nfts/script/trailblazer/trailblazers-badges/sol/Deploy.s.sol index 7c380fac890..63bb1ea352e 100644 --- a/packages/nfts/script/trailblazers-badges/sol/Deploy.s.sol +++ b/packages/nfts/script/trailblazer/trailblazers-badges/sol/Deploy.s.sol @@ -5,7 +5,8 @@ import { UtilsScript } from "./Utils.s.sol"; import { Script, console } from "forge-std/src/Script.sol"; import { Merkle } from "murky/Merkle.sol"; import { ERC1967Proxy } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; -import { TrailblazersBadges } from "../../../contracts/trailblazers-badges/TrailblazersBadges.sol"; +import { TrailblazersBadges } from + "../../../../contracts/trailblazers-badges/TrailblazersBadges.sol"; import { IMinimalBlacklist } from "@taiko/blacklist/IMinimalBlacklist.sol"; contract DeployScript is Script { diff --git a/packages/nfts/script/trailblazers-badges/sol/UpgradeV2.sol b/packages/nfts/script/trailblazer/trailblazers-badges/sol/UpgradeV2.sol similarity index 92% rename from packages/nfts/script/trailblazers-badges/sol/UpgradeV2.sol rename to packages/nfts/script/trailblazer/trailblazers-badges/sol/UpgradeV2.sol index 0588945b84c..a9d80fd2993 100644 --- a/packages/nfts/script/trailblazers-badges/sol/UpgradeV2.sol +++ b/packages/nfts/script/trailblazer/trailblazers-badges/sol/UpgradeV2.sol @@ -5,7 +5,8 @@ import { UtilsScript } from "./Utils.s.sol"; import { Script, console } from "forge-std/src/Script.sol"; import { Merkle } from "murky/Merkle.sol"; import { ERC1967Proxy } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; -import { TrailblazersBadges } from "../../../contracts/trailblazers-badges/TrailblazersBadges.sol"; +import { TrailblazersBadges } from + "../../../../contracts/trailblazers-badges/TrailblazersBadges.sol"; import { IMinimalBlacklist } from "@taiko/blacklist/IMinimalBlacklist.sol"; contract UpgradeV2 is Script { diff --git a/packages/nfts/script/trailblazer/trailblazers-badges/sol/UpgradeV3.sol b/packages/nfts/script/trailblazer/trailblazers-badges/sol/UpgradeV3.sol new file mode 100644 index 00000000000..472192164cd --- /dev/null +++ b/packages/nfts/script/trailblazer/trailblazers-badges/sol/UpgradeV3.sol @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.24; + +import { UtilsScript } from "./Utils.s.sol"; +import { Script, console } from "forge-std/src/Script.sol"; +import { Merkle } from "murky/Merkle.sol"; +import { ERC1967Proxy } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; +import { TrailblazersBadges } from + "../../../../contracts/trailblazers-badges/TrailblazersBadges.sol"; +import { IMinimalBlacklist } from "@taiko/blacklist/IMinimalBlacklist.sol"; +import { TrailblazersBadgesV3 } from + "../../../../contracts/trailblazers-badges/TrailblazersBadgesV3.sol"; + +contract UpgradeV2 is Script { + UtilsScript public utils; + string public jsonLocation; + uint256 public deployerPrivateKey; + address public deployerAddress; + + address tokenV2Address = 0xa20a8856e00F5ad024a55A663F06DCc419FFc4d5; + TrailblazersBadges public tokenV2; + TrailblazersBadgesV3 public tokenV3; + + function setUp() public { + utils = new UtilsScript(); + utils.setUp(); + + jsonLocation = utils.getContractJsonLocation(); + deployerPrivateKey = utils.getPrivateKey(); + deployerAddress = utils.getAddress(); + } + + function run() public { + vm.startBroadcast(deployerPrivateKey); + tokenV2 = TrailblazersBadges(tokenV2Address); + + tokenV2.upgradeToAndCall( + address(new TrailblazersBadgesV3()), abi.encodeCall(TrailblazersBadgesV3.version, ()) + ); + + tokenV3 = TrailblazersBadgesV3(tokenV3); + + console.log("Upgraded TrailblazersBadgesV3 to:", address(tokenV3)); + } +} diff --git a/packages/nfts/script/trailblazer/trailblazers-badges/sol/Utils.s.sol b/packages/nfts/script/trailblazer/trailblazers-badges/sol/Utils.s.sol new file mode 100644 index 00000000000..3af584af2bb --- /dev/null +++ b/packages/nfts/script/trailblazer/trailblazers-badges/sol/Utils.s.sol @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.24; + +import { Script, console } from "forge-std/src/Script.sol"; +import "forge-std/src/StdJson.sol"; +import { IMinimalBlacklist } from "@taiko/blacklist/IMinimalBlacklist.sol"; +import { MockBlacklist } from "../../../../test/util/Blacklist.sol"; + +contract UtilsScript is Script { + using stdJson for string; + + address public nounsTokenAddress; + + uint256 public chainId; + + string public lowercaseNetworkKey; + string public uppercaseNetworkKey; + + function setUp() public { + // load all network configs + chainId = block.chainid; + + if (chainId == 31_337) { + lowercaseNetworkKey = "localhost"; + uppercaseNetworkKey = "LOCALHOST"; + } else if (chainId == 17_000) { + lowercaseNetworkKey = "holesky"; + uppercaseNetworkKey = "HOLESKY"; + } else if (chainId == 167_001) { + lowercaseNetworkKey = "devnet"; + uppercaseNetworkKey = "DEVNET"; + } else if (chainId == 11_155_111) { + lowercaseNetworkKey = "sepolia"; + uppercaseNetworkKey = "SEPOLIA"; + } else if (chainId == 167_008) { + lowercaseNetworkKey = "katla"; + uppercaseNetworkKey = "KATLA"; + } else if (chainId == 167_000) { + lowercaseNetworkKey = "mainnet"; + uppercaseNetworkKey = "MAINNET"; + } else if (chainId == 167_009) { + lowercaseNetworkKey = "hekla"; + uppercaseNetworkKey = "HEKLA"; + } else { + revert("Unsupported chainId"); + } + } + + function getPrivateKey() public view returns (uint256) { + string memory lookupKey = string.concat(uppercaseNetworkKey, "_PRIVATE_KEY"); + return vm.envUint(lookupKey); + } + + function getAddress() public view returns (address) { + string memory lookupKey = string.concat(uppercaseNetworkKey, "_ADDRESS"); + return vm.envAddress(lookupKey); + } + + function getContractJsonLocation() public view returns (string memory) { + string memory root = vm.projectRoot(); + return + string.concat(root, "/deployments/trailblazers-badges/", lowercaseNetworkKey, ".json"); + } + + function getBlacklist() public view returns (IMinimalBlacklist blacklistAddress) { + if (block.chainid == 167_000) { + // mainnet blacklist address + blacklistAddress = IMinimalBlacklist(vm.envAddress("BLACKLIST_ADDRESS")); + } else { + // deploy a mock blacklist otherwise + blacklistAddress = IMinimalBlacklist(0xbdEd0D2bf404bdcBa897a74E6657f1f12e5C6fb6); + } + + return blacklistAddress; + } + + function run() public { } +} diff --git a/packages/nfts/script/trailblazers-badges/sol/Utils.s.sol b/packages/nfts/script/trailblazers-badges/sol/Utils.s.sol index 6e3a113feea..3a0dc45a5c6 100644 --- a/packages/nfts/script/trailblazers-badges/sol/Utils.s.sol +++ b/packages/nfts/script/trailblazers-badges/sol/Utils.s.sol @@ -3,8 +3,8 @@ pragma solidity 0.8.24; import { Script, console } from "forge-std/src/Script.sol"; import "forge-std/src/StdJson.sol"; -import { IMinimalBlacklist } from "@taiko/blacklist/IMinimalBlacklist.sol"; import { MockBlacklist } from "../../../test/util/Blacklist.sol"; +import { IMinimalBlacklist } from "@taiko/blacklist/IMinimalBlacklist.sol"; contract UtilsScript is Script { using stdJson for string; @@ -16,6 +16,8 @@ contract UtilsScript is Script { string public lowercaseNetworkKey; string public uppercaseNetworkKey; + error UNSUPPORTED_CHAIN_ID(); + function setUp() public { // load all network configs chainId = block.chainid; @@ -42,7 +44,7 @@ contract UtilsScript is Script { lowercaseNetworkKey = "hekla"; uppercaseNetworkKey = "HEKLA"; } else { - revert("Unsupported chainId"); + revert UNSUPPORTED_CHAIN_ID(); } } diff --git a/packages/nfts/test/trailblazer/eventRegister/EventRegister.t.sol b/packages/nfts/test/trailblazer/eventRegister/EventRegister.t.sol new file mode 100644 index 00000000000..702165d3b2e --- /dev/null +++ b/packages/nfts/test/trailblazer/eventRegister/EventRegister.t.sol @@ -0,0 +1,269 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +import { Test } from "forge-std/src/Test.sol"; +import "../../../contracts/eventRegister/EventRegister.sol"; + +contract EventRegisterTest is Test { + EventRegister public eventRegister; + + address public owner = address(0x1); + address public manager = address(0x2); + address public user1 = address(0x3); + address public user2 = address(0x4); + + string public eventName1 = "Spring Season"; + string public eventName2 = "Summer Season"; + + function setUp() public { + vm.startPrank(owner); + eventRegister = new EventRegister(); + vm.stopPrank(); + + vm.startPrank(owner); + eventRegister.grantEventManagerRole(manager); + vm.stopPrank(); + } + + function testDeployerHasAdminRole() public view { + assertTrue( + eventRegister.hasRole(eventRegister.DEFAULT_ADMIN_ROLE(), owner), + "Owner should have DEFAULT_ADMIN_ROLE" + ); + } + + function testManagerHasEventManagerRole() public view { + assertTrue( + eventRegister.hasRole(eventRegister.EVENT_MANAGER_ROLE(), manager), + "Manager should have EVENT_MANAGER_ROLE" + ); + } + + function testAdminCanGrantEventManagerRole() public { + vm.startPrank(owner); + eventRegister.grantEventManagerRole(user1); + vm.stopPrank(); + + assertTrue( + eventRegister.hasRole(eventRegister.EVENT_MANAGER_ROLE(), user1), + "User1 should have EVENT_MANAGER_ROLE" + ); + } + + function testAdminCanRevokeEventManagerRole() public { + vm.startPrank(owner); + eventRegister.grantEventManagerRole(user1); + eventRegister.revokeEventManagerRole(user1); + vm.stopPrank(); + + assertFalse( + eventRegister.hasRole(eventRegister.EVENT_MANAGER_ROLE(), user1), + "User1 should not have EVENT_MANAGER_ROLE" + ); + } + + function testOnlyEventManagersCanCreateEvents() public { + vm.startPrank(user1); + vm.expectRevert(); + eventRegister.createEvent(eventName1); + vm.stopPrank(); + + vm.startPrank(manager); + eventRegister.createEvent(eventName1); + vm.stopPrank(); + + (uint256 id, string memory name, bool registrationOpen) = eventRegister.getEvent(0); + assertEq(id, 0, "Event ID should be 0"); + assertEq(name, eventName1, "Event name should match"); + assertTrue(registrationOpen, "Registration should be open"); + } + + function testOpenAndCloseRegistrations() public { + vm.startPrank(manager); + eventRegister.createEvent(eventName1); + eventRegister.closeRegistration(0); + vm.stopPrank(); + + vm.startPrank(user1); + vm.expectRevert("Registrations closed"); + eventRegister.register(0); + vm.stopPrank(); + + vm.startPrank(manager); + eventRegister.openRegistration(0); + vm.stopPrank(); + + vm.startPrank(user1); + eventRegister.register(0); + vm.stopPrank(); + + assertTrue(eventRegister.registrations(0, user1), "User1 should be registered"); + } + + function testOnlyEventManagersCanOpenCloseRegistrations() public { + vm.startPrank(manager); + eventRegister.createEvent(eventName1); + vm.stopPrank(); + + vm.startPrank(user1); + vm.expectRevert(); + eventRegister.closeRegistration(0); + vm.stopPrank(); + + vm.startPrank(user1); + vm.expectRevert(); + eventRegister.openRegistration(0); + vm.stopPrank(); + } + + function testUserCanRegisterForOpenEvent() public { + vm.startPrank(manager); + eventRegister.createEvent(eventName1); + vm.stopPrank(); + + vm.startPrank(user1); + eventRegister.register(0); + vm.stopPrank(); + + assertTrue(eventRegister.registrations(0, user1), "User1 should be registered for event 0"); + } + + function testUserCannotRegisterTwice() public { + vm.startPrank(manager); + eventRegister.createEvent(eventName1); + vm.stopPrank(); + + vm.startPrank(user1); + eventRegister.register(0); + vm.expectRevert("Already registered"); + eventRegister.register(0); + vm.stopPrank(); + } + + function testUserCannotRegisterForNonExistentEvent() public { + vm.startPrank(user1); + vm.expectRevert("Event not found"); + eventRegister.register(999); + vm.stopPrank(); + } + + function testCannotCreateEventAsNonManager() public { + vm.startPrank(user1); + vm.expectRevert(); + eventRegister.createEvent("Autumn Season"); + vm.stopPrank(); + } + + function testCannotOpenRegistrationAsNonManager() public { + vm.startPrank(manager); + eventRegister.createEvent(eventName1); + vm.stopPrank(); + + vm.startPrank(user1); + vm.expectRevert(); + eventRegister.openRegistration(0); + vm.stopPrank(); + } + + function testCannotCloseRegistrationAsNonManager() public { + vm.startPrank(manager); + eventRegister.createEvent(eventName1); + vm.stopPrank(); + + vm.startPrank(user1); + vm.expectRevert(); + eventRegister.closeRegistration(0); + vm.stopPrank(); + } + + function testMultipleEventCreation() public { + vm.startPrank(manager); + eventRegister.createEvent(eventName1); + eventRegister.createEvent(eventName2); + vm.stopPrank(); + + (uint256 id1, string memory name1, bool regOpen1) = eventRegister.getEvent(0); + assertEq(id1, 0, "First event ID should be 0"); + assertEq(name1, eventName1, "First event name should match"); + assertTrue(regOpen1, "First event registration should be open"); + + (uint256 id2, string memory name2, bool regOpen2) = eventRegister.getEvent(1); + assertEq(id2, 1, "Second event ID should be 1"); + assertEq(name2, eventName2, "Second event name should match"); + assertTrue(regOpen2, "Second event registration should be open"); + } + + function testCannotCloseAlreadyClosedRegistration() public { + vm.startPrank(manager); + eventRegister.createEvent(eventName1); + eventRegister.closeRegistration(0); + vm.expectRevert("Already closed"); + eventRegister.closeRegistration(0); + vm.stopPrank(); + } + + function testCannotOpenAlreadyOpenRegistration() public { + vm.startPrank(manager); + eventRegister.createEvent(eventName1); + vm.expectRevert("Already open"); + eventRegister.openRegistration(0); + vm.stopPrank(); + } + + function testCannotRegisterAfterClosingRegistration() public { + vm.startPrank(manager); + eventRegister.createEvent(eventName1); + eventRegister.closeRegistration(0); + vm.stopPrank(); + + vm.startPrank(user1); + vm.expectRevert("Registrations closed"); + eventRegister.register(0); + vm.stopPrank(); + } + + function testMultipleUsersRegistration() public { + vm.startPrank(manager); + eventRegister.createEvent(eventName1); + vm.stopPrank(); + + vm.startPrank(user1); + eventRegister.register(0); + vm.stopPrank(); + + vm.startPrank(user2); + eventRegister.register(0); + vm.stopPrank(); + + assertTrue(eventRegister.registrations(0, user1), "User1 should be registered"); + assertTrue(eventRegister.registrations(0, user2), "User2 should be registered"); + } + + function testGetRegisteredEvents() public { + vm.startPrank(manager); + eventRegister.createEvent(eventName1); + eventRegister.createEvent(eventName2); + vm.stopPrank(); + + vm.startPrank(user1); + eventRegister.register(0); + eventRegister.register(1); + vm.stopPrank(); + + uint256[] memory registeredEvents = eventRegister.getRegisteredEvents(user1); + assertEq(registeredEvents.length, 2, "User1 should have registered for 2 events"); + assertEq(registeredEvents[0], 0, "First event ID should be 0"); + assertEq(registeredEvents[1], 1, "Second event ID should be 1"); + } + + function testGetEventDetails() public { + vm.startPrank(manager); + eventRegister.createEvent(eventName1); + vm.stopPrank(); + + (uint256 id, string memory name, bool registrationOpen) = eventRegister.getEvent(0); + assertEq(id, 0, "Event ID should be 0"); + assertEq(name, eventName1, "Event name should match"); + assertTrue(registrationOpen, "Registration should be open"); + } +} diff --git a/packages/nfts/test/galxe/RegisterGalxePoints.t.sol b/packages/nfts/test/trailblazer/galxe/RegisterGalxePoints.t.sol similarity index 92% rename from packages/nfts/test/galxe/RegisterGalxePoints.t.sol rename to packages/nfts/test/trailblazer/galxe/RegisterGalxePoints.t.sol index fa4539035f7..7d074bda361 100644 --- a/packages/nfts/test/galxe/RegisterGalxePoints.t.sol +++ b/packages/nfts/test/trailblazer/galxe/RegisterGalxePoints.t.sol @@ -3,7 +3,7 @@ pragma solidity 0.8.24; import { Test } from "forge-std/src/Test.sol"; -import { RegisterGalxePoints } from "../../contracts/galxe/RegisterGalxePoints.sol"; +import { RegisterGalxePoints } from "../../../contracts/galxe/RegisterGalxePoints.sol"; contract RegisterGalxePointsTest is Test { RegisterGalxePoints public registerGalxePoints; diff --git a/packages/nfts/test/trailblazers-badges/TrailblazersBadges.t.sol b/packages/nfts/test/trailblazer/trailblazers-badges/TrailblazersBadges.t.sol similarity index 98% rename from packages/nfts/test/trailblazers-badges/TrailblazersBadges.t.sol rename to packages/nfts/test/trailblazer/trailblazers-badges/TrailblazersBadges.t.sol index 7fe6531a114..193ab093deb 100644 --- a/packages/nfts/test/trailblazers-badges/TrailblazersBadges.t.sol +++ b/packages/nfts/test/trailblazer/trailblazers-badges/TrailblazersBadges.t.sol @@ -3,11 +3,11 @@ pragma solidity 0.8.24; import { Test } from "forge-std/src/Test.sol"; -import { TrailblazersBadges } from "../../contracts/trailblazers-badges/TrailblazersBadges.sol"; +import { TrailblazersBadges } from "../../../contracts/trailblazers-badges/TrailblazersBadges.sol"; import { Merkle } from "murky/Merkle.sol"; import { ERC1967Proxy } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; -import { UtilsScript } from "../../script/taikoon/sol/Utils.s.sol"; -import { MockBlacklist } from "../util/Blacklist.sol"; +import { UtilsScript } from "../../../script/taikoon/sol/Utils.s.sol"; +import { MockBlacklist } from "../../util/Blacklist.sol"; import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; contract TrailblazersBadgesTest is Test { diff --git a/packages/nfts/test/trailblazers-badges/TrailblazersBadgesV3.t.sol b/packages/nfts/test/trailblazer/trailblazers-badges/TrailblazersBadgesV3.t.sol similarity index 87% rename from packages/nfts/test/trailblazers-badges/TrailblazersBadgesV3.t.sol rename to packages/nfts/test/trailblazer/trailblazers-badges/TrailblazersBadgesV3.t.sol index d4f4bc34002..cd64ea8f320 100644 --- a/packages/nfts/test/trailblazers-badges/TrailblazersBadgesV3.t.sol +++ b/packages/nfts/test/trailblazer/trailblazers-badges/TrailblazersBadgesV3.t.sol @@ -3,13 +3,14 @@ pragma solidity 0.8.24; import { Test } from "forge-std/src/Test.sol"; -import { TrailblazersBadges } from "../../contracts/trailblazers-badges/TrailblazersBadges.sol"; +import { TrailblazersBadges } from "../../../contracts/trailblazers-badges/TrailblazersBadges.sol"; import { Merkle } from "murky/Merkle.sol"; import { ERC1967Proxy } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; -import { UtilsScript } from "../../script/taikoon/sol/Utils.s.sol"; -import { MockBlacklist } from "../util/Blacklist.sol"; +import { UtilsScript } from "../../../script/taikoon/sol/Utils.s.sol"; +import { MockBlacklist } from "../../util/Blacklist.sol"; import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; -import { TrailblazersBadgesV3 } from "../../contracts/trailblazers-badges/TrailblazersBadgesV3.sol"; +import { TrailblazersBadgesV3 } from + "../../../contracts/trailblazers-badges/TrailblazersBadgesV3.sol"; contract TrailblazersBadgesV3Test is Test { UtilsScript public utils;