For judges

How to verify our claims

Sixty seconds, no trust required. Run three commands, visit three URLs, check one hex string, and follow the explorer links to the verified on-chain runs.

Three commands

terminal# 1 · prove + verify a proof against the committed key, off any browser
pnpm api            # start services on http://127.0.0.1:3000
pnpm api:smoke      # exercises /accredit, /prove, /verify end to end

# 2 · confirm Poseidon parity (off-chain == circuit == on-chain host)
pnpm gate:a

# 3 · run the full self-serve loop on testnet
pnpm selfserve:smoke

Three URLs

No wallet required for any of these:

/prove                  run a real Groth16 proof in your browser (~1 s)
/verify/<tx-or-commitment>   independently verify any subscription
/docs/host-functions    the Gate A parity witness, verbatim

One hex string to check

Poseidon parity, three identical outputs

For the input pair "1", "2", the off-chain library, the Circom witness, and the Soroban host all return:

115cc0f5e7d690413df64c6b9662e9cf2a3617f2743245519e19607a4417189a

Byte-identical across all three. No contract contains userland Poseidon or BN254 arithmetic.

Explorer links, verified on-chain runs

Why PoolPass beats simpler alternatives

Not just set membership

PoolPass composes set membership (Merkle inclusion), range compliance (amount within cap), a privacy-preserving nullifier (one subscription per epoch per secret), and real settlement (Testnet USDC transfer plus pool-token mint) inside one atomic Soroban invocation. Simpler ZK demos verify a proof and stop. PoolPass verifies and settles: the contract transfers tokens, mints a pool receipt, and records the commitment in a single transaction.

Gate A parity work matters

Most submissions skip cryptographic parity testing. A userland-Poseidon submission could ship a subtle parameter mismatch between the off-chain library and the on-chain implementation and never know until a proof silently fails. PoolPass forced the native host invocation and proved byte-identical output across three independent implementations: the circomlib JS library, the Circom circuit witness, and the Soroban Poseidon host function. That is the engineering rigor a production system needs, and it is verifiable on-chain.

Native host functions, not WASM fallback

The Groth16 verification runs through Stellar Protocol 25/26 bn254_pairing_check, not a WASM contract reimplementation of BN254 arithmetic. This is cheaper, faster, and exactly the use case the protocol upgrades were designed for. The Poseidon hashing likewise uses the native poseidon_hash host function introduced in Protocol 25. No contract in the system contains userland elliptic-curve or hash arithmetic.

Threat model and honest limits

  • Self-serve issuer. The accreditation gate is simulated for the demo. A real deployment requires the issuer to perform KYC/AML before committing a leaf. The ZK proof verifies membership in whatever set the issuer commits, but the integrity of that set depends on the issuer’s off-chain process.
  • Trusted setup. The Groth16 parameters are a test fixture generated for this demo. A mainnet deployment requires a Phase-2 ceremony (Powers of Tau) to eliminate the trapdoor risk.
  • Public inputs. The subscription amount is a public input and is visible on-chain. The commitment does not hide the amount.
  • Timing correlation. The on-chain wallet address and the timing of subscriptions are observable. Cross-epoch linkability is mitigated by the investor_secret in the nullifier, but intra-epoch timing is a leak.
  • Issuer centralization. The issuer service is a single point of trust for tree commitments. A production system would use a multisig or an on-chain governance mechanism.
  • Merkle depth. Depth-3 (8 leaves) is demo scale. Production requires depth 16 to 20 (65K to 1M leaves). The circuit is parameterized for depth but compiled for 3; scaling requires recompilation and a new trusted setup.