🧠 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 nullifier has 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

Share this project:

Updates