@devlab.group/agent-commerce 1.4.1 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/README.md +168 -183
  2. package/dist/ap2.d.ts +21 -24
  3. package/dist/ap2.js +68 -82
  4. package/dist/ap2.js.map +1 -1
  5. package/dist/authorization-BpbWGGrf.d.ts +115 -0
  6. package/dist/{chunk-NWIQLJNU.js → chunk-74BSEXB5.js} +37 -34
  7. package/dist/chunk-74BSEXB5.js.map +1 -0
  8. package/dist/chunk-AFI2CEZS.js +10 -0
  9. package/dist/chunk-AFI2CEZS.js.map +1 -0
  10. package/dist/chunk-ATTWSFQP.js +680 -0
  11. package/dist/chunk-ATTWSFQP.js.map +1 -0
  12. package/dist/{chunk-ZEGFLRGV.js → chunk-IVJC5YLE.js} +63 -26
  13. package/dist/chunk-IVJC5YLE.js.map +1 -0
  14. package/dist/{chunk-GF6KHHKL.js → chunk-RAJGPGJG.js} +5 -8
  15. package/dist/chunk-RAJGPGJG.js.map +1 -0
  16. package/dist/chunk-V2V2E7HX.js +27 -0
  17. package/dist/chunk-V2V2E7HX.js.map +1 -0
  18. package/dist/cli/index.js +812 -573
  19. package/dist/cli/index.js.map +1 -1
  20. package/dist/constants-Bkq04FRZ.d.ts +48 -0
  21. package/dist/guardrails-71XwtXlT.d.ts +31 -0
  22. package/dist/index.d.ts +245 -265
  23. package/dist/index.js +1222 -626
  24. package/dist/index.js.map +1 -1
  25. package/dist/mcp.d.ts +6 -59
  26. package/dist/mcp.js +64 -53
  27. package/dist/mcp.js.map +1 -1
  28. package/dist/mpp.d.ts +50 -0
  29. package/dist/mpp.js +353 -0
  30. package/dist/mpp.js.map +1 -0
  31. package/dist/protocol-adapter-DTkrXTev.d.ts +238 -0
  32. package/dist/runtime-CiYbgGpi.d.ts +303 -0
  33. package/dist/{types-CJtwLu1c.d.ts → types-O6lg3Q5V.d.ts} +17 -23
  34. package/dist/x402.d.ts +43 -75
  35. package/dist/x402.js +13 -693
  36. package/dist/x402.js.map +1 -1
  37. package/package.json +13 -2
  38. package/dist/authorization-CPDcpJVa.d.ts +0 -123
  39. package/dist/chunk-GF6KHHKL.js.map +0 -1
  40. package/dist/chunk-NWIQLJNU.js.map +0 -1
  41. package/dist/chunk-ZEGFLRGV.js.map +0 -1
  42. package/dist/guardrails-Dmwi63KB.d.ts +0 -38
  43. package/dist/protocol-adapter-Bebmggf_.d.ts +0 -273
  44. package/dist/runtime-CKq3d_3K.d.ts +0 -340
package/dist/x402.js CHANGED
@@ -1,48 +1,11 @@
1
- import { assertPayToIsNotDevAddress, resolveX402Deployment, assertDevKeyIsLocalOnly, describeDeploymentMode } from './chunk-NWIQLJNU.js';
2
- export { SUPPORTED_NETWORK_IDS } from './chunk-NWIQLJNU.js';
3
- import { CommerceError, systemClock, NOOP_LOGGER, PACKAGE_VERSION } from './chunk-ZEGFLRGV.js';
4
- import { PaymentRequirementsV2Schema, PaymentPayloadV2Schema } from '@x402/core/schemas';
5
- import { getAddress, toHex, isAddress, createWalletClient, http, publicActions, createPublicClient, HttpRequestError, TimeoutError, WaitForTransactionReceiptTimeoutError, encodeAbiParameters, keccak256, ContractFunctionRevertedError, ContractFunctionExecutionError } from 'viem';
1
+ import { chainIdFromCaip2 } from './chunk-ATTWSFQP.js';
2
+ export { createX402PaymentProvider, createX402PaymentProvider as x402 } from './chunk-ATTWSFQP.js';
3
+ export { SUPPORTED_NETWORK_IDS } from './chunk-74BSEXB5.js';
4
+ import { CommerceError } from './chunk-IVJC5YLE.js';
5
+ import { PaymentRequirementsV2Schema } from '@x402/core/schemas';
6
+ import { getAddress, toHex } from 'viem';
6
7
  import { privateKeyToAccount } from 'viem/accounts';
7
- import { x402Facilitator } from '@x402/core/facilitator';
8
- import { HTTPFacilitatorClient } from '@x402/core/http';
9
- import { isEIP3009Payload, toFacilitatorEvmSigner } from '@x402/evm';
10
- import { registerExactEvmScheme } from '@x402/evm/exact/facilitator';
11
8
 
12
- var LOCAL_CHAIN_ID = 84532;
13
- function chainIdFromCaip2(network) {
14
- const match = /^eip155:(\d+)$/.exec(network);
15
- if (!match?.[1]) return void 0;
16
- const chainId = Number(match[1]);
17
- return Number.isSafeInteger(chainId) && chainId > 0 ? chainId : void 0;
18
- }
19
- function buildLocalChain(rpcUrl, chainId = LOCAL_CHAIN_ID) {
20
- return {
21
- id: chainId,
22
- name: "agent-commerce-local",
23
- nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
24
- rpcUrls: {
25
- default: { http: [rpcUrl] }
26
- }
27
- };
28
- }
29
- function createLocalPublicClient(rpcUrl, timeoutMs, chainId) {
30
- return createPublicClient({
31
- chain: buildLocalChain(rpcUrl, chainId),
32
- transport: http(rpcUrl, { timeout: timeoutMs } )
33
- });
34
- }
35
- function createLocalFacilitatorClient(rpcUrl, signerPrivateKey) {
36
- const chain = buildLocalChain(rpcUrl);
37
- const account = privateKeyToAccount(signerPrivateKey);
38
- return createWalletClient({
39
- account,
40
- chain,
41
- transport: http(rpcUrl)
42
- }).extend(publicActions);
43
- }
44
-
45
- // src/payments/x402/client.ts
46
9
  var X402_VERSION = 2;
47
10
  var TRANSFER_WITH_AUTHORIZATION_TYPES = {
48
11
  TransferWithAuthorization: [
@@ -57,18 +20,19 @@ var TRANSFER_WITH_AUTHORIZATION_TYPES = {
57
20
  async function createPaymentProof(options) {
58
21
  const requirements = PaymentRequirementsV2Schema.parse(options.accepts);
59
22
  if (!requirements.extra || typeof requirements.extra["name"] !== "string" || typeof requirements.extra["version"] !== "string") {
60
- throw new Error(
61
- "createPaymentProof: payment requirement is missing extra.name/extra.version \u2014 cannot build the EIP-712 domain"
23
+ throw new CommerceError(
24
+ "INPUT_INVALID",
25
+ "createPaymentProof: payment requirement is missing extra.name/extra.version, so the EIP-712 domain cannot be built"
62
26
  );
63
27
  }
64
28
  const chainId = chainIdFromCaip2(requirements.network);
65
29
  if (chainId === void 0) {
66
- throw new Error(
67
- `createPaymentProof: network "${requirements.network}" is not a CAIP-2 eip155 identifier \u2014 cannot determine the chain id to sign against`
30
+ throw new CommerceError(
31
+ "INPUT_INVALID",
32
+ `createPaymentProof: network "${requirements.network}" is not a CAIP-2 eip155 identifier, so there is no chain id to sign against`
68
33
  );
69
34
  }
70
35
  const account = privateKeyToAccount(options.buyerPrivateKey);
71
- void options.rpcUrl;
72
36
  const nonce = options.overrides?.nonce ?? randomNonce();
73
37
  const nowSeconds = Math.floor(Date.now() / 1e3);
74
38
  const validAfter = options.overrides?.validAfter ?? 0;
@@ -116,650 +80,6 @@ function randomNonce() {
116
80
  return toHex(bytes);
117
81
  }
118
82
 
119
- // src/payments/x402/amount.ts
120
- var DECIMAL_STRING_PATTERN = /^\d+(?:\.\d+)?$/;
121
- function parseCanonicalAmount(amount, decimals) {
122
- if (!Number.isInteger(decimals) || decimals < 0) {
123
- throw new CommerceError("INTERNAL_ERROR", `Invalid asset decimals: ${decimals}`);
124
- }
125
- if (!DECIMAL_STRING_PATTERN.test(amount)) {
126
- throw new CommerceError(
127
- "PAYMENT_INVALID",
128
- `Amount "${amount}" is not a valid non-negative decimal string`,
129
- {
130
- details: { amount }
131
- }
132
- );
133
- }
134
- const [integerPart = "0", fractionPart = ""] = amount.split(".");
135
- if (fractionPart.length > decimals) {
136
- throw new CommerceError(
137
- "PAYMENT_INVALID",
138
- `Amount "${amount}" has more precision than the asset supports (${decimals} decimals)`,
139
- { details: { amount, assetDecimals: decimals } }
140
- );
141
- }
142
- const paddedFraction = fractionPart.padEnd(decimals, "0");
143
- const combined = `${integerPart}${paddedFraction}`;
144
- return BigInt(combined);
145
- }
146
- function formatCanonicalAmount(baseUnits, decimals) {
147
- if (!Number.isInteger(decimals) || decimals < 0) {
148
- throw new CommerceError("INTERNAL_ERROR", `Invalid asset decimals: ${decimals}`);
149
- }
150
- const negative = baseUnits < 0n;
151
- const abs = negative ? -baseUnits : baseUnits;
152
- const digits = abs.toString().padStart(decimals + 1, "0");
153
- const integerPart = digits.slice(0, digits.length - decimals) || "0";
154
- const fractionPart = digits.slice(digits.length - decimals).replace(/0+$/, "");
155
- const sign = negative ? "-" : "";
156
- return fractionPart.length > 0 ? `${sign}${integerPart}.${fractionPart}` : `${sign}${integerPart}`;
157
- }
158
- var SETTLEMENT_CONFIRMATION_TIMEOUT_MS = 6e4;
159
- function createLocalFacilitatorBinding(client, network, isTransportError) {
160
- const build = () => {
161
- let failed = false;
162
- const watch = (promise) => promise.catch((err) => {
163
- if (isTransportError(err)) failed = true;
164
- throw err;
165
- });
166
- const signer = {
167
- address: client.account.address,
168
- readContract: (args) => watch(client.readContract(args)),
169
- verifyTypedData: (args) => watch(client.verifyTypedData(args)),
170
- writeContract: (args) => watch(client.writeContract(args)),
171
- sendTransaction: (args) => watch(client.sendTransaction(args)),
172
- waitForTransactionReceipt: (args) => watch(client.waitForTransactionReceipt(args)),
173
- getCode: (args) => watch(client.getCode(args))
174
- };
175
- const facilitator = registerExactEvmScheme(new x402Facilitator(), {
176
- signer: toFacilitatorEvmSigner(signer, {
177
- confirmationTimeoutMs: SETTLEMENT_CONFIRMATION_TIMEOUT_MS
178
- }),
179
- networks: network
180
- });
181
- return { facilitator, failed: () => failed };
182
- };
183
- return {
184
- kind: "local",
185
- describe: "local (in-process)",
186
- open() {
187
- const { facilitator, failed } = build();
188
- return {
189
- verify: (payload, requirements) => facilitator.verify(payload, requirements),
190
- settle: (payload, requirements) => facilitator.settle(payload, requirements),
191
- transportFailed: failed
192
- };
193
- },
194
- async supported() {
195
- return build().facilitator.getSupported().kinds;
196
- }
197
- };
198
- }
199
- function cdpAuthHeaders(apiKeyId, apiKeySecret) {
200
- const loading = import('@coinbase/x402').then(
201
- (mod) => mod.createCdpAuthHeaders(apiKeyId, apiKeySecret),
202
- (cause) => {
203
- throw new CommerceError(
204
- "CONFIG_INVALID",
205
- 'x402 provider: facilitator.auth.type is "cdp", which needs the optional peer "@coinbase/x402". Install it (npm install @coinbase/x402), or use a facilitator that accepts a static token with auth.type "bearer".',
206
- { cause }
207
- );
208
- }
209
- );
210
- loading.catch(() => {
211
- });
212
- return async () => {
213
- const create = await loading;
214
- if (!create) {
215
- throw new CommerceError(
216
- "PAYMENT_PROVIDER_UNAVAILABLE",
217
- "x402 provider: @coinbase/x402 returned no auth-header factory"
218
- );
219
- }
220
- return await create();
221
- };
222
- }
223
- function createRemoteFacilitatorBinding(options) {
224
- const auth = options.auth;
225
- let createAuthHeaders;
226
- if (auth.type === "bearer") {
227
- createAuthHeaders = async () => {
228
- const headers = { Authorization: `Bearer ${auth.token}` };
229
- return { verify: headers, settle: headers, supported: headers };
230
- };
231
- } else if (auth.type === "cdp") {
232
- createAuthHeaders = cdpAuthHeaders(auth.apiKeyId, auth.apiKeySecret);
233
- }
234
- const client = new HTTPFacilitatorClient({
235
- url: options.url,
236
- timeoutMs: options.timeoutMs ?? SETTLEMENT_CONFIRMATION_TIMEOUT_MS,
237
- ...createAuthHeaders ? { createAuthHeaders } : {}
238
- });
239
- return {
240
- kind: "remote",
241
- describe: `remote ${new URL(options.url).origin} (auth=${auth.type})`,
242
- open() {
243
- let failed = false;
244
- const watch = async (call) => {
245
- try {
246
- return await call();
247
- } catch (err) {
248
- failed = true;
249
- throw err;
250
- }
251
- };
252
- return {
253
- verify: (payload, requirements) => watch(() => client.verify(payload, requirements)),
254
- settle: (payload, requirements) => watch(() => client.settle(payload, requirements)),
255
- transportFailed: () => failed
256
- };
257
- },
258
- async supported() {
259
- const response = await client.getSupported();
260
- return response.kinds;
261
- }
262
- };
263
- }
264
- function decodePaymentSubmission(payloadBase64) {
265
- let json;
266
- try {
267
- const decoded = Buffer.from(payloadBase64, "base64").toString("utf8");
268
- json = JSON.parse(decoded);
269
- } catch {
270
- return void 0;
271
- }
272
- const result = PaymentPayloadV2Schema.safeParse(json);
273
- return result.success ? result.data : void 0;
274
- }
275
- function isExactEvmPayload(payload) {
276
- return payload.accepted.scheme === "exact" && typeof payload.payload === "object" && payload.payload !== null && isEIP3009Payload(payload.payload) && typeof payload.payload.signature === "string";
277
- }
278
- function computeReplayKey(input) {
279
- const encoded = encodeAbiParameters(
280
- [{ type: "uint256" }, { type: "address" }, { type: "address" }, { type: "bytes32" }],
281
- [BigInt(input.chainId), input.asset, input.from, input.nonce]
282
- );
283
- return keccak256(encoded);
284
- }
285
-
286
- // src/payments/x402/provider.ts
287
- var X402_VERSION2 = 2;
288
- var X402_PROTOCOL_VERSION = String(X402_VERSION2);
289
- var DEFAULT_MAX_TIMEOUT_SECONDS = 60;
290
- var DEFAULT_MIME_TYPE = "application/json";
291
- var HEALTH_TIMEOUT_MS = 4e3;
292
- var SETTLEMENT_PENDING_REASON = "settlement_pending";
293
- var MAX_REASON_LENGTH = 64;
294
- var REASON_SHAPE = /^[a-z0-9_.-]+$/i;
295
- function sanitiseReason(reason, fallback) {
296
- if (reason === void 0) return fallback;
297
- const trimmed = reason.trim();
298
- if (trimmed.length === 0 || trimmed.length > MAX_REASON_LENGTH) return fallback;
299
- return REASON_SHAPE.test(trimmed) ? trimmed : fallback;
300
- }
301
- var DEFAULT_IDS = {
302
- next: (prefix) => `${prefix ? `${prefix}_` : ""}${crypto.randomUUID()}`
303
- };
304
- function createX402PaymentProvider(options) {
305
- if (!isAddress(options.asset)) {
306
- throw new CommerceError(
307
- "CONFIG_INVALID",
308
- `x402 provider: "asset" is not a valid EVM address: ${options.asset}`
309
- );
310
- }
311
- if (!isAddress(options.payTo)) {
312
- throw new CommerceError(
313
- "CONFIG_INVALID",
314
- `x402 provider: "payTo" is not a valid EVM address: ${options.payTo}`
315
- );
316
- }
317
- assertPayToIsNotDevAddress(options.rpcUrl, options.payTo);
318
- if (!Number.isInteger(options.assetDecimals) || options.assetDecimals < 0) {
319
- throw new CommerceError(
320
- "CONFIG_INVALID",
321
- `x402 provider: "assetDecimals" must be a non-negative integer`
322
- );
323
- }
324
- const { profile, mode } = resolveX402Deployment({
325
- network: options.network,
326
- payTo: options.payTo,
327
- asset: options.asset,
328
- assetName: options.assetName,
329
- assetVersion: options.assetVersion,
330
- facilitator: options.facilitator,
331
- ...options.allowMainnet !== void 0 ? { allowMainnet: options.allowMainnet } : {},
332
- ...options.allowUnauthenticatedFacilitator !== void 0 ? { allowUnauthenticatedFacilitator: options.allowUnauthenticatedFacilitator } : {}
333
- });
334
- const network = options.network;
335
- let binding;
336
- if (options.facilitator.mode === "local") {
337
- assertDevKeyIsLocalOnly(options.rpcUrl, options.facilitator.signerPrivateKey);
338
- let client;
339
- try {
340
- client = createLocalFacilitatorClient(
341
- options.rpcUrl,
342
- options.facilitator.signerPrivateKey
343
- );
344
- } catch (cause) {
345
- throw new CommerceError(
346
- "CONFIG_INVALID",
347
- "x402 provider: could not construct a local facilitator signer from facilitator.signerPrivateKey \u2014 it must be a valid 32-byte hex private key",
348
- { cause }
349
- );
350
- }
351
- binding = createLocalFacilitatorBinding(client, network, isProviderUnavailableError);
352
- } else {
353
- binding = createRemoteFacilitatorBinding({
354
- url: options.facilitator.url,
355
- auth: options.facilitator.auth
356
- });
357
- }
358
- const healthPublicClient = createLocalPublicClient(
359
- options.rpcUrl,
360
- HEALTH_TIMEOUT_MS,
361
- profile.chainId
362
- );
363
- const clock = options.clock ?? systemClock;
364
- const ids = options.ids ?? DEFAULT_IDS;
365
- const logger = options.logger ?? NOOP_LOGGER;
366
- const maxTimeoutSeconds = options.maxTimeoutSeconds ?? DEFAULT_MAX_TIMEOUT_SECONDS;
367
- const descriptor = {
368
- name: "x402",
369
- kind: "payment",
370
- implementationVersion: PACKAGE_VERSION,
371
- supportedSpec: `x402/v${X402_VERSION2} scheme=exact family=eip155`,
372
- capabilities: [
373
- "exact-evm",
374
- `${binding.kind}-facilitator`,
375
- "eip-3009",
376
- "caip-2",
377
- `mode=${mode}`
378
- ],
379
- status: "stable",
380
- unsupported: [
381
- "svm",
382
- "permit2",
383
- "upto scheme",
384
- "per-request signed facilitator credentials (e.g. CDP JWT)"
385
- ]
386
- };
387
- async function createRequirement(context) {
388
- const amount = parseCanonicalAmount(context.amount, options.assetDecimals).toString();
389
- const requirements = {
390
- scheme: "exact",
391
- network,
392
- asset: options.asset,
393
- amount,
394
- payTo: options.payTo,
395
- maxTimeoutSeconds,
396
- // `name`/`version` are what let a buyer and the facilitator build the
397
- // same EIP-712 domain without an on-chain `version()` call, so
398
- // verification stays deterministic against a token that has none.
399
- //
400
- // `assetTransferMethod` is stated rather than left to default: x402 v2's
401
- // `exact`/EVM scheme also has a Permit2 path, and a client that picked it
402
- // would produce a payload this provider cannot settle. Declaring the one
403
- // supported method turns that into a client-side non-choice instead of a
404
- // server-side rejection.
405
- extra: {
406
- name: options.assetName,
407
- version: options.assetVersion,
408
- assetTransferMethod: "eip3009"
409
- }
410
- };
411
- const envelope = {
412
- x402Version: X402_VERSION2,
413
- resource: {
414
- url: `resource://agent-commerce/${encodeURIComponent(options.network)}/${options.payTo}/resources/${encodeURIComponent(context.resource.id)}`,
415
- description: context.resource.description ?? context.resource.name,
416
- mimeType: DEFAULT_MIME_TYPE
417
- },
418
- accepts: [requirements]
419
- };
420
- const expiresAt = new Date(clock.now().getTime() + maxTimeoutSeconds * 1e3).toISOString();
421
- return {
422
- id: ids.next("payreq"),
423
- requestId: context.requestId,
424
- resourceId: context.resource.id,
425
- provider: "x402",
426
- amount: context.amount,
427
- currency: context.currency,
428
- destination: options.payTo,
429
- network: options.network,
430
- asset: options.asset,
431
- expiresAt,
432
- challenge: {
433
- provider: "x402",
434
- version: X402_PROTOCOL_VERSION,
435
- accepts: [requirements],
436
- envelope
437
- }
438
- };
439
- }
440
- async function verify(context) {
441
- const { requirement, submission } = context;
442
- const rejected = (reason) => ({
443
- status: "rejected",
444
- provider: "x402",
445
- amount: requirement.amount,
446
- currency: requirement.currency,
447
- ...requirement.network !== void 0 ? { network: requirement.network } : {},
448
- ...requirement.asset !== void 0 ? { asset: requirement.asset } : {},
449
- rejectionReason: reason
450
- });
451
- if (requirement.provider !== "x402" || requirement.challenge.provider !== "x402") {
452
- return rejected("wrong_provider");
453
- }
454
- const requirements = requirement.challenge.accepts[0];
455
- if (!requirements) {
456
- return rejected("missing_payment_requirements");
457
- }
458
- if (requirement.expiresAt !== void 0 && Date.parse(requirement.expiresAt) < clock.now().getTime()) {
459
- return rejected("requirement_expired");
460
- }
461
- if (!isAddress(requirements.asset) || getAddress(requirements.asset) !== getAddress(options.asset)) {
462
- return rejected("wrong_asset");
463
- }
464
- const payload = decodePaymentSubmission(submission.payload);
465
- if (!payload) {
466
- return rejected("malformed_payment_payload");
467
- }
468
- if (payload.x402Version !== X402_VERSION2) {
469
- return rejected("unsupported_x402_version");
470
- }
471
- if (!isExactEvmPayload(payload)) {
472
- return rejected("unsupported_scheme");
473
- }
474
- const { authorization } = payload.payload;
475
- if (!isAddress(authorization.from) || !isAddress(authorization.to)) {
476
- return rejected("malformed_payment_payload");
477
- }
478
- if (payload.accepted.network !== options.network) {
479
- return rejected("wrong_network");
480
- }
481
- if (getAddress(authorization.to) !== getAddress(options.payTo)) {
482
- return rejected("wrong_recipient");
483
- }
484
- let authorizedValue;
485
- let required;
486
- try {
487
- authorizedValue = BigInt(authorization.value);
488
- required = BigInt(requirements.amount);
489
- } catch {
490
- return rejected("malformed_payment_payload");
491
- }
492
- if (authorizedValue !== required) {
493
- return rejected("wrong_amount");
494
- }
495
- const scope = binding.open();
496
- let sdkResult;
497
- try {
498
- sdkResult = await scope.verify(payload, requirements);
499
- } catch (err) {
500
- if (isProviderUnavailableError(err) || scope.transportFailed()) {
501
- throw new CommerceError(
502
- "PAYMENT_PROVIDER_UNAVAILABLE",
503
- `x402 provider: ${binding.kind} facilitator unreachable during verify()`,
504
- {
505
- cause: err
506
- }
507
- );
508
- }
509
- logger.warn({ err: describeError(err) }, "x402 verify(): unexpected SDK error");
510
- return rejected("unexpected_verify_error");
511
- }
512
- if (!sdkResult.isValid) {
513
- if (scope.transportFailed()) {
514
- throw new CommerceError(
515
- "PAYMENT_PROVIDER_UNAVAILABLE",
516
- `x402 provider: ${binding.kind} facilitator unreachable during verify()`,
517
- { details: { reportedReason: sdkResult.invalidReason ?? "invalid_payment" } }
518
- );
519
- }
520
- if (sdkResult.invalidReason !== void 0) {
521
- logger.debug(
522
- { reportedReason: sdkResult.invalidReason },
523
- "x402 verify(): facilitator rejection reason"
524
- );
525
- }
526
- return rejected(sanitiseReason(sdkResult.invalidReason, "invalid_payment"));
527
- }
528
- const payloadChainId = chainIdFromCaip2(payload.accepted.network);
529
- if (payloadChainId === void 0) {
530
- return rejected("wrong_network");
531
- }
532
- const replayKey = computeReplayKey({
533
- chainId: payloadChainId,
534
- asset: getAddress(requirements.asset),
535
- from: getAddress(authorization.from),
536
- nonce: authorization.nonce
537
- });
538
- return {
539
- status: "verified",
540
- provider: "x402",
541
- payer: getAddress(authorization.from),
542
- payee: getAddress(authorization.to),
543
- amount: formatCanonicalAmount(authorizedValue, options.assetDecimals),
544
- currency: requirement.currency,
545
- network: payload.accepted.network,
546
- asset: getAddress(requirements.asset),
547
- replayKey
548
- };
549
- }
550
- async function settle(context) {
551
- const { requirement, submission, verification } = context;
552
- if (verification.status !== "verified") {
553
- throw new CommerceError(
554
- "PAYMENT_INVALID",
555
- "x402 provider: settle() called without a successful verify()"
556
- );
557
- }
558
- const requirements = requirement.challenge.accepts[0];
559
- if (!requirements) {
560
- throw new CommerceError(
561
- "PAYMENT_INVALID",
562
- "x402 provider: settle() found no payment requirements to settle against"
563
- );
564
- }
565
- const payload = decodePaymentSubmission(submission.payload);
566
- if (!payload || !isExactEvmPayload(payload)) {
567
- throw new CommerceError(
568
- "PAYMENT_INVALID",
569
- "x402 provider: settle() received an undecodable payment payload"
570
- );
571
- }
572
- const rejectedSettlement = (rejectionReason) => ({
573
- status: "rejected",
574
- provider: "x402",
575
- amount: verification.amount,
576
- currency: requirement.currency,
577
- ...verification.network !== void 0 ? { network: verification.network } : {},
578
- ...verification.asset !== void 0 ? { asset: verification.asset } : {},
579
- ...verification.replayKey !== void 0 ? { replayKey: verification.replayKey } : {},
580
- rejectionReason
581
- });
582
- const scope = binding.open();
583
- let sdkResult;
584
- try {
585
- sdkResult = await scope.settle(payload, requirements);
586
- } catch (err) {
587
- if (isProviderUnavailableError(err) || scope.transportFailed()) {
588
- throw new CommerceError(
589
- "PAYMENT_PROVIDER_UNAVAILABLE",
590
- `x402 provider: ${binding.kind} facilitator unreachable during settle()`,
591
- { cause: err }
592
- );
593
- }
594
- if (!isOnChainRevertError(err)) {
595
- logger.warn(
596
- { err: describeError(err) },
597
- "x402 settle(): settlement failed with an unclassified error; outcome unknown"
598
- );
599
- throw new CommerceError(
600
- "PAYMENT_PROVIDER_UNAVAILABLE",
601
- "x402 provider: settlement failed with an unclassified error; outcome unknown",
602
- { cause: err }
603
- );
604
- }
605
- logger.warn(
606
- { err: describeError(err), rejectionReason: "transaction_reverted" },
607
- "x402 settle(): settlement transaction reverted on chain"
608
- );
609
- return rejectedSettlement("transaction_reverted");
610
- }
611
- if (!sdkResult.success) {
612
- if (sdkResult.errorReason === SETTLEMENT_PENDING_REASON || scope.transportFailed()) {
613
- throw new CommerceError(
614
- "PAYMENT_PROVIDER_UNAVAILABLE",
615
- "x402 provider: settlement was broadcast but could not be confirmed",
616
- {
617
- ...sdkResult.transaction ? { details: { transactionHash: sdkResult.transaction } } : {}
618
- }
619
- );
620
- }
621
- if (sdkResult.errorReason !== void 0) {
622
- logger.debug(
623
- { reportedReason: sdkResult.errorReason },
624
- "x402 settle(): facilitator failure reason"
625
- );
626
- }
627
- return {
628
- ...rejectedSettlement(sanitiseReason(sdkResult.errorReason, "settlement_failed")),
629
- network: sdkResult.network,
630
- ...sdkResult.payer !== void 0 ? { payer: sdkResult.payer } : {}
631
- };
632
- }
633
- return {
634
- status: "settled",
635
- provider: "x402",
636
- externalReference: sdkResult.transaction,
637
- ...sdkResult.payer !== void 0 ? { payer: sdkResult.payer } : {},
638
- payee: requirements.payTo,
639
- amount: verification.amount,
640
- currency: requirement.currency,
641
- network: sdkResult.network,
642
- ...verification.asset !== void 0 ? { asset: verification.asset } : {},
643
- ...verification.replayKey !== void 0 ? { replayKey: verification.replayKey } : {},
644
- settledAt: clock.nowIso()
645
- };
646
- }
647
- async function computeHealth() {
648
- const startedAt = clock.monotonicMs();
649
- const checkedAt = clock.nowIso();
650
- try {
651
- const chainId = await healthPublicClient.getChainId();
652
- if (chainId !== profile.chainId) {
653
- return {
654
- status: "fail",
655
- detail: `RPC at ${options.rpcUrl} reports chain id ${chainId}, expected ${profile.chainId} (${profile.displayName})`,
656
- checkedAt,
657
- durationMs: clock.monotonicMs() - startedAt
658
- };
659
- }
660
- const code = await healthPublicClient.getCode({ address: options.asset });
661
- if (!code || code === "0x") {
662
- return {
663
- status: "fail",
664
- detail: `No contract code found at configured asset address ${options.asset}`,
665
- checkedAt,
666
- durationMs: clock.monotonicMs() - startedAt
667
- };
668
- }
669
- if (binding.kind === "remote") {
670
- const kinds = await binding.supported();
671
- const supportsUs = kinds.some(
672
- (kind) => kind.x402Version === X402_VERSION2 && kind.scheme === "exact" && kind.network === options.network
673
- );
674
- if (!supportsUs) {
675
- return {
676
- status: "fail",
677
- detail: `Facilitator ${binding.describe} does not advertise x402 v${X402_VERSION2} scheme=exact on ${options.network} (${profile.displayName}); no payment on this deployment can settle`,
678
- checkedAt,
679
- durationMs: clock.monotonicMs() - startedAt
680
- };
681
- }
682
- return {
683
- status: "pass",
684
- detail: `${describeDeploymentMode(mode)} \u2014 RPC ${options.rpcUrl} reachable, chain id ${profile.chainId} (${profile.displayName}), asset ${options.asset} has code, facilitator ${binding.describe} supports exact/${options.network}`,
685
- checkedAt,
686
- durationMs: clock.monotonicMs() - startedAt
687
- };
688
- }
689
- const isAnvil = await probeIsAnvilNode(healthPublicClient);
690
- if (!isAnvil) {
691
- return {
692
- status: "fail",
693
- detail: `RPC at ${options.rpcUrl} reports chain id ${profile.chainId} but does not answer "anvil_nodeInfo" \u2014 it does not look like a local Anvil dev node. Refusing to treat it as safe for a local-facilitator dev key.`,
694
- checkedAt,
695
- durationMs: clock.monotonicMs() - startedAt
696
- };
697
- }
698
- return {
699
- status: "pass",
700
- detail: `${describeDeploymentMode(mode)} \u2014 RPC ${options.rpcUrl} reachable, chain id ${profile.chainId}, asset ${options.asset} has code, confirmed Anvil dev node`,
701
- checkedAt,
702
- durationMs: clock.monotonicMs() - startedAt
703
- };
704
- } catch (err) {
705
- return {
706
- status: "fail",
707
- detail: `x402 health check failed: ${describeError(err)}`,
708
- checkedAt,
709
- durationMs: clock.monotonicMs() - startedAt
710
- };
711
- }
712
- }
713
- const HEALTH_CACHE_MS = 5e3;
714
- let cachedHealth;
715
- let inFlightHealth;
716
- async function health() {
717
- const now = clock.monotonicMs();
718
- if (cachedHealth && now - cachedHealth.at < HEALTH_CACHE_MS) {
719
- return cachedHealth.value;
720
- }
721
- if (inFlightHealth) return inFlightHealth;
722
- inFlightHealth = computeHealth().then((value) => {
723
- cachedHealth = { at: clock.monotonicMs(), value };
724
- return value;
725
- }).finally(() => {
726
- inFlightHealth = void 0;
727
- });
728
- return inFlightHealth;
729
- }
730
- return {
731
- name: "x402",
732
- descriptor,
733
- createRequirement,
734
- verify,
735
- settle,
736
- health
737
- };
738
- }
739
- function isOnChainRevertError(err) {
740
- return err instanceof ContractFunctionRevertedError || err instanceof ContractFunctionExecutionError;
741
- }
742
- function isProviderUnavailableError(err) {
743
- if (err instanceof HttpRequestError || err instanceof TimeoutError || err instanceof WaitForTransactionReceiptTimeoutError) {
744
- return true;
745
- }
746
- const message = describeError(err).toLowerCase();
747
- return message.includes("econnrefused") || message.includes("fetch failed") || message.includes("enotfound") || message.includes("etimedout") || message.includes("timed out") || message.includes("network error");
748
- }
749
- function describeError(err) {
750
- if (err instanceof Error) return `${err.name}: ${err.message}`;
751
- return String(err);
752
- }
753
- async function probeIsAnvilNode(client) {
754
- const request = client.request;
755
- try {
756
- await request({ method: "anvil_nodeInfo", params: [] });
757
- return true;
758
- } catch {
759
- return false;
760
- }
761
- }
762
-
763
- export { createPaymentProof, createX402PaymentProvider, createX402PaymentProvider as x402 };
83
+ export { createPaymentProof };
764
84
  //# sourceMappingURL=x402.js.map
765
85
  //# sourceMappingURL=x402.js.map