Permits & Tickets

Permits are the on-chain representation of tickets in TIX Protocol. Each permit is a unique, verifiable record of ticket ownership.

Permit Structure

struct Permit {
    owner: Pubkey,      // Current ticket holder's wallet
    status: u8,         // 0=Active, 1=Used, 2=Void
    version: u64,       // Increments on every change
    ticket_id: u64,     // Unique identifier within event
    padding: [u8; 16],  // Reserved for listing lock data
}

Status Values

Status
Value
Description
Allowed Actions

Active

0

Ticket is valid

Transfer, list, resell

Used

1

Ticket has been redeemed

None (terminal state)

Void

2

Ticket has been cancelled

None (terminal state)

Paged Storage

Permits are stored in contiguous vectors within PermitPage accounts, minimizing account creation cost.

Storage Layout

Page Calculation

Page Initialization

On first write to a page, the program allocates a vector of 128 default permits:

Version Tracking

Every permit has a version number that increments on any mutation:

Operation
Version Increment

issue_permit

0 → 1

transfer_permit

+1

mark_used

+1

mark_void

+1

accept_offer

+1

Purpose

Version tracking prevents stale operations:

  1. Listing Version Mismatch — When a listing is created, it stores the current permit version. If the permit changes before purchase, the sale is rejected.

  2. Race Condition Prevention — Concurrent transfer attempts are serialized by version checks.

Listing Locks

When a ticket is listed for sale, the permit is "locked" to prevent simultaneous transfers.

Lock Mechanism

The permit's padding area stores lock information:

Byte Range
Purpose

padding[0]

Lock flag (0=unlocked, 1=locked)

padding[1..9]

Expiry timestamp (i64 little-endian)

Lock Flow

Expiry Handling

If a listing expires:

  • transfer_permit can proceed (checks expiry timestamp)

  • accept_offer will fail with ListingExpired

  • cancel_listing can clean up the stale listing

Guard Rails

TicketAlreadyMinted

Prevents re-issuing the same ticket slot:

SupplyExceeded

Prevents minting beyond event capacity:

PermitNotActive

Blocks operations on used/void tickets:

ListingActive

Prevents transfers while listing is active:

Best Practices

For Integrators

  1. Batch Issuance — Issue multiple tickets in a single transaction when possible

  2. Page Pre-allocation — Consider ticket ID assignment in your own database to minimize page creation

  3. Version Caching — Store permit versions when creating listings

For Clients

  1. Always Fetch Fresh — Get latest permit state before operations

  2. Handle Version Errors — Retry with updated version on mismatch

  3. Check Lock Status — Verify permit isn't locked before transfer attempts

Last updated