Shielded transfers
Sanect's privacy module lets you shield SNCT into a private pool, send privately within the pool, and unshield back to a public address. Your address never links to what you send -- you choose per transaction whether to transact publicly or privately.
The system uses a custom 2-in/2-out Noir circuit on Aztec's universal Plonk SRS (structured reference string). No per-circuit trusted setup ceremony is required -- the universal SRS is already public and audited.
How it works
Shielded transfers have three steps:
1. Shield (deposit)
Deposit public SNCT into the shielded pool. Your SNCT leaves your public balance and a private commitment is added to the pool's Merkle tree. Only you (with your viewing key) know the commitment belongs to you.
2. Send (private transfer)
Transfer SNCT within the pool from one set of commitments to another. The sender, receiver, and amount are all hidden. The zk-SNARK proof ensures the transfer is valid (no double-spending, correct balances) without revealing any details.
3. Unshield (withdraw)
Withdraw SNCT from the pool back to any public EVM address. The pool burns your private commitment and sends SNCT to the recipient via an internal call{value}.
Key derivation
Your shielded wallet keys are derived from a MetaMask signature:
- MetaMask prompts you to
personal_signa frozen derivation message. - The signature is fed through HKDF-SHA256 (RFC 5869) with domain-separated labels to produce:
- A viewing key -- lets you scan the chain and identify your notes.
- A spending key -- lets you create valid proofs to spend your notes.
- An X25519 public key is derived from the viewing key for encrypted memo exchange.
Your keys never leave your browser. The derivation message is frozen -- the same signature always produces the same keys.
Encrypted memos
When you shield or send, an encrypted memo is attached to the transaction so the recipient can identify their note:
- Encryption: X25519 ECDH + ChaCha20-Poly1305.
- Fresh ephemeral keypair per memo -- no key reuse across transactions.
- Memo size: 240 bytes (144 bytes ciphertext + 96 bytes reserved for future fuzzy message detection).
- Trial decryption: Your wallet tries to decrypt every memo on chain. Most will fail (they belong to other users) -- failures are silently discarded.
Browser proving
Proof generation runs entirely in your browser. No server-side prover, no trusted third party.
- Stack:
@noir-lang/noir_js+UltraHonkBackend(Aztec's Barretenberg wasm build). - Performance: ~2--3 seconds on a modern laptop.
- Circuit artifacts are loaded from the dApp's static assets on first use.
The "Shielded" button in the dApp is gated by browserProvingReady() -- it only appears once the wasm prover and circuit artifacts have loaded.
Mainnet contracts
| Contract | Address |
|---|---|
| Poseidon2Bn254 | 0xaEE4A735027b86ccbF43042f4C31d1a856943c1C |
| HonkVerifier | 0x580d7c1Ff04a5761D58a8Db08C37fb33c5e395f2 |
| ShieldedPool v1.4 | 0x5532Dc2B743D45Fd0121E382696521ED62DD3ecf |
Testnet contracts
| Contract | Address |
|---|---|
| Poseidon2Bn254 | 0xE4AdD1a1aA6E9df2037b4285D0E8b052fdB6bB04 |
| HonkVerifier | 0x8fDd518356e570D72f426Bc4d1485d46c60b74Cd |
| ShieldedPool v1.4 | 0x0571969a9C0554D908387255a30DE5c392AA4b8e |
Merkle tree
The shielded pool uses a depth-32 incremental Poseidon-2 Merkle tree. Each leaf is a commitment to a note (containing the value, asset ID, owner, and blinding factor).
Initial root (depth-32 Poseidon-2 zeros):
0x0b59baa35b9dc267744f0ccb4e3b0255c1fc512460d91130c6bc19fb2668568dThe pool maintains a ring buffer of 32 recent roots, so a proof generated against a slightly stale root is still accepted.
Anonymity set
All users share a single shielded pool -- one global anonymity set. Unlike per-contract or per-user models, every shield and send contributes to the same pool, making it harder to correlate senders and receivers as adoption grows.
Current anonymity-set statistics are visible on the explorer at scan.sanect.com/shielded.
UI
The shielded transfer interface is available in the Staking dApp:
- Go to app.sanect.com.
- Click the Shielded button.
- Click Derive to generate your viewing and spending keys (MetaMask will prompt for a signature).
- Use the Shield, Send, or Unshield tabs.
The panel shows your shielded balance (number of unspent notes) and the pool's TVL and anonymity-set size.
What's next
- Off-chain note indexer -- wallets currently trial-decrypt every memo from the pool's deploy block. An off-chain indexer will pre-filter, cutting sync time from minutes to seconds at scale.
- Fuzzy message detection (FMD) -- Penumbra-style FMD will let wallets skip most irrelevant memos entirely, further reducing sync overhead. The 96-byte FMD reservation in the memo format is already allocated for this.
- External audit -- the privacy module is undergoing external audit before handling significant value.
- ERC-20 shielding -- the pool contract's storage layout already supports multi-asset via
asset_id. ERC-20 shielding is additive (no redeploy needed).
Next step: Full privacy model technical details