Skip to content

solana-thailand/BeThere

Repository files navigation

BeThere — Solana-Powered Event Check-In

Turn every event into an on-chain experience.

Solana Rust Cloudflare Workers Tests

Free events have 30-40% no-show rates. BeThere fixes this with USDC deposit commitments — attendees get their money back when they show up, forfeit if they don't. Built on Solana for $0.001 NFT badges, $0.00087 on-chain costs, and < 500ms check-in at the edge.

🎯 The Problem → The Solution

Problem BeThere Solution
30-40% no-show rates for free events USDC deposit commitment — skin in the game
No on-chain proof of attendance Compressed NFT badges (cNFT) — 990x cheaper than POAP
Web2-only event tools Solana-native: deposits, refunds, NFTs all on-chain
Expensive NFT minting ($0.50/ea) cNFT on Solana: $0.001 per badge
ETH gas fees too high Solana: $0.00087 per transaction

🏗️ Stack

RustSolana (Quasar)Cloudflare WorkersLeptos WASMGoogle Sheets

100% Rust codebase — shared types from on-chain program → edge worker → WASM frontend. Zero serialization bugs.

📊 Key Numbers

Metric Value
On-chain program 63 KB (optimized)
NFT mint cost $0.001 per badge
Transaction cost $0.00087 (at $172/SOL)
Check-in latency < 500ms (edge worker)
Tests 68 passing (39 worker + 29 on-chain)
Program ID (devnet) C6HDeZES9aPpNwe3UvS9ecmfcRhH1XeJb8PGJmLG3z3T

🎮 Live Demo Flow (Devnet)

1. 📋 Organizer creates event → sets $5 USDC deposit
2. 📝 Attendee clicks "Reserve Spot" → auto-redirect to deposit page
3. 🪙 Attendee deposits USDC via Phantom wallet (Solana Pay QR)
   → Or uploads THB slip → auto-redirect to ticket/QR page
4. 📱 Staff scans QR at door → on-chain check-in
5. 💰 Attendee gets refund + compressed NFT badge
6. ❌ No-show? → Organizer claims forfeited deposit

Quick Start

# 1. Install prerequisites
cargo install wasm-bindgen-cli --version 0.2.100
cd worker && npm install && cd ..

# 2. Build frontend
cd frontend-leptos && trunk build && cd ..

# 3. Configure secrets (first time only)
cd worker
npx wrangler secret put JWT_SECRET
npx wrangler secret put GOOGLE_CLIENT_ID
npx wrangler secret put GOOGLE_CLIENT_SECRET
npx wrangler secret put GOOGLE_REDIRECT_URI
npx wrangler secret put GOOGLE_SERVICE_ACCOUNT_EMAIL
npx wrangler secret put GOOGLE_SERVICE_ACCOUNT_PRIVATE_KEY
npx wrangler secret put GOOGLE_SERVICE_ACCOUNT_TOKEN_URI
npx wrangler secret put GOOGLE_SHEET_ID
npx wrangler secret put STAFF_EMAILS
npx wrangler secret put SUPER_ADMIN_EMAILS

# Dev mode (optional — for local E2E testing without Google OAuth)
# ⚠️ NEVER enable in production!
echo 'DEV_MODE = "1"' >> worker/.dev.vars
echo 'DEV_EMAIL = "your-email@example.com"' >> worker/.dev.vars

# 4. Create KV namespaces (first time only)
npx wrangler kv namespace create EVENTS
npx wrangler kv namespace create EVENTS --preview
# Update wrangler.toml with returned IDs

# 5. Run locally
cd worker && ./deploy.sh dev

# 6. Seed first event (after server is running)
curl -X POST http://localhost:8787/api/events/seed -H "Cookie: session=<jwt>"

Open http://localhost:8787.

Note: deploy.sh automatically handles the Yarn PnP (~/.pnp.cjs) conflict with wrangler's esbuild bundler — no manual mv needed.

Workspace Structure

event-checkin/
├── domain/               — Shared types & logic (compiles x86_64 + wasm32)
├── worker/               — Cloudflare Worker (wasm32-unknown-unknown)
├── frontend-leptos/      — Leptos WASM frontend (standalone trunk build)
├── Cargo.toml            — Workspace root (members: domain, worker)
└── README.md

The domain/ crate contains shared types (Attendee, Claims, AppConfig), QR generation, and sheet row parsing. The worker/ crate consumes it, replacing reqwest with worker::Fetch and rsa/jsonwebtoken with V8 SubtleCrypto via wasm-bindgen.

Google Sheet Layout

The attendee sheet (tab name configurable via GOOGLE_SHEET_NAME, default "Attendees"):

Column Index Field Notes
A 0 api_id Unique ID (e.g. gst-abc123)
B 1 name Full name
C 2 first_name First name
D 3 last_name Last name
E 4 email Attendee email
F 5 ticket_name Ticket type
G 6 registration_date ISO 8601 registration date
H 7 approval_status Approval state
I 8 participation_type In-Person / Online
J 9 phone Phone number
K 10 contact_channel Telegram / Discord / etc.
L 11 contact_handle Contact username
M 12 deposit_agreed Yes/No
N 13 deposit_method USDC / THB / etc.
O 14 deposit_amount Deposit amount
P 15 deposit_tx_signature Transaction signature
Q 16 deposit_verified Deposit verification status
R 17 checked_in_at ISO 8601 timestamp
S 18 checked_in_by Staff email who checked in
T 19 solana_address Filled at claim time (attendee wallet)
U 20 qr_code_url QR code link
V 21 claim_token UUID generated at check-in (for NFT claim)
W 22 claimed_at Timestamp when NFT + refund claimed

A separate "staff" sheet tab (configurable via GOOGLE_STAFF_SHEET_NAME) holds authorized staff emails in column A (header in row 1, emails from row 2). This is unioned with the STAFF_EMAILS secret — a user is staff if their email appears in either source.

Deployment

# Build frontend (if changed)
cd frontend-leptos && trunk build && cd ..

# Deploy to Cloudflare Workers
cd worker && ./deploy.sh

The deploy.sh script handles the Yarn PnP conflict automatically. Alternatively, you can run npx wrangler deploy directly if you don't have ~/.pnp.cjs.

Non-secret vars are in worker/wrangler.toml [vars]:

Var Default Purpose
SERVER_URL https://event-checkin.workers.dev Public URL for OAuth redirect
GOOGLE_SHEET_NAME Attendees Attendee sheet tab name
GOOGLE_STAFF_SHEET_NAME staff Staff sheet tab name
EVENT_NAME (none) Default event name for seeding
ORGANIZER_EMAILS (none) Comma-separated organizer emails for seeding
SUPER_ADMIN_EMAILS (secret) Global admins who can create/manage all events

The frontend is served from frontend-leptos/dist/ via Workers Assets with SPA fallback.

API Endpoints

Auth & Users

Method Path Auth Description
GET /api/health No Health check
GET /api/auth/url No Google OAuth URL (optional ?redirect= param)
GET /api/auth/callback No OAuth callback, sets HttpOnly cookie, redirects based on role
POST /api/auth/logout No Clear session cookie
GET /api/auth/me Cookie Current user info + role (super_admin/organizer/staff/attendee)
GET /api/my-registration/{slug} Cookie Get signed-in user's registration for a specific event
GET /api/my-registrations Cookie List all registrations for the signed-in user

Public Event & Registration

Method Path Auth Description
GET /api/public/events No List upcoming active events (nearest first)
GET /api/public/event/{slug} No Get public event details (no auth required)
POST /api/public/register Cookie Register for event (email from JWT, not body)
GET /api/public/ticket/{id} No Get attendee ticket/QR slip details
GET /api/badge.svg No NFT badge SVG (by claim token)
GET /api/badge-hd.svg No NFT badge HD SVG (by claim token)
POST /api/waitlist No Join waitlist (email + use case)

Events (Admin)

Method Path Auth Description
GET /api/events Cookie List all events
POST /api/events Cookie + SuperAdmin Create new event
GET /api/events/{id} Cookie Get event config
PUT /api/events/{id} Cookie + SuperAdmin Update event config
DELETE /api/events/{id} Cookie + SuperAdmin Archive event
POST /api/events/{id}/restore Cookie + SuperAdmin Restore archived event to Draft
DELETE /api/events/{id}/delete Cookie + SuperAdmin Permanently delete event (?force=true for devnet cleanup)
POST /api/events/seed Cookie + SuperAdmin Seed event from env vars
POST /api/events/migrate Cookie + SuperAdmin Migrate quiz KV → event KV
GET /api/events/{id}/audit Cookie + Organizer Get audit trail for event
GET /api/audit/global Cookie + SuperAdmin Get system-wide audit trail

Attendees & Check-In

Method Path Auth Description
GET /api/attendees Cookie + Event Staff List all attendees + stats
GET /api/attendee/{id} Cookie + Event Staff Single attendee details
POST /api/checkin/{id} Cookie + Event Staff Check in attendee
POST /api/attendee/{id}/undo-checkin Cookie + Event Staff Undo check-in
POST /api/generate-qrs Cookie + Event Staff Generate QR codes
POST /api/admin/flush-cache Cookie + Staff Flush server-side caches

Walk-in Attendees

Method Path Auth Description
POST /api/walkin/register Cookie + Staff Register walk-in attendee (on-the-spot)
GET /api/walkin/list Cookie + Staff List walk-in attendees for event
GET /api/walkin/export Cookie + Staff Export walk-in attendees as CSV
POST /api/walkin/sync Cookie + Staff Sync walk-in attendees to Google Sheet

Quiz & Adventure

Method Path Auth Description
GET /api/quiz No Get quiz questions (public)
POST /api/quiz/{token}/submit No Submit quiz answers
GET /api/quiz/{token}/status No Get quiz progress
PUT /api/admin/quiz Cookie + Staff Create/update quiz config
GET /api/adventure/{token}/status No Get adventure progress
POST /api/adventure/{token}/save No Save adventure progress
GET /api/admin/adventure Cookie + Staff Get adventure config
PUT /api/admin/adventure Cookie + Staff Update adventure config

Claim & NFT

Method Path Auth Description
GET /api/claim/{token} No Get claim info
POST /api/claim/{token} No Claim NFT badge + refund

Deposits

Method Path Auth Description
GET /api/deposit/status/{attendee_id} No Check deposit status for attendee
POST /api/deposit/usdc No Build Solana Pay deposit TX (USDC)
GET /api/deposit/usdc/tx No Solana Pay TX callback (wallet fetches serialized TX)
GET /api/deposit/usdc/confirm No Poll deposit TX confirmation via Solana RPC
POST /api/deposit/usdc/webhook No Record TX signature, verify on-chain
POST /api/deposit/thb/upload No Upload PromptPay slip URL (THB)
GET /api/deposit/thb/pending Cookie + Staff List pending THB slips
POST /api/deposit/thb/verify Cookie + Staff Verify/reject THB slip

Escrow (On-Chain)

Method Path Auth Description
POST /api/escrow/init Cookie + Organizer Initialize on-chain escrow PDA + vault ATA (single TX)
POST /api/escrow/mark-checked-in Cookie + Organizer Mark attendee checked-in on-chain
POST /api/escrow/refund No Build refund TX for attendee's wallet to sign
POST /api/escrow/claim-forfeited Cookie + Organizer Claim forfeited deposit (no-show)
POST /api/escrow/deactivate-event Cookie + Organizer Build deactivate escrow TX
POST /api/escrow/close-event Cookie + Organizer Build close escrow TX (reclaim rent)
POST /api/escrow/close-deposit No Close individual deposit PDA (rent reclaim)
POST /api/escrow/backfill-wallets Cookie + Organizer Backfill wallet addresses from KV to on-chain
GET /api/escrow/events/{event_id} Cookie + Organizer Get on-chain escrow event data
POST /api/escrow/sync Cookie + Organizer Sync on-chain escrow events to KV cache
POST /api/escrow/onchain-webhook No Webhook for on-chain escrow events

Refunds & Cancellation

Method Path Auth Description
GET /api/refund/queue Cookie + Staff List pending refunds
POST /api/refund/mark/{id} Cookie + Staff Mark refund as completed
POST /api/refund/batch-thb Cookie + Staff Batch THB refund for event cancellation
GET /api/escrow/refund-queue Cookie + Staff USDC refund queue (cancellation workflow)
GET /api/escrow/cancel-status Cookie + Staff Event cancellation status overview

Frontend Routes

Path Page Auth
/ Landing — marketing page, upcoming events, My Registrations (auth-aware nav) Public
/login Login — Google OAuth sign-in (staff/organizer entry point) Public
/e/{slug} Public Event — event details, countdown, registration with Google Sign-In Public
/deposit/{attendee_id} Deposit — wallet adapter + QR for USDC/THB deposit Public
/ticket/{attendee_id} Ticket — QR code slip with check-in status Public
/claim/{token} Claim — quiz + NFT badge + refund Token-gated
/staff Scanner — camera QR + manual lookup + walk-in registration Staff
/admin Dashboard — attendee list, stats, escrow, cancellation, walk-in export/sync Staff
/admin/events Events — create, edit, manage events SuperAdmin
/adventure Rust Adventures — educational game Public

Architecture

worker/src/             — Cloudflare Worker
  handlers/             — API endpoints (auth, check-in, QR, attendee, events, quiz, claim, adventure, health)
    deposit/              — Deposit/refund handlers (split by payment track)
      usdc.rs               — USDC deposit flow: status, initiate, TX callback, confirm, webhook
      thb.rs                — THB slip upload/verify, refund queue, batch refund
      escrow.rs             — On-chain escrow: init, mark-checked-in, close, claim-forfeited, cancel
    ext.rs              — Shared utilities (EventIdQuery, resolve_event_with_access, resolve_kv)
  adventure.rs          — Adventure business logic (save progress, check completion)
  auth.rs               — Google OAuth + JWT + role resolution (super_admin/organizer/staff)
  error.rs              — Typed AppError → Axum IntoResponse integration
  event_store.rs        — KV event registry CRUD, seed, migration, hard_delete_event
  audit_store.rs        — Append-only audit trail (per-event + global, 27 action types)
  quiz.rs               — Quiz business logic (scoring, KV interaction)
  sheets/               — Google Sheets API
    mod.rs                — Access token, column mapping, attendee/staff queries, KV cache
    write.rs              — Sheet mutations: check-in, claim, QR URLs, row append
  solana.rs             — Helius cNFT minting (mintCompressedNft RPC, MintRequest struct)
  solana_escrow/        — Solana escrow TX builders
    mod.rs                — Types, constants, EscrowError
    crypto.rs             — SHA-256, base58, PDA/ATA derivation (WASM SubtleCrypto + native)
    wire.rs               — Blockhash cache, tx serialization, message account ordering
    tx_builders.rs        — 9 build_* functions using shared EscrowCtx + finalize_tx
  crypto.rs             — SubtleCrypto bridge (RSA-SHA256, HMAC-SHA256)
  http.rs               — HTTP client wrapping worker::Fetch
  middleware.rs         — Security headers, auth guard, correlation IDs
  state.rs              — AppState from Env bindings

domain/src/             — Shared (compiles x86_64 + wasm32)
  config/               — AppConfig (grouped: OAuth, Sheets, Solana, Nft, Server, EventDefaults)
  models/               — Attendee, Claims, EventConfig, AdventureConfig, AppError, API response types
  qr/                   — QR URL generation + base64 image

frontend-leptos/src/
  pages/                — Landing, Login, Scanner, Admin, Claim, Quiz Editor, Adventure
  pages/adventure/      — Game engine, level definitions, types
  api.rs                — API client types and fetch wrappers
  components.rs         — Shared components + role helpers
  utils.rs              — Helpers (timestamps, badges, participation)
  js/                   — Camera + QR detection module

Solana Escrow Architecture

The escrow system uses PDAs (Program Derived Addresses) to hold attendee USDC deposits on-chain. The escrow program is deployed on devnet at C6HDeZES9aPpNwe3UvS9ecmfcRhH1XeJb8PGJmLG3z3T.

Escrow Flow (5 steps, all validated on devnet):

1. create_event        →  Organizer signs  →  EventEscrow PDA + Vault ATA initialized (single TX)
2. deposit             →  Attendee signs   →  USDC → vault (Solana Pay)
3. mark_checked_in     →  Organizer signs  →  Attendee checked-in on-chain
4. refund              →  Attendee signs   →  USDC → attendee (after event ends)
5. claim_forfeited     →  Organizer signs  →  Forfeited deposits → organizer (after refund deadline)

PDA Seeds:

  • EventEscrow: ["escrow", organizer_pubkey, event_id_u64_le]
  • AttendeeDeposit: ["deposit", event_escrow_pubkey, attendee_pubkey]
  • Vault ATA: Associated Token Account for (EventEscrow, USDC mint)

Important constraints:

  • Refund requires clock > event_end (event must have ended) — no check-in required
  • Deposits are rejected after the event has ended (event_end > now check)
  • mark_checked_in rejects after event_end (SEC-011: prevents post-event attendance manipulation)
  • claim_forfeited requires clock > refund_deadline (post-deadline only)
  • All SPL token transfers use transfer_checked() with 6-decimal USDC (Token-2022 compatible)

Security note: SEC-001 (check-in gate rug pull) has been fixed — refunds no longer require checked_in == true. Attendees can refund after event_end regardless of check-in status. See docs/security_audit.md for full audit.

Constants:

Constant Devnet Mainnet
Program ID C6HDeZES9aPpNwe3UvS9ecmfcRhH1XeJb8PGJmLG3z3T TBD
USDC Mint 4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1m

Transaction building: All TX builders are in worker/src/solana_escrow/tx_builders.rs. They share an EscrowCtx that resolves program IDs + derives PDAs once, and a finalize_tx() helper that handles message building → blockhash → serialization → base64. This eliminates ~480 lines of duplicated boilerplate across the 9 builder functions.

Performance Layers

Layer Mechanism TTL Benefit
Google access token KV cache 3500s Eliminates RSA-JWT signing per request (~100ms saved)
Attendee data KV cache 30s Eliminates Google Sheets full-scan per request (~200-800ms saved)
Solana blockhash KV cache 30s Eliminates RPC call per TX build (~50-200ms saved)
TX builder context EscrowCtx Per-request Resolves 7 program IDs + 3 PDAs once instead of per-instruction (~3-5ms saved)
Cache invalidation On write-through Immediate Mutations (check-in, claim) invalidate cache instantly

Tests

# All unit tests
cargo test

# Individual crates
cargo test -p event-checkin-domain   # Shared types, QR logic
cargo test -p event-checkin-worker   # Crypto, auth, sheets, events

# Full 10-step E2E test (requires running worker + worker/.dev.vars with HELIUS_API_KEY)
./scripts/e2e/test_full_e2e.sh

# Devnet API test suite (7 tests, no browser needed)
./scripts/e2e/test_devnet.sh

# Mint-only test (single cNFT mint on devnet)
./scripts/e2e/test_devnet.sh --mint-only

# Worker WASM build check
cargo check -p event-checkin-worker --target wasm32-unknown-unknown

# Clippy
cargo clippy --all-targets

Devnet Escrow E2E

# Full 5-step escrow E2E on Solana devnet (requires USDC-funded attendee wallet)
ATTENDEE_WALLET=~/.config/solana/id.json bash scripts/e2e/test_escrow_devnet.sh

See scripts/e2e/test_escrow_devnet.sh for the complete test flow. All 24 tests validated on devnet.

Features

  • Camera QR Scanner — BarcodeDetector (Chrome) + jsQR fallback (Firefox/Safari)
  • Staff check-in logging — Records which staff member checked in each attendee (column J)
  • Sheet-based staff list — Staff emails loaded from "staff" sheet tab + env var (unioned)
  • Event format model — In-Person / Online / Hybrid with participation-type badges
  • Admin stats — Checked-in count, In-Person vs Online breakdown
  • Force QR regenerate — Admin can regenerate codes per attendee
  • CSP compliant — Zero eval() calls, no unsafe-eval directive
  • Edge deployment — Cloudflare Workers with SubtleCrypto for JWT signing
  • Multi-event support — KV-based event registry with per-event config, staff, quiz
  • Per-event access control — 4-tier role system: super_admin → organizer → staff → attendee
  • Google Sign-In for attendees — Dual-purpose OAuth for staff and attendees; email locked to Google account
  • Self-registration — Attendees register via public event page (/e/{slug}) with Google identity
  • My Registrations — Signed-in attendees see their events + status on landing page with auth-aware nav
  • Quiz-gated claim — Attendees complete quiz before claiming NFT badge
  • Landing page — Auth-aware nav bar, upcoming events, interactive swimlane, waitlist, FAQ, social proof
  • Rust Adventures — Educational tile-based game teaching Solana/Rust concepts
  • Security hardened — Cookie Secure flag, secret redaction in Debug, attendee-validated adventure saves
  • Automated E2E tests — 10-step full E2E suite + 7-test devnet suite
  • PDA escrow deposits — USDC deposits held in on-chain PDAs, refundable after event
  • Solana Pay integration — Deposit via QR code scan or wallet adapter (Phantom, Backpack, Solflare)
  • Dual-track deposits — USDC (on-chain escrow) or THB (PromptPay QR + slip verification)
  • Single-TX escrow init — Admin creates vault ATA + event escrow in one transaction via wallet signing
  • On-chain check-in — Staff marks attendees checked in on-chain via wallet-signed TX (escrow refund gate)
  • Wallet adapter interop — Shared JS module for wallet detection, connection, TX signing across scanner + admin
  • Wallet error recovery — Structured error classification with user-friendly guidance (wrong network, insufficient funds, user rejected, program error)
  • Escrow lifecycle management — Full deactivate → close flow in admin UI, rent reclamation
  • Event cancellation workflow — THB batch refund + USDC refund queue + cancel status (organizer-initiated)
  • Walk-in attendee management — On-the-spot registration, CSV export, Google Sheet sync with idempotency
  • Force delete for devnet cleanup — SuperAdmin can hard-delete events with ?force=true
  • Slug auto-deduplication — Recurring events get auto-incremented suffix on name collision
  • Audit trail — Append-only event log tracking all state-changing operations with actor attribution
  • Dev-mode payment gating — Solana wallet options hidden in production, shown only when dev_mode: true
  • Attendee flow persistence — localStorage resume for partial registrations, auto-redirect to deposit/ticket page

Security

Area Status Notes
Auth ✅ Secure JWT HMAC-SHA256, constant-time comparison, 24h expiry
Cookie ✅ Secure HttpOnly; Secure; SameSite=Lax; Path=/api
Admin routes ✅ Secure require_auth middleware, staff email verification
Claim gates ✅ Secure Sequential check-in → quiz → adventure → mint, no bypass
Solana RPC ✅ Secure Hardcoded method, serde serialization, null-safe deserialization, no user-controlled params
Secrets ✅ Secure All via env.secret(), redacted from Debug output
Escrow (on-chain) ✅ Secure Immutable params after creation, checked arithmetic, canonical PDA derivation, transfer_checked() (SEC-009 fixed), event_end guard (SEC-011 fixed)
Escrow (business logic) ✅ Secure SEC-001/002/003/004 all fixed — refunds don't require check-in, fields locked after escrow init, $1K deposit cap, archive guards escrow
Escrow (Token-2022) ✅ Secure SEC-009 fixed — all transfers use transfer_checked() with 6-decimal USDC
Double-claim ⚠️ Deferred KV dedup lock recommended before high-traffic events
Audit logging 🟡 Basic Append-only audit trail per event (CRUD + escrow + check-in + deposits). Global audit for deletions. Missing: on-chain CPI event indexing, UI viewer
JWT revocation ⚠️ Deferred KV blacklist recommended for compromised tokens
Dev mode ⚠️ Local only DEV_MODE=1 bypasses JWT verification — only for .dev.vars, never production

See docs/security_audit.md for the full escrow security audit (11 findings, 8 fixed, Safe Solana Builder cross-reference). See .handovers/025_security_audit_e2e_nft_config.md for the earlier auth/RPC audit.

Roles & Access Control

Role Can Do
super_admin Create/edit/delete events, manage all events, full dashboard
organizer Edit assigned event config, manage quiz, view dashboard
staff Check in attendees, view attendee list for assigned event
attendee Register for events, view own registrations, deposit, claim NFT
(unauthenticated) View landing page, public event pages, play adventure, take quiz

🏆 What's Built (Devnet-Validated)

✅ 10 phases complete — from check-in to escrow to attendee identity. Everything runs on Solana devnet with real wallets.

Core Flow Status Details
QR check-in Camera scan + manual lookup, staff logging
cNFT badges Compressed NFTs via Helius, $0.001 mint
Quiz gating Per-event quiz before NFT claim
Adventure gating Rust-themed educational game (10 levels)
Multi-event KV registry, 4-tier roles (super_admin/organizer/staff/attendee)
USDC escrow PDA-based deposits, refund, claim forfeited
Dual-track payments USDC (on-chain) + PromptPay THB (fiat QR)
Wallet adapter Phantom, Solflare, Backpack, Coinbase
Attendee identity Google Sign-In for registration, email locked to JWT
Self-registration Public event page /e/{slug} with countdown + deposit CTA
My Registrations Landing page auth-aware nav, event status tracking
Walk-in management On-the-spot registration, CSV export, Sheet sync
Event cancellation THB batch refund, USDC refund queue, status tracking
Wallet error recovery Structured error classification + user-friendly guidance
Security audit 11 findings, 8 fixed, SEC-001–011 addressed
E2E tests 68 tests (39 worker + 29 on-chain), devnet validated

📈 Competitive Landscape

Feature BeThere Luma Eventbrite POAP Kickback*
On-chain deposits ✅ USDC escrow ✅ ETH (defunct)
Attendance NFTs ✅ cNFT ✅ (Ethereum)
Deposit refund ✅ Auto Manual ✅ Payout pool
No-show penalty ✅ Forfeit to org ✅ Pool split
Quiz/Adventure gating ✅ Built-in
Cost per NFT $0.001 N/A N/A ~$0.50 N/A
Stablecoin deposits ✅ USDC ❌ (volatile ETH)
Open source ✅ (archived)

*Kickback (2016–2022) — Ethereum event deposit platform, shut down due to gas costs and team burnout. BeThere addresses every structural weakness. See docs/competitive_analysis_kickback.md for full analysis.

🗺️ Roadmap

Phase Feature Status
1–6 Check-in → NFT → Quiz → Multi-event → Adventure → Security ✅ Done
7 NFT config + production deployment 🟡 Devnet working
8–9 USDC escrow + security hardening ✅ Done (devnet deployed)
10 Mainnet deployment 📋 Next (~1.5 SOL cost)
11 Platform fees (1-2% on forfeited deposits) 📋 Planned
12 Multi-organizer SaaS 📋 Planned

See DISCUSSION.md for the full architecture direction and decisions.

About

BeThere is a commitment protocol eliminating no-shows via Stake-to-Attend. Users lock a refundable USDC deposit to secure event spots. On QR check-in, and meet the condition(s) like complete the quiz of the event, funds return instantly, and a Proof-of-Presence cNFT is minted ($0.001). No-shows forfeit to the organizer. Built 100% in Rust on Solana

Resources

Security policy

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors