ETHMatch
A privacy-focused dating and social app, pairing World ID verification with Walrus-backed storage, matching, and chat.
A social experience inside World App
ETHMatch was built at ETHGlobal New Delhi 2025 as a dating and social mini app. The experience brings profile discovery, likes, mutual matches, and conversations into a mobile interface, with World App providing wallet and verification capabilities. The repository also includes a blind-date flow with an in-app payment step.
The project is split between a Next.js frontend and a separate TypeScript backend. The frontend owns the screens and the integration with MiniKit. The backend models people and their relationships as a graph, answers matching queries, and persists graph snapshots through Walrus.
Authentication and proof of personhood
Wallet authentication uses MiniKit’s signed-message flow with a nonce. The NextAuth credentials provider checks the signed nonce and verifies the returned SIWE message before creating a JWT-backed session. That session carries the wallet identity and basic profile information needed by the interface.
World ID verification is a separate step. The client requests a MiniKit verification action with a signal and a selected verification level, then sends the resulting payload to the verification API. The app can use the result when building a profile’s verification state. Authentication establishes the wallet session; the verification flow adds a proof-of-personhood signal to the experience.
Profiles and likes as a graph
Each person is represented by a node identified through their wallet address. Profile properties include a username, age, city, preferences, verification state, and a profile-picture reference. Likes and other interactions are stored as directed relationships between nodes.
This model makes the core matching rule easy to follow. The backend walks a person’s outgoing like relationships and checks whether each recipient has a corresponding like pointing back. Reciprocal likes become mutual matches, with a set preventing duplicates. The discovery query also excludes the current person and profiles they have already interacted with.
- Profile discovery reads candidate Person nodes from the graph.
- A like creates a directed relationship between two people.
- Mutual relationships turn that interaction history into a match list.
Persisting the social graph with Walrus
The graph service maintains nodes, relationships, and indexes in memory for application queries. After a profile or relationship changes, a five-second debounce schedules a graph save. Batching those changes avoids sending a separate storage request for every interaction in a short burst.
The Walrus service serializes the nodes, relationships, metadata, and timestamp into a JSON document. It sends that document to the publisher as a blob and records the returned blob ID. The read path uses an aggregator to retrieve the document and rebuild the application’s graph state. This keeps the live query model and the persisted snapshot connected through a concrete, inspectable storage format.
From a match to the mobile interface
Next.js API routes forward profile, recommendation, match, and message requests to the backend. They preserve the upstream status and response content type, and return a defined error response when the backend is unreachable. This gives the client a consistent same-origin API surface for its screens.
The interface includes separate views for exploring profiles, received and sent interactions, matches, and chat. MiniKit haptic commands are used around interactions, extending the mini-app integration beyond sign-in. The blind-date screen adds a payment flow that initiates a payment, invokes MiniKit, and checks the result through a confirmation endpoint.
What the prototype connects
The useful part of ETHMatch is the complete connection between identity, social state, and persistence: a wallet-backed session enters the app, verification contributes a profile signal, graph relationships drive discovery and matching, and Walrus stores the resulting state. Each piece has a defined role in the user journey.
The source makes those pieces independently understandable through frontend API routes, a graph service, and a storage adapter. That separation also gives the project room to evolve its matching rules or storage format while keeping the surrounding mobile experience coherent.
