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.
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:
509a4c3556a863a058446e87ca33aa4cda12ab713905ce3bec4d66aea1c25dbc— view on stellar.expert47c1d9e6b4b38fe9df0f8bae3834708b21ac2d4adc0d96ffa45e45e7d26a70b8— view on stellar.expert
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.
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.
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.
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
┌─────────────────────────────────────────────────────────────┐
│ 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.
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 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.
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.
Proof generation is real, per-input, and input-specific — not pre-generated. Every /api/prove request:
- Runs
commitment_hashercircuit (Noir) to compute the holder's Pedersen commitment from their private inputs - Writes a
Prover.tomlwith the computed commitment and Merkle path - Runs
nargo execute(beta.22) for constraint validation — fast rejection with specific error messages before paying proving cost - If constraints pass: switches to nargo beta.9 (local binary copy, ~instant), regenerates witness, runs
bb v0.87.0 provewith--scheme ultra_honk --oracle_hash keccak --output_format bytes_and_fields - 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.
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
| 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 |
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).
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
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_checkhost 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.
| 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.0Pre-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 defaultgit clone https://github.com/phllp-tanstic/verum.git
cd verum
git submodule update --init --recursive
cd frontend && npm installCreate 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 2015cd ~/verum/frontend
PORT=3001 node server.cjscloudflared tunnel --url http://localhost:3001Set the printed trycloudflare.com URL as VITE_API_URL in your Vercel project environment variables and redeploy.
cd ~/verum/frontend
npm run dev # http://localhost:5173 (proxies /api → localhost:3001)~/verum/start.sh # starts server on 3001 + cloudflared tunnel in tmux-
Setup tab: paste a funded Stellar testnet secret key. Generate one free at lab.stellar.org. Click Connect — creates trustline on VERUM asset.
-
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).
-
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.
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 noirupProof size: 14,592 bytes (456 field elements × 32 bytes). Public inputs: 96 bytes (3 field elements: commitment, merkle_root, min_capital_threshold).
| 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 |
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.
- Noir — ZK circuit language
- Barretenberg — UltraHonk proving backend
- Soroban — Stellar smart contracts
- yugocabrio/rs-soroban-ultrahonk — UltraHonk verifier using native BN254 host functions
- Stellar CLI — Contract deployment and interaction
- @stellar/stellar-sdk — JavaScript SDK
Built for Stellar Hacks: Real-World ZK · June 2026