A decentralized application for event management and ticketing with:
- escrowed payments,
- Selfie Check abuse-prevention verification &
- NFT proof of attendance.
π₯ Demo Video: Watch Kosmos on YouTube
Demoed on Ethereum Sepolia.
- Overview
- Sponsor Tracks
- Problem & Solution
- Target Users
- Feature Scope
- Tech Stack
- System Architecture
- Pages & Routes
- Folder Structure
- Environment Variables
- Project Building Steps
- Contributing
| Field | Detail |
|---|---|
| Project name | Kosmos |
| Type | Event management + escrowed ticketing + onchain attendance |
| Core value | Human-gated event access, escrowed payments and provable attendance tied together in one event lifecycle. |
| Tagline | Tickets backed by escrow, not promises. |
| Chain | Ethereum Sepolia |
| Timeline | September 4 β 16, 2026 (ETHOnline 2026) |
| Application | Next.js web application with authenticated host and attendee flows |
| Contracts | Foundry-deployed EventEscrowFactory, EventEscrow, TicketNFT and KosmosSubnameRegistry contracts on Sepolia |
| Validation | Sepolia lifecycle validation completed with 17/17 Foundry tests passing, plus lint, typecheck and production build passing |
Kosmos was built for ETHOnline 2026 as an event platform where hosts create events, attendees pass a human-verification gate before joining, paid attendance is backed by an event escrow and checked-in attendees receive an NFT proof of attendance when the event is settled.
The validated Sepolia deployment and lifecycle results are documented in docs/SEPOLIA_VALIDATION.md.
Kosmos integrates four sponsor technologies. The three official prize tracks selected for submission are World, Uniswap and ENS. Privy remains an important part of the product's authentication and wallet flow, but Kosmos is not being submitted to the Privy prize track.
| Sponsor | Track | Used for | Entered as official track? |
|---|---|---|---|
| World | Selfie Check | Selfie Check-compatible World ID flow before an attendee can continue through the event join flow; used as an abuse-prevention and repeat-participation signal | Yes |
| Uniswap | Best Uniswap Stack Contribution | Exact-output USDC β ETH payment flow on Sepolia using the Uniswap Trading API, Permit2 approval/signing support and transaction simulation before the attendee deposits ETH into escrow | Yes |
| ENS | Best Use of ENSv2 | ENSv2 Sepolia-backed Kosmos profile subnames such as name.kosmos.eth, with a dedicated Permissioned Resolver deployed for each registered subname |
Yes |
| Privy | Authentication / wallets | Email authentication, access-token verification and linked external Ethereum wallet selection for onchain actions | No |
Every event join begins behind the World verification component. The implementation:
- requests an RP signature server-side,
- invokes IDKit with the
join-kosmos-eventaction, - uses the attendee's authenticated external wallet as the verification signal,
- verifies the proof server-side,
- validates the returned signal hash,
- stores the Selfie Check nullifier, and
- prevents reuse of the same nullifier for an active join request to the same event.
The complete World integration feedback, including the Developer Portal and Sandbox testing experience, is documented in docs/WORLD_FEEDBACK.md.
Key implementation files:
app/web/components/worldid/SelfieCheckButton.tsxapp/web/app/api/world-id/rp-signature/route.tsapp/web/app/api/verify-selfie/route.tsapp/web/lib/worldid/verify.ts
Selfie Check was exercised through the World staging / Sandbox Simulator flow used during development. This README does not claim production Selfie Check access.
Kosmos uses the Uniswap Trading API for a paid-join flow on Sepolia.
The implemented flow is:
Sepolia USDC
β Uniswap exact-output quote
β approval / Permit2 handling where required
β Uniswap swap
β exact ETH output arrives in the authenticated attendee's external wallet
β attendee calls EventEscrow.deposit(attendee) with the event's exact ETH price
The swap and escrow deposit are separate transactions. If the swap succeeds but the subsequent escrow deposit fails, the UI keeps the pending deposit amount and exposes a retry path.
Important implementation references:
app/web/lib/uniswap/client.ts#L51-L96β approval check, quote request, Permit2 configuration, V2/V3/V4 protocols, swap construction and transaction simulationapp/web/app/api/swap-quote/route.ts#L129-L156βEXACT_OUTPUTquote with the authenticated wallet as recipient and approval checkapp/web/app/api/swap-build/route.ts#L177-L197β quote recipient validation and swap constructionapp/web/components/join/UniswapPayButton.tsx#L433-L584β escrow deposit, retry handling and swap β deposit flowcontracts/src/EventEscrow.sol#L81β payable attendee deposit entry point
Detailed builder feedback is available in docs/UNISWAP_FEEDBACK.md.
The Uniswap Developer Feedback Form will be submitted after the Phase 15 changes are merged to
main, using the final public feedback-document URL.
Kosmos uses ENSv2 on Sepolia to give users human-readable Kosmos identities such as:
name.kosmos.eth
Registration is not only a database alias. The backend checks the Kosmos ENSv2 subname registry, deploys a Permissioned Resolver for the user and registers the resulting subname through KosmosSubnameRegistry.
Relevant implementation:
contracts/src/KosmosSubnameRegistry.sol#L21-L63β ENSv2 Permissioned Registry integration and subname registrationapp/web/lib/ens/register-subname.ts#L33-L99β Permissioned Resolver deployment and subname registrationapp/web/app/api/ens/check-availability/route.tsβ subname availability checkingapp/web/app/api/ens/register/route.tsβ authenticated registration flowapp/web/app/[ens-subname]/page.tsxβ human-readable Kosmos profile route
The optional co-host Enhanced Access Control feature is not part of the completed submission build. The current co-host API route remains a placeholder.
Traditional event ticketing separates identity, payment, attendance and settlement into independent systems.
Hosts have limited protection against automated or repeated participation attempts. Attendees often have to trust a platform to hold and release event funds correctly. After an event, attendance is usually represented by a database flag, email or screenshot rather than a user-owned onchain record.
Kosmos connects those steps into one event lifecycle.
A host creates an event backed by an onchain escrow. Before an attendee can continue through the join flow, Kosmos performs a World Selfie Check-compatible verification. Paid attendance is backed by the event escrow, and the implemented Uniswap path swaps Sepolia USDC for the exact ETH amount required before the attendee deposits it into escrow. Hosts can approve or deny join requests, attendees can cancel before an event begins, QR tickets support physical check-in and event settlement releases eligible escrow funds while minting attendance NFTs for checked-in attendees.
Here's how it works:
- Event creation : A host creates an event with its core details and an escrow-backed event lifecycle.
- Identity check : A join attempt first passes through the World Selfie Check-compatible verification flow.
- Payment : For the implemented Uniswap path, Sepolia USDC is swapped for an exact ETH output to the attendee's authenticated external wallet and then deposited into the event escrow.
- Approval : Where host approval is enabled, the host accepts or denies a join request. Denial refunds the attendee's escrowed deposit.
- Ticketing : Accepted attendees receive access to a QR ticket associated with their join record.
- Cancellation : An eligible attendee can cancel before the event starts and receive an escrow refund.
- Check-in : The host scans the attendee's QR code to mark attendance.
- Settlement : The host ends the event, eligible escrow value is settled to the host and checked-in attendees receive TicketNFT proof-of-attendance tokens.
- Identity : User profiles are represented with Kosmos ENSv2 subnames such as
name.kosmos.eth.
| User type | Role |
|---|---|
| Host | Creates events, manages join requests, starts events, scans attendee tickets and ends events to trigger settlement and attendance NFT minting |
| Joinee / Attendee | Discovers events, completes the Selfie Check gate, pays when required, receives a QR ticket after acceptance, checks in and receives an NFT when eligible |
There's no separate permanent account type. The same authenticated user can host one event and attend another.
- Event creation : Event details, capacity, approval settings and cover-image upload backed by IPFS storage through Pinata
- Event discovery : Browse events and open individual event pages
- Identity verification : World ID Selfie Check-compatible gate before the event join flow
- Uniswap payment option : Exact-output Sepolia USDC β ETH swap followed by an explicit ETH deposit into the event escrow
- Direct ETH escrow deposit : Payable
EventEscrow.deposit(attendee)path for event deposits - Approval workflow : Host accepts or denies pending join requests
- Refunds : Denial refunds and attendee cancellation refunds validated on Sepolia
- Ticketing : QR-code ticket generation for accepted attendees
- Check-in : Host-side QR scanner using
html5-qrcode - Settlement : End-event payout flow plus TicketNFT minting for checked-in attendees
- Onchain identity : ENSv2 Sepolia subname registration with a Permissioned Resolver
- Wallet & auth : Privy authentication plus a linked external Ethereum wallet for onchain actions
- Email : Resend-backed transactional email components, including payment confirmation, QR-ticket and reminder templates
- Metadata & images : Pinata-backed IPFS uploads for event cover images and NFT metadata
- Validation : Happy path, no-show, denial refund and cancellation refund tested on Sepolia
Two optional roadmap items remain intentionally unfinished:
- Co-host Enhanced Access Control permissions β
/api/events/[id]/co-hostis currently a placeholder route. - Automated 24-hour reminder scheduler β reminder-email support exists, but
/api/cron/reminderremains a placeholder and no automated scheduler is claimed.
Mainnet deployment is also outside the current submission build. Kosmos is validated on Ethereum Sepolia.
| Purpose | Tool |
|---|---|
| Smart Contracts | Solidity + Foundry + OpenZeppelin |
| Chain | Ethereum Sepolia |
| Frontend | Next.js 16 App Router + React 19 + Tailwind CSS |
| Web3 | viem |
| Auth & Wallets | Privy |
| Human verification | World ID / IDKit Selfie Check-compatible flow |
| Onchain identity | ENSv2 Permissioned Registry + Permissioned Resolver |
| Payments | Uniswap Trading API |
| Backend | Next.js API routes |
| Database | PostgreSQL + Drizzle ORM |
| IPFS | Pinata |
| Resend + React Email | |
| QR Code | qrcode for generation + html5-qrcode for scanning |
| Validation | Foundry tests + ESLint + TypeScript typecheck + Next.js production build |
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β FRONTEND β
β Next.js App Router + React β
β β
β Privy Β· World IDKit Β· viem Β· Uniswap payment UI β
ββββββββββββ¬ββββββββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββ
β β
β image / metadata β API requests
βΌ βΌ
ββββββββββββββββββββββββ βββββββββββββββββββββββββββββββββββββββββ
β PINATA β β NEXT.JS API ROUTES β
β β β β
β Event cover images β β /api/events β
β NFT metadata β β /api/join β
β stored on IPFS β β /api/verify-selfie β
ββββββββββββββββββββββββ β /api/world-id/rp-signature β
β /api/swap-quote β
β /api/swap-build β
β /api/ens/register β
β /api/checkin/[ticketId] β
β /api/events/[id]/end β
βββββββββββββ¬ββββββββββββββββ¬ββββββββββββ
β β
ββββββββββββββββΌββββ ββββββΌββββββββββββββββββββββ
β POSTGRESQL β β ETHEREUM SEPOLIA β
β Drizzle ORM β β β
β β β EventEscrowFactory β
β users β β EventEscrow β
β events β β TicketNFT β
β join requests β β KosmosSubnameRegistry β
β ticket state β β ENSv2 registry/resolvers β
ββββββββββββββββββββ ββββββββββββββββββββββββββββ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β EXTERNAL SERVICES β
β β
β World ID / IDKit β Selfie Check-compatible join verification β
β Uniswap API β exact-output USDC β ETH swap β
β Privy β authentication + linked external wallet β
β Resend β transactional email β
β Pinata β IPFS uploads β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Host fills the event form and selects a cover image
β Next.js event API validates the request
β Cover image is uploaded to IPFS through Pinata
β Event creation uses the escrow-backed event lifecycle
β Event data stores the relevant onchain and IPFS references
β Event becomes available through discovery and management views
Attendee opens an event and clicks Join
β World IDKit Selfie Check-compatible flow runs
β RP request data is signed server-side
β Proof is verified server-side
β Wallet signal hash and per-event nullifier reuse are validated
β Free events can continue to the join-request flow
For the Uniswap paid path:
β /api/swap-quote requests an EXACT_OUTPUT Sepolia USDC β ETH quote
β Quote recipient is the authenticated attendee external wallet
β Approval / Permit2 requirements are handled
β A fresh quote is used before swap construction
β /api/swap-build validates the quote and constructs the swap
β Exact ETH output arrives in the attendee wallet
β Attendee submits EventEscrow.deposit(attendee) with the exact event price
β If the deposit fails after the swap, the UI exposes a deposit retry path
β Join request is recorded for the host workflow
Host reviews a pending join request
β Accept: attendee continues as an approved participant
β Deny: escrow refund path returns the eligible attendee deposit
Approved attendee may cancel before the event starts
β EventEscrow.cancelByAttendee() executes the attendee refund path
Host starts the event
β QR scanner becomes available
β Host scans attendee ticket
β Backend validates the ticket and records check-in state
Host ends the event
β Event escrow lifecycle settles
β Eligible host payout is transferred
β TicketNFT proof-of-attendance tokens are minted for checked-in attendees
β No-show attendee receives no attendance NFT and no refund
The final Sepolia test pass covered:
- Successful attendance, check-in, settlement and NFT mint
- Approved attendee no-show with no NFT and no refund
- Join-request denial with deposit refund
- Pre-event attendee cancellation with deposit refund
Final verification:
forge test -vv β 17 passed, 0 failed
npm run lint β PASS
npm run typecheck β PASS
npm run build β PASS
Validated Sepolia contracts:
| Contract | Sepolia address |
|---|---|
| EventEscrowFactory | 0x5540800a975a46228d2A42D671da1F2e8867aD81 |
| TicketNFT | 0xc85365cEd1A610575002E4a3d22188882665AdA7 |
The deployed ENSv2 Kosmos subname registry used by the application is:
| Contract | Sepolia address |
|---|---|
| KosmosSubnameRegistry | 0xb98adc04d45365d48e3dbb46d181a43659be0b82 |
EventEscrow instances are created per event through the escrow factory.
See docs/SEPOLIA_VALIDATION.md for the detailed lifecycle validation log.
The application uses a single Next.js application for the landing page, authenticated product flow and API routes.
| Route | Access | Description |
|---|---|---|
/ |
Public | Landing page |
/signin |
Public | Privy authentication, linked-wallet flow and ENS profile setup |
/home |
Sign-in required | Post-sign-in hub |
/create |
Sign-in required | Event creation form |
/discover |
Sign-in required | Event discovery |
/discover/[event-slug] |
Sign-in required | Event detail + join flow |
/[ens-subname] |
Sign-in required | Human-readable Kosmos profile dashboard |
/events/[id]/manage |
Host flow | Join-request management and event lifecycle controls |
/events/[id]/checkin |
Host flow | QR scanner for attendee check-in |
/style-guide |
Development | Kosmos UI/style guide |
| Route | Description |
|---|---|
/api/auth/sync |
Synchronize authenticated Privy user state |
/api/events |
Create/list events |
/api/events/[id] |
Event-specific data |
/api/events/[id]/join-requests |
Host join-request listing |
/api/events/[id]/join-status |
Attendee join status |
/api/events/[id]/end |
Event settlement + attendance metadata/NFT flow |
/api/verify-selfie |
World Selfie Check proof verification |
/api/world-id/rp-signature |
World RP request signing |
/api/swap-quote |
Uniswap exact-output quote + approval check |
/api/swap-build |
Validate quote / Permit2 data and build the swap transaction |
/api/join |
Submit the event join request |
/api/join/[id]/approve |
Host approval |
/api/join/[id]/deny |
Host denial + refund flow |
/api/join/[id]/cancel |
Attendee cancellation + refund flow |
/api/checkin/[ticketId] |
Validate and record attendee check-in |
/api/ens/check-availability |
Check Kosmos ENSv2 subname availability |
/api/ens/register |
Register a Kosmos ENSv2 subname |
/api/events/[id]/co-host |
Placeholder for optional co-host EAC work; not implemented |
/api/cron/reminder |
Placeholder for optional automated reminder scheduler |
Kosmos/
βββ app/
β βββ web/ # Next.js frontend + API application
β βββ app/
β β βββ [ens-subname]/ # ENS-backed profile route
β β βββ create/ # Event creation
β β βββ discover/ # Event discovery + detail pages
β β βββ events/ # Host manage/check-in views
β β βββ api/
β β βββ auth/
β β βββ events/
β β βββ join/
β β βββ verify-selfie/
β β βββ world-id/
β β βββ swap-quote/
β β βββ swap-build/
β β βββ checkin/
β β βββ ens/
β β βββ cron/
β βββ components/
β β βββ checkin/
β β βββ events/
β β βββ join/
β β βββ tickets/
β β βββ worldid/
β β βββ ui/
β βββ emails/ # React Email templates
β βββ lib/
β βββ db/
β βββ email/
β βββ ens/
β βββ ipfs/
β βββ uniswap/
β βββ worldid/
β
βββ contracts/ # Foundry contracts + deployment scripts + tests
β βββ src/
β β βββ EventEscrow.sol
β β βββ EventEscrowFactory.sol
β β βββ TicketNFT.sol
β β βββ KosmosSubnameRegistry.sol
β βββ script/
β β βββ DeployEventEscrowFactoryV2.s.sol
β β βββ DeployTicketSystem.s.sol
β β βββ ens/
β βββ test/
β βββ EventEscrow.t.sol
β βββ TicketNFT.t.sol
β
βββ docs/
β βββ SEPOLIA_VALIDATION.md
β βββ UNISWAP_FEEDBACK.md
β βββ WORLD_FEEDBACK.md
β
βββ brand/ # Kosmos brand assets and source-of-truth
βββ shared/ # Shared types, ABIs and contract constants
βββ LICENSE
βββ README.md
The current web application environment template is app/web/.env.example.
# ββ Database ββββββββββββββββββββββββββββββββ
DATABASE_URL="postgresql://user:password@host:6543/postgres"
DIRECT_DATABASE_URL="postgresql://user:password@host:5432/postgres"
# ββ Privy ββββββββββββββββββββββββββββββββββ
NEXT_PUBLIC_PRIVY_APP_ID=
PRIVY_APP_SECRET=
# ββ ENS / Sepolia ββββββββββββββββββββββββββ
SEPOLIA_RPC_URL=
ADMIN_PRIVATE_KEY=
# ββ Email ββββββββββββββββββββββββββββββββββ
RESEND_API_KEY=
EMAIL_FROM=
# ββ IPFS βββββββββββββββββββββββββββββββββββ
PINATA_JWT=
PINATA_GATEWAY=
# ββ World ID βββββββββββββββββββββββββββββββ
NEXT_PUBLIC_WORLD_APP_ID=
NEXT_PUBLIC_WORLD_RP_ID=
RP_SIGNING_KEY=
# ββ Uniswap ββββββββββββββββββββββββββββββββ
UNISWAP_API_KEY=
# ββ App ββββββββββββββββββββββββββββββββββββ
NEXT_PUBLIC_APP_URL=Never commit
.env.local, private keys, API keys, signing keys or service JWTs.
A step-by-step record of the ETHOnline 2026 build.
- Logo and brand identity
- Core UI component library
- Project scaffold and repo setup
- Database schema setup
- Deploy EventEscrow contract system
- Deploy TicketNFT contract
- ENSv2 subname registry & resolver setup
- Landing page
- Privy sign-in with linked external wallet
- Custom profile ID and dashboard route
- Home hub page
- Email service integration
- Event creation flow
- Discover page and dashboards
- World ID Selfie Check integration
- Uniswap exact-output swap + escrow payment flow
- Request-to-join and host approve/deny flow
- Cancellation and refund flow
- Co-host Enhanced Access Control permissions (optional/not implemented)
- 24-hour automated reminder scheduler (optional/not implemented)
- Check-in scanner
- Start/end event payout and NFT mint flow
- Cross-page polish and QA
- Sepolia testnet validation
- Mainnet deployment (optional/not implemented)
- Uniswap integration feedback document
- World Selfie Check feedback document
- Demo prep and submission
- π¨ Improvements to the design and UI are welcome.
- π¨ Try to break the app by testing real flows and edge cases. If you find a bug, check whether an issue already exists before opening a new one.
- π‘ Keep application code strongly typed with TypeScript and validate API inputs where appropriate.
- π± For UI changes, test both desktop and mobile viewport sizes before submitting.
- π Never commit private keys, API keys, signing keys, JWTs,
.env.localfiles or other secrets.
-
Fork the repository
Fork the Kosmos repository to your GitHub account.
-
Clone your fork
git clone https://github.com/<your-github-username>/kosmos.git cd kosmos
-
Create a feature branch
Start from the latest
developbranch:git checkout develop git pull origin develop git checkout -b <type>/<short-description>
Example:
git checkout -b fix/mobile-event-card
-
Set up the web application
The Next.js application lives in:
app/webInstall the web dependencies:
cd app/web npm installCreate your local environment file:
cp .env.example .env.local
Configure the required environment variables locally.
Never commit the populated
.env.localfile.Start the development server:
npm run dev
The app is available at:
http://localhost:3000 -
Set up the smart contracts when needed
If your contribution affects Solidity contracts, open another terminal from the repository root and move to:
cd contractsMake sure Foundry is installed, then run:
forge build forge test -
Make your changes
Keep changes focused and avoid modifying unrelated files.
Before committing web application changes, run from
app/web:npm run lint npm run typecheck npm run build
If contracts were modified, also run from
contracts:forge test -
Commit your changes
Stage only the files you changed:
git add <files-you-edited>
Use a Conventional Commit message:
git commit -m "<type>: <short description>"Prefix Use for feat:A new feature fix:A bug fix docs:Documentation changes style:Formatting with no behavior change refactor:Code restructure without a feature/bug change test:Adding or updating tests chore:Build, dependency or tooling work -
Push your branch
git push origin <your-branch-name>
-
Create a Pull Request. π
Open a pull request against the Kosmos
developbranch and describe your changes clearly.Before opening a PR, check:
-
npm run lintpasses fromapp/web -
npm run typecheckpasses fromapp/web -
npm run buildpasses fromapp/web -
forge testpasses when Solidity contracts are affected - The relevant feature works locally
- UI changes have been checked at appropriate viewport sizes
- No secrets or local environment files are included in the diff
- The PR contains only relevant changes
Getting help:
- Open a GitHub Discussion for general questions
- Comment directly on the issue you're working on
- Reach out on Twitter
-