Cryptographic Receipts
Verifiable proof of every transaction with signatures and hashes
What is a Receipt?
Every request in x402++ generates a cryptographically signed receipt that proves:
✓ Work Was Done
Provider's Ed25519 signature proves they processed the request
✓ Data Integrity
SHA-256 hashes prove input/output weren't tampered
✓ SLA Compliance
Automatic verification of latency and quality metrics
✓ Fair Charges
Receipt shows exactly what was charged
Receipt Structure
interface Receipt {
// Identity
receiptId: string;
sessionId: string;
requestNumber: number; // Sequence in session
// Cryptographic Proof
inputHash: string; // SHA-256 of input
outputHash: string; // SHA-256 of output
signature: string; // Ed25519 signature
// Timing
requestStartedAt: number; // Start timestamp
requestCompletedAt: number; // End timestamp
latencyMs: number; // Actual latency
// Financial
amountCharged: number; // Amount deducted
// SLA Verification
slaVerification: {
met: boolean, // Was SLA met?
metrics: {
latency?: {
expected: number,
actual: number,
met: boolean
}
},
refundAmount?: number // If breached
},
// Provider
providerPubkey: string; // Who created this
// Optional
pdaAddress?: string; // On-chain anchor
metadata?: Record<string, any>;
}Verification Process
Receipt Verification Steps: ─────────────────────────── 1. SIGNATURE VERIFICATION ┌──────────────────────────────────────┐ │ Extract signature from receipt │ │ Remove signature field │ │ Serialize remaining fields to JSON │ │ Hash with SHA-256 │ │ Verify Ed25519 signature │ │ → Valid = Provider signed this ✓ │ └──────────────────────────────────────┘ 2. DATA INTEGRITY CHECK ┌──────────────────────────────────────┐ │ Hash original input with SHA-256 │ │ Compare to receipt.inputHash │ │ → Match = Input not tampered ✓ │ │ │ │ Hash original output with SHA-256 │ │ Compare to receipt.outputHash │ │ → Match = Output not tampered ✓ │ └──────────────────────────────────────┘ 3. SLA VERIFICATION ┌──────────────────────────────────────┐ │ Check latencyMs <= sla.maxLatencyMs │ │ → If yes: SLA met ✓ │ │ → If no: Refund triggered │ └──────────────────────────────────────┘ 4. TIMESTAMP VALIDATION ┌──────────────────────────────────────┐ │ Verify completedAt > startedAt │ │ Verify latency = completed - started │ │ → Prevents time manipulation ✓ │ └──────────────────────────────────────┘
Verifying a Receipt
import { verifyReceipt, verifyReceiptHashes } from 'x402pp-core';
// 1. Verify signature
try {
verifyReceipt(receipt);
console.log('✓ Signature valid');
} catch (error) {
console.error('✗ Invalid signature!');
throw error;
}
// 2. Verify data integrity
const hashesValid = verifyReceiptHashes(
receipt,
originalInput,
originalOutput
);
if (!hashesValid) {
console.error('✗ Data tampered!');
throw new Error('Receipt hashes dont match');
}
// 3. Check SLA
if (!receipt.slaVerification.met) {
console.log('⚠ SLA breached');
console.log('Refund amount:', receipt.slaVerification.refundAmount);
}
console.log('✓ Receipt fully verified');Automatic SLA Refunds
// Scenario: SLA requires <2000ms, but request took 3500ms
const receipt = {
latencyMs: 3500,
amountCharged: 0.008,
slaVerification: {
met: false, // SLA breached!
metrics: {
latency: {
expected: 2000,
actual: 3500,
met: false
}
},
refundAmount: 0.004 // 50% refund for breach
}
};
// Agent automatically gets 0.004 USDC refund
// Balance: 0.996 → 1.0 (refund applied)
// Only paid 0.004 instead of 0.008
// Fair and automatic!On-Chain Anchoring
Optionally anchor receipts to Solana for permanent, immutable proof.
Solana Program (Anchor)
pub fn anchor_receipt(
ctx: Context<AnchorReceipt>,
receipt_id: String,
session_id: String,
input_hash: String,
output_hash: String,
latency_ms: u64,
sla_met: bool,
) -> Result<()> {
let receipt = &mut ctx.accounts.receipt;
receipt.receipt_id = receipt_id;
receipt.session_id = session_id;
receipt.input_hash = input_hash;
receipt.output_hash = output_hash;
receipt.latency_ms = latency_ms;
receipt.sla_met = sla_met;
receipt.timestamp = Clock::get()?.unix_timestamp;
msg!("Receipt anchored: {}", receipt.receipt_id);
Ok(())
}