x402z-server 0.1.0 → 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.
- package/README.md +81 -26
- package/dist/index.d.mts +80 -7
- package/dist/index.d.ts +80 -7
- package/dist/index.js +285 -2
- package/dist/index.mjs +281 -2
- package/package.json +5 -4
package/README.md
CHANGED
|
@@ -17,7 +17,12 @@ pnpm add x402z-server
|
|
|
17
17
|
## Usage
|
|
18
18
|
|
|
19
19
|
```ts
|
|
20
|
-
import {
|
|
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,34 +30,39 @@ 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
|
-
|
|
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",
|
|
61
|
+
onPaid: async () => ({
|
|
62
|
+
body: "demo content from server",
|
|
63
|
+
}),
|
|
51
64
|
},
|
|
52
65
|
},
|
|
53
|
-
onPaid: async () => ({
|
|
54
|
-
body: "demo content from server",
|
|
55
|
-
}),
|
|
56
66
|
});
|
|
57
67
|
|
|
58
68
|
server.listen(8080);
|
|
@@ -62,12 +72,12 @@ server.listen(8080);
|
|
|
62
72
|
|
|
63
73
|
- `createX402zServer(config)`
|
|
64
74
|
- `facilitatorUrl` (required): HTTP facilitator endpoint
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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
|
+
- `routes["METHOD /path"].onRequest`: optional pre-payment hook; return a response to short-circuit
|
|
80
|
+
- `routes["METHOD /path"].onPaid`: handler for successful payment (returns response body + optional headers)
|
|
71
81
|
|
|
72
82
|
## Examples
|
|
73
83
|
|
|
@@ -92,19 +102,54 @@ const server = await createX402zServer({
|
|
|
92
102
|
],
|
|
93
103
|
description: "Exact USDC payment demo",
|
|
94
104
|
mimeType: "text/plain",
|
|
105
|
+
onPaid: async () => ({ body: "paid content" }),
|
|
95
106
|
},
|
|
96
107
|
},
|
|
97
|
-
onPaid: async () => ({ body: "paid content" }),
|
|
98
108
|
});
|
|
99
109
|
|
|
100
110
|
server.listen(8090);
|
|
101
111
|
```
|
|
102
112
|
|
|
113
|
+
## Choosing Payment Methods (Server)
|
|
114
|
+
|
|
115
|
+
Server routes accept an ordered list of payment methods, each paired with its own `payTo` and `price`.
|
|
116
|
+
|
|
117
|
+
You can specify methods in two ways:
|
|
118
|
+
|
|
119
|
+
1) **Name string** (scheme + token + network alias):
|
|
120
|
+
```ts
|
|
121
|
+
const method = "exact.USDC.base";
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
2) **Config entry** (from `SCHEME_CONFIG`):
|
|
125
|
+
```ts
|
|
126
|
+
const method = SCHEME_CONFIG.exact.USDC.base;
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Build your `accepts` array from these entries:
|
|
130
|
+
```ts
|
|
131
|
+
const accepts = buildAcceptsFromConfigs([
|
|
132
|
+
{ method: "exact.USDC.base", payTo, price: "$0.01" },
|
|
133
|
+
{ method: SCHEME_CONFIG["erc7984-mind-v1"].CONFIDENTIAL_USDC.sepolia, payTo, price: "1.0" },
|
|
134
|
+
]);
|
|
135
|
+
```
|
|
136
|
+
|
|
103
137
|
## Notes
|
|
104
138
|
|
|
105
139
|
- Confidential scheme name: `erc7984-mind-v1`
|
|
106
140
|
- Exact scheme name: `exact`
|
|
107
141
|
- `confidential.batcherAddress` is required in requirements; clients bind encrypted inputs to it.
|
|
142
|
+
- 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`).
|
|
143
|
+
- 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`.
|
|
144
|
+
|
|
145
|
+
## Choosing Schemes/Networks
|
|
146
|
+
|
|
147
|
+
Select supported methods using `SCHEME_CONFIG` entries in your server code. Pair each entry with its own `payTo` and `price`.
|
|
148
|
+
|
|
149
|
+
## Scheme Naming Guidance
|
|
150
|
+
|
|
151
|
+
- 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).
|
|
152
|
+
- Introduce a new scheme name only when the payment flow differs (for example, different contract method, different typed data, or different authorization logic).
|
|
108
153
|
|
|
109
154
|
## API surface
|
|
110
155
|
|
|
@@ -112,6 +157,12 @@ Exports:
|
|
|
112
157
|
- `X402zEvmServerScheme`: server scheme implementation for erc7984-mind-v1.
|
|
113
158
|
- `registerX402zEvmServerScheme`: registers the scheme with x402 server.
|
|
114
159
|
- `createX402zServer`: helper to configure the paywalled server.
|
|
160
|
+
- `buildExactAccept`, `buildConfidentialAccept`, `buildConfidentialServerConfig`: helpers to construct accept items and confidential config.
|
|
161
|
+
- `buildAcceptsFromConfigs`: build `accepts` from `SCHEME_CONFIG` entries paired with `payTo`/`price`.
|
|
162
|
+
- `SCHEME_CONFIG`: full scheme/token/network config (addresses, network, decimals, EIP-712, batcher). Source of truth lives in `x402z-scheme-config`.
|
|
163
|
+
- `buildConfidentialServerConfigFromSchemeConfig`: build confidential server config from a `SCHEME_CONFIG` entry.
|
|
164
|
+
- `SCHEME_CONFIG_NAMES`: all supported method names as strings (e.g., `exact.USDC.base`).
|
|
165
|
+
- `getSchemeConfigByName`: resolve a method name string to its config entry.
|
|
115
166
|
- `x402ResourceServer`: core x402 resource server.
|
|
116
167
|
- `HTTPFacilitatorClient`: HTTP facilitator client wrapper.
|
|
117
168
|
- `x402HTTPResourceServer`: HTTP resource server helpers.
|
|
@@ -146,3 +197,7 @@ Types:
|
|
|
146
197
|
- `UnpaidResponseBody`: unpaid response shape.
|
|
147
198
|
- `UnpaidResponseResult`: unpaid response result.
|
|
148
199
|
- re-exported types from `@x402/core/types`: shared x402 types.
|
|
200
|
+
- `ExactTokenConfig`, `ConfidentialTokenConfig`: resolved token config for exact/confidential schemes.
|
|
201
|
+
- `ExactTokenSymbol`, `ConfidentialTokenSymbol`: allowed token symbols per scheme.
|
|
202
|
+
- `PaymentMethodPricing`: `{ method, payTo, price, maxTimeoutSeconds? }` for `buildAcceptsFromConfigs`.
|
|
203
|
+
- `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 = {
|
|
@@ -58,11 +60,6 @@ type X402zRouteAccept = {
|
|
|
58
60
|
type NoScheme = {
|
|
59
61
|
scheme?: never;
|
|
60
62
|
};
|
|
61
|
-
type X402zRouteOptions = NoScheme & {
|
|
62
|
-
accepts: X402zRouteAccept[];
|
|
63
|
-
description: string;
|
|
64
|
-
mimeType: string;
|
|
65
|
-
};
|
|
66
63
|
type X402zRouteHandler = (args: {
|
|
67
64
|
paymentRequirements: PaymentRequirements;
|
|
68
65
|
settleHeaders: Record<string, string>;
|
|
@@ -71,6 +68,27 @@ type X402zRouteHandler = (args: {
|
|
|
71
68
|
headers?: Record<string, string>;
|
|
72
69
|
body: string;
|
|
73
70
|
}>;
|
|
71
|
+
type X402zRequestHandler = (args: {
|
|
72
|
+
method: string;
|
|
73
|
+
path: string;
|
|
74
|
+
url: string;
|
|
75
|
+
headers: Record<string, string>;
|
|
76
|
+
}) => Promise<{
|
|
77
|
+
status?: number;
|
|
78
|
+
headers?: Record<string, string>;
|
|
79
|
+
body: string;
|
|
80
|
+
} | void> | {
|
|
81
|
+
status?: number;
|
|
82
|
+
headers?: Record<string, string>;
|
|
83
|
+
body: string;
|
|
84
|
+
} | void;
|
|
85
|
+
type X402zRouteOptions = NoScheme & {
|
|
86
|
+
accepts: X402zRouteAccept[];
|
|
87
|
+
description: string;
|
|
88
|
+
mimeType: string;
|
|
89
|
+
onRequest?: X402zRequestHandler;
|
|
90
|
+
onPaid: X402zRouteHandler;
|
|
91
|
+
};
|
|
74
92
|
type X402zBaseServerOptions = {
|
|
75
93
|
facilitatorUrl: string;
|
|
76
94
|
debug?: boolean;
|
|
@@ -99,9 +117,64 @@ type X402zConfidentialServerConfig = {
|
|
|
99
117
|
} & NoScheme;
|
|
100
118
|
type X402zServerOptions = X402zBaseServerOptions & {
|
|
101
119
|
routes: Record<string, X402zRouteOptions>;
|
|
102
|
-
onPaid: X402zRouteHandler;
|
|
103
120
|
confidential?: X402zConfidentialServerConfig;
|
|
104
121
|
} & NoScheme;
|
|
105
122
|
declare function createX402zServer(config: X402zServerOptions): Promise<http.Server<typeof http.IncomingMessage, typeof http.ServerResponse>>;
|
|
106
123
|
|
|
107
|
-
|
|
124
|
+
type ExactAcceptInput = {
|
|
125
|
+
network: Network;
|
|
126
|
+
payTo: string;
|
|
127
|
+
price: Price;
|
|
128
|
+
asset: `0x${string}`;
|
|
129
|
+
decimals: number;
|
|
130
|
+
eip712Name: string;
|
|
131
|
+
eip712Version: string;
|
|
132
|
+
maxTimeoutSeconds?: number;
|
|
133
|
+
};
|
|
134
|
+
type ConfidentialAcceptInput = {
|
|
135
|
+
network: Network;
|
|
136
|
+
payTo: string;
|
|
137
|
+
price: string;
|
|
138
|
+
maxTimeoutSeconds?: number;
|
|
139
|
+
};
|
|
140
|
+
type ExactMethodPricing = {
|
|
141
|
+
method: ExactTokenConfig | SchemeConfigName;
|
|
142
|
+
payTo: string;
|
|
143
|
+
price: Price;
|
|
144
|
+
maxTimeoutSeconds?: number;
|
|
145
|
+
};
|
|
146
|
+
type ConfidentialMethodPricing = {
|
|
147
|
+
method: ConfidentialTokenConfig | SchemeConfigName;
|
|
148
|
+
payTo: string;
|
|
149
|
+
price: string;
|
|
150
|
+
maxTimeoutSeconds?: number;
|
|
151
|
+
};
|
|
152
|
+
type PaymentMethodPricing = ExactMethodPricing | ConfidentialMethodPricing;
|
|
153
|
+
type ConfidentialRuntimeInput = {
|
|
154
|
+
signer: X402zConfidentialServerConfig["signer"];
|
|
155
|
+
relayer: X402zConfidentialServerConfig["relayer"];
|
|
156
|
+
resourceHash?: X402zConfidentialServerConfig["resourceHash"];
|
|
157
|
+
};
|
|
158
|
+
type ConfidentialConfigInput = {
|
|
159
|
+
asset: `0x${string}`;
|
|
160
|
+
eip712: {
|
|
161
|
+
name: string;
|
|
162
|
+
version: string;
|
|
163
|
+
};
|
|
164
|
+
decimals: number;
|
|
165
|
+
batcherAddress: `0x${string}`;
|
|
166
|
+
resourceHash?: `0x${string}`;
|
|
167
|
+
};
|
|
168
|
+
type BuildConfidentialServerConfigFromConfigInput = {
|
|
169
|
+
config: ConfidentialTokenConfig | SchemeConfigName | null | undefined;
|
|
170
|
+
signer: X402zConfidentialServerConfig["signer"];
|
|
171
|
+
relayer: X402zConfidentialServerConfig["relayer"];
|
|
172
|
+
resourceHash?: X402zConfidentialServerConfig["resourceHash"];
|
|
173
|
+
};
|
|
174
|
+
declare function buildExactAccept(config: ExactAcceptInput): X402zRouteAccept;
|
|
175
|
+
declare function buildConfidentialAccept(config: ConfidentialAcceptInput): X402zRouteAccept;
|
|
176
|
+
declare function buildAcceptsFromConfigs(methods: PaymentMethodPricing[]): X402zRouteAccept[];
|
|
177
|
+
declare function buildConfidentialServerConfig(config: ConfidentialConfigInput, runtime: ConfidentialRuntimeInput): X402zConfidentialServerConfig;
|
|
178
|
+
declare function buildConfidentialServerConfigFromSchemeConfig(input: BuildConfidentialServerConfigFromConfigInput): X402zConfidentialServerConfig | undefined;
|
|
179
|
+
|
|
180
|
+
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 = {
|
|
@@ -58,11 +60,6 @@ type X402zRouteAccept = {
|
|
|
58
60
|
type NoScheme = {
|
|
59
61
|
scheme?: never;
|
|
60
62
|
};
|
|
61
|
-
type X402zRouteOptions = NoScheme & {
|
|
62
|
-
accepts: X402zRouteAccept[];
|
|
63
|
-
description: string;
|
|
64
|
-
mimeType: string;
|
|
65
|
-
};
|
|
66
63
|
type X402zRouteHandler = (args: {
|
|
67
64
|
paymentRequirements: PaymentRequirements;
|
|
68
65
|
settleHeaders: Record<string, string>;
|
|
@@ -71,6 +68,27 @@ type X402zRouteHandler = (args: {
|
|
|
71
68
|
headers?: Record<string, string>;
|
|
72
69
|
body: string;
|
|
73
70
|
}>;
|
|
71
|
+
type X402zRequestHandler = (args: {
|
|
72
|
+
method: string;
|
|
73
|
+
path: string;
|
|
74
|
+
url: string;
|
|
75
|
+
headers: Record<string, string>;
|
|
76
|
+
}) => Promise<{
|
|
77
|
+
status?: number;
|
|
78
|
+
headers?: Record<string, string>;
|
|
79
|
+
body: string;
|
|
80
|
+
} | void> | {
|
|
81
|
+
status?: number;
|
|
82
|
+
headers?: Record<string, string>;
|
|
83
|
+
body: string;
|
|
84
|
+
} | void;
|
|
85
|
+
type X402zRouteOptions = NoScheme & {
|
|
86
|
+
accepts: X402zRouteAccept[];
|
|
87
|
+
description: string;
|
|
88
|
+
mimeType: string;
|
|
89
|
+
onRequest?: X402zRequestHandler;
|
|
90
|
+
onPaid: X402zRouteHandler;
|
|
91
|
+
};
|
|
74
92
|
type X402zBaseServerOptions = {
|
|
75
93
|
facilitatorUrl: string;
|
|
76
94
|
debug?: boolean;
|
|
@@ -99,9 +117,64 @@ type X402zConfidentialServerConfig = {
|
|
|
99
117
|
} & NoScheme;
|
|
100
118
|
type X402zServerOptions = X402zBaseServerOptions & {
|
|
101
119
|
routes: Record<string, X402zRouteOptions>;
|
|
102
|
-
onPaid: X402zRouteHandler;
|
|
103
120
|
confidential?: X402zConfidentialServerConfig;
|
|
104
121
|
} & NoScheme;
|
|
105
122
|
declare function createX402zServer(config: X402zServerOptions): Promise<http.Server<typeof http.IncomingMessage, typeof http.ServerResponse>>;
|
|
106
123
|
|
|
107
|
-
|
|
124
|
+
type ExactAcceptInput = {
|
|
125
|
+
network: Network;
|
|
126
|
+
payTo: string;
|
|
127
|
+
price: Price;
|
|
128
|
+
asset: `0x${string}`;
|
|
129
|
+
decimals: number;
|
|
130
|
+
eip712Name: string;
|
|
131
|
+
eip712Version: string;
|
|
132
|
+
maxTimeoutSeconds?: number;
|
|
133
|
+
};
|
|
134
|
+
type ConfidentialAcceptInput = {
|
|
135
|
+
network: Network;
|
|
136
|
+
payTo: string;
|
|
137
|
+
price: string;
|
|
138
|
+
maxTimeoutSeconds?: number;
|
|
139
|
+
};
|
|
140
|
+
type ExactMethodPricing = {
|
|
141
|
+
method: ExactTokenConfig | SchemeConfigName;
|
|
142
|
+
payTo: string;
|
|
143
|
+
price: Price;
|
|
144
|
+
maxTimeoutSeconds?: number;
|
|
145
|
+
};
|
|
146
|
+
type ConfidentialMethodPricing = {
|
|
147
|
+
method: ConfidentialTokenConfig | SchemeConfigName;
|
|
148
|
+
payTo: string;
|
|
149
|
+
price: string;
|
|
150
|
+
maxTimeoutSeconds?: number;
|
|
151
|
+
};
|
|
152
|
+
type PaymentMethodPricing = ExactMethodPricing | ConfidentialMethodPricing;
|
|
153
|
+
type ConfidentialRuntimeInput = {
|
|
154
|
+
signer: X402zConfidentialServerConfig["signer"];
|
|
155
|
+
relayer: X402zConfidentialServerConfig["relayer"];
|
|
156
|
+
resourceHash?: X402zConfidentialServerConfig["resourceHash"];
|
|
157
|
+
};
|
|
158
|
+
type ConfidentialConfigInput = {
|
|
159
|
+
asset: `0x${string}`;
|
|
160
|
+
eip712: {
|
|
161
|
+
name: string;
|
|
162
|
+
version: string;
|
|
163
|
+
};
|
|
164
|
+
decimals: number;
|
|
165
|
+
batcherAddress: `0x${string}`;
|
|
166
|
+
resourceHash?: `0x${string}`;
|
|
167
|
+
};
|
|
168
|
+
type BuildConfidentialServerConfigFromConfigInput = {
|
|
169
|
+
config: ConfidentialTokenConfig | SchemeConfigName | null | undefined;
|
|
170
|
+
signer: X402zConfidentialServerConfig["signer"];
|
|
171
|
+
relayer: X402zConfidentialServerConfig["relayer"];
|
|
172
|
+
resourceHash?: X402zConfidentialServerConfig["resourceHash"];
|
|
173
|
+
};
|
|
174
|
+
declare function buildExactAccept(config: ExactAcceptInput): X402zRouteAccept;
|
|
175
|
+
declare function buildConfidentialAccept(config: ConfidentialAcceptInput): X402zRouteAccept;
|
|
176
|
+
declare function buildAcceptsFromConfigs(methods: PaymentMethodPricing[]): X402zRouteAccept[];
|
|
177
|
+
declare function buildConfidentialServerConfig(config: ConfidentialConfigInput, runtime: ConfidentialRuntimeInput): X402zConfidentialServerConfig;
|
|
178
|
+
declare function buildConfidentialServerConfigFromSchemeConfig(input: BuildConfidentialServerConfigFromConfigInput): X402zConfidentialServerConfig | undefined;
|
|
179
|
+
|
|
180
|
+
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
|
|
348
|
+
accepts
|
|
250
349
|
};
|
|
251
350
|
}
|
|
252
351
|
const httpServer = new import_http.x402HTTPResourceServer(resourceServer, routes);
|
|
@@ -255,6 +354,35 @@ async function createX402zServer(config) {
|
|
|
255
354
|
const adapter = buildAdapter(req);
|
|
256
355
|
const method = adapter.getMethod();
|
|
257
356
|
const path = adapter.getPath();
|
|
357
|
+
const routeKey = `${method} ${path}`;
|
|
358
|
+
const routeConfig = config.routes[routeKey];
|
|
359
|
+
if (routeConfig?.onRequest) {
|
|
360
|
+
const headers = {};
|
|
361
|
+
for (const [key, value] of Object.entries(req.headers)) {
|
|
362
|
+
if (value === void 0) continue;
|
|
363
|
+
headers[key.toLowerCase()] = Array.isArray(value) ? value.join(",") : value;
|
|
364
|
+
}
|
|
365
|
+
const response = await routeConfig.onRequest({
|
|
366
|
+
method,
|
|
367
|
+
path,
|
|
368
|
+
url: adapter.getUrl(),
|
|
369
|
+
headers
|
|
370
|
+
});
|
|
371
|
+
if (response) {
|
|
372
|
+
const status = response.status ?? 200;
|
|
373
|
+
const responseHeaders = {
|
|
374
|
+
...response.headers ?? {}
|
|
375
|
+
};
|
|
376
|
+
if (!responseHeaders["Content-Type"] && !responseHeaders["content-type"]) {
|
|
377
|
+
responseHeaders["Content-Type"] = "text/plain";
|
|
378
|
+
}
|
|
379
|
+
sendText(res, status, responseHeaders, response.body);
|
|
380
|
+
if (debugEnabled) {
|
|
381
|
+
console.debug(`[server] ${method} ${path} -> ${status} (onRequest)`);
|
|
382
|
+
}
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
258
386
|
const paymentHeader = adapter.getHeader("payment-signature") ?? adapter.getHeader("x402-payment") ?? adapter.getHeader("x-payment");
|
|
259
387
|
if (debugEnabled) {
|
|
260
388
|
console.debug(`[server] ${method} ${path}`);
|
|
@@ -371,7 +499,15 @@ async function createX402zServer(config) {
|
|
|
371
499
|
}
|
|
372
500
|
}
|
|
373
501
|
const settleHeaders = settle.headers ?? {};
|
|
374
|
-
|
|
502
|
+
if (!routeConfig) {
|
|
503
|
+
res.writeHead(500, { ...corsHeaders, "Content-Type": "application/json" });
|
|
504
|
+
res.end(JSON.stringify({ error: "route_config_missing" }));
|
|
505
|
+
if (debugEnabled) {
|
|
506
|
+
console.debug(`[server] ${method} ${path} -> 500 route_config_missing`);
|
|
507
|
+
}
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
510
|
+
const payload = await routeConfig.onPaid({
|
|
375
511
|
paymentRequirements: result.paymentRequirements,
|
|
376
512
|
settleHeaders
|
|
377
513
|
});
|
|
@@ -410,14 +546,161 @@ async function createX402zServer(config) {
|
|
|
410
546
|
return server;
|
|
411
547
|
}
|
|
412
548
|
|
|
549
|
+
// src/config/builders.ts
|
|
550
|
+
var import_x402z_scheme_config = require("x402z-scheme-config");
|
|
551
|
+
function parseMoneyToDecimal2(money) {
|
|
552
|
+
if (typeof money === "number") {
|
|
553
|
+
return money;
|
|
554
|
+
}
|
|
555
|
+
if (typeof money !== "string") {
|
|
556
|
+
throw new Error("Invalid price type for exact scheme");
|
|
557
|
+
}
|
|
558
|
+
const clean = money.replace(/^\$/, "").trim();
|
|
559
|
+
const amount = Number.parseFloat(clean);
|
|
560
|
+
if (Number.isNaN(amount)) {
|
|
561
|
+
throw new Error(`Invalid money format: ${money}`);
|
|
562
|
+
}
|
|
563
|
+
return amount;
|
|
564
|
+
}
|
|
565
|
+
function convertToTokenAmount2(amount, decimals) {
|
|
566
|
+
const [intPart, decPart = ""] = String(amount).split(".");
|
|
567
|
+
const paddedDec = decPart.padEnd(decimals, "0").slice(0, decimals);
|
|
568
|
+
return (intPart + paddedDec).replace(/^0+/, "") || "0";
|
|
569
|
+
}
|
|
570
|
+
function buildExactAccept(config) {
|
|
571
|
+
const decimalAmount = parseMoneyToDecimal2(config.price);
|
|
572
|
+
const amount = convertToTokenAmount2(decimalAmount, config.decimals);
|
|
573
|
+
return {
|
|
574
|
+
scheme: "exact",
|
|
575
|
+
payTo: config.payTo,
|
|
576
|
+
price: {
|
|
577
|
+
amount,
|
|
578
|
+
asset: config.asset,
|
|
579
|
+
extra: {
|
|
580
|
+
name: config.eip712Name,
|
|
581
|
+
version: config.eip712Version
|
|
582
|
+
}
|
|
583
|
+
},
|
|
584
|
+
network: config.network,
|
|
585
|
+
...config.maxTimeoutSeconds ? { maxTimeoutSeconds: config.maxTimeoutSeconds } : {}
|
|
586
|
+
};
|
|
587
|
+
}
|
|
588
|
+
function buildConfidentialAccept(config) {
|
|
589
|
+
return {
|
|
590
|
+
scheme: "erc7984-mind-v1",
|
|
591
|
+
payTo: config.payTo,
|
|
592
|
+
price: config.price,
|
|
593
|
+
network: config.network,
|
|
594
|
+
maxTimeoutSeconds: config.maxTimeoutSeconds ?? 300
|
|
595
|
+
};
|
|
596
|
+
}
|
|
597
|
+
function buildAcceptsFromConfigs(methods) {
|
|
598
|
+
const accepts = [];
|
|
599
|
+
for (const method of methods) {
|
|
600
|
+
const config = resolveMethodConfig(method.method);
|
|
601
|
+
const isConfidential = (0, import_x402z_scheme_config.isConfidentialTokenConfig)(config);
|
|
602
|
+
if (isConfidential) {
|
|
603
|
+
const confidential = method;
|
|
604
|
+
accepts.push(
|
|
605
|
+
buildConfidentialAccept({
|
|
606
|
+
network: config.network,
|
|
607
|
+
payTo: confidential.payTo,
|
|
608
|
+
price: confidential.price,
|
|
609
|
+
maxTimeoutSeconds: confidential.maxTimeoutSeconds
|
|
610
|
+
})
|
|
611
|
+
);
|
|
612
|
+
continue;
|
|
613
|
+
}
|
|
614
|
+
const exact = method;
|
|
615
|
+
accepts.push(
|
|
616
|
+
buildExactAccept({
|
|
617
|
+
network: config.network,
|
|
618
|
+
payTo: exact.payTo,
|
|
619
|
+
price: exact.price,
|
|
620
|
+
asset: config.asset,
|
|
621
|
+
decimals: config.decimals,
|
|
622
|
+
eip712Name: config.eip712.name,
|
|
623
|
+
eip712Version: config.eip712.version,
|
|
624
|
+
maxTimeoutSeconds: exact.maxTimeoutSeconds
|
|
625
|
+
})
|
|
626
|
+
);
|
|
627
|
+
}
|
|
628
|
+
return accepts;
|
|
629
|
+
}
|
|
630
|
+
function buildConfidentialServerConfig(config, runtime) {
|
|
631
|
+
return {
|
|
632
|
+
asset: config.asset,
|
|
633
|
+
eip712: {
|
|
634
|
+
name: config.eip712.name,
|
|
635
|
+
version: config.eip712.version
|
|
636
|
+
},
|
|
637
|
+
decimals: config.decimals,
|
|
638
|
+
batcherAddress: config.batcherAddress,
|
|
639
|
+
resourceHash: runtime.resourceHash ?? config.resourceHash,
|
|
640
|
+
signer: runtime.signer,
|
|
641
|
+
relayer: runtime.relayer
|
|
642
|
+
};
|
|
643
|
+
}
|
|
644
|
+
function buildConfidentialServerConfigFromConfig(input) {
|
|
645
|
+
if (!input.config) {
|
|
646
|
+
return void 0;
|
|
647
|
+
}
|
|
648
|
+
const resolved = resolveMethodConfig(input.config);
|
|
649
|
+
if (!(0, import_x402z_scheme_config.isConfidentialTokenConfig)(resolved)) {
|
|
650
|
+
throw new Error("Confidential config required for confidential server config");
|
|
651
|
+
}
|
|
652
|
+
return buildConfidentialServerConfig(
|
|
653
|
+
{
|
|
654
|
+
asset: resolved.asset,
|
|
655
|
+
eip712: {
|
|
656
|
+
name: resolved.eip712.name,
|
|
657
|
+
version: resolved.eip712.version
|
|
658
|
+
},
|
|
659
|
+
decimals: resolved.decimals,
|
|
660
|
+
batcherAddress: resolved.batcherAddress
|
|
661
|
+
},
|
|
662
|
+
{
|
|
663
|
+
signer: input.signer,
|
|
664
|
+
relayer: input.relayer,
|
|
665
|
+
resourceHash: input.resourceHash
|
|
666
|
+
}
|
|
667
|
+
);
|
|
668
|
+
}
|
|
669
|
+
function buildConfidentialServerConfigFromSchemeConfig(input) {
|
|
670
|
+
if (!input.config) {
|
|
671
|
+
return void 0;
|
|
672
|
+
}
|
|
673
|
+
return buildConfidentialServerConfigFromConfig(input);
|
|
674
|
+
}
|
|
675
|
+
function resolveMethodConfig(method) {
|
|
676
|
+
if (typeof method !== "string") {
|
|
677
|
+
return method;
|
|
678
|
+
}
|
|
679
|
+
const resolved = (0, import_x402z_scheme_config.getSchemeConfigByName)(method);
|
|
680
|
+
if (!resolved) {
|
|
681
|
+
throw new Error(`Unknown scheme config name: ${method}`);
|
|
682
|
+
}
|
|
683
|
+
return resolved;
|
|
684
|
+
}
|
|
685
|
+
|
|
413
686
|
// src/index.ts
|
|
687
|
+
var import_x402z_scheme_config2 = require("x402z-scheme-config");
|
|
414
688
|
var import_server4 = require("@x402/core/server");
|
|
415
689
|
var import_http2 = require("@x402/core/http");
|
|
416
690
|
// Annotate the CommonJS export names for ESM import in node:
|
|
417
691
|
0 && (module.exports = {
|
|
418
692
|
HTTPFacilitatorClient,
|
|
693
|
+
SCHEME_CONFIG,
|
|
694
|
+
SCHEME_CONFIG_NAMES,
|
|
419
695
|
X402zEvmServerScheme,
|
|
696
|
+
buildAcceptsFromConfigs,
|
|
697
|
+
buildConfidentialAccept,
|
|
698
|
+
buildConfidentialServerConfig,
|
|
699
|
+
buildConfidentialServerConfigFromSchemeConfig,
|
|
700
|
+
buildExactAccept,
|
|
420
701
|
createX402zServer,
|
|
702
|
+
getSchemeConfigByName,
|
|
703
|
+
isConfidentialTokenConfig,
|
|
421
704
|
registerX402zEvmServerScheme,
|
|
422
705
|
x402HTTPResourceServer,
|
|
423
706
|
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
|
|
312
|
+
accepts
|
|
223
313
|
};
|
|
224
314
|
}
|
|
225
315
|
const httpServer = new x402HTTPResourceServer(resourceServer, routes);
|
|
@@ -228,6 +318,35 @@ async function createX402zServer(config) {
|
|
|
228
318
|
const adapter = buildAdapter(req);
|
|
229
319
|
const method = adapter.getMethod();
|
|
230
320
|
const path = adapter.getPath();
|
|
321
|
+
const routeKey = `${method} ${path}`;
|
|
322
|
+
const routeConfig = config.routes[routeKey];
|
|
323
|
+
if (routeConfig?.onRequest) {
|
|
324
|
+
const headers = {};
|
|
325
|
+
for (const [key, value] of Object.entries(req.headers)) {
|
|
326
|
+
if (value === void 0) continue;
|
|
327
|
+
headers[key.toLowerCase()] = Array.isArray(value) ? value.join(",") : value;
|
|
328
|
+
}
|
|
329
|
+
const response = await routeConfig.onRequest({
|
|
330
|
+
method,
|
|
331
|
+
path,
|
|
332
|
+
url: adapter.getUrl(),
|
|
333
|
+
headers
|
|
334
|
+
});
|
|
335
|
+
if (response) {
|
|
336
|
+
const status = response.status ?? 200;
|
|
337
|
+
const responseHeaders = {
|
|
338
|
+
...response.headers ?? {}
|
|
339
|
+
};
|
|
340
|
+
if (!responseHeaders["Content-Type"] && !responseHeaders["content-type"]) {
|
|
341
|
+
responseHeaders["Content-Type"] = "text/plain";
|
|
342
|
+
}
|
|
343
|
+
sendText(res, status, responseHeaders, response.body);
|
|
344
|
+
if (debugEnabled) {
|
|
345
|
+
console.debug(`[server] ${method} ${path} -> ${status} (onRequest)`);
|
|
346
|
+
}
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
231
350
|
const paymentHeader = adapter.getHeader("payment-signature") ?? adapter.getHeader("x402-payment") ?? adapter.getHeader("x-payment");
|
|
232
351
|
if (debugEnabled) {
|
|
233
352
|
console.debug(`[server] ${method} ${path}`);
|
|
@@ -344,7 +463,15 @@ async function createX402zServer(config) {
|
|
|
344
463
|
}
|
|
345
464
|
}
|
|
346
465
|
const settleHeaders = settle.headers ?? {};
|
|
347
|
-
|
|
466
|
+
if (!routeConfig) {
|
|
467
|
+
res.writeHead(500, { ...corsHeaders, "Content-Type": "application/json" });
|
|
468
|
+
res.end(JSON.stringify({ error: "route_config_missing" }));
|
|
469
|
+
if (debugEnabled) {
|
|
470
|
+
console.debug(`[server] ${method} ${path} -> 500 route_config_missing`);
|
|
471
|
+
}
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
474
|
+
const payload = await routeConfig.onPaid({
|
|
348
475
|
paymentRequirements: result.paymentRequirements,
|
|
349
476
|
settleHeaders
|
|
350
477
|
});
|
|
@@ -383,7 +510,150 @@ async function createX402zServer(config) {
|
|
|
383
510
|
return server;
|
|
384
511
|
}
|
|
385
512
|
|
|
513
|
+
// src/config/builders.ts
|
|
514
|
+
import { getSchemeConfigByName, isConfidentialTokenConfig } from "x402z-scheme-config";
|
|
515
|
+
function parseMoneyToDecimal2(money) {
|
|
516
|
+
if (typeof money === "number") {
|
|
517
|
+
return money;
|
|
518
|
+
}
|
|
519
|
+
if (typeof money !== "string") {
|
|
520
|
+
throw new Error("Invalid price type for exact scheme");
|
|
521
|
+
}
|
|
522
|
+
const clean = money.replace(/^\$/, "").trim();
|
|
523
|
+
const amount = Number.parseFloat(clean);
|
|
524
|
+
if (Number.isNaN(amount)) {
|
|
525
|
+
throw new Error(`Invalid money format: ${money}`);
|
|
526
|
+
}
|
|
527
|
+
return amount;
|
|
528
|
+
}
|
|
529
|
+
function convertToTokenAmount2(amount, decimals) {
|
|
530
|
+
const [intPart, decPart = ""] = String(amount).split(".");
|
|
531
|
+
const paddedDec = decPart.padEnd(decimals, "0").slice(0, decimals);
|
|
532
|
+
return (intPart + paddedDec).replace(/^0+/, "") || "0";
|
|
533
|
+
}
|
|
534
|
+
function buildExactAccept(config) {
|
|
535
|
+
const decimalAmount = parseMoneyToDecimal2(config.price);
|
|
536
|
+
const amount = convertToTokenAmount2(decimalAmount, config.decimals);
|
|
537
|
+
return {
|
|
538
|
+
scheme: "exact",
|
|
539
|
+
payTo: config.payTo,
|
|
540
|
+
price: {
|
|
541
|
+
amount,
|
|
542
|
+
asset: config.asset,
|
|
543
|
+
extra: {
|
|
544
|
+
name: config.eip712Name,
|
|
545
|
+
version: config.eip712Version
|
|
546
|
+
}
|
|
547
|
+
},
|
|
548
|
+
network: config.network,
|
|
549
|
+
...config.maxTimeoutSeconds ? { maxTimeoutSeconds: config.maxTimeoutSeconds } : {}
|
|
550
|
+
};
|
|
551
|
+
}
|
|
552
|
+
function buildConfidentialAccept(config) {
|
|
553
|
+
return {
|
|
554
|
+
scheme: "erc7984-mind-v1",
|
|
555
|
+
payTo: config.payTo,
|
|
556
|
+
price: config.price,
|
|
557
|
+
network: config.network,
|
|
558
|
+
maxTimeoutSeconds: config.maxTimeoutSeconds ?? 300
|
|
559
|
+
};
|
|
560
|
+
}
|
|
561
|
+
function buildAcceptsFromConfigs(methods) {
|
|
562
|
+
const accepts = [];
|
|
563
|
+
for (const method of methods) {
|
|
564
|
+
const config = resolveMethodConfig(method.method);
|
|
565
|
+
const isConfidential = isConfidentialTokenConfig(config);
|
|
566
|
+
if (isConfidential) {
|
|
567
|
+
const confidential = method;
|
|
568
|
+
accepts.push(
|
|
569
|
+
buildConfidentialAccept({
|
|
570
|
+
network: config.network,
|
|
571
|
+
payTo: confidential.payTo,
|
|
572
|
+
price: confidential.price,
|
|
573
|
+
maxTimeoutSeconds: confidential.maxTimeoutSeconds
|
|
574
|
+
})
|
|
575
|
+
);
|
|
576
|
+
continue;
|
|
577
|
+
}
|
|
578
|
+
const exact = method;
|
|
579
|
+
accepts.push(
|
|
580
|
+
buildExactAccept({
|
|
581
|
+
network: config.network,
|
|
582
|
+
payTo: exact.payTo,
|
|
583
|
+
price: exact.price,
|
|
584
|
+
asset: config.asset,
|
|
585
|
+
decimals: config.decimals,
|
|
586
|
+
eip712Name: config.eip712.name,
|
|
587
|
+
eip712Version: config.eip712.version,
|
|
588
|
+
maxTimeoutSeconds: exact.maxTimeoutSeconds
|
|
589
|
+
})
|
|
590
|
+
);
|
|
591
|
+
}
|
|
592
|
+
return accepts;
|
|
593
|
+
}
|
|
594
|
+
function buildConfidentialServerConfig(config, runtime) {
|
|
595
|
+
return {
|
|
596
|
+
asset: config.asset,
|
|
597
|
+
eip712: {
|
|
598
|
+
name: config.eip712.name,
|
|
599
|
+
version: config.eip712.version
|
|
600
|
+
},
|
|
601
|
+
decimals: config.decimals,
|
|
602
|
+
batcherAddress: config.batcherAddress,
|
|
603
|
+
resourceHash: runtime.resourceHash ?? config.resourceHash,
|
|
604
|
+
signer: runtime.signer,
|
|
605
|
+
relayer: runtime.relayer
|
|
606
|
+
};
|
|
607
|
+
}
|
|
608
|
+
function buildConfidentialServerConfigFromConfig(input) {
|
|
609
|
+
if (!input.config) {
|
|
610
|
+
return void 0;
|
|
611
|
+
}
|
|
612
|
+
const resolved = resolveMethodConfig(input.config);
|
|
613
|
+
if (!isConfidentialTokenConfig(resolved)) {
|
|
614
|
+
throw new Error("Confidential config required for confidential server config");
|
|
615
|
+
}
|
|
616
|
+
return buildConfidentialServerConfig(
|
|
617
|
+
{
|
|
618
|
+
asset: resolved.asset,
|
|
619
|
+
eip712: {
|
|
620
|
+
name: resolved.eip712.name,
|
|
621
|
+
version: resolved.eip712.version
|
|
622
|
+
},
|
|
623
|
+
decimals: resolved.decimals,
|
|
624
|
+
batcherAddress: resolved.batcherAddress
|
|
625
|
+
},
|
|
626
|
+
{
|
|
627
|
+
signer: input.signer,
|
|
628
|
+
relayer: input.relayer,
|
|
629
|
+
resourceHash: input.resourceHash
|
|
630
|
+
}
|
|
631
|
+
);
|
|
632
|
+
}
|
|
633
|
+
function buildConfidentialServerConfigFromSchemeConfig(input) {
|
|
634
|
+
if (!input.config) {
|
|
635
|
+
return void 0;
|
|
636
|
+
}
|
|
637
|
+
return buildConfidentialServerConfigFromConfig(input);
|
|
638
|
+
}
|
|
639
|
+
function resolveMethodConfig(method) {
|
|
640
|
+
if (typeof method !== "string") {
|
|
641
|
+
return method;
|
|
642
|
+
}
|
|
643
|
+
const resolved = getSchemeConfigByName(method);
|
|
644
|
+
if (!resolved) {
|
|
645
|
+
throw new Error(`Unknown scheme config name: ${method}`);
|
|
646
|
+
}
|
|
647
|
+
return resolved;
|
|
648
|
+
}
|
|
649
|
+
|
|
386
650
|
// src/index.ts
|
|
651
|
+
import {
|
|
652
|
+
SCHEME_CONFIG,
|
|
653
|
+
SCHEME_CONFIG_NAMES,
|
|
654
|
+
getSchemeConfigByName as getSchemeConfigByName2,
|
|
655
|
+
isConfidentialTokenConfig as isConfidentialTokenConfig2
|
|
656
|
+
} from "x402z-scheme-config";
|
|
387
657
|
import { x402ResourceServer as x402ResourceServer2 } from "@x402/core/server";
|
|
388
658
|
import {
|
|
389
659
|
HTTPFacilitatorClient as HTTPFacilitatorClient2,
|
|
@@ -391,8 +661,17 @@ import {
|
|
|
391
661
|
} from "@x402/core/http";
|
|
392
662
|
export {
|
|
393
663
|
HTTPFacilitatorClient2 as HTTPFacilitatorClient,
|
|
664
|
+
SCHEME_CONFIG,
|
|
665
|
+
SCHEME_CONFIG_NAMES,
|
|
394
666
|
X402zEvmServerScheme,
|
|
667
|
+
buildAcceptsFromConfigs,
|
|
668
|
+
buildConfidentialAccept,
|
|
669
|
+
buildConfidentialServerConfig,
|
|
670
|
+
buildConfidentialServerConfigFromSchemeConfig,
|
|
671
|
+
buildExactAccept,
|
|
395
672
|
createX402zServer,
|
|
673
|
+
getSchemeConfigByName2 as getSchemeConfigByName,
|
|
674
|
+
isConfidentialTokenConfig2 as isConfidentialTokenConfig,
|
|
396
675
|
registerX402zEvmServerScheme,
|
|
397
676
|
x402HTTPResourceServer2 as x402HTTPResourceServer,
|
|
398
677
|
x402ResourceServer2 as x402ResourceServer
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "x402z-server",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
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.
|
|
12
|
-
"@x402/evm": "^2.
|
|
11
|
+
"@x402/core": "^2.2.0",
|
|
12
|
+
"@x402/evm": "^2.2.0",
|
|
13
13
|
"viem": "^2.43.3",
|
|
14
|
-
"x402z-shared": "0.1.
|
|
14
|
+
"x402z-shared": "0.1.2",
|
|
15
|
+
"x402z-scheme-config": "0.1.2"
|
|
15
16
|
},
|
|
16
17
|
"devDependencies": {
|
|
17
18
|
"jest": "^29.7.0",
|