import { b as IHashFunction, c as MerkleProof, M as MembershipProof, A as ASPClientConfig, R as RegisterASPOptions, P as PublishRootOptions, I as IASPProvider } from './types-EgAvBFvJ.js'; /** * Poseidon Hash over BN254 * * ~100-bit security (post Kim-Barbulescu 2016). * Uses circomlibjs for compatibility with Circom circuits compiled for BN254. * * Use PoseidonBLS12381 for 128-bit security (institutional/audit requirements). */ /** * BN254 field prime (used by Ethereum precompiles, Circom default) */ declare const BN254_FIELD_PRIME = 21888242871839275222246405745257275088548364400416034343698204186575808495617n; declare class PoseidonBN254 implements IHashFunction { readonly name = "Poseidon-BN254"; readonly fieldPrime = 21888242871839275222246405745257275088548364400416034343698204186575808495617n; readonly securityBits = 100; hash2(left: bigint, right: bigint): Promise; hashN(inputs: bigint[]): Promise; } /** * Poseidon Hash over BLS12-381 * * 128-bit security — meets institutional audit requirements. * Uses the poseidon-bls12381 npm package with constants derived from * the official Poseidon reference implementation. * * This is the DEFAULT hash function for the UPC SDK. * * Compatible with: * - Circom circuits compiled with `--prime bls12381` * - poseidon-bls12381-circom Circom templates * - EIP-2537 on-chain verification (Pectra, live since May 2025) */ /** * BLS12-381 scalar field prime */ declare const BLS12_381_FIELD_PRIME = 52435875175126190479447740508185965837690552500527637822603658699938581184513n; declare class PoseidonBLS12381 implements IHashFunction { readonly name = "Poseidon-BLS12-381"; readonly fieldPrime = 52435875175126190479447740508185965837690552500527637822603658699938581184513n; readonly securityBits = 128; hash2(left: bigint, right: bigint): Promise; hashN(inputs: bigint[]): Promise; } /** * Hash module — pluggable ZK-friendly hash functions * * Default: PoseidonBLS12381 (128-bit security) * Alternative: PoseidonBN254 (100-bit security, Circom/Ethereum legacy) */ /** * The default hash function for the UPC SDK. * BLS12-381 Poseidon — 128-bit security. */ declare function getDefaultHashFunction(): PoseidonBLS12381; /** * Merkle Tree * * LeanIMT-compatible Merkle tree with pluggable hash function. * Default hash: Poseidon over BLS12-381 (128-bit security). */ /** * Default tree depth (supports ~1M members) */ declare const DEFAULT_TREE_DEPTH = 20; /** * Maximum tree depth */ declare const MAX_TREE_DEPTH = 32; /** * LeanIMT-compatible Merkle Tree * * Features: * - Dynamic depth (grows as needed up to maxDepth) * - Single-child optimization (LeanIMT: when sibling is 0, propagate current) * - Pluggable hash function (default: Poseidon-BLS12-381, 128-bit security) * - Async hash operations */ declare class MerkleTree { private leaves; private nodes; private cachedRoot; readonly maxDepth: number; readonly hashFn: IHashFunction; constructor(maxDepth?: number, hashFn?: IHashFunction); /** * Insert a leaf into the tree */ insert(leaf: bigint): number; /** * Get the number of leaves in the tree */ get size(): number; /** * Get the current depth of the tree */ get depth(): number; /** * Check if a leaf exists in the tree */ has(leaf: bigint): boolean; /** * Get the index of a leaf */ indexOf(leaf: bigint): number; /** * Get the current root (async because hash is async) */ getRoot(): Promise; /** * Get a Merkle proof for a leaf at index */ getProof(index: number): Promise; /** * Get the hash of a node at (level, index) */ private getNodeHash; /** * Compute the root hash */ private computeRoot; } /** * Build a Merkle tree from an array of leaves */ declare function buildMerkleTree(leaves: bigint[], maxDepth?: number, hashFn?: IHashFunction): MerkleTree; /** * Verify a Merkle proof using the specified hash function. * Defaults to BLS12-381 Poseidon (128-bit security). */ declare function verifyMerkleProof(leaf: bigint, proof: MerkleProof, hashFn?: IHashFunction): Promise; /** * Membership Proof Generation * * Generate ZK-ready membership proofs for ASP trees. * * Two proof-shape conventions live here: * * - **LeanIMT (SNARK / BLS12-381 side, default).** Dynamic depth with * zero-sibling propagation. `verifyMembershipProof` mirrors the * convention: when `pathElements[i] === 0n`, skip the hash and * propagate `current`. The verifying circuit on the SNARK side * replays the same rule, so zero-padding extra levels is a no-op. * - **Always-hash, fixed-depth (STARK / Poseidon31 side).** No * propagation; every level hashes regardless of the sibling. The * in-trace AIR (`Poseidon31MerklePathEval` at `ASP_TREE_DEPTH = 20`) * uses this convention, so a LeanIMT proof must be transformed via * `padLeanIMTProofToDepth` before being fed to the prover. * * Default hash: Poseidon over BLS12-381 (128-bit security) for the * LeanIMT path; Poseidon31 over M31 for the always-hash path. */ /** * Generate a membership proof for a single-member tree. * * With LeanIMT single-child optimization, a single-leaf tree * has root = leaf, and all path elements are 0. */ declare function generateSingleMemberProof(identity: bigint, treeDepth?: number): MembershipProof; /** * Generate a membership proof for a multi-member tree. * * @param identity - The identity to prove membership for * @param allMembers - All members in the tree * @param treeDepth - Target tree depth for padding (default: 20) * @param hashFn - Hash function to use (default: Poseidon-BLS12-381) */ declare function generateMultiMemberProof(identity: bigint, allMembers: bigint[], treeDepth?: number, hashFn?: IHashFunction): Promise; /** * Generate a membership proof, automatically choosing single or multi-member mode. * * @param identity - The identity to prove membership for * @param members - All members in the tree * @param treeDepth - Target tree depth for padding (default: 20) * @param hashFn - Hash function to use (default: Poseidon-BLS12-381) */ declare function generateMembershipProof(identity: bigint, members?: bigint[], treeDepth?: number, hashFn?: IHashFunction): Promise; /** * Compute the Merkle root for a set of members. */ declare function computeMerkleRoot(members: bigint[], treeDepth?: number, hashFn?: IHashFunction): Promise; /** * Verify a membership proof locally. * * @param identity - The identity to verify * @param proof - The membership proof to check * @param hashFn - Hash function to use (default: Poseidon-BLS12-381) */ declare function verifyMembershipProof(identity: bigint, proof: MembershipProof, hashFn?: IHashFunction): Promise; /** * Padded path + AIR-shape root produced by [`padLeanIMTProofToDepth`]. * * `root` is the AIR-shape root: the value an always-hash verifier of * `targetDepth` levels lands on after starting from the leaf and * processing `pathElements` / `pathIndices`. Crucially this is **not** * (in general) the LeanIMT root, because the always-hash semantics * differs from LeanIMT propagation: * * - When the LeanIMT proof carries a zero sibling at level `i`, the * LeanIMT root computation skips the hash and propagates `current`. * - The AIR (and `verifyMerklePath`) hashes anyway. Once you replay * a LeanIMT proof through always-hash semantics, the resulting * root differs from the LeanIMT root the moment any sibling was * zero or the dynamic LeanIMT depth was below `targetDepth`. * * The on-chain pool's `pub_asp_root` is whatever the AIR computes, so * this `root` is what the asp-whitelist must publish for STARK paths. * The `pathElements` / `pathIndices` fields satisfy the contract * `verifyMerklePath(leaf, pathElements, pathIndices) === root`. */ interface AIRShapeProof { /** Path siblings at the AIR's fixed depth. Length equals `targetDepth`. */ pathElements: bigint[]; /** * Path index bits, one per level. Length equals `targetDepth`. * Bigint to match the in-circuit M31 representation that * `verifyMerklePath` consumes. */ pathIndices: bigint[]; /** * AIR-shape root: the value `verifyMerklePath` recovers given * `(leaf, pathElements, pathIndices)`. Differs from the input * LeanIMT root whenever propagation kicked in. */ root: bigint; } /** * **TRANSITIONAL — not the long-term canonical tree model.** This is * a compatibility shim for adapting LeanIMT proofs (the SDK's existing * `MerkleTree` shape) to the always-hash Poseidon31 AIR until the * STARK-side tree itself is rebuilt under always-hash semantics * (`AlwaysHashMerkleTree`, planned). The helper's per-leaf output is * self-consistent, but in sparsely-populated LeanIMT trees different * leaves produce different roots, so it cannot back a single * `pub_asp_root` shared by every member — see the limitation note * below before reaching for it in production paths. * * Transform a LeanIMT proof into a self-consistent, always-hash, * fixed-depth proof the in-trace Poseidon31 ASP-Merkle AIR can * consume. * * Algorithm (proof of correctness sits inline in * `pad-lean-imt-proof-to-depth.test.ts`): * * 1. **Strip propagation levels.** Drop every `(sibling = 0n, * indexBit)` pair from the input — those are LeanIMT no-ops that * the AIR has no analogue for. Keep the relative order of the * surviving non-zero levels. * 2. **Replay survivors through always-hash.** Walking from the * leaf, fold each kept level through the supplied `hashTwoFn` * (default: Poseidon31 `hashTwo`) using the level's `indexBit`. * 3. **Extend with a zero-hash chain to `targetDepth`.** For every * remaining level, append `(sibling = 0n, indexBit = 0n)` and * fold `current = hashTwoFn(current, 0n)`. The output's * `pathElements`/`pathIndices` are length `targetDepth`; the * final `current` is the AIR-shape root the prover will see. * * `verifyMerklePath(leaf, output.pathElements, output.pathIndices)` * is guaranteed by construction to return `output.root`. * * # Limitation: cross-leaf root agreement * * In a sparsely-populated LeanIMT tree (`leafCount` not a power of * two), different leaves carry different numbers of propagation * levels in their proofs. Stripping then padding produces depth-20 * proofs that all self-verify but **land on different roots across * leaves**. Concretely, for a 5-leaf tree the leftmost leaf has 3 * non-zero LeanIMT levels (3 real hashes + 17 zero pads = 20), while * the rightmost has 1 (1 real hash + 19 zero pads = 20). The two * roots differ. * * This is fine for verifying *one* membership proof end-to-end (the * AIR replays exactly what this helper computed), but it means the * asp-whitelist cannot use the LeanIMT-then-pad shape to publish a * single `pub_asp_root` shared by every member. That requires * building the tree itself under always-hash semantics — every * internal node hashes regardless, empty subtrees represented by * precomputed `ZSR(k)` values. Tracked as a Phase 7 follow-up. * * Until that follow-up lands, downstream consumers should call this * helper only when: * - The LeanIMT proof has no zero siblings (the `leafCount` is a * power of two and the leaf sits in the dense subtree), OR * - They are operating against a single-membership root produced * for that specific leaf (i.e., not a globally-published one). * * @param leaf — The leaf value the proof attests membership for. * @param pathElements — Sibling at each LeanIMT level. * @param pathIndices — Side bit at each LeanIMT level (0 = current is * left, 1 = current is right). Accepted as `number[]` (the * `MerkleTree.getProof()` shape) or `bigint[]`. * @param targetDepth — The AIR's fixed depth (e.g. `20` for ASP-Merkle, * `32` for state-Merkle). * @param hashTwoFn — Pair-hash override. Defaults to the Poseidon31 * `hashTwo` that the AIR's chain primitive uses. Override only for * unit tests or alternative AIRs. * @throws if `pathElements.length` exceeds `targetDepth` (a LeanIMT * path strictly longer than the AIR depth has nowhere to go) or if * `pathElements.length !== pathIndices.length`. */ declare function padLeanIMTProofToDepth(leaf: bigint, pathElements: bigint[], pathIndices: readonly (number | bigint)[], targetDepth: number, hashTwoFn?: (left: bigint, right: bigint) => bigint): AIRShapeProof; /** * Identity Derivation * * Utilities for computing ZK-friendly identity commitments * from various key formats. */ /** * Compute a Poseidon-based identity commitment from a BabyJubJub public key. * * This is the standard identity format for SNARK circuits. * * @param pubKeyX - X coordinate of the BabyJubJub public key * @param pubKeyY - Y coordinate of the BabyJubJub public key * @returns Identity commitment as Poseidon(pubKeyX, pubKeyY) */ declare function computeIdentityFromBabyJubJub(pubKeyX: bigint, pubKeyY: bigint): Promise; /** * Compute an identity commitment from an Ethereum address. * * For simple allowlists where the identity is just the address. * The address is cast to a field element (uint160 fits in BN254 field). * * @param address - Ethereum address (hex string) * @returns Address as bigint */ declare function computeIdentityFromAddress(address: `0x${string}`): bigint; /** * Compute a Poseidon-hashed identity from a secret. * * Similar to Semaphore v4's identity model: identity = hash(secret). * * @param secret - A secret value known only to the user * @returns Identity commitment as Poseidon(secret) */ declare function computeIdentityFromSecret(secret: bigint): Promise; /** * ASP Client * * High-level API for interacting with the ASP system. * Orchestrates between providers (off-chain storage) and * the blockchain (on-chain registry). */ /** * ASP Client instance */ declare class ASPClient { private readonly provider; private readonly publicClient; private readonly registryAddress; private readonly hashFn; private aspId; constructor(config: ASPClientConfig); /** * Register a new ASP on-chain */ register(options: RegisterASPOptions): Promise; /** * Get the current ASP ID */ getASPId(): bigint | undefined; /** * Set the ASP ID (for existing registrations) */ setASPId(aspId: bigint): void; /** * Add a member identity to the tree */ addMember(identity: bigint): Promise; /** * Remove a member identity from the tree */ removeMember(identity: bigint): Promise; /** * Get all members */ getMembers(): Promise; /** * Check if an identity is a member */ hasMember(identity: bigint): Promise; /** * Get the current Merkle root from the provider */ getRoot(): Promise; /** * Generate a membership proof for an identity */ generateProof(identity: bigint): Promise; /** * Publish the current Merkle root on-chain */ publishRoot(options: PublishRootOptions): Promise<`0x${string}`>; /** * Publish a STARK-side Merkle root on-chain. * * The STARK side maintains a parallel tree (typically over the same * membership set) using a STARK-friendly hash (e.g., Poseidon31). The * root is supplied by the caller because the SNARK provider doesn't * compute it — the caller is expected to manage a `MerkleTree` with a * `PoseidonM31` hash and pass its root here. * * Operators MUST publish in lock-step with the SNARK root so both * proof systems stay aligned (an attacker who can update only one of * the two roots can keep the other stale and bypass compliance on * one path). */ publishStarkRoot(starkRoot: bigint, options: PublishRootOptions): Promise<`0x${string}`>; /** * Check if a root is valid on-chain (current or historical) */ isValidRoot(root: bigint): Promise; /** * Check if a STARK-side root is valid on-chain (current or historical). */ isValidStarkRoot(starkRoot: bigint): Promise; /** * Get the current on-chain root */ getCurrentOnChainRoot(): Promise; /** * Get the current on-chain STARK root. */ getCurrentOnChainStarkRoot(): Promise; /** * Get the provider */ getProvider(): IASPProvider; } /** * Create a new ASP client */ declare function createASPClient(config: ASPClientConfig): ASPClient; /** * ASP List — Types and Utilities * * An ASP list is a curated registry of Association Set Providers for a specific chain, * similar to Uniswap's token lists. Platform operators maintain their own ASP lists * to control which compliance providers their users can choose from. * * UPC provides the types and loader — the lists themselves are maintained by consumers. */ /** * ASP type classification */ type ASPType = 'auto-whitelist' | 'kyc' | 'sanctions' | 'accredited-investor' | 'custom'; /** * Security level of an ASP */ type ASPSecurityLevel = 'demo' | 'production' | 'institutional'; /** * A single ASP entry in the list */ interface ASPEntry { /** On-chain ASP identifier (from ASPRegistryHub.registerASP()) */ aspId: number; /** Chain ID this ASP operates on */ chainId: number; /** Human-readable name */ name: string; /** Type of compliance check */ type: ASPType; /** Description of what this ASP verifies */ description: string; /** Operator address (who manages the Merkle tree) */ operator: `0x${string}`; /** ASPRegistryHub contract address */ registryAddress: `0x${string}`; /** * URL to fetch membership proofs. * Expected endpoints: * GET {proofEndpoint}/{address} → { root, pathElements, pathIndices } * GET {proofEndpoint}/../root → { root } * GET {proofEndpoint}/../status → { memberCount, ... } */ proofEndpoint: string; /** * URL for user registration (null for auto-whitelist ASPs). * For KYC ASPs, this links to the verification portal. */ registrationUrl: string | null; /** Security level */ securityLevel: ASPSecurityLevel; /** Whether this ASP is currently active */ active: boolean; /** Optional logo URL */ logoUrl?: string; } /** * ASP list for a specific chain */ interface ASPList { /** List name */ name: string; /** Chain ID */ chainId: number; /** Semantic version */ version: string; /** Last update date (ISO 8601) */ updatedAt: string; /** ASP entries */ asps: ASPEntry[]; } /** * Load an ASP list from a JSON object. * Validates the structure and returns typed entries. */ declare function parseASPList(json: unknown): ASPList; /** * Filter an ASP list to only active entries */ declare function getActiveASPs(list: ASPList): ASPEntry[]; /** * Find an ASP by ID in a list */ declare function findASPById(list: ASPList, aspId: number): ASPEntry | undefined; /** * Fetch a membership proof from an ASP's proof endpoint */ declare function fetchASPProof(asp: ASPEntry, address: `0x${string}`): Promise<{ root: bigint; pathElements: bigint[]; pathIndices: number[]; }>; export { ASPClient as A, BLS12_381_FIELD_PRIME as B, DEFAULT_TREE_DEPTH as D, MAX_TREE_DEPTH as M, PoseidonBLS12381 as P, type ASPEntry as a, type ASPList as b, type ASPSecurityLevel as c, type ASPType as d, BN254_FIELD_PRIME as e, MerkleTree as f, PoseidonBN254 as g, buildMerkleTree as h, computeIdentityFromAddress as i, computeIdentityFromBabyJubJub as j, computeIdentityFromSecret as k, computeMerkleRoot as l, createASPClient as m, fetchASPProof as n, findASPById as o, generateMembershipProof as p, generateMultiMemberProof as q, generateSingleMemberProof as r, getActiveASPs as s, getDefaultHashFunction as t, parseASPList as u, verifyMembershipProof as v, verifyMerkleProof as w, type AIRShapeProof as x, padLeanIMTProofToDepth as y };