UPH — Universal Private Helpers

TypeScript Utilities

Reusable TypeScript utilities for Merkle trees, Poseidon hashing, stealth addresses, and note encryption.

Status

These utilities currently live in upp-sdk/src/utils/ and upc-sdk/src/core/. They will be extracted into @permissionless-technologies/uph once ready for standalone publication.

TypeScript Utilities

Merkle Tree (LeanIMT)

A Lean Incremental Merkle Tree implementation compatible with on-chain verification.

import { LeanIMT } from '@permissionless-technologies/uph'

const tree = new LeanIMT({
  hashFunction: poseidonBLS12381, // or poseidonBN254
})

// Add leaves
await tree.insert(leaf1)
await tree.insert(leaf2)

// Generate membership proof
const proof = tree.generateProof(leafIndex)
// { leaf, pathElements, pathIndices, root }

// Verify proof locally
const valid = tree.verifyProof(proof)

// Get current root
const root = tree.root

Compatible with: On-chain LeanIMT Merkle trees used in UPP and UPC.


Poseidon Hash Wrappers

PoseidonBLS12381

Poseidon hash over BLS12-381 scalar field. 128-bit security.

import { PoseidonBLS12381 } from '@permissionless-technologies/uph'

const hash = PoseidonBLS12381.hash([a, b, c])
const hash2 = PoseidonBLS12381.hash2(a, b)

PoseidonBN254

Poseidon hash over BN254 scalar field (Groth16 compatible). ~100-bit security.

import { PoseidonBN254 } from '@permissionless-technologies/uph'

const hash = PoseidonBN254.hash([a, b, c])

Compatible with: circomlibjs Poseidon — same outputs for ZK circuit verification.


Stealth Address Generation

Hash-based stealth addresses for recipient privacy.

import { StealthAddress } from '@permissionless-technologies/uph'

// Generate keys from wallet signature
const { spendingKey, viewingKey } = StealthAddress.deriveKeys(signature)

// Encode stealth meta-address (bech32m, 0zk prefix)
const metaAddress = StealthAddress.encodeMetaAddress({
  spendingKey: spendingKey.publicKey,
  viewingKey: viewingKey.publicKey,
  chainId: 1,
})

// Generate one-time payment address for a recipient
const { oneTimeAddress, ephemeralPubKey, encryptedData } =
  StealthAddress.generatePaymentAddress(recipientMetaAddress)

// Scan for received payments
const received = StealthAddress.scan(events, viewingKey.privateKey)

Note Encryption

AES-256-GCM encryption for private note data.

import { NoteEncryption } from '@permissionless-technologies/uph'

// Encrypt a note
const { ciphertext, iv } = NoteEncryption.encrypt({
  note: { amount, blinding, origin, token },
  sharedSecret: ecdhSharedSecret,
})

// Decrypt a note
const note = NoteEncryption.decrypt({
  ciphertext,
  iv,
  sharedSecret: ecdhSharedSecret,
})

Identity Derivation

Derive ZK identity commitments from various sources.

import { Identity } from '@permissionless-technologies/uph'

// From Ethereum address
const commitment = Identity.fromAddress('0x...', poseidonBLS12381)

// From BabyJubJub key
const commitment = Identity.fromBabyJubJub(publicKey, poseidonBN254)

// From arbitrary secret
const commitment = Identity.fromSecret(secret, poseidonBLS12381)

Circom Templates

Circuit templates available for inclusion in your own Circom circuits:

include "@permissionless-technologies/uph/circuits/MerkleProof.circom";
include "@permissionless-technologies/uph/circuits/MembershipProof.circom";

MerkleProof(levels)

template MerkleProof(levels) {
    signal input leaf;
    signal input root;
    signal input pathElements[levels];
    signal input pathIndices[levels];

    // Proves: leaf is a member of tree with given root
}

MembershipProof(levels)

template MembershipProof(levels) {
    signal input identityCommitment;
    signal input merkleRoot;
    signal input pathElements[levels];
    signal input pathIndices[levels];
    signal output nullifier;

    // Proves membership in an ASP tree and outputs nullifier
}

On this page