🧠 Inspiration
Most Web3 chat or social platforms either sacrifice privacy or rely on trusted intermediaries.
If you want to send a message anonymously, someone still sees who paid, who relayed, or when it was scheduled.
If you want to send a message in the future, you usually have to trust an off-chain service or a centralized scheduler.
This project started with a simple question:
Can we send messages and payments on-chain — now or in the future — without revealing who we are, and without trusting anyone?
That question led to the idea of combining:
- Time-locked messages
- Anonymous funding
- Zero-knowledge proofs
- Purely on-chain scheduling
🛠️ How We Built It
The system is composed of three core smart contracts, each with a clearly separated responsibility.
1️⃣ Message Contracts (Atomic Messages)
Each message is its own smart contract, storing:
- message content
- sender alias (not identity)
- optional future timestamp
- a payment receiver address
This makes every message:
- immutable
- independently fundable
- auditable without revealing the sender
2️⃣ Chatroom (On-chain Messaging + Scheduling)
Each chatroom:
- stores posted messages
- supports future messages using an on-chain min-heap priority queue
- automatically releases messages when
[ \text{block.timestamp} \ge \text{sendingTime} ]
No cron jobs, no keepers, no centralized scheduler — just deterministic Solidity logic.
3️⃣ Chatsystem (ZK Vault + Anonymity Layer)
This is the privacy core of the project. Users use relayers to deposit money or send messages.
🔐 Anonymous Deposits
Users deposit a fixed denomination (0.1 ETH) into a global vault by submitting a cryptographic commitment: [ \text{commitment} = \text{PedersenHash(secret, nullifier)} ]
Commitments are stored in a Merkle tree, whose root represents all valid deposits.
🧾 Zero-Knowledge Withdrawal
To fund a message anonymously, a user generates a ZK proof that:
- they know a valid
(secret, nullifier) - the commitment exists in the Merkle tree
- the
nullifierhas never been used before
The contract verifies:
- Merkle root validity
- proof correctness
- nullifier uniqueness
Then it releases funds without learning who the sender is.
🧪 What We Learned
- How Merkle trees and nullifiers prevent double-spending without identities
- The importance of matching hash functions between ZK circuits and Solidity
- Designing on-chain data structures (like heaps) under gas constraints
- How relayers can exist without becoming trust points
- How ZK turns “prove without revealing” into a real UX primitive
⚔️ Challenges We Faced
🔹 Hash Compatibility
Noir uses Pedersen hashes, while Solidity does not natively support them.
We had to carefully ensure consistency between circuit logic and on-chain verification.
🔹 On-chain Scheduling
Ethereum has no native scheduler. Implementing a min-heap priority queue in Solidity and call only when any user asks for whole chat history .
🔹 Preventing Double-Spend
Ensuring that each anonymous deposit can only be used once required:
- nullifier tracking
- strict root validation
- replay protection
🔹 UX vs Cryptography
ZK systems are powerful but fragile. Small mistakes in inputs, ordering, or hashing break proofs — balancing correctness with usability was a constant challenge.
🌱 What’s Next
- Add encrypted message content (only recipients can decrypt)
- Build a front-end that abstracts ZK complexity for non-crypto users
✨ Final Thought
This project proves that privacy, time, and money can coexist on-chain —
without trust, without identity, and without intermediaries.
Messages can wait. Identities don’t have to exist.
Built With
- noir
- solidity
- zkproof
Log in or sign up for Devpost to join the conversation.