x402z-facilitator 0.0.12 → 0.1.0

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 CHANGED
@@ -19,7 +19,7 @@ pnpm add x402z-facilitator
19
19
  Set env vars:
20
20
 
21
21
  - `FACILITATOR_EVM_PRIVATE_KEY`
22
- - `FACILITATOR_EVM_RPC_URL`
22
+ - `RPC_URL` (preferred) or `FACILITATOR_EVM_RPC_URL`
23
23
  - `FACILITATOR_EVM_CHAIN_ID` (default 11155111)
24
24
  - `FACILITATOR_NETWORKS` (default `eip155:11155111`)
25
25
  - `FACILITATOR_PORT` (default 8040)
@@ -0,0 +1,751 @@
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 batchIntervalMs = process.env.FACILITATOR_BATCH_INTERVAL_MS ? Number(process.env.FACILITATOR_BATCH_INTERVAL_MS) : void 0;
550
+ const receiptTimeoutMs = process.env.FACILITATOR_RECEIPT_TIMEOUT_MS ? Number(process.env.FACILITATOR_RECEIPT_TIMEOUT_MS) : void 0;
551
+ const receiptConfirmations = process.env.FACILITATOR_RECEIPT_CONFIRMATIONS ? Number(process.env.FACILITATOR_RECEIPT_CONFIRMATIONS) : void 0;
552
+ const receiptPollingIntervalMs = process.env.FACILITATOR_RECEIPT_POLLING_INTERVAL_MS ? Number(process.env.FACILITATOR_RECEIPT_POLLING_INTERVAL_MS) : void 0;
553
+ const gasMultiplier = process.env.FACILITATOR_GAS_MULTIPLIER ? Number(process.env.FACILITATOR_GAS_MULTIPLIER) : void 0;
554
+ const feeMultiplier = process.env.FACILITATOR_FEE_MULTIPLIER ? Number(process.env.FACILITATOR_FEE_MULTIPLIER) : void 0;
555
+ const debugEnabled = process.env.X402Z_DEBUG === "1";
556
+ const account = privateKeyToAccount(privateKey);
557
+ if (debugEnabled) {
558
+ console.debug("[x402z-facilitator] config", {
559
+ chainId,
560
+ networks,
561
+ rpcUrl,
562
+ waitForReceipt,
563
+ batcherAddress,
564
+ gasMultiplier,
565
+ feeMultiplier,
566
+ batchIntervalMs,
567
+ receipt: {
568
+ confirmations: receiptConfirmations,
569
+ timeoutMs: receiptTimeoutMs,
570
+ pollingIntervalMs: receiptPollingIntervalMs
571
+ },
572
+ address: account.address
573
+ });
574
+ }
575
+ const client = createWalletClient({
576
+ account,
577
+ chain: {
578
+ id: chainId,
579
+ name: "custom",
580
+ nativeCurrency: { name: "native", symbol: "NATIVE", decimals: 18 },
581
+ rpcUrls: { default: { http: [rpcUrl] } }
582
+ },
583
+ transport: http(rpcUrl)
584
+ }).extend(publicActions);
585
+ const baseSigner = toFacilitatorEvmSigner({
586
+ address: account.address,
587
+ readContract: (args) => client.readContract({
588
+ ...args,
589
+ args: args.args || []
590
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
591
+ }),
592
+ verifyTypedData: (args) => client.verifyTypedData(args),
593
+ writeContract: async (args) => {
594
+ let gas;
595
+ let maxFeePerGas;
596
+ let maxPriorityFeePerGas;
597
+ if (gasMultiplier && gasMultiplier > 0) {
598
+ try {
599
+ const estimated = await client.estimateContractGas({
600
+ ...args,
601
+ args: args.args || [],
602
+ account
603
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
604
+ });
605
+ const scale = BigInt(Math.round(gasMultiplier * 1e3));
606
+ const scaled = estimated * scale / 1000n;
607
+ gas = scaled > estimated ? scaled : estimated;
608
+ } catch (error) {
609
+ if (debugEnabled) {
610
+ console.debug("[x402z-facilitator] gas estimate failed", error);
611
+ }
612
+ }
613
+ }
614
+ if (feeMultiplier && feeMultiplier > 0) {
615
+ try {
616
+ const fees = await client.estimateFeesPerGas();
617
+ if (fees.maxFeePerGas) {
618
+ const scale = BigInt(Math.round(feeMultiplier * 1e3));
619
+ maxFeePerGas = fees.maxFeePerGas * scale / 1000n;
620
+ }
621
+ if (fees.maxPriorityFeePerGas) {
622
+ const scale = BigInt(Math.round(feeMultiplier * 1e3));
623
+ maxPriorityFeePerGas = fees.maxPriorityFeePerGas * scale / 1000n;
624
+ }
625
+ } catch (error) {
626
+ if (debugEnabled) {
627
+ console.debug("[x402z-facilitator] fee estimate failed", error);
628
+ }
629
+ }
630
+ }
631
+ return client.writeContract({
632
+ ...args,
633
+ args: args.args || [],
634
+ ...gas ? { gas } : {},
635
+ ...maxFeePerGas ? { maxFeePerGas } : {},
636
+ ...maxPriorityFeePerGas ? { maxPriorityFeePerGas } : {}
637
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
638
+ });
639
+ },
640
+ sendTransaction: async (args) => {
641
+ let maxFeePerGas;
642
+ let maxPriorityFeePerGas;
643
+ if (feeMultiplier && feeMultiplier > 0) {
644
+ try {
645
+ const fees = await client.estimateFeesPerGas();
646
+ if (fees.maxFeePerGas) {
647
+ const scale = BigInt(Math.round(feeMultiplier * 1e3));
648
+ maxFeePerGas = fees.maxFeePerGas * scale / 1000n;
649
+ }
650
+ if (fees.maxPriorityFeePerGas) {
651
+ const scale = BigInt(Math.round(feeMultiplier * 1e3));
652
+ maxPriorityFeePerGas = fees.maxPriorityFeePerGas * scale / 1000n;
653
+ }
654
+ } catch (error) {
655
+ if (debugEnabled) {
656
+ console.debug("[x402z-facilitator] fee estimate failed", error);
657
+ }
658
+ }
659
+ }
660
+ return client.sendTransaction({
661
+ to: args.to,
662
+ data: args.data,
663
+ ...maxFeePerGas ? { maxFeePerGas } : {},
664
+ ...maxPriorityFeePerGas ? { maxPriorityFeePerGas } : {}
665
+ });
666
+ },
667
+ waitForTransactionReceipt: (args) => client.waitForTransactionReceipt(args),
668
+ getCode: (args) => client.getCode(args)
669
+ });
670
+ const signer = {
671
+ ...baseSigner,
672
+ simulateContract: async (args) => {
673
+ const result = await client.simulateContract({
674
+ ...args,
675
+ args: args.args || [],
676
+ account
677
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
678
+ });
679
+ return result.result;
680
+ }
681
+ };
682
+ const facilitator = new x402Facilitator();
683
+ registerExactEvmScheme(facilitator, {
684
+ signer: baseSigner,
685
+ networks
686
+ });
687
+ for (const network of networks) {
688
+ facilitator.register(
689
+ network,
690
+ new X402zEvmFacilitator({
691
+ signer,
692
+ waitForReceipt,
693
+ batcherAddress,
694
+ batchIntervalMs,
695
+ receipt: {
696
+ confirmations: receiptConfirmations,
697
+ timeoutMs: receiptTimeoutMs,
698
+ pollingIntervalMs: receiptPollingIntervalMs
699
+ }
700
+ })
701
+ );
702
+ }
703
+ const supportedConfidential = confidentialAsset && confidentialEip712Name && confidentialEip712Version && batcherAddress ? {
704
+ asset: confidentialAsset,
705
+ eip712: {
706
+ name: confidentialEip712Name,
707
+ version: confidentialEip712Version
708
+ },
709
+ decimals: confidentialDecimals ?? 6,
710
+ batcherAddress
711
+ } : null;
712
+ if (supportedConfidential) {
713
+ const originalGetSupported = facilitator.getSupported.bind(facilitator);
714
+ facilitator.getSupported = () => {
715
+ const supported = originalGetSupported();
716
+ return {
717
+ ...supported,
718
+ kinds: supported.kinds.map((kind) => {
719
+ if (kind.scheme !== "erc7984-mind-v1") {
720
+ return kind;
721
+ }
722
+ const extra = {
723
+ ...kind.extra,
724
+ confidential: supportedConfidential
725
+ };
726
+ return { ...kind, extra };
727
+ })
728
+ };
729
+ };
730
+ }
731
+ return facilitator;
732
+ }
733
+ function startFacilitator() {
734
+ const facilitator = createFacilitatorFromEnv();
735
+ const port = Number(process.env.FACILITATOR_PORT ?? "8040");
736
+ const server = createFacilitatorService({ facilitator, port });
737
+ server.listen(port);
738
+ return { port };
739
+ }
740
+ if (__require.main === module) {
741
+ const { port } = startFacilitator();
742
+ console.log(`Confidential facilitator listening on :${port}`);
743
+ }
744
+
745
+ export {
746
+ X402zEvmFacilitator,
747
+ createFacilitatorService,
748
+ startFacilitatorService,
749
+ createFacilitatorFromEnv,
750
+ startFacilitator
751
+ };
package/dist/index.js CHANGED
@@ -546,6 +546,7 @@ function startFacilitatorService(config) {
546
546
  // src/service/bootstrap.ts
547
547
  var import_facilitator = require("@x402/core/facilitator");
548
548
  var import_evm = require("@x402/evm");
549
+ var import_facilitator2 = require("@x402/evm/exact/facilitator");
549
550
  var import_viem2 = require("viem");
550
551
  var import_accounts = require("viem/accounts");
551
552
  function requireEnv(key) {
@@ -558,13 +559,20 @@ function requireEnv(key) {
558
559
  function createFacilitatorFromEnv() {
559
560
  const privateKey = requireEnv("FACILITATOR_EVM_PRIVATE_KEY");
560
561
  const chainId = Number(process.env.FACILITATOR_EVM_CHAIN_ID ?? "11155111");
561
- const rpcUrl = requireEnv("FACILITATOR_EVM_RPC_URL");
562
+ const rpcUrl = process.env.FACILITATOR_EVM_RPC_URL ?? process.env.RPC_URL;
563
+ if (!rpcUrl) {
564
+ throw new Error("Missing required env var: RPC_URL or FACILITATOR_EVM_RPC_URL");
565
+ }
562
566
  const networks = (process.env.FACILITATOR_NETWORKS ?? "eip155:11155111").split(",").map((network) => network.trim()).filter(Boolean);
563
567
  const waitForReceipt = (process.env.FACILITATOR_WAIT_FOR_RECEIPT ?? "true") === "true";
564
568
  const batcherAddress = process.env.FACILITATOR_BATCHER_ADDRESS;
565
569
  if (!batcherAddress) {
566
570
  throw new Error("FACILITATOR_BATCHER_ADDRESS is required");
567
571
  }
572
+ const confidentialAsset = process.env.FACILITATOR_CONFIDENTIAL_ASSET;
573
+ const confidentialEip712Name = process.env.FACILITATOR_CONFIDENTIAL_EIP712_NAME;
574
+ const confidentialEip712Version = process.env.FACILITATOR_CONFIDENTIAL_EIP712_VERSION;
575
+ const confidentialDecimals = process.env.FACILITATOR_CONFIDENTIAL_DECIMALS ? Number(process.env.FACILITATOR_CONFIDENTIAL_DECIMALS) : void 0;
568
576
  const batchIntervalMs = process.env.FACILITATOR_BATCH_INTERVAL_MS ? Number(process.env.FACILITATOR_BATCH_INTERVAL_MS) : void 0;
569
577
  const receiptTimeoutMs = process.env.FACILITATOR_RECEIPT_TIMEOUT_MS ? Number(process.env.FACILITATOR_RECEIPT_TIMEOUT_MS) : void 0;
570
578
  const receiptConfirmations = process.env.FACILITATOR_RECEIPT_CONFIRMATIONS ? Number(process.env.FACILITATOR_RECEIPT_CONFIRMATIONS) : void 0;
@@ -699,6 +707,10 @@ function createFacilitatorFromEnv() {
699
707
  }
700
708
  };
701
709
  const facilitator = new import_facilitator.x402Facilitator();
710
+ (0, import_facilitator2.registerExactEvmScheme)(facilitator, {
711
+ signer: baseSigner,
712
+ networks
713
+ });
702
714
  for (const network of networks) {
703
715
  facilitator.register(
704
716
  network,
@@ -715,6 +727,34 @@ function createFacilitatorFromEnv() {
715
727
  })
716
728
  );
717
729
  }
730
+ const supportedConfidential = confidentialAsset && confidentialEip712Name && confidentialEip712Version && batcherAddress ? {
731
+ asset: confidentialAsset,
732
+ eip712: {
733
+ name: confidentialEip712Name,
734
+ version: confidentialEip712Version
735
+ },
736
+ decimals: confidentialDecimals ?? 6,
737
+ batcherAddress
738
+ } : null;
739
+ if (supportedConfidential) {
740
+ const originalGetSupported = facilitator.getSupported.bind(facilitator);
741
+ facilitator.getSupported = () => {
742
+ const supported = originalGetSupported();
743
+ return {
744
+ ...supported,
745
+ kinds: supported.kinds.map((kind) => {
746
+ if (kind.scheme !== "erc7984-mind-v1") {
747
+ return kind;
748
+ }
749
+ const extra = {
750
+ ...kind.extra,
751
+ confidential: supportedConfidential
752
+ };
753
+ return { ...kind, extra };
754
+ })
755
+ };
756
+ };
757
+ }
718
758
  return facilitator;
719
759
  }
720
760
  function startFacilitator() {
package/dist/index.mjs CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  createFacilitatorService,
4
4
  startFacilitator,
5
5
  startFacilitatorService
6
- } from "./chunk-TDT6WJI7.mjs";
6
+ } from "./chunk-PDAG647E.mjs";
7
7
 
8
8
  // src/scheme/register.ts
9
9
  function registerX402zEvmFacilitatorScheme(facilitator, config) {
@@ -26,6 +26,7 @@ __export(bootstrap_exports, {
26
26
  module.exports = __toCommonJS(bootstrap_exports);
27
27
  var import_facilitator = require("@x402/core/facilitator");
28
28
  var import_evm = require("@x402/evm");
29
+ var import_facilitator2 = require("@x402/evm/exact/facilitator");
29
30
  var import_viem2 = require("viem");
30
31
  var import_accounts = require("viem/accounts");
31
32
 
@@ -539,13 +540,20 @@ function requireEnv(key) {
539
540
  function createFacilitatorFromEnv() {
540
541
  const privateKey = requireEnv("FACILITATOR_EVM_PRIVATE_KEY");
541
542
  const chainId = Number(process.env.FACILITATOR_EVM_CHAIN_ID ?? "11155111");
542
- const rpcUrl = requireEnv("FACILITATOR_EVM_RPC_URL");
543
+ const rpcUrl = process.env.FACILITATOR_EVM_RPC_URL ?? process.env.RPC_URL;
544
+ if (!rpcUrl) {
545
+ throw new Error("Missing required env var: RPC_URL or FACILITATOR_EVM_RPC_URL");
546
+ }
543
547
  const networks = (process.env.FACILITATOR_NETWORKS ?? "eip155:11155111").split(",").map((network) => network.trim()).filter(Boolean);
544
548
  const waitForReceipt = (process.env.FACILITATOR_WAIT_FOR_RECEIPT ?? "true") === "true";
545
549
  const batcherAddress = process.env.FACILITATOR_BATCHER_ADDRESS;
546
550
  if (!batcherAddress) {
547
551
  throw new Error("FACILITATOR_BATCHER_ADDRESS is required");
548
552
  }
553
+ const confidentialAsset = process.env.FACILITATOR_CONFIDENTIAL_ASSET;
554
+ const confidentialEip712Name = process.env.FACILITATOR_CONFIDENTIAL_EIP712_NAME;
555
+ const confidentialEip712Version = process.env.FACILITATOR_CONFIDENTIAL_EIP712_VERSION;
556
+ const confidentialDecimals = process.env.FACILITATOR_CONFIDENTIAL_DECIMALS ? Number(process.env.FACILITATOR_CONFIDENTIAL_DECIMALS) : void 0;
549
557
  const batchIntervalMs = process.env.FACILITATOR_BATCH_INTERVAL_MS ? Number(process.env.FACILITATOR_BATCH_INTERVAL_MS) : void 0;
550
558
  const receiptTimeoutMs = process.env.FACILITATOR_RECEIPT_TIMEOUT_MS ? Number(process.env.FACILITATOR_RECEIPT_TIMEOUT_MS) : void 0;
551
559
  const receiptConfirmations = process.env.FACILITATOR_RECEIPT_CONFIRMATIONS ? Number(process.env.FACILITATOR_RECEIPT_CONFIRMATIONS) : void 0;
@@ -680,6 +688,10 @@ function createFacilitatorFromEnv() {
680
688
  }
681
689
  };
682
690
  const facilitator = new import_facilitator.x402Facilitator();
691
+ (0, import_facilitator2.registerExactEvmScheme)(facilitator, {
692
+ signer: baseSigner,
693
+ networks
694
+ });
683
695
  for (const network of networks) {
684
696
  facilitator.register(
685
697
  network,
@@ -696,6 +708,34 @@ function createFacilitatorFromEnv() {
696
708
  })
697
709
  );
698
710
  }
711
+ const supportedConfidential = confidentialAsset && confidentialEip712Name && confidentialEip712Version && batcherAddress ? {
712
+ asset: confidentialAsset,
713
+ eip712: {
714
+ name: confidentialEip712Name,
715
+ version: confidentialEip712Version
716
+ },
717
+ decimals: confidentialDecimals ?? 6,
718
+ batcherAddress
719
+ } : null;
720
+ if (supportedConfidential) {
721
+ const originalGetSupported = facilitator.getSupported.bind(facilitator);
722
+ facilitator.getSupported = () => {
723
+ const supported = originalGetSupported();
724
+ return {
725
+ ...supported,
726
+ kinds: supported.kinds.map((kind) => {
727
+ if (kind.scheme !== "erc7984-mind-v1") {
728
+ return kind;
729
+ }
730
+ const extra = {
731
+ ...kind.extra,
732
+ confidential: supportedConfidential
733
+ };
734
+ return { ...kind, extra };
735
+ })
736
+ };
737
+ };
738
+ }
699
739
  return facilitator;
700
740
  }
701
741
  function startFacilitator() {
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  createFacilitatorFromEnv,
3
3
  startFacilitator
4
- } from "../chunk-TDT6WJI7.mjs";
4
+ } from "../chunk-PDAG647E.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.0.12",
3
+ "version": "0.1.0",
4
4
  "main": "./dist/index.js",
5
5
  "module": "./dist/index.mjs",
6
6
  "types": "./dist/index.d.ts",
@@ -11,7 +11,7 @@
11
11
  "@x402/core": "^2.0.0",
12
12
  "@x402/evm": "^2.0.0",
13
13
  "viem": "^2.39.3",
14
- "x402z-shared": "0.0.19"
14
+ "x402z-shared": "0.1.0"
15
15
  },
16
16
  "devDependencies": {
17
17
  "jest": "^29.7.0",