Documentation Index Fetch the complete documentation index at: https://mintlify.com/nhestrompia/shielded-x402/llms.txt
Use this file to discover all available pages before exploring further.
Overview
The ShieldedClientSDK class handles anonymous proof-backed x402 payment payload construction. It manages deposits, spending proofs, and zero-knowledge proof generation for privacy-preserving payments.
Constructor
const sdk = new ShieldedClientSDK ( config : ShieldedClientConfig )
config
ShieldedClientConfig
required
Configuration object for the SDK signer
(message: string) => Promise<string>
required
Function to sign payment payloads (typically wallet.signMessage)
depositFn
(amount: bigint, commitment: Hex) => Promise<{ txHash: Hex; leafIndex: number }>
Optional function to execute onchain deposits
Optional zero-knowledge proof provider for generating real Noir proofs. See Proof Generation
Methods
deposit
Create a new shielded note and optionally deposit onchain.
await sdk . deposit (
amount : bigint ,
ownerPkHash : Hex
): Promise < { note : ShieldedNote ; txHash ?: Hex ; leafIndex : number } >
Amount to deposit (in smallest denomination)
Hash of the owner’s public key (BN254 field element)
The created shielded note Note commitment (derived from amount, rho, pkHash)
Position in Merkle tree (-1 if not yet inserted)
Transaction hash if depositFn was provided and executed
Example
const { note , txHash } = await sdk . deposit (
100_000_000 n , // 100 USDC (6 decimals)
'0x1234...' // owner pkHash
);
console . log ( 'Note commitment:' , note . commitment );
console . log ( 'Leaf index:' , note . leafIndex );
buildSpendProof
Build a spend proof bundle without generating the actual zero-knowledge proof (placeholder proof 0x00).
sdk . buildSpendProof (
params : SpendBuildParams
): SpendProofBundle
Parameters for building the spend proof Merkle witness proving note inclusion
Secret used to derive the nullifier (prevent double-spend)
Merchant’s public key hash (BN254 field element)
Merchant’s Ethereum address
Amount to pay (must be ≤ note.amount)
Challenge nonce from 402 response
Encrypted receipt data (can be ‘0x’)
Blinding factor for merchant note (auto-generated if omitted)
Blinding factor for change note (auto-generated if omitted)
Nullifier secret for change note (auto-generated if omitted)
Payment response with proof and public inputs
Change note returned to spender
Nullifier secret for the change note (store this!)
Blinding factor used for merchant note
buildSpendProofWithProvider
Build a spend proof bundle and generate the actual zero-knowledge proof using the configured proofProvider.
await sdk . buildSpendProofWithProvider (
params : SpendBuildParams
): Promise < SpendProofBundle >
Parameters and return type are identical to buildSpendProof, but the returned proof field will contain a real zero-knowledge proof instead of 0x00.
Example
const bundle = await sdk . buildSpendProofWithProvider ({
note ,
witness ,
nullifierSecret: '0xabc...' ,
merchantPubKey: '0x1234...' ,
merchantAddress: '0x5678...' ,
amount: 40_000_000 n ,
challengeNonce: '0x9999...' ,
encryptedReceipt: '0x'
});
console . log ( 'Proof:' , bundle . response . proof );
console . log ( 'Change amount:' , bundle . changeNote . amount );
prepare402Payment
Prepare a complete 402 payment including proof generation and signature.
await sdk . prepare402Payment (
requirement : PaymentRequirement ,
note : ShieldedNote ,
witness : MerkleWitness ,
nullifierSecret : Hex ,
baseHeaders ?: HeadersInit
): Promise < Prepared402Payment >
requirement
PaymentRequirement
required
Payment requirement from 402 response (parsed from X-Payment-Required header)
Merkle witness for the note
Nullifier secret for the note
Optional base headers to include in the payment request
Original payment requirement
Headers with X-Payment-Signature added
Payment response with proof
Change note to store locally
Nullifier secret for change note (store this!)
Example
const prepared = await sdk . prepare402Payment (
requirement ,
note ,
witness ,
nullifierSecret ,
{ 'User-Agent' : 'MyAgent/1.0' }
);
// Use prepared.headers in retry request
const response = await fetch ( url , {
headers: prepared . headers
});
pay402
Sign a payment response and generate the X-Payment-Signature header.
await sdk . pay402 (
paymentResponse : ShieldedPaymentResponse ,
requirement : PaymentRequirement ,
challengeNonce : Hex
): Promise <{
payload : string ;
signature : string ;
paymentSignatureHeader : string ;
}>
paymentResponse
ShieldedPaymentResponse
required
The payment response containing proof and public inputs
requirement
PaymentRequirement
required
Original payment requirement
Challenge nonce from the requirement
JSON-stringified payment response
Complete X-Payment-Signature header value
Utility Functions
buildWitnessFromCommitments
Derive a Merkle witness from a list of commitments.
import { buildWitnessFromCommitments } from '@shielded-x402/client' ;
const witness = buildWitnessFromCommitments (
commitments : Hex [],
targetIndex : number
): MerkleWitness
Array of commitment hashes in tree order
Index of the note to prove (0-based)
Sibling hashes from leaf to root
Binary path (0 = left, 1 = right)
Example
const commitments = noteStore . getCommitments ();
const witness = buildWitnessFromCommitments ( commitments , note . leafIndex );
Proof Generation
Using a ProofProvider
To generate real zero-knowledge proofs, configure a ProofProvider:
import { createProofProvider } from '@shielded-x402/client' ;
// Create proof provider (loads bundled circuit)
const proofProvider = await createProofProvider ();
const sdk = new ShieldedClientSDK ({
endpoint: RELAYER_URL ,
signer : async ( message ) => account . signMessage ({ message }),
proofProvider
});
Custom Circuit
Load a custom Noir circuit artifact:
import { createNoirJsProofProviderFromCircuit } from '@shielded-x402/client' ;
import circuit from './my-circuit.json' ;
const proofProvider = await createNoirJsProofProviderFromCircuit ( circuit , {
backendProofOptions: { verifierTarget: 'evm' }
});
Low-Level ProofProvider
Implement a custom proof provider:
import { createNoirJsProofProvider } from '@shielded-x402/client' ;
import { Noir } from '@noir-lang/noir_js' ;
import { UltraHonkBackend } from '@aztec/bb.js' ;
const noir = new Noir ( circuit );
const backend = new UltraHonkBackend ( circuit . bytecode );
const proofProvider = createNoirJsProofProvider ({
noir ,
backend ,
enforcePublicInputsMatch: true
});
Type Definitions
ShieldedNote
interface ShieldedNote {
amount : bigint ;
rho : Hex ; // Random blinding factor
pkHash : Hex ; // Owner public key hash
commitment : Hex ; // Commitment = H(amount, rho, pkHash)
leafIndex : number ; // Position in Merkle tree
}
MerkleWitness
interface MerkleWitness {
root : Hex ; // Merkle root
path : Hex []; // Sibling hashes
indexBits : number []; // Binary path (0/1)
}
ShieldedPaymentResponse
interface ShieldedPaymentResponse {
proof : Hex ; // Zero-knowledge proof
publicInputs : Hex []; // [nullifier, root, merchantCommitment, changeCommitment, challengeHash, amount]
nullifier : Hex ; // Note nullifier (prevents double-spend)
root : Hex ; // Merkle root
merchantCommitment : Hex ; // Commitment for merchant note
changeCommitment : Hex ; // Commitment for change note
challengeHash : Hex ; // Hash of challenge parameters
encryptedReceipt : Hex ; // Encrypted receipt data
txHint : string ; // Transaction hint (e.g., 'leaf:0')
}
Best Practices
Securely Store Nullifier Secrets
Nullifier secrets are critical for spending notes. Store them encrypted alongside note commitments in a secure wallet or database. const nullifierSecretsByCommitment = new Map < string , Hex >();
nullifierSecretsByCommitment . set ( note . commitment , changeNullifierSecret );
After each spend, store the returned change note and its nullifier secret for future transactions. const prepared = await sdk . prepare402Payment ( ... );
noteStore . ingestSpend ({
merchantCommitment: prepared . response . merchantCommitment ,
changeCommitment: prepared . response . changeCommitment
});
nullifierSecrets . set (
prepared . changeNote . commitment ,
prepared . changeNullifierSecret
);
Verify Proof Provider Outputs
The SDK enforces public input matching by default. Disable only if you trust the proof provider: const provider = createNoirJsProofProvider ({
noir ,
backend ,
enforcePublicInputsMatch: false // Not recommended
});
Handle BN254 Field Constraints
All random values (rho, nullifierSecret) are automatically generated within the BN254 field modulus. When providing custom values, ensure they are < 21888242871839275222246405745257275088548364400416034343698204186575808495617n.
See Also