StarkShield
Private payroll & payments & donations on Starknet
Created on 4th December 2025
•
StarkShield
Private payroll & payments & donations on Starknet
The problem StarkShield solves
Real-World Use Cases
- Private Payroll: Companies can pay employees without revealing individual salaries or employer-employee relationships on-chain
- Anonymous Donations: Donors can support causes confidentially without exposing their identity or donation amounts
- Confidential Business Payments: Businesses can make payments without revealing commercial relationships, pricing, or transaction volumes to competitors
- Personal Financial Privacy: Individuals can transact without building a public financial profile linked to their address
Technical Improvements
- Breaking Transaction Graphs: Prevents blockchain analysis from linking senders and recipients through transaction history
- Amount Privacy: Hides transaction values while maintaining verifiable correctness through zero-knowledge proofs
- Double-Spend Prevention: Uses cryptographic nullifiers instead of transparent spent markers, preserving privacy while preventing fraud
- UTXO Fragmentation Solution: 2-in-2-out design prevents accumulation of many small notes that would make future transactions expensive
Challenges I ran into
1. Poseidon Hash Inconsistency Across Platforms
Problem: Noir circuits computed different Merkle roots than Cairo contracts for the same data, causing all proofs to fail verification.
Solution:
- Used Garaga's poseidon_hash_2_bn254 in Cairo to match Noir's BN254 parameters exactly
- Wrote tests: computed same hash in Noir, Cairo, and JavaScript (circomlibjs)
2. Race Condition with Merkle Root Updates
Problem: User generates proof against root R1 at block N. Before transaction confirms, another user deposits, changing root to R2. Original proof becomes invalid.
Solution: Implemented circular buffer storing last 100 historical roots in the contract. Transfer verification accepts proofs against any known root (current or recent historical). Old roots rotate out after ~100-200 transactions, providing sufficient time window while preventing unbounded
storage growth.
3. Encryption Keys Without Wallet Private Key Access
Problem: Users need to decrypt on-chain ciphertexts to identify their notes, but Starknet wallets don't expose private keys or provide decryption APIs.
Solution: Signature-derived encryption keys - users sign a deterministic message ("Generate encryption keys"), then client derives secp256k1 keypair from the signature via SHA-256. Same wallet always produces same encryption keys, enabling cross-device note recovery without storing keys on-chain.
4. UTXO Fragmentation Problem
Problem: Like Bitcoin, initial 1-input-2-output design caused rapid note fragmentation. User deposits 100 tokens, makes 5 transfers of 10 tokens each, ends up with 6+ small notes.
Solution: Implemented 2-input-2-output UTXO model:
- Each transfer consumes 2 notes, creates 2 notes (one to recipient, one as change)
- Users can efficiently combine fragmented notes during regular transfers
- Dummy notes (amount 0) used when only 1 real input needed
5. Breaking Transaction Linkability with Nullifiers
Problem: Initial design marked commitments as "spent" directly, but this reveals which old commitment was consumed to create new commitments - observers could trace Alice → Bob by linking spent commitment A to new commitments B and C, completely breaking privacy.
Solution: Implemented cryptographic nullifiers using Poseidon(commitment, secret). When spending, users publish the nullifier instead of revealing which commitment was spent. Since:
- Only the note owner knows the secret to compute the valid nullifier
- Nullifiers are one-way hashes that don't reveal the original commitment
- Each commitment produces a unique nullifier (prevents double-spending)
Result: On-chain observers see nullifier N and new commitments B, C but cannot determine which old commitment A was consumed, breaking the transaction graph.
Tracks Applied (1)
Cross-Chain Privacy Solutions
Starknet