ENS Mail

Web3 Email: ENS subnames + XMTP inbox + .limo profiles on Sepolia

ENS Mail

Created At

ETHGlobal New Delhi

Project Description

ENSMail — Project Description (detailed)

Short summary: ENSMail is a decentralized, wallet-native messaging and identity platform that turns ENS subnames (e.g., alice.mail.eth) into personal, censorship-resistant inboxes and public profiles served via .limo pages. Messaging is wallet-to-wallet (XMTP or Push), encrypted end-to-end, and hosted on decentralized storage (IPFS / Limo). The Sepolia testnet is used for all blockchain flows in this hackathon build.

Problem this solves

Modern messaging and email are centralized, subject to censorship, account lockouts, and provider control. ENSMail replaces username/password accounts with wallet-owned ENS subnames and moves message delivery/storage off centralized servers so users truly own identity, profile, and messages — while preserving a familiar “email/inbox” UX.

What ENSMail is / main features

ENS subnames as addresses: alice.mail.eth acts like [email protected].

Universal .limo pages: Public profile and inbox reachable at alice.mail.eth.limo.

Wallet authentication: MetaMask / WalletConnect (via wagmi / RainbowKit) — no passwords.

Wallet-to-wallet encrypted messaging: Powered by XMTP (or Push Protocol as opt-in).

ENS metadata: profile data stored in ENS text records (displayName, avatar, inboxPointer, etc.).

Decentralized hosting: Frontend dApp deployed to IPFS; contenthash on ENS points .limo pages to that IPFS hash (universal dApp model).

Optional smart contracts: Registrar, reputation/verification, token-gated inbox or subscription payments.

Rich UX: Dark theme (black → deep blue gradient), responsive layout, polished animations (Framer Motion), mobile-first design.

High-level architecture

ENS Layer (Identity)

Parent domain: mail.eth (on Sepolia for dev).

Subnames: username.mail.eth owned by user wallets.

ENS text records store metadata:

displayName → "Alice"

avatar → ipfs://Qm...

inboxPointer → (XMTP channel ID or wallet address pointer)

contenthash → IPFS hash of universal frontend (so alice.mail.eth.limo loads the same app).

Limo / Frontend Layer

Single universal dApp (Next.js) deployed to IPFS; served for every subname via .limo.

On load, dApp reads the subname from the URL (alice.mail.eth) and fetches ENS metadata to personalize the page.

Messaging Layer

XMTP handles encrypted wallet-to-wallet messages and off-chain storage.

Frontend creates XMTP identity (signed by wallet) and reads/writes conversations tied to wallet addresses.

Optional Smart Contracts

Custom registrar (for paid or governed registrations), reputation contract, token-gated access contract, subscription/payment contract.

Storage

Avatars, attachments, and optional message backups stored on IPFS/Arweave.

Notifications

Optional Push Protocol for push notifications; in-app toasts for real-time UX.

End-to-end user flow (step by step) A. Onboarding / Claiming subname

User opens mail.eth.limo (universal dApp) → clicks Connect Wallet (MetaMask on Sepolia).

User chooses a subname (alice.mail.eth) in the registration UI.

Frontend checks availability, prompts transaction to register subname (either via standard Sepolia ENS flow or via your custom registrar contract).

After transaction confirms, frontend writes ENS text records:

displayName = "Alice"

avatar = ipfs://QmAvatarHash

inboxPointer = <xmtp pointer or wallet address>

contenthash = ipfs://<universal_dapp_hash> (so .limo loads the app).

The wallet now “owns” alice.mail.eth.

B. Getting an inbox / first login

User visits alice.mail.eth.limo — Limo resolves contenthash and serves the universal dApp.

The dApp reads alice.mail.eth ENS records, finds inboxPointer.

User connects wallet, XMTP identity is initialized, and existing conversations are synced (decrypted locally).

C. Sending a message

Alice types recipient bob.mail.eth.

App resolves bob.mail.eth → gets Bob’s wallet/XMTP pointer.

XMTP creates or opens a conversation, encrypts the message, and sends it off-chain to XMTP storage nodes.

Bob receives and decrypts the message locally when he opens his inbox.

D. Reading & notifications

Bob logs into bob.mail.eth.limo, XMTP pulls messages and the UI marks new/unread with timestamps. Optional Push sends notifications for new messages.

Example ENS records (recommended keys)

contenthash → ipfs://QmUniversalDappHash

text.displayName → Alice

text.avatar → ipfs://QmAvatar...

text.inboxPointer → xmtp://<channel-id> or simply 0xBobWalletAddress (your app interprets the pointer)

Smart contracts (what to write & why)

MVP — no contracts required: ENS (Sepolia) + XMTP + frontend provide inbox functionality. Optional contracts (recommended for features / hackathon polish):

ENSMailRegistrar.sol — controlled subname registration with optional fee:

Enforce single-ownership, simple pricing, minting flows.

Emit events to power off-chain indexing (analytics, leaderboards).

Reputation.sol — on-chain or hybrid reputation storage:

Users can attest/trust/block other addresses, issue verified badges (optionally NFT badges).

TokenGate.sol — token-gated messaging:

Require sender to hold an ERC-721/20 to message a protected inbox.

SubscriptionManager.sol — optional recurring payments via manual payments or streaming (superfluid-style integration optional).

A simple registrar pseudo-interface:

function registerSubname(string calldata label, address owner) external payable; function setSubnameMetadata(string calldata label, bytes calldata data) external;

(Use ENS NameWrapper / ENS registry calls or call ENS APIs from serverless functions.)

Security & privacy considerations

E2E Encryption: Messages are encrypted client-side via XMTP; private keys never leave the signer.

No plaintext on-chain: ENS stores only pointers (text records, contenthash), not message contents.

Ownership security: Subname ownership is enforced by ENS; if you deploy a custom registrar, design safe transfer and recovery flows.

Spam & abuse mitigation: Provide token gating, allowlist, reputation/block features, regex-based validation for messages, and client-side spam filters.

Content hosting integrity: IPFS contenthash stored in ENS ensures the frontend served via .limo is verifiable; consider signed metadata for additional guarantees.

Rate limits & UX protections: Add client-side rate limiting and sensible transaction UX to avoid accidental multi-tx spam.

Tech stack & key libraries

Frontend: Next.js (or React) + TailwindCSS + Framer Motion (animations)

Wallet / Auth: wagmi + RainbowKit (MetaMask / WalletConnect)

ENS: ethers.js + ensjs / ENS NameWrapper / Sepolia ENS contracts

Messaging: XMTP SDK (client-side), optional Push Protocol

Storage / Hosting: IPFS (Pinata / Infura / web3.storage), .limo gateway for ENS-hosted pages

Dev tools: Hardhat (for optional contracts), Ethers, Alchemy/Infura RPC endpoints

Deployment: IPFS pinning + .limo contenthash updates; Vercel / Netlify for admin or CI (optional)

Typical .env variables (what to include)

NEXT_PUBLIC_RPC_URL_SEPOLIA — Sepolia RPC (Alchemy/Infura)

NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID — WalletConnect project id (if used)

NEXT_PUBLIC_XMTP_ENV — dev or production (XMTP SDK env)

REGISTRAR_CONTRACT_ADDRESS — (if you deploy custom registrar)

NEXT_PUBLIC_PARENT_ENS — mail.eth

IPFS_API_KEY / IPFS_SECRET — for uploads/pinning (Pinata or web3.storage)

NEXT_PUBLIC_APP_ORIGIN — base URL for callbacks (optional)

(Project guide should list exact names and sample values.)

Demo plan (hackathon / judges walkthrough)

Live claim: Show subname registration on Sepolia (transaction in MetaMask).

Public profile: Visit alice.mail.eth.limo and show the profile & avatar.

Send/Receive: Two browser windows/wallets — live XMTP message from alice.mail.eth → bob.mail.eth.

Token gating / NFT attach: Show token-gated inbox or attach an NFT/POAP to a message.

Optional contract demo: If registrar deployed, show registration fee flow and contract event logs.

Highlight: “All messages are E2E encrypted, no centralized server, and profile + inbox are accessible via .limo.”

Developer notes & recommended implementation approach

Universal dApp approach: Deploy a single universal frontend to IPFS and set every subname’s contenthash to that IPFS hash — the app then personalizes itself by reading the subname in the URL. This avoids per-user hosting complexity.

ENS operations: Use ethers.js and the ENS library to check availability, submit registration transactions, and set text records.

XMTP integration: Initialize XMTP client with the connected signer in the browser; use conversations.newConversation(peerAddress) to create/send and conversation.streamMessages() to read.

Indexing & UX: Optionally maintain a lightweight off-chain index (e.g., via a simple serverless function or TheGraph) for faster inbox summaries, but ensure message privacy is preserved (do not store plaintext).

Testing: Use Sepolia test ETH for transactions and XRTP dev/test XMTP env if available.

Future work & extensions

Reliable on-chain verification badges (NFT-based verification).

Bridging to email/gateways for cross-protocol notifications (opt-in).

Rich attachments with encrypted IPFS blobs and permissioned access.

Federated inbox aggregator for users who own multiple ENS subnames.

Scalable spam mitigation: decentralized reputation or moderation layer

How it's Made

I built this project using React + Vite for a fast, modular frontend with a clean developer experience. The UI was designed with a dark minimalist theme and smooth animations to ensure an intuitive UX across both desktop and mobile.

On the blockchain side,i used Ethereum Sepolia testnet with ENS test contracts to simulate subdomain registration. Instead of requiring real ENS setup, we hardcoded a registry contract for demo purposes, where users can “buy” subnames like alice.mail.eth directly inside the dApp.

For decentralized messaging, we integrated XMTP. Each registered ENS subname automatically generates an inbox tied to the user’s wallet, where they can send and receive wallet to wallet messages. The .limo gateway was also simulated, so profiles and inboxes could be accessed at subname.mail.eth.limo in the demo.

We handled wallet authentication using ethers.js + wagmi for smooth MetaMask connection flows. Environment variables store Sepolia RPC URLs, contract addresses, and XMTP keys.

One of the hacky parts was simulating ENS subdomain minting without relying on the official ENS contracts, since deploying the full ENS registry isn’t feasible in a hackathon timeframe. Instead, we wrote a lightweight mock ENS registry contract that maps subnames to wallet addresses and integrated it into the UI flow. This allowed us to demonstrate the full experience subdomain purchase, profile creation, and inbox generation while staying on Sepolia testnet.

This combination of ENS + XMTP + React gave us a working prototype of a Web3 Gmail: decentralized identity, messaging, and profiles, all powered by your wallet.

background image mobile

Join the mailing list

Get the latest news and updates