Portable cross-merchant customer reputation on Solana.
Pay any Solana merchant in USDC. Earn a portable on-chain attestation. Get an auto-applied discount at the next merchant that trusts your history. No per-brand loyalty accounts.
- Status: devnet MVP. Mainnet ships after audit.
- Devnet program:
ABSmAN3fhCdnEnAdRiKUWjpBwrZb2FZ41EYD3hnFN5xT - IDL spec: Anchor 0.30.1 (committed at
proof-pay-app/src/lib/proof_pay.json)
"Enforce the relationship in code, not in a loyalty database."
Every Solana commerce stack today gives each merchant its own siloed loyalty token. Your purchase history at one merchant can't help you at the next one, even when both run on the same chain. Customers re-prove themselves at every storefront; merchants re-acquire customers from scratch.
ProofPay flips it: your purchase history is yours, living on-chain as a portable attestation the customer owns. Any future merchant can read it, apply their own discount policy, and price accordingly — with zero integration between merchants.
flowchart LR
Customer[Customer wallet] -->|pays USDC| Pay[Solana Pay URL]
Pay -->|pay_and_attest ix| Program[ProofPay Anchor Program]
Program -->|SPL CPI| Treasury[(Merchant USDC ATA)]
Program -->|SAS CPI or fallback PDA| Attestation[(Attestation account)]
Merchant[Merchant dashboard] -->|register_merchant, set_policy| Program
Merchant -->|getProgramAccounts| Ledger[(On-chain state)]
Program -->|reads customer attestation count + merchant policy| Program
Program -->|discounted amount| Treasury
One Anchor program + one Next.js app + Solana Attestation Service (with internal PDA fallback). Devnet-first; mainnet next.
This section is intentionally about intent and roadmap, not shipped features. Nothing below is wired into the deployed devnet binary today — these are the answers to the obvious questions about how ProofPay would operate as a business.
Revenue (roadmapped, not implemented). A 10 bps protocol fee on pay_and_attest, skimmed atomically in the same CPI as the merchant transfer. Merchants would pay nothing to install; ProofPay earns on volume. Linear, fully on-chain auditable, and an order of magnitude cheaper than card-network take rates. Targeted for the mainnet deploy alongside the SAS CPI path.
Sybil model. Attestations can only be minted by real USDC payments, so farming N wallets costs N × purchase_minimum in actual spend and splits reputation across N accounts — economically self-limiting for small discounts. For merchants who want a hard human-gate, the policy engine is designed to extend with an optional required_issuer field; only attestations co-signed by a trusted credential issuer (proof-of-human, KYC, or any SAS-issued schema) would count toward the discount threshold. The field is on the roadmap, not in the deployed binary.
Distribution. ProofPay is designed to ship through existing Solana Pay processors and stablecoin-native wallet networks rather than direct-to-merchant — each processor partnership would unlock an entire merchant book with a single one-instruction integration. Zero processors are signed today; this is post-hackathon GTM.
proof-pay/ # Anchor workspace (Rust program + LiteSVM tests)
programs/proof_pay/src/
lib.rs
state.rs # MerchantRegistry, ProofPayAttestation, CustomerCounter, PolicyRule
error.rs
attestation.rs # internal-PDA writer + USE_SAS_CPI feature flag
instructions/
register_merchant.rs
set_policy.rs
pay_and_attest.rs
close_merchant.rs
tests/ # LiteSVM harness — happy path, discount, portability
Anchor.toml, Cargo.toml, rust-toolchain.toml
proof-pay-app/ # Next.js 14 App Router frontend
src/
app/
page.tsx # landing
merchant/page.tsx # merchant dashboard (onboard, policy, tx list)
checkout/page.tsx # customer checkout (pay, trust score, receipt)
api/solana-pay/ # Solana Pay transaction-request endpoint
components/proofpay/ # nav, onboard, policy editor, tx list, trust score, receipt
components/ui/ # Button, Input (shadcn-style)
lib/
anchor.ts # useProofPayProgram() hook
proof_pay.json # committed IDL (Anchor 0.30 spec)
pda.ts # registry / counter / attestation PDA derivation
config.ts # PROGRAM_ID, USDC_MINT, RPC_URL (env-overridable)
format.ts # USDC + bps formatters
.github/workflows/ci.yml # anchor build + litesvm + next lint
Prereqs
- Rust 1.86+ (the codebase uses crates that require
edition2024— older toolchains won't build) - Solana CLI 1.18+ with a funded devnet keypair at
~/.config/solana/id.json - Anchor 0.30.1 (
avm install 0.30.1 && avm use 0.30.1) - Node 20+ and npm
# 1. (Optional) build + test the Anchor program. The committed IDL already matches
# the deployed binary, so you can skip straight to step 3 if you only want to
# run the dapp against the live devnet program.
cd proof-pay
anchor build
cargo test --manifest-path tests/Cargo.toml
# 2. (Optional) deploy your own copy. If you do, run `anchor keys sync`, copy the
# new program ID into proof-pay-app/.env.local, and re-publish the IDL on-chain.
solana airdrop 2
anchor deploy --provider.cluster devnet
anchor idl init --filepath ../proof-pay-app/src/lib/proof_pay.json \
--provider.cluster devnet $(solana address -k target/deploy/proof_pay-keypair.json)
# 3. Run the frontend against the existing devnet deployment.
cd ../proof-pay-app
cp .env.example .env.local # only edit if you re-deployed in step 2
npm install
npm run dev
# 4. Open http://localhost:3000
# a) /merchant — connect a fresh Phantom wallet, register, set a policy
# b) /checkout?merchant=<merchantAuthorityPubkey>&amount=10proof-pay-app/.env.local:
NEXT_PUBLIC_PROOFPAY_PROGRAM_ID=ABSmAN3fhCdnEnAdRiKUWjpBwrZb2FZ41EYD3hnFN5xT
NEXT_PUBLIC_USDC_MINT=Gh9ZwEmdLJ8DscKNTkTqPbNwLNNBjuSzaG9Vp2KGtKJr
NEXT_PUBLIC_SOLANA_RPC_URL=https://api.devnet.solana.com
All three values are public (NEXT_PUBLIC_* is bundled into the browser; the defaults in src/lib/config.ts already point at the live devnet program). No secrets are required to run the dapp.
One instruction per user-visible action:
register_merchant(name)— creates aMerchantRegistryPDA seeded by the merchant's wallet. Pins the USDC mint and treasury ATA at registration time sopay_and_attestcan't be tricked into routing a different token.set_policy(rules)— merchant-only (has_one = authority). Stores up to threePolicyRuleentries:{min_attestations, discount_bps, valid_until}.pay_and_attest(amount_usdc)— atomic:- Reads the customer's running attestation count from a
CustomerCounterPDA (init-if-needed so first-time customers don't need a separate opt-in). - Evaluates the merchant's policy → best-matching
discount_bps(caps at 9000 bps = 90% off). - SPL CPI transfers
amount_usdc - discountfrom customer ATA → merchant treasury. - Writes a
ProofPayAttestationPDA seeded by(customer, registry, counter)and bumps the counter.
- Reads the customer's running attestation count from a
close_merchant()— optional cleanup; refunds rent.
The attestation::USE_SAS_CPI flag switches the attestation path from the internal PDA to a CPI into the Solana Attestation Service (sas-lib). Internal PDA is the MVP default.
| UI surface | Program method | Source |
|---|---|---|
/merchant → "Register merchant" |
register_merchant(name) |
onboard-panel.tsx |
/merchant → "Save policy" |
set_policy(rules) |
policy-editor.tsx |
/checkout → "Pay & earn attestation" |
pay_and_attest(amount_usdc) |
checkout/page.tsx |
| Trust score & dynamic discount preview | program.account.customerCounter.fetch + program.account.merchantRegistry.fetch |
same files |
The Anchor client is created lazily once a wallet is connected — see useProofPayProgram().
Three LiteSVM tests cover the full flow in-process (no test validator needed):
happy_path_register_and_single_payment— registration + one purchase.discount_applies_after_threshold— policy withmin_attestations = 2kicks in on purchase #3.portable_reputation_across_two_merchants— attestations from merchant A discount the first-ever purchase at merchant B. This is the hero test; it's the actual product promise in a unit test.
Run them with cargo test --manifest-path tests/Cargo.toml.
- Devnet only. Mainnet deploy + audit is next.
- USDC-only. Multi-token is on the roadmap.
- No sybil resistance — one wallet = one customer. A pluggable identity provider (e.g. Humanship ID) is the next integration.
- Internal Attestation PDA path is default; SAS CPI path is wired but gated behind a
USE_SAS_CPIfeature flag untilsas-libversion is pinned. - The committed IDL was hand-derived from the program source (Anchor 0.30 spec, real discriminators) because the host toolchain currently can't run
anchor idl buildagainstark-bn254. The on-chain account decoders and instruction encoders all match the deployed binary; if you re-deploy with a code change that touches account layout, regenerateproof-pay-app/src/lib/proof_pay.jsonaccordingly.
- Mainnet deploy + SAS CPI path enabled.
- Sybil-resistant attestations via a pluggable identity provider.
- Merchant SDK (
@proof-pay/sdk) for third-party checkout embedding. - Compressed attestations via SAS + Light Protocol once compression lands.
- Policy DSL — move from struct rules to a small expression language.
- Multi-token support beyond USDC.
- Solana Attestation Service — the attestation primitive.
- Solana Pay — the payment rail.
- Anchor, LiteSVM — program dev + test.
MIT.
