ninah
Private stablecoin payments on Base
Created on 16th January 2026
•
ninah
Private stablecoin payments on Base
The problem ninah solves
Over $1 billion in stablecoins moves on-chain every month. Transaction volumes have exploded from just $1.69 billion in January 2019 to a record $969.9 billion in August 2025 one of the fastest adoption curves in digital asset history. Stablecoins now account for over 40% of all cryptocurrency trading volume and facilitate the majority of market activity by both transaction count and value. The market cap has surged from $4.17 billion in 2020 to $283.7 billion in September 2025.
But here's the problem: every single one of these transactions is permanently visible on a public ledger. Anyone can trace what you spend, how much you hold, who you interact with, and your complete financial history.
Yet despite this massive stablecoin scale, Web3 adoption remains limited not because users don’t care about privacy, but because onboarding is a nightmare. Over 60% of potential users abandon onboarding due to wallet setup, gas fees, and seed phrase management. The core issue is that users are asked to manage 12-24 word seed phrases with the constant threat of permanent fund loss, understand why they need ETH just to send USDC, and decode cryptic hex addresses like 0x7a16...3f2b instead of simple usernames.
ninah fixes this by creating a privacy-preserving stablecoin payment system built on Base that eliminates both the privacy problem and the UX nightmare in one solution.
What ninah Makes Possible
-
Pay anyone privately - Enter username and amount. Stealth address
generation happens automatically. No crypto knowledge required. -
Receive without exposure - Your public username maps to unique
one-time addresses. Senders never see your main wallet. -
Familiar UX - Email login, no gas fees, no seed phrases.
-
No network confusion - Locked to Base. No chain switching, no wrong-network mistakes.
Who This Is For
| User | Problem Today | With ninah |
|---|---|---|
| Freelancers | Clients see your entire payment history when you invoice them | Send @username, client sees nothing else |
| Employers | Paying salaries on-chain exposes everyone's compensation | Each employee receives to a unique stealth address |
| Everyday users | Paying a friend reveals your wallet balance | Private by default, no extra steps |
| Merchants | Customer payments create linkable transaction graphs | Each payment arrives at an unlinkable address |
Privacy and usability have always been tradeoffs. ninah eliminates that
tradeoff by hiding complexity stealth addresses and ZK proofs run in the
background while users just see @usernames and one-click payments.
Challenges I ran into
Challenges I Ran Into
1. ZK Proof Generation Costs
Problem: Generating real SP1 zero-knowledge proofs requires computational
resources and funds for on-chain verification. During development and testing,
this created a bottleneck every username registration or payment claim needed
a real proof.
Solution: Implemented a MockSP1Verifier contract that accepts validly structured proofs without cryptographic verification. The frontend generates properly-encoded public values (username hash, commitment) so the architecture mirrors production.
2. Username Privacy Leak
Problem: Our initial design stored usernames in plaintext on-chain. This
defeated the privacy purpose anyone could enumerate all registered usernames
and link them to wallet addresses.
Solution: Usernames are now stored as keccak256(username) hashes. The
registration function checks usernameHash → address mapping, returning
"username taken" without revealing what usernames exist. Users store their
plaintext username locally and can recover it by re-entering it (hash comparison).
Privacy preserved, UX intact.
3. Three-Layer Payment Scanning
Problem: Detecting incoming stealth payments required extracting the
ephemeralPubkeyHash to verify ownership. But ERC-4337 smart wallet
transactions wrap the actual payment call in multiple layers:
| Layer | Contract | Function |
|---|---|---|
| 1 | EntryPoint | handleOps(UserOperation[]) |
| 2 | Smart Wallet | execute(address, value, calldata) |
| 3 | Ninah | sendToStealth(address, amount, ephemeralPubkey) |
The ephemeralPubkeyHash is emitted in the StealthPaymentSent event and
stored on-chain, but to verify a payment belongs to user, we need to recover
the full ephemeralPubkey which meant unwrapping all three layers of calldata.
Solution: Built a recursive decoder in useStealthPayments.ts that:
- Fetches StealthPaymentSent events from the contract
- Retrieves the originating transaction hash
- Decodes Layer 1 → extracts UserOperation calldata
- Decodes Layer 2 → extracts smart wallet inner call
- Decodes Layer 3 → extracts ephemeralPubkey from sendToStealth args
- Uses ephemeralPubkey + viewing key to verify if payment is ours
This handles the full ERC-4337 flow regardless of paymaster or bundler configuration.
4. Claiming Breaks Privacy
Problem: Stealth addresses hide the recipient during payment but claiming creates a new leak. When a user claims funds, the transaction is sent from their main wallet. on the on-chain this creates a visible link:
StealthAddress → claimed by → MainWallet
Anyone watching can now associate the stealth address with the user's identity,
defeating the privacy we worked to build.
Status: Active research. Current approaches being evaluated:
| Approach | Tradeoff |
|---|---|
| Relayer network | User signs message, relayer submits tx. Adds trust assumption + fees. |
| Claim to new wallet | Preserves privacy but fragments funds. UX complexity. |
| ZK claim proof | Prove ownership without revealing claimer. Higher gas + complexity. |
| Delayed batch claims | Obscure timing correlation. Partial solution only. |
Tracks Applied (1)
Base Track
Technologies used
