x402z-client-web 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 +117 -0
- package/dist/index.d.mts +67 -0
- package/dist/index.d.ts +67 -0
- package/dist/index.js +335 -0
- package/dist/index.mjs +313 -0
- package/package.json +33 -0
package/README.md
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# x402z-client-web
|
|
2
|
+
|
|
3
|
+
Browser helpers for the erc7984-mind-v1 x402 scheme.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add x402z-client-web x402z-shared-web
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Folder map
|
|
12
|
+
|
|
13
|
+
- `src/http/`: HTTP client helpers
|
|
14
|
+
- `src/scheme/`: scheme implementation + registration
|
|
15
|
+
- `src/index.ts`: public exports
|
|
16
|
+
|
|
17
|
+
## Usage (Browser)
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
import { createX402zClient } from "x402z-client-web";
|
|
21
|
+
import {
|
|
22
|
+
createRelayer,
|
|
23
|
+
createBrowserSigner,
|
|
24
|
+
getBrowserEthereumProvider,
|
|
25
|
+
getSepoliaConfig,
|
|
26
|
+
} from "x402z-shared-web";
|
|
27
|
+
import type { FhevmInstanceConfig } from "x402z-shared-web";
|
|
28
|
+
|
|
29
|
+
const provider = getBrowserEthereumProvider();
|
|
30
|
+
const relayerConfig: FhevmInstanceConfig = {
|
|
31
|
+
...(getSepoliaConfig() as object),
|
|
32
|
+
network: provider,
|
|
33
|
+
};
|
|
34
|
+
const relayer = await createRelayer(relayerConfig);
|
|
35
|
+
const signer = await createBrowserSigner(provider);
|
|
36
|
+
|
|
37
|
+
const client = createX402zClient({
|
|
38
|
+
preferredMethods: ["erc7984-mind-v1.CONFIDENTIAL_USDC.sepolia"] as const,
|
|
39
|
+
signer,
|
|
40
|
+
relayer,
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const paid = await client.pay("https://example.com/demo");
|
|
44
|
+
console.log(paid.response.status);
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
`createX402zClient` builds the confidential payment input automatically using the
|
|
48
|
+
`confidential.batcherAddress` provided by the server’s payment requirements.
|
|
49
|
+
`createRelayer` calls `initSDK()` automatically on first use.
|
|
50
|
+
|
|
51
|
+
## Choosing Payment Methods (Client)
|
|
52
|
+
|
|
53
|
+
The client picks the **first** preferred method that matches the server’s `accepts` list
|
|
54
|
+
by `scheme + network + asset`.
|
|
55
|
+
|
|
56
|
+
You can specify preferred methods in two ways:
|
|
57
|
+
|
|
58
|
+
1) **Name string** (scheme + token + network alias):
|
|
59
|
+
```ts
|
|
60
|
+
preferredMethods: ["exact.USDC.base", "erc7984-mind-v1.CONFIDENTIAL_USDC.sepolia"] as const
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
2) **Config entry** (from `SCHEME_CONFIG`):
|
|
64
|
+
```ts
|
|
65
|
+
import { SCHEME_CONFIG } from "x402z-scheme-config";
|
|
66
|
+
|
|
67
|
+
preferredMethods: [
|
|
68
|
+
SCHEME_CONFIG.exact.USDC.base,
|
|
69
|
+
SCHEME_CONFIG["erc7984-mind-v1"].CONFIDENTIAL_USDC.sepolia,
|
|
70
|
+
]
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
If `preferredMethods` is omitted, the client defaults to `SCHEME_CONFIG_NAMES`
|
|
74
|
+
(all known methods, in order).
|
|
75
|
+
|
|
76
|
+
## API
|
|
77
|
+
|
|
78
|
+
- `createX402zClient(config)`
|
|
79
|
+
- `preferredMethods` (optional): ordered list of preferred methods.
|
|
80
|
+
- Name form: `"exact.USDC.base"` (matches scheme/token/network alias from `x402z-scheme-config`).
|
|
81
|
+
- Config form: `SCHEME_CONFIG.exact.USDC.base`.
|
|
82
|
+
- `signer` (required): signer used for both exact and confidential schemes
|
|
83
|
+
- `relayer` (required when using `erc7984-mind-v1`): relayer instance (browser)
|
|
84
|
+
- `fetch` (optional): custom fetch implementation
|
|
85
|
+
- `client.pay(url, options?)`: performs the 402 handshake and retries with payment headers
|
|
86
|
+
- returns `{ response, paymentRequired?, confidentialRequirements? }`
|
|
87
|
+
|
|
88
|
+
## Examples
|
|
89
|
+
|
|
90
|
+
See `examples/README.md` for the full-process web examples (`examples/full-process/web-vite` and `examples/full-process/web-next`).
|
|
91
|
+
|
|
92
|
+
## Notes
|
|
93
|
+
|
|
94
|
+
- Web builds use `@zama-fhe/relayer-sdk/web` (no CDN script tag or bundle).
|
|
95
|
+
- Requires an injected wallet that supports `eth_signTypedData_v4` (e.g. MetaMask).
|
|
96
|
+
- Confidential scheme name: `erc7984-mind-v1`
|
|
97
|
+
- Exact scheme name: `exact`
|
|
98
|
+
|
|
99
|
+
## API surface
|
|
100
|
+
|
|
101
|
+
Exports:
|
|
102
|
+
- `createX402zClient`: convenience client wrapper for browser pay flows.
|
|
103
|
+
- `X402zEvmClientScheme`: client scheme implementation for erc7984-mind-v1.
|
|
104
|
+
- `registerX402zEvmClientScheme`: registers the scheme with x402 client.
|
|
105
|
+
|
|
106
|
+
Types:
|
|
107
|
+
- `X402zClient`: client instance type.
|
|
108
|
+
- `PreferredPaymentMethod`: preferred payment method selector (object form or scheme-config name string).
|
|
109
|
+
- `X402zClientOptions`: options for `createX402zClient`.
|
|
110
|
+
- `X402zConfidentialClientOptions`: options for confidential payments.
|
|
111
|
+
- `X402zExactClientOptions`: options for exact payments.
|
|
112
|
+
- `PayOptions`: per-request payment options.
|
|
113
|
+
- `X402zClientSchemeOptions`: scheme config for client registration.
|
|
114
|
+
- `X402zClientRegistrationOptions`: registration options for the scheme.
|
|
115
|
+
- `ConfidentialRequirementsExtra`: scheme requirements extension payload.
|
|
116
|
+
- `RelayerInstance`: relayer instance type.
|
|
117
|
+
- re-exported types from `@x402/core/types`: shared x402 types.
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { PaymentRequirements, SchemeNetworkClient, PaymentPayload, Network, PaymentRequired } from '@x402/core/types';
|
|
2
|
+
export * from '@x402/core/types';
|
|
3
|
+
import { SelectPaymentRequirements, PaymentPolicy, x402Client } from '@x402/core/client';
|
|
4
|
+
import { ClientEvmSigner } from '@x402/evm';
|
|
5
|
+
import { ConfidentialPaymentInput, RelayerInstance } from 'x402z-shared-web';
|
|
6
|
+
export { ConfidentialRequirementsExtra, RelayerInstance } from 'x402z-shared-web';
|
|
7
|
+
import { SchemeConfigName, ExactTokenConfig, ConfidentialTokenConfig } from 'x402z-scheme-config';
|
|
8
|
+
|
|
9
|
+
type X402zClientSchemeOptions = {
|
|
10
|
+
signer: ClientEvmSigner;
|
|
11
|
+
buildPayment: (requirements: PaymentRequirements) => ConfidentialPaymentInput | Promise<ConfidentialPaymentInput>;
|
|
12
|
+
eip712?: {
|
|
13
|
+
name: string;
|
|
14
|
+
version: string;
|
|
15
|
+
};
|
|
16
|
+
hashEncryptedAmountInput?: (encryptedAmountInput: `0x${string}`) => `0x${string}`;
|
|
17
|
+
clock?: () => number;
|
|
18
|
+
};
|
|
19
|
+
declare class X402zEvmClientScheme implements SchemeNetworkClient {
|
|
20
|
+
private readonly config;
|
|
21
|
+
readonly scheme = "erc7984-mind-v1";
|
|
22
|
+
private readonly hashFn;
|
|
23
|
+
private readonly clock;
|
|
24
|
+
constructor(config: X402zClientSchemeOptions);
|
|
25
|
+
createPaymentPayload(x402Version: number, paymentRequirements: PaymentRequirements): Promise<Pick<PaymentPayload, "x402Version" | "payload">>;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
type PreferredPaymentMethod = SchemeConfigName | ExactTokenConfig | ConfidentialTokenConfig;
|
|
29
|
+
type BaseClientOptions = {
|
|
30
|
+
fetch?: typeof fetch;
|
|
31
|
+
debug?: boolean;
|
|
32
|
+
};
|
|
33
|
+
type X402zClientOptions = {
|
|
34
|
+
preferredMethods?: readonly PreferredPaymentMethod[];
|
|
35
|
+
signer: ClientEvmSigner;
|
|
36
|
+
relayer?: RelayerInstance;
|
|
37
|
+
networks?: Network[];
|
|
38
|
+
paymentRequirementsSelector?: SelectPaymentRequirements;
|
|
39
|
+
policies?: PaymentPolicy[];
|
|
40
|
+
eip712?: X402zClientSchemeOptions["eip712"];
|
|
41
|
+
hashEncryptedAmountInput?: X402zClientSchemeOptions["hashEncryptedAmountInput"];
|
|
42
|
+
clock?: X402zClientSchemeOptions["clock"];
|
|
43
|
+
onPaymentSelected?: (info: {
|
|
44
|
+
method: PreferredPaymentMethod;
|
|
45
|
+
requirements: PaymentRequirements;
|
|
46
|
+
}) => void;
|
|
47
|
+
} & BaseClientOptions;
|
|
48
|
+
type PayOptions = {
|
|
49
|
+
headers?: Record<string, string>;
|
|
50
|
+
};
|
|
51
|
+
type PayResult = {
|
|
52
|
+
response: Response;
|
|
53
|
+
paymentRequired?: PaymentRequired;
|
|
54
|
+
confidentialRequirements?: PaymentRequirements;
|
|
55
|
+
};
|
|
56
|
+
type X402zClient = {
|
|
57
|
+
relayer?: RelayerInstance;
|
|
58
|
+
pay: (url: string, options?: PayOptions) => Promise<PayResult>;
|
|
59
|
+
};
|
|
60
|
+
declare function createX402zClient(config: X402zClientOptions): X402zClient;
|
|
61
|
+
|
|
62
|
+
type X402zClientRegistrationOptions = X402zClientSchemeOptions & {
|
|
63
|
+
networks?: Network[];
|
|
64
|
+
};
|
|
65
|
+
declare function registerX402zEvmClientScheme(client: x402Client, config: X402zClientRegistrationOptions): x402Client;
|
|
66
|
+
|
|
67
|
+
export { type PayOptions, type PayResult, type PreferredPaymentMethod, type X402zClient, type X402zClientOptions, type X402zClientRegistrationOptions, type X402zClientSchemeOptions, X402zEvmClientScheme, createX402zClient, registerX402zEvmClientScheme };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { PaymentRequirements, SchemeNetworkClient, PaymentPayload, Network, PaymentRequired } from '@x402/core/types';
|
|
2
|
+
export * from '@x402/core/types';
|
|
3
|
+
import { SelectPaymentRequirements, PaymentPolicy, x402Client } from '@x402/core/client';
|
|
4
|
+
import { ClientEvmSigner } from '@x402/evm';
|
|
5
|
+
import { ConfidentialPaymentInput, RelayerInstance } from 'x402z-shared-web';
|
|
6
|
+
export { ConfidentialRequirementsExtra, RelayerInstance } from 'x402z-shared-web';
|
|
7
|
+
import { SchemeConfigName, ExactTokenConfig, ConfidentialTokenConfig } from 'x402z-scheme-config';
|
|
8
|
+
|
|
9
|
+
type X402zClientSchemeOptions = {
|
|
10
|
+
signer: ClientEvmSigner;
|
|
11
|
+
buildPayment: (requirements: PaymentRequirements) => ConfidentialPaymentInput | Promise<ConfidentialPaymentInput>;
|
|
12
|
+
eip712?: {
|
|
13
|
+
name: string;
|
|
14
|
+
version: string;
|
|
15
|
+
};
|
|
16
|
+
hashEncryptedAmountInput?: (encryptedAmountInput: `0x${string}`) => `0x${string}`;
|
|
17
|
+
clock?: () => number;
|
|
18
|
+
};
|
|
19
|
+
declare class X402zEvmClientScheme implements SchemeNetworkClient {
|
|
20
|
+
private readonly config;
|
|
21
|
+
readonly scheme = "erc7984-mind-v1";
|
|
22
|
+
private readonly hashFn;
|
|
23
|
+
private readonly clock;
|
|
24
|
+
constructor(config: X402zClientSchemeOptions);
|
|
25
|
+
createPaymentPayload(x402Version: number, paymentRequirements: PaymentRequirements): Promise<Pick<PaymentPayload, "x402Version" | "payload">>;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
type PreferredPaymentMethod = SchemeConfigName | ExactTokenConfig | ConfidentialTokenConfig;
|
|
29
|
+
type BaseClientOptions = {
|
|
30
|
+
fetch?: typeof fetch;
|
|
31
|
+
debug?: boolean;
|
|
32
|
+
};
|
|
33
|
+
type X402zClientOptions = {
|
|
34
|
+
preferredMethods?: readonly PreferredPaymentMethod[];
|
|
35
|
+
signer: ClientEvmSigner;
|
|
36
|
+
relayer?: RelayerInstance;
|
|
37
|
+
networks?: Network[];
|
|
38
|
+
paymentRequirementsSelector?: SelectPaymentRequirements;
|
|
39
|
+
policies?: PaymentPolicy[];
|
|
40
|
+
eip712?: X402zClientSchemeOptions["eip712"];
|
|
41
|
+
hashEncryptedAmountInput?: X402zClientSchemeOptions["hashEncryptedAmountInput"];
|
|
42
|
+
clock?: X402zClientSchemeOptions["clock"];
|
|
43
|
+
onPaymentSelected?: (info: {
|
|
44
|
+
method: PreferredPaymentMethod;
|
|
45
|
+
requirements: PaymentRequirements;
|
|
46
|
+
}) => void;
|
|
47
|
+
} & BaseClientOptions;
|
|
48
|
+
type PayOptions = {
|
|
49
|
+
headers?: Record<string, string>;
|
|
50
|
+
};
|
|
51
|
+
type PayResult = {
|
|
52
|
+
response: Response;
|
|
53
|
+
paymentRequired?: PaymentRequired;
|
|
54
|
+
confidentialRequirements?: PaymentRequirements;
|
|
55
|
+
};
|
|
56
|
+
type X402zClient = {
|
|
57
|
+
relayer?: RelayerInstance;
|
|
58
|
+
pay: (url: string, options?: PayOptions) => Promise<PayResult>;
|
|
59
|
+
};
|
|
60
|
+
declare function createX402zClient(config: X402zClientOptions): X402zClient;
|
|
61
|
+
|
|
62
|
+
type X402zClientRegistrationOptions = X402zClientSchemeOptions & {
|
|
63
|
+
networks?: Network[];
|
|
64
|
+
};
|
|
65
|
+
declare function registerX402zEvmClientScheme(client: x402Client, config: X402zClientRegistrationOptions): x402Client;
|
|
66
|
+
|
|
67
|
+
export { type PayOptions, type PayResult, type PreferredPaymentMethod, type X402zClient, type X402zClientOptions, type X402zClientRegistrationOptions, type X402zClientSchemeOptions, X402zEvmClientScheme, createX402zClient, registerX402zEvmClientScheme };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
X402zEvmClientScheme: () => X402zEvmClientScheme,
|
|
24
|
+
createX402zClient: () => createX402zClient,
|
|
25
|
+
registerX402zEvmClientScheme: () => registerX402zEvmClientScheme
|
|
26
|
+
});
|
|
27
|
+
module.exports = __toCommonJS(index_exports);
|
|
28
|
+
|
|
29
|
+
// src/http/client.ts
|
|
30
|
+
var import_client = require("@x402/core/client");
|
|
31
|
+
var import_http = require("@x402/core/http");
|
|
32
|
+
var import_client2 = require("@x402/evm/exact/client");
|
|
33
|
+
var import_viem2 = require("viem");
|
|
34
|
+
var import_x402z_shared_web2 = require("x402z-shared-web");
|
|
35
|
+
var import_x402z_scheme_config = require("x402z-scheme-config");
|
|
36
|
+
|
|
37
|
+
// src/scheme/scheme.ts
|
|
38
|
+
var import_viem = require("viem");
|
|
39
|
+
var import_x402z_shared_web = require("x402z-shared-web");
|
|
40
|
+
var ZERO_BYTES32 = "0x0000000000000000000000000000000000000000000000000000000000000000";
|
|
41
|
+
var DECIMAL_POINT = ".";
|
|
42
|
+
function normalizeIntegerAmount(value, fallback) {
|
|
43
|
+
const normalized = (0, import_x402z_shared_web.normalizeAmount)(value);
|
|
44
|
+
if (!normalized.includes(DECIMAL_POINT)) {
|
|
45
|
+
return normalized;
|
|
46
|
+
}
|
|
47
|
+
const fallbackNormalized = (0, import_x402z_shared_web.normalizeAmount)(fallback);
|
|
48
|
+
if (fallbackNormalized.includes(DECIMAL_POINT)) {
|
|
49
|
+
throw new Error(`Invalid amount: ${normalized}`);
|
|
50
|
+
}
|
|
51
|
+
return fallbackNormalized;
|
|
52
|
+
}
|
|
53
|
+
var X402zEvmClientScheme = class {
|
|
54
|
+
constructor(config) {
|
|
55
|
+
this.config = config;
|
|
56
|
+
this.scheme = "erc7984-mind-v1";
|
|
57
|
+
this.hashFn = config.hashEncryptedAmountInput ?? import_x402z_shared_web.hashEncryptedAmountInput;
|
|
58
|
+
this.clock = config.clock ?? (() => Math.floor(Date.now() / 1e3));
|
|
59
|
+
}
|
|
60
|
+
async createPaymentPayload(x402Version, paymentRequirements) {
|
|
61
|
+
const input = await this.config.buildPayment(paymentRequirements);
|
|
62
|
+
const extra = paymentRequirements.extra;
|
|
63
|
+
const eip712 = extra?.eip712 ?? this.config.eip712;
|
|
64
|
+
if (!eip712?.name || !eip712?.version) {
|
|
65
|
+
throw new Error("Missing EIP-712 domain parameters (name, version) in requirements or config");
|
|
66
|
+
}
|
|
67
|
+
const now = this.clock();
|
|
68
|
+
const validAfter = input.validAfter ?? Math.max(0, now - 60);
|
|
69
|
+
const validBefore = input.validBefore ?? now + paymentRequirements.maxTimeoutSeconds;
|
|
70
|
+
const nonce = input.nonce ?? (0, import_x402z_shared_web.createNonce)();
|
|
71
|
+
const maxClearAmount = normalizeIntegerAmount(
|
|
72
|
+
input.maxClearAmount ?? extra?.confidential?.maxClearAmount ?? paymentRequirements.amount,
|
|
73
|
+
paymentRequirements.amount
|
|
74
|
+
);
|
|
75
|
+
const resourceHash = input.resourceHash ?? extra?.confidential?.resourceHash ?? ZERO_BYTES32;
|
|
76
|
+
const authorization = {
|
|
77
|
+
holder: this.config.signer.address,
|
|
78
|
+
payee: (0, import_viem.getAddress)(paymentRequirements.payTo),
|
|
79
|
+
maxClearAmount,
|
|
80
|
+
resourceHash,
|
|
81
|
+
validAfter: (0, import_x402z_shared_web.normalizeAmount)(validAfter),
|
|
82
|
+
validBefore: (0, import_x402z_shared_web.normalizeAmount)(validBefore),
|
|
83
|
+
nonce,
|
|
84
|
+
encryptedAmountHash: this.hashFn(input.encryptedAmountInput)
|
|
85
|
+
};
|
|
86
|
+
const chainId = parseInt(paymentRequirements.network.split(":")[1]);
|
|
87
|
+
const signature = await this.config.signer.signTypedData({
|
|
88
|
+
domain: {
|
|
89
|
+
name: eip712.name,
|
|
90
|
+
version: eip712.version,
|
|
91
|
+
chainId,
|
|
92
|
+
verifyingContract: (0, import_viem.getAddress)(paymentRequirements.asset)
|
|
93
|
+
},
|
|
94
|
+
types: import_x402z_shared_web.confidentialPaymentTypes,
|
|
95
|
+
primaryType: "ConfidentialPayment",
|
|
96
|
+
message: {
|
|
97
|
+
holder: (0, import_viem.getAddress)(authorization.holder),
|
|
98
|
+
payee: (0, import_viem.getAddress)(authorization.payee),
|
|
99
|
+
maxClearAmount: BigInt(authorization.maxClearAmount),
|
|
100
|
+
resourceHash: authorization.resourceHash,
|
|
101
|
+
validAfter: BigInt(authorization.validAfter),
|
|
102
|
+
validBefore: BigInt(authorization.validBefore),
|
|
103
|
+
nonce: authorization.nonce,
|
|
104
|
+
encryptedAmountHash: authorization.encryptedAmountHash
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
const payload = {
|
|
108
|
+
authorization,
|
|
109
|
+
signature,
|
|
110
|
+
encryptedAmountInput: input.encryptedAmountInput,
|
|
111
|
+
inputProof: input.inputProof
|
|
112
|
+
};
|
|
113
|
+
return {
|
|
114
|
+
x402Version,
|
|
115
|
+
payload
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
// src/scheme/register.ts
|
|
121
|
+
function registerX402zEvmClientScheme(client, config) {
|
|
122
|
+
if (config.networks && config.networks.length > 0) {
|
|
123
|
+
for (const network of config.networks) {
|
|
124
|
+
client.register(network, new X402zEvmClientScheme(config));
|
|
125
|
+
}
|
|
126
|
+
return client;
|
|
127
|
+
}
|
|
128
|
+
client.register("eip155:*", new X402zEvmClientScheme(config));
|
|
129
|
+
return client;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// src/http/client.ts
|
|
133
|
+
function resolvePreferredMethod(method) {
|
|
134
|
+
if (typeof method !== "string") {
|
|
135
|
+
return method;
|
|
136
|
+
}
|
|
137
|
+
const config = (0, import_x402z_scheme_config.getSchemeConfigByName)(method);
|
|
138
|
+
if (!config) {
|
|
139
|
+
throw new Error(`Unknown preferred method: ${method}`);
|
|
140
|
+
}
|
|
141
|
+
return config;
|
|
142
|
+
}
|
|
143
|
+
function matchesMethod(requirements, method) {
|
|
144
|
+
const resolved = resolvePreferredMethod(method);
|
|
145
|
+
const scheme = (0, import_x402z_scheme_config.isConfidentialTokenConfig)(resolved) ? "erc7984-mind-v1" : "exact";
|
|
146
|
+
if (requirements.scheme !== scheme) return false;
|
|
147
|
+
if (requirements.network !== resolved.network) return false;
|
|
148
|
+
if (requirements.asset && requirements.asset?.toLowerCase() !== resolved.asset.toLowerCase()) return false;
|
|
149
|
+
return true;
|
|
150
|
+
}
|
|
151
|
+
function filterPaymentRequired(paymentRequired, method) {
|
|
152
|
+
const accepts = paymentRequired.accepts.filter(
|
|
153
|
+
(requirement) => matchesMethod(requirement, method)
|
|
154
|
+
);
|
|
155
|
+
return { ...paymentRequired, accepts };
|
|
156
|
+
}
|
|
157
|
+
function createExactPayer(config, debugEnabled) {
|
|
158
|
+
const client = new import_client.x402Client(config.paymentRequirementsSelector);
|
|
159
|
+
(0, import_client2.registerExactEvmScheme)(client, {
|
|
160
|
+
signer: config.signer,
|
|
161
|
+
networks: config.networks,
|
|
162
|
+
policies: config.policies,
|
|
163
|
+
paymentRequirementsSelector: config.paymentRequirementsSelector
|
|
164
|
+
});
|
|
165
|
+
const httpClient = new import_http.x402HTTPClient(client);
|
|
166
|
+
return {
|
|
167
|
+
async buildHeaders(paymentRequired) {
|
|
168
|
+
const payload = await httpClient.createPaymentPayload(paymentRequired);
|
|
169
|
+
const payHeaders = httpClient.encodePaymentSignatureHeader(payload);
|
|
170
|
+
if (debugEnabled) {
|
|
171
|
+
console.debug("[x402z-client] payment payload", payload);
|
|
172
|
+
console.debug("[x402z-client] payment headers", payHeaders);
|
|
173
|
+
}
|
|
174
|
+
return { headers: payHeaders };
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
function createConfidentialPayer(config, debugEnabled) {
|
|
179
|
+
const buildPayment = async (requirements) => {
|
|
180
|
+
if (!(0, import_viem2.isAddress)(requirements.asset)) {
|
|
181
|
+
throw new Error(`Invalid token address from requirements: ${requirements.asset}`);
|
|
182
|
+
}
|
|
183
|
+
const extra = requirements.extra;
|
|
184
|
+
const batcherAddress = extra?.confidential?.batcherAddress;
|
|
185
|
+
if (!batcherAddress) {
|
|
186
|
+
throw new Error("Missing confidential.batcherAddress in payment requirements");
|
|
187
|
+
}
|
|
188
|
+
if (debugEnabled) {
|
|
189
|
+
console.debug("[x402z-client] encrypt input", {
|
|
190
|
+
tokenAddress: requirements.asset,
|
|
191
|
+
batcherAddress,
|
|
192
|
+
amount: requirements.amount
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
const encrypted = await (0, import_x402z_shared_web2.createEncryptedAmountInput)(
|
|
196
|
+
config.relayer,
|
|
197
|
+
requirements.asset,
|
|
198
|
+
batcherAddress,
|
|
199
|
+
Number(requirements.amount)
|
|
200
|
+
);
|
|
201
|
+
return {
|
|
202
|
+
encryptedAmountInput: encrypted.handle,
|
|
203
|
+
inputProof: encrypted.inputProof
|
|
204
|
+
};
|
|
205
|
+
};
|
|
206
|
+
const client = new import_client.x402Client();
|
|
207
|
+
registerX402zEvmClientScheme(client, {
|
|
208
|
+
signer: config.signer,
|
|
209
|
+
buildPayment,
|
|
210
|
+
eip712: config.eip712,
|
|
211
|
+
hashEncryptedAmountInput: config.hashEncryptedAmountInput,
|
|
212
|
+
clock: config.clock,
|
|
213
|
+
networks: config.networks
|
|
214
|
+
});
|
|
215
|
+
const httpClient = new import_http.x402HTTPClient(client);
|
|
216
|
+
const extractConfidentialRequirements = (paymentRequired) => {
|
|
217
|
+
const requirements = paymentRequired.accepts.find(
|
|
218
|
+
(requirement) => requirement.scheme === "erc7984-mind-v1"
|
|
219
|
+
);
|
|
220
|
+
if (!requirements) {
|
|
221
|
+
throw new Error("Missing erc7984-mind-v1 payment requirements");
|
|
222
|
+
}
|
|
223
|
+
return requirements;
|
|
224
|
+
};
|
|
225
|
+
return {
|
|
226
|
+
relayer: config.relayer,
|
|
227
|
+
extractConfidentialRequirements,
|
|
228
|
+
async buildHeaders(paymentRequired) {
|
|
229
|
+
const payload = await httpClient.createPaymentPayload(paymentRequired);
|
|
230
|
+
const payHeaders = httpClient.encodePaymentSignatureHeader(payload);
|
|
231
|
+
if (debugEnabled) {
|
|
232
|
+
console.debug("[x402z-client] payment payload", payload);
|
|
233
|
+
console.debug("[x402z-client] payment headers", payHeaders);
|
|
234
|
+
}
|
|
235
|
+
return { headers: payHeaders };
|
|
236
|
+
}
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
function createX402zClient(config) {
|
|
240
|
+
const fetchFn = config.fetch ?? globalThis.fetch;
|
|
241
|
+
const debugEnabled = config.debug ?? false;
|
|
242
|
+
if (!fetchFn) {
|
|
243
|
+
throw new Error("fetch is not available; provide a fetch implementation");
|
|
244
|
+
}
|
|
245
|
+
const preferredMethods = config.preferredMethods?.length ? [...config.preferredMethods] : import_x402z_scheme_config.SCHEME_CONFIG_NAMES;
|
|
246
|
+
const exactPayer = createExactPayer(
|
|
247
|
+
{
|
|
248
|
+
signer: config.signer,
|
|
249
|
+
networks: config.networks,
|
|
250
|
+
paymentRequirementsSelector: config.paymentRequirementsSelector,
|
|
251
|
+
policies: config.policies
|
|
252
|
+
},
|
|
253
|
+
debugEnabled
|
|
254
|
+
);
|
|
255
|
+
const confidentialPayer = config.relayer ? createConfidentialPayer(
|
|
256
|
+
{
|
|
257
|
+
signer: config.signer,
|
|
258
|
+
relayer: config.relayer,
|
|
259
|
+
eip712: config.eip712,
|
|
260
|
+
hashEncryptedAmountInput: config.hashEncryptedAmountInput,
|
|
261
|
+
clock: config.clock,
|
|
262
|
+
networks: config.networks
|
|
263
|
+
},
|
|
264
|
+
debugEnabled
|
|
265
|
+
) : void 0;
|
|
266
|
+
const parserClient = new import_client.x402Client();
|
|
267
|
+
const parserHttp = new import_http.x402HTTPClient(parserClient);
|
|
268
|
+
const pay = async (url, options) => {
|
|
269
|
+
const initial = await fetchFn(url, { headers: options?.headers });
|
|
270
|
+
if (initial.status !== 402) {
|
|
271
|
+
return { response: initial };
|
|
272
|
+
}
|
|
273
|
+
const paymentRequired = parserHttp.getPaymentRequiredResponse(
|
|
274
|
+
(name) => initial.headers.get(name),
|
|
275
|
+
await initial.json().catch(() => ({}))
|
|
276
|
+
);
|
|
277
|
+
const selectedMethod = preferredMethods.find(
|
|
278
|
+
(method) => paymentRequired.accepts.some(
|
|
279
|
+
(requirement) => matchesMethod(requirement, method)
|
|
280
|
+
)
|
|
281
|
+
);
|
|
282
|
+
if (!selectedMethod) {
|
|
283
|
+
throw new Error("No preferred payment methods are supported by payment requirements");
|
|
284
|
+
}
|
|
285
|
+
const filteredRequirements = filterPaymentRequired(paymentRequired, selectedMethod);
|
|
286
|
+
if (filteredRequirements.accepts.length === 0) {
|
|
287
|
+
throw new Error("Missing payment requirements for selected method");
|
|
288
|
+
}
|
|
289
|
+
let headersResult;
|
|
290
|
+
let confidentialRequirements;
|
|
291
|
+
const selectedRequirements = filteredRequirements.accepts[0];
|
|
292
|
+
config.onPaymentSelected?.({
|
|
293
|
+
method: selectedMethod,
|
|
294
|
+
requirements: selectedRequirements
|
|
295
|
+
});
|
|
296
|
+
if (selectedRequirements.scheme === "exact") {
|
|
297
|
+
headersResult = await exactPayer.buildHeaders(filteredRequirements);
|
|
298
|
+
} else {
|
|
299
|
+
if (!confidentialPayer) {
|
|
300
|
+
throw new Error("Confidential scheme is selected but relayer is missing");
|
|
301
|
+
}
|
|
302
|
+
confidentialRequirements = confidentialPayer.extractConfidentialRequirements(filteredRequirements);
|
|
303
|
+
headersResult = await confidentialPayer.buildHeaders(filteredRequirements);
|
|
304
|
+
}
|
|
305
|
+
const mergedHeaders = { ...options?.headers ?? {}, ...headersResult.headers };
|
|
306
|
+
const paidResponse = await fetchFn(url, { headers: mergedHeaders });
|
|
307
|
+
if (debugEnabled) {
|
|
308
|
+
try {
|
|
309
|
+
const body = await paidResponse.clone().text();
|
|
310
|
+
console.debug("[x402z-client] response", {
|
|
311
|
+
status: paidResponse.status,
|
|
312
|
+
headers: Object.fromEntries(paidResponse.headers.entries()),
|
|
313
|
+
body
|
|
314
|
+
});
|
|
315
|
+
} catch (error) {
|
|
316
|
+
console.debug("[x402z-client] response", {
|
|
317
|
+
status: paidResponse.status,
|
|
318
|
+
headers: Object.fromEntries(paidResponse.headers.entries()),
|
|
319
|
+
body: "<unavailable>"
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
return { response: paidResponse, paymentRequired, confidentialRequirements };
|
|
324
|
+
};
|
|
325
|
+
return {
|
|
326
|
+
relayer: confidentialPayer?.relayer,
|
|
327
|
+
pay
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
331
|
+
0 && (module.exports = {
|
|
332
|
+
X402zEvmClientScheme,
|
|
333
|
+
createX402zClient,
|
|
334
|
+
registerX402zEvmClientScheme
|
|
335
|
+
});
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
// src/http/client.ts
|
|
2
|
+
import { x402Client } from "@x402/core/client";
|
|
3
|
+
import { x402HTTPClient } from "@x402/core/http";
|
|
4
|
+
import { registerExactEvmScheme } from "@x402/evm/exact/client";
|
|
5
|
+
import { isAddress } from "viem";
|
|
6
|
+
import {
|
|
7
|
+
createEncryptedAmountInput
|
|
8
|
+
} from "x402z-shared-web";
|
|
9
|
+
import { getSchemeConfigByName, SCHEME_CONFIG_NAMES, isConfidentialTokenConfig } from "x402z-scheme-config";
|
|
10
|
+
|
|
11
|
+
// src/scheme/scheme.ts
|
|
12
|
+
import { getAddress } from "viem";
|
|
13
|
+
import {
|
|
14
|
+
confidentialPaymentTypes,
|
|
15
|
+
createNonce,
|
|
16
|
+
hashEncryptedAmountInput,
|
|
17
|
+
normalizeAmount
|
|
18
|
+
} from "x402z-shared-web";
|
|
19
|
+
var ZERO_BYTES32 = "0x0000000000000000000000000000000000000000000000000000000000000000";
|
|
20
|
+
var DECIMAL_POINT = ".";
|
|
21
|
+
function normalizeIntegerAmount(value, fallback) {
|
|
22
|
+
const normalized = normalizeAmount(value);
|
|
23
|
+
if (!normalized.includes(DECIMAL_POINT)) {
|
|
24
|
+
return normalized;
|
|
25
|
+
}
|
|
26
|
+
const fallbackNormalized = normalizeAmount(fallback);
|
|
27
|
+
if (fallbackNormalized.includes(DECIMAL_POINT)) {
|
|
28
|
+
throw new Error(`Invalid amount: ${normalized}`);
|
|
29
|
+
}
|
|
30
|
+
return fallbackNormalized;
|
|
31
|
+
}
|
|
32
|
+
var X402zEvmClientScheme = class {
|
|
33
|
+
constructor(config) {
|
|
34
|
+
this.config = config;
|
|
35
|
+
this.scheme = "erc7984-mind-v1";
|
|
36
|
+
this.hashFn = config.hashEncryptedAmountInput ?? hashEncryptedAmountInput;
|
|
37
|
+
this.clock = config.clock ?? (() => Math.floor(Date.now() / 1e3));
|
|
38
|
+
}
|
|
39
|
+
async createPaymentPayload(x402Version, paymentRequirements) {
|
|
40
|
+
const input = await this.config.buildPayment(paymentRequirements);
|
|
41
|
+
const extra = paymentRequirements.extra;
|
|
42
|
+
const eip712 = extra?.eip712 ?? this.config.eip712;
|
|
43
|
+
if (!eip712?.name || !eip712?.version) {
|
|
44
|
+
throw new Error("Missing EIP-712 domain parameters (name, version) in requirements or config");
|
|
45
|
+
}
|
|
46
|
+
const now = this.clock();
|
|
47
|
+
const validAfter = input.validAfter ?? Math.max(0, now - 60);
|
|
48
|
+
const validBefore = input.validBefore ?? now + paymentRequirements.maxTimeoutSeconds;
|
|
49
|
+
const nonce = input.nonce ?? createNonce();
|
|
50
|
+
const maxClearAmount = normalizeIntegerAmount(
|
|
51
|
+
input.maxClearAmount ?? extra?.confidential?.maxClearAmount ?? paymentRequirements.amount,
|
|
52
|
+
paymentRequirements.amount
|
|
53
|
+
);
|
|
54
|
+
const resourceHash = input.resourceHash ?? extra?.confidential?.resourceHash ?? ZERO_BYTES32;
|
|
55
|
+
const authorization = {
|
|
56
|
+
holder: this.config.signer.address,
|
|
57
|
+
payee: getAddress(paymentRequirements.payTo),
|
|
58
|
+
maxClearAmount,
|
|
59
|
+
resourceHash,
|
|
60
|
+
validAfter: normalizeAmount(validAfter),
|
|
61
|
+
validBefore: normalizeAmount(validBefore),
|
|
62
|
+
nonce,
|
|
63
|
+
encryptedAmountHash: this.hashFn(input.encryptedAmountInput)
|
|
64
|
+
};
|
|
65
|
+
const chainId = parseInt(paymentRequirements.network.split(":")[1]);
|
|
66
|
+
const signature = await this.config.signer.signTypedData({
|
|
67
|
+
domain: {
|
|
68
|
+
name: eip712.name,
|
|
69
|
+
version: eip712.version,
|
|
70
|
+
chainId,
|
|
71
|
+
verifyingContract: getAddress(paymentRequirements.asset)
|
|
72
|
+
},
|
|
73
|
+
types: confidentialPaymentTypes,
|
|
74
|
+
primaryType: "ConfidentialPayment",
|
|
75
|
+
message: {
|
|
76
|
+
holder: getAddress(authorization.holder),
|
|
77
|
+
payee: getAddress(authorization.payee),
|
|
78
|
+
maxClearAmount: BigInt(authorization.maxClearAmount),
|
|
79
|
+
resourceHash: authorization.resourceHash,
|
|
80
|
+
validAfter: BigInt(authorization.validAfter),
|
|
81
|
+
validBefore: BigInt(authorization.validBefore),
|
|
82
|
+
nonce: authorization.nonce,
|
|
83
|
+
encryptedAmountHash: authorization.encryptedAmountHash
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
const payload = {
|
|
87
|
+
authorization,
|
|
88
|
+
signature,
|
|
89
|
+
encryptedAmountInput: input.encryptedAmountInput,
|
|
90
|
+
inputProof: input.inputProof
|
|
91
|
+
};
|
|
92
|
+
return {
|
|
93
|
+
x402Version,
|
|
94
|
+
payload
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
// src/scheme/register.ts
|
|
100
|
+
function registerX402zEvmClientScheme(client, config) {
|
|
101
|
+
if (config.networks && config.networks.length > 0) {
|
|
102
|
+
for (const network of config.networks) {
|
|
103
|
+
client.register(network, new X402zEvmClientScheme(config));
|
|
104
|
+
}
|
|
105
|
+
return client;
|
|
106
|
+
}
|
|
107
|
+
client.register("eip155:*", new X402zEvmClientScheme(config));
|
|
108
|
+
return client;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// src/http/client.ts
|
|
112
|
+
function resolvePreferredMethod(method) {
|
|
113
|
+
if (typeof method !== "string") {
|
|
114
|
+
return method;
|
|
115
|
+
}
|
|
116
|
+
const config = getSchemeConfigByName(method);
|
|
117
|
+
if (!config) {
|
|
118
|
+
throw new Error(`Unknown preferred method: ${method}`);
|
|
119
|
+
}
|
|
120
|
+
return config;
|
|
121
|
+
}
|
|
122
|
+
function matchesMethod(requirements, method) {
|
|
123
|
+
const resolved = resolvePreferredMethod(method);
|
|
124
|
+
const scheme = isConfidentialTokenConfig(resolved) ? "erc7984-mind-v1" : "exact";
|
|
125
|
+
if (requirements.scheme !== scheme) return false;
|
|
126
|
+
if (requirements.network !== resolved.network) return false;
|
|
127
|
+
if (requirements.asset && requirements.asset?.toLowerCase() !== resolved.asset.toLowerCase()) return false;
|
|
128
|
+
return true;
|
|
129
|
+
}
|
|
130
|
+
function filterPaymentRequired(paymentRequired, method) {
|
|
131
|
+
const accepts = paymentRequired.accepts.filter(
|
|
132
|
+
(requirement) => matchesMethod(requirement, method)
|
|
133
|
+
);
|
|
134
|
+
return { ...paymentRequired, accepts };
|
|
135
|
+
}
|
|
136
|
+
function createExactPayer(config, debugEnabled) {
|
|
137
|
+
const client = new x402Client(config.paymentRequirementsSelector);
|
|
138
|
+
registerExactEvmScheme(client, {
|
|
139
|
+
signer: config.signer,
|
|
140
|
+
networks: config.networks,
|
|
141
|
+
policies: config.policies,
|
|
142
|
+
paymentRequirementsSelector: config.paymentRequirementsSelector
|
|
143
|
+
});
|
|
144
|
+
const httpClient = new x402HTTPClient(client);
|
|
145
|
+
return {
|
|
146
|
+
async buildHeaders(paymentRequired) {
|
|
147
|
+
const payload = await httpClient.createPaymentPayload(paymentRequired);
|
|
148
|
+
const payHeaders = httpClient.encodePaymentSignatureHeader(payload);
|
|
149
|
+
if (debugEnabled) {
|
|
150
|
+
console.debug("[x402z-client] payment payload", payload);
|
|
151
|
+
console.debug("[x402z-client] payment headers", payHeaders);
|
|
152
|
+
}
|
|
153
|
+
return { headers: payHeaders };
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
function createConfidentialPayer(config, debugEnabled) {
|
|
158
|
+
const buildPayment = async (requirements) => {
|
|
159
|
+
if (!isAddress(requirements.asset)) {
|
|
160
|
+
throw new Error(`Invalid token address from requirements: ${requirements.asset}`);
|
|
161
|
+
}
|
|
162
|
+
const extra = requirements.extra;
|
|
163
|
+
const batcherAddress = extra?.confidential?.batcherAddress;
|
|
164
|
+
if (!batcherAddress) {
|
|
165
|
+
throw new Error("Missing confidential.batcherAddress in payment requirements");
|
|
166
|
+
}
|
|
167
|
+
if (debugEnabled) {
|
|
168
|
+
console.debug("[x402z-client] encrypt input", {
|
|
169
|
+
tokenAddress: requirements.asset,
|
|
170
|
+
batcherAddress,
|
|
171
|
+
amount: requirements.amount
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
const encrypted = await createEncryptedAmountInput(
|
|
175
|
+
config.relayer,
|
|
176
|
+
requirements.asset,
|
|
177
|
+
batcherAddress,
|
|
178
|
+
Number(requirements.amount)
|
|
179
|
+
);
|
|
180
|
+
return {
|
|
181
|
+
encryptedAmountInput: encrypted.handle,
|
|
182
|
+
inputProof: encrypted.inputProof
|
|
183
|
+
};
|
|
184
|
+
};
|
|
185
|
+
const client = new x402Client();
|
|
186
|
+
registerX402zEvmClientScheme(client, {
|
|
187
|
+
signer: config.signer,
|
|
188
|
+
buildPayment,
|
|
189
|
+
eip712: config.eip712,
|
|
190
|
+
hashEncryptedAmountInput: config.hashEncryptedAmountInput,
|
|
191
|
+
clock: config.clock,
|
|
192
|
+
networks: config.networks
|
|
193
|
+
});
|
|
194
|
+
const httpClient = new x402HTTPClient(client);
|
|
195
|
+
const extractConfidentialRequirements = (paymentRequired) => {
|
|
196
|
+
const requirements = paymentRequired.accepts.find(
|
|
197
|
+
(requirement) => requirement.scheme === "erc7984-mind-v1"
|
|
198
|
+
);
|
|
199
|
+
if (!requirements) {
|
|
200
|
+
throw new Error("Missing erc7984-mind-v1 payment requirements");
|
|
201
|
+
}
|
|
202
|
+
return requirements;
|
|
203
|
+
};
|
|
204
|
+
return {
|
|
205
|
+
relayer: config.relayer,
|
|
206
|
+
extractConfidentialRequirements,
|
|
207
|
+
async buildHeaders(paymentRequired) {
|
|
208
|
+
const payload = await httpClient.createPaymentPayload(paymentRequired);
|
|
209
|
+
const payHeaders = httpClient.encodePaymentSignatureHeader(payload);
|
|
210
|
+
if (debugEnabled) {
|
|
211
|
+
console.debug("[x402z-client] payment payload", payload);
|
|
212
|
+
console.debug("[x402z-client] payment headers", payHeaders);
|
|
213
|
+
}
|
|
214
|
+
return { headers: payHeaders };
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
function createX402zClient(config) {
|
|
219
|
+
const fetchFn = config.fetch ?? globalThis.fetch;
|
|
220
|
+
const debugEnabled = config.debug ?? false;
|
|
221
|
+
if (!fetchFn) {
|
|
222
|
+
throw new Error("fetch is not available; provide a fetch implementation");
|
|
223
|
+
}
|
|
224
|
+
const preferredMethods = config.preferredMethods?.length ? [...config.preferredMethods] : SCHEME_CONFIG_NAMES;
|
|
225
|
+
const exactPayer = createExactPayer(
|
|
226
|
+
{
|
|
227
|
+
signer: config.signer,
|
|
228
|
+
networks: config.networks,
|
|
229
|
+
paymentRequirementsSelector: config.paymentRequirementsSelector,
|
|
230
|
+
policies: config.policies
|
|
231
|
+
},
|
|
232
|
+
debugEnabled
|
|
233
|
+
);
|
|
234
|
+
const confidentialPayer = config.relayer ? createConfidentialPayer(
|
|
235
|
+
{
|
|
236
|
+
signer: config.signer,
|
|
237
|
+
relayer: config.relayer,
|
|
238
|
+
eip712: config.eip712,
|
|
239
|
+
hashEncryptedAmountInput: config.hashEncryptedAmountInput,
|
|
240
|
+
clock: config.clock,
|
|
241
|
+
networks: config.networks
|
|
242
|
+
},
|
|
243
|
+
debugEnabled
|
|
244
|
+
) : void 0;
|
|
245
|
+
const parserClient = new x402Client();
|
|
246
|
+
const parserHttp = new x402HTTPClient(parserClient);
|
|
247
|
+
const pay = async (url, options) => {
|
|
248
|
+
const initial = await fetchFn(url, { headers: options?.headers });
|
|
249
|
+
if (initial.status !== 402) {
|
|
250
|
+
return { response: initial };
|
|
251
|
+
}
|
|
252
|
+
const paymentRequired = parserHttp.getPaymentRequiredResponse(
|
|
253
|
+
(name) => initial.headers.get(name),
|
|
254
|
+
await initial.json().catch(() => ({}))
|
|
255
|
+
);
|
|
256
|
+
const selectedMethod = preferredMethods.find(
|
|
257
|
+
(method) => paymentRequired.accepts.some(
|
|
258
|
+
(requirement) => matchesMethod(requirement, method)
|
|
259
|
+
)
|
|
260
|
+
);
|
|
261
|
+
if (!selectedMethod) {
|
|
262
|
+
throw new Error("No preferred payment methods are supported by payment requirements");
|
|
263
|
+
}
|
|
264
|
+
const filteredRequirements = filterPaymentRequired(paymentRequired, selectedMethod);
|
|
265
|
+
if (filteredRequirements.accepts.length === 0) {
|
|
266
|
+
throw new Error("Missing payment requirements for selected method");
|
|
267
|
+
}
|
|
268
|
+
let headersResult;
|
|
269
|
+
let confidentialRequirements;
|
|
270
|
+
const selectedRequirements = filteredRequirements.accepts[0];
|
|
271
|
+
config.onPaymentSelected?.({
|
|
272
|
+
method: selectedMethod,
|
|
273
|
+
requirements: selectedRequirements
|
|
274
|
+
});
|
|
275
|
+
if (selectedRequirements.scheme === "exact") {
|
|
276
|
+
headersResult = await exactPayer.buildHeaders(filteredRequirements);
|
|
277
|
+
} else {
|
|
278
|
+
if (!confidentialPayer) {
|
|
279
|
+
throw new Error("Confidential scheme is selected but relayer is missing");
|
|
280
|
+
}
|
|
281
|
+
confidentialRequirements = confidentialPayer.extractConfidentialRequirements(filteredRequirements);
|
|
282
|
+
headersResult = await confidentialPayer.buildHeaders(filteredRequirements);
|
|
283
|
+
}
|
|
284
|
+
const mergedHeaders = { ...options?.headers ?? {}, ...headersResult.headers };
|
|
285
|
+
const paidResponse = await fetchFn(url, { headers: mergedHeaders });
|
|
286
|
+
if (debugEnabled) {
|
|
287
|
+
try {
|
|
288
|
+
const body = await paidResponse.clone().text();
|
|
289
|
+
console.debug("[x402z-client] response", {
|
|
290
|
+
status: paidResponse.status,
|
|
291
|
+
headers: Object.fromEntries(paidResponse.headers.entries()),
|
|
292
|
+
body
|
|
293
|
+
});
|
|
294
|
+
} catch (error) {
|
|
295
|
+
console.debug("[x402z-client] response", {
|
|
296
|
+
status: paidResponse.status,
|
|
297
|
+
headers: Object.fromEntries(paidResponse.headers.entries()),
|
|
298
|
+
body: "<unavailable>"
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
return { response: paidResponse, paymentRequired, confidentialRequirements };
|
|
303
|
+
};
|
|
304
|
+
return {
|
|
305
|
+
relayer: confidentialPayer?.relayer,
|
|
306
|
+
pay
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
export {
|
|
310
|
+
X402zEvmClientScheme,
|
|
311
|
+
createX402zClient,
|
|
312
|
+
registerX402zEvmClientScheme
|
|
313
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "x402z-client-web",
|
|
3
|
+
"version": "0.1.2",
|
|
4
|
+
"main": "./dist/index.js",
|
|
5
|
+
"module": "./dist/index.mjs",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist"
|
|
9
|
+
],
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"import": "./dist/index.mjs",
|
|
14
|
+
"require": "./dist/index.js"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"@x402/core": "^2.2.0",
|
|
19
|
+
"@x402/evm": "^2.2.0",
|
|
20
|
+
"viem": "^2.39.3",
|
|
21
|
+
"x402z-shared-web": "0.1.2",
|
|
22
|
+
"x402z-scheme-config": "0.1.2"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"jest": "^29.7.0",
|
|
26
|
+
"ts-jest": "^29.2.5",
|
|
27
|
+
"@types/jest": "^29.5.12"
|
|
28
|
+
},
|
|
29
|
+
"scripts": {
|
|
30
|
+
"build": "tsup src/index.ts --format cjs,esm --dts",
|
|
31
|
+
"test": "jest --config jest.config.cjs --passWithNoTests"
|
|
32
|
+
}
|
|
33
|
+
}
|