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 ~1 − 2 i n s t e a d o f 1-2 instead of ~ 1 − 2 in s t e a d o f 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
363 d3d373d3d3d363d73{implementation} 5 af43d82803e903d91602b57fd5bf3
Implementation Contract : Deploy once with full bytecode
Clone Contract : Deploy lightweight proxy that delegates to implementation
Initialization : Each clone maintains its own storage
Cost : Clone deployment costs ~45 bytes of bytecode vs full contract (~10-20kb)
Contract State
Immutable Variables
The implementation contract address for EventTicket (set in constructor, immutable)
Public Variables
Platform fee in basis points (default: 250 = 2.5%, max: 1000 = 10%)
Address that receives platform fees from all events
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)
Initial platform fee recipient address
What happens in constructor:
Deploys a new EventTicket implementation contract
Stores the implementation address as immutable
Sets the platform fee recipient
Sets platformFeeBps to default 250 (2.5%)
Example:
// Deploy factory with platform fee recipient
EventTicketFactory factory = new EventTicketFactory ( 0 xPlatformAddress);
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 )
Name of the event (e.g., “DevCon 2024”)
Symbol for the NFT tickets (e.g., “DEVCON24”)
Maximum number of tickets (1-10,000)
Unix timestamp when ticket sales begin
Unix timestamp when ticket sales end (must be before eventDate)
Unix timestamp of the actual event
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:
Generates a unique event ID
Deploys a minimal proxy clone of the implementation
Initializes the clone with provided parameters
Stores the contract address in mappings
Emits EventContractDeployed event
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 )
Address of the event organizer
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 )
Address of the EventTicket implementation contract
Example:
const implementation = await factory . getImplementation ();
console . log ( 'Implementation contract:' , implementation );
Administrative Functions
Update the platform fee percentage (owner only).
function setPlatformFee ( uint256 newFeeBps ) external onlyOwner
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.
Update the address that receives platform fees (owner only).
function setPlatformFeeRecipient ( address newRecipient ) external onlyOwner
New platform fee recipient address (cannot be zero address)
Requirements:
Caller must be contract owner
newRecipient cannot be zero address
Example:
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
event EventContractDeployed (
uint256 indexed eventId ,
address indexed organizer ,
address contractAddress ,
string eventName ,
uint256 totalSupply
);
Emitted when a new event contract is deployed
eventId: Unique ID for the event
organizer: Address that created the event
contractAddress: Address of the deployed EventTicket clone
eventName: Name of the event
totalSupply: Maximum tickets for this event
event PlatformFeeUpdated ( uint256 oldFee , uint256 newFee );
Emitted when platform fee is changed
PlatformFeeRecipientUpdated
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:
Minimal Proxy (Clone)
Full Contract
Savings
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)
Per Event Deployment: ~2,800,000 gas (~$1.95)
Total for 10 events: ~28,000,000 gas (~$19.50)
Gas Saved per Event: ~2,620,000 gas
Percentage Saved: ~93%
Cost per Event:
- Full: ~$1.95
- Clone: ~$0.10
💰 Savings: ~$1.85 per event
Gas costs calculated at 30 gwei gas price and $0.70 MATIC. Actual costs vary with network conditions.
Integration Example
Complete Integration
Complete Integration
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:
Event Monitoring
Event Monitoring
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
Test First
Always test event creation on a testnet before mainnet deployment
Validate Parameters
Verify all timestamps and parameters before calling createEvent
Store Addresses
Save deployed event contract addresses in your database
Monitor Events
Set up event listeners for real-time event tracking
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