Skip to main content

Overview

EventTicketFactory is a factory contract that deploys minimal proxy clones of the EventTicket contract, reducing deployment costs by approximately 90% compared to deploying full contracts.
Contract Location: src/packages/contracts/src/EventTicketFactory.solInherits: Ownable, ReentrancyGuardPattern: EIP-1167 Minimal Proxy (Clone Factory)

Why Use a Factory?

Gas Savings

Deploy events for ~12insteadof 1-2 instead of ~20-30 by using minimal proxies

Consistent Code

All events use the same audited implementation contract

Easy Discovery

Track all events by organizer and retrieve event contracts easily

Centralized Management

Update platform fees and settings from a single location

Minimal Proxy Pattern

The factory uses OpenZeppelin’s Clones library (EIP-1167) to create minimal proxies:
// Minimal proxy bytecode (~45 bytes)
// Delegates all calls to the implementation contract
363d3d373d3d3d363d73{implementation}5af43d82803e903d91602b57fd5bf3
  1. Implementation Contract: Deploy once with full bytecode
  2. Clone Contract: Deploy lightweight proxy that delegates to implementation
  3. Initialization: Each clone maintains its own storage
  4. Cost: Clone deployment costs ~45 bytes of bytecode vs full contract (~10-20kb)

Contract State

Immutable Variables

implementation
address
The implementation contract address for EventTicket (set in constructor, immutable)

Public Variables

platformFeeBps
uint256
Platform fee in basis points (default: 250 = 2.5%, max: 1000 = 10%)
platformFeeRecipient
address
Address that receives platform fees from all events
nextEventId
uint256
Counter for unique event IDs (starts at 1)

Mappings

eventContracts
mapping(uint256 => address)
Maps event IDs to deployed contract addresses
organizerEvents
mapping(address => address[])
Maps organizer addresses to arrays of their event contracts

Constructor

constructor(address _platformFeeRecipient) Ownable(msg.sender)
_platformFeeRecipient
address
required
Initial platform fee recipient address
What happens in constructor:
  1. Deploys a new EventTicket implementation contract
  2. Stores the implementation address as immutable
  3. Sets the platform fee recipient
  4. Sets platformFeeBps to default 250 (2.5%)
Example:
// Deploy factory with platform fee recipient
EventTicketFactory factory = new EventTicketFactory(0xPlatformAddress);

Core Functions

Create Event

Deploy a new EventTicket contract as a minimal proxy clone.
function createEvent(
    string memory eventName,
    string memory eventSymbol,
    uint256 totalSupply,
    uint256 ticketPrice,
    uint256 saleStart,
    uint256 saleEnd,
    uint256 eventDate,
    string memory baseTokenURI
) external nonReentrant returns (address eventContract)
eventName
string
required
Name of the event (e.g., “DevCon 2024”)
eventSymbol
string
required
Symbol for the NFT tickets (e.g., “DEVCON24”)
totalSupply
uint256
required
Maximum number of tickets (1-10,000)
ticketPrice
uint256
required
Price per ticket in wei
saleStart
uint256
required
Unix timestamp when ticket sales begin
saleEnd
uint256
required
Unix timestamp when ticket sales end (must be before eventDate)
eventDate
uint256
required
Unix timestamp of the actual event
baseTokenURI
string
required
Base URI for token metadata (e.g., “https://api.example.com/metadata/”)
eventContract
address
Address of the newly deployed EventTicket clone
Requirements:
  • eventName must not be empty
  • totalSupply must be between 1 and 10,000
  • saleStart must be before saleEnd
  • saleEnd must be before or equal to eventDate
What Happens:
  1. Generates a unique event ID
  2. Deploys a minimal proxy clone of the implementation
  3. Initializes the clone with provided parameters
  4. Stores the contract address in mappings
  5. Emits EventContractDeployed event
  6. Returns the deployed contract address
Example:
const { ethers } = require('ethers');

const factory = new ethers.Contract(factoryAddress, factoryABI, signer);

const now = Math.floor(Date.now() / 1000);
const tx = await factory.createEvent(
  'Summer Music Festival',
  'SMF2024',
  5000, // 5000 tickets
  ethers.parseEther('0.05'), // 0.05 MATIC per ticket
  now, // Sale starts now
  now + (7 * 24 * 60 * 60), // Sale ends in 7 days
  now + (30 * 24 * 60 * 60), // Event in 30 days
  'https://api.summermusic.com/metadata/'
);

const receipt = await tx.wait();
const event = receipt.logs.find(log => log.eventName === 'EventContractDeployed');
const eventAddress = event.args.contractAddress;

console.log('Event created at:', eventAddress);
Events Emitted:
event EventContractDeployed(
    uint256 indexed eventId,
    address indexed organizer,
    address contractAddress,
    string eventName,
    uint256 totalSupply
);

Query Functions

Get Organizer Events

Retrieve all event contracts created by a specific organizer.
function getOrganizerEvents(address organizer) external view returns (address[] memory)
organizer
address
required
Address of the event organizer
return
address[]
Array of event contract addresses owned by the organizer
Example:
const events = await factory.getOrganizerEvents(organizerAddress);
console.log(`Organizer has created ${events.length} events:`);
events.forEach((addr, i) => {
  console.log(`${i + 1}. ${addr}`);
});

Get Implementation Address

Retrieve the implementation contract address.
function getImplementation() external view returns (address)
return
address
Address of the EventTicket implementation contract
Example:
JavaScript
const implementation = await factory.getImplementation();
console.log('Implementation contract:', implementation);

Administrative Functions

Set Platform Fee

Update the platform fee percentage (owner only).
function setPlatformFee(uint256 newFeeBps) external onlyOwner
newFeeBps
uint256
required
New platform fee in basis points (max: 1000 = 10%)
Requirements:
  • Caller must be contract owner
  • newFeeBps must be ≤ 1000 (10%)
Example:
// Set platform fee to 3% (300 basis points)
const tx = await factory.setPlatformFee(300);
await tx.wait();
Events Emitted:
event PlatformFeeUpdated(uint256 oldFee, uint256 newFee);
Fee changes only apply to newly created events. Existing event contracts retain their original fee structure.

Set Platform Fee Recipient

Update the address that receives platform fees (owner only).
function setPlatformFeeRecipient(address newRecipient) external onlyOwner
newRecipient
address
required
New platform fee recipient address (cannot be zero address)
Requirements:
  • Caller must be contract owner
  • newRecipient cannot be zero address
Example:
JavaScript
const tx = await factory.setPlatformFeeRecipient(newRecipientAddress);
await tx.wait();
Events Emitted:
event PlatformFeeRecipientUpdated(address oldRecipient, address newRecipient);
Recipient changes only apply to newly created events. Existing event contracts send fees to their original recipient.

Events

EventContractDeployed
event
event EventContractDeployed(
    uint256 indexed eventId,
    address indexed organizer,
    address contractAddress,
    string eventName,
    uint256 totalSupply
);
Emitted when a new event contract is deployed
PlatformFeeUpdated
event
event PlatformFeeUpdated(uint256 oldFee, uint256 newFee);
Emitted when platform fee is changed
PlatformFeeRecipientUpdated
event
event PlatformFeeRecipientUpdated(address oldRecipient, address newRecipient);
Emitted when platform fee recipient is changed

Gas Comparison

Here’s a comparison of gas costs between minimal proxy deployment and full contract deployment:
Factory Deployment: ~2,100,000 gas (~$1.50)
Per Event Clone: ~180,000 gas (~$0.10)

Total for 10 events: ~3,900,000 gas (~$2.70)
Gas costs calculated at 30 gwei gas price and $0.70 MATIC. Actual costs vary with network conditions.

Integration Example

const { ethers } = require('ethers');

// Connect to factory
const provider = new ethers.JsonRpcProvider('https://polygon-rpc.com');
const signer = new ethers.Wallet(privateKey, provider);
const factory = new ethers.Contract(factoryAddress, factoryABI, signer);

// Create multiple events
const events = [
  {
    name: 'Tech Conference 2024',
    symbol: 'TECH24',
    supply: 500,
    price: ethers.parseEther('0.1')
  },
  {
    name: 'Music Festival',
    symbol: 'MUSIC24',
    supply: 2000,
    price: ethers.parseEther('0.05')
  }
];

const now = Math.floor(Date.now() / 1000);

for (const event of events) {
  const tx = await factory.createEvent(
    event.name,
    event.symbol,
    event.supply,
    event.price,
    now,
    now + (7 * 24 * 60 * 60),
    now + (30 * 24 * 60 * 60),
    `https://api.example.com/${event.symbol}/`
  );
  
  const receipt = await tx.wait();
  const deployedEvent = receipt.logs.find(
    log => log.eventName === 'EventContractDeployed'
  );
  
  console.log(`${event.name} deployed to: ${deployedEvent.args.contractAddress}`);
}

// Query all events for this organizer
const organizerEvents = await factory.getOrganizerEvents(signer.address);
console.log(`Total events created: ${organizerEvents.length}`);

Event Discovery

Monitor the factory contract for new event deployments:
const factory = new ethers.Contract(factoryAddress, factoryABI, provider);

// Listen for new events
factory.on('EventContractDeployed', (eventId, organizer, contractAddress, eventName, totalSupply) => {
  console.log('New Event Created!');
  console.log('Event ID:', eventId.toString());
  console.log('Organizer:', organizer);
  console.log('Contract:', contractAddress);
  console.log('Name:', eventName);
  console.log('Tickets:', totalSupply.toString());
});

// Query historical events
const filter = factory.filters.EventContractDeployed();
const events = await factory.queryFilter(filter, -10000); // Last 10k blocks

events.forEach(event => {
  console.log(`${event.args.eventName} at ${event.args.contractAddress}`);
});

Security Considerations

  • Reentrancy: Factory uses ReentrancyGuard on createEvent
  • Access Control: Only owner can modify fees and recipients
  • Immutable Implementation: Implementation address cannot be changed after deployment
  • Clone Initialization: Clones can only be initialized once

Best Practices

1

Test First

Always test event creation on a testnet before mainnet deployment
2

Validate Parameters

Verify all timestamps and parameters before calling createEvent
3

Store Addresses

Save deployed event contract addresses in your database
4

Monitor Events

Set up event listeners for real-time event tracking
5

Fee Management

Only update fees between major platform updates

EventTicket

The implementation contract that all events clone

Deployment Guide

How to deploy the factory contract

Build docs developers (and LLMs) love