UPP — Universal Private PoolConcepts

Account Model

UTXO-style notes and commitments — how UPP stores private balances on-chain without revealing amounts or ownership.

Design Status

The note structure and commitment scheme are under active development. The model described here may evolve as we finalize compliance requirements.

Notes, Not Balances

UPP uses notes (like Bitcoin UTXOs) instead of account balances:

Ethereum:  Address → Balance (1000 tokens)
UPP:       Note A (500) + Note B (300) + Note C (200) = 1000 tokens

Transactions consume notes and create new ones.

Note Structure

interface Note {
  amount: bigint       // Token amount
  blinding: bigint     // Random value (hides commitment)
  origin: Address      // Original depositor (for compliance)
  sender: Address      // Who sent this note
  token: Address       // ERC-20 contract
}

Origin vs Sender:

  • origin = who first deposited. Used for ASP compliance. Only changes via merge.
  • sender = who sent this specific note. Changes each transfer.

Commitments

Notes are stored on-chain as Poseidon hashes over BLS12-381:

ownerHash  = Poseidon(spendingSecret)
commitment = Poseidon(amount, ownerHash, blinding, origin, token)

Properties:

  • Hiding: Can't see note contents from the commitment
  • Binding: Can't create duplicate commitments (blinding ensures uniqueness)
  • Token-locked: No transmutation attacks across token types
  • Hash-based ownership: No elliptic curve operations — ownerHash is a Poseidon hash of the spending secret, not a public key on BabyJubJub. This simplifies the circuit and makes the ownership model field-agnostic.

Nullifiers

Prevent double-spending:

nullifier = Poseidon(spendingSecret, leafIndex, commitment)

When spent:

  1. Nullifier computed inside ZK proof (never revealed: spendingKey stays private)
  2. Nullifier published on-chain
  3. Contract records it as spent
  4. Same note can never be spent again

Merkle Tree

All commitments stored in a LeanIMT:

  • Depth: 32 levels
  • Hash: Poseidon over BLS12-381 scalar field
  • Capacity: ~4 billion notes
  • On-chain: Custom InternalLeanIMTBLS12381 fork of zk-kit with BLS12-381 field constant

Transfer Example

Alice sends 600 of 1000 UPD to Bob:

Note A is nullified. Notes B and C are new leaves in the tree.

On this page