Confidential Qualified Yield Market built with Zama.
Privyields is a hackathon dApp for private allocation into yield products. A user proves they are qualified without revealing their full asset amount, chooses or issues a public yield product, wraps USDC into confidential cUSDC, allocates encrypted capital, receives encrypted reward accounting, claims encrypted cUSDC, and decrypts their own final balance.
- User connects a Sepolia wallet.
- User generates and submits an Arkworks Groth16 asset-threshold proof: assets >= 1,000,000 USDC.
- After qualification, the user selects or issues a public yield product. This gate can later map different asset tiers to different visible products.
- User mints demo MockUSDC or reuses an existing balance, approves the wrapper, and calls
ConfidentialUSDC.wrap. - User encrypts the deposit amount with Zama Relayer SDK.
- User calls
confidentialTransferAndCallon cUSDC with the selected product id. ConfidentialYieldVaultreceives the encrypted amount in its callback and records encrypted principal.- Product publisher publishes APR when needed, then the vault settles encrypted rewards using FHE arithmetic.
- User decrypts their own reward through the Zama SDK, claims encrypted cUSDC, and can decrypt the final cUSDC balance plus local balance history.
MockUSDC: 6-decimal ERC-20 for demo minting.ConfidentialUSDC: OpenZeppelin ERC-7984 confidential token wrapper over mock USDC.QualifiedInvestorGroth16Verifier: generated BN254 Groth16 verifier exported from the Arkworks circuit.Groth16QualifiedInvestorRegistry: verifies asset-threshold proofs and marks qualified wallets.MockQualifiedInvestorRegistry: fallback demo registry used by legacy tests and mock-only flows.YieldProductMarket: 10-product seed marketplace, permissionless product listing, and public demo APR publisher.ConfidentialYieldVault: confidential deposit receiver, encrypted principal/reward ledger, and encrypted cUSDC reward claims.
The confidential asset rail uses encrypted balances, encrypted transfers, and transfer-and-call callback deposits. The current implementation is configured for Zama Sepolia through ZamaEthereumConfig.
The ZK component is intentionally limited to qualification:
private input: assetAmount, salt
public input: commitment, threshold, user, nullifierHash
statement: assetAmount >= threshold and commitment binds assetAmount to the user
For the live hackathon demo, the frontend calls a Next API route that runs the Rust Arkworks prover, receives a Groth16 proof JSON, self-registers the demo commitment, and then submits the proof to Groth16QualifiedInvestorRegistry. The exact asset amount remains off-chain. This is a self-serve hackathon flow so fresh judge wallets can complete the demo without a separate issuer service. In production, commitment registration should come from a real bank, custodian, or compliance issuer instead of being publicly callable.
The Arkworks setup is generated by a deterministic demo script for repeatable judging and testing. It is not a production trusted setup ceremony. The legacy circuits/ directory contains the earlier Circom design sketch; the connected verifier path for this demo is the Rust Arkworks implementation under zk/qualified-investor.
The Next app is a guided production demo:
- EN/ZH copy, light/dark theme, slides, docs, and config pages
- wallet connection and Sepolia deployment state
- 10-product yield market plus permissionless product issuance
- mint/approve/wrap cUSDC controls, with an option to skip minting when the wallet already has MockUSDC
- encrypted deposit via Zama Relayer SDK with visible debug events
- public demo APR update and user-triggered encrypted reward settlement
- reward decrypt, encrypted claim, final cUSDC balance decrypt, local balance history, and Etherscan transaction links
Set these environment variables after deployment:
NEXT_PUBLIC_MOCK_USDC=0x...
NEXT_PUBLIC_CUSDC=0x...
NEXT_PUBLIC_MARKET=0x...
NEXT_PUBLIC_VAULT=0x...
NEXT_PUBLIC_QUALIFICATION_REGISTRY=0x...
NEXT_PUBLIC_GROTH16_VERIFIER=0x...
NEXT_PUBLIC_REGISTRY_MODE=groth16
NEXT_PUBLIC_ZK_ASSET_THRESHOLD=1000000000000
NEXT_PUBLIC_CHAIN_NAME=sepolianpm install
npm run compile
npm test
npm run typecheck
npm run build
npm run deploy:sepolia
npm run deploy:sepolia:production
npm run smoke:localhost
npm run server:local
./deploy-server.sh
DEPLOY_NETWORK=sepolia ./deploy-server.sh
./zk-qualified-demo.sh
npm run devFor Sepolia deploys, configure a deployer key plus an RPC endpoint. A single private key is required by the production deploy guard so the script does not accidentally use Hardhat's default zero-balance mnemonic:
cat > .env.sepolia.local <<'EOF'
DEPLOYER_PRIVATE_KEY=0x...
ALCHEMY_API_KEY=...
# or: SEPOLIA_RPC_URL=https://eth-sepolia.g.alchemy.com/v2/...
EOF
npm run deploy:sepolia:productionThe script checks that the RPC is Sepolia and that the deployer has at least MIN_DEPLOYER_ETH (default 0.02) before deploying. After contracts are deployed, it syncs the frontend to zama-hackthon, writes .env.local plus public/deployment-config.json, builds Next.js, and restarts the production server. To reuse existing deployments/sepolia artifacts and only republish the frontend, run scripts/deploy-sepolia-production.sh --frontend-only.
- Local Hardhat tests cover product setup, vault-only deposit recording, the fallback demo qualification registry, and the Arkworks Groth16 registry verifier path.
- Local smoke mode: run
npm run chain,npm run deploy:localhost, thennpm run smoke:localhost. This validates deployment, product listing, qualification, mock USDC minting, approval, and rate publishing. - Server-local mode: run
npm run server:localon a VPS to start a local Hardhat chain, deploy contracts, write.env.local, and start the frontend. Prefer SSH tunnels for the frontend and RPC ports instead of exposing the Hardhat RPC publicly. - One-command server update: run
./deploy-server.sh. The script syncs the repo tozama-hackthon:~/privyields, prepares Arkworks verifier assets, installs dependencies, compiles contracts, restarts the local demo chain, deploys contracts, writes.env.local, builds the Next app, and startsnext starton port 3000 for Caddy to proxy. - Sepolia server update: run
npm run deploy:sepolia:production. Contracts are deployed from the local machine usingDEPLOYER_PRIVATE_KEY, then the server receives the deployment artifacts, frontend, prover binary, and proving keys. The frontend generates Groth16 proofs through/api/zk/proveand submits them to Sepolia with the connected wallet. - Product issuance is a direct on-chain transaction from the connected wallet. A separate Rust backend is not required unless the product marketplace later needs moderation, metadata hosting, indexing, or issuer reputation services.
- APR publishing is also public in the hackathon deployment so judge wallets can complete the reward flow. In production this should move back to product operators, strategy roles, or governed publishers.
- Sepolia demo funding does not require real USDC.
MockUSDC.mintis part of the deployed demo, and the frontend can mint and approve during the guided flow.DEMO_WALLET=0x... npm run mint:demo:sepoliais still available for pre-funding. - Arkworks ZK path: run
./zk-qualified-demo.shto generate BN254 Groth16 proving/verifying keys, produce an asset-threshold proof, verify it locally in Rust, export a Solidity verifier, compile it, and enable the Groth16 registry test. - FHE transactions require the Zama Sepolia coprocessor contracts; use Sepolia for the live encrypted flow.
- MetaMask cannot display cUSDC balances directly because cUSDC is confidential. Use the final page's
Decrypt cUSDC Balanceaction to decrypt the connected wallet's cumulative confidential balance. - The balance history is browser-local. It stores encrypted cUSDC and reward handles after wrap, allocation, reward settlement, and claim.
Decrypt Historybatch decrypts hidden rows with one wallet signature where possible. - The vault-custody model keeps funds in
ConfidentialYieldVault. Products are strategy labels and demo rate publishers, not external custodians. - Rewards are paid as encrypted cUSDC from the vault reserve for demo purposes.