x402z-server 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -17,7 +17,12 @@ pnpm add x402z-server
17
17
  ## Usage
18
18
 
19
19
  ```ts
20
- import { createX402zServer } from "x402z-server";
20
+ import {
21
+ buildAcceptsFromConfigs,
22
+ buildConfidentialServerConfigFromSchemeConfig,
23
+ createX402zServer,
24
+ } from "x402z-server";
25
+ import { SCHEME_CONFIG } from "x402z-scheme-config";
21
26
  import { createRelayer, SepoliaConfig } from "x402z-shared";
22
27
 
23
28
  const relayer = await createRelayer({
@@ -25,27 +30,32 @@ const relayer = await createRelayer({
25
30
  network: "https://sepolia.infura.io/v3/...",
26
31
  });
27
32
 
33
+ const allowedPayments = [
34
+ {
35
+ method: "exact.USDC.base",
36
+ payTo: "0xPayTo",
37
+ price: "$0.01",
38
+ },
39
+ {
40
+ method: SCHEME_CONFIG["erc7984-mind-v1"].CONFIDENTIAL_USDC.sepolia,
41
+ payTo: "0xPayTo",
42
+ price: "1.0",
43
+ maxTimeoutSeconds: 300,
44
+ },
45
+ ] as const;
46
+
47
+ const confidentialEntry = SCHEME_CONFIG["erc7984-mind-v1"].CONFIDENTIAL_USDC.sepolia;
48
+
28
49
  const server = await createX402zServer({
29
50
  facilitatorUrl: "http://localhost:8040",
30
- confidential: {
31
- asset: "0xToken",
32
- eip712: { name: "FHEToken Confidential", version: "1" },
33
- decimals: 6,
34
- batcherAddress: "0xBatcher",
51
+ confidential: buildConfidentialServerConfigFromSchemeConfig({
52
+ config: confidentialEntry,
35
53
  signer: { address, signTypedData },
36
54
  relayer,
37
- },
55
+ }),
38
56
  routes: {
39
57
  "GET /demo": {
40
- accepts: [
41
- {
42
- scheme: "erc7984-mind-v1",
43
- payTo: "0xPayTo",
44
- price: "1.0",
45
- network: "eip155:11155111",
46
- maxTimeoutSeconds: 300,
47
- },
48
- ],
58
+ accepts: buildAcceptsFromConfigs(allowedPayments),
49
59
  description: "Demo content",
50
60
  mimeType: "text/plain",
51
61
  },
@@ -62,12 +72,11 @@ server.listen(8080);
62
72
 
63
73
  - `createX402zServer(config)`
64
74
  - `facilitatorUrl` (required): HTTP facilitator endpoint
65
- - `confidential` (optional): confidential scheme config (required if any route uses `erc7984-mind-v1`)
66
- - `confidential.signer`: signer used to decrypt transfer amounts
67
- - `confidential.relayer`: FHEVM relayer instance used for decryption
68
- - `confidential.asset`, `confidential.eip712`, `confidential.decimals`, `confidential.batcherAddress`: optional overrides; when omitted, defaults are pulled from the facilitator `/supported` response
69
- - `routes`: map of `METHOD /path` to payment requirements
70
- - `onPaid`: handler for successful payment (returns response body + optional headers)
75
+ - `confidential` (optional): confidential scheme config (required if any route uses `erc7984-mind-v1`)
76
+ - `confidential.signer`: signer used to decrypt transfer amounts
77
+ - `confidential.relayer`: FHEVM relayer instance used for decryption
78
+ - `routes`: map of `METHOD /path` to payment requirements
79
+ - `onPaid`: handler for successful payment (returns response body + optional headers)
71
80
 
72
81
  ## Examples
73
82
 
@@ -100,11 +109,46 @@ const server = await createX402zServer({
100
109
  server.listen(8090);
101
110
  ```
102
111
 
112
+ ## Choosing Payment Methods (Server)
113
+
114
+ Server routes accept an ordered list of payment methods, each paired with its own `payTo` and `price`.
115
+
116
+ You can specify methods in two ways:
117
+
118
+ 1) **Name string** (scheme + token + network alias):
119
+ ```ts
120
+ const method = "exact.USDC.base";
121
+ ```
122
+
123
+ 2) **Config entry** (from `SCHEME_CONFIG`):
124
+ ```ts
125
+ const method = SCHEME_CONFIG.exact.USDC.base;
126
+ ```
127
+
128
+ Build your `accepts` array from these entries:
129
+ ```ts
130
+ const accepts = buildAcceptsFromConfigs([
131
+ { method: "exact.USDC.base", payTo, price: "$0.01" },
132
+ { method: SCHEME_CONFIG["erc7984-mind-v1"].CONFIDENTIAL_USDC.sepolia, payTo, price: "1.0" },
133
+ ]);
134
+ ```
135
+
103
136
  ## Notes
104
137
 
105
138
  - Confidential scheme name: `erc7984-mind-v1`
106
139
  - Exact scheme name: `exact`
107
140
  - `confidential.batcherAddress` is required in requirements; clients bind encrypted inputs to it.
141
+ - Scheme defaults are exported as `SCHEME_CONFIG` from `x402z-scheme-config` (and re-exported by `x402z-server`). Keys are `scheme -> token -> networkAlias` (e.g., `SCHEME_CONFIG.exact.USDC.base`).
142
+ - You can also refer to a method by its string name (e.g., `exact.USDC.base`). A full list is available as `SCHEME_CONFIG_NAMES`.
143
+
144
+ ## Choosing Schemes/Networks
145
+
146
+ Select supported methods using `SCHEME_CONFIG` entries in your server code. Pair each entry with its own `payTo` and `price`.
147
+
148
+ ## Scheme Naming Guidance
149
+
150
+ - Use a single scheme name when the payment flow is identical and only the asset changes (for example, multiple ERC-20 tokens using the exact same exact-scheme logic).
151
+ - Introduce a new scheme name only when the payment flow differs (for example, different contract method, different typed data, or different authorization logic).
108
152
 
109
153
  ## API surface
110
154
 
@@ -112,6 +156,12 @@ Exports:
112
156
  - `X402zEvmServerScheme`: server scheme implementation for erc7984-mind-v1.
113
157
  - `registerX402zEvmServerScheme`: registers the scheme with x402 server.
114
158
  - `createX402zServer`: helper to configure the paywalled server.
159
+ - `buildExactAccept`, `buildConfidentialAccept`, `buildConfidentialServerConfig`: helpers to construct accept items and confidential config.
160
+ - `buildAcceptsFromConfigs`: build `accepts` from `SCHEME_CONFIG` entries paired with `payTo`/`price`.
161
+ - `SCHEME_CONFIG`: full scheme/token/network config (addresses, network, decimals, EIP-712, batcher). Source of truth lives in `x402z-scheme-config`.
162
+ - `buildConfidentialServerConfigFromSchemeConfig`: build confidential server config from a `SCHEME_CONFIG` entry.
163
+ - `SCHEME_CONFIG_NAMES`: all supported method names as strings (e.g., `exact.USDC.base`).
164
+ - `getSchemeConfigByName`: resolve a method name string to its config entry.
115
165
  - `x402ResourceServer`: core x402 resource server.
116
166
  - `HTTPFacilitatorClient`: HTTP facilitator client wrapper.
117
167
  - `x402HTTPResourceServer`: HTTP resource server helpers.
@@ -146,3 +196,7 @@ Types:
146
196
  - `UnpaidResponseBody`: unpaid response shape.
147
197
  - `UnpaidResponseResult`: unpaid response result.
148
198
  - re-exported types from `@x402/core/types`: shared x402 types.
199
+ - `ExactTokenConfig`, `ConfidentialTokenConfig`: resolved token config for exact/confidential schemes.
200
+ - `ExactTokenSymbol`, `ConfidentialTokenSymbol`: allowed token symbols per scheme.
201
+ - `PaymentMethodPricing`: `{ method, payTo, price, maxTimeoutSeconds? }` for `buildAcceptsFromConfigs`.
202
+ - `BuildConfidentialServerConfigFromConfigInput`: input shape for `buildConfidentialServerConfigFromSchemeConfig`.
package/dist/index.d.mts CHANGED
@@ -4,6 +4,8 @@ import { x402ResourceServer } from '@x402/core/server';
4
4
  export { x402ResourceServer } from '@x402/core/server';
5
5
  import * as http from 'http';
6
6
  import { RelayerSigner, RelayerInstance } from 'x402z-shared';
7
+ import { ExactTokenConfig, SchemeConfigName, ConfidentialTokenConfig } from 'x402z-scheme-config';
8
+ export { ConfidentialTokenConfig, ConfidentialTokenSymbol, ExactTokenConfig, ExactTokenSymbol, SCHEME_CONFIG, SCHEME_CONFIG_NAMES, SchemeConfig, SchemeConfigName, getSchemeConfigByName, isConfidentialTokenConfig } from 'x402z-scheme-config';
7
9
  export { CompiledRoute, DynamicPayTo, DynamicPrice, FacilitatorClient, FacilitatorConfig, HTTPAdapter, HTTPFacilitatorClient, HTTPProcessResult, HTTPRequestContext, HTTPResponseInstructions, PaymentOption, PaywallConfig, PaywallProvider, ProcessSettleFailureResponse, ProcessSettleResultResponse, ProcessSettleSuccessResponse, RouteConfigurationError, RouteValidationError, RoutesConfig, UnpaidResponseBody, UnpaidResponseResult, x402HTTPResourceServer } from '@x402/core/http';
8
10
 
9
11
  type NoScheme$1 = {
@@ -104,4 +106,60 @@ type X402zServerOptions = X402zBaseServerOptions & {
104
106
  } & NoScheme;
105
107
  declare function createX402zServer(config: X402zServerOptions): Promise<http.Server<typeof http.IncomingMessage, typeof http.ServerResponse>>;
106
108
 
107
- export { type X402zConfidentialServerConfig, X402zEvmServerScheme, type X402zRouteAccept, type X402zRouteHandler, type X402zRouteOptions, type X402zServerNetworkOptions, type X402zServerOptions, type X402zServerRegistrationOptions, type X402zServerSchemeOptions, createX402zServer, registerX402zEvmServerScheme };
109
+ type ExactAcceptInput = {
110
+ network: Network;
111
+ payTo: string;
112
+ price: Price;
113
+ asset: `0x${string}`;
114
+ decimals: number;
115
+ eip712Name: string;
116
+ eip712Version: string;
117
+ maxTimeoutSeconds?: number;
118
+ };
119
+ type ConfidentialAcceptInput = {
120
+ network: Network;
121
+ payTo: string;
122
+ price: string;
123
+ maxTimeoutSeconds?: number;
124
+ };
125
+ type ExactMethodPricing = {
126
+ method: ExactTokenConfig | SchemeConfigName;
127
+ payTo: string;
128
+ price: Price;
129
+ maxTimeoutSeconds?: number;
130
+ };
131
+ type ConfidentialMethodPricing = {
132
+ method: ConfidentialTokenConfig | SchemeConfigName;
133
+ payTo: string;
134
+ price: string;
135
+ maxTimeoutSeconds?: number;
136
+ };
137
+ type PaymentMethodPricing = ExactMethodPricing | ConfidentialMethodPricing;
138
+ type ConfidentialRuntimeInput = {
139
+ signer: X402zConfidentialServerConfig["signer"];
140
+ relayer: X402zConfidentialServerConfig["relayer"];
141
+ resourceHash?: X402zConfidentialServerConfig["resourceHash"];
142
+ };
143
+ type ConfidentialConfigInput = {
144
+ asset: `0x${string}`;
145
+ eip712: {
146
+ name: string;
147
+ version: string;
148
+ };
149
+ decimals: number;
150
+ batcherAddress: `0x${string}`;
151
+ resourceHash?: `0x${string}`;
152
+ };
153
+ type BuildConfidentialServerConfigFromConfigInput = {
154
+ config: ConfidentialTokenConfig | SchemeConfigName | null | undefined;
155
+ signer: X402zConfidentialServerConfig["signer"];
156
+ relayer: X402zConfidentialServerConfig["relayer"];
157
+ resourceHash?: X402zConfidentialServerConfig["resourceHash"];
158
+ };
159
+ declare function buildExactAccept(config: ExactAcceptInput): X402zRouteAccept;
160
+ declare function buildConfidentialAccept(config: ConfidentialAcceptInput): X402zRouteAccept;
161
+ declare function buildAcceptsFromConfigs(methods: PaymentMethodPricing[]): X402zRouteAccept[];
162
+ declare function buildConfidentialServerConfig(config: ConfidentialConfigInput, runtime: ConfidentialRuntimeInput): X402zConfidentialServerConfig;
163
+ declare function buildConfidentialServerConfigFromSchemeConfig(input: BuildConfidentialServerConfigFromConfigInput): X402zConfidentialServerConfig | undefined;
164
+
165
+ export { type BuildConfidentialServerConfigFromConfigInput, type ConfidentialAcceptInput, type ExactAcceptInput, type PaymentMethodPricing, type X402zConfidentialServerConfig, X402zEvmServerScheme, type X402zRouteAccept, type X402zRouteHandler, type X402zRouteOptions, type X402zServerNetworkOptions, type X402zServerOptions, type X402zServerRegistrationOptions, type X402zServerSchemeOptions, buildAcceptsFromConfigs, buildConfidentialAccept, buildConfidentialServerConfig, buildConfidentialServerConfigFromSchemeConfig, buildExactAccept, createX402zServer, registerX402zEvmServerScheme };
package/dist/index.d.ts CHANGED
@@ -4,6 +4,8 @@ import { x402ResourceServer } from '@x402/core/server';
4
4
  export { x402ResourceServer } from '@x402/core/server';
5
5
  import * as http from 'http';
6
6
  import { RelayerSigner, RelayerInstance } from 'x402z-shared';
7
+ import { ExactTokenConfig, SchemeConfigName, ConfidentialTokenConfig } from 'x402z-scheme-config';
8
+ export { ConfidentialTokenConfig, ConfidentialTokenSymbol, ExactTokenConfig, ExactTokenSymbol, SCHEME_CONFIG, SCHEME_CONFIG_NAMES, SchemeConfig, SchemeConfigName, getSchemeConfigByName, isConfidentialTokenConfig } from 'x402z-scheme-config';
7
9
  export { CompiledRoute, DynamicPayTo, DynamicPrice, FacilitatorClient, FacilitatorConfig, HTTPAdapter, HTTPFacilitatorClient, HTTPProcessResult, HTTPRequestContext, HTTPResponseInstructions, PaymentOption, PaywallConfig, PaywallProvider, ProcessSettleFailureResponse, ProcessSettleResultResponse, ProcessSettleSuccessResponse, RouteConfigurationError, RouteValidationError, RoutesConfig, UnpaidResponseBody, UnpaidResponseResult, x402HTTPResourceServer } from '@x402/core/http';
8
10
 
9
11
  type NoScheme$1 = {
@@ -104,4 +106,60 @@ type X402zServerOptions = X402zBaseServerOptions & {
104
106
  } & NoScheme;
105
107
  declare function createX402zServer(config: X402zServerOptions): Promise<http.Server<typeof http.IncomingMessage, typeof http.ServerResponse>>;
106
108
 
107
- export { type X402zConfidentialServerConfig, X402zEvmServerScheme, type X402zRouteAccept, type X402zRouteHandler, type X402zRouteOptions, type X402zServerNetworkOptions, type X402zServerOptions, type X402zServerRegistrationOptions, type X402zServerSchemeOptions, createX402zServer, registerX402zEvmServerScheme };
109
+ type ExactAcceptInput = {
110
+ network: Network;
111
+ payTo: string;
112
+ price: Price;
113
+ asset: `0x${string}`;
114
+ decimals: number;
115
+ eip712Name: string;
116
+ eip712Version: string;
117
+ maxTimeoutSeconds?: number;
118
+ };
119
+ type ConfidentialAcceptInput = {
120
+ network: Network;
121
+ payTo: string;
122
+ price: string;
123
+ maxTimeoutSeconds?: number;
124
+ };
125
+ type ExactMethodPricing = {
126
+ method: ExactTokenConfig | SchemeConfigName;
127
+ payTo: string;
128
+ price: Price;
129
+ maxTimeoutSeconds?: number;
130
+ };
131
+ type ConfidentialMethodPricing = {
132
+ method: ConfidentialTokenConfig | SchemeConfigName;
133
+ payTo: string;
134
+ price: string;
135
+ maxTimeoutSeconds?: number;
136
+ };
137
+ type PaymentMethodPricing = ExactMethodPricing | ConfidentialMethodPricing;
138
+ type ConfidentialRuntimeInput = {
139
+ signer: X402zConfidentialServerConfig["signer"];
140
+ relayer: X402zConfidentialServerConfig["relayer"];
141
+ resourceHash?: X402zConfidentialServerConfig["resourceHash"];
142
+ };
143
+ type ConfidentialConfigInput = {
144
+ asset: `0x${string}`;
145
+ eip712: {
146
+ name: string;
147
+ version: string;
148
+ };
149
+ decimals: number;
150
+ batcherAddress: `0x${string}`;
151
+ resourceHash?: `0x${string}`;
152
+ };
153
+ type BuildConfidentialServerConfigFromConfigInput = {
154
+ config: ConfidentialTokenConfig | SchemeConfigName | null | undefined;
155
+ signer: X402zConfidentialServerConfig["signer"];
156
+ relayer: X402zConfidentialServerConfig["relayer"];
157
+ resourceHash?: X402zConfidentialServerConfig["resourceHash"];
158
+ };
159
+ declare function buildExactAccept(config: ExactAcceptInput): X402zRouteAccept;
160
+ declare function buildConfidentialAccept(config: ConfidentialAcceptInput): X402zRouteAccept;
161
+ declare function buildAcceptsFromConfigs(methods: PaymentMethodPricing[]): X402zRouteAccept[];
162
+ declare function buildConfidentialServerConfig(config: ConfidentialConfigInput, runtime: ConfidentialRuntimeInput): X402zConfidentialServerConfig;
163
+ declare function buildConfidentialServerConfigFromSchemeConfig(input: BuildConfidentialServerConfigFromConfigInput): X402zConfidentialServerConfig | undefined;
164
+
165
+ export { type BuildConfidentialServerConfigFromConfigInput, type ConfidentialAcceptInput, type ExactAcceptInput, type PaymentMethodPricing, type X402zConfidentialServerConfig, X402zEvmServerScheme, type X402zRouteAccept, type X402zRouteHandler, type X402zRouteOptions, type X402zServerNetworkOptions, type X402zServerOptions, type X402zServerRegistrationOptions, type X402zServerSchemeOptions, buildAcceptsFromConfigs, buildConfidentialAccept, buildConfidentialServerConfig, buildConfidentialServerConfigFromSchemeConfig, buildExactAccept, createX402zServer, registerX402zEvmServerScheme };
package/dist/index.js CHANGED
@@ -21,8 +21,17 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
23
  HTTPFacilitatorClient: () => import_http2.HTTPFacilitatorClient,
24
+ SCHEME_CONFIG: () => import_x402z_scheme_config2.SCHEME_CONFIG,
25
+ SCHEME_CONFIG_NAMES: () => import_x402z_scheme_config2.SCHEME_CONFIG_NAMES,
24
26
  X402zEvmServerScheme: () => X402zEvmServerScheme,
27
+ buildAcceptsFromConfigs: () => buildAcceptsFromConfigs,
28
+ buildConfidentialAccept: () => buildConfidentialAccept,
29
+ buildConfidentialServerConfig: () => buildConfidentialServerConfig,
30
+ buildConfidentialServerConfigFromSchemeConfig: () => buildConfidentialServerConfigFromSchemeConfig,
31
+ buildExactAccept: () => buildExactAccept,
25
32
  createX402zServer: () => createX402zServer,
33
+ getSchemeConfigByName: () => import_x402z_scheme_config2.getSchemeConfigByName,
34
+ isConfidentialTokenConfig: () => import_x402z_scheme_config2.isConfidentialTokenConfig,
26
35
  registerX402zEvmServerScheme: () => registerX402zEvmServerScheme,
27
36
  x402HTTPResourceServer: () => import_http2.x402HTTPResourceServer,
28
37
  x402ResourceServer: () => import_server4.x402ResourceServer
@@ -161,6 +170,30 @@ async function fetchSupported(facilitatorUrl, fetchFn) {
161
170
  }
162
171
  return response.json();
163
172
  }
173
+ function normalizeExactDefaults(extra) {
174
+ const exact = extra?.exact;
175
+ if (!exact) {
176
+ return null;
177
+ }
178
+ const list = Array.isArray(exact) ? exact : [exact];
179
+ const normalized = list.map((item) => {
180
+ if (!item?.asset) {
181
+ return null;
182
+ }
183
+ const name = item.eip712?.name ?? item.name;
184
+ const version = item.eip712?.version ?? item.version;
185
+ if (!name || !version) {
186
+ return null;
187
+ }
188
+ return {
189
+ asset: item.asset,
190
+ decimals: item.decimals ?? 6,
191
+ name,
192
+ version
193
+ };
194
+ }).filter((value) => value !== null);
195
+ return normalized.length > 0 ? normalized : null;
196
+ }
164
197
  function normalizeConfidentialDefaults(extra) {
165
198
  const confidential = extra?.confidential ?? null;
166
199
  if (!confidential?.asset || !confidential.eip712?.name || !confidential.eip712?.version) {
@@ -180,6 +213,25 @@ function normalizeConfidentialDefaults(extra) {
180
213
  resourceHash: confidential.resourceHash
181
214
  };
182
215
  }
216
+ function parseMoneyToDecimal(money) {
217
+ if (typeof money === "number") {
218
+ return money;
219
+ }
220
+ if (typeof money !== "string") {
221
+ throw new Error("Invalid price type for exact scheme");
222
+ }
223
+ const clean = money.replace(/^\$/, "").trim();
224
+ const amount = Number.parseFloat(clean);
225
+ if (Number.isNaN(amount)) {
226
+ throw new Error(`Invalid money format: ${money}`);
227
+ }
228
+ return amount;
229
+ }
230
+ function convertToTokenAmount(amount, decimals) {
231
+ const [intPart, decPart = ""] = String(amount).split(".");
232
+ const paddedDec = decPart.padEnd(decimals, "0").slice(0, decimals);
233
+ return (intPart + paddedDec).replace(/^0+/, "") || "0";
234
+ }
183
235
  async function createX402zServer(config) {
184
236
  const debugEnabled = config.debug ?? process.env.X402Z_DEBUG === "1";
185
237
  const fetchFn = globalThis.fetch;
@@ -195,6 +247,23 @@ async function createX402zServer(config) {
195
247
  if (wantsConfidential && !confidentialConfig) {
196
248
  throw new Error("confidential config is required when using erc7984-mind-v1 routes");
197
249
  }
250
+ const needsExactDefaults = Object.values(config.routes).some(
251
+ (route) => route.accepts.some((accept) => accept.scheme === "exact" && typeof accept.price !== "object")
252
+ );
253
+ let supportedExactDefaults = {};
254
+ if (wantsExact && needsExactDefaults) {
255
+ if (!fetchFn) {
256
+ throw new Error("fetch is required to load exact defaults from facilitator");
257
+ }
258
+ const supported = await fetchSupported(config.facilitatorUrl, fetchFn);
259
+ for (const kind of supported.kinds ?? []) {
260
+ if (kind.scheme !== "exact") continue;
261
+ const normalized = normalizeExactDefaults(kind.extra);
262
+ if (normalized) {
263
+ supportedExactDefaults[kind.network] = normalized;
264
+ }
265
+ }
266
+ }
198
267
  if (wantsConfidential && confidentialConfig) {
199
268
  let supportedDefaults = {};
200
269
  const needsSupportedDefaults = !confidentialConfig.getNetworkConfig && (!confidentialConfig.asset || !confidentialConfig.eip712 || !confidentialConfig.batcherAddress);
@@ -244,9 +313,39 @@ async function createX402zServer(config) {
244
313
  const rpcUrl = relayer ? getRelayerRpcUrl(relayer) : null;
245
314
  const routes = {};
246
315
  for (const [path, route] of Object.entries(config.routes)) {
316
+ const accepts = route.accepts.map((accept) => {
317
+ if (accept.scheme !== "exact") {
318
+ return accept;
319
+ }
320
+ if (typeof accept.price === "object") {
321
+ return accept;
322
+ }
323
+ const defaults = supportedExactDefaults[accept.network];
324
+ if (!defaults || defaults.length === 0) {
325
+ throw new Error(`Missing exact defaults for network ${accept.network}`);
326
+ }
327
+ if (defaults.length > 1) {
328
+ throw new Error(
329
+ `Multiple exact assets configured for ${accept.network}. Provide a Price object with an explicit asset.`
330
+ );
331
+ }
332
+ const decimalAmount = parseMoneyToDecimal(accept.price);
333
+ const amount = convertToTokenAmount(decimalAmount, defaults[0].decimals);
334
+ return {
335
+ ...accept,
336
+ price: {
337
+ amount,
338
+ asset: defaults[0].asset,
339
+ extra: {
340
+ name: defaults[0].name,
341
+ version: defaults[0].version
342
+ }
343
+ }
344
+ };
345
+ });
247
346
  routes[path] = {
248
347
  ...route,
249
- accepts: route.accepts
348
+ accepts
250
349
  };
251
350
  }
252
351
  const httpServer = new import_http.x402HTTPResourceServer(resourceServer, routes);
@@ -410,14 +509,161 @@ async function createX402zServer(config) {
410
509
  return server;
411
510
  }
412
511
 
512
+ // src/config/builders.ts
513
+ var import_x402z_scheme_config = require("x402z-scheme-config");
514
+ function parseMoneyToDecimal2(money) {
515
+ if (typeof money === "number") {
516
+ return money;
517
+ }
518
+ if (typeof money !== "string") {
519
+ throw new Error("Invalid price type for exact scheme");
520
+ }
521
+ const clean = money.replace(/^\$/, "").trim();
522
+ const amount = Number.parseFloat(clean);
523
+ if (Number.isNaN(amount)) {
524
+ throw new Error(`Invalid money format: ${money}`);
525
+ }
526
+ return amount;
527
+ }
528
+ function convertToTokenAmount2(amount, decimals) {
529
+ const [intPart, decPart = ""] = String(amount).split(".");
530
+ const paddedDec = decPart.padEnd(decimals, "0").slice(0, decimals);
531
+ return (intPart + paddedDec).replace(/^0+/, "") || "0";
532
+ }
533
+ function buildExactAccept(config) {
534
+ const decimalAmount = parseMoneyToDecimal2(config.price);
535
+ const amount = convertToTokenAmount2(decimalAmount, config.decimals);
536
+ return {
537
+ scheme: "exact",
538
+ payTo: config.payTo,
539
+ price: {
540
+ amount,
541
+ asset: config.asset,
542
+ extra: {
543
+ name: config.eip712Name,
544
+ version: config.eip712Version
545
+ }
546
+ },
547
+ network: config.network,
548
+ ...config.maxTimeoutSeconds ? { maxTimeoutSeconds: config.maxTimeoutSeconds } : {}
549
+ };
550
+ }
551
+ function buildConfidentialAccept(config) {
552
+ return {
553
+ scheme: "erc7984-mind-v1",
554
+ payTo: config.payTo,
555
+ price: config.price,
556
+ network: config.network,
557
+ maxTimeoutSeconds: config.maxTimeoutSeconds ?? 300
558
+ };
559
+ }
560
+ function buildAcceptsFromConfigs(methods) {
561
+ const accepts = [];
562
+ for (const method of methods) {
563
+ const config = resolveMethodConfig(method.method);
564
+ const isConfidential = (0, import_x402z_scheme_config.isConfidentialTokenConfig)(config);
565
+ if (isConfidential) {
566
+ const confidential = method;
567
+ accepts.push(
568
+ buildConfidentialAccept({
569
+ network: config.network,
570
+ payTo: confidential.payTo,
571
+ price: confidential.price,
572
+ maxTimeoutSeconds: confidential.maxTimeoutSeconds
573
+ })
574
+ );
575
+ continue;
576
+ }
577
+ const exact = method;
578
+ accepts.push(
579
+ buildExactAccept({
580
+ network: config.network,
581
+ payTo: exact.payTo,
582
+ price: exact.price,
583
+ asset: config.asset,
584
+ decimals: config.decimals,
585
+ eip712Name: config.eip712.name,
586
+ eip712Version: config.eip712.version,
587
+ maxTimeoutSeconds: exact.maxTimeoutSeconds
588
+ })
589
+ );
590
+ }
591
+ return accepts;
592
+ }
593
+ function buildConfidentialServerConfig(config, runtime) {
594
+ return {
595
+ asset: config.asset,
596
+ eip712: {
597
+ name: config.eip712.name,
598
+ version: config.eip712.version
599
+ },
600
+ decimals: config.decimals,
601
+ batcherAddress: config.batcherAddress,
602
+ resourceHash: runtime.resourceHash ?? config.resourceHash,
603
+ signer: runtime.signer,
604
+ relayer: runtime.relayer
605
+ };
606
+ }
607
+ function buildConfidentialServerConfigFromConfig(input) {
608
+ if (!input.config) {
609
+ return void 0;
610
+ }
611
+ const resolved = resolveMethodConfig(input.config);
612
+ if (!(0, import_x402z_scheme_config.isConfidentialTokenConfig)(resolved)) {
613
+ throw new Error("Confidential config required for confidential server config");
614
+ }
615
+ return buildConfidentialServerConfig(
616
+ {
617
+ asset: resolved.asset,
618
+ eip712: {
619
+ name: resolved.eip712.name,
620
+ version: resolved.eip712.version
621
+ },
622
+ decimals: resolved.decimals,
623
+ batcherAddress: resolved.batcherAddress
624
+ },
625
+ {
626
+ signer: input.signer,
627
+ relayer: input.relayer,
628
+ resourceHash: input.resourceHash
629
+ }
630
+ );
631
+ }
632
+ function buildConfidentialServerConfigFromSchemeConfig(input) {
633
+ if (!input.config) {
634
+ return void 0;
635
+ }
636
+ return buildConfidentialServerConfigFromConfig(input);
637
+ }
638
+ function resolveMethodConfig(method) {
639
+ if (typeof method !== "string") {
640
+ return method;
641
+ }
642
+ const resolved = (0, import_x402z_scheme_config.getSchemeConfigByName)(method);
643
+ if (!resolved) {
644
+ throw new Error(`Unknown scheme config name: ${method}`);
645
+ }
646
+ return resolved;
647
+ }
648
+
413
649
  // src/index.ts
650
+ var import_x402z_scheme_config2 = require("x402z-scheme-config");
414
651
  var import_server4 = require("@x402/core/server");
415
652
  var import_http2 = require("@x402/core/http");
416
653
  // Annotate the CommonJS export names for ESM import in node:
417
654
  0 && (module.exports = {
418
655
  HTTPFacilitatorClient,
656
+ SCHEME_CONFIG,
657
+ SCHEME_CONFIG_NAMES,
419
658
  X402zEvmServerScheme,
659
+ buildAcceptsFromConfigs,
660
+ buildConfidentialAccept,
661
+ buildConfidentialServerConfig,
662
+ buildConfidentialServerConfigFromSchemeConfig,
663
+ buildExactAccept,
420
664
  createX402zServer,
665
+ getSchemeConfigByName,
666
+ isConfidentialTokenConfig,
421
667
  registerX402zEvmServerScheme,
422
668
  x402HTTPResourceServer,
423
669
  x402ResourceServer
package/dist/index.mjs CHANGED
@@ -134,6 +134,30 @@ async function fetchSupported(facilitatorUrl, fetchFn) {
134
134
  }
135
135
  return response.json();
136
136
  }
137
+ function normalizeExactDefaults(extra) {
138
+ const exact = extra?.exact;
139
+ if (!exact) {
140
+ return null;
141
+ }
142
+ const list = Array.isArray(exact) ? exact : [exact];
143
+ const normalized = list.map((item) => {
144
+ if (!item?.asset) {
145
+ return null;
146
+ }
147
+ const name = item.eip712?.name ?? item.name;
148
+ const version = item.eip712?.version ?? item.version;
149
+ if (!name || !version) {
150
+ return null;
151
+ }
152
+ return {
153
+ asset: item.asset,
154
+ decimals: item.decimals ?? 6,
155
+ name,
156
+ version
157
+ };
158
+ }).filter((value) => value !== null);
159
+ return normalized.length > 0 ? normalized : null;
160
+ }
137
161
  function normalizeConfidentialDefaults(extra) {
138
162
  const confidential = extra?.confidential ?? null;
139
163
  if (!confidential?.asset || !confidential.eip712?.name || !confidential.eip712?.version) {
@@ -153,6 +177,25 @@ function normalizeConfidentialDefaults(extra) {
153
177
  resourceHash: confidential.resourceHash
154
178
  };
155
179
  }
180
+ function parseMoneyToDecimal(money) {
181
+ if (typeof money === "number") {
182
+ return money;
183
+ }
184
+ if (typeof money !== "string") {
185
+ throw new Error("Invalid price type for exact scheme");
186
+ }
187
+ const clean = money.replace(/^\$/, "").trim();
188
+ const amount = Number.parseFloat(clean);
189
+ if (Number.isNaN(amount)) {
190
+ throw new Error(`Invalid money format: ${money}`);
191
+ }
192
+ return amount;
193
+ }
194
+ function convertToTokenAmount(amount, decimals) {
195
+ const [intPart, decPart = ""] = String(amount).split(".");
196
+ const paddedDec = decPart.padEnd(decimals, "0").slice(0, decimals);
197
+ return (intPart + paddedDec).replace(/^0+/, "") || "0";
198
+ }
156
199
  async function createX402zServer(config) {
157
200
  const debugEnabled = config.debug ?? process.env.X402Z_DEBUG === "1";
158
201
  const fetchFn = globalThis.fetch;
@@ -168,6 +211,23 @@ async function createX402zServer(config) {
168
211
  if (wantsConfidential && !confidentialConfig) {
169
212
  throw new Error("confidential config is required when using erc7984-mind-v1 routes");
170
213
  }
214
+ const needsExactDefaults = Object.values(config.routes).some(
215
+ (route) => route.accepts.some((accept) => accept.scheme === "exact" && typeof accept.price !== "object")
216
+ );
217
+ let supportedExactDefaults = {};
218
+ if (wantsExact && needsExactDefaults) {
219
+ if (!fetchFn) {
220
+ throw new Error("fetch is required to load exact defaults from facilitator");
221
+ }
222
+ const supported = await fetchSupported(config.facilitatorUrl, fetchFn);
223
+ for (const kind of supported.kinds ?? []) {
224
+ if (kind.scheme !== "exact") continue;
225
+ const normalized = normalizeExactDefaults(kind.extra);
226
+ if (normalized) {
227
+ supportedExactDefaults[kind.network] = normalized;
228
+ }
229
+ }
230
+ }
171
231
  if (wantsConfidential && confidentialConfig) {
172
232
  let supportedDefaults = {};
173
233
  const needsSupportedDefaults = !confidentialConfig.getNetworkConfig && (!confidentialConfig.asset || !confidentialConfig.eip712 || !confidentialConfig.batcherAddress);
@@ -217,9 +277,39 @@ async function createX402zServer(config) {
217
277
  const rpcUrl = relayer ? getRelayerRpcUrl(relayer) : null;
218
278
  const routes = {};
219
279
  for (const [path, route] of Object.entries(config.routes)) {
280
+ const accepts = route.accepts.map((accept) => {
281
+ if (accept.scheme !== "exact") {
282
+ return accept;
283
+ }
284
+ if (typeof accept.price === "object") {
285
+ return accept;
286
+ }
287
+ const defaults = supportedExactDefaults[accept.network];
288
+ if (!defaults || defaults.length === 0) {
289
+ throw new Error(`Missing exact defaults for network ${accept.network}`);
290
+ }
291
+ if (defaults.length > 1) {
292
+ throw new Error(
293
+ `Multiple exact assets configured for ${accept.network}. Provide a Price object with an explicit asset.`
294
+ );
295
+ }
296
+ const decimalAmount = parseMoneyToDecimal(accept.price);
297
+ const amount = convertToTokenAmount(decimalAmount, defaults[0].decimals);
298
+ return {
299
+ ...accept,
300
+ price: {
301
+ amount,
302
+ asset: defaults[0].asset,
303
+ extra: {
304
+ name: defaults[0].name,
305
+ version: defaults[0].version
306
+ }
307
+ }
308
+ };
309
+ });
220
310
  routes[path] = {
221
311
  ...route,
222
- accepts: route.accepts
312
+ accepts
223
313
  };
224
314
  }
225
315
  const httpServer = new x402HTTPResourceServer(resourceServer, routes);
@@ -383,7 +473,150 @@ async function createX402zServer(config) {
383
473
  return server;
384
474
  }
385
475
 
476
+ // src/config/builders.ts
477
+ import { getSchemeConfigByName, isConfidentialTokenConfig } from "x402z-scheme-config";
478
+ function parseMoneyToDecimal2(money) {
479
+ if (typeof money === "number") {
480
+ return money;
481
+ }
482
+ if (typeof money !== "string") {
483
+ throw new Error("Invalid price type for exact scheme");
484
+ }
485
+ const clean = money.replace(/^\$/, "").trim();
486
+ const amount = Number.parseFloat(clean);
487
+ if (Number.isNaN(amount)) {
488
+ throw new Error(`Invalid money format: ${money}`);
489
+ }
490
+ return amount;
491
+ }
492
+ function convertToTokenAmount2(amount, decimals) {
493
+ const [intPart, decPart = ""] = String(amount).split(".");
494
+ const paddedDec = decPart.padEnd(decimals, "0").slice(0, decimals);
495
+ return (intPart + paddedDec).replace(/^0+/, "") || "0";
496
+ }
497
+ function buildExactAccept(config) {
498
+ const decimalAmount = parseMoneyToDecimal2(config.price);
499
+ const amount = convertToTokenAmount2(decimalAmount, config.decimals);
500
+ return {
501
+ scheme: "exact",
502
+ payTo: config.payTo,
503
+ price: {
504
+ amount,
505
+ asset: config.asset,
506
+ extra: {
507
+ name: config.eip712Name,
508
+ version: config.eip712Version
509
+ }
510
+ },
511
+ network: config.network,
512
+ ...config.maxTimeoutSeconds ? { maxTimeoutSeconds: config.maxTimeoutSeconds } : {}
513
+ };
514
+ }
515
+ function buildConfidentialAccept(config) {
516
+ return {
517
+ scheme: "erc7984-mind-v1",
518
+ payTo: config.payTo,
519
+ price: config.price,
520
+ network: config.network,
521
+ maxTimeoutSeconds: config.maxTimeoutSeconds ?? 300
522
+ };
523
+ }
524
+ function buildAcceptsFromConfigs(methods) {
525
+ const accepts = [];
526
+ for (const method of methods) {
527
+ const config = resolveMethodConfig(method.method);
528
+ const isConfidential = isConfidentialTokenConfig(config);
529
+ if (isConfidential) {
530
+ const confidential = method;
531
+ accepts.push(
532
+ buildConfidentialAccept({
533
+ network: config.network,
534
+ payTo: confidential.payTo,
535
+ price: confidential.price,
536
+ maxTimeoutSeconds: confidential.maxTimeoutSeconds
537
+ })
538
+ );
539
+ continue;
540
+ }
541
+ const exact = method;
542
+ accepts.push(
543
+ buildExactAccept({
544
+ network: config.network,
545
+ payTo: exact.payTo,
546
+ price: exact.price,
547
+ asset: config.asset,
548
+ decimals: config.decimals,
549
+ eip712Name: config.eip712.name,
550
+ eip712Version: config.eip712.version,
551
+ maxTimeoutSeconds: exact.maxTimeoutSeconds
552
+ })
553
+ );
554
+ }
555
+ return accepts;
556
+ }
557
+ function buildConfidentialServerConfig(config, runtime) {
558
+ return {
559
+ asset: config.asset,
560
+ eip712: {
561
+ name: config.eip712.name,
562
+ version: config.eip712.version
563
+ },
564
+ decimals: config.decimals,
565
+ batcherAddress: config.batcherAddress,
566
+ resourceHash: runtime.resourceHash ?? config.resourceHash,
567
+ signer: runtime.signer,
568
+ relayer: runtime.relayer
569
+ };
570
+ }
571
+ function buildConfidentialServerConfigFromConfig(input) {
572
+ if (!input.config) {
573
+ return void 0;
574
+ }
575
+ const resolved = resolveMethodConfig(input.config);
576
+ if (!isConfidentialTokenConfig(resolved)) {
577
+ throw new Error("Confidential config required for confidential server config");
578
+ }
579
+ return buildConfidentialServerConfig(
580
+ {
581
+ asset: resolved.asset,
582
+ eip712: {
583
+ name: resolved.eip712.name,
584
+ version: resolved.eip712.version
585
+ },
586
+ decimals: resolved.decimals,
587
+ batcherAddress: resolved.batcherAddress
588
+ },
589
+ {
590
+ signer: input.signer,
591
+ relayer: input.relayer,
592
+ resourceHash: input.resourceHash
593
+ }
594
+ );
595
+ }
596
+ function buildConfidentialServerConfigFromSchemeConfig(input) {
597
+ if (!input.config) {
598
+ return void 0;
599
+ }
600
+ return buildConfidentialServerConfigFromConfig(input);
601
+ }
602
+ function resolveMethodConfig(method) {
603
+ if (typeof method !== "string") {
604
+ return method;
605
+ }
606
+ const resolved = getSchemeConfigByName(method);
607
+ if (!resolved) {
608
+ throw new Error(`Unknown scheme config name: ${method}`);
609
+ }
610
+ return resolved;
611
+ }
612
+
386
613
  // src/index.ts
614
+ import {
615
+ SCHEME_CONFIG,
616
+ SCHEME_CONFIG_NAMES,
617
+ getSchemeConfigByName as getSchemeConfigByName2,
618
+ isConfidentialTokenConfig as isConfidentialTokenConfig2
619
+ } from "x402z-scheme-config";
387
620
  import { x402ResourceServer as x402ResourceServer2 } from "@x402/core/server";
388
621
  import {
389
622
  HTTPFacilitatorClient as HTTPFacilitatorClient2,
@@ -391,8 +624,17 @@ import {
391
624
  } from "@x402/core/http";
392
625
  export {
393
626
  HTTPFacilitatorClient2 as HTTPFacilitatorClient,
627
+ SCHEME_CONFIG,
628
+ SCHEME_CONFIG_NAMES,
394
629
  X402zEvmServerScheme,
630
+ buildAcceptsFromConfigs,
631
+ buildConfidentialAccept,
632
+ buildConfidentialServerConfig,
633
+ buildConfidentialServerConfigFromSchemeConfig,
634
+ buildExactAccept,
395
635
  createX402zServer,
636
+ getSchemeConfigByName2 as getSchemeConfigByName,
637
+ isConfidentialTokenConfig2 as isConfidentialTokenConfig,
396
638
  registerX402zEvmServerScheme,
397
639
  x402HTTPResourceServer2 as x402HTTPResourceServer,
398
640
  x402ResourceServer2 as x402ResourceServer
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "x402z-server",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "main": "./dist/index.js",
5
5
  "module": "./dist/index.mjs",
6
6
  "types": "./dist/index.d.ts",
@@ -8,10 +8,11 @@
8
8
  "dist"
9
9
  ],
10
10
  "dependencies": {
11
- "@x402/core": "^2.0.0",
12
- "@x402/evm": "^2.0.0",
11
+ "@x402/core": "^2.2.0",
12
+ "@x402/evm": "^2.2.0",
13
13
  "viem": "^2.43.3",
14
- "x402z-shared": "0.1.0"
14
+ "x402z-shared": "0.1.2",
15
+ "x402z-scheme-config": "0.1.2"
15
16
  },
16
17
  "devDependencies": {
17
18
  "jest": "^29.7.0",