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 +1 -1
- package/dist/chunk-PDAG647E.mjs +751 -0
- package/dist/index.js +41 -1
- package/dist/index.mjs +1 -1
- package/dist/service/bootstrap.js +41 -1
- package/dist/service/bootstrap.mjs +1 -1
- package/package.json +2 -2
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 =
|
|
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
|
@@ -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 =
|
|
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() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "x402z-facilitator",
|
|
3
|
-
"version": "0.0
|
|
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
|
|
14
|
+
"x402z-shared": "0.1.0"
|
|
15
15
|
},
|
|
16
16
|
"devDependencies": {
|
|
17
17
|
"jest": "^29.7.0",
|