x402z-facilitator 0.1.0 → 0.1.11

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,768 @@
1
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
+ }) : x)(function(x) {
4
+ if (typeof require !== "undefined") return require.apply(this, arguments);
5
+ throw Error('Dynamic require of "' + x + '" is not supported');
6
+ });
7
+
8
+ // src/service/bootstrap.ts
9
+ import { x402Facilitator } from "@x402/core/facilitator";
10
+ import { toFacilitatorEvmSigner } from "@x402/evm";
11
+ import { registerExactEvmScheme } from "@x402/evm/exact/facilitator";
12
+ import { createWalletClient, http, publicActions } from "viem";
13
+ import { privateKeyToAccount } from "viem/accounts";
14
+
15
+ // src/service/service.ts
16
+ import { createServer } from "http";
17
+ async function readJson(req) {
18
+ const chunks = [];
19
+ for await (const chunk of req) {
20
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
21
+ }
22
+ const body = Buffer.concat(chunks).toString("utf8");
23
+ return JSON.parse(body);
24
+ }
25
+ function sendJson(res, status, payload) {
26
+ res.writeHead(status, { "content-type": "application/json" });
27
+ res.end(JSON.stringify(payload));
28
+ }
29
+ function createFacilitatorService(config) {
30
+ const server = createServer(async (req, res) => {
31
+ try {
32
+ const method = req.method ?? "GET";
33
+ const url = req.url ?? "/";
34
+ if (process.env.X402Z_DEBUG === "1") {
35
+ console.debug(`[x402z-facilitator] ${method} ${url}`);
36
+ }
37
+ if (!req.url) {
38
+ sendJson(res, 404, { error: "not_found" });
39
+ return;
40
+ }
41
+ if (req.method === "GET" && req.url === "/supported") {
42
+ sendJson(res, 200, config.facilitator.getSupported());
43
+ return;
44
+ }
45
+ if (req.method === "POST" && req.url === "/verify") {
46
+ const body = await readJson(req);
47
+ const result = await config.facilitator.verify(body.paymentPayload, body.paymentRequirements);
48
+ sendJson(res, 200, result);
49
+ return;
50
+ }
51
+ if (req.method === "POST" && req.url === "/settle") {
52
+ const body = await readJson(req);
53
+ const result = await config.facilitator.settle(body.paymentPayload, body.paymentRequirements);
54
+ sendJson(res, 200, result);
55
+ return;
56
+ }
57
+ sendJson(res, 404, { error: "not_found" });
58
+ } catch (error) {
59
+ const message = error instanceof Error ? error.message : "internal_error";
60
+ sendJson(res, 500, { error: message });
61
+ }
62
+ });
63
+ return server;
64
+ }
65
+ function startFacilitatorService(config) {
66
+ const port = config.port ?? 8040;
67
+ const server = createFacilitatorService(config);
68
+ server.listen(port);
69
+ return { port };
70
+ }
71
+
72
+ // src/scheme/scheme.ts
73
+ import { decodeEventLog, getAddress, isAddressEqual } from "viem";
74
+ import {
75
+ confidentialPaymentTypes,
76
+ confidentialTokenAbi,
77
+ hashEncryptedAmountInput
78
+ } from "x402z-shared";
79
+ var batcherAbi = [
80
+ {
81
+ inputs: [
82
+ { internalType: "address", name: "token", type: "address" },
83
+ {
84
+ components: [
85
+ {
86
+ components: [
87
+ { internalType: "address", name: "holder", type: "address" },
88
+ { internalType: "address", name: "payee", type: "address" },
89
+ { internalType: "uint256", name: "maxClearAmount", type: "uint256" },
90
+ { internalType: "bytes32", name: "resourceHash", type: "bytes32" },
91
+ { internalType: "uint48", name: "validAfter", type: "uint48" },
92
+ { internalType: "uint48", name: "validBefore", type: "uint48" },
93
+ { internalType: "bytes32", name: "nonce", type: "bytes32" },
94
+ { internalType: "bytes32", name: "encryptedAmountHash", type: "bytes32" }
95
+ ],
96
+ internalType: "struct IFHEToken.ConfidentialPayment",
97
+ name: "p",
98
+ type: "tuple"
99
+ },
100
+ { internalType: "externalEuint64", name: "encryptedAmountInput", type: "bytes32" },
101
+ { internalType: "bytes", name: "inputProof", type: "bytes" },
102
+ { internalType: "bytes", name: "sig", type: "bytes" }
103
+ ],
104
+ internalType: "struct FHETokenBatcher.Request[]",
105
+ name: "requests",
106
+ type: "tuple[]"
107
+ }
108
+ ],
109
+ name: "batchConfidentialTransferWithAuthorization",
110
+ outputs: [
111
+ { internalType: "bool[]", name: "successes", type: "bool[]" },
112
+ { internalType: "bytes32[]", name: "transferredHandles", type: "bytes32[]" }
113
+ ],
114
+ stateMutability: "nonpayable",
115
+ type: "function"
116
+ },
117
+ {
118
+ anonymous: false,
119
+ inputs: [
120
+ { indexed: true, internalType: "uint256", name: "index", type: "uint256" },
121
+ { indexed: false, internalType: "bytes32", name: "transferredHandle", type: "bytes32" }
122
+ ],
123
+ name: "BatchItemSuccess",
124
+ type: "event"
125
+ },
126
+ {
127
+ anonymous: false,
128
+ inputs: [
129
+ { indexed: true, internalType: "uint256", name: "index", type: "uint256" },
130
+ { indexed: false, internalType: "bytes", name: "reason", type: "bytes" }
131
+ ],
132
+ name: "BatchItemFailure",
133
+ type: "event"
134
+ }
135
+ ];
136
+ var X402zEvmFacilitator = class {
137
+ constructor(config) {
138
+ this.config = config;
139
+ this.scheme = "erc7984-mind-v1";
140
+ this.caipFamily = "eip155:*";
141
+ this.queue = [];
142
+ this.hashFn = config.hashEncryptedAmountInput ?? hashEncryptedAmountInput;
143
+ this.clock = config.clock ?? (() => Math.floor(Date.now() / 1e3));
144
+ this.checkUsedNonces = config.checkUsedNonces ?? true;
145
+ this.waitForReceipt = config.waitForReceipt ?? true;
146
+ this.batchIntervalMs = Math.max(0, config.batchIntervalMs ?? 15e3);
147
+ this.receiptOptions = config.receipt;
148
+ this.batcherAddress = getAddress(config.batcherAddress);
149
+ }
150
+ getExtra(_) {
151
+ return void 0;
152
+ }
153
+ getSigners(_) {
154
+ return [...this.config.signer.getAddresses()];
155
+ }
156
+ async verify(payload, requirements) {
157
+ const confidentialPayload = payload.payload;
158
+ if (payload.accepted.scheme !== this.scheme || requirements.scheme !== this.scheme) {
159
+ return {
160
+ isValid: false,
161
+ invalidReason: "unsupported_scheme",
162
+ payer: confidentialPayload?.authorization?.holder
163
+ };
164
+ }
165
+ if (payload.accepted.network !== requirements.network) {
166
+ return {
167
+ isValid: false,
168
+ invalidReason: "network_mismatch",
169
+ payer: confidentialPayload.authorization.holder
170
+ };
171
+ }
172
+ const extra = requirements.extra;
173
+ const eip712 = extra?.eip712;
174
+ if (!eip712?.name || !eip712?.version) {
175
+ return {
176
+ isValid: false,
177
+ invalidReason: "missing_eip712_domain",
178
+ payer: confidentialPayload.authorization.holder
179
+ };
180
+ }
181
+ const now = this.clock();
182
+ const validAfter = Number(confidentialPayload.authorization.validAfter);
183
+ const validBefore = Number(confidentialPayload.authorization.validBefore);
184
+ if (Number.isNaN(validAfter) || Number.isNaN(validBefore)) {
185
+ return {
186
+ isValid: false,
187
+ invalidReason: "invalid_validity_window",
188
+ payer: confidentialPayload.authorization.holder
189
+ };
190
+ }
191
+ if (now < validAfter || now > validBefore) {
192
+ return {
193
+ isValid: false,
194
+ invalidReason: "authorization_expired",
195
+ payer: confidentialPayload.authorization.holder
196
+ };
197
+ }
198
+ if (!isAddressEqual(getAddress(confidentialPayload.authorization.payee), getAddress(requirements.payTo))) {
199
+ return {
200
+ isValid: false,
201
+ invalidReason: "recipient_mismatch",
202
+ payer: confidentialPayload.authorization.holder
203
+ };
204
+ }
205
+ const computedHash = this.hashFn(confidentialPayload.encryptedAmountInput);
206
+ if (computedHash !== confidentialPayload.authorization.encryptedAmountHash) {
207
+ return {
208
+ isValid: false,
209
+ invalidReason: "encrypted_amount_mismatch",
210
+ payer: confidentialPayload.authorization.holder
211
+ };
212
+ }
213
+ const chainId = parseInt(requirements.network.split(":")[1]);
214
+ const isValidSignature = await this.config.signer.verifyTypedData({
215
+ address: confidentialPayload.authorization.holder,
216
+ domain: {
217
+ name: eip712.name,
218
+ version: eip712.version,
219
+ chainId,
220
+ verifyingContract: getAddress(requirements.asset)
221
+ },
222
+ types: confidentialPaymentTypes,
223
+ primaryType: "ConfidentialPayment",
224
+ message: {
225
+ holder: getAddress(confidentialPayload.authorization.holder),
226
+ payee: getAddress(confidentialPayload.authorization.payee),
227
+ maxClearAmount: BigInt(confidentialPayload.authorization.maxClearAmount),
228
+ resourceHash: confidentialPayload.authorization.resourceHash,
229
+ validAfter: BigInt(confidentialPayload.authorization.validAfter),
230
+ validBefore: BigInt(confidentialPayload.authorization.validBefore),
231
+ nonce: confidentialPayload.authorization.nonce,
232
+ encryptedAmountHash: confidentialPayload.authorization.encryptedAmountHash
233
+ },
234
+ signature: confidentialPayload.signature
235
+ });
236
+ if (!isValidSignature) {
237
+ return {
238
+ isValid: false,
239
+ invalidReason: "invalid_signature",
240
+ payer: confidentialPayload.authorization.holder
241
+ };
242
+ }
243
+ if (this.checkUsedNonces) {
244
+ const used = await this.config.signer.readContract({
245
+ address: getAddress(requirements.asset),
246
+ abi: confidentialTokenAbi,
247
+ functionName: "usedNonces",
248
+ args: [confidentialPayload.authorization.holder, confidentialPayload.authorization.nonce]
249
+ });
250
+ if (used) {
251
+ return {
252
+ isValid: false,
253
+ invalidReason: "nonce_already_used",
254
+ payer: confidentialPayload.authorization.holder
255
+ };
256
+ }
257
+ }
258
+ try {
259
+ const txRequest = {
260
+ address: this.batcherAddress,
261
+ abi: batcherAbi,
262
+ functionName: "batchConfidentialTransferWithAuthorization",
263
+ args: [
264
+ getAddress(requirements.asset),
265
+ [
266
+ {
267
+ p: confidentialPayload.authorization,
268
+ encryptedAmountInput: confidentialPayload.encryptedAmountInput,
269
+ inputProof: confidentialPayload.inputProof,
270
+ sig: confidentialPayload.signature
271
+ }
272
+ ]
273
+ ]
274
+ };
275
+ const [successes] = await this.config.signer.simulateContract(txRequest);
276
+ if (!successes[0]) {
277
+ return {
278
+ isValid: false,
279
+ invalidReason: "preflight_failed",
280
+ payer: confidentialPayload.authorization.holder
281
+ };
282
+ }
283
+ } catch {
284
+ return {
285
+ isValid: false,
286
+ invalidReason: "preflight_failed",
287
+ payer: confidentialPayload.authorization.holder
288
+ };
289
+ }
290
+ return {
291
+ isValid: true,
292
+ payer: confidentialPayload.authorization.holder
293
+ };
294
+ }
295
+ async settle(payload, requirements) {
296
+ const valid = await this.verify(payload, requirements);
297
+ const confidentialPayload = payload.payload;
298
+ if (!valid.isValid) {
299
+ return {
300
+ success: false,
301
+ errorReason: valid.invalidReason ?? "invalid_payment",
302
+ payer: confidentialPayload.authorization.holder,
303
+ transaction: "",
304
+ network: requirements.network
305
+ };
306
+ }
307
+ return new Promise((resolve) => {
308
+ this.queue.push({ payload, requirements, resolve });
309
+ this.scheduleFlush();
310
+ });
311
+ }
312
+ scheduleFlush() {
313
+ if (this.flushTimer) {
314
+ return;
315
+ }
316
+ const delay = this.batchIntervalMs;
317
+ this.flushTimer = setTimeout(() => {
318
+ this.flushTimer = void 0;
319
+ void this.flushQueue();
320
+ }, delay);
321
+ }
322
+ async flushQueue() {
323
+ if (this.queue.length === 0) {
324
+ return;
325
+ }
326
+ const queued = this.queue;
327
+ this.queue = [];
328
+ const byToken = /* @__PURE__ */ new Map();
329
+ for (const entry of queued) {
330
+ const tokenKey = getAddress(entry.requirements.asset);
331
+ const group = byToken.get(tokenKey) ?? [];
332
+ group.push(entry);
333
+ byToken.set(tokenKey, group);
334
+ }
335
+ for (const [tokenAddress, entries] of byToken.entries()) {
336
+ await this.flushBatch(tokenAddress, entries);
337
+ }
338
+ if (this.queue.length > 0) {
339
+ this.scheduleFlush();
340
+ }
341
+ }
342
+ async flushBatch(tokenAddress, entries) {
343
+ const buildRequests = (batchEntries2) => batchEntries2.map(({ entry }) => {
344
+ const confidentialPayload = entry.payload.payload;
345
+ return {
346
+ p: confidentialPayload.authorization,
347
+ encryptedAmountInput: confidentialPayload.encryptedAmountInput,
348
+ inputProof: confidentialPayload.inputProof,
349
+ sig: confidentialPayload.signature
350
+ };
351
+ });
352
+ const originalEntries = entries.map((entry, index) => ({ entry, index }));
353
+ let batchEntries = originalEntries;
354
+ let requests = buildRequests(batchEntries);
355
+ let txRequest = {
356
+ address: this.batcherAddress,
357
+ abi: batcherAbi,
358
+ functionName: "batchConfidentialTransferWithAuthorization",
359
+ args: [tokenAddress, requests]
360
+ };
361
+ if (process.env.X402Z_DEBUG === "1") {
362
+ console.debug("[x402z-facilitator] settle tx", {
363
+ batcherAddress: this.batcherAddress,
364
+ tokenAddress,
365
+ functionName: txRequest.functionName,
366
+ to: txRequest.address,
367
+ size: requests.length
368
+ });
369
+ }
370
+ try {
371
+ const [successes, transferredHandles] = await this.config.signer.simulateContract(txRequest);
372
+ if (successes.length === batchEntries.length && transferredHandles.length === batchEntries.length) {
373
+ const filtered = [];
374
+ batchEntries.forEach((item, index) => {
375
+ if (successes[index]) {
376
+ filtered.push(item);
377
+ return;
378
+ }
379
+ const confidentialPayload = item.entry.payload.payload;
380
+ item.entry.resolve({
381
+ success: false,
382
+ errorReason: "preflight_failed",
383
+ payer: confidentialPayload.authorization.holder,
384
+ transaction: "",
385
+ network: item.entry.requirements.network,
386
+ batch: { index: item.index, success: false, transferredHandle: transferredHandles[index] }
387
+ });
388
+ });
389
+ if (filtered.length === 0) {
390
+ return;
391
+ }
392
+ batchEntries = filtered;
393
+ requests = buildRequests(batchEntries);
394
+ txRequest = {
395
+ ...txRequest,
396
+ args: [tokenAddress, requests]
397
+ };
398
+ }
399
+ } catch (error) {
400
+ if (process.env.X402Z_DEBUG === "1") {
401
+ console.error("[x402z-facilitator] settle tx error", error);
402
+ }
403
+ for (const entry of entries) {
404
+ const confidentialPayload = entry.payload.payload;
405
+ entry.resolve({
406
+ success: false,
407
+ errorReason: "preflight_failed",
408
+ payer: confidentialPayload.authorization.holder,
409
+ transaction: "",
410
+ network: entry.requirements.network
411
+ });
412
+ }
413
+ return;
414
+ }
415
+ const settleEntries = batchEntries.map((item) => item.entry);
416
+ let txHash;
417
+ try {
418
+ txHash = await this.config.signer.writeContract(txRequest);
419
+ } catch (error) {
420
+ for (const entry of settleEntries) {
421
+ const confidentialPayload = entry.payload.payload;
422
+ entry.resolve({
423
+ success: false,
424
+ errorReason: "settlement_failed",
425
+ payer: confidentialPayload.authorization.holder,
426
+ transaction: "",
427
+ network: entry.requirements.network
428
+ });
429
+ }
430
+ return;
431
+ }
432
+ if (process.env.X402Z_DEBUG === "1") {
433
+ console.debug("[x402z-facilitator] tx submitted", txHash);
434
+ }
435
+ let receipt;
436
+ if (this.waitForReceipt) {
437
+ receipt = await this.config.signer.waitForTransactionReceipt({ hash: txHash });
438
+ if (process.env.X402Z_DEBUG === "1") {
439
+ console.debug("[x402z-facilitator] tx receipt", receipt);
440
+ }
441
+ if (receipt.status !== "success") {
442
+ for (const entry of settleEntries) {
443
+ const confidentialPayload = entry.payload.payload;
444
+ entry.resolve({
445
+ success: false,
446
+ errorReason: "settlement_failed",
447
+ payer: confidentialPayload.authorization.holder,
448
+ transaction: txHash,
449
+ network: entry.requirements.network
450
+ });
451
+ }
452
+ return;
453
+ }
454
+ }
455
+ if (!this.waitForReceipt) {
456
+ settleEntries.forEach((entry) => {
457
+ const confidentialPayload = entry.payload.payload;
458
+ entry.resolve({
459
+ success: true,
460
+ payer: confidentialPayload.authorization.holder,
461
+ transaction: txHash,
462
+ network: entry.requirements.network
463
+ });
464
+ });
465
+ return;
466
+ }
467
+ const batchResults = /* @__PURE__ */ new Map();
468
+ if (receipt?.logs) {
469
+ for (const log of receipt.logs) {
470
+ if (!log?.address) {
471
+ continue;
472
+ }
473
+ if (!isAddressEqual(getAddress(log.address), this.batcherAddress)) {
474
+ continue;
475
+ }
476
+ const decoded = decodeEventLog({
477
+ abi: batcherAbi,
478
+ data: log.data,
479
+ topics: log.topics
480
+ });
481
+ if (process.env.X402Z_DEBUG === "1") {
482
+ console.debug("[x402z-facilitator] batch log", decoded);
483
+ }
484
+ if (decoded.eventName === "BatchItemSuccess") {
485
+ const args = decoded.args;
486
+ batchResults.set(Number(args.index), {
487
+ success: true,
488
+ transferredHandle: args.transferredHandle
489
+ });
490
+ } else if (decoded.eventName === "BatchItemFailure") {
491
+ const args = decoded.args;
492
+ batchResults.set(Number(args.index), {
493
+ success: false,
494
+ failureReason: args.reason
495
+ });
496
+ }
497
+ }
498
+ }
499
+ settleEntries.forEach((entry, index) => {
500
+ const confidentialPayload = entry.payload.payload;
501
+ const batchResult = batchResults.get(index);
502
+ if (!batchResult || !batchResult.success) {
503
+ entry.resolve({
504
+ success: false,
505
+ errorReason: "settlement_failed",
506
+ payer: confidentialPayload.authorization.holder,
507
+ transaction: txHash,
508
+ network: entry.requirements.network,
509
+ ...batchResult ? { batch: { index, ...batchResult } } : {}
510
+ });
511
+ return;
512
+ }
513
+ entry.resolve({
514
+ success: true,
515
+ payer: confidentialPayload.authorization.holder,
516
+ transaction: txHash,
517
+ network: entry.requirements.network,
518
+ batch: { index, ...batchResult }
519
+ });
520
+ });
521
+ }
522
+ };
523
+
524
+ // src/service/bootstrap.ts
525
+ function requireEnv(key) {
526
+ const value = process.env[key];
527
+ if (!value) {
528
+ throw new Error(`Missing required env var: ${key}`);
529
+ }
530
+ return value;
531
+ }
532
+ function createFacilitatorFromEnv() {
533
+ const privateKey = requireEnv("FACILITATOR_EVM_PRIVATE_KEY");
534
+ const chainId = Number(process.env.FACILITATOR_EVM_CHAIN_ID ?? "11155111");
535
+ const rpcUrl = process.env.FACILITATOR_EVM_RPC_URL ?? process.env.RPC_URL;
536
+ if (!rpcUrl) {
537
+ throw new Error("Missing required env var: RPC_URL or FACILITATOR_EVM_RPC_URL");
538
+ }
539
+ const networks = (process.env.FACILITATOR_NETWORKS ?? "eip155:11155111").split(",").map((network) => network.trim()).filter(Boolean);
540
+ const waitForReceipt = (process.env.FACILITATOR_WAIT_FOR_RECEIPT ?? "true") === "true";
541
+ const batcherAddress = process.env.FACILITATOR_BATCHER_ADDRESS;
542
+ if (!batcherAddress) {
543
+ throw new Error("FACILITATOR_BATCHER_ADDRESS is required");
544
+ }
545
+ const confidentialAsset = process.env.FACILITATOR_CONFIDENTIAL_ASSET;
546
+ const confidentialEip712Name = process.env.FACILITATOR_CONFIDENTIAL_EIP712_NAME;
547
+ const confidentialEip712Version = process.env.FACILITATOR_CONFIDENTIAL_EIP712_VERSION;
548
+ const confidentialDecimals = process.env.FACILITATOR_CONFIDENTIAL_DECIMALS ? Number(process.env.FACILITATOR_CONFIDENTIAL_DECIMALS) : void 0;
549
+ const exactAsset = process.env.FACILITATOR_EXACT_ASSET;
550
+ const exactDecimals = process.env.FACILITATOR_EXACT_DECIMALS ? Number(process.env.FACILITATOR_EXACT_DECIMALS) : void 0;
551
+ const exactNetwork = process.env.FACILITATOR_EXACT_NETWORK;
552
+ const batchIntervalMs = process.env.FACILITATOR_BATCH_INTERVAL_MS ? Number(process.env.FACILITATOR_BATCH_INTERVAL_MS) : void 0;
553
+ const receiptTimeoutMs = process.env.FACILITATOR_RECEIPT_TIMEOUT_MS ? Number(process.env.FACILITATOR_RECEIPT_TIMEOUT_MS) : void 0;
554
+ const receiptConfirmations = process.env.FACILITATOR_RECEIPT_CONFIRMATIONS ? Number(process.env.FACILITATOR_RECEIPT_CONFIRMATIONS) : void 0;
555
+ const receiptPollingIntervalMs = process.env.FACILITATOR_RECEIPT_POLLING_INTERVAL_MS ? Number(process.env.FACILITATOR_RECEIPT_POLLING_INTERVAL_MS) : void 0;
556
+ const gasMultiplier = process.env.FACILITATOR_GAS_MULTIPLIER ? Number(process.env.FACILITATOR_GAS_MULTIPLIER) : void 0;
557
+ const feeMultiplier = process.env.FACILITATOR_FEE_MULTIPLIER ? Number(process.env.FACILITATOR_FEE_MULTIPLIER) : void 0;
558
+ const debugEnabled = process.env.X402Z_DEBUG === "1";
559
+ const account = privateKeyToAccount(privateKey);
560
+ if (debugEnabled) {
561
+ console.debug("[x402z-facilitator] config", {
562
+ chainId,
563
+ networks,
564
+ rpcUrl,
565
+ waitForReceipt,
566
+ batcherAddress,
567
+ gasMultiplier,
568
+ feeMultiplier,
569
+ batchIntervalMs,
570
+ receipt: {
571
+ confirmations: receiptConfirmations,
572
+ timeoutMs: receiptTimeoutMs,
573
+ pollingIntervalMs: receiptPollingIntervalMs
574
+ },
575
+ address: account.address
576
+ });
577
+ }
578
+ const client = createWalletClient({
579
+ account,
580
+ chain: {
581
+ id: chainId,
582
+ name: "custom",
583
+ nativeCurrency: { name: "native", symbol: "NATIVE", decimals: 18 },
584
+ rpcUrls: { default: { http: [rpcUrl] } }
585
+ },
586
+ transport: http(rpcUrl)
587
+ }).extend(publicActions);
588
+ const baseSigner = toFacilitatorEvmSigner({
589
+ address: account.address,
590
+ readContract: (args) => client.readContract({
591
+ ...args,
592
+ args: args.args || []
593
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
594
+ }),
595
+ verifyTypedData: (args) => client.verifyTypedData(args),
596
+ writeContract: async (args) => {
597
+ let gas;
598
+ let maxFeePerGas;
599
+ let maxPriorityFeePerGas;
600
+ if (gasMultiplier && gasMultiplier > 0) {
601
+ try {
602
+ const estimated = await client.estimateContractGas({
603
+ ...args,
604
+ args: args.args || [],
605
+ account
606
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
607
+ });
608
+ const scale = BigInt(Math.round(gasMultiplier * 1e3));
609
+ const scaled = estimated * scale / 1000n;
610
+ gas = scaled > estimated ? scaled : estimated;
611
+ } catch (error) {
612
+ if (debugEnabled) {
613
+ console.debug("[x402z-facilitator] gas estimate failed", error);
614
+ }
615
+ }
616
+ }
617
+ if (feeMultiplier && feeMultiplier > 0) {
618
+ try {
619
+ const fees = await client.estimateFeesPerGas();
620
+ if (fees.maxFeePerGas) {
621
+ const scale = BigInt(Math.round(feeMultiplier * 1e3));
622
+ maxFeePerGas = fees.maxFeePerGas * scale / 1000n;
623
+ }
624
+ if (fees.maxPriorityFeePerGas) {
625
+ const scale = BigInt(Math.round(feeMultiplier * 1e3));
626
+ maxPriorityFeePerGas = fees.maxPriorityFeePerGas * scale / 1000n;
627
+ }
628
+ } catch (error) {
629
+ if (debugEnabled) {
630
+ console.debug("[x402z-facilitator] fee estimate failed", error);
631
+ }
632
+ }
633
+ }
634
+ return client.writeContract({
635
+ ...args,
636
+ args: args.args || [],
637
+ ...gas ? { gas } : {},
638
+ ...maxFeePerGas ? { maxFeePerGas } : {},
639
+ ...maxPriorityFeePerGas ? { maxPriorityFeePerGas } : {}
640
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
641
+ });
642
+ },
643
+ sendTransaction: async (args) => {
644
+ let maxFeePerGas;
645
+ let maxPriorityFeePerGas;
646
+ if (feeMultiplier && feeMultiplier > 0) {
647
+ try {
648
+ const fees = await client.estimateFeesPerGas();
649
+ if (fees.maxFeePerGas) {
650
+ const scale = BigInt(Math.round(feeMultiplier * 1e3));
651
+ maxFeePerGas = fees.maxFeePerGas * scale / 1000n;
652
+ }
653
+ if (fees.maxPriorityFeePerGas) {
654
+ const scale = BigInt(Math.round(feeMultiplier * 1e3));
655
+ maxPriorityFeePerGas = fees.maxPriorityFeePerGas * scale / 1000n;
656
+ }
657
+ } catch (error) {
658
+ if (debugEnabled) {
659
+ console.debug("[x402z-facilitator] fee estimate failed", error);
660
+ }
661
+ }
662
+ }
663
+ return client.sendTransaction({
664
+ to: args.to,
665
+ data: args.data,
666
+ ...maxFeePerGas ? { maxFeePerGas } : {},
667
+ ...maxPriorityFeePerGas ? { maxPriorityFeePerGas } : {}
668
+ });
669
+ },
670
+ waitForTransactionReceipt: (args) => client.waitForTransactionReceipt(args),
671
+ getCode: (args) => client.getCode(args)
672
+ });
673
+ const signer = {
674
+ ...baseSigner,
675
+ simulateContract: async (args) => {
676
+ const result = await client.simulateContract({
677
+ ...args,
678
+ args: args.args || [],
679
+ account
680
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
681
+ });
682
+ return result.result;
683
+ }
684
+ };
685
+ const facilitator = new x402Facilitator();
686
+ registerExactEvmScheme(facilitator, {
687
+ signer: baseSigner,
688
+ networks
689
+ });
690
+ for (const network of networks) {
691
+ facilitator.register(
692
+ network,
693
+ new X402zEvmFacilitator({
694
+ signer,
695
+ waitForReceipt,
696
+ batcherAddress,
697
+ batchIntervalMs,
698
+ receipt: {
699
+ confirmations: receiptConfirmations,
700
+ timeoutMs: receiptTimeoutMs,
701
+ pollingIntervalMs: receiptPollingIntervalMs
702
+ }
703
+ })
704
+ );
705
+ }
706
+ const supportedConfidential = confidentialAsset && confidentialEip712Name && confidentialEip712Version && batcherAddress ? {
707
+ asset: confidentialAsset,
708
+ eip712: {
709
+ name: confidentialEip712Name,
710
+ version: confidentialEip712Version
711
+ },
712
+ decimals: confidentialDecimals ?? 6,
713
+ batcherAddress
714
+ } : null;
715
+ const supportedExact = exactAsset ? {
716
+ asset: exactAsset,
717
+ decimals: exactDecimals ?? 6
718
+ } : null;
719
+ if (supportedConfidential || supportedExact) {
720
+ const originalGetSupported = facilitator.getSupported.bind(facilitator);
721
+ facilitator.getSupported = () => {
722
+ const supported = originalGetSupported();
723
+ return {
724
+ ...supported,
725
+ kinds: supported.kinds.map((kind) => {
726
+ if (kind.scheme !== "erc7984-mind-v1") {
727
+ if (!supportedExact || kind.scheme !== "exact") {
728
+ return kind;
729
+ }
730
+ if (exactNetwork && kind.network !== exactNetwork) {
731
+ return kind;
732
+ }
733
+ const extra2 = {
734
+ ...kind.extra,
735
+ exact: supportedExact
736
+ };
737
+ return { ...kind, extra: extra2 };
738
+ }
739
+ const extra = {
740
+ ...kind.extra,
741
+ confidential: supportedConfidential
742
+ };
743
+ return { ...kind, extra };
744
+ })
745
+ };
746
+ };
747
+ }
748
+ return facilitator;
749
+ }
750
+ function startFacilitator() {
751
+ const facilitator = createFacilitatorFromEnv();
752
+ const port = Number(process.env.FACILITATOR_PORT ?? "8040");
753
+ const server = createFacilitatorService({ facilitator, port });
754
+ server.listen(port);
755
+ return { port };
756
+ }
757
+ if (__require.main === module) {
758
+ const { port } = startFacilitator();
759
+ console.log(`Confidential facilitator listening on :${port}`);
760
+ }
761
+
762
+ export {
763
+ X402zEvmFacilitator,
764
+ createFacilitatorService,
765
+ startFacilitatorService,
766
+ createFacilitatorFromEnv,
767
+ startFacilitator
768
+ };
package/dist/index.js CHANGED
@@ -573,6 +573,9 @@ function createFacilitatorFromEnv() {
573
573
  const confidentialEip712Name = process.env.FACILITATOR_CONFIDENTIAL_EIP712_NAME;
574
574
  const confidentialEip712Version = process.env.FACILITATOR_CONFIDENTIAL_EIP712_VERSION;
575
575
  const confidentialDecimals = process.env.FACILITATOR_CONFIDENTIAL_DECIMALS ? Number(process.env.FACILITATOR_CONFIDENTIAL_DECIMALS) : void 0;
576
+ const exactAsset = process.env.FACILITATOR_EXACT_ASSET;
577
+ const exactDecimals = process.env.FACILITATOR_EXACT_DECIMALS ? Number(process.env.FACILITATOR_EXACT_DECIMALS) : void 0;
578
+ const exactNetwork = process.env.FACILITATOR_EXACT_NETWORK;
576
579
  const batchIntervalMs = process.env.FACILITATOR_BATCH_INTERVAL_MS ? Number(process.env.FACILITATOR_BATCH_INTERVAL_MS) : void 0;
577
580
  const receiptTimeoutMs = process.env.FACILITATOR_RECEIPT_TIMEOUT_MS ? Number(process.env.FACILITATOR_RECEIPT_TIMEOUT_MS) : void 0;
578
581
  const receiptConfirmations = process.env.FACILITATOR_RECEIPT_CONFIRMATIONS ? Number(process.env.FACILITATOR_RECEIPT_CONFIRMATIONS) : void 0;
@@ -736,7 +739,11 @@ function createFacilitatorFromEnv() {
736
739
  decimals: confidentialDecimals ?? 6,
737
740
  batcherAddress
738
741
  } : null;
739
- if (supportedConfidential) {
742
+ const supportedExact = exactAsset ? {
743
+ asset: exactAsset,
744
+ decimals: exactDecimals ?? 6
745
+ } : null;
746
+ if (supportedConfidential || supportedExact) {
740
747
  const originalGetSupported = facilitator.getSupported.bind(facilitator);
741
748
  facilitator.getSupported = () => {
742
749
  const supported = originalGetSupported();
@@ -744,7 +751,17 @@ function createFacilitatorFromEnv() {
744
751
  ...supported,
745
752
  kinds: supported.kinds.map((kind) => {
746
753
  if (kind.scheme !== "erc7984-mind-v1") {
747
- return kind;
754
+ if (!supportedExact || kind.scheme !== "exact") {
755
+ return kind;
756
+ }
757
+ if (exactNetwork && kind.network !== exactNetwork) {
758
+ return kind;
759
+ }
760
+ const extra2 = {
761
+ ...kind.extra,
762
+ exact: supportedExact
763
+ };
764
+ return { ...kind, extra: extra2 };
748
765
  }
749
766
  const extra = {
750
767
  ...kind.extra,
package/dist/index.mjs CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  createFacilitatorService,
4
4
  startFacilitator,
5
5
  startFacilitatorService
6
- } from "./chunk-PDAG647E.mjs";
6
+ } from "./chunk-2BNYP3FU.mjs";
7
7
 
8
8
  // src/scheme/register.ts
9
9
  function registerX402zEvmFacilitatorScheme(facilitator, config) {
@@ -554,6 +554,9 @@ function createFacilitatorFromEnv() {
554
554
  const confidentialEip712Name = process.env.FACILITATOR_CONFIDENTIAL_EIP712_NAME;
555
555
  const confidentialEip712Version = process.env.FACILITATOR_CONFIDENTIAL_EIP712_VERSION;
556
556
  const confidentialDecimals = process.env.FACILITATOR_CONFIDENTIAL_DECIMALS ? Number(process.env.FACILITATOR_CONFIDENTIAL_DECIMALS) : void 0;
557
+ const exactAsset = process.env.FACILITATOR_EXACT_ASSET;
558
+ const exactDecimals = process.env.FACILITATOR_EXACT_DECIMALS ? Number(process.env.FACILITATOR_EXACT_DECIMALS) : void 0;
559
+ const exactNetwork = process.env.FACILITATOR_EXACT_NETWORK;
557
560
  const batchIntervalMs = process.env.FACILITATOR_BATCH_INTERVAL_MS ? Number(process.env.FACILITATOR_BATCH_INTERVAL_MS) : void 0;
558
561
  const receiptTimeoutMs = process.env.FACILITATOR_RECEIPT_TIMEOUT_MS ? Number(process.env.FACILITATOR_RECEIPT_TIMEOUT_MS) : void 0;
559
562
  const receiptConfirmations = process.env.FACILITATOR_RECEIPT_CONFIRMATIONS ? Number(process.env.FACILITATOR_RECEIPT_CONFIRMATIONS) : void 0;
@@ -717,7 +720,11 @@ function createFacilitatorFromEnv() {
717
720
  decimals: confidentialDecimals ?? 6,
718
721
  batcherAddress
719
722
  } : null;
720
- if (supportedConfidential) {
723
+ const supportedExact = exactAsset ? {
724
+ asset: exactAsset,
725
+ decimals: exactDecimals ?? 6
726
+ } : null;
727
+ if (supportedConfidential || supportedExact) {
721
728
  const originalGetSupported = facilitator.getSupported.bind(facilitator);
722
729
  facilitator.getSupported = () => {
723
730
  const supported = originalGetSupported();
@@ -725,7 +732,17 @@ function createFacilitatorFromEnv() {
725
732
  ...supported,
726
733
  kinds: supported.kinds.map((kind) => {
727
734
  if (kind.scheme !== "erc7984-mind-v1") {
728
- return kind;
735
+ if (!supportedExact || kind.scheme !== "exact") {
736
+ return kind;
737
+ }
738
+ if (exactNetwork && kind.network !== exactNetwork) {
739
+ return kind;
740
+ }
741
+ const extra2 = {
742
+ ...kind.extra,
743
+ exact: supportedExact
744
+ };
745
+ return { ...kind, extra: extra2 };
729
746
  }
730
747
  const extra = {
731
748
  ...kind.extra,
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  createFacilitatorFromEnv,
3
3
  startFacilitator
4
- } from "../chunk-PDAG647E.mjs";
4
+ } from "../chunk-2BNYP3FU.mjs";
5
5
  export {
6
6
  createFacilitatorFromEnv,
7
7
  startFacilitator
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "x402z-facilitator",
3
- "version": "0.1.0",
3
+ "version": "0.1.11",
4
4
  "main": "./dist/index.js",
5
5
  "module": "./dist/index.mjs",
6
6
  "types": "./dist/index.d.ts",