Skip to content

Repository files navigation

Verum

ZK-Gated Compliance on Stellar

A verification layer for tokenized real-world assets. Bridge privacy and strict regulatory requirements instantly with zero-knowledge proofs.

Demo Site→  ·  Demo Video →  ·  GitHub →

ZK-gated eligibility layer for tokenized real world assets on Stellar. Investors prove accreditation status, jurisdictional eligibility, and committed capital in a single zero knowledge proof. A Soroban smart contract verifies the proof on chain and authorizes the investor's trustline without the issuer collecting, storing, or accessing the underlying identity or financial data.

Built for the Stellar Hacks: Real-World ZK hackathon.

Live Demo

Frontend: https://verum-stellar.vercel.app

Testnet Contracts (Stellar Testnet — Protocol 27):

Contract ID
UltraHonk Verifier CAPYRFFCUDA5PA5MNMW4YXERXJKKRHJY3YVAGGYN6Z4NMEHQ3GF7MH75
Verum Gate CDDIWIOH7WWOLRLM752P7X6P56G6BC572D6V42EWK7RVG7U72AWC6PXO
VERUM SAC CA3O5DGQCKHEPR7A2AQYZGCM5UTT3SLTK3Q2FP5UW4KKZ4OUWDWSWWYK
Issuer (alice) GA7I5KL4USD53U2XL7UVUICBSY3PQEHWECNAUXGEO42FILRBZSGHCBKQ

Confirmed testnet authorization transactions:

The Problem

Stellar's AUTH_REQUIRED trustline flag enables issuers to control which accounts can hold regulated assets. This capability is essential for tokenized real world assets. Products such as Franklin Templeton's BENJI fund, tokenized bond platforms, and regulated stablecoins use authorized trustlines to enforce compliance requirements including investor accreditation, jurisdictional eligibility, and minimum capital thresholds.

The limitation is that authorization decisions require the issuer to verify each investor's eligibility using sensitive personal information. Today, this means collecting and processing identity documents, KYC records, accreditation evidence, and financial information before authorizing a trustline.

As a result, compliant RWA issuers become custodians of highly sensitive personal data for every eligible holder. This increases regulatory obligations, expands the attack surface for data breaches, and creates unnecessary privacy risk. The dependency exists because Stellar's authorization model does not natively support zero knowledge proofs of regulatory eligibility. Authorization decisions therefore require off chain verification of investor identity and compliance data before a trustline can be approved.

Existing approaches do not eliminate the underlying trust assumption.

Approach Limitation
Issuer managed KYC databases Issuers collect and store investor identity and compliance data, increasing regulatory obligations and data breach risk.
On chain allowlists (Securitize, Tokeny) Authorization is based on off chain identity verification. The issuer or identity provider still maintains the mapping between investors and authorized accounts.
Nethermind Privacy Pools Protects transaction privacy but does not address issuer side compliance or trustline authorization.
Stellar association sets and view keys Improve selective disclosure but still rely on a trusted service provider with access to the underlying compliance data.

None of these approaches remove the requirement for a trusted party to possess and verify the underlying identity and compliance information. Verum replaces that requirement with zero knowledge proofs that can be verified on chain.

The Solution

Replace manual eligibility verification with a zero-knowledge proof.

During onboarding, an investor commits their eligibility attributes using a Pedersen hash without disclosing the underlying identity or financial data. When requesting trustline authorisation, the investor generates a Noir proof that the committed attributes satisfy the issuer's published eligibility policy. A Soroban smart contract verifies the proof on-chain using BN254 pairing verification. Once the proof is validated, the authorisation workflow approves the investor's trustline without exposing the underlying eligibility data.

The issuer publishes an eligibility policy and verification key instead of maintaining a database containing investor identity documents and compliance records.

The privacy guarantee is independent of regulatory interpretation. The issuer never receives the underlying personally identifiable information required to generate the proof, eliminating the need to store, secure, or disclose that data as part of the authorisation workflow.

Why Verum Is Different

Existing compliance architectures for regulated digital assets rely on a trusted party to maintain an allowlist derived from off-chain identity verification. The trusted party may be the issuer, a transfer agent, or a compliance service provider, but the underlying identity database still exists.

Verum replaces that dependency with zero knowledge proofs. Trustline authorization is based on cryptographic verification of eligibility rather than disclosure of identity and compliance records.

Our review of existing implementations found no prior system that combines zero knowledge proof based eligibility verification with Stellar trustline authorization.

  • Existing RWA platforms such as Securitize, Tokeny, and Polymesh rely on conventional identity verification with allowlists derived from off chain compliance processes.
  • Nethermind Privacy Pools focuses on transaction privacy rather than issuer side authorization.
  • Stellar's association sets architecture improves selective disclosure but still depends on a trusted provider to maintain eligibility information.

The zero knowledge proof is a core protocol component rather than an added privacy feature. It enables authorization decisions to be verified without requiring the issuer or another trusted intermediary to access the underlying identity and compliance data.

Architecture

System Overview

flowchart TD
    H[Holder] -->|Private inputs| C[Verum Circuit\nNoir / UltraHonk]
    C -->|ZK Proof + Public Inputs| G[Verum Gate\nSoroban Contract]
    G -->|verify_proof call| V[UltraHonk Verifier\nSoroban Contract]
    V -->|BN254 pairing_check\nnative host function| V
    V -->|valid / panic| G
    G -->|SetTrustLineFlags| S[Stellar Asset Contract\nVERUM SAC]
    S -->|trustline authorized| H
    I[Issuer] -->|publishes merkle_root\nand min_threshold| G
    I -->|issuer.require_auth| G

    style C fill:#1a1a3e,stroke:#4646C8,color:#fff
    style G fill:#1a1a3e,stroke:#4646C8,color:#fff
    style V fill:#1a1a3e,stroke:#4646C8,color:#fff
    style S fill:#1a1a2e,stroke:#27272A,color:#A1A1AA
    style H fill:#0f1515,stroke:#27272A,color:#A1A1AA
    style I fill:#0f1515,stroke:#27272A,color:#A1A1AA
Loading

The Three-Contract Stack

┌─────────────────────────────────────────────────────────────┐
│  UltraHonk Verifier Contract                                │
│  yugocabrio/rs-soroban-ultrahonk — uses Stellar's native    │
│  BN254 host functions (g1_add, g1_mul, pairing_check).      │
│  Stores VK at deploy time. Passes testnet budget.           │
│  verify_proof(public_inputs, proof_bytes) → () or panic     │
├─────────────────────────────────────────────────────────────┤
│  Verum Gate Contract                                        │
│  Policy + Application layer. Calls verifier, then SAC.      │
│  verify_and_authorize(holder, proof, inputs) → ()           │
├─────────────────────────────────────────────────────────────┤
│  Stellar Asset Contract (VERUM SAC)                         │
│  Protocol-native compliance flags. AUTH_REQUIRED enforced.  │
│  SetTrustLineFlags → trustline authorized                   │
└─────────────────────────────────────────────────────────────┘

This separation follows the canonical Stellar ZK pattern: verifier handles cryptographic validity only, the Gate handles business logic, and the SAC handles state transition. Each layer has exactly one responsibility.

Critical verifier choice: The indextree/ultrahonk_soroban_contract reference implementation performs BN254 math inside WASM and exceeds Stellar testnet's per-transaction CPU budget (Error(Budget, ExceededLimit)). yugocabrio/rs-soroban-ultrahonk offloads elliptic curve operations to Stellar's native Protocol 25 BN254 host functions (g1_add, g1_mul, pairing_check), which run outside the WASM VM at a fraction of the cost. This is why the hackathon resources list both — the native-host-function implementation is the one that actually works on public networks.

Data Model

Each holder/investor commits one Pedersen hash at onboarding:

commitment = pedersen_hash(accredited_flag, country_code, committed_capital, secret_nonce)
Field Visibility Description
accredited_flag Private 0 or 1 — accredited investor status
country_code Private ISO 3166-1 numeric — holder's jurisdiction
committed_capital Private Available capital in USD as u64
secret_nonce Private Random blinding value — makes commitment unlinkable across issuers
commitment Public (on-chain) Pedersen hash of all four private fields
merkle_root Public (on-chain) Root of issuer's permitted-jurisdiction Merkle tree
min_capital_threshold Public (on-chain) Issuer's minimum capital requirement

The commitment is computed server-side per-request using a standalone commitment_hasher Noir circuit that mirrors the main circuit's Pedersen hash exactly — ensuring byte-identical semantics without any external library dependency.

The Circuit

The Verum circuit (verum_circuit/src/main.nr) proves four things simultaneously in a single UltraHonk proof. All four must hold — there is no partial credit.

fn main(
    // Private inputs — known only to the holder, never transmitted
    accredited_flag: Field,
    country_code: Field,
    committed_capital: u64,
    secret_nonce: Field,
    sibling0: Field,
    direction0: bool,
    sibling1: Field,
    direction1: bool,

    // Public inputs — known to both holder and on-chain verifier
    commitment: pub Field,
    merkle_root: pub Field,
    min_capital_threshold: pub u64
) {
    // Check 1: Commitment binding
    let computed_commitment = std::hash::pedersen_hash(
        [accredited_flag, country_code, committed_capital as Field, secret_nonce]
    );
    assert(computed_commitment == commitment);

    // Check 2: Accreditation — equality check
    assert(accredited_flag == 1);

    // Check 3: Jurisdiction — Merkle membership check
    let leaf = std::hash::pedersen_hash([country_code]);
    let node0 = if direction0 {
        std::hash::pedersen_hash([sibling0, leaf])
    } else {
        std::hash::pedersen_hash([leaf, sibling0])
    };
    let computed_root = if direction1 {
        std::hash::pedersen_hash([sibling1, node0])
    } else {
        std::hash::pedersen_hash([node0, sibling1])
    };
    assert(computed_root == merkle_root);

    // Check 4: Capital — range check
    assert(committed_capital >= min_capital_threshold);
}

Three distinct proof shapes in one circuit:

  • Equality check (accreditation)
  • Merkle set-membership (jurisdiction — proves membership without revealing which permitted country)
  • Range proof (capital — proves sufficiency without revealing the exact amount)

The commitment check binds all private inputs together — an adversary cannot mix-and-match valid field values from different holders or different sessions to fabricate a passing proof.

Permitted Jurisdiction Tree

For the demo, four jurisdictions are committed into a 4-leaf Pedersen Merkle tree:

                    root
                 0x2356709c...
                /             \
          node01               node23
       0x11969ea7...        0x27888d46...
       /         \           /         \
   leaf0(US)  leaf1(GB)  leaf2(CA)  leaf3(DE)

The issuer publishes only the root. A holder proves their country code is one of the four leaves via a 2-sibling Merkle path — revealing nothing about which specific country they are. To test rejection, select "North Korea (KP) — Not Permitted" in the demo — the Merkle membership check fails before any proof is generated.

Proving Pipeline

Proof generation is real, per-input, and input-specific — not pre-generated. Every /api/prove request:

  1. Runs commitment_hasher circuit (Noir) to compute the holder's Pedersen commitment from their private inputs
  2. Writes a Prover.toml with the computed commitment and Merkle path
  3. Runs nargo execute (beta.22) for constraint validation — fast rejection with specific error messages before paying proving cost
  4. If constraints pass: switches to nargo beta.9 (local binary copy, ~instant), regenerates witness, runs bb v0.87.0 prove with --scheme ultra_honk --oracle_hash keccak --output_format bytes_and_fields
  5. Restores nargo beta.22

Proof generation time: ~1.3 seconds. Nargo version switching uses pre-cached local binaries at ~/.nargo-versions/{beta22,beta9}/nargo — not noirup network downloads (~7-12 minutes per switch empirically measured).

A serialization mutex prevents concurrent requests from corrupting the shared Prover.toml and witness files.

End-to-End Flow

sequenceDiagram
    participant H as Holder
    participant FE as Frontend (Vercel)
    participant BE as Backend (Express + Cloudflare Tunnel)
    participant N as Noir toolchain
    participant G as Verum Gate
    participant V as Verifier Contract
    participant S as VERUM SAC

    H->>FE: Enter private eligibility data
    FE->>BE: POST /api/prove
    BE->>N: commitment_hasher circuit → commitment
    BE->>N: nargo execute (constraint check)
    N-->>BE: witness solved / constraint failure
    BE->>N: bb v0.87.0 prove (real per-input proof)
    N-->>BE: 14,592 bytes proof
    BE-->>FE: proof bytes + public inputs
    FE->>H: Show proof generated
    H->>FE: Click "Submit to Verum Gate"
    FE->>BE: POST /api/authorize
    BE->>G: verify_and_authorize(holder, proof, inputs)
    G->>V: verify_proof(public_inputs, proof_bytes)
    V->>V: BN254 native host pairing_check
    V-->>G: () or panic
    G->>S: set_authorized(holder, true)
    S-->>G: trustline authorized
    G-->>BE: tx hash
    BE-->>FE: success
    FE->>H: ✓ Trustline Authorized + stellar.expert link
Loading

Technology Stack

Technology Role Why
Noir (v1.0.0-beta.22) ZK circuit language Rust-like DSL, catches under-constrained circuits at compile time
Barretenberg (v0.87.0) On-chain proving backend Required for --oracle_hash keccak --output_format bytes_and_fields — must match verifier compiled version
Barretenberg (v5.0.0-nightly) Development proving Circuit iteration only — not used for on-chain artifacts
yugocabrio/rs-soroban-ultrahonk UltraHonk verifier Uses native BN254 host functions — passes testnet CPU budget. indextree variant exceeds budget.
Soroban (Rust) Smart contract platform Native BN254 pairing_check host functions (Protocol 25+)
BN254 / UltraHonk Proof system Natively supported by Stellar's Protocol 25/26 host functions
Pedersen hash Commitment / Merkle hashing ZK-friendly, stable in Noir stdlib, verified against official test vectors
Stellar Asset Contract (SAC) Trustline authorization SetTrustLineFlags is the on-chain primitive — Verum wires ZK verification directly into this existing mechanism
env.invoke_contract() Cross-contract calls Bypasses contractimport! macro XDR encoding mismatches
@stellar/stellar-sdk v16 Stellar JS SDK Transaction building, signing, submission
Express.js Backend Proof generation pipeline + Stellar transaction submission
Vite Frontend build Static build deployed to Vercel
Cloudflare Tunnel Backend exposure Exposes local Express server publicly without cloud deployment
commitment_hasher Standalone Noir circuit Computes per-request Pedersen commitment matching main circuit exactly

Key engineering decisions

Why yugocabrio not indextree? The indextree verifier performs BN254 elliptic curve math entirely inside Soroban WASM, consuming ~500M+ CPU instructions per verification — well above Stellar testnet's per-transaction budget. yugocabrio/rs-soroban-ultrahonk delegates curve operations to Stellar's native g1_add, g1_mul, and pairing_check host functions (Protocol 25, CAP-0074), which execute outside the WASM VM. Verification confirmed within testnet budget with null return.

Why env.invoke_contract() not contractimport!? The macro generates a typed client from the WASM spec, but its ABI for Bytes arguments differs from how the live deployed contract receives them cross-contract. env.invoke_contract() bypasses this layer and passes raw host objects.

Why two bb versions? The on-chain verifier was compiled against bb v0.87.0 with --oracle_hash keccak --output_format bytes_and_fields. Newer bb produces incompatible wire formats. The solution: ~/bb_v087/bb for on-chain-compatible artifacts, current nightly for circuit development.

Why Pedersen hash over Poseidon2? Poseidon2 is pub(crate) in Noir stdlib at v1.0.0-beta.22 — intentionally private to stdlib internals. Pedersen hash is fully public, stable, and has published official test vectors.

Why per-request proving? The original server returned identical pre-generated proof bytes regardless of inputs — passing any input combination would return the same proof. This was a critical credibility flaw: a judge inspecting two submissions would see identical proofHex. The current server computes a real, input-specific commitment and generates a real UltraHonk proof per request, verifiable on-chain against the submitted public inputs.

Why local binary switching not noirup? noirup re-fetches the toolchain binary over the network on every invocation (~7-12 minutes measured empirically). Both nargo versions are pre-cached at ~/.nargo-versions/{beta22,beta9}/nargo and switched via local file copy (~instant).

Repository Structure

verum/
├── verum_circuit/              # Noir ZK circuit
│   ├── src/main.nr             # Four-check eligibility circuit
│   ├── Prover.toml             # Demo input values
│   └── target/
│       ├── proof               # Generated per-request at runtime
│       ├── public_inputs       # Generated per-request at runtime
│       ├── vk                  # Verification key (deployed with verifier contract)
│       └── verum_circuit.json  # Compiled circuit artifact
│
├── commitment_hasher/          # Standalone Noir circuit for per-request commitment
│   └── src/main.nr             # Mirrors main circuit's pedersen_hash exactly
│
├── verum_gate/                 # Soroban Gate contract (Rust)
│   └── contracts/verum-gate/
│       └── src/lib.rs          # verify_and_authorize → verify_proof → set_authorized
│
├── rs-soroban-ultrahonk/       # yugocabrio verifier (native BN254 host functions)
│
├── frontend/                   # Full application
│   ├── server.cjs              # Express backend — proving pipeline + Stellar calls
│   ├── src/main.js             # Browser demo logic (Vite module)
│   ├── index.html              # Full page: nav, hero, platform, demo, footer
│   ├── vite.config.js          # Vite config with node polyfills
│   └── .env                    # Testnet config (not committed)
│
└── Dockerfile                  # Ubuntu 24.04 + Node 22 + nargo beta.22/beta.9 + bb v0.87.0

Honest Scope Statement

What is real and fully functional on Stellar testnet:

  • Noir circuit with four checks, proven and verified on-chain (Protocol 27)
  • UltraHonk proof verification via native BN254 pairing_check host function
  • Per-request, input-specific proof generation (~1.3s) — not pre-generated artifacts
  • Dynamic Pedersen commitment computed from actual holder inputs per request
  • Cross-contract proof-gated trustline authorization via SetTrustLineFlags
  • Live frontend at https://verum-stellar.vercel.app — publicly accessible

What is simplified for the demo, with the simplification labeled:

  • Holder secret key UX: The demo asks the holder to paste their Stellar secret key into the frontend to sign the trustline creation transaction. In production this is replaced by Freighter wallet (or any Stellar wallet) signing client-side — the private key never leaves the holder's device. The cryptographic flow is identical; only the signing mechanism changes.
  • Self-attested commitment: The holder's commitment is computed from self-declared inputs. In production, a licensed verifier (broker-dealer, CPA, attorney) would sign the commitment at onboarding — the cryptographic flow is identical, only the trust root changes.
  • Static jurisdiction set: Four countries in a demo Merkle tree. A production issuer updates the Merkle root as jurisdictions change.

What is explicitly deferred:

  • Sanctions screening — OFAC SDN-list non-membership proofs require a dynamic denylist with governance. Architecturally identical to the Merkle membership pattern, operationally distinct.
  • Holding-cap proofs — requires live attestation updates after each transfer, not one-time onboarding commitment.
  • Licensed verifier integration — business/legal problem distinct from the cryptographic problem this project solves.
  • Freighter wallet integration — replaces the secret key paste in Step 1; straightforward but out of scope for the demo.

Setup & Installation

Prerequisites

Requirement Version Install
WSL2 (Ubuntu 24.04) 24.04+ wsl --install
Rust 1.96+ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
Noir (nargo) 1.0.0-beta.22 noirup -v 1.0.0-beta.22
Barretenberg (bb) 0.87.0 See note below
Stellar CLI 27.0.0 cargo install --locked stellar-cli
Node.js 22+ Via NodeSource or nvm
cloudflared latest sudo apt install cloudflared

Pinned bb v0.87.0 (required for on-chain-compatible proof generation):

mkdir -p ~/bb_v087
cd ~/bb_v087
curl -L https://github.com/AztecProtocol/aztec-packages/releases/download/aztec-packages-v0.87.0/barretenberg-amd64-linux.tar.gz -o bb.tar.gz
tar -xzf bb.tar.gz && chmod +x bb && ./bb --version  # should print 0.87.0

Pre-cache both nargo versions (eliminates network downloads at runtime):

mkdir -p ~/.nargo-versions/beta22 ~/.nargo-versions/beta9
cp ~/.nargo/bin/nargo ~/.nargo-versions/beta22/nargo   # while beta.22 is active
noirup -v 1.0.0-beta.9
cp ~/.nargo/bin/nargo ~/.nargo-versions/beta9/nargo
noirup -v 1.0.0-beta.22   # restore default

Clone and install

git clone https://github.com/phllp-tanstic/verum.git
cd verum
git submodule update --init --recursive
cd frontend && npm install

Environment variables

Create frontend/.env:

ALICE_SECRET=S...                        # Issuer's Stellar secret key
GATE_CONTRACT=CDDIWIOH7WWOLRLM752P7X6P56G6BC572D6V42EWK7RVG7U72AWC6PXO
VERIFIER_CONTRACT=CAPYRFFCUDA5PA5MNMW4YXERXJKKRHJY3YVAGGYN6Z4NMEHQ3GF7MH75
SAC_CONTRACT=CA3O5DGQCKHEPR7A2AQYZGCM5UTT3SLTK3Q2FP5UW4KKZ4OUWDWSWWYK
ALICE_PUBKEY=GA7I5KL4USD53U2XL7UVUICBSY3PQEHWECNAUXGEO42FILRBZSGHCBKQ
NETWORK=testnet
RPC_URL=https://soroban-testnet.stellar.org
NETWORK_PASSPHRASE=Test SDF Network ; September 2015

Running Locally

Start backend

cd ~/verum/frontend
PORT=3001 node server.cjs

Expose publicly (Cloudflare Tunnel)

cloudflared tunnel --url http://localhost:3001

Set the printed trycloudflare.com URL as VITE_API_URL in your Vercel project environment variables and redeploy.

Frontend dev server

cd ~/verum/frontend
npm run dev   # http://localhost:5173 (proxies /api → localhost:3001)

Startup script

~/verum/start.sh   # starts server on 3001 + cloudflared tunnel in tmux

Running the Demo

  1. Setup tab: paste a funded Stellar testnet secret key. Generate one free at lab.stellar.org. Click Connect — creates trustline on VERUM asset.

  2. Generate Proof tab: select jurisdiction, accreditation status, capital amount, nonce. Click Generate. The backend computes a real commitment, validates constraints, and generates a real UltraHonk proof specific to your inputs (~1.3s).

  3. Authorize tab: click Submit. The proof is verified on-chain by the Verum Gate contract via BN254 native host functions. On success, your trustline is authorized and a stellar.expert link to the transaction is shown.

To demo rejection: select "Not Accredited" or "North Korea (KP) — Not Permitted". The constraint solver fails before any proof is generated and returns a specific error message identifying which check failed.

Proof Artifacts

Every proof is generated fresh per-request:

# Step 1: compute commitment via standalone hasher circuit
nargo execute   # in commitment_hasher/ — outputs pedersen_hash of holder inputs

# Step 2: validate constraints (fast rejection path)
nargo execute   # in verum_circuit/ — fails here if any check fails

# Step 3: generate on-chain-compatible proof
~/.nargo-versions/beta9/nargo execute   # witness in beta.9 format
~/bb_v087/bb prove \
  -b target/verum_circuit.json \
  -w target/verum_circuit.gz \
  -o target \
  --scheme ultra_honk \
  --oracle_hash keccak \
  --output_format bytes_and_fields

# Step 4: restore default toolchain
~/.nargo-versions/beta22/nargo   # via file copy, not noirup

Proof size: 14,592 bytes (456 field elements × 32 bytes). Public inputs: 96 bytes (3 field elements: commitment, merkle_root, min_capital_threshold).

Future Work

Feature Description Complexity
Freighter wallet integration Replace secret key paste with client-side signing via Freighter — private key never leaves holder device Low engineering, high UX impact
Sanctions screening OFAC SDN-list non-membership proof against a regularly-updated Merkle root Medium — same Merkle pattern, harder operationally
Holding-cap proofs Prove current holdings below a concentration limit High — requires live attestation updates per transfer
Licensed verifier integration Broker-dealer, CPA, or attorney signs commitment at onboarding Business/legal, not engineering
Multi-issuer registry Shared commitment registry so holders prove once across issuers Medium engineering
Named Cloudflare tunnel Permanent backend URL surviving restarts, eliminating manual Vercel env var updates Low — requires domain
Mainnet deployment Deploy to Stellar mainnet with real XLM Straightforward — toolchain identical to testnet

Architecture Summary

Zero knowledge is the core of the protocol. The proof is what allows trustline authorization without exposing eligibility data. Without it, the issuer must fall back to conventional identity review and direct access to investor information.

Built for Stellar's authorization model. Verum integrates Stellar's native trustline authorization workflow, the Stellar Asset Contract, and pairing_check host functions. The system is designed around primitives that already exist on Stellar rather than adapting a generic zero knowledge application to the network.

Aligned with Protocol 25 and 26. UltraHonk verification runs through Stellar's native BN254 host functions using yugocabrio/rs-soroban-ultrahonk, which delegates elliptic curve operations to the host instead of executing them in WASM. That matches the performance model introduced in Protocol 25 and refined in Protocol 26, making on-chain verification practical within network resource limits.

Clear implementation boundaries. The cryptographic protocol, circuit, proof generation, and on-chain verification are implemented end to end. The remaining work for production deployment is operational, including regulated identity attestation providers, issuer workflows, and regulatory acceptance of zero knowledge based compliance.

A distinct compliance model. Our review of Stellar projects, privacy infrastructure, and tokenized asset platforms did not surface another implementation that combines zero knowledge eligibility proofs with Stellar trustline authorization to replace issuer-managed identity databases. Verum demonstrates that architecture as a working system.

Built With


Built for Stellar Hacks: Real-World ZK · June 2026

About

ZK-gated eligibility layer for tokenized real-world assets on Stellar. Prove accreditation, jurisdiction, and capital in one zero-knowledge proof without revealing private data.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages