How Adduce Issues Verifiable Credentials on Solana: A Technical Deep Dive

For technical evaluators, lead architects, and CTOs transitioning government and enterprise workflows to on-chain infrastructure.
The legacy administration of public sector services is plagued by a fundamental structural bottleneck: the asymmetry between digital data storage and physical document issuance. We see this across certificate-based legal aid systems globally: the Toevoeging in the Netherlands, the Aide Juridictionnelle in France, the Patrocinio a spese dello Stato in Italy, the Asistencia Juridica Gratuita in Spain, the Verfahrenshilfe in Austria, the Apoio Judiciario in Portugal, and Legal Aid Certificates in Germany, Canada, and Ireland. The workflow remains tragically analog: sensitive eligibility data is stored securely in government databases, but the authorization voucher handed to the citizen and the lawyer is a physical piece of paper.
This disconnect creates immense verification friction. When a lawyer submits a paper voucher for reimbursement, administrators must manually verify its authenticity against siloed databases. The result? 6-12 month payment delays that drain liquidity from legal aid providers and restrict access to justice for 500M+ citizens across 9 countries.
Adduce resolves this asymmetry by digitizing the exact voucher-style workflow into a high-speed, cryptographically secure protocol on Solana. The framework: Issue. Prove. Settle.
The Architecture: Issue. Prove. Settle.
To replace sovereign legal documents, the architecture must balance absolute transparency in verification with absolute privacy for the credential holder. Adduce achieves this through a three-phase cryptographic lifecycle:
- A trusted authority (the Issuer) mints a verifiable credential on-chain
- The user (the Holder) generates a cryptographic proof of eligibility without revealing sensitive data
- The service provider (the Verifier) submits this proof to the blockchain for settlement
The data already exists digitally in government systems. The output is still paper. That asymmetry is the entire bottleneck. Adduce doesn't digitize the database. It digitizes the proof.
Phase 1: Issue
SAS Attestations and Program Derived Addresses
Adduce utilizes the Solana Attestation Service (SAS), which provides a standardized framework to create verifiable claims directly on the Solana network.
The SAS architecture relies on Solana's Program Derived Addresses (PDAs): deterministic accounts controlled by a specific program. Because PDAs have no private keys, their state can only be mutated by the owning program, ensuring immutability of the credential's metadata.
The issuance ecosystem consists of three PDA components:
| Component | Purpose | Example |
|---|---|---|
| Credentials | Top-level identity of the issuing organization | Ministry of Justice |
| Schemas | Structural template defining data fields | jurisdiction, eligibility_tier, expiry_date |
| Attestations | Individual claims issued to specific citizens | Citizen X is eligible in jurisdiction DE until 2027 |
When a court issues a legal aid certificate, the Adduce SDK creates an Attestation PDA, computes a Poseidon commitment root of all credential fields, and opens a case bound to the credential:
import { AdduceClient, createLawyerCommitment } from "@adduce/sdk";
const adduce = new AdduceClient({ cluster: "devnet", wallet: courtWallet });
// Generate lawyer commitment (SHA-256 hash, pubkey never on-chain)
const lc = createLawyerCommitment(lawyerWallet.publicKey);
// Open case with credential binding + authorized payment amount
const { casePda, lawyerCommitment } = await adduce.openCase({
caseId: "CASE-DE-2025-001",
lawyerPubkey: lawyerWallet.publicKey,
applicant: citizenWallet.publicKey,
authorizedAmount: 8500, // fee schedule amount
}, "DE");
// Link SAS credential to the case (5-point validation)
await adduce.linkCredential({
caseId: "CASE-DE-2025-001",
credentialAddress: sasPda,
commitmentRoot: poseidonRoot, // Poseidon hash of all 6 fields + salts
}, "DE");
The SAS Program is deployed at 22zoJMtdu4tQc2PzL74ZUT7FrwgB1Udec8DdW4yw4BdG. The Adduce program performs 5-point validation during linkCredential: owner check, schema match, expiry check, citizen binding, and commitment root verification.
Custodial Model
Not all citizens have Solana wallets. For elderly applicants, refugees, or those with limited technical access, Adduce supports a custodial model: the court opens the case using a hashed national ID instead of a wallet public key. The citizen never touches the blockchain.
import { hashCitizenId } from "@adduce/sdk";
const citizenIdHash = hashCitizenId("DE-PERSONALAUSWEIS-L01234567");
await adduce.openCaseCustodial({
caseId: "CASE-DE-2025-002",
lawyerPubkey: lawyerWallet.publicKey,
citizenNationalId: "DE-PERSONALAUSWEIS-L01234567",
custodian: courtWallet.publicKey,
authorizedAmount: 8500,
}, "DE");
Phase 2: Prove
Poseidon Commitments and Groth16 ZK Proofs
Once the credential is issued, the holder must prove eligibility to a lawyer. But data privacy regulations prohibit broadcasting plain-text demographic or financial data to a public ledger.
Adduce implements zero-knowledge selective disclosure using a custom Circom circuit with Groth16 proofs over the BN254 curve. The Poseidon hash function is used instead of SHA-256 because it minimizes constraints within ZK circuits, making proof generation fast enough to run on a standard device.
The 7 public inputs verified on-chain:
| # | Public Input | What It Proves |
|---|---|---|
| 0 | commitmentRoot | All credential fields hash to the on-chain root |
| 1 | disclosedValue | The selectively revealed field value |
| 2 | disclosureIndex | Which field is being disclosed |
| 3 | predicateValue | Threshold for range check (e.g., current timestamp) |
| 4 | predicateIndex | Which field the predicate applies to |
| 5 | predicateSatisfied | 1 if the predicate holds (enforced on-chain) |
| 6 | issuerPubkeyHash | Binding to the credential issuer |
The proof tells the verifier: "jurisdiction is DE and credential is not expired" without revealing eligibility tier, exact expiry date, applicant identity, case type, or any salts.
Phase 3: Settle
On-Chain Verification and Payment
The lawyer submits the 256-byte proof to the Solana blockchain for verification and settlement. In legacy systems, this reconciliation step causes 6-12 month delays. With Adduce, it's a single transaction.
The verify_zk_disclosure instruction checks the proof using Solana's native alt_bn128 syscall (the same BN254 curve used by Light Protocol and Ethereum ZK rollups). Verification requires approximately 200,000 compute units, costing around $0.0001.
// Verify ZK proof on-chain (anyone can call this)
await adduce.verifyZkDisclosure(
"CASE-DE-2025-001",
proofBytes, // 256 bytes
publicInputs // 7 x 32 bytes
);
// Close case (reviewer or delegate)
await adduce.closeCase("CASE-DE-2025-001", credentialAddress, "DE");
// Record payment with amount validation
await adduce.markPaid({
caseId: "CASE-DE-2025-001",
disbursedAmount: 8500,
paymentReference: "INV-2025-04-001", // SAP/ERP reference for audit
}, "DE");
markPaid enforces that disbursedAmount <= authorizedAmount (set during case opening). The payment reference is stored on-chain for government audit trails. Settlement supports both on-chain USDC (400ms) and off-chain bank transfer with the on-chain reference for reconciliation.
Revocation
If a citizen's eligibility changes, the issuing authority deletes the SAS attestation account. One transaction. Instant. Global. Every downstream instruction (anchor_document, close_case) re-checks credential liveness at the protocol level. There is no stale window: the moment the account is deleted, all verification fails.
The Adduce Program: 14 Instructions
| Instruction | Role | What It Does |
|---|---|---|
initialize | Authority | Create jurisdiction config (293 bytes) |
open_case | Authority | Create case with lawyer commitment + authorized amount |
open_case_custodial | Authority | Case for citizen without wallet |
link_credential | Reviewer | Bind SAS credential (5-point validation) |
reassign_lawyer | Authority | Switch lawyer with audit trail |
anchor_document | Lawyer | Anchor document hash (proves identity via salt) |
close_case | Reviewer | Close with credential liveness re-check |
update_case_status | Reviewer | Stayed, Appealed, Withdrawn, Remanded |
reopen_case | Authority | Closed back to InProgress with reason |
mark_paid | Payer | Amount validated, payment reference stored |
verify_zk_disclosure | Anyone | Groth16 proof verification (alt_bn128) |
add_delegate | Authority | Add delegate reviewer (max 3) |
remove_delegate | Authority | Remove delegate |
fund_operations | Authority | Top up operations wallet |
Program deployed at 3f1yBTY6xb6ESdzzb9LxAozv7uVsj9Y9AMEpnAwKJRNV on Solana Devnet.
Comparison
| Metric | Legacy Paper Certificates | Adduce on Solana |
|---|---|---|
| Issuance | Physical printing, postal mailing | SAS Attestation PDA ($0.004) |
| Verification | 6-12 months (manual processing) | 400ms (on-chain settlement) |
| Privacy | Plain-text documents | 256-byte ZK proof (Groth16) |
| Commitment scheme | None | Poseidon hash (7,883 constraints) |
| Cost per settlement | High administrative overhead | $0.0001 (ZK verification) |
| Revocation | Weeks (manual notification) | Instant (account deletion) |
| Cross-border | Bilateral agreements required | Any Solana node, any jurisdiction |
| Document storage | Filing cabinets | Arweave (encrypted, permanent, $0.004) |
Start Building
npm install @adduce/sdk
Connect to the deployed program. Call functions. No cloning, no deploying, no consortium.