A fully on-chain ticketing system built on Linera Protocol using the Linera SDK with royalty-aware marketplace, NFT tickets, and cross-chain transfers. Deployed and tested on Conway Testnet.
This project is built entirely with the Linera SDK:
// src/contract.rs - Hub-and-Spoke Cross-Chain Ticketing Contract
use linera_sdk::{
linera_base_types::{ChainId, DataBlobHash, StreamUpdate, WithContractAbi},
views::{RootView, View},
Contract, ContractRuntime,
};
impl Contract for TicketingContract {
type Message = Message;
type InstantiationArgument = ();
type Parameters = ApplicationParameters;
type EventValue = StreamEvent;
async fn execute_operation(&mut self, operation: Operation) -> Self::Response { ... }
async fn execute_message(&mut self, message: Message) -> Self::Response { ... }
}// src/service.rs - GraphQL API for queries and mutations
use async_graphql::{EmptySubscription, Object, Request, Response, Schema};
use linera_sdk::{Service, ServiceRuntime, views::View};
impl Service for TicketingService {
type Parameters = ();
async fn new(runtime: ServiceRuntime<Self>) -> Self { ... }
async fn handle_query(&self, request: Request) -> Response { ... }
}// web-client/src/contexts/WalletContext.jsx - Multi-wallet support
import { initialize, Client, Faucet, signer } from '@linera/client';
// Initialize WASM (auto-resolves path)
await initialize();
const privateKeySigner = signer.PrivateKey.fromMnemonic(mnemonic);
const ownerAddress = privateKeySigner.address();
// Connect to Conway Faucet and claim chain
const faucet = new Faucet('https://faucet.testnet-conway.linera.net/');
const lineraWallet = await faucet.createWallet();
const claimedChainId = await faucet.claimChain(lineraWallet, ownerAddress);
// Create Linera client for blockchain operations
const client = await new Client(lineraWallet, privateKeySigner, false);
const app = await client.application(applicationId);Key SDK Features Used:
linera_sdk::Contract- Smart contract logic with message handlinglinera_sdk::Service- GraphQL API service with async-graphqllinera_views- Persistent state (MapView, RegisterView, SetView)linera_base_types- ChainId, DataBlobHash, StreamUpdate types- Cross-chain messaging via
runtime.send_message() - Event streaming via
runtime.emit()andMARKETPLACE_STREAM @linera/client- Web SDK (v0.15.11) for frontend blockchain interaction@linera/client/signer- Embedded wallet signing with mnemonic support
Connects to Conway Testnet via:
- Faucet:
https://faucet.testnet-conway.linera.net/ - Network Config: Downloaded from
https://faucet.testnet-conway.linera.net/network.json - Deployment:
linera publish-and-createwith marketplace chain parameters
Latest Deployment (Wave 6):
- Chain ID:
72f9d0af181a93b93aed812c8dbd12cba13d73cac273d05fc20391a9e7f9dbf3 - Application ID:
8fa9a02f7552969ad7c217418082becf0c04b4041de185e683e90894822918a1 - All mutations tested and working β
- Ticket history & provenance tracking β
src/lib.rsβ ABI definitions (events, tickets, listings, messages, stream events)src/contract.rsβ Hub-and-spoke contract: events, mint, transfer, marketplace, cross-chain syncsrc/service.rsβ GraphQL service with async-graphql for queries/mutationssrc/state.rsβ Application state with MapView, RegisterView, SetViewweb-client/β React + Vite frontend with Apollo Client
- β Event Creation with customizable royalty system (0-25%)
- β NFT Ticket Minting with metadata and blob storage
- β Cross-Chain Transfers using Linera's microchain architecture
- β On-Chain Marketplace (list, buy, cancel with atomic transactions)
- β Automatic Royalty Distribution to event organizers
- β GraphQL API for all operations (async-graphql)
- β Modern React Frontend with Apollo Client
- β Conway Testnet Deployment with automatic retry logic (5 retries, 3s delays)
- β Multi-Wallet Support with MetaMask integration
- β Owner-Based Ticket Tracking for wallet-specific queries
- β Hub-and-Spoke Architecture with event stream synchronization
- β Event Discovery with search, filters, and sorting
- β Ticket History & Provenance tracking with ownership timeline
- β Transaction History in wallet modal
- β MetaMask Transaction Approval with user-friendly signing popups
- β Enhanced Search - Search by event name AND seat number in marketplace
- β Automated Service Startup - Reads marketplace chain from .env
- β Direct SDK Integration with graceful fallback to hub proxy
- Rust stable with
wasm32-unknown-unknowntarget:rustup target add wasm32-unknown-unknown - Linera CLI installed: Follow Linera installation guide
- Node.js 18+ and npm (for frontend)
# Deploy to Conway testnet (initializes wallet, builds, publishes)
./scripts/deploy.sh
# Start Linera service on marketplace chain (reads from .env)
./scripts/start-service.sh
# If you need to reset and start fresh:
./scripts/deploy.sh --clean# Step 1: Initialize wallet (delete existing if corrupted)
rm -rf ~/.config/linera
linera wallet init --faucet https://faucet.testnet-conway.linera.net
# Step 2: Get your chain ID
linera wallet show
# Step 3: Set default chain
linera wallet set-default YOUR_CHAIN_ID
# Step 4: Build WASM
cargo build --release --target wasm32-unknown-unknown
# Step 5: Publish and create application
linera publish-and-create \
target/wasm32-unknown-unknown/release/ticketing_contract.wasm \
target/wasm32-unknown-unknown/release/ticketing_service.wasm
# Step 6: Start Linera service on marketplace chain (Terminal 1)
# Option A: Use helper script (reads from .env)
./scripts/start-service.sh
# Option B: Manual command
linera wallet set-default YOUR_MARKETPLACE_CHAIN_ID
linera service --port 8080
# Step 7: Update frontend config and run (Terminal 2)
# Edit web-client/src/providers/GraphQLProvider.jsx with your Chain ID and App ID
cd web-client
npm install
npm run dev -- --host 0.0.0.0cd web-client
npm install
npm run dev -- --host 0.0.0.0Open http://localhost:5173 (or http://YOUR_SERVER_IP:5173 for remote access)
Error: "storage operation error"
rm -rf ~/.config/linera
linera wallet init --faucet https://faucet.testnet-conway.linera.netError: "client is not configured to propose on chain"
# Reset wallet and reinitialize
rm -rf ~/.config/linera
linera wallet init --faucet https://faucet.testnet-conway.linera.net
linera wallet set-default $(linera wallet show | grep -oP 'Chain ID:\s*\K[a-f0-9]+' | head -1)Error: "default chain requested but none set"
linera wallet set-default YOUR_CHAIN_IDFor local testing with single-validator network:
# Terminal 1: Start local network
linera net up --with-faucet
# Terminal 2: Deploy application
cargo build --release --target wasm32-unknown-unknown
linera publish-and-create \
target/wasm32-unknown-unknown/release/ticketing_contract.wasm \
target/wasm32-unknown-unknown/release/ticketing_service.wasm
# Terminal 3: Start service
linera service --port 8080
# Terminal 4: Run frontend
cd web-client && npm run devThe contract uses Linera's microchain architecture for:
- Instant finality: Transactions complete in milliseconds
- Cross-chain messaging: Transfer tickets between chains atomically
- Event-driven design: Subscribe to ticket transfers, marketplace events, and royalty payments
- State isolation: Each user can have their own microchain for privacy
-
Create Event (Organizer only)
- Set ticket supply, price, royalty percentage
- Metadata stored on-chain
- Automatic NFT ID generation
-
Mint Ticket (Anyone)
- Pay event price
- Receive unique NFT ticket ID
- Instant ownership verification
-
Transfer Ticket (Cross-chain)
- Send ticket to any Linera account
- Automatic state update on both chains
- Maintains ownership history
-
List on Marketplace (Ticket owner)
- Set asking price
- Atomic listing creation
- Cancel anytime before sale
-
Buy from Marketplace (Anyone)
- Instant purchase
- Automatic royalty distribution to organizer
- Atomic ownership transfer
Multi-account marketplace testing via CLI:
# Simulate different users buying/selling
bash scripts/simulate-buyer.shThe contract uses a hub-and-spoke architecture:
- Hub Chain: Stores all shared data (events, listings, ticket references)
- User Chains: Forward operations to hub via cross-chain messages
- Event Streaming: Hub emits events that user chains subscribe to for sync
For Production: Each user claims their own chain via faucet, operations route through hub for marketplace consistency.
- Project: Ticketh - Decentralized NFT Ticketing Platform
- Developer: PhiBao
- Discord: kiter99
- Wallet Address:
0x86E95581E41946ED84956433a8a9c836bCbA636c - GitHub: https://github.com/PhiBao/
- Submission: Linera Wave 5
- Network: Conway Testnet
- Chain ID:
72f9d0af181a93b93aed812c8dbd12cba13d73cac273d05fc20391a9e7f9dbf3 - Application ID:
6c15a3503c97265c6de4a3a5cc28d2074f4c45cea4880ced82132443b96768fb - Service Port: 8080 (local)
- Frontend Port: 5173 (local)
- CHANGELOG.md - Complete feature list and version history
This project demonstrates several Linera SDK capabilities:
- Hub-and-Spoke Architecture: Central hub chain with user chains forwarding operations
- Cross-Chain Messaging:
runtime.send_message()for ticket transfers and marketplace ops - Event Streaming:
runtime.emit()withMARKETPLACE_STREAMfor state synchronization - GraphQL Service: async-graphql integration with
linera_sdk::Service - Persistent State: MapView, RegisterView, SetView from
linera_views - Blob Storage: DataBlobHash for ticket metadata references
- Owner-Based Permissions: Wallet address tracking for ownership verification
- Atomic State Updates: Marketplace operations are all-or-nothing
- Testnet Timestamp Issues: Conway validators have clock drift (mitigated by 5-retry logic with 3s delays)
- Faucet Dependency: New users need faucet to claim chains
- Future Enhancement: See CHANGELOG.md for Wave 6/7 roadmap