@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.
- package/README.md +117 -0
- package/dist/chunk-SKFD6TSD.mjs +266 -0
- package/dist/index.d.mts +1083 -0
- package/dist/index.d.ts +1083 -0
- package/dist/index.js +3581 -0
- package/dist/index.mjs +3317 -0
- package/dist/tableland.d.mts +46 -0
- package/dist/tableland.d.ts +46 -0
- package/dist/tableland.js +268 -0
- package/dist/tableland.mjs +6 -0
- package/package.json +74 -0
package/dist/index.d.mts
ADDED
|
@@ -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 };
|