Architecture
Architecture
A Soroban contract on testnet, a snarkjs circuit, three Node services, and this Next.js frontend. The private path runs entirely in the browser; the services hold only issuer-side state.
Contracts
| Role | Contract ID |
|---|---|
| PoolPass | CBVARDGO…M7K2FV |
| Testnet USDC (SAC) | CAX5YVXG…I4KP77 |
| Pool token | CBUXSNRS…VY2KWC |
| Poseidon parity gate | CDBKFLR5…VIFUWD |
| Groth16 parity gate | CALDSMCD…EI6UFX |
PoolPass methods
contract interfaceinitialize(issuer, usdc_sac, pool_token, groth16_vk: Bytes, merkle_depth: u32, pool_name: String) -> Result<(), Error>
update_accredited_set(issuer, leaf_hashes: Vec<BytesN<32>>) -> Result<BytesN<32>, Error>
subscribe(investor, amount: i128, proof: Bytes, public_inputs: Vec<BytesN<32>>) -> Result<BytesN<32>, Error>
get_pool_info() -> Result<PoolInfo, Error>
advance_epoch(issuer) -> Result<u32, Error>The issuer must authorize set updates and epoch advances. The investor must authorize subscribe and its nested Testnet USDC transfer.
Events
PoolPass uses the SDK 26 #[contractevent] API. The generated publish prepends the snake-case event-name symbol to the topic vector; non-topic fields are a canonical ScMap, named rather than positional. Reproduced verbatim from the integration contract:
event shapesRootUpdated { root, leaf_count, epoch(topic), timestamp }
topics: Symbol("root_updated"), epoch: u32
data: { leaf_count: u32, root: BytesN<32>, timestamp: u64 } // key order: leaf_count, root, timestamp
Subscribed { investor(topic), amount, nullifier, commitment, epoch(topic), timestamp }
topics: Symbol("subscribed"), investor: Address, epoch: u32
data: { amount: i128, commitment: BytesN<32>, nullifier: BytesN<32>, timestamp: u64 } // key order: amount, commitment, nullifier, timestamp
EpochAdvanced { epoch(topic), timestamp }
topics: Symbol("epoch_advanced"), epoch: u32
data: { timestamp: u64 }For RPC getEvents, filter by type: "contract", the PoolPass contract ID, and the encoded first topic via nativeToScVal(name, { type: "symbol" }).toXDR("base64").
Services
| Service | Role |
|---|---|
| api | Fastify HTTP: /accredit, /faucet, /verify, /prove (fallback), /pool/demo. |
| indexer | Reads RPC getEvents, dedupes by event ID, persists to services/data/events.json. |
| demo-issuer | Holds the issuer key; commits leaves and mints Testnet USDC. |
Repository
layoutcontracts/ Soroban contracts (poolpass, mock token, gates)
circuits/ Circom circuit, artifacts, fixtures, VK
src/ shared crypto: poseidon, field, merkle, groth16 serializer
services/ api, indexer, demo-issuer
merkle-tools/ standalone tree + proof helpers
web/ this Next.js frontend
deployments.json machine-readable source of truth