@haven_ai/sdk 0.1.23-alpha.2 → 0.1.25-alpha.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.
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { AsyncLocalStorage } from 'async_hooks';
2
2
  import { exact } from 'x402/schemes';
3
- import { hashTypedData } from 'viem';
3
+ import { recoverTypedDataAddress, hashTypedData } from 'viem';
4
4
  import { privateKeyToAccount } from 'viem/accounts';
5
5
  import { ethers } from 'ethers';
6
6
  import { createHash } from 'crypto';
@@ -342,7 +342,7 @@ function signHash(privateKey, hash) {
342
342
  );
343
343
  }
344
344
  }
345
- async function signUserOpTypedDataForDelegation(privateKey, typedData) {
345
+ async function signTypedDataVerbatim(privateKey, typedData, label) {
346
346
  try {
347
347
  const wallet = new ethers.Wallet(privateKey);
348
348
  const types = { ...typedData.types };
@@ -354,10 +354,16 @@ async function signUserOpTypedDataForDelegation(privateKey, typedData) {
354
354
  );
355
355
  } catch (err) {
356
356
  throw new HavenSigningError(
357
- `Failed to sign delegation UserOperation: ${err instanceof Error ? err.message : String(err)}`
357
+ `Failed to sign ${label}: ${err instanceof Error ? err.message : String(err)}`
358
358
  );
359
359
  }
360
360
  }
361
+ async function signUserOpTypedDataForDelegation(privateKey, typedData) {
362
+ return signTypedDataVerbatim(privateKey, typedData, "delegation UserOperation");
363
+ }
364
+ async function signSettlementDelegationTypedData(privateKey, typedData) {
365
+ return signTypedDataVerbatim(privateKey, typedData, "x402 settlement delegation");
366
+ }
361
367
  function addressFromKey(privateKey) {
362
368
  try {
363
369
  return new ethers.Wallet(privateKey).address;
@@ -435,12 +441,128 @@ function decodeBase64Json(value, label) {
435
441
  }
436
442
  }
437
443
 
444
+ // src/sweep.ts
445
+ var SWEEP_BASE_CHAIN_ID = 8453;
446
+ var SWEEP_BASE_SEPOLIA_CHAIN_ID = 84532;
447
+ var SWEEP_BASE_USDC_ADDRESS = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
448
+ var SWEEP_BASE_SEPOLIA_USDC_ADDRESS = "0x036CbD53842c5426634e7929541eC2318f3dCF7e";
449
+ var USDC_EIP712_DOMAIN_BY_CHAIN = {
450
+ [SWEEP_BASE_CHAIN_ID]: {
451
+ name: "USD Coin",
452
+ version: "2",
453
+ chainId: SWEEP_BASE_CHAIN_ID,
454
+ verifyingContract: SWEEP_BASE_USDC_ADDRESS
455
+ },
456
+ [SWEEP_BASE_SEPOLIA_CHAIN_ID]: {
457
+ name: "USDC",
458
+ version: "2",
459
+ chainId: SWEEP_BASE_SEPOLIA_CHAIN_ID,
460
+ verifyingContract: SWEEP_BASE_SEPOLIA_USDC_ADDRESS
461
+ }
462
+ };
463
+ var USDC_ADDRESS_BY_CHAIN = {
464
+ [SWEEP_BASE_CHAIN_ID]: SWEEP_BASE_USDC_ADDRESS,
465
+ [SWEEP_BASE_SEPOLIA_CHAIN_ID]: SWEEP_BASE_SEPOLIA_USDC_ADDRESS
466
+ };
467
+ var SWEEPABLE_CHAIN_IDS = Object.keys(USDC_ADDRESS_BY_CHAIN).map(Number);
468
+ function isSweepableChain(chainId) {
469
+ return chainId in USDC_ADDRESS_BY_CHAIN;
470
+ }
471
+ var TRANSFER_WITH_AUTHORIZATION_TYPES = {
472
+ TransferWithAuthorization: [
473
+ { name: "from", type: "address" },
474
+ { name: "to", type: "address" },
475
+ { name: "value", type: "uint256" },
476
+ { name: "validAfter", type: "uint256" },
477
+ { name: "validBefore", type: "uint256" },
478
+ { name: "nonce", type: "bytes32" }
479
+ ]
480
+ };
481
+ function sweepUsdcAddress(chainId) {
482
+ const address = USDC_ADDRESS_BY_CHAIN[chainId];
483
+ if (!address) {
484
+ throw new HavenSigningError(
485
+ `Sweep is not supported on chain ${chainId}. Supported: ${SWEEPABLE_CHAIN_IDS.join(", ")}.`
486
+ );
487
+ }
488
+ return address;
489
+ }
490
+ function sweepUsdcDomain(chainId) {
491
+ const domain = USDC_EIP712_DOMAIN_BY_CHAIN[chainId];
492
+ if (!domain) {
493
+ throw new HavenSigningError(
494
+ `Sweep is not supported on chain ${chainId}. Supported: ${SWEEPABLE_CHAIN_IDS.join(", ")}.`
495
+ );
496
+ }
497
+ return domain;
498
+ }
499
+ function sameAddress(a, b) {
500
+ return a.toLowerCase() === b.toLowerCase();
501
+ }
502
+ function buildSweepTypedData(auth) {
503
+ const domain = sweepUsdcDomain(auth.chainId);
504
+ const expectedToken = sweepUsdcAddress(auth.chainId);
505
+ if (!sameAddress(auth.token, expectedToken)) {
506
+ throw new HavenSigningError(
507
+ `Sweep token ${auth.token} is not the canonical USDC contract for chain ${auth.chainId}.`
508
+ );
509
+ }
510
+ if (!/^0x[0-9a-fA-F]{64}$/.test(auth.nonce)) {
511
+ throw new HavenSigningError("Sweep nonce must be a 0x-prefixed 32-byte hex string.");
512
+ }
513
+ return {
514
+ domain,
515
+ types: TRANSFER_WITH_AUTHORIZATION_TYPES,
516
+ primaryType: "TransferWithAuthorization",
517
+ message: {
518
+ from: auth.from,
519
+ to: auth.to,
520
+ value: BigInt(auth.value),
521
+ validAfter: BigInt(auth.validAfter),
522
+ validBefore: BigInt(auth.validBefore),
523
+ nonce: auth.nonce
524
+ }
525
+ };
526
+ }
527
+ function buildSweepAuthorizationMessage(auth) {
528
+ return `Haven sweep authorization v1
529
+ ${stableStringify({
530
+ version: 1,
531
+ kind: "haven.sweep.authorization",
532
+ from: auth.from.toLowerCase(),
533
+ to: auth.to.toLowerCase(),
534
+ value: auth.value,
535
+ validAfter: auth.validAfter,
536
+ validBefore: auth.validBefore,
537
+ nonce: auth.nonce.toLowerCase(),
538
+ token: auth.token.toLowerCase(),
539
+ chainId: auth.chainId
540
+ })}`;
541
+ }
542
+ function stableStringify(value) {
543
+ if (value === null || typeof value !== "object") return JSON.stringify(value);
544
+ if (Array.isArray(value)) return `[${value.map((item) => stableStringify(item)).join(",")}]`;
545
+ const object = value;
546
+ return `{${Object.keys(object).sort().map((key) => `${JSON.stringify(key)}:${stableStringify(object[key])}`).join(",")}}`;
547
+ }
548
+
438
549
  // src/x402.ts
439
550
  var BASE_USDC_ADDRESS = "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913";
440
551
  var BASE_SEPOLIA_USDC_ADDRESS = "0x036cbd53842c5426634e7929541ec2318f3dcf7e";
441
552
  var STANDARD_X402_USDC_ADDRESSES = /* @__PURE__ */ new Set([BASE_USDC_ADDRESS, BASE_SEPOLIA_USDC_ADDRESS]);
442
553
  var X402_IDEMPOTENCY_BUCKET_MS = 3e5;
443
554
  var DECIMAL_ATOMIC_AMOUNT_RE = /^[0-9]+$/;
555
+ var X402_PAYMENT_HEADER_MAX_LENGTH = 65536;
556
+ var BASE64_RE = /^[A-Za-z0-9+/]*={0,2}$/;
557
+ var ADDRESS_RE = /^0x[0-9a-fA-F]{40}$/;
558
+ var SIGNATURE_RE = /^0x[0-9a-fA-F]{130}$/;
559
+ var NONCE_RE = /^0x[0-9a-fA-F]{64}$/;
560
+ var X402PaymentHeaderValidationError = class extends Error {
561
+ constructor() {
562
+ super("Invalid X-PAYMENT header.");
563
+ this.name = "X402PaymentHeaderValidationError";
564
+ }
565
+ };
444
566
  function isPositiveDecimalAtomicAmount(value) {
445
567
  return DECIMAL_ATOMIC_AMOUNT_RE.test(value) && BigInt(value) > 0n;
446
568
  }
@@ -483,7 +605,7 @@ function normalizePaymentRequired(value) {
483
605
  if (!candidate || typeof candidate !== "object" || typeof candidate.x402Version !== "number" || !Array.isArray(candidate.accepts)) {
484
606
  return null;
485
607
  }
486
- const accepts = candidate.accepts.map((option) => normalizePaymentOption(option)).filter((option) => !!option);
608
+ const accepts = candidate.accepts.map((option) => normalizePaymentOption(option)).filter((option) => !!option && typeof option === "object");
487
609
  if (accepts.length === 0) return null;
488
610
  const first = accepts[0];
489
611
  const resourceUrl = candidate.resource?.url ?? first.resource;
@@ -588,13 +710,52 @@ function selectPaymentOption(accepts) {
588
710
  }
589
711
  return null;
590
712
  }
713
+ var ERC7710_ASSET_TRANSFER_METHOD = "erc7710";
714
+ function x402AssetTransferMethod(option) {
715
+ const raw = option.extra?.assetTransferMethod;
716
+ return typeof raw === "string" ? raw : null;
717
+ }
718
+ function isErc7710Option(option) {
719
+ return x402AssetTransferMethod(option) === ERC7710_ASSET_TRANSFER_METHOD;
720
+ }
721
+ function x402FacilitatorAddresses(option) {
722
+ const raw = option.extra?.facilitatorAddresses;
723
+ if (!Array.isArray(raw)) return null;
724
+ const addresses = raw.filter((a) => typeof a === "string" && ADDRESS_RE.test(a));
725
+ return addresses.length > 0 ? addresses : null;
726
+ }
727
+ function isPayableStandardOption(opt) {
728
+ return opt.scheme === "exact" && opt.network in STANDARD_X402_NETWORKS && STANDARD_X402_USDC_ADDRESSES.has(opt.asset.toLowerCase()) && isPositiveDecimalAtomicAmount(optionAuthorizationAmount(opt));
729
+ }
591
730
  function selectStandardPaymentOption(accepts) {
592
731
  if (!accepts || accepts.length === 0) return null;
593
732
  for (const opt of accepts) {
594
- if (opt.scheme === "exact" && opt.network in STANDARD_X402_NETWORKS && STANDARD_X402_USDC_ADDRESSES.has(opt.asset.toLowerCase()) && isPositiveDecimalAtomicAmount(optionAuthorizationAmount(opt))) {
595
- return opt;
733
+ if (opt === null || typeof opt !== "object") continue;
734
+ if (!isErc7710Option(opt) && isPayableStandardOption(opt)) return opt;
735
+ }
736
+ return null;
737
+ }
738
+ function selectErc7710PaymentOption(accepts) {
739
+ if (!accepts || accepts.length === 0) return null;
740
+ for (const opt of accepts) {
741
+ if (opt === null || typeof opt !== "object") continue;
742
+ if (isErc7710Option(opt) && isPayableStandardOption(opt)) return opt;
743
+ }
744
+ return null;
745
+ }
746
+ function selectX402SettlementScheme(accepts, opts) {
747
+ if (opts.delegationRail) {
748
+ const preferred = selectErc7710PaymentOption(accepts);
749
+ if (preferred) {
750
+ return {
751
+ scheme: "erc7710",
752
+ option: preferred,
753
+ facilitatorAddresses: x402FacilitatorAddresses(preferred)
754
+ };
596
755
  }
597
756
  }
757
+ const fallback = selectStandardPaymentOption(accepts);
758
+ if (fallback) return { scheme: "eip3009", option: fallback, facilitatorAddresses: null };
598
759
  return null;
599
760
  }
600
761
  function x402AuthorizationAmount(option) {
@@ -624,7 +785,7 @@ function buildX402ExpectedMessage(context) {
624
785
  payload.typedDataHash = context.typedDataHash.toLowerCase();
625
786
  }
626
787
  return `Haven x402 expected context v${version}
627
- ${stableStringify(payload)}`;
788
+ ${stableStringify2(payload)}`;
628
789
  }
629
790
  function toStandardPaymentRequirements(paymentRequired, option) {
630
791
  const network = STANDARD_X402_NETWORKS[option.network];
@@ -655,6 +816,89 @@ function toStandardPaymentRequirements(paymentRequired, option) {
655
816
  extra: option.extra
656
817
  };
657
818
  }
819
+ async function validateStandardX402PaymentHeader(paymentHeader, context) {
820
+ try {
821
+ if (typeof paymentHeader !== "string" || paymentHeader.length === 0 || paymentHeader.length > X402_PAYMENT_HEADER_MAX_LENGTH || paymentHeader.length % 4 !== 0 || !BASE64_RE.test(paymentHeader)) {
822
+ throw new Error("wire");
823
+ }
824
+ const decoded = decodeBase64Json(paymentHeader);
825
+ if (!decoded || typeof decoded !== "object" || Array.isArray(decoded)) throw new Error("shape");
826
+ const version = decoded.x402Version;
827
+ const payload = decoded.payload;
828
+ if (version !== 1 && version !== 2 || !payload || typeof payload !== "object" || Array.isArray(payload)) {
829
+ throw new Error("shape");
830
+ }
831
+ if (version === 1) {
832
+ if (!hasOnlyKeys(decoded, ["x402Version", "scheme", "network", "payload"])) throw new Error("shape");
833
+ if (decoded.scheme !== "exact" || decoded.network !== standardX402WireNetwork(context.network)) {
834
+ throw new Error("context");
835
+ }
836
+ } else {
837
+ if (!hasOnlyKeys(decoded, ["x402Version", "accepted", "payload"])) throw new Error("shape");
838
+ const accepted = selectStandardPaymentOption([decoded.accepted]);
839
+ if (!accepted || !matchesHeaderContext(accepted, context)) throw new Error("context");
840
+ }
841
+ const record = payload;
842
+ if (!hasOnlyKeys(record, ["signature", "authorization"])) throw new Error("shape");
843
+ if (typeof record.signature !== "string" || !SIGNATURE_RE.test(record.signature)) throw new Error("shape");
844
+ const authorization = record.authorization;
845
+ if (!authorization || typeof authorization !== "object" || Array.isArray(authorization)) throw new Error("shape");
846
+ const auth = authorization;
847
+ if (!hasOnlyKeys(auth, ["from", "to", "value", "validAfter", "validBefore", "nonce"])) throw new Error("shape");
848
+ if (typeof auth.from !== "string" || !ADDRESS_RE.test(auth.from) || typeof auth.to !== "string" || !ADDRESS_RE.test(auth.to) || typeof auth.value !== "string" || !isPositiveDecimalAtomicAmount(auth.value) || typeof auth.validAfter !== "string" || !DECIMAL_ATOMIC_AMOUNT_RE.test(auth.validAfter) || typeof auth.validBefore !== "string" || !DECIMAL_ATOMIC_AMOUNT_RE.test(auth.validBefore) || typeof auth.nonce !== "string" || !NONCE_RE.test(auth.nonce)) {
849
+ throw new Error("shape");
850
+ }
851
+ if (!sameAddress2(auth.from, context.payer) || !sameAddress2(auth.to, context.merchantTo) || auth.value !== context.amountAtomic) {
852
+ throw new Error("context");
853
+ }
854
+ const validAfter = BigInt(auth.validAfter);
855
+ const validBefore = BigInt(auth.validBefore);
856
+ const now = BigInt(Math.floor(Date.now() / 1e3));
857
+ if (validBefore <= now || validAfter > validBefore) throw new Error("expired");
858
+ const typedData = buildSweepTypedData({
859
+ from: auth.from,
860
+ to: auth.to,
861
+ value: auth.value,
862
+ validAfter: auth.validAfter,
863
+ validBefore: auth.validBefore,
864
+ nonce: auth.nonce,
865
+ token: context.asset,
866
+ chainId: context.chainId
867
+ });
868
+ const recovered = await recoverTypedDataAddress({
869
+ ...typedData,
870
+ // `buildSweepTypedData` keeps the public domain framework-neutral;
871
+ // viem brands contract addresses at this crypto call boundary only.
872
+ domain: {
873
+ ...typedData.domain,
874
+ verifyingContract: typedData.domain.verifyingContract
875
+ },
876
+ message: {
877
+ ...typedData.message,
878
+ from: typedData.message.from,
879
+ to: typedData.message.to,
880
+ nonce: typedData.message.nonce
881
+ },
882
+ signature: record.signature
883
+ });
884
+ if (!sameAddress2(recovered, context.payer)) throw new Error("recovery");
885
+ } catch {
886
+ throw new X402PaymentHeaderValidationError();
887
+ }
888
+ }
889
+ function hasOnlyKeys(value, allowed) {
890
+ return Object.keys(value).every((key) => allowed.includes(key)) && allowed.every((key) => key in value);
891
+ }
892
+ function sameAddress2(left, right) {
893
+ return left.toLowerCase() === right.toLowerCase();
894
+ }
895
+ function standardX402WireNetwork(network) {
896
+ return STANDARD_X402_NETWORKS[network] ?? null;
897
+ }
898
+ function matchesHeaderContext(option, context) {
899
+ if (option.scheme !== "exact" || !sameAddress2(option.payTo, context.merchantTo) || !sameAddress2(option.asset, context.asset) || option.network !== context.network || x402AuthorizationAmount(option) !== context.amountAtomic) return false;
900
+ return option.resource === void 0 || option.resource === context.resourceUrl;
901
+ }
658
902
  function buildX402IdempotencyKey(paymentRequired, option, now = Date.now()) {
659
903
  const bucket = Math.floor(now / X402_IDEMPOTENCY_BUCKET_MS);
660
904
  const material = [
@@ -691,15 +935,15 @@ function resolveTokenFromAddress(address, network) {
691
935
  }
692
936
  return ALL_TOKENS[lower] ?? null;
693
937
  }
694
- function stableStringify(value) {
938
+ function stableStringify2(value) {
695
939
  if (value === null || typeof value !== "object") {
696
940
  const primitive = JSON.stringify(value);
697
941
  return primitive === void 0 ? "undefined" : primitive;
698
942
  }
699
943
  if (value instanceof Date) return JSON.stringify(value.toISOString());
700
- if (Array.isArray(value)) return `[${value.map((item) => stableStringify(item)).join(",")}]`;
944
+ if (Array.isArray(value)) return `[${value.map((item) => stableStringify2(item)).join(",")}]`;
701
945
  const object = value;
702
- return `{${Object.keys(object).sort().map((key) => `${JSON.stringify(key)}:${stableStringify(object[key])}`).join(",")}}`;
946
+ return `{${Object.keys(object).sort().map((key) => `${JSON.stringify(key)}:${stableStringify2(object[key])}`).join(",")}}`;
703
947
  }
704
948
  function createJsonRpcProvider(url) {
705
949
  return new ethers.JsonRpcProvider(url);
@@ -770,6 +1014,7 @@ var PAYMENT_STATE_STATUS_CODES = {
770
1014
  };
771
1015
  function chainIdFromNetwork(network) {
772
1016
  if (network === "base") return 8453;
1017
+ if (network === "base-sepolia") return 84532;
773
1018
  if (!network?.startsWith("eip155:")) return void 0;
774
1019
  const chainId = Number(network.slice("eip155:".length));
775
1020
  return Number.isFinite(chainId) ? chainId : void 0;
@@ -818,7 +1063,7 @@ function messageForState(label, status, paymentId, nextAction) {
818
1063
  }
819
1064
  return `${label} is ${status}; next_action=${nextAction} (payment_id: ${paymentId}).`;
820
1065
  }
821
- function sameAddress(a, b) {
1066
+ function sameAddress3(a, b) {
822
1067
  return Boolean(a && b && a.toLowerCase() === b.toLowerCase());
823
1068
  }
824
1069
  function decimalFromUsdcAtomic(value) {
@@ -1155,6 +1400,14 @@ var HavenClient = class {
1155
1400
  }
1156
1401
  return signUserOpTypedDataForDelegation(this.delegateKey, signData.typed_data);
1157
1402
  }
1403
+ if (scheme === "eip712_delegation") {
1404
+ if (!signData.typed_data) {
1405
+ throw new HavenSigningError(
1406
+ "sign_data.signature_scheme is eip712_delegation but typed_data is missing \u2014 refusing to sign the bare hash (the settlement would be rejected on redemption)."
1407
+ );
1408
+ }
1409
+ return signSettlementDelegationTypedData(this.delegateKey, signData.typed_data);
1410
+ }
1158
1411
  if (scheme === void 0) {
1159
1412
  return signHash(this.delegateKey, signData.hash);
1160
1413
  }
@@ -1725,6 +1978,140 @@ var HavenClient = class {
1725
1978
  this.cacheX402Receipt(idempotencyKey, paymentHeader, receipt);
1726
1979
  return receipt;
1727
1980
  }
1981
+ /**
1982
+ * Pay a merchant through **erc7710 direct settlement** (#1454, epic #1450).
1983
+ *
1984
+ * The whole point of this path is what it does NOT do. There is no funding
1985
+ * leg: the merchant redeems a delegation chain and pulls from the treasury
1986
+ * directly, so the delegate EOA never holds the money, no sweep can strand
1987
+ * it, and the #713 reconciliation class does not apply. It is also why this
1988
+ * method is SMALLER than the 3009 path — the backend assembles the merchant
1989
+ * `X-PAYMENT` header in `assembleSettlementPayload`, so the SDK builds no
1990
+ * header locally.
1991
+ *
1992
+ * authorize (payTo = the MERCHANT) → sign the child → settle → header
1993
+ *
1994
+ * The caller then retries the merchant with that header. **Nothing has
1995
+ * settled when this returns** — that is why it does not return an
1996
+ * `X402Receipt`.
1997
+ *
1998
+ * Requires a delegation-rail account. The backend enforces that
1999
+ * (`validateGenericSchemeRail`), and so does this method, before building a
2000
+ * request the backend would only reject: an error a client can explain is
2001
+ * worth more than a 400 it has to decode.
2002
+ *
2003
+ * **MCP callers must pass `options.resourceUrl`.** An in-band MCP 402
2004
+ * challenge frequently carries no `resource` object at all, so
2005
+ * `paymentRequired.resource?.url` is undefined and the backend answers
2006
+ * "Valid url is required". The QA scenario this path was ported from falls
2007
+ * back to the request URL for exactly that reason — the SDK cannot, because
2008
+ * it never saw the request. Pass it.
2009
+ */
2010
+ async settleX402Erc7710(paymentRequired, options = {}) {
2011
+ if (!this.delegateKey) {
2012
+ throw new HavenSigningError(
2013
+ "delegateKey is required for x402 payments. Pass it in the HavenClient config."
2014
+ );
2015
+ }
2016
+ const prepared = await this.prepareX402Erc7710(paymentRequired, options);
2017
+ const signature = await this.signForData(prepared.signData);
2018
+ const paymentHeader = await this.submitX402Erc7710(prepared.paymentId, signature);
2019
+ return { ...prepared.settlement, paymentHeader };
2020
+ }
2021
+ /**
2022
+ * The AUTHORIZE half of erc7710 settlement (#1456): select the scheme, build
2023
+ * the request, and return the child to be signed — without signing it.
2024
+ *
2025
+ * Split out because the hosted topology cannot use `settleX402Erc7710()`:
2026
+ * that method signs in-process with `delegateKey`, and hosted Haven does not
2027
+ * have one and must not. The hosted MCP server drives these two halves with
2028
+ * the LOCAL signer in between, so the key stays where it belongs and the
2029
+ * request shaping stays in one place rather than being reimplemented.
2030
+ */
2031
+ async prepareX402Erc7710(paymentRequired, options = {}) {
2032
+ const delegationRail = options.delegationRail ?? (await this.getAgent()).executionRail === "delegation";
2033
+ if (!delegationRail) {
2034
+ throw new HavenApiError(
2035
+ "erc7710 settlement requires a delegation-rail account; this one is not on it. Use authorizeX402() for the standard EIP-3009 path.",
2036
+ 400
2037
+ );
2038
+ }
2039
+ const selection = selectX402SettlementScheme(paymentRequired.accepts, { delegationRail });
2040
+ if (!selection || selection.scheme !== "erc7710") {
2041
+ throw new HavenApiError(
2042
+ "This merchant does not advertise an erc7710 settlement option (no accepts[] entry carries extra.assetTransferMethod: 'erc7710'). Use authorizeX402() for the standard EIP-3009 path.",
2043
+ 400
2044
+ );
2045
+ }
2046
+ const option = selection.option;
2047
+ const merchantPayTo = option.payTo;
2048
+ const amountAtomic = x402AuthorizationAmount(option);
2049
+ const raw = await this.post("/x402", {
2050
+ url: options.resourceUrl ?? paymentRequired.resource?.url,
2051
+ // payTo = the MERCHANT is what selects direct settlement server-side.
2052
+ // The explicit settlementScheme must AGREE with that shape (#1360) —
2053
+ // disagreement is a 400 by design, so that a stale delegate address
2054
+ // becomes a loud mismatch instead of a silent reroute to the 3009 leg.
2055
+ payTo: merchantPayTo,
2056
+ settlementScheme: "erc7710",
2057
+ amount: amountAtomic,
2058
+ asset: option.asset,
2059
+ network: option.network,
2060
+ // The v2 header echoes the accepted entry field-for-field, so the quoted
2061
+ // timeout must round-trip or the merchant rejects the echo (#1064).
2062
+ maxTimeoutSeconds: option.maxTimeoutSeconds,
2063
+ // #1058: forward the advertised facilitators verbatim — the child becomes
2064
+ // redeemable ONLY by them. `null` here means the merchant advertised none
2065
+ // (or an empty array, which the backend 400s on), so the field is OMITTED
2066
+ // rather than sent empty. See x402FacilitatorAddresses.
2067
+ ...selection.facilitatorAddresses ? { facilitatorAddresses: selection.facilitatorAddresses } : {}
2068
+ });
2069
+ if (!raw.payment_id) {
2070
+ throw new HavenApiError("No payment_id returned from x402/authorize", 500, raw);
2071
+ }
2072
+ const signData = raw.sign_data;
2073
+ if (signData?.signature_scheme !== "eip712_delegation" || !signData.typed_data) {
2074
+ throw new HavenApiError(
2075
+ `x402/authorize did not return an erc7710 settlement child (signature_scheme was ${JSON.stringify(signData?.signature_scheme)}). Refusing to sign a payload this path did not ask for.`,
2076
+ 500,
2077
+ raw
2078
+ );
2079
+ }
2080
+ return {
2081
+ paymentId: raw.payment_id,
2082
+ signData,
2083
+ settlement: {
2084
+ paymentId: raw.payment_id,
2085
+ merchantPayTo,
2086
+ amountAtomic,
2087
+ asset: option.asset,
2088
+ network: option.network,
2089
+ facilitatorAddresses: selection.facilitatorAddresses
2090
+ }
2091
+ };
2092
+ }
2093
+ /**
2094
+ * The SETTLE half (#1456): exchange the signed child for the merchant header.
2095
+ *
2096
+ * The SDK builds no header on this path — the backend assembles the MetaMask
2097
+ * erc7710 payload in `assembleSettlementPayload`. Whoever produced the
2098
+ * signature (an in-process delegate key, or the local edge signer over the
2099
+ * hosted boundary) is irrelevant here.
2100
+ */
2101
+ async submitX402Erc7710(paymentId, signature) {
2102
+ const settled = await this.post(
2103
+ `/x402/${paymentId}/settle`,
2104
+ { signature }
2105
+ );
2106
+ if (!settled.payment_header) {
2107
+ throw new HavenApiError(
2108
+ "x402 settle returned no payment_header \u2014 the merchant cannot be retried.",
2109
+ 500,
2110
+ settled
2111
+ );
2112
+ }
2113
+ return settled.payment_header;
2114
+ }
1728
2115
  async resumeAuthorizedX402(input) {
1729
2116
  if (!this.delegateKey) {
1730
2117
  throw new HavenSigningError(
@@ -2076,8 +2463,18 @@ var HavenClient = class {
2076
2463
  * balance — otherwise it rejects with "Payment verification failed". The
2077
2464
  * SDK's local path already does this (see authorizeStandardX402); the hosted
2078
2465
  * split flow regressed when the 5→3 collapse removed the incidental
2079
- * inter-call latency that used to mask it. No-op when the funding tx hash or
2080
- * a chain RPC (chainRpcs[chainId]) is unavailable.
2466
+ * inter-call latency that used to mask it.
2467
+ *
2468
+ * **NOT a no-op when the funding tx hash is absent** (#1508). The WAIT is
2469
+ * skipped without a hash or a chain RPC, but the `GET /payments/:id` read
2470
+ * below runs UNCONDITIONALLY — it is how the fallback hash and the chainId
2471
+ * are obtained. That distinction is load-bearing: this method must never be
2472
+ * called on a scheme with no funding leg, because the read itself fails once
2473
+ * the intent reaches a status the backend maps to a non-2xx (`submitted` is a
2474
+ * 409), turning a settled payment into a reported error. The previous wording
2475
+ * here said "No-op when the funding tx hash ... is unavailable", and the
2476
+ * hosted erc7710 path was written against that promise — see
2477
+ * `deliverMerchantPayment`'s `noFundingLeg` option.
2081
2478
  */
2082
2479
  async ensureFundingConfirmed(paymentId, fundingTxHash) {
2083
2480
  const status = await this.getPaymentStatus(paymentId);
@@ -2086,8 +2483,10 @@ var HavenClient = class {
2086
2483
  async completeX402MerchantCall(input) {
2087
2484
  const evidenceContext = await this.resolveX402MerchantCompletionContext({
2088
2485
  paymentId: input.paymentId,
2089
- url: input.url
2486
+ url: input.url,
2487
+ noFundingLeg: input.noFundingLeg === true
2090
2488
  });
2489
+ const fundingTxHash = evidenceContext.txHash;
2091
2490
  const shouldHandshakeMcp = isMcpUrl(input.url) || input.mcpTransport?.handshakeRequired === true;
2092
2491
  const x402Wallet = shouldHandshakeMcp ? await this.resolveX402WalletForMerchantCall() : this.x402PayerAddress();
2093
2492
  let mcpSessionId;
@@ -2111,33 +2510,37 @@ var HavenClient = class {
2111
2510
  body = text;
2112
2511
  }
2113
2512
  if (!surfaced.ok) {
2114
- await this.recordMerchantRetryRejected({
2115
- rail: "x402",
2116
- paymentId: evidenceContext.paymentId,
2117
- txHash: evidenceContext.txHash,
2118
- resourceUrl: evidenceContext.resourceUrl,
2119
- merchant: {
2120
- merchant_status: surfaced.status,
2121
- merchant_status_text: surfaced.statusText,
2122
- merchant_headers: Object.fromEntries(surfaced.headers.entries()),
2123
- merchant_body: text
2124
- },
2125
- details: {
2126
- merchant_to: evidenceContext.merchantAddress
2127
- }
2128
- });
2513
+ if (!input.noFundingLeg && fundingTxHash) {
2514
+ await this.recordMerchantRetryRejected({
2515
+ rail: "x402",
2516
+ paymentId: evidenceContext.paymentId,
2517
+ txHash: fundingTxHash,
2518
+ resourceUrl: evidenceContext.resourceUrl,
2519
+ merchant: {
2520
+ merchant_status: surfaced.status,
2521
+ merchant_status_text: surfaced.statusText,
2522
+ merchant_headers: Object.fromEntries(surfaced.headers.entries()),
2523
+ merchant_body: text
2524
+ },
2525
+ details: {
2526
+ merchant_to: evidenceContext.merchantAddress
2527
+ }
2528
+ });
2529
+ }
2129
2530
  } else {
2130
- await this.reportMachinePaymentEvidence({
2131
- paymentId: evidenceContext.paymentId,
2132
- rail: "x402",
2133
- txHash: evidenceContext.txHash,
2134
- resourceUrl: evidenceContext.resourceUrl,
2135
- merchantStatus: surfaced.status,
2136
- paymentProofHeaderName: "X-PAYMENT",
2137
- paymentProofHeader: input.paymentHeader,
2138
- protocolReceiptHeaderName: protocolReceiptHeader ? "PAYMENT-RESPONSE" : void 0,
2139
- protocolReceiptHeader
2140
- });
2531
+ if (!input.noFundingLeg && fundingTxHash) {
2532
+ await this.reportMachinePaymentEvidence({
2533
+ paymentId: evidenceContext.paymentId,
2534
+ rail: "x402",
2535
+ txHash: fundingTxHash,
2536
+ resourceUrl: evidenceContext.resourceUrl,
2537
+ merchantStatus: surfaced.status,
2538
+ paymentProofHeaderName: "X-PAYMENT",
2539
+ paymentProofHeader: input.paymentHeader,
2540
+ protocolReceiptHeaderName: protocolReceiptHeader ? "PAYMENT-RESPONSE" : void 0,
2541
+ protocolReceiptHeader
2542
+ });
2543
+ }
2141
2544
  await this.reportMerchantReceipt(evidenceContext.paymentId, surfaced);
2142
2545
  }
2143
2546
  return {
@@ -2183,11 +2586,11 @@ var HavenClient = class {
2183
2586
  status
2184
2587
  );
2185
2588
  }
2186
- const readyForMerchantCompletion = status.nextAction === AgentPaymentNextAction.RetryOriginalX402Request || status.kind === "payment_intent" && status.status === "confirmed" && status.phase === AgentPaymentPhase.PaymentConfirmed && status.nextAction === AgentPaymentNextAction.None;
2589
+ const readyForMerchantCompletion = input.noFundingLeg ? status.kind === "payment_intent" && status.status === "submitted" : status.nextAction === AgentPaymentNextAction.RetryOriginalX402Request || status.kind === "payment_intent" && status.status === "confirmed" && status.phase === AgentPaymentPhase.PaymentConfirmed && status.nextAction === AgentPaymentNextAction.None;
2187
2590
  if (!readyForMerchantCompletion) {
2188
2591
  throw new HavenPaymentStateError(status.message, PAYMENT_STATE_STATUS_CODES[status.status] ?? 409, status);
2189
2592
  }
2190
- if (!status.txHash) {
2593
+ if (!input.noFundingLeg && !status.txHash) {
2191
2594
  throw new HavenApiError(
2192
2595
  `x402 payment ${status.paymentId} is ready for merchant completion but has no Haven transaction hash.`,
2193
2596
  502,
@@ -2255,7 +2658,7 @@ var HavenClient = class {
2255
2658
  status.paymentId
2256
2659
  );
2257
2660
  }
2258
- if (status.merchantAddress && !sameAddress(status.merchantAddress, option.payTo)) {
2661
+ if (status.merchantAddress && !sameAddress3(status.merchantAddress, option.payTo)) {
2259
2662
  throw new HavenApiError(
2260
2663
  "x402 resume request does not match the approved merchant.",
2261
2664
  409,
@@ -2950,6 +3353,7 @@ var HavenClient = class {
2950
3353
  token: raw.token,
2951
3354
  resourceUrl: raw.resource_url,
2952
3355
  merchantAddress: raw.merchant_address,
3356
+ payerAddress: raw.payer_address ?? null,
2953
3357
  txHash: raw.tx_hash,
2954
3358
  expiresAt: raw.expires_at,
2955
3359
  chainId: raw.chain_id,
@@ -3412,14 +3816,18 @@ normal, not an error.
3412
3816
 
3413
3817
  1. \`mcp__haven__haven_discover_tools\` to find a payable service and its
3414
3818
  \`catalog_id\`.
3415
- 2. \`mcp__haven__haven_prepare_catalog_purchase\` with \`catalog_id\` and a
3819
+ 2. If the user needs the live price before authorizing a cap, call
3820
+ \`mcp__haven__haven_quote_catalog_purchase\` with \`catalog_id\`. It is
3821
+ read-only and informational only: it never reserves a price or creates a
3822
+ payment. Tell the user its \`amount\` / \`amount_atomic\`, then choose a cap.
3823
+ 3. \`mcp__haven__haven_prepare_catalog_purchase\` with \`catalog_id\` and a
3416
3824
  spending cap. A cap is REQUIRED on this tool and is best practice on every
3417
3825
  paid call below too \u2014 it caps what the LIVE merchant quote may charge,
3418
3826
  checked before any funding intent is created. Write it the way the user
3419
3827
  said it: \`max_amount_human\` is whole tokens, so "no more than 1 USDC" is
3420
3828
  \`max_amount_human: "1"\`. (\`max_amount\` is the atomic-unit form, where
3421
3829
  "1" means 0.000001 USDC \u2014 do not convert by hand, and never send both.)
3422
- 3. Then FOLLOW THE RESPONSE'S GUIDANCE FIELDS: \`next_action\`, \`next_tool\`,
3830
+ 4. Then FOLLOW THE RESPONSE'S GUIDANCE FIELDS: \`next_action\`, \`next_tool\`,
3423
3831
  and \`next_arguments\` name the exact next call \u2014 act on those first; the
3424
3832
  prose in this section is fallback and debugging detail. If the catalog
3425
3833
  entry is missing or degraded, the response instead names
@@ -3447,7 +3855,12 @@ do not re-pay.
3447
3855
 
3448
3856
  Step-by-step alternative (also key-safe; for an older signer or backend, or
3449
3857
  when you already have a merchant URL and tool name instead of a
3450
- \`catalog_id\`): \`mcp__haven__haven_pay_mcp_tool\` then
3858
+ \`catalog_id\`): if the user needs the live price before choosing a cap, first
3859
+ call \`mcp__haven__haven_quote_mcp_tool\` with that merchant URL, tool name,
3860
+ and arguments. It is informational only; then call
3861
+ \`mcp__haven__haven_pay_mcp_tool\` with the same inputs and the explicit cap.
3862
+ The paid call always obtains a fresh quote before it creates any intent. Then
3863
+ continue \`mcp__haven__haven_pay_mcp_tool\` \u2192
3451
3864
  \`mcp__haven-signer__haven_sign\` \u2192 \`mcp__haven__haven_submit\` \u2192
3452
3865
  \`mcp__haven-signer__haven_x402_sign_header\` \u2192
3453
3866
  \`mcp__haven__haven_complete_mcp_tool\`. Pass \`payment_required\`,
@@ -3469,11 +3882,13 @@ when the result says \`retry_original_x402_request\`.
3469
3882
  \`arguments\` field (for example
3470
3883
  \`tool_arguments: { "tier": "50gb" }\` -> \`arguments: { "tier": "50gb" }\`).
3471
3884
 
3472
- **Prices:** show the user the live price from the pay-tool result, never a
3473
- catalog price. \`haven_discover_tools\` prices are indicative
3474
- (\`price_is_indicative\`) and can be stale. The pay-tool result's \`amount\` /
3475
- \`amount_atomic\` is the amount Haven authorizes for the call \u2014 a ceiling the
3476
- merchant settles at or below \u2014 so present it as the most the user will pay.
3885
+ **Prices:** show the user the live price from a read-only quote or the pay-tool
3886
+ result, never a catalog price. \`haven_discover_tools\` prices are indicative
3887
+ (\`price_is_indicative\`) and can be stale. A read-only quote is informational
3888
+ only and does not reserve a price; the later paid call re-quotes and enforces
3889
+ the cap. The pay-tool result's \`amount\` / \`amount_atomic\` is the amount
3890
+ Haven authorizes for that call \u2014 a ceiling the merchant settles at or below \u2014
3891
+ so present it as the most the user will pay.
3477
3892
 
3478
3893
  **Status:** \`mcp__haven__haven_get_payment_status\` with a \`payment_id\` to
3479
3894
  check on queued or in-flight payments. Do not poll in a tight loop.
@@ -3540,6 +3955,7 @@ the agent in the Haven dashboard under Agents. New requests stop immediately
3540
3955
  for that credential.
3541
3956
  `;
3542
3957
  var SKILL_FOLDER_NAME = "haven-pay";
3958
+ var HAVEN_SKILL_BODY_MD = HAVEN_SKILL_MD.replace(/^---\n[\s\S]*?\n---\n+/, "");
3543
3959
 
3544
3960
  // src/node-version.ts
3545
3961
  var HAVEN_MINIMUM_NODE_VERSION = "22.0.0";
@@ -3580,111 +3996,6 @@ function major(version) {
3580
3996
  return String(parseNodeVersion(version)[0]);
3581
3997
  }
3582
3998
 
3583
- // src/sweep.ts
3584
- var SWEEP_BASE_CHAIN_ID = 8453;
3585
- var SWEEP_BASE_SEPOLIA_CHAIN_ID = 84532;
3586
- var SWEEP_BASE_USDC_ADDRESS = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
3587
- var SWEEP_BASE_SEPOLIA_USDC_ADDRESS = "0x036CbD53842c5426634e7929541eC2318f3dCF7e";
3588
- var USDC_EIP712_DOMAIN_BY_CHAIN = {
3589
- [SWEEP_BASE_CHAIN_ID]: {
3590
- name: "USD Coin",
3591
- version: "2",
3592
- chainId: SWEEP_BASE_CHAIN_ID,
3593
- verifyingContract: SWEEP_BASE_USDC_ADDRESS
3594
- },
3595
- [SWEEP_BASE_SEPOLIA_CHAIN_ID]: {
3596
- name: "USDC",
3597
- version: "2",
3598
- chainId: SWEEP_BASE_SEPOLIA_CHAIN_ID,
3599
- verifyingContract: SWEEP_BASE_SEPOLIA_USDC_ADDRESS
3600
- }
3601
- };
3602
- var USDC_ADDRESS_BY_CHAIN = {
3603
- [SWEEP_BASE_CHAIN_ID]: SWEEP_BASE_USDC_ADDRESS,
3604
- [SWEEP_BASE_SEPOLIA_CHAIN_ID]: SWEEP_BASE_SEPOLIA_USDC_ADDRESS
3605
- };
3606
- var SWEEPABLE_CHAIN_IDS = Object.keys(USDC_ADDRESS_BY_CHAIN).map(Number);
3607
- function isSweepableChain(chainId) {
3608
- return chainId in USDC_ADDRESS_BY_CHAIN;
3609
- }
3610
- var TRANSFER_WITH_AUTHORIZATION_TYPES = {
3611
- TransferWithAuthorization: [
3612
- { name: "from", type: "address" },
3613
- { name: "to", type: "address" },
3614
- { name: "value", type: "uint256" },
3615
- { name: "validAfter", type: "uint256" },
3616
- { name: "validBefore", type: "uint256" },
3617
- { name: "nonce", type: "bytes32" }
3618
- ]
3619
- };
3620
- function sweepUsdcAddress(chainId) {
3621
- const address = USDC_ADDRESS_BY_CHAIN[chainId];
3622
- if (!address) {
3623
- throw new HavenSigningError(
3624
- `Sweep is not supported on chain ${chainId}. Supported: ${SWEEPABLE_CHAIN_IDS.join(", ")}.`
3625
- );
3626
- }
3627
- return address;
3628
- }
3629
- function sweepUsdcDomain(chainId) {
3630
- const domain = USDC_EIP712_DOMAIN_BY_CHAIN[chainId];
3631
- if (!domain) {
3632
- throw new HavenSigningError(
3633
- `Sweep is not supported on chain ${chainId}. Supported: ${SWEEPABLE_CHAIN_IDS.join(", ")}.`
3634
- );
3635
- }
3636
- return domain;
3637
- }
3638
- function sameAddress2(a, b) {
3639
- return a.toLowerCase() === b.toLowerCase();
3640
- }
3641
- function buildSweepTypedData(auth) {
3642
- const domain = sweepUsdcDomain(auth.chainId);
3643
- const expectedToken = sweepUsdcAddress(auth.chainId);
3644
- if (!sameAddress2(auth.token, expectedToken)) {
3645
- throw new HavenSigningError(
3646
- `Sweep token ${auth.token} is not the canonical USDC contract for chain ${auth.chainId}.`
3647
- );
3648
- }
3649
- if (!/^0x[0-9a-fA-F]{64}$/.test(auth.nonce)) {
3650
- throw new HavenSigningError("Sweep nonce must be a 0x-prefixed 32-byte hex string.");
3651
- }
3652
- return {
3653
- domain,
3654
- types: TRANSFER_WITH_AUTHORIZATION_TYPES,
3655
- primaryType: "TransferWithAuthorization",
3656
- message: {
3657
- from: auth.from,
3658
- to: auth.to,
3659
- value: BigInt(auth.value),
3660
- validAfter: BigInt(auth.validAfter),
3661
- validBefore: BigInt(auth.validBefore),
3662
- nonce: auth.nonce
3663
- }
3664
- };
3665
- }
3666
- function buildSweepAuthorizationMessage(auth) {
3667
- return `Haven sweep authorization v1
3668
- ${stableStringify2({
3669
- version: 1,
3670
- kind: "haven.sweep.authorization",
3671
- from: auth.from.toLowerCase(),
3672
- to: auth.to.toLowerCase(),
3673
- value: auth.value,
3674
- validAfter: auth.validAfter,
3675
- validBefore: auth.validBefore,
3676
- nonce: auth.nonce.toLowerCase(),
3677
- token: auth.token.toLowerCase(),
3678
- chainId: auth.chainId
3679
- })}`;
3680
- }
3681
- function stableStringify2(value) {
3682
- if (value === null || typeof value !== "object") return JSON.stringify(value);
3683
- if (Array.isArray(value)) return `[${value.map((item) => stableStringify2(item)).join(",")}]`;
3684
- const object = value;
3685
- return `{${Object.keys(object).sort().map((key) => `${JSON.stringify(key)}:${stableStringify2(object[key])}`).join(",")}}`;
3686
- }
3687
-
3688
3999
  // src/merchant-discovery.ts
3689
4000
  var MERCHANT_DISCOVERY_PATHS = ["/.well-known/haven-demo-merchant", "/"];
3690
4001
  var DISCOVERY_MAX_BYTES = 64 * 1024;
@@ -3729,6 +4040,6 @@ function sameUrl(a, b) {
3729
4040
  }
3730
4041
  }
3731
4042
 
3732
- export { AGENT_PAYMENT_FAILURE_CODE_VALUES, AGENT_PAYMENT_NEXT_ACTION_VALUES, AGENT_PAYMENT_PHASE_VALUES, AGENT_PAYMENT_RAIL_VALUES, AgentPaymentFailureCode, AgentPaymentFailureCodeDescriptions, AgentPaymentFailureCodeSchema, AgentPaymentNextAction, AgentPaymentNextActionDescriptions, AgentPaymentNextActionSchema, AgentPaymentPhase, AgentPaymentPhaseDescriptions, AgentPaymentPhaseSchema, AgentPaymentRail, AgentPaymentRailDescriptions, AgentPaymentRailSchema, AgentPaymentWarningCode, DISCOVERY_MAX_BYTES, HAVEN_MINIMUM_NODE_VERSION, HAVEN_SKILL_MD, HavenApiError, HavenClient, HavenError, HavenPaymentStateError, HavenSigningError, HavenTimeoutError, HavenUnsupportedSignerVersionError, MERCHANT_DISCOVERY_PATHS, MerchantTimeoutError, RECEIPT_VERSION, SIGNER_UPDATE_FALLBACK, SKILL_FOLDER_NAME, SWEEP_BASE_CHAIN_ID, SWEEP_BASE_SEPOLIA_CHAIN_ID, SWEEP_BASE_SEPOLIA_USDC_ADDRESS, SWEEP_BASE_USDC_ADDRESS, SignerRefusalCode, TRANSFER_WITH_AUTHORIZATION_TYPES, X402UnexpectedStatusError, X402_MAX_AUTHORIZATION_WINDOW_SECONDS, X402_SETTLEMENT_FORWARD_MARGIN_SECONDS, addressFromKey, buildSweepAuthorizationMessage, buildSweepTypedData, buildX402ExpectedMessage, compareNodeVersions, composeDescription, decodeBase64Json, decodeBase64Utf8, discoverMerchantMcpUrl, encodeBase64Json, encodeBase64Utf8, encodePaymentProof, havenTools, isSupportedNodeVersion, isSweepableChain, parsePaymentRequired, parsePaymentRequiredResponse, resolveTokenFromAddress, sameUrl, selectPaymentOption, selectStandardPaymentOption, signHash, signUserOpTypedDataForDelegation, sweepUsdcAddress, sweepUsdcDomain, toStandardPaymentRequirements, toolDescriptions, unsupportedNodeVersionMessage, verifyPaymentReceipt, verifySignature, x402AuthorizationAmount };
4043
+ export { AGENT_PAYMENT_FAILURE_CODE_VALUES, AGENT_PAYMENT_NEXT_ACTION_VALUES, AGENT_PAYMENT_PHASE_VALUES, AGENT_PAYMENT_RAIL_VALUES, AgentPaymentFailureCode, AgentPaymentFailureCodeDescriptions, AgentPaymentFailureCodeSchema, AgentPaymentNextAction, AgentPaymentNextActionDescriptions, AgentPaymentNextActionSchema, AgentPaymentPhase, AgentPaymentPhaseDescriptions, AgentPaymentPhaseSchema, AgentPaymentRail, AgentPaymentRailDescriptions, AgentPaymentRailSchema, AgentPaymentWarningCode, DISCOVERY_MAX_BYTES, ERC7710_ASSET_TRANSFER_METHOD, HAVEN_MINIMUM_NODE_VERSION, HAVEN_SKILL_BODY_MD, HAVEN_SKILL_MD, HavenApiError, HavenClient, HavenError, HavenPaymentStateError, HavenSigningError, HavenTimeoutError, HavenUnsupportedSignerVersionError, MERCHANT_DISCOVERY_PATHS, MerchantTimeoutError, RECEIPT_VERSION, SIGNER_UPDATE_FALLBACK, SKILL_FOLDER_NAME, SWEEP_BASE_CHAIN_ID, SWEEP_BASE_SEPOLIA_CHAIN_ID, SWEEP_BASE_SEPOLIA_USDC_ADDRESS, SWEEP_BASE_USDC_ADDRESS, SignerRefusalCode, TRANSFER_WITH_AUTHORIZATION_TYPES, X402PaymentHeaderValidationError, X402UnexpectedStatusError, X402_MAX_AUTHORIZATION_WINDOW_SECONDS, X402_SETTLEMENT_FORWARD_MARGIN_SECONDS, addressFromKey, buildSweepAuthorizationMessage, buildSweepTypedData, buildX402ExpectedMessage, compareNodeVersions, composeDescription, decodeBase64Json, decodeBase64Utf8, discoverMerchantMcpUrl, encodeBase64Json, encodeBase64Utf8, encodePaymentProof, havenTools, isErc7710Option, isSupportedNodeVersion, isSweepableChain, normalizePaymentRequired, parsePaymentRequired, parsePaymentRequiredResponse, resolveTokenFromAddress, sameUrl, selectErc7710PaymentOption, selectPaymentOption, selectStandardPaymentOption, selectX402SettlementScheme, signHash, signUserOpTypedDataForDelegation, sweepUsdcAddress, sweepUsdcDomain, toStandardPaymentRequirements, toolDescriptions, unsupportedNodeVersionMessage, validateStandardX402PaymentHeader, verifyPaymentReceipt, verifySignature, x402AssetTransferMethod, x402AuthorizationAmount, x402FacilitatorAddresses };
3733
4044
  //# sourceMappingURL=index.js.map
3734
4045
  //# sourceMappingURL=index.js.map