Skip to main content

Privacy Architecture

Imagine you have money in your wallet. Normally, when you use it on a blockchain, everyone can see how much you have, who you pay, and how much you send. Orbinum changes this using zero-knowledge cryptography, allowing you to make transactions where these details remain private.

This guide shows you how that privacy system works, what operations you can perform, and what guarantees you get.


What can you do in Orbinum?

Shield (Protect)

Deposit public tokens into the private pool. Your funds become confidential.

Transfer (Send)

Send private tokens to other users without revealing amounts or identities.

Unshield (Reveal)

Withdraw tokens from the private pool to a public account visible on the blockchain.

Note Disclosure

Share a disclosure key to prove the exact contents of a note — value and asset — without granting spending capability.


How it works: The Shielded Pool

The shielded pool is like a common vault where all private funds are mixed together. When you deposit tokens, they become notes — cryptographic receipts that only you can spend.

What is a "note"?

A note is like a private bill that contains:

  • Amount: how many tokens it represents
  • Asset ID: what type of token it is (ORB, USDT, etc.)
  • Owner: who can spend it (your public key)
  • Blinding: a random number to hide all of the above

Operation 1: Shield (Deposit to Private Pool)

When you shield, you convert public tokens into private ones by depositing them into the shielded pool.

Step-by-step flow

1
Create a note off-chain

Your wallet generates a note with your amount, asset ID, public key, and a random blinding factor

2
Calculate the commitment

commitment = Poseidon(amount, asset_id, owner_pubkey, blinding)

3
Send the shield transaction

The runtime locks your public tokens and adds the commitment to the active Merkle tree of the forest

4
Store your note locally

Your wallet keeps the complete note so it can spend it later. Losing the device is recoverable: the note's contents also travel on-chain inside an encrypted memo that only your wallet can read, so importing a note backup or running a manual rescan rebuilds the vault. What you must not lose is access to the wallet itself — the keys derive from its signature. See Note Discovery.

What is still public?

Visible
  • Your public account deposited funds
  • The deposit timestamp
  • Commitment (meaningless hash)
Hidden
  • Exact amount deposited
  • Asset ID (partially)
  • Blinding factor
  • Where those funds will go next

Batching

shield_batch deposits up to 20 notes in a single extrinsic, each with its own asset, amount, commitment, and memo. It emits one Shielded event per operation and fails atomically — if any single deposit is invalid, the whole batch reverts.

Useful when splitting a balance into several denominations at once, which is also good privacy hygiene: a set of round-numbered notes blends into the crowd better than one distinctive amount.


Operation 2: Private Transfer

A private transfer moves funds within the shielded pool without revealing who sends, who receives, or how much is sent.

Step-by-step flow

1
Select input notes

Your wallet searches for notes you own with enough balance to cover the amount to send

2
Create output notes

One note for the recipient (e.g., 50 USDT) and one for you with the change (e.g., 50 USDT remaining)

3
Generate ZK proofs off-chain

This takes 2-5 seconds. The proof validates that:

  • You own the input notes
  • The notes are in the Merkle tree
  • Amounts balance (input = output)
  • Nullifiers are correct
4
Send the transaction with the proof

The runtime verifies the ZK proof (~15ms), marks nullifiers as spent, and adds the new commitments

What remains public?

Visible
  • A private transaction occurred
  • Nullifiers (meaningless hashes)
  • New commitments (meaningless hashes)
  • Transaction timestamp
Hidden
  • Who sent
  • Who received
  • How much was sent
  • Which asset was used
  • Relationship between inputs and outputs
What is a "nullifier"?

A nullifier is a unique hash calculated from a note when you spend it. It's published on-chain to prevent that same note from being spent twice (double-spending). Without your private key, no one can link the nullifier to the original commitment, maintaining your privacy.


Operation 3: Unshield (Withdrawal from Private Pool)

Unshield takes tokens out of the private pool and sends them to a public account. This operation reveals the amount and the recipient.

Step-by-step flow

1
Select a private note

Choose a note with sufficient balance for the withdrawal

2
Generate ZK ownership proof

The proof demonstrates you own the note and that the amount is correct, without revealing other details

3
Send unshield transaction

Includes: ZK proof, nullifier, amount to withdraw, and recipient's public address

4
Funds are transferred publicly

The runtime verifies the proof, marks the nullifier as spent, and transfers tokens to the public account

What remains public?

Visible
  • Exact amount withdrawn
  • Public recipient
  • Asset ID
  • Timestamp
Hidden
  • Who withdrew the funds
  • Where the funds came from
  • How much balance remained in the note
  • Previous history of those funds
Consider this before unshielding

Unshield links your private identity with a public account. If someone observes that you deposited 100 USDT and then withdraw 100 USDT shortly after, they can infer those funds are probably yours. For maximum privacy, wait and mix with other users before withdrawing.


Operation 4: Note Disclosure

Note disclosure lets you prove the exact contents of a shielded note to any third party — value, asset, and commitment — without granting any spending capability over that note.

This is done through a disclosure key: a compact, shareable string with the prefix orbdisc: that encodes the plaintext preimage of the on-chain commitment.

What it reveals

Exact token amount, asset ID, owner public key (Baby JubJub Ax), and blinding scalar. The commitment is cryptographically re-verified from these inputs.

What it never reveals

Spending key, nullifier, EVM wallet address, and any other note owned by the same key. The recipient cannot spend or freeze the disclosed note.

Verified locally

Verification recomputes Poseidon4(value, asset_id, owner_pk, blinding) and checks it against the embedded commitment. No network call needed.

tip

See the Note Disclosure page for the full technical reference, key format, SDK usage, and security properties.


The Merkle Forest: Where Commitments Live

Commitments are stored in binary Merkle trees of depth 20. Each tree holds up to 1,048,576 notes; when one fills up it is sealed and a fresh tree opens automatically, so the pool never runs out of room.

Why a Merkle tree?

The Merkle tree allows proving that a note exists in the pool without revealing which one it is. When you spend a note, you generate a "Merkle proof" that demonstrates its inclusion in the tree without pointing to exactly where it is. This breaks the link between deposits and spends, maintaining your privacy.

Sealing and the forest

When a tree reaches its 1,048,576th leaf, the runtime seals it in the same block as that final insert:

  • The tree's final root becomes a permanent anchor. Unlike the rolling ring of recent roots, a sealed root never expires — so a note in a sealed tree stays spendable indefinitely, and a proof generated against it does not go stale.
  • The active tree resets to empty and subsequent commitments land in the next tree.

Global leaf indices are u32, which caps the forest at roughly 4,096 trees of depth 20. Your wallet does not need to track any of this: the leaf index determines the tree, and the SDK's treeIdOf resolves it.

Anonymity set and tree boundaries

A Merkle proof is proof of membership in one tree. Your anonymity set is the tree holding your note, not the entire pool history.

In practice this bounds the set at ~1M notes rather than fragmenting it in any surprising way, and in a young pool everyone shares tree 0. But it is a real property to be aware of: a note in a sparsely-populated freshly-opened tree has a smaller crowd to hide in than one in a full sealed tree.


Privacy Guarantees and Limitations

✅ What is protected

  • Amounts in private transfers (fully hidden)
  • Identities of sender and recipient in transfers
  • Relationship between inputs and outputs (unlinkable)
  • History of a note before being spent
  • Individual balances within the pool

⚠️ What remains visible

  • Shield/unshield operations link public accounts with the pool
  • Transaction timing (when they occur in each block)
  • Merkle tree growth (size of the anonymity set)
  • Asset type can be partially inferred in some cases
  • Unshield amounts are completely public
Best practices for privacy
  • Use common denominations: instead of 137.42 USDT, round to 100 or 150
  • Wait before unshielding: don't withdraw immediately after depositing
  • Mix with other users: the more transactions there are, the larger your anonymity set
  • Split funds into multiple notes: don't use a single large note

Multi-Asset: One Pool for All Tokens

Orbinum's shielded pool supports multiple assets (ORB, USDT, DAI, etc.) in the same pool, sharing one anonymity set rather than splitting into a separate tree per asset.

Advantages of the multi-asset approach

Unified anonymity set

All notes from all assets contribute to the same anonymity set, increasing everyone's privacy

Efficient verification

Asset-agnostic trees simplify proof verification compared to separate trees per asset

Future atomic swaps

The design facilitates private cross-asset exchanges in future versions


Learn More