Swix

Overview
Swix is a ticketing platform where resale rules are enforced by the contract rather than by policy. A ticket cannot be listed above a set percentage of its face value, and a share of every resale routes to the promoter automatically. There is no policy to appeal to. The transaction reverts.
Still in progress. Both contracts are deployed and working on Base Sepolia, the test network for Base, with a live marketplace frontend on top of them where fans sign in with email through Privy instead of a crypto wallet. Card payments are what I would want solved before calling it real. A Stripe backend for them is started but not finished.
The transfer restriction
Once a ticket can move directly between wallets, every other rule becomes optional. A price cap that can be routed around is not a cap.
TicketNFT overrides _update(), the function every mint, transfer, and burn
passes through in OpenZeppelin v5, and blocks transfers between two real
addresses unless the marketplace contract is the caller. Minting and burning
pass through untouched. Wallet-to-wallet transfers revert. The marketplace
becomes the only path a ticket can take between owners, which is what gives
every other rule its authority.
What the marketplace enforces
Listing a ticket verifies ownership, confirms the ticket has not already been scanned at a gate, and rejects any price above 120% of that ticket's face value. Face value is stored per ticket at mint, so the ceiling is calculated per ticket rather than per event. A $50 general admission and a $100 VIP each carry their own cap with no additional bookkeeping.
Purchase splits payment in the same transaction that transfers the ticket, 5% to the promoter and the remainder to the seller. No invoicing and no payout delay, because the split is not a separate step.
Both percentages are constants, fixed at 120 and 5 across every event. That is the clearest limitation in the design as it stands. Making them per-event parameters set at deployment is the obvious next change, and it would let one contract serve an artist who wants no resale alongside one who wants an active secondary market.
Decisions that came from getting it wrong first
Several choices in these contracts exist because the obvious version had a problem.
The listing is deleted before any tokens or funds move. Transferring first
leaves a window for a malicious contract to call back into buyTicket before
the first call completes and drain the sale.
Payouts use call rather than transfer. Solidity's transfer forwards a
fixed 2300 gas stipend that fails silently when the recipient is a contract
wallet rather than a plain address, which promoters increasingly are.
Approval is checked at listing rather than at purchase. It could wait, but then the failure lands on the buyer for something the seller neglected to do. The error belongs with the person who can fix it.
Batch minting validates every array length before writing state, so a mismatched upload reverts whole instead of leaving an event half minted.
Thirty-eight tests cover the two contracts. The run I trusted most was the end-to-end test on Sepolia: mint a ticket, list it, buy it from a second wallet, confirm where the funds landed. The royalty split came out exact at 0.0005 ETH to the promoter and 0.0095 to the seller on a 0.01 ETH sale.
What I learned
- I deployed TicketNFT before realizing it had no way to reference the marketplace contract I wrote afterward. Live contracts cannot be patched, so the fix was a fresh deployment and a full migration to the new address. I now spend considerably longer on an interface before it goes on chain.
- Minting, gate scanning, and administration are three distinct powers. Collapsing them into a single owner address would have given a gate scanner the ability to mint tickets.
- The blockchain layer is the part a user should never see. Every decision since has been about hiding it, with prices shown in dollars rather than ETH and email login and card payments so a fan never learns what a wallet is.
- Gas costs determine the architecture. Fees on a $30 ticket can approach the price of the ticket on Ethereum mainnet, which pushed the project toward a cheaper chain before much else was settled.
Status
Both contracts are live on Base Sepolia, and swixtickets.com runs against them:
- TicketNFT, 0x59bf81aFD460FEAB71a1Fc7E1C13534e0146AF52
- Marketplace, 0x7B91647dDe9481cBa003822360fF69D216Ecf170
The Marketplace source is verified and readable. TicketNFT is not verified yet, so its page shows compiled bytecode rather than Solidity.
The same two contracts, unchanged, were first deployed to Ethereum Sepolia, where the end-to-end test above ran:
- TicketNFT, 0x8596E3bf2599b213524bC2F9c8B7E99bf1b5210B
- Marketplace, 0x8dDbB81679Fa374D019785c248e3477230CfA096
I moved to Base because gas on a $30 ticket has to round to nothing, and Base Sepolia mirrors Base mainnet, so launching there is a configuration change rather than a rewrite.
Next: finish card payments, per-event cap and royalty configuration, the move to Base mainnet, and a small pilot event to find out what breaks when the users are not me.


