Party Manager (solidity)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import "lib/openzeppelin-contracts/contracts/access/Ownable.sol";
import "lib/openzeppelin-contracts/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "lib/openzeppelin-contracts/contracts/utils/ReentrancyGuard.sol";
contract PartyManager is ERC721URIStorage, Ownable, ReentrancyGuard {
struct Party {
uint256 id;
string name;
string description;
string venue;
uint256 startTime;
uint256 endTime;
uint256 entryFee;
uint256 maxParticipants;
address admin;
address host;
bool approved;
address[] participants;
mapping(address => bool) hasMintedNFT;
}
mapping(uint256 => Party) public parties;
address[] public adminList;
mapping(address => bool) public admins;
mapping(uint256 => mapping(uint256 => uint256)) public salePrices; // partyId => tokenId => price
mapping(uint256 => mapping(uint256 => address)) public nftOwners; // partyId => tokenId => owner
uint256 public partyCount;
uint256 public nftCounter;
uint256 public constant SUPERADMIN_NFT_FEE = 10; // 10% fee on NFT sales
event AdminAdded(address indexed admin);
event AdminRemoved(address indexed admin);
event PartyCreated(uint256 indexed partyId, address indexed admin);
event PartyApproved(uint256 indexed partyId);
event PartyDeleted(uint256 indexed partyId);
event UserJoinedParty(uint256 indexed partyId, address indexed user);
event NFTMinted(uint256 indexed partyId, address indexed user, uint256 nftId);
event NFTListed(uint256 indexed partyId, uint256 indexed tokenId, uint256 price);
event NFTUnlisted(uint256 indexed partyId, uint256 indexed tokenId);
event NFTSold(uint256 indexed partyId, uint256 indexed tokenId, address indexed buyer, uint256 price);
modifier onlyAdmin() {
require(admins[msg.sender], "Not an admin");
_;
}
modifier onlyDuringParty(uint256 _partyId) {
require(block.timestamp >= parties[_partyId].startTime && block.timestamp <= parties[_partyId].endTime, "Party time over");
_;
}
modifier onlyBeforeParty(uint256 _partyId) {
require(block.timestamp < parties[_partyId].startTime, "Party already started");
_;
}
modifier onlyApprovedParty(uint256 _partyId) {
require(parties[_partyId].approved, "Party not approved");
_;
}
constructor() ERC721("PartyNFT", "PNFT") Ownable(msg.sender) {}
// SuperAdmin Functions
function addAdmin(address _admin) external onlyOwner {
require(!admins[_admin], "Already an admin");
admins[_admin] = true;
adminList.push(_admin);
emit AdminAdded(_admin);
}
function removeAdmin(address _admin) external onlyOwner {
require(admins[_admin], "Not an admin");
admins[_admin] = false;
// Remove from adminList array
for (uint i = 0; i < adminList.length; i++) {
if (adminList[i] == _admin) {
adminList[i] = adminList[adminList.length - 1];
adminList.pop();
break;
}
}
emit AdminRemoved(_admin);
}
function approveParty(uint256 _partyId) external onlyOwner {
parties[_partyId].approved = true;
emit PartyApproved(_partyId);
}
function deleteParty(uint256 _partyId) external onlyOwner {
delete parties[_partyId];
emit PartyDeleted(_partyId);
}
// Admin Functions
function createParty(
string memory _name,
string memory _description,
string memory _venue,
uint256 _startTime,
uint256 _endTime,
uint256 _entryFee,
uint256 _maxParticipants,
address _host
) external onlyAdmin {
require(_startTime < _endTime, "Invalid time range");
partyCount++;
Party storage party = parties[partyCount];
party.id = partyCount;
party.name = _name;
party.description = _description;
party.venue = _venue;
party.startTime = _startTime;
party.endTime = _endTime;
party.entryFee = _entryFee;
party.maxParticipants = _maxParticipants;
party.admin = msg.sender;
party.host = _host;
party.approved = false;
emit PartyCreated(partyCount, msg.sender);
}
// User Functions
function joinParty(uint256 _partyId) external payable onlyApprovedParty(_partyId) onlyBeforeParty(_partyId) {
Party storage party = parties[_partyId];
require(msg.value == party.entryFee, "Incorrect entry fee");
require(party.participants.length < party.maxParticipants, "Party full");
payable(party.admin).transfer(msg.value);
party.participants.push(msg.sender);
emit UserJoinedParty(_partyId, msg.sender);
}
function mintNFT(uint256 _partyId, string memory _tokenURI) external onlyDuringParty(_partyId) onlyApprovedParty(_partyId) {
Party storage party = parties[_partyId];
require(!party.hasMintedNFT[msg.sender], "NFT already minted");
nftCounter++;
_mint(msg.sender, nftCounter);
_setTokenURI(nftCounter, _tokenURI);
nftOwners[_partyId][nftCounter] = msg.sender;
party.hasMintedNFT[msg.sender] = true;
emit NFTMinted(_partyId, msg.sender, nftCounter);
}
// NFT Marketplace Functions
function listNFT(uint256 _partyId, uint256 _tokenId, uint256 price) external {
require(ownerOf(_tokenId) == msg.sender, "Not NFT owner");
require(price > 0, "Price must be greater than zero");
salePrices[_partyId][_tokenId] = price;
emit NFTListed(_partyId, _tokenId, price);
}
function unlistNFT(uint256 _partyId, uint256 _tokenId) external {
require(nftOwners[_partyId][_tokenId] == msg.sender, "Not seller");
delete salePrices[_partyId][_tokenId];
emit NFTUnlisted(_partyId, _tokenId);
}
function buyNFT(uint256 _partyId, uint256 _tokenId) external payable {
uint256 price = salePrices[_partyId][_tokenId];
require(price > 0, "NFT not for sale");
require(msg.value == price, "Incorrect price");
address seller = nftOwners[_partyId][_tokenId];
uint256 superAdminFee = (price * SUPERADMIN_NFT_FEE) / 100;
uint256 sellerAmount = price - superAdminFee;
payable(owner()).transfer(superAdminFee);
payable(seller).transfer(sellerAmount);
_transfer(seller, msg.sender, _tokenId);
nftOwners[_partyId][_tokenId] = msg.sender;
delete salePrices[_partyId][_tokenId];
emit NFTSold(_partyId, _tokenId, msg.sender, price);
}
// Fetching Admin List
function getAllAdmins() external view returns (address[] memory) {
return adminList;
}
function getParty(uint256 _partyId) external view returns (uint256 id,string memory name,string memory description,string memory venue,uint256 startTime,uint256 endTime,uint256 entryFee,uint256 maxParticipants,address admin,address host,bool approved,uint256 participantCount) {
Party storage party = parties[_partyId];
return (
party.id,
party.name,
party.description,
party.venue,
party.startTime,
party.endTime,
party.entryFee,
party.maxParticipants,
party.admin,
party.host,
party.approved,
party.participants.length
);
}
}
This PartyManager smart contract is a party management and NFT ticketing system built with Solidity. It supports:
- Party creation & approval (by admins/owner),
- User participation with entry fees,
- NFT minting for event attendance,
- NFT resale via an internal marketplace.
It uses OpenZeppelin libraries for secure access control and token management:
Ownableβ manages super admin (contract owner) privileges.ERC721URIStorageβ for minting NFTs with metadata URIs.ReentrancyGuardβ protects against reentrancy attacks in sensitive functions (e.g.,buyNFT()).
π§ CONTRACT STRUCTURE & STATE VARIABLES
β
Struct: Party
Represents an event. Contains:
id,name,description,venue,startTime,endTimeentryFeeβ amount users must pay to joinmaxParticipants,participantsβ limit and list of attendeesadmin,hostβ managing and organizing addressesapprovedβ must betruefor users to join or minthasMintedNFTβ mapping per party to track if a user minted an NFT (1/user/party)
π Storage Mappings:
parties: party ID βPartystructadminList: array of current admin addressesadmins: address β bool (isAdmin)salePrices: party ID + token ID β price (for NFT marketplace)nftOwners: party ID + token ID β current owner
π CONTRACT FUNCTIONS β EXPLAINED IN FULL
ποΈ SuperAdmin Functions (Owner only)
addAdmin(address _admin)
- Adds a new admin to
adminListand sets theiradmins[_admin] = true. - Emits
AdminAdded.
removeAdmin(address _admin)
- Removes an admin from the mapping and from the
adminListarray. - Emits
AdminRemoved.
approveParty(uint256 _partyId)
- Marks a party as approved so that users can join and mint NFTs.
- Emits
PartyApproved.
deleteParty(uint256 _partyId)
- Deletes the party from storage.
- Emits
PartyDeleted.
π§ Admin Functions (Only admins)
createParty(...)
Creates a new party.
- Must set:
- Valid time (
startTime < endTime) - Entry fee and participant cap
- Event metadata: name, venue, description, etc.
- Valid time (
- Emits
PartyCreated.
πββοΈ User Functions
joinParty(uint256 _partyId)
- User sends ETH equal to the entry fee.
- Allowed only before the party starts & only if approved.
- Fee is forwarded to the partyβs admin.
- Adds user to the
participantslist. - Emits
UserJoinedParty.
mintNFT(uint256 _partyId, string memory _tokenURI)
- Allowed only during the party & only if approved.
- One NFT per user per party.
- Mints ERC721 NFT with metadata.
- Tracks owner and mints count.
- Emits
NFTMinted.
π NFT Marketplace Functions
listNFT(uint256 _partyId, uint256 _tokenId, uint256 price)
- NFT owner lists their token for sale at a given price.
- Price must be > 0.
- Emits
NFTListed.
unlistNFT(uint256 _partyId, uint256 _tokenId)
- Removes the NFT from sale.
- Only callable by current owner.
- Emits
NFTUnlisted.
buyNFT(uint256 _partyId, uint256 _tokenId)
- Buyer must pay the exact listed price.
- Transfers NFT to buyer.
- Sends:
- 90% to seller
- 10% fee to contract owner (
SUPERADMIN_NFT_FEE = 10)
- Cleans up ownership and sale data.
- Emits
NFTSold.
π‘οΈ ReentrancyGuard protects this function.
π View Functions
getAllAdmins()
- Returns the current list of admins.
getParty(uint256 _partyId)
- Returns all details of a party, including number of participants.
- Helpful for off-chain UI display.
β οΈ MODIFIERS
These restrict when and who can call certain functions.
onlyAdmin: only addresses marked as admins.onlyDuringParty(_partyId): current block time must be between start & end.onlyBeforeParty(_partyId): current block time must be before party starts.onlyApprovedParty(_partyId): the party must be approved by owner.
π¦ EVENTS β For DApps or Indexers
All major actions emit events:
- Admin additions/removals
- Party lifecycle (create/approve/delete)
- User joins
- NFT minting, listing, sale
πΈ ECONOMIC FLOW
- Users pay entry fee β sent to party
admin - Users mint NFTs β free but limited to one per user
- Users sell NFTs β seller receives 90%, contract owner receives 10%
π SECURITY CONSIDERATIONS
- Uses OpenZeppelin
Ownablefor access control - Uses
ReentrancyGuardinbuyNFT - NFT minting is restricted per user per party
- Entry fees are transferred immediately to prevent locked funds
β USE CASES
- IRL or Virtual Events with limited access
- Ticketing where entry is tied to NFT mint
- NFT trading for event memorabilia or access tokens