How It Works

Step by Step

1. User Signs a Transaction

The user creates a standard EIP-1559 transaction in their wallet (e.g., "Send 1 ETH to 0xAlice"). The wallet signs this transaction normally — no special UI or workflow changes.

2. Proxy Intercepts

The RPC proxy (running as the wallet's RPC endpoint) intercepts the eth_sendTransaction call. Instead of forwarding it to the blockchain, the proxy:

  • Parses the transaction to extract the intent (recipient, amount, token)

  • Checks whether the recipient has a registered receiving key

  • Selects funding notes (UTXOs) from the sender's shielded balance

3. ZK Proof Generation

The proxy feeds the transaction data and funding notes into a Noir circuit, which generates a zero-knowledge proof that:

  • The sender authorized this specific transaction (ECDSA signature verification)

  • The funding notes exist in the Merkle tree (membership proof)

  • The funding notes have not been spent before (nullifier uniqueness)

  • The input amounts cover the output amounts plus fees (balance check)

  • The new notes are correctly encrypted for the recipient

4. Relayer Submission

The proof and public inputs are sent to a relayer, which:

  • Submits the transaction to the Nullmask smart contract

  • Pays the gas fees (reimbursed from the shielded transaction's fee allocation)

  • Hides the sender's IP address and identity

5. On-Chain Verification

The Nullmask contract:

  • Verifies the ZK proof using the on-chain verifier

  • Records note nullifiers (preventing double-spending)

  • Records the transaction nullifier (preventing replay attacks)

  • Adds new note commitments to the Merkle tree

  • Emits encrypted note data in events for the recipient to scan

6. Recipient Scans

The recipient's proxy continuously scans blockchain events for new notes. It trial-decrypts each note using the recipient's viewing key. Successfully decrypted notes are added to the recipient's shielded balance.

UTXO Model

Nullmask uses a UTXO (Unspent Transaction Output) model similar to Bitcoin and Zcash. Each unit of value in the privacy pool is stored as a note — an encrypted container with:

  • Owner identity (hashed receiving key)

  • Value and token type

  • Random trapdoors for hiding the owner and value

  • Link to the action that created it (nullifiers hash)

Note commitments are stored on-chain in a Merkle tree. Only the commitment is visible on-chain; the actual note contents are encrypted and emitted in event logs.

Last updated