@devlab.group/agent-commerce 2.3.0 → 2.4.1

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 (62) hide show
  1. package/README.md +17 -13
  2. package/dist/ap2.d.ts +6 -5
  3. package/dist/ap2.js +3 -3
  4. package/dist/{authorization-CMkIuAkO.d.ts → authorization-DjAecwuW.d.ts} +1 -1
  5. package/dist/{chunk-TNS7U5Q4.js → chunk-CXWC5LWM.js} +200 -8
  6. package/dist/chunk-CXWC5LWM.js.map +1 -0
  7. package/dist/{chunk-Q5TII32R.js → chunk-DTKU3Z2M.js} +3 -3
  8. package/dist/{chunk-Q5TII32R.js.map → chunk-DTKU3Z2M.js.map} +1 -1
  9. package/dist/{chunk-XVU46YAH.js → chunk-QPV3EGJO.js} +4 -4
  10. package/dist/{chunk-XVU46YAH.js.map → chunk-QPV3EGJO.js.map} +1 -1
  11. package/dist/{chunk-4ZSE5DCH.js → chunk-WZW5ORZH.js} +3 -3
  12. package/dist/{chunk-4ZSE5DCH.js.map → chunk-WZW5ORZH.js.map} +1 -1
  13. package/dist/cli/{ap2-GINAXBFV.js → ap2-GWSP4LYY.js} +3 -3
  14. package/dist/cli/{ap2-GINAXBFV.js.map → ap2-GWSP4LYY.js.map} +1 -1
  15. package/dist/cli/{chunk-55N55KL4.js → chunk-G2JEY7MB.js} +22 -6
  16. package/dist/cli/chunk-G2JEY7MB.js.map +1 -0
  17. package/dist/cli/{chunk-KABFUZ6L.js → chunk-GJIMDMYT.js} +4 -4
  18. package/dist/cli/{chunk-KABFUZ6L.js.map → chunk-GJIMDMYT.js.map} +1 -1
  19. package/dist/cli/chunk-ITWNSS2B.js +8 -0
  20. package/dist/cli/{chunk-DYHVNKRB.js.map → chunk-ITWNSS2B.js.map} +1 -1
  21. package/dist/cli/{chunk-2LN5PO5I.js → chunk-PIMFPCNZ.js} +3 -2
  22. package/dist/cli/chunk-PIMFPCNZ.js.map +1 -0
  23. package/dist/cli/{chunk-BR7CMJOE.js → chunk-TNMLDPUO.js} +200 -8
  24. package/dist/cli/chunk-TNMLDPUO.js.map +1 -0
  25. package/dist/cli/{chunk-KTV52MCU.js → chunk-WYOGIANL.js} +3 -3
  26. package/dist/cli/{chunk-KTV52MCU.js.map → chunk-WYOGIANL.js.map} +1 -1
  27. package/dist/cli/{config-ZOFHJ5FB.js → config-UI4EDG4L.js} +5 -5
  28. package/dist/cli/{config-ZOFHJ5FB.js.map → config-UI4EDG4L.js.map} +1 -1
  29. package/dist/cli/index.js +87 -8
  30. package/dist/cli/index.js.map +1 -1
  31. package/dist/cli/{mcp-V3BNRUWL.js → mcp-PGBRWZVA.js} +3 -3
  32. package/dist/cli/{mcp-V3BNRUWL.js.map → mcp-PGBRWZVA.js.map} +1 -1
  33. package/dist/cli/{provider-BTIFALIT.js → provider-I2ESL6RS.js} +9 -6
  34. package/dist/cli/provider-I2ESL6RS.js.map +1 -0
  35. package/dist/cli/{run-DS2KXUTW.js → run-JK4ERPJV.js} +23 -12
  36. package/dist/cli/run-JK4ERPJV.js.map +1 -0
  37. package/dist/cli/{x402-YTKFNWVQ.js → x402-7WBFZQXV.js} +6 -6
  38. package/dist/cli/{x402-YTKFNWVQ.js.map → x402-7WBFZQXV.js.map} +1 -1
  39. package/dist/{constants-nackLItD.d.ts → constants-BG22tE8S.d.ts} +2 -1
  40. package/dist/index.d.ts +19 -11
  41. package/dist/index.js +32 -7
  42. package/dist/index.js.map +1 -1
  43. package/dist/mcp.d.ts +5 -4
  44. package/dist/mcp.js +1 -1
  45. package/dist/mpp.d.ts +5 -4
  46. package/dist/mpp.js +7 -4
  47. package/dist/mpp.js.map +1 -1
  48. package/dist/{networks-DTWRlj-K.d.ts → networks-DDHYJLcg.d.ts} +2 -1
  49. package/dist/{protocol-adapter-CfxU8Z2T.d.ts → protocol-adapter-C9odYl6l.d.ts} +4 -3
  50. package/dist/{runtime-7vLNYvmU.d.ts → runtime-Cl_BLVZV.d.ts} +8 -1
  51. package/dist/{types-CVw6Sf46.d.ts → types-j7MnUXYN.d.ts} +2 -1
  52. package/dist/x402.d.ts +27 -18
  53. package/dist/x402.js +4 -4
  54. package/package.json +6 -6
  55. package/dist/chunk-TNS7U5Q4.js.map +0 -1
  56. package/dist/cli/chunk-2LN5PO5I.js.map +0 -1
  57. package/dist/cli/chunk-55N55KL4.js.map +0 -1
  58. package/dist/cli/chunk-BR7CMJOE.js.map +0 -1
  59. package/dist/cli/chunk-DYHVNKRB.js +0 -8
  60. package/dist/cli/index.d.ts +0 -1
  61. package/dist/cli/provider-BTIFALIT.js.map +0 -1
  62. package/dist/cli/run-DS2KXUTW.js.map +0 -1
package/README.md CHANGED
@@ -279,16 +279,17 @@ See [docs/configuration.md](docs/configuration.md).
279
279
 
280
280
  ## Protocol support
281
281
 
282
- | Protocol | Status | Pinned revision |
283
- | -------- | ------------ | ------------------------------------------------------------------------------------------------------------ |
284
- | **HTTP** | Supported | native routes |
285
- | **MCP** | Supported | `2026-07-28` and `2025-11-25`, `@modelcontextprotocol/server@2.3.0` |
286
- | **x402** | Supported | x402 v2 (`@x402/core`, `@x402/evm`, `@x402/svm`, `@x402/near`), scheme `exact`, EVM and Solana; NEAR partial |
287
- | **MPP** | Experimental | `mppx@0.13.1`, `charge` + `evm` + EIP-3009, USDC on Base Sepolia and Base |
288
- | **A2A** | Experimental | A2A v1.0.1, binding `JSONRPC`, method `SendMessage`, x402 extension |
289
- | **ACP** | Experimental | ACP `2026-04-17`, REST checkout + discovery |
290
- | **AP2** | Experimental | AP2 `v0.2.0`, Direct Checkout Mandate verification |
291
- | UCP | Planned | no implementation |
282
+ | Protocol | Status | Pinned revision |
283
+ | ------------ | ------------ | ------------------------------------------------------------------------------------------------------------ |
284
+ | **HTTP** | Supported | native routes |
285
+ | **MCP** | Supported | `2026-07-28` and `2025-11-25`, `@modelcontextprotocol/server@2.3.0` |
286
+ | **x402** | Supported | x402 v2 (`@x402/core`, `@x402/evm`, `@x402/svm`, `@x402/near`), scheme `exact`, EVM and Solana; NEAR partial |
287
+ | **MPP** | Experimental | `mppx@0.13.1`, `charge` + `evm` + EIP-3009, USDC on Base Sepolia and Base |
288
+ | **A2A** | Experimental | A2A v1.0.1, binding `JSONRPC`, method `SendMessage`, x402 extension |
289
+ | **ACP** | Experimental | ACP `2026-04-17`, REST checkout + discovery |
290
+ | **AP2** | Experimental | AP2 `v0.2.0`, Direct Checkout Mandate verification |
291
+ | **ERC-8409** | Experimental | draft `SergeevDmitry/ERCs@0ee33230`: signed quotes on x402 EVM offers |
292
+ | UCP | Planned | no implementation |
292
293
 
293
294
  [AP2](docs/protocols/ap2.md) is an authorization method, not a transport. It verifies a
294
295
  mandate before a paid resource settles; the gateway does not issue the mandate
@@ -296,7 +297,8 @@ or its Checkout Receipt.
296
297
 
297
298
  The experimental entries above ship off by default with documented subsets:
298
299
  [A2A](docs/protocols/a2a.md), [ACP](docs/protocols/acp.md),
299
- [AP2](docs/protocols/ap2.md#not-implemented) and [MPP](docs/protocols/mpp.md). Supported
300
+ [AP2](docs/protocols/ap2.md#not-implemented), [MPP](docs/protocols/mpp.md) and
301
+ [ERC-8409 quotes](docs/protocols/x402.md#erc-8409-signed-quotes). Supported
300
302
  [MCP](docs/protocols/mcp.md) and [x402](docs/protocols/x402.md) are also scoped.
301
303
  ACP exposes only its five checkout operations; merchant `payment_data` is never
302
304
  converted into a gateway payment proof.
@@ -308,7 +310,8 @@ prints the `unsupported` lists for A2A, ACP and AP2 when they are enabled.
308
310
  ### Versioning
309
311
 
310
312
  Minor releases may break experimental configurations, integrations or types.
311
- This applies to MPP, A2A, ACP, AP2 and `agent-commerce import openapi`.
313
+ This applies to MPP, A2A, ACP, AP2, ERC-8409 quotes and
314
+ `agent-commerce import openapi`.
312
315
  Runtime adapters and providers report `status: experimental` in
313
316
  `GET /.well-known/agent-commerce`. Pin an exact version and test upgrades
314
317
  before deploying them. Since 2.1, some supported x402 behavior and types
@@ -318,7 +321,8 @@ differ from 2.0; see [Upgrading from 2.0 to 2.x](docs/migration-2.1.md).
318
321
 
319
322
  - **Non-custodial.** Buyer payments go directly to the merchant address in
320
323
  `payTo` (`recipient` for MPP). The gateway does not require buyer or merchant
321
- private keys. Local facilitator mode does hold a separate gas-paying signer.
324
+ private keys. Local facilitator mode uses a separate key to pay gas.
325
+ Experimental ERC-8409 quotes use a dedicated, unfunded signing key.
322
326
  - **Fail closed.** Missing, malformed, expired, replayed, wrong-amount,
323
327
  wrong-recipient, wrong-network and wrong-asset payments are rejected.
324
328
  - **Replay-safe twice over.** The EIP-3009 or Permit2 nonce stops a double
package/dist/ap2.d.ts CHANGED
@@ -1,7 +1,7 @@
1
- import { E as EnabledAp2Config } from './types-CVw6Sf46.js';
2
- export { A as AP2_CHECKOUT_MANDATE_VCT, a as AP2_CHECKOUT_PROFILE, b as AP2_DEFAULT_CLOCK_SKEW_SECONDS, c as AP2_MAX_CLOCK_SKEW_SECONDS, d as AP2_SIGNING_ALGORITHM, e as AP2_SPEC_VERSION, f as Ap2AuthorizationConfig, g as Ap2Mode, h as Ap2TrustedIssuer, i as Ap2TrustedKey } from './types-CVw6Sf46.js';
3
- import { A as AuthorizationProvider } from './authorization-CMkIuAkO.js';
4
- import { C as Clock, L as Logger } from './runtime-7vLNYvmU.js';
1
+ import { E as EnabledAp2Config } from './types-j7MnUXYN.js';
2
+ export { A as AP2_CHECKOUT_MANDATE_VCT, a as AP2_CHECKOUT_PROFILE, b as AP2_DEFAULT_CLOCK_SKEW_SECONDS, c as AP2_MAX_CLOCK_SKEW_SECONDS, d as AP2_SIGNING_ALGORITHM, e as AP2_SPEC_VERSION, f as Ap2AuthorizationConfig, g as Ap2Mode, h as Ap2TrustedIssuer, i as Ap2TrustedKey } from './types-j7MnUXYN.js';
3
+ import { AuthorizationProvider } from './index.js';
4
+ import { Clock, Logger } from './index.js';
5
5
 
6
6
  /**
7
7
  * A private ES256 key: either a private JWK (the pair of the public one in the
@@ -158,4 +158,5 @@ interface Ap2AuthorizationProvider extends AuthorizationProvider {
158
158
  }
159
159
  declare function createAp2AuthorizationProvider(options: Ap2AuthorizationProviderOptions): Ap2AuthorizationProvider;
160
160
 
161
- export { AP2_CAPABILITIES, AP2_REJECTION_REASONS, AP2_UNSUPPORTED, type Ap2AuthorizationProvider, type Ap2AuthorizationProviderOptions, type Ap2RejectionReason, type Ap2SigningKey, type CreateCheckoutJwtOptions, EnabledAp2Config, createAp2AuthorizationProvider as ap2, createAp2AuthorizationProvider, createCheckoutJwt };
161
+ export { AP2_CAPABILITIES, AP2_REJECTION_REASONS, AP2_UNSUPPORTED, EnabledAp2Config, createAp2AuthorizationProvider as ap2, createAp2AuthorizationProvider, createCheckoutJwt };
162
+ export type { Ap2AuthorizationProvider, Ap2AuthorizationProviderOptions, Ap2RejectionReason, Ap2SigningKey, CreateCheckoutJwtOptions };
package/dist/ap2.js CHANGED
@@ -1,6 +1,6 @@
1
- import { AP2_CHECKOUT_MANDATE_VCT, AP2_SIGNING_ALGORITHM, AP2_DIGEST_ALGORITHM, AP2_CHECKOUT_PROFILE, AP2_SPEC_VERSION, AP2_KEY_TYPE, AP2_JWK_CURVE, openSqliteDatabase } from './chunk-4ZSE5DCH.js';
2
- export { AP2_CHECKOUT_MANDATE_VCT, AP2_CHECKOUT_PROFILE, AP2_DEFAULT_CLOCK_SKEW_SECONDS, AP2_MAX_CLOCK_SKEW_SECONDS, AP2_SIGNING_ALGORITHM, AP2_SPEC_VERSION } from './chunk-4ZSE5DCH.js';
3
- import { systemClock, NOOP_LOGGER, CommerceError, PACKAGE_VERSION } from './chunk-Q5TII32R.js';
1
+ import { AP2_CHECKOUT_MANDATE_VCT, AP2_SIGNING_ALGORITHM, AP2_DIGEST_ALGORITHM, AP2_CHECKOUT_PROFILE, AP2_SPEC_VERSION, AP2_KEY_TYPE, AP2_JWK_CURVE, openSqliteDatabase } from './chunk-WZW5ORZH.js';
2
+ export { AP2_CHECKOUT_MANDATE_VCT, AP2_CHECKOUT_PROFILE, AP2_DEFAULT_CLOCK_SKEW_SECONDS, AP2_MAX_CLOCK_SKEW_SECONDS, AP2_SIGNING_ALGORITHM, AP2_SPEC_VERSION } from './chunk-WZW5ORZH.js';
3
+ import { systemClock, NOOP_LOGGER, CommerceError, PACKAGE_VERSION } from './chunk-DTKU3Z2M.js';
4
4
  import { isRecord } from './chunk-2CJNA7MS.js';
5
5
  import { SignJWT, importPKCS8, importJWK, jwtVerify, decodeProtectedHeader, decodeJwt } from 'jose';
6
6
  import canonicalize from 'canonicalize';
@@ -1,4 +1,4 @@
1
- import { g as AuthorizationMethodName, A as AdapterDescriptor, n as PaymentRequirement, c as AdapterHealth } from './runtime-7vLNYvmU.js';
1
+ import { g as AuthorizationMethodName, A as AdapterDescriptor, n as PaymentRequirement, c as AdapterHealth } from './runtime-Cl_BLVZV.js';
2
2
 
3
3
  /**
4
4
  * Canonical authorization model. FROZEN CONTRACT.
@@ -1,14 +1,15 @@
1
- import { findX402Network, normalizeAddress, assertPayToIsNotDevAddress, resolveX402Deployment, assertDevKeyIsLocalOnly, describeRpc, X402_DEFAULT_PAYMENT_FLOW, REUSE_REJECTED_AUTHORIZATION_METADATA_KEY, SETTLE_AFTER_BACKEND_METADATA_KEY, sameAddress, LOCAL_CHAIN_ID, isTransactionReference, x402OfferName, isEvmAddress, describeDeploymentMode } from './chunk-XVU46YAH.js';
2
- import { CommerceError, systemClock, NOOP_LOGGER, PACKAGE_VERSION, redactedErrorText } from './chunk-Q5TII32R.js';
1
+ import { isWellKnownDevKey, findX402Network, normalizeAddress, assertPayToIsNotDevAddress, resolveX402Deployment, assertDevKeyIsLocalOnly, describeRpc, X402_DEFAULT_PAYMENT_FLOW, REUSE_REJECTED_AUTHORIZATION_METADATA_KEY, SETTLE_AFTER_BACKEND_METADATA_KEY, sameAddress, LOCAL_CHAIN_ID, isTransactionReference, x402OfferName, isEvmAddress, describeDeploymentMode } from './chunk-QPV3EGJO.js';
2
+ import { CommerceError, systemClock, NOOP_LOGGER, PACKAGE_VERSION, redactedErrorText } from './chunk-DTKU3Z2M.js';
3
3
  import { computeReplayKey, computePermit2ReplayKey } from './chunk-KA2AX5FH.js';
4
4
  import { isRecord } from './chunk-2CJNA7MS.js';
5
5
  import { x402ExactPermit2ProxyAddress, toFacilitatorEvmSigner, PERMIT2_ADDRESS } from '@x402/evm';
6
- import { isAddress, getAddress, formatUnits, createWalletClient, http, publicActions, createPublicClient, HttpRequestError, TimeoutError, WaitForTransactionReceiptTimeoutError, BaseError, ContractFunctionRevertedError, erc20Abi } from 'viem';
6
+ import { isAddress, keccak256, stringToBytes, getAddress, formatUnits, createWalletClient, http, publicActions, createPublicClient, HttpRequestError, TimeoutError, WaitForTransactionReceiptTimeoutError, BaseError, ContractFunctionRevertedError, erc20Abi } from 'viem';
7
7
  import { privateKeyToAccount } from 'viem/accounts';
8
8
  import { HTTPFacilitatorClient } from '@x402/core/http';
9
9
  import { SettleError, VerifyError } from '@x402/core/types';
10
10
  import { x402Facilitator } from '@x402/core/facilitator';
11
11
  import { registerExactEvmScheme } from '@x402/evm/exact/facilitator';
12
+ import { randomBytes } from 'crypto';
12
13
  import { PaymentPayloadV2Schema } from '@x402/core/schemas';
13
14
 
14
15
  // src/payments/x402/provider/constants.ts
@@ -770,6 +771,172 @@ function offerCurrency(offer, quoted) {
770
771
  return canonical !== void 0 && sameAddress(offer.profile, offer.asset, canonical.address) ? canonical.symbol : quoted;
771
772
  }
772
773
 
774
+ // src/core/canonical-json.ts
775
+ var LONE_SURROGATE = new RegExp("\\p{Cs}", "u");
776
+ function canonicalJson(value) {
777
+ if (typeof value === "string")
778
+ return LONE_SURROGATE.test(value) ? void 0 : JSON.stringify(value);
779
+ if (typeof value === "number") return Number.isFinite(value) ? JSON.stringify(value) : void 0;
780
+ if (value === null || typeof value === "boolean") return JSON.stringify(value);
781
+ if (Array.isArray(value)) {
782
+ const items = value.map(canonicalJson);
783
+ return items.includes(void 0) ? void 0 : `[${items.join(",")}]`;
784
+ }
785
+ if (typeof value !== "object") return void 0;
786
+ const members = [];
787
+ for (const key of Object.keys(value).sort()) {
788
+ const member = canonicalJson(value[key]);
789
+ if (member === void 0 || LONE_SURROGATE.test(key)) return void 0;
790
+ members.push(`${JSON.stringify(key)}:${member}`);
791
+ }
792
+ return `{${members.join(",")}}`;
793
+ }
794
+
795
+ // src/payments/x402/provider/quote.ts
796
+ var QUOTE_EXTENSION = "erc-8409";
797
+ var QUOTE_REQUEST_SCHEME = "agent-commerce/request/v1";
798
+ var QUOTE_DOMAIN = { name: "Signed Service Payment Quotes", version: "1" };
799
+ var QUOTE_TYPES = {
800
+ ServicePaymentQuote: [
801
+ { name: "issuer", type: "address" },
802
+ { name: "payer", type: "address" },
803
+ { name: "recipient", type: "address" },
804
+ { name: "asset", type: "address" },
805
+ { name: "amount", type: "uint256" },
806
+ { name: "requestScheme", type: "string" },
807
+ { name: "requestHash", type: "bytes32" },
808
+ { name: "quoteId", type: "bytes32" },
809
+ { name: "validAfter", type: "uint64" },
810
+ { name: "validUntil", type: "uint64" }
811
+ ]
812
+ };
813
+ var BEARER = "0x0000000000000000000000000000000000000000";
814
+ var QUOTE_INFO_SCHEMA = {
815
+ type: "object",
816
+ required: ["quotes"],
817
+ properties: {
818
+ quotes: {
819
+ type: "array",
820
+ items: {
821
+ type: "object",
822
+ required: ["chainId", "quote", "signature"],
823
+ properties: {
824
+ chainId: { type: "string" },
825
+ // Every field is a string in the ERC's JSON encoding
826
+ quote: {
827
+ type: "object",
828
+ required: QUOTE_TYPES.ServicePaymentQuote.map((field) => field.name),
829
+ properties: Object.fromEntries(
830
+ QUOTE_TYPES.ServicePaymentQuote.map((field) => [field.name, { type: "string" }])
831
+ )
832
+ },
833
+ signature: { type: "string" }
834
+ }
835
+ }
836
+ }
837
+ }
838
+ };
839
+ var QUOTE_CACHE_LIMIT = 1024;
840
+ function createQuoteIssuer(options) {
841
+ const { signerPrivateKey, offers, deploymentMode, clock, maxTimeoutSeconds } = options;
842
+ let account;
843
+ try {
844
+ account = privateKeyToAccount(signerPrivateKey);
845
+ } catch (cause) {
846
+ throw new CommerceError(
847
+ "CONFIG_INVALID",
848
+ 'x402 provider: quote.signerPrivateKey is not a valid private key (expected "0x" followed by 64 hex characters)',
849
+ { cause }
850
+ );
851
+ }
852
+ if (deploymentMode !== "local" && isWellKnownDevKey(signerPrivateKey)) {
853
+ throw new CommerceError(
854
+ "CONFIG_INVALID",
855
+ "x402 provider: quote.signerPrivateKey is a well-known Anvil development key, so anyone could sign quotes as this gateway. Use a dedicated key outside the local chain."
856
+ );
857
+ }
858
+ const evmOffers = offers.filter((offer) => offer.family === "evm");
859
+ if (evmOffers.length === 0) {
860
+ throw new CommerceError(
861
+ "CONFIG_INVALID",
862
+ "x402 provider: quote needs an EVM offer; ERC-8409 quotes cover EVM networks only"
863
+ );
864
+ }
865
+ const issuer = account.address;
866
+ if (evmOffers.some((offer) => offer.payTo.toLowerCase() === issuer.toLowerCase())) {
867
+ throw new CommerceError(
868
+ "CONFIG_INVALID",
869
+ "x402 provider: quote.signerPrivateKey controls a payTo address. Use a dedicated key, not the merchant wallet."
870
+ );
871
+ }
872
+ const cache = /* @__PURE__ */ new Map();
873
+ async function quoteFor(offer, amount, requestHash) {
874
+ const now = Math.floor(clock.now().getTime() / 1e3);
875
+ const { chainId } = offer.profile;
876
+ const key = [
877
+ chainId,
878
+ offer.payTo.toLowerCase(),
879
+ offer.asset.toLowerCase(),
880
+ amount,
881
+ requestHash
882
+ ].join("|");
883
+ const cached = cache.get(key);
884
+ if (cached !== void 0 && Number(cached.quote.validUntil) - now >= maxTimeoutSeconds) {
885
+ return cached;
886
+ }
887
+ const validUntil = now + 2 * maxTimeoutSeconds;
888
+ const quote = {
889
+ issuer,
890
+ payer: BEARER,
891
+ recipient: offer.payTo,
892
+ asset: offer.asset,
893
+ amount,
894
+ requestScheme: QUOTE_REQUEST_SCHEME,
895
+ requestHash,
896
+ // Use a new ID for each signed body, as the ERC requires
897
+ quoteId: `0x${randomBytes(32).toString("hex")}`,
898
+ validAfter: String(now),
899
+ validUntil: String(validUntil)
900
+ };
901
+ const signature = await account.signTypedData({
902
+ domain: { ...QUOTE_DOMAIN, chainId },
903
+ types: QUOTE_TYPES,
904
+ primaryType: "ServicePaymentQuote",
905
+ message: {
906
+ ...quote,
907
+ amount: BigInt(amount),
908
+ validAfter: BigInt(now),
909
+ validUntil: BigInt(validUntil)
910
+ }
911
+ });
912
+ const signed = { chainId: String(chainId), quote, signature };
913
+ cache.delete(key);
914
+ if (cache.size >= QUOTE_CACHE_LIMIT) {
915
+ const oldest = cache.keys().next().value;
916
+ if (oldest !== void 0) cache.delete(oldest);
917
+ }
918
+ cache.set(key, signed);
919
+ return signed;
920
+ }
921
+ return {
922
+ issuer,
923
+ async extensionsFor(resourceId, input, quoted) {
924
+ if (quoted.length === 0) return void 0;
925
+ const requestHash = quoteRequestHash(resourceId, input);
926
+ if (requestHash === void 0) return void 0;
927
+ const quotes = [];
928
+ for (const { offer, amount } of quoted) {
929
+ quotes.push(await quoteFor(offer, amount, requestHash));
930
+ }
931
+ return { [QUOTE_EXTENSION]: { info: { quotes }, schema: QUOTE_INFO_SCHEMA } };
932
+ }
933
+ };
934
+ }
935
+ function quoteRequestHash(resourceId, input) {
936
+ const canonical = canonicalJson({ input, resource: resourceId });
937
+ return canonical === void 0 ? void 0 : keccak256(stringToBytes(canonical));
938
+ }
939
+
773
940
  // src/payments/x402/amount.ts
774
941
  var DECIMAL_STRING_PATTERN = /^\d+(?:\.\d+)?$/;
775
942
  function parseCanonicalAmount(amount, decimals) {
@@ -883,6 +1050,14 @@ function createRequirementBuilders(ctx, svmFeePayer) {
883
1050
  }))
884
1051
  );
885
1052
  const single = offers.length === 1 ? offers[0] : void 0;
1053
+ const extensions = await ctx.quotes?.extensionsFor(
1054
+ context.resource.id,
1055
+ context.input,
1056
+ offers.flatMap((offer, index) => {
1057
+ const accepted = accepts[index];
1058
+ return offer.family === "evm" && accepted ? [{ offer, amount: accepted.amount }] : [];
1059
+ })
1060
+ );
886
1061
  const resourceId = encodeURIComponent(context.resource.id);
887
1062
  const envelope = {
888
1063
  x402Version: X402_VERSION,
@@ -893,7 +1068,8 @@ function createRequirementBuilders(ctx, svmFeePayer) {
893
1068
  // delivery carries the backend's own type when it is text and fits the body
894
1069
  mimeType: options.resourceMimeTypes?.[context.resource.id] ?? DEFAULT_MIME_TYPE
895
1070
  },
896
- accepts
1071
+ accepts,
1072
+ ...extensions !== void 0 ? { extensions } : {}
897
1073
  };
898
1074
  const expiresAt = new Date(clock.now().getTime() + maxTimeoutSeconds * 1e3).toISOString();
899
1075
  return {
@@ -1319,6 +1495,13 @@ function createX402PaymentProvider(options) {
1319
1495
  const ids = options.ids ?? DEFAULT_IDS;
1320
1496
  const logger = options.logger ?? NOOP_LOGGER;
1321
1497
  const maxTimeoutSeconds = options.maxTimeoutSeconds ?? DEFAULT_MAX_TIMEOUT_SECONDS;
1498
+ const quotes = options.quote ? createQuoteIssuer({
1499
+ signerPrivateKey: options.quote.signerPrivateKey,
1500
+ offers,
1501
+ deploymentMode,
1502
+ clock,
1503
+ maxTimeoutSeconds
1504
+ }) : void 0;
1322
1505
  const evmOffers = offers.filter((offer) => offer.family === "evm");
1323
1506
  const hasSvm = offers.some((offer) => offer.family === "svm");
1324
1507
  const hasNear = offers.some((offer) => offer.family === "near");
@@ -1342,7 +1525,8 @@ function createX402PaymentProvider(options) {
1342
1525
  `mode=${deploymentMode}`,
1343
1526
  "authorization-flow",
1344
1527
  "upfront-flow",
1345
- ...severalOffers ? ["multiple-offers"] : []
1528
+ ...severalOffers ? ["multiple-offers"] : [],
1529
+ ...quotes ? ["erc-8409-quotes"] : []
1346
1530
  ],
1347
1531
  status: "stable",
1348
1532
  unsupported: [
@@ -1374,6 +1558,7 @@ function createX402PaymentProvider(options) {
1374
1558
  ids,
1375
1559
  logger,
1376
1560
  maxTimeoutSeconds,
1561
+ quotes,
1377
1562
  svm,
1378
1563
  near
1379
1564
  };
@@ -1392,7 +1577,14 @@ function createX402PaymentProvider(options) {
1392
1577
  ...severalOffers ? { selectRequirement } : {},
1393
1578
  verify,
1394
1579
  settle,
1395
- health
1580
+ health,
1581
+ ...quotes ? {
1582
+ quote: {
1583
+ issuer: quotes.issuer,
1584
+ requestScheme: QUOTE_REQUEST_SCHEME,
1585
+ extension: QUOTE_EXTENSION
1586
+ }
1587
+ } : {}
1396
1588
  };
1397
1589
  }
1398
1590
  function loadOptionalModule(load, missing) {
@@ -1407,5 +1599,5 @@ function loadOptionalModule(load, missing) {
1407
1599
  }
1408
1600
 
1409
1601
  export { DEFAULT_IDS, chainIdFromCaip2, createX402PaymentProvider, parseCanonicalAmount };
1410
- //# sourceMappingURL=chunk-TNS7U5Q4.js.map
1411
- //# sourceMappingURL=chunk-TNS7U5Q4.js.map
1602
+ //# sourceMappingURL=chunk-CXWC5LWM.js.map
1603
+ //# sourceMappingURL=chunk-CXWC5LWM.js.map