@arcenpay/node 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1083 @@
1
+ import { X402VerifiedPaymentContext, ArcenCompanyKeys, ArcenUserKeys, IdentifyResult, ConsumeEntitlementResult, FlagResult, EntitlementResult, ArcenCompany, EmbedAccessTokenResponse, UsageProof, PublicInputs, BillingSettlement, PlanTier } from '@arcenpay/sdk';
2
+ export { ArcenCompany, ArcenCompanyKeys, ArcenUser, ArcenUserKeys, ConsumeEntitlementResult, EntitlementResult, FlagResult, IdentifyResult } from '@arcenpay/sdk';
3
+ import { Request, Response, NextFunction } from 'express';
4
+ export { TablelandConfig, TablelandService } from './tableland.mjs';
5
+
6
+ declare global {
7
+ namespace Express {
8
+ interface Request {
9
+ meapPayment?: X402VerifiedPaymentContext;
10
+ }
11
+ }
12
+ }
13
+
14
+ /**
15
+ * ArcenClient — Node.js SDK for the ArcenPay Protocol.
16
+ *
17
+ * Usage pattern (Schematic-style):
18
+ * 1. Call identify() once per session → stores session token
19
+ * 2. Call checkFlag() / checkEntitlement() / track() with no keys
20
+ */
21
+
22
+ interface ArcenClientConfig {
23
+ apiKey: string;
24
+ baseUrl?: string;
25
+ timeoutMs?: number;
26
+ }
27
+ interface TrackEventInput {
28
+ name: string;
29
+ company?: ArcenCompanyKeys;
30
+ user?: ArcenUserKeys;
31
+ traits?: Record<string, unknown>;
32
+ idempotencyKey?: string;
33
+ }
34
+ interface IdentifyInput {
35
+ company?: ArcenCompanyKeys & {
36
+ name?: string;
37
+ traits?: Record<string, unknown>;
38
+ };
39
+ user?: ArcenUserKeys & {
40
+ name?: string;
41
+ email?: string;
42
+ };
43
+ expiresIn?: number;
44
+ }
45
+ interface ActivateSubscriptionInput {
46
+ planId: string | number | bigint;
47
+ paymentAccount: string;
48
+ company: ArcenCompanyKeys & {
49
+ name?: string;
50
+ traits?: Record<string, unknown>;
51
+ };
52
+ subscriberEmail?: string;
53
+ }
54
+ interface ActivateSubscriptionResult {
55
+ tokenId: string;
56
+ txHash: string;
57
+ companyId: string;
58
+ planId: string;
59
+ paymentAccount: string;
60
+ }
61
+ interface ConsumeEntitlementInput {
62
+ featureKey: string;
63
+ company?: ArcenCompanyKeys;
64
+ user?: ArcenUserKeys;
65
+ traits?: Record<string, unknown>;
66
+ idempotencyKey?: string;
67
+ }
68
+ declare class ArcenClient {
69
+ private readonly apiKey;
70
+ private readonly baseUrl;
71
+ private readonly timeoutMs;
72
+ private session;
73
+ constructor(config: ArcenClientConfig);
74
+ private buildAuthHeader;
75
+ private request;
76
+ identify(input: IdentifyInput): Promise<IdentifyResult>;
77
+ track(event: string | TrackEventInput): Promise<void>;
78
+ consumeEntitlement(input: string | ConsumeEntitlementInput): Promise<ConsumeEntitlementResult>;
79
+ checkFlag(key: string, companyKeys?: ArcenCompanyKeys, userKeys?: ArcenUserKeys): Promise<FlagResult>;
80
+ checkEntitlement(key: string, companyKeys?: ArcenCompanyKeys, userKeys?: ArcenUserKeys): Promise<EntitlementResult>;
81
+ checkFlags(featureKeys: string[], companyKeys?: ArcenCompanyKeys, userKeys?: ArcenUserKeys): Promise<Record<string, FlagResult>>;
82
+ listCompanies(options?: {
83
+ limit?: number;
84
+ search?: string;
85
+ cursor?: string;
86
+ }): Promise<{
87
+ data: ArcenCompany[];
88
+ count: number;
89
+ nextCursor?: string;
90
+ }>;
91
+ getCompany(id: string): Promise<{
92
+ data: ArcenCompany & {
93
+ entitlements: unknown[];
94
+ };
95
+ }>;
96
+ createCompany(data: {
97
+ name: string;
98
+ wallet?: string;
99
+ email?: string;
100
+ id?: string;
101
+ traits?: Record<string, unknown>;
102
+ }): Promise<{
103
+ data: ArcenCompany;
104
+ }>;
105
+ updateCompany(id: string, data: {
106
+ name?: string;
107
+ traits?: Record<string, unknown>;
108
+ activePlanId?: string | null;
109
+ }): Promise<{
110
+ data: ArcenCompany;
111
+ }>;
112
+ setCompanyOverride(companyId: string, featureKey: string, value: boolean, reason?: string): Promise<void>;
113
+ activateSubscription(input: ActivateSubscriptionInput): Promise<ActivateSubscriptionResult>;
114
+ /** @deprecated Use identify() instead */
115
+ createAccessToken(companyKeys?: ArcenCompanyKeys, userKeys?: ArcenUserKeys, expiresIn?: number): Promise<EmbedAccessTokenResponse>;
116
+ }
117
+ declare class ArcenApiError extends Error {
118
+ readonly statusCode: number;
119
+ constructor(message: string, statusCode: number);
120
+ }
121
+
122
+ /**
123
+ * NonceStore — Interface for x402 nonce deduplication.
124
+ *
125
+ * Implementations must provide atomic check-and-set semantics:
126
+ * if `markUsed` returns true, the nonce was not previously seen
127
+ * and is now marked as used. If it returns false, the nonce is
128
+ * a replay and must be rejected.
129
+ */
130
+ interface NonceStore {
131
+ /**
132
+ * Atomically check if nonce was used and mark it used.
133
+ * @returns true if nonce was fresh (now marked), false if already seen (replay)
134
+ */
135
+ markUsed(key: string, ttlMs: number): Promise<boolean>;
136
+ /** Check if nonce has been used. */
137
+ has(key: string): Promise<boolean>;
138
+ /** Stop background processes (timers etc.) */
139
+ stop(): void;
140
+ }
141
+ declare class InMemoryNonceStore implements NonceStore {
142
+ private readonly seen;
143
+ private readonly cleanupTimer;
144
+ constructor(cleanupIntervalMs?: number);
145
+ markUsed(key: string, ttlMs: number): Promise<boolean>;
146
+ has(key: string): Promise<boolean>;
147
+ stop(): void;
148
+ }
149
+ interface RedisLike {
150
+ set(key: string, value: string, exMode: "EX", exValue: number, nxMode: "NX"): Promise<string | null>;
151
+ get(key: string): Promise<string | null>;
152
+ quit(): Promise<string>;
153
+ }
154
+ declare class RedisNonceStore implements NonceStore {
155
+ private readonly redis;
156
+ private readonly prefix;
157
+ constructor(redis: RedisLike, prefix?: string);
158
+ markUsed(key: string, ttlMs: number): Promise<boolean>;
159
+ has(key: string): Promise<boolean>;
160
+ stop(): void;
161
+ }
162
+
163
+ type X402SettlePaymentInput = {
164
+ signer: `0x${string}`;
165
+ amount: bigint;
166
+ sessionId: string;
167
+ planId: string;
168
+ paymentHeader: string;
169
+ payload: Record<string, unknown>;
170
+ request: Request;
171
+ };
172
+ type X402SettlePaymentResult = {
173
+ settledAmount: bigint | string | number;
174
+ settlementTxHash?: string | null;
175
+ };
176
+ interface X402MiddlewareOptions {
177
+ /** Plan ID for pricing reference */
178
+ planId: string;
179
+ /** Price per API call in USDC (human-readable, e.g., "0.001") */
180
+ ratePerCall: string;
181
+ /** Network identifier (e.g., "base-mainnet") */
182
+ network?: string;
183
+ /** Chain ID for contract reads */
184
+ chainId?: number;
185
+ /** RPC URL for on-chain validation */
186
+ rpcUrl?: string;
187
+ /** Payment facilitator contract address */
188
+ payTo?: string;
189
+ /** Optional settlement hook. Called after payment verification succeeds. */
190
+ settlePayment?: (input: X402SettlePaymentInput) => Promise<X402SettlePaymentResult>;
191
+ /**
192
+ * If true, allow requests when balance check RPC/contract read fails.
193
+ * Default false for production-safe fail-closed behavior.
194
+ */
195
+ failOpenOnBalanceCheck?: boolean;
196
+ /**
197
+ * Maximum age of payment timestamp in milliseconds.
198
+ * Payments older than this window are rejected.
199
+ * Default: 300_000 (5 minutes).
200
+ */
201
+ maxPaymentAgeMs?: number;
202
+ /**
203
+ * TTL for nonce deduplication entries in milliseconds.
204
+ * Set to at least 2x maxPaymentAgeMs.
205
+ * Default: 600_000 (10 minutes).
206
+ */
207
+ nonceTtlMs?: number;
208
+ /**
209
+ * Pluggable nonce store for durable anti-replay protection.
210
+ * Defaults to InMemoryNonceStore if not provided.
211
+ * Use RedisNonceStore for multi-instance production deployments.
212
+ */
213
+ nonceStore?: NonceStore;
214
+ }
215
+ /**
216
+ * x402 Express Middleware — Production-grade payment validation
217
+ *
218
+ * Validation algorithm (PRD §7.1.1):
219
+ * 1. Parse X-PAYMENT header (base64 JSON)
220
+ * 2. Verify EIP-712 typed data signature → recover signer
221
+ * 3. Verify signer matches payment.from
222
+ * 4. Check SessionVault.getAgentBalance(signer) >= maxAmountRequired
223
+ * 5. Return 402 on failure, attach req.meapPayment on success
224
+ */
225
+ declare function x402Middleware(options: X402MiddlewareOptions): (req: Request, res: Response, next: NextFunction) => Promise<void>;
226
+
227
+ interface UsageLogEntry {
228
+ sessionId: string;
229
+ callData: {
230
+ endpoint: string;
231
+ method: string;
232
+ timestamp: number;
233
+ responseSize?: number;
234
+ };
235
+ signature: string;
236
+ }
237
+ interface UsageAggregation {
238
+ sessionId: string;
239
+ callCount: number;
240
+ windowStart: number;
241
+ windowEnd: number;
242
+ logs: UsageLogEntry[];
243
+ }
244
+ type ProofMode = "strict" | "dev";
245
+ declare const UsageProofErrorCodes: {
246
+ readonly TOOLING_UNAVAILABLE: "TOOLING_UNAVAILABLE";
247
+ readonly ARTIFACTS_MISSING: "ARTIFACTS_MISSING";
248
+ readonly ARTIFACT_VERSION_MISMATCH: "ARTIFACT_VERSION_MISMATCH";
249
+ readonly INVALID_VERIFICATION_KEY: "INVALID_VERIFICATION_KEY";
250
+ readonly INVALID_INPUT: "INVALID_INPUT";
251
+ readonly PROOF_GENERATION_FAILED: "PROOF_GENERATION_FAILED";
252
+ readonly PROOF_SIGNAL_MISMATCH: "PROOF_SIGNAL_MISMATCH";
253
+ };
254
+ type UsageProofErrorCode = (typeof UsageProofErrorCodes)[keyof typeof UsageProofErrorCodes];
255
+ declare class UsageProofError extends Error {
256
+ readonly code: UsageProofErrorCode;
257
+ readonly cause?: unknown;
258
+ constructor(code: UsageProofErrorCode, message: string, cause?: unknown);
259
+ }
260
+ interface UsageServiceConfig {
261
+ signerPrivateKey?: string;
262
+ chainId?: number;
263
+ rpcUrl?: string;
264
+ proofMode?: ProofMode;
265
+ circuitsRootDir?: string;
266
+ circuitBuildDir?: string;
267
+ circuitWasmPath?: string;
268
+ zkeyPath?: string;
269
+ verificationKeyPath?: string;
270
+ proveScriptPath?: string;
271
+ artifactVersion?: string;
272
+ artifactManifestPath?: string;
273
+ }
274
+ declare class UsageService {
275
+ private logs;
276
+ private readonly config;
277
+ private readonly signerAccount;
278
+ private readonly publicClient;
279
+ private readonly walletClient;
280
+ private runtimeValidated;
281
+ constructor(config?: UsageServiceConfig);
282
+ getProofMode(): ProofMode;
283
+ getProofRuntimeConfig(): {
284
+ proofMode: ProofMode;
285
+ proveScriptPath: string;
286
+ circuitWasmPath: string;
287
+ zkeyPath: string;
288
+ verificationKeyPath: string;
289
+ artifactVersion: string;
290
+ artifactManifestPath: string;
291
+ };
292
+ assertProofRuntimeReady(): void;
293
+ logUsage(sessionId: string, callData: UsageLogEntry["callData"]): Promise<void>;
294
+ getUsageCount(sessionId: string): number;
295
+ getAllSessions(): string[];
296
+ aggregateUsage(sessionId: string, windowEnd: number): UsageAggregation;
297
+ generateProof(sessionId: string, windowEnd: number): Promise<{
298
+ proof: UsageProof;
299
+ publicInputs: PublicInputs;
300
+ }>;
301
+ submitProof(proof: UsageProof, publicInputs: PublicInputs): Promise<BillingSettlement>;
302
+ clearLogs(sessionId: string, windowEnd: number): void;
303
+ private resolveCircuitsRoot;
304
+ private assertProofTooling;
305
+ private assertProofArtifacts;
306
+ private assertArtifactVersion;
307
+ private assertVerificationKey;
308
+ private wrapProofError;
309
+ private loadCircuitProver;
310
+ private assertPublicSignals;
311
+ private hashUsageLog;
312
+ private normalizeBytes32;
313
+ private addressToBytes32;
314
+ private fieldToBytes32;
315
+ private toFieldElement;
316
+ }
317
+
318
+ type SettlementCallback = (settlement: BillingSettlement) => void;
319
+ /**
320
+ * SettlementService — Monitors on-chain BillingSettled events
321
+ * and provides session vault balance queries via viem
322
+ */
323
+ declare class SettlementService {
324
+ private rpcUrl;
325
+ private chainId;
326
+ private zkVerifierAddress;
327
+ private sessionVaultAddress;
328
+ private watchers;
329
+ private pollInterval;
330
+ private lastBlock;
331
+ private publicClient;
332
+ constructor(config: {
333
+ rpcUrl: string;
334
+ chainId?: number;
335
+ zkVerifierAddress?: string;
336
+ sessionVaultAddress?: string;
337
+ });
338
+ /**
339
+ * Queries the SessionVault for remaining balance
340
+ */
341
+ getSessionBalance(agentAddress: string): Promise<bigint>;
342
+ /**
343
+ * Gets details for a specific session
344
+ */
345
+ getSessionDetails(sessionId: string): Promise<any>;
346
+ /**
347
+ * Subscribes to on-chain BillingSettled events
348
+ */
349
+ watchSettlements(callback: SettlementCallback, intervalMs?: number): () => void;
350
+ /**
351
+ * Initialize the starting block number for event polling
352
+ */
353
+ private initBlockNumber;
354
+ /**
355
+ * Polls for new BillingSettled events from ZKUsageVerifier
356
+ */
357
+ private pollSettlements;
358
+ /**
359
+ * Stops all settlement watchers
360
+ */
361
+ stop(): void;
362
+ }
363
+
364
+ interface SettlementWriterConfig {
365
+ rpcUrl: string;
366
+ privateKey: `0x${string}`;
367
+ chainId?: number;
368
+ sessionVaultAddress?: `0x${string}`;
369
+ feeCollectorAddress?: `0x${string}`;
370
+ providerAddress?: `0x${string}`;
371
+ waitForReceipt?: boolean;
372
+ }
373
+ interface SettleForSignerInput {
374
+ signer: `0x${string}`;
375
+ amount: bigint;
376
+ sessionIdHint: string;
377
+ providerAddress?: `0x${string}`;
378
+ }
379
+ interface SettlementWriteResult {
380
+ transactionHash: `0x${string}`;
381
+ settledAmount: bigint;
382
+ sessionId: `0x${string}`;
383
+ providerAddress: `0x${string}`;
384
+ }
385
+ /**
386
+ * SettlementWriterService
387
+ *
388
+ * Writes `SessionVault.settle(sessionId, amount, provider)` transactions
389
+ * using a facilitator signer configured in runtime environment.
390
+ */
391
+ declare class SettlementWriterService {
392
+ private readonly chainId;
393
+ private readonly sessionVaultAddress;
394
+ private readonly feeCollectorAddress;
395
+ private readonly providerAddress;
396
+ private readonly signerAddress;
397
+ private readonly waitForReceipt;
398
+ private readonly publicClient;
399
+ private readonly walletClient;
400
+ constructor(config: SettlementWriterConfig);
401
+ getSignerAddress(): `0x${string}`;
402
+ getSessionVaultAddress(): `0x${string}`;
403
+ isSignerAuthorized(address?: `0x${string}`): Promise<boolean>;
404
+ private getSession;
405
+ private resolveSessionId;
406
+ settleForSigner(input: SettleForSignerInput): Promise<SettlementWriteResult>;
407
+ }
408
+
409
+ type SubscriptionEventType = 'minted' | 'renewed' | 'cancelled' | 'plan_changed' | 'billing_executed';
410
+ interface SubscriptionEventData {
411
+ type: SubscriptionEventType;
412
+ tokenId: bigint;
413
+ subscriber?: string;
414
+ planId?: bigint;
415
+ previousPlanId?: bigint;
416
+ expiration?: bigint;
417
+ amountPaid?: bigint;
418
+ account?: string;
419
+ merchant?: string;
420
+ billingReason?: bigint;
421
+ merchantAmount?: bigint;
422
+ protocolFee?: bigint;
423
+ transactionHash: string;
424
+ blockNumber: bigint;
425
+ timestamp: number;
426
+ }
427
+ type EventCallback = (event: SubscriptionEventData) => void | Promise<void>;
428
+ /**
429
+ * EventListenerService — Persistent event watchers for the SubscriptionRegistry.
430
+ *
431
+ * Watches for:
432
+ * - SubscriptionMinted(subscriber, tokenId, planId, expiration)
433
+ * - SubscriptionRenewed(tokenId, newExpiration, amountPaid)
434
+ * - SubscriptionCancelled(tokenId)
435
+ * - SubscriptionPlanChanged(tokenId, previousPlanId, newPlanId, expiration)
436
+ *
437
+ * PRD §9.1 — On-Chain Event Listeners
438
+ */
439
+ declare class EventListenerService {
440
+ private publicClient;
441
+ private registryAddress;
442
+ private autopayModuleAddress;
443
+ private callbacks;
444
+ private pollInterval;
445
+ private lastBlock;
446
+ constructor(config: {
447
+ rpcUrl: string;
448
+ chainId?: number;
449
+ registryAddress?: string;
450
+ });
451
+ /**
452
+ * Register a callback for a specific event type (or '*' for all events)
453
+ */
454
+ on(eventType: SubscriptionEventType | '*', callback: EventCallback): () => void;
455
+ /**
456
+ * Start polling for events at the given interval
457
+ */
458
+ start(intervalMs?: number): Promise<void>;
459
+ /**
460
+ * Stop polling
461
+ */
462
+ stop(): void;
463
+ /**
464
+ * Poll for new events since lastBlock
465
+ */
466
+ private poll;
467
+ /**
468
+ * Dispatch event to registered callbacks
469
+ */
470
+ private emit;
471
+ }
472
+
473
+ interface WebhookConfig {
474
+ /** Provider-configured webhook endpoint URL */
475
+ url: string;
476
+ /** HMAC-SHA256 signing secret */
477
+ secret: string;
478
+ /** Max retries (default: 5) */
479
+ maxRetries?: number;
480
+ /** Retry delays in ms (exponential backoff) */
481
+ retryDelays?: number[];
482
+ /** Optional callback invoked on every delivery attempt (success or failure). */
483
+ onDelivery?: (entry: WebhookDeliveryLog, payload: WebhookPayload) => Promise<void> | void;
484
+ }
485
+ interface WebhookPayload {
486
+ event: string;
487
+ timestamp: number;
488
+ data: Record<string, unknown>;
489
+ }
490
+ interface WebhookDeliveryLog {
491
+ id: string;
492
+ event: string;
493
+ url: string;
494
+ statusCode: number | null;
495
+ attemptNumber: number;
496
+ deliveredAt: number;
497
+ success: boolean;
498
+ error?: string;
499
+ }
500
+ /**
501
+ * WebhookService — Delivers HMAC-signed webhook payloads
502
+ * to provider-configured endpoints with exponential backoff retry.
503
+ *
504
+ * Payload format:
505
+ * { event: 'subscription.renewed', timestamp, data: { tokenId, address, planId, amount, txHash } }
506
+ *
507
+ * Signing:
508
+ * HMAC-SHA256(JSON.stringify(payload), signingSecret) → X-MEAP-Signature header
509
+ */
510
+ declare class WebhookService {
511
+ private config;
512
+ private deliveryLog;
513
+ private pendingRetries;
514
+ constructor(config: WebhookConfig);
515
+ /**
516
+ * Deliver a webhook for a subscription event
517
+ */
518
+ deliverEvent(event: SubscriptionEventData): Promise<void>;
519
+ /**
520
+ * Deliver an arbitrary webhook payload
521
+ */
522
+ deliver(payload: WebhookPayload, attempt?: number): Promise<void>;
523
+ /**
524
+ * HMAC-SHA256 signature of the payload body
525
+ */
526
+ private sign;
527
+ /**
528
+ * Schedule a retry with exponential backoff
529
+ */
530
+ private scheduleRetry;
531
+ /**
532
+ * Log a delivery attempt
533
+ */
534
+ private log;
535
+ private persistDelivery;
536
+ /**
537
+ * Get the delivery log for dashboard display
538
+ */
539
+ getDeliveryLog(limit?: number): WebhookDeliveryLog[];
540
+ /**
541
+ * Verify an incoming webhook signature (for consumers).
542
+ *
543
+ * @example
544
+ * ```ts
545
+ * import { verifyWebhookSignature } from '@arcenpay/node';
546
+ *
547
+ * app.post('/webhooks/meap', (req, res) => {
548
+ * const raw = JSON.stringify(req.body);
549
+ * const sig = req.headers['x-meap-signature'] as string;
550
+ * if (!verifyWebhookSignature(raw, sig, process.env.MEAP_WEBHOOK_SECRET!)) {
551
+ * return res.status(401).json({ error: 'Invalid signature' });
552
+ * }
553
+ * });
554
+ * ```
555
+ */
556
+ static verifySignature(body: string, signature: string, secret: string): boolean;
557
+ /**
558
+ * Cancel all pending retries and clean up
559
+ */
560
+ stop(): void;
561
+ }
562
+ /**
563
+ * Standalone helper for verifying incoming MEAP webhook signatures.
564
+ * Equivalent to `WebhookService.verifySignature()` — exported for tree-shaking convenience.
565
+ *
566
+ * @param body - Raw request body string (before any JSON.parse)
567
+ * @param signature - Value from the `X-MEAP-Signature` request header
568
+ * @param secret - Shared HMAC signing secret configured on the provider's webhook endpoint
569
+ */
570
+ declare function verifyWebhookSignature(body: string, signature: string, secret: string): boolean;
571
+
572
+ interface EmailConfig {
573
+ /** Resend API key */
574
+ apiKey: string;
575
+ /** Sender email address (e.g. noreply@arcenpay.io) */
576
+ fromAddress: string;
577
+ /** Provider brand name (injected into templates) */
578
+ brandName?: string;
579
+ }
580
+ interface EmailRecipient {
581
+ email: string;
582
+ walletAddress: string;
583
+ name?: string;
584
+ }
585
+ type EmailTemplate = 'welcome' | 'renewal_receipt' | 'payment_failed' | 'subscription_expiring' | 'access_revoked' | 'low_session_balance';
586
+ /**
587
+ * EmailService — Transactional email notifications via Resend API.
588
+ *
589
+ * Templates: Welcome, Renewal Receipt, Payment Failed,
590
+ * Subscription Expiring, Access Revoked, Low Session Balance.
591
+ *
592
+ * PRD §9.2 — providers configure their Resend API key in Dashboard → Settings.
593
+ */
594
+ declare class EmailService {
595
+ private apiKey;
596
+ private fromAddress;
597
+ private brandName;
598
+ constructor(config: EmailConfig);
599
+ /**
600
+ * Send an email using a pre-defined template
601
+ */
602
+ sendTemplate(template: EmailTemplate, recipient: EmailRecipient, data: Record<string, string>): Promise<{
603
+ success: boolean;
604
+ messageId?: string;
605
+ error?: string;
606
+ }>;
607
+ /**
608
+ * Build the email payload for a given template
609
+ */
610
+ private buildEmail;
611
+ /**
612
+ * Wrap template body in a consistent email layout
613
+ */
614
+ private wrapInLayout;
615
+ }
616
+
617
+ interface LitAccessCondition {
618
+ contractAddress: string;
619
+ standardContractType: 'ERC721' | 'ERC1155' | 'Custom';
620
+ chain: string;
621
+ method: string;
622
+ parameters: string[];
623
+ returnValueTest: {
624
+ comparator: '>' | '<' | '=' | '>=' | '<=';
625
+ value: string;
626
+ };
627
+ }
628
+ interface LitConfig {
629
+ /**
630
+ * Supported Lit network aliases.
631
+ * Legacy names are kept for backwards compatibility with older env files.
632
+ */
633
+ network: 'manzano' | 'habanero' | 'datil-dev' | 'datil-test' | 'datil';
634
+ /** SubscriptionRegistry contract address */
635
+ registryAddress: string;
636
+ /** Chain name used in ACC conditions (e.g., 'baseSepolia', 'base', 'sepolia') */
637
+ chain: string;
638
+ }
639
+ type LitAuthInput = {
640
+ authSig?: unknown;
641
+ sessionSigs?: unknown;
642
+ };
643
+ declare class LitProtocolService {
644
+ private config;
645
+ private litClient;
646
+ constructor(config: LitConfig);
647
+ connect(): Promise<void>;
648
+ buildSubscriptionACC(_walletAddress?: string): LitAccessCondition[];
649
+ buildTierACC(minimumTier: PlanTier): LitAccessCondition[];
650
+ encrypt(content: string, accessConditions: LitAccessCondition[]): Promise<{
651
+ ciphertext: string;
652
+ dataToEncryptHash: string;
653
+ }>;
654
+ decrypt(ciphertext: string, dataToEncryptHash: string, accessConditions: LitAccessCondition[], auth: LitAuthInput): Promise<string>;
655
+ disconnect(): Promise<void>;
656
+ private ensureConnected;
657
+ }
658
+
659
+ interface UsageEntry {
660
+ sessionId: string;
661
+ endpoint: string;
662
+ method: string;
663
+ timestamp: number;
664
+ responseSize: number;
665
+ signature: string;
666
+ }
667
+ /**
668
+ * AggregatorService — Accumulates signed usage logs into a Merkle tree
669
+ * and triggers zk-SNARK proof generation when the threshold is reached.
670
+ *
671
+ * Architecture:
672
+ * 1. Facilitator logs each x402 API call to the aggregator
673
+ * 2. Aggregator builds a Merkle tree of log hashes
674
+ * 3. When threshold is reached, the proving pipeline is triggered
675
+ * 4. The proof + Merkle root are submitted to ZKUsageVerifier.sol
676
+ *
677
+ * For production: replace in-memory maps with Redis for persistence
678
+ * PRD §7.2 — ZKVUB Off-Chain Aggregation
679
+ */
680
+ declare class AggregatorService {
681
+ private logs;
682
+ private merkleRoots;
683
+ private settlementThreshold;
684
+ constructor(config?: {
685
+ settlementThreshold?: number;
686
+ });
687
+ /**
688
+ * Append a signed usage entry
689
+ */
690
+ addEntry(entry: UsageEntry): {
691
+ totalEntries: number;
692
+ thresholdReached: boolean;
693
+ };
694
+ /**
695
+ * Build a Merkle tree from session's usage logs
696
+ * Returns the Merkle root hash
697
+ */
698
+ buildMerkleTree(sessionId: string): string;
699
+ /**
700
+ * Get circuit inputs for zk-SNARK proving
701
+ * These inputs are passed to the Circom circuit
702
+ */
703
+ getCircuitInputs(sessionId: string): {
704
+ callCount: number;
705
+ merkleRoot: string;
706
+ windowStart: number;
707
+ windowEnd: number;
708
+ logHashes: string[];
709
+ };
710
+ /**
711
+ * Clear entries for a settled session window
712
+ */
713
+ clearSettledEntries(sessionId: string, windowEnd: number): void;
714
+ /**
715
+ * Get aggregation stats for monitoring
716
+ */
717
+ getStats(): {
718
+ sessions: number;
719
+ totalEntries: number;
720
+ pendingSettlements: number;
721
+ };
722
+ /**
723
+ * Hash a single usage entry (leaf in the Merkle tree)
724
+ */
725
+ private hashEntry;
726
+ /**
727
+ * Compute Merkle root from leaf hashes
728
+ */
729
+ private computeMerkleRoot;
730
+ }
731
+
732
+ interface RedisUsageStoreConfig {
733
+ url: string;
734
+ keyPrefix?: string;
735
+ ttlSeconds?: number;
736
+ }
737
+ interface PersistedUsageEntry {
738
+ sessionId: string;
739
+ signer?: string;
740
+ endpoint: string;
741
+ method: string;
742
+ timestamp: number;
743
+ amount?: string;
744
+ settlementTxHash?: string | null;
745
+ }
746
+ declare class RedisUsageStore {
747
+ private readonly config;
748
+ private readonly client;
749
+ private connected;
750
+ constructor(config: RedisUsageStoreConfig);
751
+ connect(): Promise<void>;
752
+ appendUsage(entry: PersistedUsageEntry): Promise<void>;
753
+ getUsageCount(sessionId: string): Promise<number>;
754
+ getUsageEntries(sessionId: string, limit?: number): Promise<PersistedUsageEntry[]>;
755
+ setLatestSettlementTx(sessionId: string, txHash: string): Promise<void>;
756
+ getLatestSettlementTx(sessionId: string): Promise<string | null>;
757
+ stop(): Promise<void>;
758
+ }
759
+
760
+ interface ProofOrchestratorConfig extends UsageServiceConfig {
761
+ /** Max retries for proof submission (default: 3) */
762
+ maxRetries?: number;
763
+ /** Backoff delays in ms between retries (default: [2000, 5000, 15000]) */
764
+ retryDelays?: number[];
765
+ /** Auto-submit threshold: generate + submit proof when a session reaches this many calls */
766
+ autoSubmitThreshold?: number;
767
+ /** Settlement window duration in seconds (default: 3600 = 1 hour) */
768
+ windowDurationSeconds?: number;
769
+ }
770
+ type ProofStatus = "pending" | "generating" | "submitting" | "settled" | "failed";
771
+ interface ProofJob {
772
+ sessionId: string;
773
+ windowEnd: number;
774
+ windowStart: number | null;
775
+ callCount: string | null;
776
+ settlementAmount: string | null;
777
+ status: ProofStatus;
778
+ attempts: number;
779
+ nullifier: string | null;
780
+ txHash: string | null;
781
+ error: string | null;
782
+ createdAt: number;
783
+ lastAttemptAt: number;
784
+ }
785
+ /**
786
+ * ProofOrchestrator coordinates the full ZKVUB settlement pipeline:
787
+ *
788
+ * 1. Monitor usage aggregation counts per session
789
+ * 2. When threshold is reached, freeze window and generate proof
790
+ * 3. Submit proof on-chain with deterministic retry
791
+ * 4. Track nullifiers to prevent double-settlement
792
+ * 5. Clean up after successful settlement
793
+ */
794
+ declare class ProofOrchestrator {
795
+ private readonly usageService;
796
+ private readonly config;
797
+ /**
798
+ * Nullifiers that have been submitted — prevents double-settlement.
799
+ * Key: nullifier hash, Value: settlement timestamp
800
+ */
801
+ private readonly settledNullifiers;
802
+ /** Active proof jobs indexed by `sessionId:windowEnd` */
803
+ private readonly activeJobs;
804
+ /** Completed job history (last 200) */
805
+ private readonly jobHistory;
806
+ /** Interval for auto-submit checks */
807
+ private autoCheckInterval;
808
+ constructor(config?: ProofOrchestratorConfig);
809
+ /**
810
+ * Records usage and auto-triggers proof+submission when threshold is reached
811
+ */
812
+ recordUsage(sessionId: string, callData: {
813
+ endpoint: string;
814
+ method: string;
815
+ timestamp: number;
816
+ responseSize?: number;
817
+ }): Promise<void>;
818
+ /**
819
+ * Manually triggers proof generation + submission for a session
820
+ */
821
+ generateAndSubmit(sessionId: string): Promise<ProofJob>;
822
+ /**
823
+ * Execute a proof job with deterministic retry
824
+ */
825
+ private executeJob;
826
+ /**
827
+ * Move a job from active to history
828
+ */
829
+ private archiveJob;
830
+ /**
831
+ * Start auto-submit monitoring (polls every 30s)
832
+ */
833
+ startAutoSubmit(): void;
834
+ /**
835
+ * Stop auto-submit monitoring
836
+ */
837
+ stop(): void;
838
+ /**
839
+ * Get current active proof jobs
840
+ */
841
+ getActiveJobs(): ProofJob[];
842
+ /**
843
+ * Get completed job history
844
+ */
845
+ getJobHistory(limit?: number): ProofJob[];
846
+ /**
847
+ * Check if a nullifier has already been settled
848
+ */
849
+ isNullifierSettled(nullifier: string): boolean;
850
+ /**
851
+ * Get the underlying UsageService for direct access
852
+ */
853
+ getUsageService(): UsageService;
854
+ }
855
+
856
+ interface AxelarTransportConfig {
857
+ /** Axelar Gateway contract address on source chain */
858
+ gatewayAddress: `0x${string}`;
859
+ /** Axelar Gas Service contract address for prepaying gas */
860
+ gasServiceAddress: `0x${string}`;
861
+ /** RPC URL for source chain */
862
+ rpcUrl: string;
863
+ /** Private key for signing dispatch transactions */
864
+ privateKey: `0x${string}`;
865
+ /** Source chain ID */
866
+ chainId?: number;
867
+ /** MirrorRegistry address on destination chain */
868
+ mirrorRegistryAddress: `0x${string}`;
869
+ }
870
+ interface DispatchResult {
871
+ transactionHash: `0x${string}`;
872
+ messageId: string;
873
+ route: "axelar";
874
+ destinationChain: string;
875
+ gasEstimate: bigint;
876
+ }
877
+ /**
878
+ * AxelarTransport — Dispatches cross-chain GMP messages via Axelar Network.
879
+ *
880
+ * Flow:
881
+ * 1. Encode mirror payload (subscriber, tokenId, planId, tier, expiration, timestamp)
882
+ * 2. Estimate gas via Axelar Gas Service
883
+ * 3. Pre-pay gas via payNativeGasForContractCall
884
+ * 4. Call gateway.callContract() to dispatch to destination MirrorRegistry
885
+ */
886
+ declare class AxelarTransport {
887
+ private readonly config;
888
+ private readonly chainId;
889
+ private readonly publicClient;
890
+ private readonly walletClient;
891
+ private readonly signerAddress;
892
+ constructor(config: AxelarTransportConfig);
893
+ /**
894
+ * Dispatch a cross-chain mirror update via Axelar GMP
895
+ */
896
+ dispatch(destinationChainId: number, payload: `0x${string}`, refundAddress: `0x${string}`, destinationAddressOverride?: `0x${string}`): Promise<DispatchResult>;
897
+ /**
898
+ * Get the estimated gas cost for a dispatch
899
+ */
900
+ estimateGas(destinationChainId: number, payload: `0x${string}`, destinationAddressOverride?: `0x${string}`): Promise<bigint>;
901
+ /**
902
+ * Get signer's gas balance (for alert monitoring)
903
+ */
904
+ getSignerBalance(): Promise<bigint>;
905
+ }
906
+
907
+ interface CCIPTransportConfig {
908
+ /** CCIP Router contract address on source chain */
909
+ routerAddress: `0x${string}`;
910
+ /** RPC URL for source chain */
911
+ rpcUrl: string;
912
+ /** Private key for signing dispatch transactions */
913
+ privateKey: `0x${string}`;
914
+ /** Source chain ID */
915
+ chainId?: number;
916
+ /** MirrorRegistry address on destination chain */
917
+ mirrorRegistryAddress: `0x${string}`;
918
+ /** LINK token address for fee payment (if using LINK) */
919
+ linkTokenAddress?: `0x${string}`;
920
+ /** Gas limit for destination execution */
921
+ gasLimit?: bigint;
922
+ }
923
+ interface CCIPDispatchResult {
924
+ transactionHash: `0x${string}`;
925
+ messageId: string;
926
+ route: "ccip";
927
+ destinationChainSelector: bigint;
928
+ fee: bigint;
929
+ }
930
+ /**
931
+ * CCIPTransport — Fallback cross-chain transport via Chainlink CCIP.
932
+ *
933
+ * Used when Axelar dispatch fails. Sends CCIP messages with:
934
+ * - EVM2AnyMessage encoding
935
+ * - Native gas fee payment (ETH)
936
+ * - Configurable destination gas limit
937
+ */
938
+ declare class CCIPTransport {
939
+ private readonly config;
940
+ private readonly chainId;
941
+ private readonly publicClient;
942
+ private readonly walletClient;
943
+ private readonly signerAddress;
944
+ private readonly gasLimit;
945
+ constructor(config: CCIPTransportConfig);
946
+ /**
947
+ * Dispatch a cross-chain mirror update via Chainlink CCIP
948
+ */
949
+ dispatch(destinationChainId: number, payload: `0x${string}`, refundAddress: `0x${string}`, destinationAddressOverride?: `0x${string}`): Promise<CCIPDispatchResult>;
950
+ /**
951
+ * Build a CCIP EVM2AnyMessage
952
+ */
953
+ private buildMessage;
954
+ /**
955
+ * Estimate the CCIP fee for a dispatch
956
+ */
957
+ estimateFee(destinationChainId: number, payload: `0x${string}`, destinationAddressOverride?: `0x${string}`): Promise<bigint>;
958
+ /**
959
+ * Get signer's gas balance (for alert monitoring)
960
+ */
961
+ getSignerBalance(): Promise<bigint>;
962
+ }
963
+
964
+ interface BillingKeeperConfig {
965
+ /** RPC URL for the chain */
966
+ rpcUrl: string;
967
+ /** Chain ID */
968
+ chainId?: number;
969
+ /** Private key for the keeper signer (pays gas for execute calls) */
970
+ privateKey: `0x${string}`;
971
+ /** Polling interval in ms (default: 60000 = 1 minute) */
972
+ intervalMs?: number;
973
+ /** Max subscriptions to process per batch (default: 20) */
974
+ batchSize?: number;
975
+ /** How many seconds before expiry to trigger renewal (default: 86400 = 1 day) */
976
+ renewalBufferSeconds?: number;
977
+ }
978
+ interface RenewalResult {
979
+ tokenId: bigint;
980
+ subscriber: string;
981
+ txHash: `0x${string}`;
982
+ success: boolean;
983
+ error?: string;
984
+ }
985
+ /**
986
+ * BillingKeeper — Automated subscription renewal service.
987
+ *
988
+ * Polls SubscriptionRegistry for subscriptions nearing expiry,
989
+ * then calls ERC7579AutopayModule.execute() to pull the renewal
990
+ * payment from the subscriber's smart account.
991
+ *
992
+ * Architecture:
993
+ * 1. Poll registry for subscriptions expiring within `renewalBufferSeconds`
994
+ * 2. For each, check that the subscriber has an installed AutopayModule
995
+ * 3. Call execute() on the module to trigger the payment pull
996
+ * 4. Log results and emit renewal events
997
+ *
998
+ * PRD MEAP-004 — Automated Renewal Execution
999
+ */
1000
+ declare class BillingKeeper {
1001
+ private readonly chainId;
1002
+ private readonly publicClient;
1003
+ private readonly walletClient;
1004
+ private readonly signerAddress;
1005
+ private readonly intervalMs;
1006
+ private readonly batchSize;
1007
+ private readonly renewalBufferSeconds;
1008
+ private readonly registryAddress;
1009
+ private readonly autopayModuleAddress;
1010
+ private pollTimer;
1011
+ private processing;
1012
+ private renewalLog;
1013
+ private callbacks;
1014
+ constructor(config: BillingKeeperConfig);
1015
+ /**
1016
+ * Register a callback for renewal events
1017
+ */
1018
+ onRenewal(callback: (result: RenewalResult) => void): () => void;
1019
+ /**
1020
+ * Start the keeper polling loop
1021
+ */
1022
+ start(): void;
1023
+ /**
1024
+ * Stop the keeper
1025
+ */
1026
+ stop(): void;
1027
+ /**
1028
+ * Get renewal history
1029
+ */
1030
+ getRenewalLog(limit?: number): RenewalResult[];
1031
+ /**
1032
+ * Get keeper stats
1033
+ */
1034
+ getStats(): {
1035
+ running: boolean;
1036
+ totalRenewals: number;
1037
+ successfulRenewals: number;
1038
+ failedRenewals: number;
1039
+ };
1040
+ /**
1041
+ * Poll for subscriptions needing renewal
1042
+ */
1043
+ private poll;
1044
+ /**
1045
+ * Find subscriptions expiring between now and the renewal deadline
1046
+ */
1047
+ private findExpiringSubscriptions;
1048
+ /**
1049
+ * Process a single subscription renewal
1050
+ */
1051
+ private processRenewal;
1052
+ /**
1053
+ * Check if AutopayModule is installed for a subscriber
1054
+ */
1055
+ private checkAutopayInstalled;
1056
+ /**
1057
+ * Log a renewal result
1058
+ */
1059
+ private logRenewal;
1060
+ }
1061
+
1062
+ declare const PROTOCOL_EVENT_SCHEMA_VERSION = "2026-03-09.1";
1063
+ type ProtocolEventName = "subscription.minted" | "subscription.renewed" | "subscription.renewal_failed" | "subscription.cancelled" | "proof.generated" | "proof.submitted" | "proof.settled" | "invoice.issued" | "invoice.delivery_failed" | "invoice.paid";
1064
+ type ProtocolEventSource = "facilitator" | "dashboard" | "contracts" | "ops";
1065
+ interface ProtocolEvent<TPayload = Record<string, unknown>> {
1066
+ schemaVersion: string;
1067
+ name: ProtocolEventName;
1068
+ source: ProtocolEventSource;
1069
+ occurredAt: string;
1070
+ idempotencyKey: string;
1071
+ chainId?: number;
1072
+ payload: TPayload;
1073
+ }
1074
+ declare function buildEventIdempotencyKey(name: ProtocolEventName, uniqueParts: Array<string | number | bigint>): string;
1075
+ declare function createProtocolEvent<TPayload>(name: ProtocolEventName, payload: TPayload, options: {
1076
+ source: ProtocolEventSource;
1077
+ idempotencyKey: string;
1078
+ chainId?: number;
1079
+ occurredAt?: string | number | Date;
1080
+ }): ProtocolEvent<TPayload>;
1081
+ declare function mapProtocolEventToBillingType(name: ProtocolEventName): "SUBSCRIPTION_MINTED" | "SUBSCRIPTION_RENEWED" | "SUBSCRIPTION_CANCELLED" | "PAYMENT_FAILED";
1082
+
1083
+ export { type ActivateSubscriptionInput, type ActivateSubscriptionResult, AggregatorService, ArcenApiError, ArcenClient, type ArcenClientConfig, AxelarTransport, type AxelarTransportConfig, BillingKeeper, type BillingKeeperConfig, type CCIPDispatchResult, CCIPTransport, type CCIPTransportConfig, type DispatchResult, type EmailConfig, EmailService, type EmailTemplate, EventListenerService, type IdentifyInput, InMemoryNonceStore, type LitAccessCondition, type LitConfig, LitProtocolService, type NonceStore, PROTOCOL_EVENT_SCHEMA_VERSION, type PersistedUsageEntry, type ProofMode, ProofOrchestrator, type ProofOrchestratorConfig, type ProtocolEvent, type ProtocolEventName, type ProtocolEventSource, type RedisLike, RedisNonceStore, RedisUsageStore, type RedisUsageStoreConfig, type SettleForSignerInput, SettlementService, type SettlementWriteResult, type SettlementWriterConfig, SettlementWriterService, type SubscriptionEventData, type SubscriptionEventType, type TrackEventInput, UsageProofError, type UsageProofErrorCode, UsageProofErrorCodes, UsageService, type UsageServiceConfig, type WebhookConfig, type WebhookDeliveryLog, type WebhookPayload, WebhookService, type X402MiddlewareOptions, buildEventIdempotencyKey, createProtocolEvent, mapProtocolEventToBillingType, verifyWebhookSignature, x402Middleware };