This directory contains the Soroban smart contracts for Agora's on-chain event and ticketing flow.
This project uses a Rust cargo workspace, not Scarb. Build, test, and package management all run through the files in this directory:
Cargo.toml: workspace definition for the Soroban contractscontracts/event_registry: event lifecycle, organizer controls, inventory, loyalty, staking, and governancecontracts/ticket_payment: ticket purchases, escrow, refunds, settlement, transfers, auctions, and payment-side governancescripts/deploy_devnet.sh: deploys or upgrades both contracts on Stellar testnet/devnet-style environmentsscripts/generate_coverage.sh: generates coverage artifacts for theticket-paymentcrate
event_registry is the source of truth for event state. It stores event metadata, tier configuration, organizer ownership, inventory counters, promo settings, scanner permissions, loyalty profiles, staking records, and multi-admin governance proposals.
Key storage keys in contracts/event_registry/src/types.rs:
Event(event_id): fullEventInforecord for an eventOrganizerEvent,OrganizerEventShard,OrganizerEventCount: organizer-to-event indexes using sharded storageEventReceipt,OrganizerReceipt*: lightweight archived-event historyPlatformWallet,PlatformFee,TicketPaymentContract,Initialized: core contract configurationMultiSigConfig,ProposalCounter,Proposal,ActiveProposals: admin governance stateSeries,SeriesPass,HolderSeriesPass,SeriesEvent: series and season-pass supportBlacklistedOrganizer,BlacklistLog: organizer moderation and audit trailAuthorizedScanner: per-event scanner authorizationGuestProfile: loyalty tracking for attendeesOrganizerStake,MinStakeAmount,StakingToken,TotalStaked,StakersList: organizer staking and verificationTokenWhitelist,GlobalPromoBps,PromoExpiry,GlobalEventCount,GlobalActiveEventCount,GlobalTicketsSold: platform-wide policy and aggregate counters
Main public functions in contracts/event_registry/src/lib.rs:
initialize(admin, platform_wallet, platform_fee_percent, usdc_token): one-time setup; stores admin config and whitelists the initial payment tokenget_version()/version(): contract version helpersregister_event(args): creates a new event with metadata, tier map, supply limits, refund settings, and optional sales-goal configurationget_event(event_id): returns the currentEventInfoget_event_payment_info(event_id): returns payment-facing config such as payment wallet, fee settings, and tiersupdate_event_status(event_id, is_active): toggles whether an event is accepting paymentscancel_event(event_id): permanently cancels an eventarchive_event(event_id): removes full event state and retains a minimal receipt for historical lookupupdate_metadata(event_id, new_metadata_cid): updates event metadata CIDstore_event(event_info): internal-style public entrypoint used to persist event stateget_organizer_address(event_id): returns the organizer for an eventget_total_tickets_sold(event_id): returns sold inventory for an eventget_managed_events_count(): total events ever registeredget_active_events_count(): total currently active eventsget_global_tickets_sold(): aggregate platform ticket salesevent_exists(event_id): quick existence checkget_organizer_events(organizer): returns event IDs owned by an organizerget_organizer_receipts(organizer): returns archived event receipts for an organizerset_platform_fee(new_fee_percent)/get_platform_fee(): manage the default platform feeset_custom_event_fee(event_id, custom_fee_bps): set or clear a per-event fee overrideget_admin()/set_admin(new_admin): legacy single-admin getter/setter retained alongside multisig supportget_platform_wallet(): returns the fee-collection walletset_ticket_payment_contract(ticket_payment_address)/get_ticket_payment_contract(): links the payment contract allowed to mutate inventoryincrement_inventory(event_id, tier_id, quantity): increases ticket counters after successful purchasesdecrement_inventory(event_id, tier_id): decreases counters after refunds or reversalsregister_series(series_id, name, event_ids, organizer_address, metadata_cid): groups multiple events into a seriesget_series(series_id): fetches series metadataissue_series_pass(pass_id, series_id, holder, usage_limit, expires_at): mints a reusable series passget_series_pass(pass_id)/get_holder_series_pass(holder, series_id): retrieves series-pass recordsblacklist_organizer(organizer_address, reason)/remove_from_blacklist(organizer_address, reason): moderation controls for organizersis_organizer_blacklisted(organizer_address)/get_blacklist_audit_log(): moderation queriesset_global_promo(global_promo_bps, promo_expiry)/get_global_promo_bps()/get_promo_expiry(): global promo configurationpostpone_event(event_id, grace_period_end): marks an event as postponed and opens a refund grace periodauthorize_scanner(event_id, scanner)/is_scanner_authorized(event_id, scanner): scanner authorization used by ticket check-inset_staking_config(token, min_stake_amount): configures organizer stakingstake_collateral(organizer, amount)/unstake_collateral(organizer): manages organizer collateraldistribute_staker_rewards(caller, total_reward)/claim_staker_rewards(organizer): reward distribution and claims for stakersget_organizer_stake(organizer)/is_organizer_verified(organizer): staking status lookupsupdate_loyalty_score(caller, guest, tickets_purchased, amount_spent): updates attendee loyalty after ticket activityget_guest_profile(guest)/get_loyalty_discount_bps(guest): loyalty reads used by the payment contractget_multisig_config()/is_admin(address)/set_multisig_config(caller, admins, threshold): multisig configurationpropose_parameter_change(proposer, change, expiry_ledgers): generic governance proposal creationpropose_add_admin(proposer, admin, expiry_ledgers)/propose_remove_admin(proposer, admin, expiry_ledgers)/propose_set_threshold(proposer, threshold, expiry_ledgers)/propose_set_platform_wallet(proposer, wallet, expiry_ledgers): convenience governance proposal helpersapprove_proposal(approver, proposal_id)/execute_proposal(executor, proposal_id): multisig approval and executionget_proposal(proposal_id)/get_active_proposals(): governance queriesupgrade(new_wasm_hash): upgrades the contract code
ticket_payment handles the monetary side of the platform. It validates event payment settings against event_registry, processes purchases, keeps escrow balances, settles platform fees, supports refunds and transfers, and emits payment-centric events for indexers and off-chain services.
Key storage keys in contracts/ticket_payment/src/types.rs:
Payment(payment_id): full payment recordEventPayment*,BuyerPayment*,EventPaymentStatus*: sharded indexes for event, buyer, and status-based lookupsBalances(event_id): escrow and organizer/platform amounts for an eventAdmin,UsdcToken,PlatformWallet,EventRegistry,Initialized: base contract configurationTokenWhitelist,OracleAddress,SlippageBps: accepted assets and pricing controlsTransferFee(event_id): secondary transfer fee per eventBulkRefundIndex,PartialRefundIndex,PartialRefundPercentage,DisputeStatus(event_id),IsPaused: operational safety and refund stateTotalVolumeProcessed,TotalFeesCollected(token),ActiveEscrowTotal,ActiveEscrowByToken(token): protocol-wide accountingDiscountCodeHash,DiscountCodeUsed: discount-code registration and redemption trackingWithdrawalCap,DailyWithdrawalAmount: withdrawal throttlingHighestBid,AuctionClosed: auction stateGovernor,TotalGovernors,Proposal,ProposalCount: payment-side governance
Main responsibilities in contracts/ticket_payment/src/contract.rs:
- Initializes with admin, payment token, platform wallet, and linked
event_registrycontract - Processes ticket purchases and updates event inventory through
event_registry - Confirms payments and records transaction hashes
- Supports guest refunds, admin refunds, automatic refunds, bulk refunds, and partial refunds
- Tracks event escrow balances and organizer/platform settlement amounts
- Handles organizer withdrawals, platform fee settlement, revenue claims, and withdrawal caps
- Supports ticket check-in, transfers, resale fee controls, and event disputes
- Integrates optional price-oracle-based asset pricing and token whitelisting
- Supports tier auctions, bid placement, auction closeout, and governance proposals for contract parameters
Run commands from contract/.
cargo build --target wasm32-unknown-unknown --releasecargo testcargo test -p event-registry
cargo test -p ticket-paymentscripts/generate_coverage.sh expects cargo-llvm-cov to be installed and writes outputs under coverage/.
./scripts/generate_coverage.shThe provided deployment flow is in scripts/deploy_devnet.sh. It is a Bash script that:
- Loads environment variables from
.env.devnet - Builds the WASM artifacts with Cargo
- Deploys or upgrades
event_registry - Initializes
event_registry - Deploys or upgrades
ticket_payment - Initializes
ticket_payment - Links
ticket_paymentback intoevent_registryviaset_ticket_payment_contract
Required environment variables include:
SOROBAN_NETWORK_PASSPHRASESOROBAN_RPC_URLSOROBAN_ACCOUNT_SECRETADMIN_ADDRESSPLATFORM_WALLET
Optional deployment inputs:
USDC_TOKEN_ADDRESS: existing Stellar asset contract to use for payments; otherwise the script expects you to provide or deploy a mock token separatelyEVENT_REGISTRY_IDTICKET_PAYMENT_ID
Typical usage:
./scripts/deploy_devnet.shUpgrade existing deployments:
./scripts/deploy_devnet.sh --upgradeBoth contracts keep their actual env.storage() calls in storage.rs instead of scattering them across entrypoints.
storage.rsacts as a thin storage access layer around Soroban persistent storage- key creation is centralized through the
DataKeyenums in each contract'stypes.rs - large append-only lists are sharded to avoid oversized storage entries
- counters and indexes are updated together so read paths such as "get organizer events" or "get buyer payments" stay efficient
- the payment contract stores accounting state separately from registry state, while the registry remains authoritative for event metadata and inventory
Events are defined in each contract's events.rs and are emitted with Soroban topics so off-chain indexers can react to contract activity.
Defined in contracts/event_registry/src/events.rs:
ContractInitializedContractUpgradedEventRegisteredEventStatusUpdatedEventCancelledEventArchivedMetadataUpdatedFeeUpdatedInventoryIncrementedInventoryDecrementedOrganizerBlacklistedOrganizerRemovedFromBlacklistEventsSuspendedGlobalPromoUpdatedEventPostponedScannerAuthorizedGoalMetCollateralStakedCollateralUnstakedStakerRewardsDistributedStakerRewardsClaimedLoyaltyScoreUpdatedCustomFeeSet- admin/governance events including proposal creation, approval/execution, and admin updates
Defined in contracts/ticket_payment/src/events.rs:
ContractInitializedPaymentProcessedPaymentStatusChangedTicketTransferredPriceSwitchedBulkRefundProcessedPartialRefundProcessedDiscountCodeAppliedGlobalPromoAppliedRevenueClaimedFeeSettledContractPausedDisputeStatusChangedTicketCheckedInBidPlacedAuctionClosed- governance events for proposal creation, voting, and execution
ContractVerificationFailed
The contracts are intentionally split by responsibility:
event_registryowns event metadata, organizer policy, inventory truth, loyalty, and stakingticket_paymentowns funds movement, escrow accounting, refunds, fee settlement, and purchase lifecycleticket_paymentcalls intoevent_registryto read event payment settings and to increment or decrement inventory after payment state changes
When you open the PR for this documentation task, link the issue and include:
Closes #issue_number