Skip to content
jes-labsPublic

About

Open reserve-coverage registry on Stellar for anchors and custodial apps. A zero-knowledge proof that reserves cover what customers are owed, published on chain, without revealing any balance.

Topics

Resources

Contributing

Stars

3 stars

Watchers

1 watching

Forks

Latest commit

 

History

143 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

███████  ██████  ██      ██    ██  █████
██      ██    ██ ██      ██    ██ ██   ██
███████ ██    ██ ██      ██    ██ ███████
     ██ ██    ██ ██       ██  ██  ██   ██
███████  ██████  ███████   ████   ██   ██

Reserve coverage for anchors and custodial apps on Stellar. reserves ≥ liabilities, proven on Stellar without publishing a customer balance.

CI License: Apache 2.0

Stellar Soroban Noir + UltraHonk Rust Go TypeScript Next.js gRPC PostgreSQL

Solva is an open reserve-coverage registry on Stellar for anchors and custodial apps. Each proof cycle publishes a proof that an institution's reserves cover what it owes its customers (R >= L), without publishing any customer balance. The proof system's zero-knowledge (hiding) mode is in development. Cycles run on request through the API, or on a fixed interval when the scheduler is turned on. Daily scheduled publishing for live institutions is coming soon. Anyone can read the record on Stellar.

An audit samples a few accounts, takes weeks, and describes a day that has already passed. Solva adds a proof that can run every day between audits. Reserves come from the source that holds them: a custodian-signed balance, or the balance of Stellar accounts read straight from the ledger. Liabilities are committed into a Poseidon2 Merkle Sum Tree. A Noir circuit proves the relation R >= L, and a Soroban contract verifies the proof on-chain using the BN254 and Poseidon2 host functions added in Stellar Protocol 25 (X-Ray).

The public sees only the totals and the commitment root; no name or balance is published. Customers can check that their own balance is in the committed tree, and the check also shows unnamed subtotals of nearby entries, including one neighbouring balance. Hiding mode is in development, and selective disclosure through a scoped viewing key for an auditor is coming soon.

Live demo: run the full flow on Stellar Testnet at joinsolva.xyz/demo: read the reserves (from a mock custodian, or from a real Testnet USDC account on Stellar), generate a proof, publish it on-chain, read the record back from the contract, and check a customer's inclusion on the contract with verify_inclusion.

Demo video: watch the walkthrough.

Why both sides of the balance sheet

Proving that you hold assets is not the same as proving those assets cover what you owe. A reserve figure can look healthy while the liabilities owed to customers are larger.

Reserve coverage = Proof of Reserves + Proof of Liabilities

Most published proof of reserves systems only attest the asset side. Solva binds both sides in one proof, so the reserve total cannot hide a larger liability total.

On Stellar, the evidence today is usually a link. SEP-1 gives an issuer one stellar.toml field, attestation_of_reserve, that holds any URL. In November 2026 the Stellar Anchor Directory listed 74 anchors and marked 6 as publishing reserve evidence; 2 of those links opened a reserve report. None of the 30 exchanges it listed was marked, because the field covers the reserves behind a token, not what an exchange owes its customers.

Architecture

Each tier uses the language that fits it. The system holds together through crypto isolation: every hash that must match the circuit lives only in the Rust prover and the Soroban contract, and the Go orchestrator never hashes for the circuit. The Merkle Sum Tree is built once, in the Rust stack that generates the proof, and recomputed on-chain by the native Poseidon2 host function. This rules out a parameter mismatch between languages.

flowchart TB
    subgraph WEB["Web tier · TypeScript"]
        DASH["Dashboard<br/>passkey login"]
        PUB["Public verify<br/>proof lookup"]
        INC["Inclusion checker"]
    end

    ORCH["Orchestrator · Go<br/>scheduler · reserve-source adapters · ECDSA verify<br/>Stellar publisher · audit log · idempotency"]

    PROVER["Prover · Rust<br/>Poseidon2 Merkle Sum Tree<br/>Noir / UltraHonk proof generation"]
    SANDBOX["Sandbox · Go<br/>mock custodian<br/>OAuth + ECDSA signed balances"]
    HORIZON["Horizon · Stellar<br/>account balances"]
    CONTRACT["proof-registry · Soroban<br/>UltraHonk verify · BN254 + Poseidon2<br/>registry + inclusion"]
    ORACLE["Oracle (coming soon) · TypeScript<br/>MCP coverage oracle · anomaly agent"]

    PG[("PostgreSQL<br/>proofs · liabilities · audit log")]
    REDIS[("Redis<br/>latest proof · idempotency lock")]

    DASH -->|REST / SDK| ORCH
    PUB -->|read proof| CONTRACT
    INC -->|verify_inclusion| CONTRACT

    ORCH -->|gRPC witness| PROVER
    ORCH -->|fetch signed balances| SANDBOX
    ORCH -->|read on-chain reserves| HORIZON
    ORCH -->|publish_proof| CONTRACT
    ORCH --- PG
    ORCH --- REDIS
    ORACLE -->|verify coverage| CONTRACT

    classDef rust fill:#1b1b1b,stroke:#b7410e,color:#fff;
    classDef go fill:#00351f,stroke:#00ADD8,color:#fff;
    classDef ts fill:#15243b,stroke:#3178C6,color:#fff;
    classDef chain fill:#000,stroke:#7b61ff,color:#fff;
    classDef data fill:#2a2a2a,stroke:#888,color:#fff;

    class PROVER rust;
    class ORCH,SANDBOX go;
    class DASH,PUB,INC,ORACLE ts;
    class CONTRACT,HORIZON chain;
    class PG,REDIS data;
Loading

Postgres stores proofs, liabilities, and the audit log. Redis caches the latest proof and holds the per-cycle idempotency lock.

Tier Language Role
Circuits Noir Coverage (R >= L), Merkle Sum, and fraud-bound constraints
Prover Rust Poseidon2 tree construction and UltraHonk proof generation
Contract Rust / Soroban On-chain proof verification, registry, inclusion check
Orchestrator Go Scheduling, reserve-source IO, ECDSA verification, publishing, REST
Sandbox Go Mock custodian with OAuth and signed balances
Web and oracle TypeScript Next.js app, docs, MCP oracle, anomaly agent, SDK

How a proof cycle works

  1. The orchestrator reads the reserves from the institution's source: a custodian-signed balance, whose ECDSA signature it verifies, or the balance of Stellar accounts from Horizon.
  2. It loads the customer liabilities for the tenant from Postgres.
  3. It sends reserves, liabilities, and the previous reserve total to the prover over gRPC.
  4. The prover builds the Poseidon2 Merkle Sum Tree, generates the Noir/UltraHonk proof, and returns the proof, the public inputs, and the serialized tree.
  5. The orchestrator publishes the proof to the Soroban proof-registry contract, which verifies it on-chain and stores the root and totals.
  6. The proof and the serialized tree are stored in Postgres, a summary of the cycle is appended to the audit log, and the latest proof is cached in Redis.
  7. The web app reads the latest proof, and customers can check inclusion on-chain with verify_inclusion. The MCP oracle, which answers coverage queries, is coming soon.

What is real, and what is simulated

Solva's cryptographic core is real and runs on Stellar Testnet today: the Noir circuit, the UltraHonk prover, and the Soroban contract that verifies the proof on-chain and stores the result. The /demo page generates real proofs and publishes them to Testnet, and each institution publishes to its own contract.

The demo has three example institutions. Two, Kestrel Anchor and Solstice Exchange, read their reserves from a mock reserve-source sandbox: a Go service that returns ECDSA-signed balances in USD, standing in for a custodian. The signatures are real and verified by the orchestrator; only the balance figures are synthetic. The third, Harbor Wallet, holds real Testnet USDC in a Stellar account, and the orchestrator reads that balance from Horizon on every cycle. For every institution, the demo reads the record back from the contract with get_latest_proof and checks a holder with verify_inclusion on the contract. The customer lists are example data.

The operator console (apps/web) is partly illustrative: the passkey wallet, publishing, and tenant provisioning call the real services, while the main dashboard's cycle history and connect-source views use mock data. Real custodian connections and a production-data console are coming soon.

Passkey smart wallet

Institutions do not hold private keys. An operator signs in with a passkey, and that passkey controls a Soroban smart wallet that owns the institution's proof registry. Publishing is owner-gated through owner.require_auth(), so every proof is authorized by the wallet. For continuous publishing, the operator grants the orchestrator's Ed25519 key as a signer scoped to that one registry, so the service can publish and nothing else. The passkey stays the root of authority and can revoke that signer at any time. The full model is in the passkey smart wallet page.

Repository layout

apps/        web (operator console), website (joinsolva.xyz), docs (Fumadocs)
services/    orchestrator (Go), prover (Rust), sandbox (Go), oracle (TypeScript)
circuits/    Noir circuits: solvency, merkle, shared lib
contracts/   Soroban proof-registry
crates/      UltraHonk verifier for Soroban
packages/    sdk-ts, contract-bindings, shared-types, ui, brand, config
proto/       gRPC definitions for orchestrator and prover
infra/       docker-compose for Postgres and Redis, hosting guides
deploy/      demo backend on one VM with Docker Compose

This is a polyglot monorepo. The proof schema, contract bindings, and shared types must stay in lockstep across the verifier, prover, orchestrator, SDK, and web app. Keeping them in one repository makes any change to them a single atomic commit.

Running it locally

Everything runs against Stellar Testnet on your machine.

Prerequisites

Install the tools below. Two of them are version-sensitive: bb and nargo must be the exact pinned versions, or proofs will not verify. Rust (1.91.1 + wasm32v1-none) and pnpm are pinned in the repo (rust-toolchain.toml, packageManager), so those resolve automatically once the tool is installed.

On Windows, use WSL2 (Ubuntu) and follow the Linux steps.

Installation guides

Docker: runs Postgres and Redis.

  • macOS: Docker Desktop, or brew install --cask docker
  • Linux: curl -fsSL https://get.docker.com | sh (docs)

Node 22 + pnpm: web tier and SDK.

# Node 22 via nvm (https://github.com/nvm-sh/nvm)
nvm install 22 && nvm use 22
# pnpm via corepack (picks up the version pinned in package.json)
corepack enable

Go 1.25: orchestrator and sandbox.

Rust 1.91.1: prover and Soroban contract. rust-toolchain.toml pins the version and the wasm32v1-none target, so rustup installs them for you.

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

Stellar CLI: deploys and reads contracts.

brew install stellar-cli        # macOS / Linux with Homebrew
# or: cargo install --locked stellar-cli

Install docs

Noir nargo 1.0.0-beta.9: the circuit toolchain.

curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash
noirup --version 1.0.0-beta.9

Barretenberg bb 0.87.0: the proving backend. Pick the build for your OS and CPU from the v0.87.0 release:

mkdir -p ~/.bb
# Apple Silicon (arm64 Mac):
curl -L https://github.com/AztecProtocol/aztec-packages/releases/download/v0.87.0/barretenberg-arm64-darwin.tar.gz | tar -xzC ~/.bb
# Intel Mac:  barretenberg-amd64-darwin.tar.gz
# Linux:      barretenberg-amd64-linux.tar.gz
echo 'export PATH="$HOME/.bb:$PATH"' >> ~/.zshrc   # or ~/.bashrc
source ~/.zshrc

Just: the command runner.

brew install just               # macOS / Linux with Homebrew
# or: cargo install just

Verify everything is installed and on PATH:

docker --version && node --version && pnpm --version && go version \
  && rustc --version && stellar --version && nargo --version \
  && bb --version && just --version

bb --version must print 0.87.0 and nargo --version 1.0.0-beta.9.

Then install dependencies and create a funded Testnet identity that signs and publishes proofs:

pnpm install
stellar keys generate tester --network testnet --fund

Docker must be running for both options below: the sandbox, prover, and orchestrator run locally, and Postgres and Redis start in containers.

Option A: the full pipeline in one command

Runs attestation through on-chain verification across all three scenarios:

export E2E_STELLAR_SIGNER_SECRET=$(stellar keys show tester)
just e2e

It deploys the registry, starts the sandbox, prover, and orchestrator, fetches signed reserves, generates the Noir/UltraHonk proof, publishes it to the Soroban contract, and verifies it on-chain. It ends with E2E PASSED: the solvent and near-breach proofs are published and verified, and the insolvent case is refused, because the system will not prove a false statement.

Option B: the interactive playground

Bring up the stack with two seeded institutions, Kestrel Anchor and Solstice Exchange, each with its own on-chain registry:

export ORCH_STELLAR_SIGNER_SECRET=$(stellar keys show tester)
just demo

Then, in a second terminal, start the website against it:

ORCHESTRATOR_URL=http://localhost:8080 SANDBOX_URL=http://localhost:8090 \
  pnpm --filter @solva/website dev

Open http://localhost:3000/demo, pick Kestrel or Solstice, and run the steps. Try the three scenarios: solvent and near-breach publish a proof, insolvent is refused on-chain. The record step reads the proof back from the contract, the holder step checks inclusion on the contract, and you can open the transaction on the public explorer. Harbor Wallet, the third institution, lives on the hosted backend only: its reserves are a funded Testnet USDC account, set up with the runbook in deploy/vm/README.md.

Other commands

  • just --list shows every target.
  • just test runs the test suites.
  • just parity-check confirms the circuit, prover, and contract compute the same Poseidon2 commitment.
  • just e2e-multitenant proves two institutions stay isolated on-chain (export E2E_STELLAR_SIGNER_SECRET first, as in Option A).

The contributor workflow is in CONTRIBUTING.md, the engineering standards are in docs/engineering-standards.md, and the rules for AI coding agents are in AGENTS.md.

Documentation

Protocol overview, quickstart, SDK reference, and the sandbox guide live in Docs. The cryptographic construction and the formal algorithm are in the whitepaper.

Project status

Built for the Stellar Real-World ZK hackathon. Running on Stellar Testnet today: the Noir/UltraHonk proving pipeline, the Soroban on-chain verifier, per-institution multi-tenancy, reserve sources per institution (custodian-signed or read on chain), and passkey smart-wallet publishing. Try it at joinsolva.xyz/demo. The demo's reserves come from a mock custodian for two institutions and from a real Testnet USDC account for the third, described above in "What is real, and what is simulated". Coming soon: real custodian connections, is_covered for contracts, public record pages, an npm package, proofs beyond 8 balances and 4 reserve figures, an independent security audit, and Mainnet. In development: hiding proofs. Open work is tracked in the issues.

Contact

Questions, partnerships, or press: hello@joinsolva.xyz.

License

Apache-2.0. See LICENSE.

About

Open reserve-coverage registry on Stellar for anchors and custodial apps. A zero-knowledge proof that reserves cover what customers are owed, published on chain, without revealing any balance.

Topics

Resources

Contributing

Stars

3 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages