11/**
2- * X402KamiyoClient - x402 payments with Kamiyo escrow protection
3- *
4- * Features:
5- * - x402 protocol v1/v2 compatibility
6- * - Escrow-backed payments with dispute resolution
7- * - SLA monitoring and automatic dispute triggering
8- * - Retry with exponential backoff
9- * - Circuit breaker for fault tolerance
10- * - Quality-based graduated refunds
11- *
12- * For cross-chain USDC payments, use the PayAIFacilitator class
13- * which integrates with PayAI Network (https://facilitator.payai.network)
2+ * x402 payment client with Kamiyo escrow protection.
143 */
154
165import {
@@ -40,37 +29,22 @@ import {
4029 LIMITS ,
4130} from './validation' ;
4231
43- // Types
44-
4532export interface X402ClientConfig {
46- /** Solana RPC connection */
4733 connection : Connection ;
48- /** Agent keypair for signing */
4934 wallet : Keypair ;
50- /** Kamiyo program ID */
5135 programId : PublicKey ;
52- /** Auto-dispute if quality falls below threshold (0-100) */
5336 qualityThreshold ?: number ;
54- /** Maximum SOL willing to pay per request */
5537 maxPricePerRequest ?: number ;
56- /** Default time lock for escrows in seconds */
5738 defaultTimeLock ?: number ;
58- /** Enable automatic SLA monitoring */
5939 enableSlaMonitoring ?: boolean ;
60- /** Default request timeout in ms */
6140 defaultTimeoutMs ?: number ;
62- /** Retry configuration */
6341 retry ?: Partial < RetryConfig > ;
64- /** Enable debug logging */
6542 debug ?: boolean ;
6643}
6744
6845export interface SlaParams {
69- /** Maximum response latency in ms */
7046 maxLatencyMs ?: number ;
71- /** Minimum quality score (0-100) */
7247 minQualityScore ?: number ;
73- /** Custom validation function */
7448 customValidator ?: ( response : unknown , latencyMs : number ) => SlaValidationResult ;
7549}
7650
@@ -82,19 +56,12 @@ export interface SlaValidationResult {
8256}
8357
8458export interface X402RequestOptions {
85- /** HTTP method */
8659 method ?: string ;
87- /** Request headers */
8860 headers ?: Record < string , string > ;
89- /** Request body */
9061 body ?: string ;
91- /** Use Kamiyo escrow for payment */
9262 useEscrow ?: boolean ;
93- /** Custom transaction ID */
9463 transactionId ?: string ;
95- /** SLA parameters to enforce */
9664 sla ?: SlaParams ;
97- /** Request timeout in ms */
9865 timeoutMs ?: number ;
9966}
10067
@@ -155,7 +122,9 @@ interface X402PaymentRequirement {
155122 } ;
156123}
157124
158- // Client Implementation
125+ const CLEANUP_INTERVAL_MS = 60_000 ;
126+ const SIGNATURE_TTL_MS = 120_000 ;
127+ const MAX_DISPUTE_RETRIES = 3 ;
159128
160129export class X402KamiyoClient {
161130 private readonly connection : Connection ;
@@ -170,10 +139,10 @@ export class X402KamiyoClient {
170139 private readonly executor : ResilientExecutor ;
171140 private readonly escrowHandler : EscrowHandler ;
172141
173- // Track active escrows
174142 private readonly activeEscrows = new Map < string , EscrowInfo > ( ) ;
175- // Track used payment signatures to prevent replay
176143 private readonly usedSignatures = new Map < string , number > ( ) ;
144+ private cleanupTimer : ReturnType < typeof setInterval > | null = null ;
145+ private destroyed = false ;
177146
178147 constructor ( config : X402ClientConfig ) {
179148 // Validate required config
@@ -215,15 +184,9 @@ export class X402KamiyoClient {
215184 programId : this . programId ,
216185 } ) ;
217186
218- // Cleanup old signatures periodically
219187 this . startSignatureCleanup ( ) ;
220188 }
221189
222- // Public API
223-
224- /**
225- * Make HTTP request with automatic x402 payment handling
226- */
227190 async request < T = unknown > (
228191 url : string ,
229192 options : X402RequestOptions = { }
@@ -309,8 +272,7 @@ export class X402KamiyoClient {
309272 const escrow = this . activeEscrows . get ( transactionId ) ;
310273 if ( escrow ) {
311274 this . log ( `SLA violation for ${ transactionId } , quality: ${ slaResult . qualityScore } ` ) ;
312- // Queue dispute asynchronously
313- this . queueDispute ( escrow , slaResult ) . catch ( e => this . log ( `Dispute failed: ${ e } ` ) ) ;
275+ this . queueDispute ( escrow , slaResult ) ;
314276 }
315277 }
316278 }
@@ -331,9 +293,6 @@ export class X402KamiyoClient {
331293 }
332294 }
333295
334- /**
335- * Create escrow for protected payment
336- */
337296 async createEscrow (
338297 provider : PublicKey ,
339298 amountLamports : number ,
@@ -399,9 +358,6 @@ export class X402KamiyoClient {
399358 }
400359 }
401360
402- /**
403- * Release escrow funds to provider
404- */
405361 async releaseEscrow ( transactionId : string ) : Promise < PaymentResult > {
406362 assertValid ( validateTransactionId ( transactionId , 'transactionId' ) , 'transactionId' ) ;
407363
@@ -433,9 +389,6 @@ export class X402KamiyoClient {
433389 }
434390 }
435391
436- /**
437- * File dispute for an escrow
438- */
439392 async disputeEscrow ( transactionId : string ) : Promise < PaymentResult > {
440393 assertValid ( validateTransactionId ( transactionId , 'transactionId' ) , 'transactionId' ) ;
441394
@@ -467,43 +420,36 @@ export class X402KamiyoClient {
467420 }
468421 }
469422
470- /**
471- * Get wallet balance in SOL
472- */
473423 async getBalance ( ) : Promise < number > {
474424 const lamports = await this . connection . getBalance ( this . wallet . publicKey ) ;
475425 return lamports / LAMPORTS_PER_SOL ;
476426 }
477427
478- /**
479- * Get public key
480- */
481428 getPublicKey ( ) : PublicKey {
482429 return this . wallet . publicKey ;
483430 }
484431
485- /**
486- * Get active escrows
487- */
488432 getActiveEscrows ( ) : Map < string , EscrowInfo > {
489433 return new Map ( this . activeEscrows ) ;
490434 }
491435
492- /**
493- * Get circuit breaker state
494- */
495436 getCircuitState ( ) : string {
496437 return this . executor . getCircuitState ( ) ;
497438 }
498439
499- /**
500- * Reset circuit breaker
501- */
502440 resetCircuit ( ) : void {
503441 this . executor . resetCircuit ( ) ;
504442 }
505443
506- // Private Methods
444+ destroy ( ) : void {
445+ if ( this . cleanupTimer ) {
446+ clearInterval ( this . cleanupTimer ) ;
447+ this . cleanupTimer = null ;
448+ }
449+ this . destroyed = true ;
450+ this . activeEscrows . clear ( ) ;
451+ this . usedSignatures . clear ( ) ;
452+ }
507453
508454 private async pay (
509455 requirement : X402PaymentRequirement ,
@@ -644,10 +590,21 @@ export class X402KamiyoClient {
644590 return { passed : violations . length === 0 , qualityScore, violations, metrics } ;
645591 }
646592
647- private async queueDispute ( escrow : EscrowInfo , slaResult : SlaValidationResult ) : Promise < void > {
593+ private queueDispute ( escrow : EscrowInfo , slaResult : SlaValidationResult ) : void {
648594 this . log ( `Filing dispute for ${ escrow . transactionId } : score ${ slaResult . qualityScore } ` ) ;
649- // In production, this would call the Kamiyo dispute instruction
650- // The oracle network evaluates and returns a quality score
595+
596+ const tryDispute = async ( attempt : number ) : Promise < void > => {
597+ if ( this . destroyed || attempt >= MAX_DISPUTE_RETRIES ) return ;
598+ try {
599+ await this . escrowHandler . dispute ( escrow . transactionId ) ;
600+ escrow . status = EscrowStatus . Disputed ;
601+ } catch ( err ) {
602+ this . log ( `Dispute attempt ${ attempt + 1 } failed: ${ err } ` ) ;
603+ setTimeout ( ( ) => tryDispute ( attempt + 1 ) , 1000 * Math . pow ( 2 , attempt ) ) ;
604+ }
605+ } ;
606+
607+ tryDispute ( 0 ) ;
651608 }
652609
653610 private async fetchWithTimeout (
@@ -672,10 +629,17 @@ export class X402KamiyoClient {
672629
673630 private parseAmount ( amount : string ) : number {
674631 const cleaned = amount . replace ( / [ ^ 0 - 9 . ] / g, '' ) ;
675- const value = parseFloat ( cleaned ) ;
676- // If contains decimal point, treat as SOL; otherwise treat as lamports
677- const hasDecimal = cleaned . includes ( '.' ) ;
678- return hasDecimal ? Math . floor ( value * LAMPORTS_PER_SOL ) : Math . floor ( value ) ;
632+ if ( ! cleaned ) return 0 ;
633+
634+ // Use BigInt for precision when possible
635+ if ( cleaned . includes ( '.' ) ) {
636+ const [ whole , frac = '' ] = cleaned . split ( '.' ) ;
637+ const padded = frac . padEnd ( 9 , '0' ) . slice ( 0 , 9 ) ;
638+ const lamports = BigInt ( whole || '0' ) * BigInt ( LAMPORTS_PER_SOL ) + BigInt ( padded ) ;
639+ return Number ( lamports ) ;
640+ }
641+
642+ return Number ( BigInt ( cleaned ) ) ;
679643 }
680644
681645 private errorResponse < T > ( error : X402Error ) : X402Response < T > {
@@ -689,21 +653,16 @@ export class X402KamiyoClient {
689653 }
690654
691655 private startSignatureCleanup ( ) : void {
692- // Clean up old signatures every 10 minutes
693- setInterval ( ( ) => {
694- const cutoff = Date . now ( ) - 600_000 ; // 10 minutes ago
656+ this . cleanupTimer = setInterval ( ( ) => {
657+ if ( this . destroyed ) return ;
658+ const cutoff = Date . now ( ) - SIGNATURE_TTL_MS ;
695659 for ( const [ sig , time ] of this . usedSignatures ) {
696- if ( time < cutoff ) {
697- this . usedSignatures . delete ( sig ) ;
698- }
660+ if ( time < cutoff ) this . usedSignatures . delete ( sig ) ;
699661 }
700- } , 600_000 ) ;
662+ } , CLEANUP_INTERVAL_MS ) ;
701663 }
702664}
703665
704- /**
705- * Create x402 client with Kamiyo protection
706- */
707666export function createX402KamiyoClient (
708667 connection : Connection ,
709668 wallet : Keypair ,
0 commit comments