Nullifiers & Ownership

Nullifiers & Ownership

Stealth Address Linkage and Preventing Double-Spends with Zero Disclosure


SnarkSide replaces the conventional model of wallet-based ownership with a system built around nullifiers — cryptographic identifiers derived from private key material and entropy salts, which serve as one-time-use markers of action or ownership. Within this framework, users control margin vaults, trade intents, and position states without ever exposing their address or identity on-chain.

This section explains the role of nullifiers in SnarkSide’s shielded trading system, their relation to stealth address linkage, and how they enforce single-use constraints (such as vault spending or trade execution) without disclosing wallet addresses, account balances, or ownership status.


Why Ownership Must Be Hidden

In traditional perp DEX models, ownership is explicitly address-linked:

  • Wallets map directly to margin balances.

  • Liquidations, funding payments, and executions all mutate on-chain msg.sender state.

  • Traders’ activity becomes traceable through time, often feeding adversarial machine learning models or MEV targeting logic.

SnarkSide removes msg.sender from the equation entirely. The chain never sees who issued a trade, opened a position, or owns a vault. Instead, all ownership is abstracted behind non-replayable nullifiers that are:

  • One-time use

  • Derived from a secret trapdoor

  • Unique to the committed object (e.g. intent or vault)

  • Non-invertible

This enables users to prove they own something, without revealing what they own or who they are.


Nullifier Construction

Nullifiers are derived using a one-way Poseidon hash function, typically:

nullifier = Poseidon(owner_secret, salt)

Where:

  • owner_secret is a private scalar (derived from a stealth address keypair or trapdoor).

  • salt is a random or domain-separated value (e.g. intent ID, vault ID, epoch).

This guarantees:

  • Collision resistance: no two nullifiers will overlap unless the exact preimage is reused.

  • Non-linkability: no observer can tie a nullifier to a wallet, address, or other commitment.

  • One-time verifiability: the nullifier is publicly posted when an object is consumed, preventing reuse.

Nullifiers function as ZK-bound serial numbers. Once a nullifier is used, it becomes a cryptographic tombstone — visible to the chain, but meaningless to attackers.


Role in CipherVault and Trade Intents

Vault Commitments

Every vault (i.e. encrypted margin state) includes an ownership nullifier embedded at commitment time:

vault_commitment = Poseidon(
  margin_amount,
  expiry,
  salt,
  nullifier_owner
)

To spend this vault (e.g. to close a position), the user must:

  1. Provide a SNARK that proves knowledge of the commitment preimage.

  2. Reveal nullifier_owner publicly as part of the settlement proof.

  3. Demonstrate the nullifier is not reused (checked on-chain).

At no point is the user’s wallet or address revealed. The chain only sees that a valid vault was spent once — and never again.


Trade Intents

Trade intents are also bound to nullifiers:

intent_commitment = Poseidon(
  notional,
  direction,
  expiry,
  nullifier_intent,
  salt
)

This prevents a trader from reusing the same trade multiple times — even if encrypted and matched off-chain repeatedly.

Only one match can settle a specific intent. If a relayer or user attempts to match it again, the nullifier will already be marked used.


Stealth Address Linkage

While the protocol does not expose addresses, users often want continuous control across multiple trades and vaults. SnarkSide supports this via stealth address derivation:

  1. User generates a root keypair sk, pk.

  2. For each trade or vault, the user derives a new one-time subkey or salt via PRF:

salt_n = H(sk || domain_separator || nonce)
  1. This salt is used to generate a fresh nullifier:

nullifier_n = Poseidon(sk, salt_n)
  1. The vault or intent is committed with nullifier_n.

Only the user with sk can regenerate the salt/nullifier pair and thus prove ownership in a settlement SNARK.

This gives the user a continuous, unlinkable ownership trail, without ever registering an address.


Preventing Double-Spends

SnarkSide enforces one-time use of nullifiers via an on-chain registry:

mapping(bytes32 => bool) public nullifierUsed;

function markNullifier(bytes32 nullifier) internal {
    require(!nullifierUsed[nullifier], "Nullifier already used");
    nullifierUsed[nullifier] = true;
}

Any attempt to settle a trade or vault transition with a previously used nullifier will revert.

This check is:

  • Stateless (no knowledge of accounts or balances)

  • Efficient (single SSTORE)

  • Cryptographically unforgeable (due to preimage secrecy)


Summary

Nullifiers in SnarkSide provide a stateless, cryptographic alternative to addresses. They enable:

  • Encrypted object ownership without disclosure

  • Replay prevention for vaults and trades

  • Shielded account behavior through one-time identifiers

  • Stealth linkage of off-chain user history

Together, they form the core of SnarkSide’s privacy-preserving state machine — one where ownership is proven, not declared.

Last updated