@lemmaoracle/x402 0.1.1 → 0.1.3

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.
Files changed (146) hide show
  1. package/dist/advanced/index.d.ts +14 -0
  2. package/dist/advanced/index.d.ts.map +1 -0
  3. package/dist/advanced/index.js +14 -0
  4. package/dist/advanced/index.js.map +1 -0
  5. package/dist/index.d.ts +25 -42
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +25 -77
  8. package/dist/index.js.map +1 -1
  9. package/dist/lemma-config.d.ts +41 -0
  10. package/dist/lemma-config.d.ts.map +1 -0
  11. package/dist/lemma-config.js +12 -0
  12. package/dist/lemma-config.js.map +1 -0
  13. package/dist/lemma-discovery.d.ts +37 -0
  14. package/dist/lemma-discovery.d.ts.map +1 -0
  15. package/dist/lemma-discovery.js +104 -0
  16. package/dist/lemma-discovery.js.map +1 -0
  17. package/dist/lemma-server.d.ts +30 -0
  18. package/dist/lemma-server.d.ts.map +1 -0
  19. package/dist/lemma-server.js +169 -0
  20. package/dist/lemma-server.js.map +1 -0
  21. package/dist/lemma-submission.d.ts +44 -0
  22. package/dist/lemma-submission.d.ts.map +1 -0
  23. package/dist/lemma-submission.js +81 -0
  24. package/dist/lemma-submission.js.map +1 -0
  25. package/package.json +14 -14
  26. package/broadcast/DeployPaymentVerifier.s.sol/10143/run-1775996174982.json +0 -34
  27. package/broadcast/DeployPaymentVerifier.s.sol/10143/run-1775996423983.json +0 -51
  28. package/broadcast/DeployPaymentVerifier.s.sol/10143/run-1775996907850.json +0 -51
  29. package/broadcast/DeployPaymentVerifier.s.sol/10143/run-latest.json +0 -51
  30. package/cache/DeployPaymentVerifier.s.sol/10143/run-1775996174982.json +0 -7
  31. package/cache/DeployPaymentVerifier.s.sol/10143/run-1775996423983.json +0 -7
  32. package/cache/DeployPaymentVerifier.s.sol/10143/run-1775996907850.json +0 -7
  33. package/cache/DeployPaymentVerifier.s.sol/10143/run-latest.json +0 -7
  34. package/cache/solidity-files-cache.json +0 -1
  35. package/circuits/README.md +0 -78
  36. package/circuits/payment-comprehensive.test.mjs +0 -272
  37. package/circuits/payment.circom +0 -89
  38. package/circuits/payment.test.mjs +0 -173
  39. package/dist/proof-generator.d.ts +0 -26
  40. package/dist/proof-generator.d.ts.map +0 -1
  41. package/dist/proof-generator.js +0 -154
  42. package/dist/proof-generator.js.map +0 -1
  43. package/dist/proof-generator.test.d.ts +0 -7
  44. package/dist/proof-generator.test.d.ts.map +0 -1
  45. package/dist/proof-generator.test.js +0 -121
  46. package/dist/proof-generator.test.js.map +0 -1
  47. package/dist/transaction-watcher.d.ts +0 -42
  48. package/dist/transaction-watcher.d.ts.map +0 -1
  49. package/dist/transaction-watcher.js +0 -88
  50. package/dist/transaction-watcher.js.map +0 -1
  51. package/dist/transaction-watcher.test.d.ts +0 -7
  52. package/dist/transaction-watcher.test.d.ts.map +0 -1
  53. package/dist/transaction-watcher.test.js +0 -111
  54. package/dist/transaction-watcher.test.js.map +0 -1
  55. package/dist/types.d.ts +0 -76
  56. package/dist/types.d.ts.map +0 -1
  57. package/dist/types.js +0 -2
  58. package/dist/types.js.map +0 -1
  59. package/foundry.lock +0 -11
  60. package/foundry.toml +0 -16
  61. package/lib/forge-std/.gitattributes +0 -1
  62. package/lib/forge-std/.github/CODEOWNERS +0 -1
  63. package/lib/forge-std/.github/dependabot.yml +0 -6
  64. package/lib/forge-std/.github/workflows/ci.yml +0 -161
  65. package/lib/forge-std/.github/workflows/sync.yml +0 -36
  66. package/lib/forge-std/CONTRIBUTING.md +0 -193
  67. package/lib/forge-std/LICENSE-APACHE +0 -203
  68. package/lib/forge-std/LICENSE-MIT +0 -25
  69. package/lib/forge-std/README.md +0 -314
  70. package/lib/forge-std/RELEASE_CHECKLIST.md +0 -12
  71. package/lib/forge-std/foundry.toml +0 -18
  72. package/lib/forge-std/package.json +0 -16
  73. package/lib/forge-std/scripts/vm.py +0 -636
  74. package/lib/forge-std/src/Base.sol +0 -48
  75. package/lib/forge-std/src/Config.sol +0 -60
  76. package/lib/forge-std/src/LibVariable.sol +0 -477
  77. package/lib/forge-std/src/Script.sol +0 -28
  78. package/lib/forge-std/src/StdAssertions.sol +0 -1300
  79. package/lib/forge-std/src/StdChains.sol +0 -303
  80. package/lib/forge-std/src/StdCheats.sol +0 -825
  81. package/lib/forge-std/src/StdConfig.sol +0 -632
  82. package/lib/forge-std/src/StdConstants.sol +0 -30
  83. package/lib/forge-std/src/StdError.sol +0 -15
  84. package/lib/forge-std/src/StdInvariant.sol +0 -161
  85. package/lib/forge-std/src/StdJson.sol +0 -275
  86. package/lib/forge-std/src/StdMath.sol +0 -47
  87. package/lib/forge-std/src/StdStorage.sol +0 -476
  88. package/lib/forge-std/src/StdStyle.sol +0 -333
  89. package/lib/forge-std/src/StdToml.sol +0 -275
  90. package/lib/forge-std/src/StdUtils.sol +0 -200
  91. package/lib/forge-std/src/Test.sol +0 -32
  92. package/lib/forge-std/src/Vm.sol +0 -2533
  93. package/lib/forge-std/src/console.sol +0 -1551
  94. package/lib/forge-std/src/console2.sol +0 -4
  95. package/lib/forge-std/src/interfaces/IERC1155.sol +0 -105
  96. package/lib/forge-std/src/interfaces/IERC165.sol +0 -12
  97. package/lib/forge-std/src/interfaces/IERC20.sol +0 -43
  98. package/lib/forge-std/src/interfaces/IERC4626.sol +0 -190
  99. package/lib/forge-std/src/interfaces/IERC6909.sol +0 -72
  100. package/lib/forge-std/src/interfaces/IERC721.sol +0 -164
  101. package/lib/forge-std/src/interfaces/IERC7540.sol +0 -145
  102. package/lib/forge-std/src/interfaces/IERC7575.sol +0 -241
  103. package/lib/forge-std/src/interfaces/IMulticall3.sol +0 -68
  104. package/lib/forge-std/src/safeconsole.sol +0 -13248
  105. package/lib/forge-std/test/CommonBase.t.sol +0 -44
  106. package/lib/forge-std/test/Config.t.sol +0 -381
  107. package/lib/forge-std/test/LibVariable.t.sol +0 -452
  108. package/lib/forge-std/test/StdAssertions.t.sol +0 -141
  109. package/lib/forge-std/test/StdChains.t.sol +0 -227
  110. package/lib/forge-std/test/StdCheats.t.sol +0 -638
  111. package/lib/forge-std/test/StdConstants.t.sol +0 -38
  112. package/lib/forge-std/test/StdError.t.sol +0 -119
  113. package/lib/forge-std/test/StdJson.t.sol +0 -49
  114. package/lib/forge-std/test/StdMath.t.sol +0 -202
  115. package/lib/forge-std/test/StdStorage.t.sol +0 -532
  116. package/lib/forge-std/test/StdStyle.t.sol +0 -110
  117. package/lib/forge-std/test/StdToml.t.sol +0 -49
  118. package/lib/forge-std/test/StdUtils.t.sol +0 -342
  119. package/lib/forge-std/test/Vm.t.sol +0 -18
  120. package/lib/forge-std/test/compilation/CompilationScript.sol +0 -8
  121. package/lib/forge-std/test/compilation/CompilationScriptBase.sol +0 -8
  122. package/lib/forge-std/test/compilation/CompilationTest.sol +0 -8
  123. package/lib/forge-std/test/compilation/CompilationTestBase.sol +0 -8
  124. package/lib/forge-std/test/fixtures/broadcast.log.json +0 -187
  125. package/lib/forge-std/test/fixtures/config.toml +0 -81
  126. package/lib/forge-std/test/fixtures/test.json +0 -8
  127. package/lib/forge-std/test/fixtures/test.toml +0 -6
  128. package/package.json.backup +0 -55
  129. package/scripts/DeployPaymentVerifier.s.sol +0 -18
  130. package/scripts/build.sh +0 -61
  131. package/scripts/deploy.sh +0 -65
  132. package/scripts/fix-verifier.mjs +0 -64
  133. package/scripts/register-circuit.ts +0 -213
  134. package/scripts/test-with-circuit.mjs +0 -88
  135. package/src/index.ts +0 -115
  136. package/src/proof-generator.test.ts +0 -139
  137. package/src/proof-generator.ts +0 -220
  138. package/src/transaction-watcher.test.ts +0 -153
  139. package/src/transaction-watcher.ts +0 -154
  140. package/src/types.ts +0 -110
  141. package/tsconfig.json +0 -14
  142. package/tsconfig.tsbuildinfo +0 -1
  143. package/vitest.config.ts +0 -18
  144. package/workers/x402-facilitator/src/index.ts +0 -582
  145. package/workers/x402-facilitator/src/x402-env.d.ts +0 -25
  146. package/workers/x402-facilitator/wrangler.toml +0 -19
@@ -1,582 +0,0 @@
1
- /**
2
- * X402 Facilitator Cloudflare Worker
3
- *
4
- * Implements the x402 facilitator spec with three endpoints:
5
- * POST /verify – lightweight pre-check (signature, amount, balance)
6
- * POST /settle – broadcast tx, wait for confirmation, generate ZK proof
7
- * POST /prepare – delegate schema preparation to Relay API
8
- *
9
- * Called by resource servers as part of the x402 payment flow.
10
- *
11
- * Network, RPC URL, and price are resolved dynamically from the incoming
12
- * request (payload / requirements) rather than pinned via environment
13
- * variables, because each resource server may target a different chain.
14
- *
15
- * ZK proof generation is delegated to @lemmaoracle/relay (Vercel) because
16
- * snarkjs/ffjavascript require APIs (URL.createObjectURL) unavailable on
17
- * Cloudflare Workers.
18
- */
19
-
20
- import type X402Env from "./x402-env";
21
- import { createPublicClient, http, type PublicClient } from "viem";
22
- import { base, baseSepolia } from "viem/chains";
23
- import { Hono } from "hono";
24
- import { cors } from "hono/cors";
25
-
26
- // ---------------------------------------------------------------------------
27
- // Request / Response types (aligned with x402 facilitator spec)
28
- // ---------------------------------------------------------------------------
29
-
30
- /** Payment payload sent by the client (forwarded by the resource server). */
31
- type PaymentPayload = Readonly<{
32
- /** EVM scheme: the signed authorization or raw signed tx */
33
- signature: `0x${string}`;
34
- /** Payer address */
35
- from: `0x${string}`;
36
- /** Network identifier, e.g. "eip155:84532" */
37
- network: string;
38
- /** Scheme identifier, e.g. "exact" */
39
- scheme: string;
40
- /** Additional scheme-specific data */
41
- [key: string]: unknown;
42
- }>;
43
-
44
- /** Payment requirements set by the resource server. */
45
- type PaymentRequirements = Readonly<{
46
- /** Scheme identifier */
47
- scheme: string;
48
- /** Network identifier */
49
- network: string;
50
- /** Required payment amount (human-readable, e.g. "$0.001") */
51
- price: string;
52
- /** Address to receive payment */
53
- payTo: `0x${string}`;
54
- /** Additional scheme-specific requirements */
55
- [key: string]: unknown;
56
- }>;
57
-
58
- /** Body of POST /verify and POST /settle */
59
- type FacilitatorRequest = Readonly<{
60
- payload: PaymentPayload;
61
- requirements: PaymentRequirements;
62
- }>;
63
-
64
- /** Response from POST /verify */
65
- type VerifyResponse = Readonly<{
66
- isValid: boolean;
67
- invalidReason: string | null;
68
- }>;
69
-
70
- /** Response from POST /settle */
71
- type SettleResponse = Readonly<{
72
- success: boolean;
73
- txHash: string;
74
- proof: {
75
- proof: string;
76
- inputs: ReadonlyArray<string>;
77
- circuitId: string;
78
- generatedAt: number;
79
- };
80
- network: string;
81
- error?: string;
82
- }>;
83
-
84
- /** Relay API response from POST /prover/prove */
85
- type RelayProveResponse = Readonly<{
86
- proof: string;
87
- inputs: ReadonlyArray<string>;
88
- error?: string;
89
- message?: string;
90
- }>;
91
-
92
- /** Relay API response from POST /prepare */
93
- type RelayPrepareResponse = Readonly<{
94
- normalized: Readonly<Record<string, unknown>>;
95
- commitments: Readonly<{
96
- root: string;
97
- leaves: ReadonlyArray<string>;
98
- randomness: ReadonlyArray<string>;
99
- }>;
100
- error?: string;
101
- message?: string;
102
- }>;
103
-
104
- /** Body of POST /prepare */
105
- type PrepareRequest = Readonly<{
106
- schemaId: string;
107
- payload: unknown;
108
- }>;
109
-
110
- // ---------------------------------------------------------------------------
111
- // Facilitator config (lightweight — no SDK dependency)
112
- // ---------------------------------------------------------------------------
113
-
114
- type FacilitatorConfig = Readonly<{
115
- payToAddress: `0x${string}`;
116
- network: string;
117
- price: string;
118
- ethereumRpcUrl: string;
119
- chainId: number;
120
- lemmaApiBase: string;
121
- lemmaApiKey?: string;
122
- circuitId: string;
123
- relayUrl: string;
124
- minAmount: bigint;
125
- requiredConfirmations: number;
126
- }>;
127
-
128
- // ---------------------------------------------------------------------------
129
- // Network helpers
130
- // ---------------------------------------------------------------------------
131
-
132
- /** Default RPC endpoints — used when no override is provided via RPC_URLS. */
133
- const DEFAULT_RPC_MAP: Readonly<Record<string, string>> = {
134
- "eip155:8453": "https://mainnet.base.org",
135
- "eip155:84532": "https://sepolia.base.org",
136
- "eip155:10143": "https://testnet-rpc.monad.xyz",
137
- };
138
-
139
- /** Map network identifier → chain id. */
140
- const chainIdForNetwork = (network: string): number => {
141
- const map: Record<string, number> = {
142
- "eip155:8453": 8453,
143
- "eip155:84532": 84532,
144
- "eip155:10143": 10143,
145
- };
146
- const id = map[network];
147
- if (id === undefined) {
148
- throw new Error(`Unsupported network: ${network}`);
149
- }
150
- return id;
151
- };
152
-
153
- /** Map network identifier → viem Chain definition. */
154
- const chainForNetwork = (network: string) => {
155
- const map: Record<string, typeof baseSepolia> = {
156
- "eip155:8453": base,
157
- "eip155:84532": baseSepolia,
158
- };
159
- // Fall back to baseSepolia for chains without a viem definition.
160
- return map[network] ?? baseSepolia;
161
- };
162
-
163
- /**
164
- * Resolve the RPC URL for a given network.
165
- *
166
- * Priority: env RPC_URLS override → DEFAULT_RPC_MAP.
167
- */
168
- const rpcForNetwork = (network: string, rpcOverrides: Record<string, string>): string => {
169
- const url = rpcOverrides[network] ?? DEFAULT_RPC_MAP[network];
170
- if (!url) {
171
- throw new Error(`No RPC URL configured for network: ${network}`);
172
- }
173
- return url;
174
- };
175
-
176
- /** Parse RPC_URLS env var (JSON string) into a record. */
177
- const parseRpcUrls = (raw?: string): Record<string, string> => {
178
- if (!raw) return {};
179
- try {
180
- return JSON.parse(raw) as Record<string, string>;
181
- } catch {
182
- return {};
183
- }
184
- };
185
-
186
- // ---------------------------------------------------------------------------
187
- // Helpers
188
- // ---------------------------------------------------------------------------
189
-
190
- /** Build a viem PublicClient for the given network. */
191
- const buildClient = (rpcUrl: string, network: string): PublicClient =>
192
- createPublicClient({
193
- chain: chainForNetwork(network),
194
- transport: http(rpcUrl),
195
- }) as PublicClient;
196
-
197
- /**
198
- * Build a per-request FacilitatorConfig by merging static env settings with
199
- * dynamic values from the request (network, price, RPC URL).
200
- */
201
- const configForRequest = (
202
- env: typeof X402Env,
203
- network: string,
204
- price: string,
205
- rpcOverrides: Record<string, string>,
206
- ): FacilitatorConfig => ({
207
- payToAddress: env.PAY_TO_ADDRESS as `0x${string}`,
208
- network,
209
- price,
210
- ethereumRpcUrl: rpcForNetwork(network, rpcOverrides),
211
- chainId: chainIdForNetwork(network),
212
- lemmaApiBase: env.LEMMA_API_BASE,
213
- lemmaApiKey: env.LEMMA_API_KEY,
214
- circuitId: env.CIRCUIT_ID ?? "x402-payment-v1",
215
- relayUrl: env.RELAY_URL,
216
- minAmount: 1000n,
217
- requiredConfirmations: 6,
218
- });
219
-
220
- /** Parse and validate a facilitator request body. */
221
- const parseFacilitatorRequest = async (
222
- body: unknown,
223
- ): Promise<{ ok: true; data: FacilitatorRequest } | { ok: false; reason: string }> => {
224
- const req = body as FacilitatorRequest | undefined;
225
- if (!req?.payload || !req?.requirements) {
226
- return { ok: false, reason: "Request must contain 'payload' and 'requirements'" };
227
- }
228
- if (!req.payload.from || !req.payload.signature) {
229
- return { ok: false, reason: "Payload must contain 'from' and 'signature'" };
230
- }
231
- if (!req.requirements.payTo || !req.requirements.network) {
232
- return { ok: false, reason: "Requirements must contain 'payTo' and 'network'" };
233
- }
234
- return { ok: true, data: req };
235
- };
236
-
237
- // ---------------------------------------------------------------------------
238
- // Relay API client — delegates proof generation to @lemmaoracle/relay
239
- // ---------------------------------------------------------------------------
240
-
241
- /**
242
- * Call the relay API's POST /prover/prove endpoint.
243
- *
244
- * This replaces the direct `prover.prove()` call that would pull in
245
- * snarkjs/ffjavascript (which use `URL.createObjectURL()` — unavailable
246
- * on Cloudflare Workers).
247
- */
248
- const relayProve = async (
249
- relayUrl: string,
250
- apiBase: string,
251
- apiKey: string | undefined,
252
- circuitId: string,
253
- witness: Readonly<Record<string, unknown>>,
254
- ): Promise<RelayProveResponse> => {
255
- const res = await fetch(`${relayUrl.replace(/\/$/, "")}/prover/prove`, {
256
- method: "POST",
257
- headers: { "Content-Type": "application/json" },
258
- body: JSON.stringify({
259
- apiBase,
260
- ...(apiKey ? { apiKey } : {}),
261
- input: { circuitId, witness },
262
- }),
263
- });
264
-
265
- const data = (await res.json()) as RelayProveResponse;
266
-
267
- if (!res.ok) {
268
- throw new Error(data.message ?? data.error ?? `Relay /prover/prove failed (${res.status})`);
269
- }
270
-
271
- return data;
272
- };
273
-
274
- /**
275
- * Call the relay API's POST /prepare endpoint.
276
- *
277
- * Fetches schema metadata, defines the schema, and prepares data.
278
- * Delegated to @lemmaoracle/relay because preparation may require
279
- * WASM execution and Node.js-specific APIs unavailable on Workers.
280
- */
281
- const relayPrepare = async (
282
- relayUrl: string,
283
- apiBase: string,
284
- apiKey: string | undefined,
285
- schemaId: string,
286
- payload: unknown,
287
- ): Promise<RelayPrepareResponse> => {
288
- const res = await fetch(`${relayUrl.replace(/\/$/, "")}/prepare`, {
289
- method: "POST",
290
- headers: { "Content-Type": "application/json" },
291
- body: JSON.stringify({
292
- apiBase,
293
- ...(apiKey ? { apiKey } : {}),
294
- input: { schemaId, payload },
295
- }),
296
- });
297
-
298
- const data = (await res.json()) as RelayPrepareResponse;
299
-
300
- if (!res.ok) {
301
- throw new Error(data.message ?? data.error ?? `Relay /prepare failed (${res.status})`);
302
- }
303
-
304
- return data;
305
- };
306
-
307
- // ---------------------------------------------------------------------------
308
- // Verification logic
309
- // ---------------------------------------------------------------------------
310
-
311
- /**
312
- * Verify a payment payload against the given requirements.
313
- *
314
- * Checks performed:
315
- * 1. Network / scheme match
316
- * 2. Signature validity (ecRecover)
317
- * 3. Payer has sufficient balance
318
- * 4. Payment amount meets the minimum requirement
319
- */
320
- const verifyPayment = async (
321
- payload: PaymentPayload,
322
- requirements: PaymentRequirements,
323
- client: PublicClient,
324
- config: FacilitatorConfig,
325
- ): Promise<VerifyResponse> => {
326
- // 1. Network match
327
- if (payload.network !== requirements.network) {
328
- return { isValid: false, invalidReason: "Network mismatch between payload and requirements" };
329
- }
330
-
331
- // 2. Scheme match
332
- if (payload.scheme !== requirements.scheme) {
333
- return { isValid: false, invalidReason: "Scheme mismatch between payload and requirements" };
334
- }
335
-
336
- // 3. Verify the payer address is a valid account with a balance
337
- try {
338
- const balance = await client.getBalance({ address: payload.from });
339
- if (balance < config.minAmount) {
340
- return { isValid: false, invalidReason: `Insufficient balance: ${balance} < ${config.minAmount}` };
341
- }
342
- } catch (err) {
343
- return { isValid: false, invalidReason: `Balance check failed: ${(err as Error).message}` };
344
- }
345
-
346
- // 4. Verify recipient matches requirements
347
- if (requirements.payTo.toLowerCase() !== config.payToAddress.toLowerCase()) {
348
- return { isValid: false, invalidReason: "payTo address does not match facilitator config" };
349
- }
350
-
351
- return { isValid: true, invalidReason: null };
352
- };
353
-
354
- // ---------------------------------------------------------------------------
355
- // Settlement logic
356
- // ---------------------------------------------------------------------------
357
-
358
- /**
359
- * Settle a payment by broadcasting the transaction and generating a ZK proof.
360
- *
361
- * Steps:
362
- * 1. Broadcast the signed transaction to the blockchain
363
- * 2. Wait for on-chain confirmation
364
- * 3. Generate ZK proof via relay API (delegated to @lemmaoracle/relay)
365
- * 4. Return txHash + proof
366
- */
367
- const settlePayment = async (
368
- payload: PaymentPayload,
369
- requirements: PaymentRequirements,
370
- client: PublicClient,
371
- config: FacilitatorConfig,
372
- ): Promise<SettleResponse> => {
373
- const network = requirements.network;
374
-
375
- // Step 1: Broadcast the signed transaction
376
- let txHash: `0x${string}`;
377
- try {
378
- txHash = await client.request({
379
- method: "eth_sendRawTransaction",
380
- params: [payload.signature],
381
- }) as `0x${string}`;
382
- } catch (err) {
383
- const message = (err as Error).message;
384
- // If the tx was already submitted (duplicate settlement), extract the hash
385
- if (message.includes("already known") || message.includes("nonce too low")) {
386
- return {
387
- success: false,
388
- txHash: "",
389
- proof: { proof: "", inputs: [], circuitId: config.circuitId, generatedAt: Date.now() },
390
- network,
391
- error: `Transaction broadcast failed: ${message}`,
392
- };
393
- }
394
- throw err;
395
- }
396
-
397
- // Step 2: Wait for on-chain confirmation
398
- const receipt = await client.waitForTransactionReceipt({
399
- hash: txHash,
400
- confirmations: config.requiredConfirmations,
401
- });
402
-
403
- if (receipt.status !== "success") {
404
- return {
405
- success: false,
406
- txHash,
407
- proof: { proof: "", inputs: [], circuitId: config.circuitId, generatedAt: Date.now() },
408
- network,
409
- error: "Transaction reverted on-chain",
410
- };
411
- }
412
-
413
- // Step 3: Generate ZK proof via relay API
414
- const proofResult = await relayProve(
415
- config.relayUrl,
416
- config.lemmaApiBase,
417
- config.lemmaApiKey,
418
- config.circuitId,
419
- {
420
- txHash,
421
- from: payload.from,
422
- network,
423
- },
424
- );
425
-
426
- // Step 4: Return settlement result
427
- return {
428
- success: true,
429
- txHash,
430
- proof: {
431
- proof: proofResult.proof,
432
- inputs: [...proofResult.inputs],
433
- circuitId: config.circuitId,
434
- generatedAt: Date.now(),
435
- },
436
- network,
437
- };
438
- };
439
-
440
- // ---------------------------------------------------------------------------
441
- // Hono App
442
- // ---------------------------------------------------------------------------
443
-
444
- const app = new Hono<{ Bindings: typeof X402Env }>();
445
-
446
- app.use("/*", cors({ origin: "*", credentials: true }));
447
-
448
- // ---------------------------------------------------------------------------
449
- // POST /verify
450
- // ---------------------------------------------------------------------------
451
-
452
- app.post("/verify", async (c) => {
453
- const body = await c.req.json();
454
- const parsed = await parseFacilitatorRequest(body);
455
-
456
- if (!parsed.ok) {
457
- return c.json<VerifyResponse>({ isValid: false, invalidReason: parsed.reason }, 400);
458
- }
459
-
460
- const { payload, requirements } = parsed.data;
461
- const rpcOverrides = parseRpcUrls(c.env.RPC_URLS);
462
-
463
- let config: FacilitatorConfig;
464
- try {
465
- config = configForRequest(c.env, requirements.network, requirements.price, rpcOverrides);
466
- } catch (err) {
467
- return c.json<VerifyResponse>(
468
- { isValid: false, invalidReason: (err as Error).message },
469
- 400,
470
- );
471
- }
472
-
473
- const rpcUrl = rpcForNetwork(requirements.network, rpcOverrides);
474
- const client = buildClient(rpcUrl, requirements.network);
475
-
476
- try {
477
- const result = await verifyPayment(payload, requirements, client, config);
478
- return c.json<VerifyResponse>(result);
479
- } catch (err) {
480
- return c.json<VerifyResponse>(
481
- { isValid: false, invalidReason: `Verification error: ${(err as Error).message}` },
482
- 500,
483
- );
484
- }
485
- });
486
-
487
- // ---------------------------------------------------------------------------
488
- // POST /settle
489
- // ---------------------------------------------------------------------------
490
-
491
- app.post("/settle", async (c) => {
492
- const body = await c.req.json();
493
- const parsed = await parseFacilitatorRequest(body);
494
-
495
- if (!parsed.ok) {
496
- return c.json({ success: false, error: parsed.reason }, 400);
497
- }
498
-
499
- const { payload, requirements } = parsed.data;
500
- const rpcOverrides = parseRpcUrls(c.env.RPC_URLS);
501
-
502
- let config: FacilitatorConfig;
503
- try {
504
- config = configForRequest(c.env, requirements.network, requirements.price, rpcOverrides);
505
- } catch (err) {
506
- return c.json(
507
- { success: false, error: (err as Error).message },
508
- 400,
509
- );
510
- }
511
-
512
- const rpcUrl = rpcForNetwork(requirements.network, rpcOverrides);
513
- const client = buildClient(rpcUrl, requirements.network);
514
-
515
- try {
516
- const result = await settlePayment(payload, requirements, client, config);
517
- return c.json<SettleResponse>(result, result.success ? 200 : 500);
518
- } catch (err) {
519
- return c.json<SettleResponse>(
520
- {
521
- success: false,
522
- txHash: "",
523
- proof: { proof: "", inputs: [], circuitId: config.circuitId, generatedAt: Date.now() },
524
- network: requirements.network,
525
- error: `Settlement error: ${(err as Error).message}`,
526
- },
527
- 500,
528
- );
529
- }
530
- });
531
-
532
- // ---------------------------------------------------------------------------
533
- // POST /prepare — Delegate schema preparation to Relay API
534
- // ---------------------------------------------------------------------------
535
-
536
- app.post("/prepare", async (c) => {
537
- const body = (await c.req.json()) as PrepareRequest | undefined;
538
-
539
- if (!body?.schemaId || body.payload === undefined) {
540
- return c.json(
541
- { success: false, error: "Request must contain 'schemaId' and 'payload'" },
542
- 400,
543
- );
544
- }
545
-
546
- try {
547
- const result = await relayPrepare(
548
- c.env.RELAY_URL,
549
- c.env.LEMMA_API_BASE,
550
- c.env.LEMMA_API_KEY,
551
- body.schemaId,
552
- body.payload,
553
- );
554
- return c.json<RelayPrepareResponse>(result);
555
- } catch (err) {
556
- return c.json(
557
- { error: `Preparation error: ${(err as Error).message}` },
558
- 502,
559
- );
560
- }
561
- });
562
-
563
- // ---------------------------------------------------------------------------
564
- // Health Check
565
- // ---------------------------------------------------------------------------
566
-
567
- app.get("/", (c) => {
568
- const rpcOverrides = parseRpcUrls(c.env.RPC_URLS);
569
- const supportedNetworks = [
570
- ...new Set([...Object.keys(DEFAULT_RPC_MAP), ...Object.keys(rpcOverrides)]),
571
- ];
572
-
573
- return c.json({
574
- service: "lemma-x402-facilitator",
575
- version: "0.4.0",
576
- circuitId: c.env.CIRCUIT_ID ?? "x402-payment-v1",
577
- supportedNetworks,
578
- endpoints: ["/verify", "/settle", "/prepare"],
579
- });
580
- });
581
-
582
- export default app;
@@ -1,25 +0,0 @@
1
- declare const X402Env: {
2
- /** Address to receive payments. */
3
- PAY_TO_ADDRESS: string;
4
- /** Lemma API base URL. */
5
- LEMMA_API_BASE: string;
6
- /** Lemma API key (secret). */
7
- LEMMA_API_KEY?: string;
8
- /** Circuit ID override. Defaults to "x402-payment-v1". */
9
- CIRCUIT_ID?: string;
10
- /**
11
- * JSON map of network identifier → RPC URL.
12
- * Example: '{"eip155:84532":"https://sepolia.base.org","eip155:10143":"https://rpc.monad.xyz"}'
13
- *
14
- * Extends (and overrides) the built-in default RPC map.
15
- */
16
- RPC_URLS?: string;
17
- /**
18
- * Base URL of the @lemmaoracle/relay Vercel deployment.
19
- * Used to delegate proof generation (snarkjs) which cannot run on Workers.
20
- * Example: 'https://relay.lemma.finance'
21
- */
22
- RELAY_URL: string;
23
- };
24
-
25
- export default X402Env;
@@ -1,19 +0,0 @@
1
- name = "x402-facilitator"
2
- workers_dev = true
3
- compatibility_date = "2024-12-30"
4
- compatibility_flags = ["nodejs_compat"]
5
- main = "src/index.ts"
6
-
7
- [vars]
8
- LEMMA_API_BASE = "https://workers.lemma.workers.dev"
9
- CIRCUIT_ID = "x402-payment-v1"
10
- RELAY_URL = "https://p01--lemma-relay-api--svxwx5rc5jzx.code.run/"
11
-
12
- # Required secrets (set via `wrangler secret put`):
13
- # PAY_TO_ADDRESS - address to receive payments
14
- # RELAY_URL - base URL of @lemmaoracle/relay (Vercel)
15
- # LEMMA_API_KEY - (optional) Lemma API key
16
- # RPC_URLS - (optional) JSON override for default RPC endpoints
17
-
18
- [dev]
19
- port = 8788