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.rootCompatible 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
}