x402z-facilitator 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 ADDED
@@ -0,0 +1,31 @@
1
+ # x402z-facilitator
2
+
3
+ Facilitator service for the erc7984-mind-v1 x402 scheme.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ pnpm add x402z-facilitator
9
+ ```
10
+
11
+ ## Run (service)
12
+
13
+ Set env vars:
14
+
15
+ - `FACILITATOR_EVM_PRIVATE_KEY`
16
+ - `FACILITATOR_EVM_RPC_URL`
17
+ - `FACILITATOR_EVM_CHAIN_ID` (default 11155111)
18
+ - `FACILITATOR_NETWORKS` (default `eip155:11155111`)
19
+ - `FACILITATOR_PORT` (default 8040)
20
+
21
+ Then run:
22
+
23
+ ```bash
24
+ pnpm -C packages/x402z-facilitator build
25
+ node packages/x402z-facilitator/dist/bootstrap.js
26
+ ```
27
+
28
+ ## Notes
29
+
30
+ - This service receives `/verify` and `/settle` HTTP calls from the server.
31
+ - It submits `confidentialTransferWithAuthorization` transactions on-chain.
@@ -0,0 +1,8 @@
1
+ import { x402Facilitator } from '@x402/core/facilitator';
2
+
3
+ declare function createConfidentialFacilitatorFromEnv(): x402Facilitator;
4
+ declare function startConfidentialFacilitator(): {
5
+ port: number;
6
+ };
7
+
8
+ export { createConfidentialFacilitatorFromEnv, startConfidentialFacilitator };
@@ -0,0 +1,8 @@
1
+ import { x402Facilitator } from '@x402/core/facilitator';
2
+
3
+ declare function createConfidentialFacilitatorFromEnv(): x402Facilitator;
4
+ declare function startConfidentialFacilitator(): {
5
+ port: number;
6
+ };
7
+
8
+ export { createConfidentialFacilitatorFromEnv, startConfidentialFacilitator };
@@ -0,0 +1,357 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/bootstrap.ts
21
+ var bootstrap_exports = {};
22
+ __export(bootstrap_exports, {
23
+ createConfidentialFacilitatorFromEnv: () => createConfidentialFacilitatorFromEnv,
24
+ startConfidentialFacilitator: () => startConfidentialFacilitator
25
+ });
26
+ module.exports = __toCommonJS(bootstrap_exports);
27
+ var import_facilitator = require("@x402/core/facilitator");
28
+ var import_evm = require("@x402/evm");
29
+ var import_viem2 = require("viem");
30
+ var import_accounts = require("viem/accounts");
31
+
32
+ // src/service.ts
33
+ var import_node_http = require("http");
34
+ async function readJson(req) {
35
+ const chunks = [];
36
+ for await (const chunk of req) {
37
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
38
+ }
39
+ const body = Buffer.concat(chunks).toString("utf8");
40
+ return JSON.parse(body);
41
+ }
42
+ function sendJson(res, status, payload) {
43
+ res.writeHead(status, { "content-type": "application/json" });
44
+ res.end(JSON.stringify(payload));
45
+ }
46
+ function createFacilitatorService(config) {
47
+ const server = (0, import_node_http.createServer)(async (req, res) => {
48
+ try {
49
+ const method = req.method ?? "GET";
50
+ const url = req.url ?? "/";
51
+ if (process.env.X402Z_DEBUG === "1") {
52
+ console.debug(`[x402z-facilitator] ${method} ${url}`);
53
+ }
54
+ if (!req.url) {
55
+ sendJson(res, 404, { error: "not_found" });
56
+ return;
57
+ }
58
+ if (req.method === "GET" && req.url === "/supported") {
59
+ sendJson(res, 200, config.facilitator.getSupported());
60
+ return;
61
+ }
62
+ if (req.method === "POST" && req.url === "/verify") {
63
+ const body = await readJson(req);
64
+ const result = await config.facilitator.verify(body.paymentPayload, body.paymentRequirements);
65
+ sendJson(res, 200, result);
66
+ return;
67
+ }
68
+ if (req.method === "POST" && req.url === "/settle") {
69
+ const body = await readJson(req);
70
+ const result = await config.facilitator.settle(body.paymentPayload, body.paymentRequirements);
71
+ sendJson(res, 200, result);
72
+ return;
73
+ }
74
+ sendJson(res, 404, { error: "not_found" });
75
+ } catch (error) {
76
+ const message = error instanceof Error ? error.message : "internal_error";
77
+ sendJson(res, 500, { error: message });
78
+ }
79
+ });
80
+ return server;
81
+ }
82
+
83
+ // src/scheme.ts
84
+ var import_viem = require("viem");
85
+ var import_x402z_shared = require("x402z-shared");
86
+ var ConfidentialEvmFacilitator = class {
87
+ constructor(config) {
88
+ this.config = config;
89
+ this.scheme = "erc7984-mind-v1";
90
+ this.caipFamily = "eip155:*";
91
+ this.hashFn = config.hashEncryptedAmountInput ?? import_x402z_shared.hashEncryptedAmountInput;
92
+ this.clock = config.clock ?? (() => Math.floor(Date.now() / 1e3));
93
+ this.checkUsedNonces = config.checkUsedNonces ?? true;
94
+ this.waitForReceipt = config.waitForReceipt ?? true;
95
+ this.receiptOptions = config.receipt;
96
+ }
97
+ getExtra(_) {
98
+ return void 0;
99
+ }
100
+ getSigners(_) {
101
+ return [...this.config.signer.getAddresses()];
102
+ }
103
+ async verify(payload, requirements) {
104
+ const confidentialPayload = payload.payload;
105
+ if (payload.accepted.scheme !== this.scheme || requirements.scheme !== this.scheme) {
106
+ return {
107
+ isValid: false,
108
+ invalidReason: "unsupported_scheme",
109
+ payer: confidentialPayload?.authorization?.holder
110
+ };
111
+ }
112
+ if (payload.accepted.network !== requirements.network) {
113
+ return {
114
+ isValid: false,
115
+ invalidReason: "network_mismatch",
116
+ payer: confidentialPayload.authorization.holder
117
+ };
118
+ }
119
+ const extra = requirements.extra;
120
+ const eip712 = extra?.eip712;
121
+ if (!eip712?.name || !eip712?.version) {
122
+ return {
123
+ isValid: false,
124
+ invalidReason: "missing_eip712_domain",
125
+ payer: confidentialPayload.authorization.holder
126
+ };
127
+ }
128
+ const now = this.clock();
129
+ const validAfter = Number(confidentialPayload.authorization.validAfter);
130
+ const validBefore = Number(confidentialPayload.authorization.validBefore);
131
+ if (Number.isNaN(validAfter) || Number.isNaN(validBefore)) {
132
+ return {
133
+ isValid: false,
134
+ invalidReason: "invalid_validity_window",
135
+ payer: confidentialPayload.authorization.holder
136
+ };
137
+ }
138
+ if (now < validAfter || now > validBefore) {
139
+ return {
140
+ isValid: false,
141
+ invalidReason: "authorization_expired",
142
+ payer: confidentialPayload.authorization.holder
143
+ };
144
+ }
145
+ if (!(0, import_viem.isAddressEqual)((0, import_viem.getAddress)(confidentialPayload.authorization.payee), (0, import_viem.getAddress)(requirements.payTo))) {
146
+ return {
147
+ isValid: false,
148
+ invalidReason: "recipient_mismatch",
149
+ payer: confidentialPayload.authorization.holder
150
+ };
151
+ }
152
+ const computedHash = this.hashFn(confidentialPayload.encryptedAmountInput);
153
+ if (computedHash !== confidentialPayload.authorization.encryptedAmountHash) {
154
+ return {
155
+ isValid: false,
156
+ invalidReason: "encrypted_amount_mismatch",
157
+ payer: confidentialPayload.authorization.holder
158
+ };
159
+ }
160
+ const chainId = parseInt(requirements.network.split(":")[1]);
161
+ const isValidSignature = await this.config.signer.verifyTypedData({
162
+ address: confidentialPayload.authorization.holder,
163
+ domain: {
164
+ name: eip712.name,
165
+ version: eip712.version,
166
+ chainId,
167
+ verifyingContract: (0, import_viem.getAddress)(requirements.asset)
168
+ },
169
+ types: import_x402z_shared.confidentialPaymentTypes,
170
+ primaryType: "ConfidentialPayment",
171
+ message: {
172
+ holder: (0, import_viem.getAddress)(confidentialPayload.authorization.holder),
173
+ payee: (0, import_viem.getAddress)(confidentialPayload.authorization.payee),
174
+ maxClearAmount: BigInt(confidentialPayload.authorization.maxClearAmount),
175
+ resourceHash: confidentialPayload.authorization.resourceHash,
176
+ validAfter: BigInt(confidentialPayload.authorization.validAfter),
177
+ validBefore: BigInt(confidentialPayload.authorization.validBefore),
178
+ nonce: confidentialPayload.authorization.nonce,
179
+ encryptedAmountHash: confidentialPayload.authorization.encryptedAmountHash
180
+ },
181
+ signature: confidentialPayload.signature
182
+ });
183
+ if (!isValidSignature) {
184
+ return {
185
+ isValid: false,
186
+ invalidReason: "invalid_signature",
187
+ payer: confidentialPayload.authorization.holder
188
+ };
189
+ }
190
+ if (this.checkUsedNonces) {
191
+ const used = await this.config.signer.readContract({
192
+ address: (0, import_viem.getAddress)(requirements.asset),
193
+ abi: import_x402z_shared.confidentialTokenAbi,
194
+ functionName: "usedNonces",
195
+ args: [confidentialPayload.authorization.holder, confidentialPayload.authorization.nonce]
196
+ });
197
+ if (used) {
198
+ return {
199
+ isValid: false,
200
+ invalidReason: "nonce_already_used",
201
+ payer: confidentialPayload.authorization.holder
202
+ };
203
+ }
204
+ }
205
+ return {
206
+ isValid: true,
207
+ payer: confidentialPayload.authorization.holder
208
+ };
209
+ }
210
+ async settle(payload, requirements) {
211
+ const valid = await this.verify(payload, requirements);
212
+ const confidentialPayload = payload.payload;
213
+ if (!valid.isValid) {
214
+ return {
215
+ success: false,
216
+ errorReason: valid.invalidReason ?? "invalid_payment",
217
+ payer: confidentialPayload.authorization.holder,
218
+ transaction: "",
219
+ network: requirements.network
220
+ };
221
+ }
222
+ const txHash = await this.config.signer.writeContract({
223
+ address: (0, import_viem.getAddress)(requirements.asset),
224
+ abi: import_x402z_shared.confidentialTokenAbi,
225
+ functionName: "confidentialTransferWithAuthorization",
226
+ args: [
227
+ confidentialPayload.authorization,
228
+ confidentialPayload.encryptedAmountInput,
229
+ confidentialPayload.inputProof,
230
+ confidentialPayload.signature
231
+ ]
232
+ });
233
+ if (process.env.X402Z_DEBUG === "1") {
234
+ console.debug("[x402z-facilitator] tx submitted", txHash);
235
+ }
236
+ if (this.waitForReceipt) {
237
+ const receipt = await this.config.signer.waitForTransactionReceipt({
238
+ hash: txHash
239
+ });
240
+ if (process.env.X402Z_DEBUG === "1") {
241
+ console.debug("[x402z-facilitator] tx receipt", receipt);
242
+ }
243
+ if (receipt.status !== "success") {
244
+ return {
245
+ success: false,
246
+ errorReason: "settlement_failed",
247
+ payer: confidentialPayload.authorization.holder,
248
+ transaction: txHash,
249
+ network: requirements.network
250
+ };
251
+ }
252
+ }
253
+ return {
254
+ success: true,
255
+ payer: confidentialPayload.authorization.holder,
256
+ transaction: txHash,
257
+ network: requirements.network
258
+ };
259
+ }
260
+ };
261
+
262
+ // src/bootstrap.ts
263
+ function requireEnv(key) {
264
+ const value = process.env[key];
265
+ if (!value) {
266
+ throw new Error(`Missing required env var: ${key}`);
267
+ }
268
+ return value;
269
+ }
270
+ function createConfidentialFacilitatorFromEnv() {
271
+ const privateKey = requireEnv("FACILITATOR_EVM_PRIVATE_KEY");
272
+ const chainId = Number(process.env.FACILITATOR_EVM_CHAIN_ID ?? "11155111");
273
+ const rpcUrl = requireEnv("FACILITATOR_EVM_RPC_URL");
274
+ const networks = (process.env.FACILITATOR_NETWORKS ?? "eip155:11155111").split(",").map((network) => network.trim()).filter(Boolean);
275
+ const waitForReceipt = (process.env.FACILITATOR_WAIT_FOR_RECEIPT ?? "true") === "true";
276
+ const receiptTimeoutMs = process.env.FACILITATOR_RECEIPT_TIMEOUT_MS ? Number(process.env.FACILITATOR_RECEIPT_TIMEOUT_MS) : void 0;
277
+ const receiptConfirmations = process.env.FACILITATOR_RECEIPT_CONFIRMATIONS ? Number(process.env.FACILITATOR_RECEIPT_CONFIRMATIONS) : void 0;
278
+ const receiptPollingIntervalMs = process.env.FACILITATOR_RECEIPT_POLLING_INTERVAL_MS ? Number(process.env.FACILITATOR_RECEIPT_POLLING_INTERVAL_MS) : void 0;
279
+ const debugEnabled = process.env.X402Z_DEBUG === "1";
280
+ const account = (0, import_accounts.privateKeyToAccount)(privateKey);
281
+ if (debugEnabled) {
282
+ console.debug("[x402z-facilitator] config", {
283
+ chainId,
284
+ networks,
285
+ rpcUrl,
286
+ waitForReceipt,
287
+ receipt: {
288
+ confirmations: receiptConfirmations,
289
+ timeoutMs: receiptTimeoutMs,
290
+ pollingIntervalMs: receiptPollingIntervalMs
291
+ },
292
+ address: account.address
293
+ });
294
+ }
295
+ const client = (0, import_viem2.createWalletClient)({
296
+ account,
297
+ chain: {
298
+ id: chainId,
299
+ name: "custom",
300
+ nativeCurrency: { name: "native", symbol: "NATIVE", decimals: 18 },
301
+ rpcUrls: { default: { http: [rpcUrl] } }
302
+ },
303
+ transport: (0, import_viem2.http)(rpcUrl)
304
+ }).extend(import_viem2.publicActions);
305
+ const signer = (0, import_evm.toFacilitatorEvmSigner)({
306
+ address: account.address,
307
+ readContract: (args) => client.readContract({
308
+ ...args,
309
+ args: args.args || []
310
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
311
+ }),
312
+ verifyTypedData: (args) => client.verifyTypedData(args),
313
+ writeContract: (args) => client.writeContract({
314
+ ...args,
315
+ args: args.args || []
316
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
317
+ }),
318
+ sendTransaction: (args) => (
319
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
320
+ client.sendTransaction({ to: args.to, data: args.data })
321
+ ),
322
+ waitForTransactionReceipt: (args) => client.waitForTransactionReceipt(args),
323
+ getCode: (args) => client.getCode(args)
324
+ });
325
+ const facilitator = new import_facilitator.x402Facilitator();
326
+ for (const network of networks) {
327
+ facilitator.register(
328
+ network,
329
+ new ConfidentialEvmFacilitator({
330
+ signer,
331
+ waitForReceipt,
332
+ receipt: {
333
+ confirmations: receiptConfirmations,
334
+ timeoutMs: receiptTimeoutMs,
335
+ pollingIntervalMs: receiptPollingIntervalMs
336
+ }
337
+ })
338
+ );
339
+ }
340
+ return facilitator;
341
+ }
342
+ function startConfidentialFacilitator() {
343
+ const facilitator = createConfidentialFacilitatorFromEnv();
344
+ const port = Number(process.env.FACILITATOR_PORT ?? "8040");
345
+ const server = createFacilitatorService({ facilitator, port });
346
+ server.listen(port);
347
+ return { port };
348
+ }
349
+ if (require.main === module) {
350
+ const { port } = startConfidentialFacilitator();
351
+ console.log(`Confidential facilitator listening on :${port}`);
352
+ }
353
+ // Annotate the CommonJS export names for ESM import in node:
354
+ 0 && (module.exports = {
355
+ createConfidentialFacilitatorFromEnv,
356
+ startConfidentialFacilitator
357
+ });
@@ -0,0 +1,105 @@
1
+ import {
2
+ ConfidentialEvmFacilitator,
3
+ __require,
4
+ createFacilitatorService
5
+ } from "./chunk-4LCQ4MN3.mjs";
6
+
7
+ // src/bootstrap.ts
8
+ import { x402Facilitator } from "@x402/core/facilitator";
9
+ import { toFacilitatorEvmSigner } from "@x402/evm";
10
+ import { createWalletClient, http, publicActions } from "viem";
11
+ import { privateKeyToAccount } from "viem/accounts";
12
+ function requireEnv(key) {
13
+ const value = process.env[key];
14
+ if (!value) {
15
+ throw new Error(`Missing required env var: ${key}`);
16
+ }
17
+ return value;
18
+ }
19
+ function createConfidentialFacilitatorFromEnv() {
20
+ const privateKey = requireEnv("FACILITATOR_EVM_PRIVATE_KEY");
21
+ const chainId = Number(process.env.FACILITATOR_EVM_CHAIN_ID ?? "11155111");
22
+ const rpcUrl = requireEnv("FACILITATOR_EVM_RPC_URL");
23
+ const networks = (process.env.FACILITATOR_NETWORKS ?? "eip155:11155111").split(",").map((network) => network.trim()).filter(Boolean);
24
+ const waitForReceipt = (process.env.FACILITATOR_WAIT_FOR_RECEIPT ?? "true") === "true";
25
+ const receiptTimeoutMs = process.env.FACILITATOR_RECEIPT_TIMEOUT_MS ? Number(process.env.FACILITATOR_RECEIPT_TIMEOUT_MS) : void 0;
26
+ const receiptConfirmations = process.env.FACILITATOR_RECEIPT_CONFIRMATIONS ? Number(process.env.FACILITATOR_RECEIPT_CONFIRMATIONS) : void 0;
27
+ const receiptPollingIntervalMs = process.env.FACILITATOR_RECEIPT_POLLING_INTERVAL_MS ? Number(process.env.FACILITATOR_RECEIPT_POLLING_INTERVAL_MS) : void 0;
28
+ const debugEnabled = process.env.X402Z_DEBUG === "1";
29
+ const account = privateKeyToAccount(privateKey);
30
+ if (debugEnabled) {
31
+ console.debug("[x402z-facilitator] config", {
32
+ chainId,
33
+ networks,
34
+ rpcUrl,
35
+ waitForReceipt,
36
+ receipt: {
37
+ confirmations: receiptConfirmations,
38
+ timeoutMs: receiptTimeoutMs,
39
+ pollingIntervalMs: receiptPollingIntervalMs
40
+ },
41
+ address: account.address
42
+ });
43
+ }
44
+ const client = createWalletClient({
45
+ account,
46
+ chain: {
47
+ id: chainId,
48
+ name: "custom",
49
+ nativeCurrency: { name: "native", symbol: "NATIVE", decimals: 18 },
50
+ rpcUrls: { default: { http: [rpcUrl] } }
51
+ },
52
+ transport: http(rpcUrl)
53
+ }).extend(publicActions);
54
+ const signer = toFacilitatorEvmSigner({
55
+ address: account.address,
56
+ readContract: (args) => client.readContract({
57
+ ...args,
58
+ args: args.args || []
59
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
60
+ }),
61
+ verifyTypedData: (args) => client.verifyTypedData(args),
62
+ writeContract: (args) => client.writeContract({
63
+ ...args,
64
+ args: args.args || []
65
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
66
+ }),
67
+ sendTransaction: (args) => (
68
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
69
+ client.sendTransaction({ to: args.to, data: args.data })
70
+ ),
71
+ waitForTransactionReceipt: (args) => client.waitForTransactionReceipt(args),
72
+ getCode: (args) => client.getCode(args)
73
+ });
74
+ const facilitator = new x402Facilitator();
75
+ for (const network of networks) {
76
+ facilitator.register(
77
+ network,
78
+ new ConfidentialEvmFacilitator({
79
+ signer,
80
+ waitForReceipt,
81
+ receipt: {
82
+ confirmations: receiptConfirmations,
83
+ timeoutMs: receiptTimeoutMs,
84
+ pollingIntervalMs: receiptPollingIntervalMs
85
+ }
86
+ })
87
+ );
88
+ }
89
+ return facilitator;
90
+ }
91
+ function startConfidentialFacilitator() {
92
+ const facilitator = createConfidentialFacilitatorFromEnv();
93
+ const port = Number(process.env.FACILITATOR_PORT ?? "8040");
94
+ const server = createFacilitatorService({ facilitator, port });
95
+ server.listen(port);
96
+ return { port };
97
+ }
98
+ if (__require.main === module) {
99
+ const { port } = startConfidentialFacilitator();
100
+ console.log(`Confidential facilitator listening on :${port}`);
101
+ }
102
+ export {
103
+ createConfidentialFacilitatorFromEnv,
104
+ startConfidentialFacilitator
105
+ };