← All posts
2026-05-0710 min

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

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:

  1. A trusted authority (the Issuer) mints a verifiable credential on-chain
  2. The user (the Holder) generates a cryptographic proof of eligibility without revealing sensitive data
  3. 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:

ComponentPurposeExample
CredentialsTop-level identity of the issuing organizationMinistry of Justice
SchemasStructural template defining data fieldsjurisdiction, eligibility_tier, expiry_date
AttestationsIndividual claims issued to specific citizensCitizen 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.

7,883
Circuit constraints
256
Bytes per proof
7
Public inputs
~660ms
Proof generation

The 7 public inputs verified on-chain:

#Public InputWhat It Proves
0commitmentRootAll credential fields hash to the on-chain root
1disclosedValueThe selectively revealed field value
2disclosureIndexWhich field is being disclosed
3predicateValueThreshold for range check (e.g., current timestamp)
4predicateIndexWhich field the predicate applies to
5predicateSatisfied1 if the predicate holds (enforced on-chain)
6issuerPubkeyHashBinding 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.

Mathematical privacy, not access-control privacy. Enterprise systems like Hyperledger Fabric use Private Data Collections (the admin promises not to share data). Adduce uses Groth16 proofs (the verifier mathematically cannot extract hidden fields, even with unlimited compute).

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

InstructionRoleWhat It Does
initializeAuthorityCreate jurisdiction config (293 bytes)
open_caseAuthorityCreate case with lawyer commitment + authorized amount
open_case_custodialAuthorityCase for citizen without wallet
link_credentialReviewerBind SAS credential (5-point validation)
reassign_lawyerAuthoritySwitch lawyer with audit trail
anchor_documentLawyerAnchor document hash (proves identity via salt)
close_caseReviewerClose with credential liveness re-check
update_case_statusReviewerStayed, Appealed, Withdrawn, Remanded
reopen_caseAuthorityClosed back to InProgress with reason
mark_paidPayerAmount validated, payment reference stored
verify_zk_disclosureAnyoneGroth16 proof verification (alt_bn128)
add_delegateAuthorityAdd delegate reviewer (max 3)
remove_delegateAuthorityRemove delegate
fund_operationsAuthorityTop up operations wallet

Program deployed at 3f1yBTY6xb6ESdzzb9LxAozv7uVsj9Y9AMEpnAwKJRNV on Solana Devnet.

Comparison

MetricLegacy Paper CertificatesAdduce on Solana
IssuancePhysical printing, postal mailingSAS Attestation PDA ($0.004)
Verification6-12 months (manual processing)400ms (on-chain settlement)
PrivacyPlain-text documents256-byte ZK proof (Groth16)
Commitment schemeNonePoseidon hash (7,883 constraints)
Cost per settlementHigh administrative overhead$0.0001 (ZK verification)
RevocationWeeks (manual notification)Instant (account deletion)
Cross-borderBilateral agreements requiredAny Solana node, any jurisdiction
Document storageFiling cabinetsArweave (encrypted, permanent, $0.004)

Start Building

npm install @adduce/sdk

Connect to the deployed program. Call functions. No cloning, no deploying, no consortium.

View on npm | Documentation | GitHub

← Back to all posts