@ampvaleo/x402-hyperliquid-server 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/dist/index.js ADDED
@@ -0,0 +1,437 @@
1
+ // src/nonce.ts
2
+ var InMemoryNonceStore = class {
3
+ used = /* @__PURE__ */ new Set();
4
+ async tryConsume(key) {
5
+ if (this.used.has(key)) {
6
+ return false;
7
+ }
8
+ this.used.add(key);
9
+ return true;
10
+ }
11
+ };
12
+
13
+ // src/requirements.ts
14
+ import {
15
+ DEFAULT_USDC_TOKEN,
16
+ HYPERLIQUID_SENDASSET_SCHEME,
17
+ getChainConfig
18
+ } from "@ampvaleo/x402-hyperliquid-core";
19
+ function buildPaymentRequirements(input) {
20
+ const cfg = getChainConfig(input.chain);
21
+ const token = input.token ?? DEFAULT_USDC_TOKEN[input.chain];
22
+ return {
23
+ scheme: HYPERLIQUID_SENDASSET_SCHEME,
24
+ network: cfg.network,
25
+ maxAmountRequired: input.price,
26
+ resource: input.resource,
27
+ description: input.description,
28
+ payTo: input.payTo,
29
+ maxTimeoutSeconds: input.maxTimeoutSeconds ?? 300,
30
+ asset: "USDC",
31
+ extra: {
32
+ chain: input.chain,
33
+ token,
34
+ sourceDex: input.sourceDex,
35
+ destinationDex: input.destinationDex
36
+ }
37
+ };
38
+ }
39
+
40
+ // src/settle.ts
41
+ var HyperliquidExchangeError = class extends Error {
42
+ status;
43
+ body;
44
+ constructor(message, status, body) {
45
+ super(message);
46
+ this.name = "HyperliquidExchangeError";
47
+ this.status = status;
48
+ this.body = body;
49
+ }
50
+ };
51
+ async function submitToExchange(payload, chain, options = {}) {
52
+ const fetchImpl = options.fetch ?? globalThis.fetch;
53
+ const body = {
54
+ action: payload.action,
55
+ nonce: payload.nonce,
56
+ signature: payload.signature
57
+ };
58
+ const res = await fetchImpl(chain.exchangeUrl, {
59
+ method: "POST",
60
+ headers: { "Content-Type": "application/json" },
61
+ body: JSON.stringify(body)
62
+ });
63
+ const text = await res.text();
64
+ let json;
65
+ try {
66
+ json = text ? JSON.parse(text) : null;
67
+ } catch {
68
+ json = text;
69
+ }
70
+ if (!res.ok) {
71
+ throw new HyperliquidExchangeError(
72
+ `Hyperliquid exchange HTTP ${res.status}`,
73
+ res.status,
74
+ json
75
+ );
76
+ }
77
+ const obj = json;
78
+ if (obj && typeof obj === "object" && obj.status === "err") {
79
+ throw new HyperliquidExchangeError(
80
+ "Hyperliquid exchange returned error status",
81
+ res.status,
82
+ json
83
+ );
84
+ }
85
+ return json;
86
+ }
87
+
88
+ // src/settlementMode.ts
89
+ var DEV_VERIFY_ONLY = "X402_HL_DEV_VERIFY_ONLY";
90
+ var ALLOW_VERIFY_ONLY_IN_PROD = "X402_HL_ALLOW_VERIFY_ONLY_IN_PROD";
91
+ function assertDevVerifyOnlyEnvAllowed() {
92
+ if (process.env[DEV_VERIFY_ONLY] !== "1") {
93
+ return;
94
+ }
95
+ if (process.env.NODE_ENV === "production" && process.env[ALLOW_VERIFY_ONLY_IN_PROD] !== "1") {
96
+ throw new Error(
97
+ `${DEV_VERIFY_ONLY}=1 cannot be used with NODE_ENV=production unless ${ALLOW_VERIFY_ONLY_IN_PROD}=1 is also set (acknowledges verify-only is unsafe for real paywalls).`
98
+ );
99
+ }
100
+ }
101
+ function resolveSettlementMode(explicitSettle) {
102
+ if (explicitSettle !== void 0) {
103
+ return {
104
+ settle: explicitSettle,
105
+ warnDevVerifyOnlyEachPaidRequest: false
106
+ };
107
+ }
108
+ if (process.env[DEV_VERIFY_ONLY] === "1") {
109
+ assertDevVerifyOnlyEnvAllowed();
110
+ return {
111
+ settle: false,
112
+ warnDevVerifyOnlyEachPaidRequest: true
113
+ };
114
+ }
115
+ return { settle: true, warnDevVerifyOnlyEachPaidRequest: false };
116
+ }
117
+ var DEV_VERIFY_ONLY_ENV = DEV_VERIFY_ONLY;
118
+ var ALLOW_VERIFY_ONLY_IN_PROD_ENV = ALLOW_VERIFY_ONLY_IN_PROD;
119
+
120
+ // src/verify.ts
121
+ import {
122
+ HYPERLIQUID_SENDASSET_SCHEME as HYPERLIQUID_SENDASSET_SCHEME2,
123
+ VIEM_SEND_ASSET_TYPES,
124
+ buildSendAssetTypedData,
125
+ getAmountDecimalsForToken
126
+ } from "@ampvaleo/x402-hyperliquid-core";
127
+ import { getAddress, parseUnits, recoverTypedDataAddress } from "viem";
128
+ function lower(a) {
129
+ return a.toLowerCase();
130
+ }
131
+ async function verifyPayment(payload, requirements, ctx) {
132
+ if (payload.scheme !== HYPERLIQUID_SENDASSET_SCHEME2) {
133
+ return { valid: false, error: "unsupported scheme" };
134
+ }
135
+ if (requirements.scheme !== HYPERLIQUID_SENDASSET_SCHEME2) {
136
+ return { valid: false, error: "requirements scheme mismatch" };
137
+ }
138
+ if (payload.network !== requirements.network) {
139
+ return { valid: false, error: "network mismatch" };
140
+ }
141
+ const chain = ctx.chain;
142
+ if (payload.signatureChainId !== chain.signatureChainId) {
143
+ return { valid: false, error: "signatureChainId mismatch" };
144
+ }
145
+ const action = payload.action;
146
+ if (action.type !== "sendAsset") {
147
+ return { valid: false, error: "invalid action type" };
148
+ }
149
+ if (action.signatureChainId !== chain.signatureChainId) {
150
+ return { valid: false, error: "action.signatureChainId mismatch" };
151
+ }
152
+ if (action.hyperliquidChain !== chain.hyperliquidLabel) {
153
+ return { valid: false, error: "hyperliquidChain mismatch" };
154
+ }
155
+ if (action.fromSubAccount !== "") {
156
+ return { valid: false, error: "fromSubAccount must be empty" };
157
+ }
158
+ const extra = requirements.extra;
159
+ if (lower(action.destination) !== lower(requirements.payTo)) {
160
+ return { valid: false, error: "destination does not match payTo" };
161
+ }
162
+ if (action.token !== extra.token) {
163
+ return { valid: false, error: "token mismatch" };
164
+ }
165
+ if (action.sourceDex !== extra.sourceDex) {
166
+ return { valid: false, error: "sourceDex mismatch" };
167
+ }
168
+ if (action.destinationDex !== extra.destinationDex) {
169
+ return { valid: false, error: "destinationDex mismatch" };
170
+ }
171
+ let unitDecimals;
172
+ try {
173
+ unitDecimals = getAmountDecimalsForToken(extra.token);
174
+ } catch {
175
+ return { valid: false, error: "unknown token amount decimals (register weiDecimals from spotMeta)" };
176
+ }
177
+ let paid;
178
+ let min;
179
+ try {
180
+ paid = parseUnits(action.amount, unitDecimals);
181
+ min = parseUnits(requirements.maxAmountRequired, unitDecimals);
182
+ } catch {
183
+ return { valid: false, error: "invalid decimal amount" };
184
+ }
185
+ if (paid < min) {
186
+ return { valid: false, error: "amount below maxAmountRequired" };
187
+ }
188
+ if (payload.accepted && mismatchesAcceptedAttack(payload)) {
189
+ return { valid: false, error: "accepted does not match signed action" };
190
+ }
191
+ const window = ctx.nonceWindowMs ?? 6e4;
192
+ const now = ctx.nowMs ?? Date.now();
193
+ if (Math.abs(now - action.nonce) > window) {
194
+ return { valid: false, error: "nonce outside allowed window" };
195
+ }
196
+ const message = {
197
+ hyperliquidChain: action.hyperliquidChain,
198
+ destination: action.destination,
199
+ sourceDex: action.sourceDex,
200
+ destinationDex: action.destinationDex,
201
+ token: action.token,
202
+ amount: action.amount,
203
+ fromSubAccount: action.fromSubAccount,
204
+ nonce: BigInt(action.nonce)
205
+ };
206
+ const typed = buildSendAssetTypedData({
207
+ chainId: chain.chainId,
208
+ message
209
+ });
210
+ const yParity = payload.signature.v >= 27 ? payload.signature.v - 27 : payload.signature.v;
211
+ if (yParity !== 0 && yParity !== 1) {
212
+ return { valid: false, error: "invalid signature v" };
213
+ }
214
+ let signer;
215
+ try {
216
+ signer = await recoverTypedDataAddress({
217
+ domain: typed.domain,
218
+ types: VIEM_SEND_ASSET_TYPES,
219
+ primaryType: typed.primaryType,
220
+ message: typed.message,
221
+ signature: {
222
+ r: payload.signature.r,
223
+ s: payload.signature.s,
224
+ yParity
225
+ }
226
+ });
227
+ signer = getAddress(signer);
228
+ } catch {
229
+ return { valid: false, error: "signature recovery failed" };
230
+ }
231
+ const nonceKey = `${signer}:${action.nonce}`;
232
+ const fresh = await ctx.nonceStore.tryConsume(nonceKey);
233
+ if (!fresh) {
234
+ return { valid: false, error: "nonce replay" };
235
+ }
236
+ return { valid: true, signer };
237
+ }
238
+ function mismatchesAcceptedAttack(payload) {
239
+ const a = payload.accepted;
240
+ if (!a) return false;
241
+ if (a.scheme !== payload.scheme || a.network !== payload.network) {
242
+ return true;
243
+ }
244
+ if (lower(a.payTo) !== lower(payload.action.destination)) {
245
+ return true;
246
+ }
247
+ if (a.extra.token !== payload.action.token) {
248
+ return true;
249
+ }
250
+ if (a.extra.sourceDex !== payload.action.sourceDex) {
251
+ return true;
252
+ }
253
+ if (a.extra.destinationDex !== payload.action.destinationDex) {
254
+ return true;
255
+ }
256
+ return false;
257
+ }
258
+ function assertValidPayment(result) {
259
+ if (!result.valid) {
260
+ throw new Error(result.error);
261
+ }
262
+ }
263
+
264
+ // src/middleware/express.ts
265
+ import {
266
+ decodePaymentHeader,
267
+ getChainConfig as getChainConfig2
268
+ } from "@ampvaleo/x402-hyperliquid-core";
269
+ function x402Hyperliquid(options) {
270
+ const chain = getChainConfig2(options.chain);
271
+ const settlement = resolveSettlementMode(options.settle);
272
+ return async (req, res, next) => {
273
+ const resource = options.resource ?? `${req.protocol}://${req.get("host") ?? "localhost"}${req.originalUrl}`;
274
+ const requirements = buildPaymentRequirements({
275
+ price: options.price,
276
+ payTo: options.payTo,
277
+ chain: options.chain,
278
+ token: options.token,
279
+ sourceDex: options.sourceDex,
280
+ destinationDex: options.destinationDex,
281
+ resource,
282
+ description: options.description,
283
+ maxTimeoutSeconds: options.maxTimeoutSeconds
284
+ });
285
+ const header = req.header("X-PAYMENT");
286
+ if (!header) {
287
+ res.status(402).json({
288
+ error: "Payment Required",
289
+ accepts: [requirements]
290
+ });
291
+ return;
292
+ }
293
+ let payload;
294
+ try {
295
+ payload = decodePaymentHeader(header);
296
+ } catch {
297
+ res.status(400).json({ error: "Invalid X-PAYMENT header" });
298
+ return;
299
+ }
300
+ const ctx = {
301
+ chain,
302
+ nonceStore: options.nonceStore,
303
+ nonceWindowMs: options.nonceWindowMs
304
+ };
305
+ const result = await verifyPayment(payload, requirements, ctx);
306
+ if (!result.valid) {
307
+ res.status(402).json({
308
+ error: result.error,
309
+ accepts: [requirements]
310
+ });
311
+ return;
312
+ }
313
+ if (settlement.warnDevVerifyOnlyEachPaidRequest) {
314
+ console.warn(
315
+ "\u26A0\uFE0F VERIFY-ONLY MODE: payment verified but NOT settled. Do not use in production."
316
+ );
317
+ }
318
+ if (settlement.settle) {
319
+ try {
320
+ await submitToExchange(payload, chain, { fetch: options.fetch });
321
+ } catch (e) {
322
+ res.status(502).json({
323
+ error: e instanceof Error ? e.message : "Settlement failed"
324
+ });
325
+ return;
326
+ }
327
+ }
328
+ req.x402 = {
329
+ signer: result.signer,
330
+ payload
331
+ };
332
+ next();
333
+ };
334
+ }
335
+
336
+ // src/middleware/next.ts
337
+ import {
338
+ decodePaymentHeader as decodePaymentHeader2,
339
+ getChainConfig as getChainConfig3
340
+ } from "@ampvaleo/x402-hyperliquid-core";
341
+ async function runX402HyperliquidGate(request, options) {
342
+ const chain = getChainConfig3(options.chain);
343
+ const settlement = resolveSettlementMode(options.settle);
344
+ const url = new URL(request.url);
345
+ const resource = options.resource ?? `${url.origin}${url.pathname}${url.search}`;
346
+ const requirements = buildPaymentRequirements({
347
+ price: options.price,
348
+ payTo: options.payTo,
349
+ chain: options.chain,
350
+ token: options.token,
351
+ sourceDex: options.sourceDex,
352
+ destinationDex: options.destinationDex,
353
+ resource,
354
+ description: options.description,
355
+ maxTimeoutSeconds: options.maxTimeoutSeconds
356
+ });
357
+ const header = request.headers.get("X-PAYMENT");
358
+ if (!header) {
359
+ return {
360
+ ok: false,
361
+ response: new Response(
362
+ JSON.stringify({ error: "Payment Required", accepts: [requirements] }),
363
+ {
364
+ status: 402,
365
+ headers: { "Content-Type": "application/json" }
366
+ }
367
+ )
368
+ };
369
+ }
370
+ let payload;
371
+ try {
372
+ payload = decodePaymentHeader2(header);
373
+ } catch {
374
+ return {
375
+ ok: false,
376
+ response: new Response(JSON.stringify({ error: "Invalid X-PAYMENT header" }), {
377
+ status: 400,
378
+ headers: { "Content-Type": "application/json" }
379
+ })
380
+ };
381
+ }
382
+ const ctx = {
383
+ chain,
384
+ nonceStore: options.nonceStore,
385
+ nonceWindowMs: options.nonceWindowMs
386
+ };
387
+ const result = await verifyPayment(payload, requirements, ctx);
388
+ if (!result.valid) {
389
+ return {
390
+ ok: false,
391
+ response: new Response(
392
+ JSON.stringify({ error: result.error, accepts: [requirements] }),
393
+ {
394
+ status: 402,
395
+ headers: { "Content-Type": "application/json" }
396
+ }
397
+ )
398
+ };
399
+ }
400
+ if (settlement.warnDevVerifyOnlyEachPaidRequest) {
401
+ console.warn(
402
+ "\u26A0\uFE0F VERIFY-ONLY MODE: payment verified but NOT settled. Do not use in production."
403
+ );
404
+ }
405
+ if (settlement.settle) {
406
+ try {
407
+ await submitToExchange(payload, chain, { fetch: options.fetch });
408
+ } catch (e) {
409
+ return {
410
+ ok: false,
411
+ response: new Response(
412
+ JSON.stringify({
413
+ error: e instanceof Error ? e.message : "Settlement failed"
414
+ }),
415
+ { status: 502, headers: { "Content-Type": "application/json" } }
416
+ )
417
+ };
418
+ }
419
+ }
420
+ return { ok: true, signer: result.signer, payload };
421
+ }
422
+ export {
423
+ ALLOW_VERIFY_ONLY_IN_PROD_ENV,
424
+ DEV_VERIFY_ONLY_ENV,
425
+ HyperliquidExchangeError,
426
+ InMemoryNonceStore,
427
+ assertDevVerifyOnlyEnvAllowed,
428
+ assertValidPayment,
429
+ buildPaymentRequirements,
430
+ mismatchesAcceptedAttack,
431
+ resolveSettlementMode,
432
+ runX402HyperliquidGate,
433
+ submitToExchange,
434
+ verifyPayment,
435
+ x402Hyperliquid
436
+ };
437
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/nonce.ts","../src/requirements.ts","../src/settle.ts","../src/settlementMode.ts","../src/verify.ts","../src/middleware/express.ts","../src/middleware/next.ts"],"sourcesContent":["/**\n * Tracks seen payment nonces to prevent replay. Keys are opaque (e.g. `signer:nonceMs`).\n */\nexport interface NonceStore {\n /** Returns true if the nonce was unused and is now recorded */\n tryConsume(key: string): Promise<boolean>;\n}\n\nexport class InMemoryNonceStore implements NonceStore {\n private readonly used = new Set<string>();\n\n async tryConsume(key: string): Promise<boolean> {\n if (this.used.has(key)) {\n return false;\n }\n this.used.add(key);\n return true;\n }\n}\n","import {\n DEFAULT_USDC_TOKEN,\n HYPERLIQUID_SENDASSET_SCHEME,\n getChainConfig,\n type HyperliquidChainName,\n type PaymentRequirements,\n} from \"@ampvaleo/x402-hyperliquid-core\";\nimport type { Hex } from \"viem\";\n\nexport interface BuildRequirementsInput {\n price: string;\n payTo: Hex;\n chain: HyperliquidChainName;\n /** Full HL token string; defaults to canonical USDC for the chain */\n token?: string;\n sourceDex: \"spot\" | \"\";\n destinationDex: \"spot\" | \"\";\n resource: string;\n description?: string;\n maxTimeoutSeconds?: number;\n}\n\nexport function buildPaymentRequirements(\n input: BuildRequirementsInput,\n): PaymentRequirements {\n const cfg = getChainConfig(input.chain);\n const token = input.token ?? DEFAULT_USDC_TOKEN[input.chain];\n return {\n scheme: HYPERLIQUID_SENDASSET_SCHEME,\n network: cfg.network,\n maxAmountRequired: input.price,\n resource: input.resource,\n description: input.description,\n payTo: input.payTo,\n maxTimeoutSeconds: input.maxTimeoutSeconds ?? 300,\n asset: \"USDC\",\n extra: {\n chain: input.chain,\n token,\n sourceDex: input.sourceDex,\n destinationDex: input.destinationDex,\n },\n };\n}\n","import type { HyperliquidChainConfig, PaymentPayload } from \"@ampvaleo/x402-hyperliquid-core\";\n\nexport class HyperliquidExchangeError extends Error {\n readonly status: number;\n readonly body: unknown;\n\n constructor(message: string, status: number, body: unknown) {\n super(message);\n this.name = \"HyperliquidExchangeError\";\n this.status = status;\n this.body = body;\n }\n}\n\nexport interface SubmitOptions {\n fetch?: typeof fetch;\n}\n\n/**\n * Submits a signed sendAsset payment to Hyperliquid's public exchange API.\n */\nexport async function submitToExchange(\n payload: PaymentPayload,\n chain: HyperliquidChainConfig,\n options: SubmitOptions = {},\n): Promise<unknown> {\n const fetchImpl = options.fetch ?? globalThis.fetch;\n const body = {\n action: payload.action,\n nonce: payload.nonce,\n signature: payload.signature,\n };\n\n const res = await fetchImpl(chain.exchangeUrl, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(body),\n });\n\n const text = await res.text();\n let json: unknown;\n try {\n json = text ? JSON.parse(text) : null;\n } catch {\n json = text;\n }\n\n if (!res.ok) {\n throw new HyperliquidExchangeError(\n `Hyperliquid exchange HTTP ${res.status}`,\n res.status,\n json,\n );\n }\n\n const obj = json as { status?: string; response?: unknown };\n if (obj && typeof obj === \"object\" && obj.status === \"err\") {\n throw new HyperliquidExchangeError(\n \"Hyperliquid exchange returned error status\",\n res.status,\n json,\n );\n }\n\n return json;\n}\n","const DEV_VERIFY_ONLY = \"X402_HL_DEV_VERIFY_ONLY\";\nconst ALLOW_VERIFY_ONLY_IN_PROD = \"X402_HL_ALLOW_VERIFY_ONLY_IN_PROD\";\n\nexport interface SettlementMode {\n settle: boolean;\n /**\n * When true, log a loud warning on each successful verification when settlement is skipped\n * because `X402_HL_DEV_VERIFY_ONLY=1` was used (not because `settle: false` was passed explicitly).\n */\n warnDevVerifyOnlyEachPaidRequest: boolean;\n}\n\n/**\n * Throws if `X402_HL_DEV_VERIFY_ONLY=1` is set in production without the explicit escape hatch.\n * Call once at process startup (e.g. when creating middleware) or before handling traffic.\n */\nexport function assertDevVerifyOnlyEnvAllowed(): void {\n if (process.env[DEV_VERIFY_ONLY] !== \"1\") {\n return;\n }\n if (\n process.env.NODE_ENV === \"production\" &&\n process.env[ALLOW_VERIFY_ONLY_IN_PROD] !== \"1\"\n ) {\n throw new Error(\n `${DEV_VERIFY_ONLY}=1 cannot be used with NODE_ENV=production unless ${ALLOW_VERIFY_ONLY_IN_PROD}=1 is also set (acknowledges verify-only is unsafe for real paywalls).`,\n );\n }\n}\n\n/**\n * Resolves whether to POST to Hyperliquid after verification.\n *\n * - If `explicitSettle` is set, it wins (no env). No dev-verify warning.\n * - Otherwise, if `X402_HL_DEV_VERIFY_ONLY=1`, settlement is off and warnings apply (after assert).\n * - Default: settle (direct mode).\n */\nexport function resolveSettlementMode(\n explicitSettle?: boolean,\n): SettlementMode {\n if (explicitSettle !== undefined) {\n return {\n settle: explicitSettle,\n warnDevVerifyOnlyEachPaidRequest: false,\n };\n }\n if (process.env[DEV_VERIFY_ONLY] === \"1\") {\n assertDevVerifyOnlyEnvAllowed();\n return {\n settle: false,\n warnDevVerifyOnlyEachPaidRequest: true,\n };\n }\n return { settle: true, warnDevVerifyOnlyEachPaidRequest: false };\n}\n\nexport const DEV_VERIFY_ONLY_ENV = DEV_VERIFY_ONLY;\nexport const ALLOW_VERIFY_ONLY_IN_PROD_ENV = ALLOW_VERIFY_ONLY_IN_PROD;\n","import {\n HYPERLIQUID_SENDASSET_SCHEME,\n VIEM_SEND_ASSET_TYPES,\n buildSendAssetTypedData,\n getAmountDecimalsForToken,\n type HyperliquidChainConfig,\n type PaymentPayload,\n type PaymentRequirements,\n} from \"@ampvaleo/x402-hyperliquid-core\";\nimport type { Address } from \"viem\";\nimport { getAddress, parseUnits, recoverTypedDataAddress } from \"viem\";\n\nimport type { NonceStore } from \"./nonce.js\";\n\nexport interface VerifyContext {\n chain: HyperliquidChainConfig;\n nonceStore: NonceStore;\n /** Max age of nonce timestamp in ms from now (default 60_000) */\n nonceWindowMs?: number;\n nowMs?: number;\n}\n\nfunction lower(a: string): string {\n return a.toLowerCase();\n}\n\n/**\n * Verifies EIP-712 signature and that `payload.action` matches server `requirements`.\n * Does **not** trust `payload.accepted` — compare only against `requirements`.\n */\nexport async function verifyPayment(\n payload: PaymentPayload,\n requirements: PaymentRequirements,\n ctx: VerifyContext,\n): Promise<\n { valid: true; signer: Address } | { valid: false; error: string }\n> {\n if (payload.scheme !== HYPERLIQUID_SENDASSET_SCHEME) {\n return { valid: false, error: \"unsupported scheme\" };\n }\n if (requirements.scheme !== HYPERLIQUID_SENDASSET_SCHEME) {\n return { valid: false, error: \"requirements scheme mismatch\" };\n }\n if (payload.network !== requirements.network) {\n return { valid: false, error: \"network mismatch\" };\n }\n\n const chain = ctx.chain;\n if (payload.signatureChainId !== chain.signatureChainId) {\n return { valid: false, error: \"signatureChainId mismatch\" };\n }\n\n const action = payload.action;\n if (action.type !== \"sendAsset\") {\n return { valid: false, error: \"invalid action type\" };\n }\n if (action.signatureChainId !== chain.signatureChainId) {\n return { valid: false, error: \"action.signatureChainId mismatch\" };\n }\n if (action.hyperliquidChain !== chain.hyperliquidLabel) {\n return { valid: false, error: \"hyperliquidChain mismatch\" };\n }\n if (action.fromSubAccount !== \"\") {\n return { valid: false, error: \"fromSubAccount must be empty\" };\n }\n\n const extra = requirements.extra;\n if (lower(action.destination) !== lower(requirements.payTo)) {\n return { valid: false, error: \"destination does not match payTo\" };\n }\n if (action.token !== extra.token) {\n return { valid: false, error: \"token mismatch\" };\n }\n if (action.sourceDex !== extra.sourceDex) {\n return { valid: false, error: \"sourceDex mismatch\" };\n }\n if (action.destinationDex !== extra.destinationDex) {\n return { valid: false, error: \"destinationDex mismatch\" };\n }\n\n let unitDecimals: number;\n try {\n unitDecimals = getAmountDecimalsForToken(extra.token);\n } catch {\n return { valid: false, error: \"unknown token amount decimals (register weiDecimals from spotMeta)\" };\n }\n let paid: bigint;\n let min: bigint;\n try {\n paid = parseUnits(action.amount, unitDecimals);\n min = parseUnits(requirements.maxAmountRequired, unitDecimals);\n } catch {\n return { valid: false, error: \"invalid decimal amount\" };\n }\n if (paid < min) {\n return { valid: false, error: \"amount below maxAmountRequired\" };\n }\n\n if (payload.accepted && mismatchesAcceptedAttack(payload)) {\n return { valid: false, error: \"accepted does not match signed action\" };\n }\n\n const window = ctx.nonceWindowMs ?? 60_000;\n const now = ctx.nowMs ?? Date.now();\n if (Math.abs(now - action.nonce) > window) {\n return { valid: false, error: \"nonce outside allowed window\" };\n }\n\n const message = {\n hyperliquidChain: action.hyperliquidChain,\n destination: action.destination,\n sourceDex: action.sourceDex,\n destinationDex: action.destinationDex,\n token: action.token,\n amount: action.amount,\n fromSubAccount: action.fromSubAccount,\n nonce: BigInt(action.nonce),\n };\n\n const typed = buildSendAssetTypedData({\n chainId: chain.chainId,\n message,\n });\n\n const yParity =\n payload.signature.v >= 27 ? payload.signature.v - 27 : payload.signature.v;\n if (yParity !== 0 && yParity !== 1) {\n return { valid: false, error: \"invalid signature v\" };\n }\n\n let signer: Address;\n try {\n signer = await recoverTypedDataAddress({\n domain: typed.domain,\n types: VIEM_SEND_ASSET_TYPES,\n primaryType: typed.primaryType,\n message: typed.message as unknown as Record<string, unknown>,\n signature: {\n r: payload.signature.r,\n s: payload.signature.s,\n yParity: yParity as 0 | 1,\n },\n });\n signer = getAddress(signer);\n } catch {\n return { valid: false, error: \"signature recovery failed\" };\n }\n\n const nonceKey = `${signer}:${action.nonce}`;\n const fresh = await ctx.nonceStore.tryConsume(nonceKey);\n if (!fresh) {\n return { valid: false, error: \"nonce replay\" };\n }\n\n return { valid: true, signer };\n}\n\n/**\n * Detects the case where the client tampers with `accepted` so it no longer reflects\n * the signed `action` (servers must never rely on `accepted` alone).\n */\nexport function mismatchesAcceptedAttack(payload: PaymentPayload): boolean {\n const a = payload.accepted;\n if (!a) return false;\n if (a.scheme !== payload.scheme || a.network !== payload.network) {\n return true;\n }\n if (lower(a.payTo) !== lower(payload.action.destination)) {\n return true;\n }\n if (a.extra.token !== payload.action.token) {\n return true;\n }\n if (a.extra.sourceDex !== payload.action.sourceDex) {\n return true;\n }\n if (a.extra.destinationDex !== payload.action.destinationDex) {\n return true;\n }\n return false;\n}\n\nexport function assertValidPayment(\n result: { valid: true; signer: Address } | { valid: false; error: string },\n): asserts result is { valid: true; signer: Address } {\n if (!result.valid) {\n throw new Error(result.error);\n }\n}\n","import {\n decodePaymentHeader,\n getChainConfig,\n type HyperliquidChainName,\n} from \"@ampvaleo/x402-hyperliquid-core\";\nimport type { PaymentPayload } from \"@ampvaleo/x402-hyperliquid-core\";\nimport type { Address } from \"viem\";\n\nimport type { NonceStore } from \"../nonce.js\";\nimport { buildPaymentRequirements, type BuildRequirementsInput } from \"../requirements.js\";\nimport { submitToExchange, type SubmitOptions } from \"../settle.js\";\nimport { resolveSettlementMode } from \"../settlementMode.js\";\nimport { verifyPayment, type VerifyContext } from \"../verify.js\";\n\nexport interface X402HyperliquidExpressOptions\n extends Omit<BuildRequirementsInput, \"resource\"> {\n /** Defaults to request URL */\n resource?: string;\n nonceStore: NonceStore;\n nonceWindowMs?: number;\n /**\n * When set, forces settlement on/off. When omitted, `X402_HL_DEV_VERIFY_ONLY=1` can disable\n * settlement for local dev (see `settlementMode.ts`).\n */\n settle?: boolean;\n fetch?: SubmitOptions[\"fetch\"];\n}\n\nexport type X402ExpressLocals = {\n signer: Address;\n payload: PaymentPayload;\n};\n\n/** Express middleware — returns 402 + `accepts` when unpaid; verifies + settles (default) when `X-PAYMENT` present */\nexport function x402Hyperliquid(options: X402HyperliquidExpressOptions) {\n const chain = getChainConfig(options.chain);\n const settlement = resolveSettlementMode(options.settle);\n\n return async (\n req: import(\"express\").Request,\n res: import(\"express\").Response,\n next: import(\"express\").NextFunction,\n ): Promise<void> => {\n const resource =\n options.resource ??\n `${req.protocol}://${req.get(\"host\") ?? \"localhost\"}${req.originalUrl}`;\n\n const requirements = buildPaymentRequirements({\n price: options.price,\n payTo: options.payTo,\n chain: options.chain as HyperliquidChainName,\n token: options.token,\n sourceDex: options.sourceDex,\n destinationDex: options.destinationDex,\n resource,\n description: options.description,\n maxTimeoutSeconds: options.maxTimeoutSeconds,\n });\n\n const header = req.header(\"X-PAYMENT\");\n if (!header) {\n res.status(402).json({\n error: \"Payment Required\",\n accepts: [requirements],\n });\n return;\n }\n\n let payload: import(\"@ampvaleo/x402-hyperliquid-core\").PaymentPayload;\n try {\n payload = decodePaymentHeader(header);\n } catch {\n res.status(400).json({ error: \"Invalid X-PAYMENT header\" });\n return;\n }\n\n const ctx: VerifyContext = {\n chain,\n nonceStore: options.nonceStore,\n nonceWindowMs: options.nonceWindowMs,\n };\n\n const result = await verifyPayment(payload, requirements, ctx);\n if (!result.valid) {\n res.status(402).json({\n error: result.error,\n accepts: [requirements],\n });\n return;\n }\n\n if (settlement.warnDevVerifyOnlyEachPaidRequest) {\n // eslint-disable-next-line no-console\n console.warn(\n \"⚠️ VERIFY-ONLY MODE: payment verified but NOT settled. Do not use in production.\",\n );\n }\n\n if (settlement.settle) {\n try {\n await submitToExchange(payload, chain, { fetch: options.fetch });\n } catch (e) {\n res.status(502).json({\n error: e instanceof Error ? e.message : \"Settlement failed\",\n });\n return;\n }\n }\n\n (req as import(\"express\").Request & { x402: X402ExpressLocals }).x402 = {\n signer: result.signer,\n payload,\n };\n next();\n };\n}\n","import {\n decodePaymentHeader,\n getChainConfig,\n type HyperliquidChainName,\n} from \"@ampvaleo/x402-hyperliquid-core\";\nimport type { Address } from \"viem\";\n\nimport type { NonceStore } from \"../nonce.js\";\nimport {\n buildPaymentRequirements,\n type BuildRequirementsInput,\n} from \"../requirements.js\";\nimport { submitToExchange, type SubmitOptions } from \"../settle.js\";\nimport { resolveSettlementMode } from \"../settlementMode.js\";\nimport { verifyPayment, type VerifyContext } from \"../verify.js\";\n\nexport interface X402HyperliquidNextOptions\n extends Omit<BuildRequirementsInput, \"resource\"> {\n resource?: string;\n nonceStore: NonceStore;\n nonceWindowMs?: number;\n settle?: boolean;\n fetch?: SubmitOptions[\"fetch\"];\n}\n\nexport type X402GateResult =\n | { ok: true; signer: Address; payload: import(\"@ampvaleo/x402-hyperliquid-core\").PaymentPayload }\n | { ok: false; response: Response };\n\n/**\n * App Router / edge-friendly gate: returns a `Response` to return early (402/400/502),\n * or `{ ok: true, ... }` when the caller should continue the route.\n */\nexport async function runX402HyperliquidGate(\n request: Request,\n options: X402HyperliquidNextOptions,\n): Promise<X402GateResult> {\n const chain = getChainConfig(options.chain);\n const settlement = resolveSettlementMode(options.settle);\n const url = new URL(request.url);\n const resource =\n options.resource ?? `${url.origin}${url.pathname}${url.search}`;\n\n const requirements = buildPaymentRequirements({\n price: options.price,\n payTo: options.payTo,\n chain: options.chain as HyperliquidChainName,\n token: options.token,\n sourceDex: options.sourceDex,\n destinationDex: options.destinationDex,\n resource,\n description: options.description,\n maxTimeoutSeconds: options.maxTimeoutSeconds,\n });\n\n const header = request.headers.get(\"X-PAYMENT\");\n if (!header) {\n return {\n ok: false,\n response: new Response(\n JSON.stringify({ error: \"Payment Required\", accepts: [requirements] }),\n {\n status: 402,\n headers: { \"Content-Type\": \"application/json\" },\n },\n ),\n };\n }\n\n let payload: import(\"@ampvaleo/x402-hyperliquid-core\").PaymentPayload;\n try {\n payload = decodePaymentHeader(header);\n } catch {\n return {\n ok: false,\n response: new Response(JSON.stringify({ error: \"Invalid X-PAYMENT header\" }), {\n status: 400,\n headers: { \"Content-Type\": \"application/json\" },\n }),\n };\n }\n\n const ctx: VerifyContext = {\n chain,\n nonceStore: options.nonceStore,\n nonceWindowMs: options.nonceWindowMs,\n };\n\n const result = await verifyPayment(payload, requirements, ctx);\n if (!result.valid) {\n return {\n ok: false,\n response: new Response(\n JSON.stringify({ error: result.error, accepts: [requirements] }),\n {\n status: 402,\n headers: { \"Content-Type\": \"application/json\" },\n },\n ),\n };\n }\n\n if (settlement.warnDevVerifyOnlyEachPaidRequest) {\n // eslint-disable-next-line no-console\n console.warn(\n \"⚠️ VERIFY-ONLY MODE: payment verified but NOT settled. Do not use in production.\",\n );\n }\n\n if (settlement.settle) {\n try {\n await submitToExchange(payload, chain, { fetch: options.fetch });\n } catch (e) {\n return {\n ok: false,\n response: new Response(\n JSON.stringify({\n error: e instanceof Error ? e.message : \"Settlement failed\",\n }),\n { status: 502, headers: { \"Content-Type\": \"application/json\" } },\n ),\n };\n }\n }\n\n return { ok: true, signer: result.signer, payload };\n}\n"],"mappings":"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","names":["HYPERLIQUID_SENDASSET_SCHEME","getChainConfig","getChainConfig","decodePaymentHeader","getChainConfig","getChainConfig","decodePaymentHeader"]}
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "@ampvaleo/x402-hyperliquid-server",
3
+ "version": "0.1.0",
4
+ "description": "Verify, settle, and middleware for x402 on Hyperliquid HyperCore",
5
+ "author": "Valeo Protocol",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/valeo-cash/x402-hyperliquid.git",
10
+ "directory": "packages/server"
11
+ },
12
+ "publishConfig": {
13
+ "access": "public"
14
+ },
15
+ "type": "module",
16
+ "main": "./dist/index.cjs",
17
+ "module": "./dist/index.js",
18
+ "types": "./dist/index.d.ts",
19
+ "exports": {
20
+ ".": {
21
+ "types": "./dist/index.d.ts",
22
+ "import": "./dist/index.js",
23
+ "require": "./dist/index.cjs"
24
+ }
25
+ },
26
+ "files": [
27
+ "dist",
28
+ "README.md"
29
+ ],
30
+ "dependencies": {
31
+ "viem": "^2.21.54",
32
+ "@ampvaleo/x402-hyperliquid-core": "0.1.0"
33
+ },
34
+ "peerDependencies": {
35
+ "express": "^4.0.0 || ^5.0.0",
36
+ "next": "^14.0.0 || ^15.0.0"
37
+ },
38
+ "peerDependenciesMeta": {
39
+ "express": {
40
+ "optional": true
41
+ },
42
+ "next": {
43
+ "optional": true
44
+ }
45
+ },
46
+ "devDependencies": {
47
+ "@types/express": "^5.0.0",
48
+ "@types/node": "^22.10.2",
49
+ "express": "^4.21.2",
50
+ "tsup": "^8.3.5",
51
+ "typescript": "^5.7.2",
52
+ "vitest": "^2.1.8"
53
+ },
54
+ "scripts": {
55
+ "build": "tsup",
56
+ "test": "vitest run"
57
+ }
58
+ }