Documentation Index
Fetch the complete documentation index at: https://docs.haus25.live/llms.txt
Use this file to discover all available pages before exploring further.
LiveTipping Contract
Real-Time Payment Processing
LiveTipping manages the competitive tipping mechanism that determines NFT ownership:
contract LiveTipping is Initializable, OwnableUpgradeable, UUPSUpgradeable,
ReentrancyGuardUpgradeable, PausableUpgradeable {
struct EventTippingData {
address creator;
uint256 startDate;
uint256 endDate;
uint256 reservePrice;
uint256 totalTips;
address highestTipper;
uint256 highestTip;
bool finalized;
mapping(address => uint256) tips;
mapping(address => uint256) tipCounts;
address[] tippers;
}
}
Tipping Competition Mechanics
Tip Processing Logic
function sendTip(uint256 eventId, string memory message)
external payable eventExists(eventId) eventInProgress(eventId)
nonReentrant whenNotPaused
{
if (msg.value == 0) revert InvalidTipAmount();
EventTippingData storage eventData = eventTipping[eventId];
// Record individual tip
uint256 tipId = eventTips[eventId].length;
eventTips[eventId].push(TipData({
tipper: msg.sender,
amount: msg.value,
timestamp: block.timestamp,
message: message
}));
// Update tipper's total
if (eventData.tips[msg.sender] == 0) {
eventData.tippers.push(msg.sender);
}
eventData.tips[msg.sender] += msg.value;
eventData.totalTips += msg.value;
// Check for new highest tipper
if (eventData.tips[msg.sender] > eventData.highestTip) {
address previousHighest = eventData.highestTipper;
eventData.highestTipper = msg.sender;
eventData.highestTip = eventData.tips[msg.sender];
emit NewHighestTipper(eventId, previousHighest, msg.sender, eventData.tips[msg.sender]);
}
emit TipReceived(eventId, msg.sender, msg.value, message, tipId);
}
Reserve Price Validation
LiveTipping enforces reserve price requirements for NFT transfers:
function finalizeEvent(uint256 eventId)
external eventExists(eventId) onlyEventCreator(eventId) nonReentrant
returns (address highestTipper, uint256 totalTips, bool reservePriceMet)
{
EventTippingData storage eventData = eventTipping[eventId];
if (block.timestamp < eventData.endDate) revert EventNotEnded();
totalTips = eventData.totalTips;
reservePriceMet = totalTips >= eventData.reservePrice;
// If reserve price not met, NFT stays with creator
highestTipper = reservePriceMet ? eventData.highestTipper : eventData.creator;
eventData.finalized = true;
emit EventFinalized(eventId, highestTipper, totalTips, reservePriceMet);
}
Distributor Contract
Revenue Sharing Engine
The Distributor manages complex revenue distribution across multiple stakeholders:
contract Distributor is Initializable, OwnableUpgradeable, UUPSUpgradeable,
ReentrancyGuardUpgradeable, PausableUpgradeable {
enum CurationScope {
NONE, // 0% - No curation
SCOPE_1, // 3% - Planner
SCOPE_2, // 7% - Promoter
SCOPE_3 // 10% - Producer
}
struct EventDistributionConfig {
uint256 creatorShare; // 8500 (85%) - base creator share
uint256 treasuryShare; // 1500 (15%) - fixed treasury share
uint256 curationFee; // 0-1000 (0-10%) - from creator portion
CurationScope curationScope;
address curator;
bool curationEnabled;
}
}
Distribution Algorithm
function distributeTips(uint256 eventId)
external payable eventExists(eventId) nonReentrant whenNotPaused
{
EventDistributionConfig storage config = eventConfigs[eventId];
uint256 amountToDistribute = msg.value;
// Calculate base distribution (85% creator, 15% treasury)
uint256 creatorAmount = (amountToDistribute * 8500) / 10000;
uint256 treasuryAmount = amountToDistribute - creatorAmount;
uint256 curationAmount = 0;
// Apply curation if enabled (deducted from treasury portion)
if (config.curationEnabled && config.curationFee > 0) {
curationAmount = (amountToDistribute * config.curationFee) / 10000;
treasuryAmount -= curationAmount;
}
// Execute distributions
IEventFactory.EventData memory eventData = IEventFactory(eventFactoryAddress).getEvent(eventId);
address eventOwner = eventData.creator;
_distributeFunds(eventId, eventOwner, creatorAmount, "creator");
_distributeFunds(eventId, treasury, treasuryAmount, "treasury");
if (curationAmount > 0) {
_distributeFunds(eventId, config.curator, curationAmount, "curation");
}
}
Curation Integration
The Distributor automatically configures curation based on deployed Curation contracts:
function enableCurationFromContract(
uint256 eventId,
address _curationContract
) external onlyEventFactory {
ICurationMinimal curation = ICurationMinimal(_curationContract);
uint256 scope = curation.getCurationScope();
uint256 fee = curation.getCuratorFee();
address curator = curation.getCurator();
// Map scope to standardized fee rates
CurationScope curationScope;
if (scope == 1) curationScope = CurationScope.SCOPE_1; // 3%
else if (scope == 2) curationScope = CurationScope.SCOPE_2; // 7%
else if (scope == 3) curationScope = CurationScope.SCOPE_3; // 10%
EventDistributionConfig storage config = eventConfigs[eventId];
config.curationFee = fee;
config.curationScope = curationScope;
config.curator = curator;
config.curationEnabled = true;
}
CreationWrapper Contract
Atomic Operations Utility
CreationWrapper enables batched operations for improved user experience:
contract CreationWrapper {
IEventFactory public immutable eventFactory;
IEventManager public immutable eventManager;
function createEventAndDelegate(
// Event creation parameters
uint256 startDate, uint256 eventDuration, uint256 reservePrice,
string calldata metadataURI, string calldata artCategory,
uint256 ticketsAmount, uint256 ticketPrice,
// Delegation parameter
address delegatee
) external {
// 1. Create event
uint256 newEventId = eventFactory.createEventForCreator(
msg.sender, startDate, eventDuration, reservePrice,
metadataURI, artCategory, ticketsAmount, ticketPrice
);
// 2. Setup delegation if specified
if (delegatee != address(0)) {
eventManager.createDelegationProxyForUser(newEventId, msg.sender, delegatee);
}
}
}
Gas Savings:
- Combines two transactions into one
- Reduces total gas costs by ~21,000 (one transaction overhead)
- Improves UX by eliminating intermediate transaction confirmations
EventFactoryLib Library
Deployment Logic Separation
EventFactoryLib contains CREATE2 deployment functions to keep EventFactory under size limits:
library EventFactoryLib {
function deployTicketKiosk(...) external returns (address) {
bytes memory bytecode = abi.encodePacked(
type(TicketKiosk).creationCode,
abi.encode(eventId, factoryAddress, creator, ticketsAmount, ticketPrice, artCategory, treasuryReceiver)
);
bytes32 salt = keccak256(abi.encodePacked(eventId, "ticketkiosk"));
return Create2.deploy(0, salt, bytecode);
}
function deployCuration(...) external returns (address) {
bytes memory bytecode = abi.encodePacked(
type(Curation).creationCode,
abi.encode(eventId, factoryAddress, creator, creator, defaultFee, scope, description, distributorContract)
);
bytes32 salt = keccak256(abi.encodePacked(eventId, "curation"));
return Create2.deploy(0, salt, bytecode);
}
function registerWithExternalContracts(...) external {
IDistributor(distributorContract).registerEvent(eventId, creator);
ILiveTipping(liveTippingContract).registerEvent(eventId, creator, startDate, eventDuration, reservePrice);
}
}
Integration Patterns
Cross-Contract Communication
Event State Synchronization
All contracts maintain synchronized event state through EventFactory queries:
// Common pattern across all supporting contracts
function validateEvent(uint256 eventId) internal view {
IEventFactory.EventData memory eventData = IEventFactory(eventFactoryAddress).getEvent(eventId);
require(eventData.creator != address(0), "Event not registered");
}
Gas Optimization Features
Batch Operations
LiveTipping:
// Theoretical batch tipping function
function batchSendTips(
uint256[] calldata eventIds,
string[] calldata messages
) external payable {
require(eventIds.length == messages.length && eventIds.length == amounts.length);
for (uint256 i = 0; i < eventIds.length; i++) {
// Process individual tips
}
}
EventStation:
function withdrawAllBalances() external nonReentrant {
uint256[] memory events = operatorEvents[msg.sender];
uint256 totalAmount = 0;
for (uint256 i = 0; i < events.length; i++) {
uint256 amount = pendingBalances[events[i]][msg.sender];
if (amount > 0) {
totalAmount += amount;
pendingBalances[events[i]][msg.sender] = 0;
}
}
(bool success, ) = msg.sender.call{value: totalAmount}("");
require(success, "Transfer failed");
}
Storage Optimization
Packed Structs:
// Optimized EventTippingData storage layout
struct EventTippingData {
address creator; // 20 bytes (slot 0)
uint96 reservePrice; // 12 bytes (slot 0)
uint128 startDate; // 16 bytes (slot 1)
uint128 endDate; // 16 bytes (slot 1)
uint256 totalTips; // 32 bytes (slot 2)
address highestTipper; // 20 bytes (slot 3)
uint96 highestTip; // 12 bytes (slot 3)
bool finalized; // 1 byte (slot 3)
// 3 storage slots saved per event
}
Design Advantages
Specialized Functionality
- LiveTipping: Optimized for high-frequency, small-value transactions
- Distributor: Handles complex revenue calculations and multi-party payments
- CreationWrapper: Improves user experience through transaction batching
Modular Integration
- Loose Coupling: Contracts can be upgraded independently
- Clear Interfaces: Well-defined interaction patterns
- Event-Driven: Comprehensive event logging for off-chain integration
Upgrade Flexibility
- UUPS Pattern: Core contracts can evolve with protocol needs
- Interface Stability: External integrations remain compatible
- Migration Support: Old contract state can be preserved during upgrades
- Emergency Controls: Pause and emergency withdrawal capabilities
The supporting contracts in the RTA protocol provide specialized functionality while maintaining the modular architecture’s flexibility and upgrade capabilities.